Changelog
GitHubOgni cambiamento rilevante, release dopo release. Lo stesso file è distribuito nel repository ed è tradotto in tutte le 19 lingue.
[1.200.0] — 2026-08-15
Aggiunto — un controllo “ancora attiva?” con un clic per gli annunci su ATS nel tuo tracker.
Aggiunto
- Su #/tracker, una candidatura il cui URL è un annuncio Greenhouse / Lever / Ashby / Workday / SmartRecruiters mostra ora un pulsante “Ancora attiva?”. Un clic interroga il JSON pubblico dell’ATS stesso — zero token, nessun browser — e mostra Attiva / Scaduta / Sconosciuta, per scovare annunci morti senza aprirli uno a uno. Conservativo per scelta: solo un 404/410 netto vale Scaduta; ogni ambiguità resta Sconosciuta (mai una falsa Scaduta).
Note
- Nuovi
server/lib/liveness-core.mjs+liveness-api.mjse una rotta di sola letturaGET /api/liveness?url=(nessuna scrittura, nessun LLM). Sicuro rispetto a SSRF: l’URL passa perisValidJobUrl, poi l’API dell’ATS è raggiunta solo viasafeGet(DNS fissato) con host fisso e segmenti validati.tests/liveness-core.test.mjs+tests/liveness-route.test.mjs. Suite: 2557.
[1.199.0] — 2026-08-15
Corretto — le tabelle larghe ora scorrono lateralmente invece di essere tagliate.
Corretto
- Nella pagina Scan (e in tutte le altre tabelle — Tracker, Statistiche, Utilizzo, Dashboard) una tabella più larga della finestra veniva tagliata senza barra di scorrimento, lasciando irraggiungibili le ultime colonne. Ora le tabelle larghe mostrano una barra di scorrimento orizzontale quando serve, così ogni colonna resta raggiungibile a qualsiasi larghezza.
Note
.table-wrapinpublic/css/components.cssè passato daoverflow: hiddenaoverflow-x: auto(come il contenitore.reports-scrollesistente); il bordo arrotondato è preservato.tests/table-wrap-scroll.test.mjs. Suite: 2540.
[1.198.0] — 2026-08-15
Aggiunto — i ritentativi di scansione ora usano backoff esponenziale, jitter e rispettano il Retry-After di un limitatore di frequenza.
Aggiunto
- Quando una bacheca di annunci limita la frequenza o fallisce brevemente (HTTP 429 / 5xx) durante una scansione, il ritentativo ora attende con backoff esponenziale + jitter invece di un ritardo breve fisso — una bacheca occupata non viene martellata alla stessa cadenza e i ritentativi concorrenti non si ri-scontrano all’unisono. Un
Retry-Afterdella bacheca viene rispettato (ma limitato, così unRetry-After: 86400ostile non può bloccare l’intera scansione). Gli errori permanenti (404, redirect rifiutati) falliscono ancora subito — invariato.
Note
- Nuove
parseRetryAfterMs()e la puracomputeRetryDelayMs()inserver/lib/http-json.mjs;fetchJsonora cattura.retryAftersu una risposta non-ok efetchJsonWithRetryaccetta unmaxDelayMsopzionale (predefinito 8000).tests/http-json.test.mjs(+9). Suite: 2536.
[1.197.0] — 2026-08-14
Aggiunto — segui una bacheca di annunci di un fondo Getro con la sola careers_url; l’id della collezione si risolve da solo.
Aggiunto
- Una bacheca Getro seguita (b2venture, Earlybird, Point Nine, …) non ha più bisogno di un
getro_collectionnumerico cercato a mano. Fornisci lacareers_urldella bacheca stessa e l’id si risolve automaticamente da quella pagina alla prima scansione — un’unica GET sicura contro l’SSRF legge ilnetwork.idnumerico direttamente dai dati incorporati nella pagina. Ungetro_collectionesplicito ha ancora la precedenza e salta del tutto il recupero.
Note
- Nuove
httpsCareersUrl(),extractCollectionId()e laresolveCollectionId()asincrona inserver/lib/sources/getro.mjs; la pagina della bacheca viene recuperata tramitesafeGet(DNS fissato, dimensione limitata), e l’id risolto resta ancorato all’hostapi.getro.comdaassertGetroUrl. L’adattatore ora corrisponde a una voceprovider: getroche porta unacareers_urlhttps anche senza id.tests/sources-getro.test.mjs(+8). Suite: 2527.
[1.196.0] — 2026-08-14
Corretto (sicurezza) — l’adapter Workday valida un endpoint api in base al suo hostname, non a una sottostringa.
Corretto
- Un valore
api:Workday inportals.ymlora è accettato solo quando il suo hostname èmyworkdayjobs.com(o un sottodominio.myworkdayjobs.com). Il vecchio controllo era una corrispondenza di sottostringa, quindi qualsiasi URL che contenesse soltanto la stringa — es.https://example.com/?x=myworkdayjobs.com— passava e sarebbe stato usato come endpoint. Gli endpoint Workday reali non sono interessati. (Segnalato da CodeQL, #443.)
Note
- Il nuovo
isWorkdayApi()analizza l’URL e verifica l’host (server/lib/portals/adapters/workday.mjs).tests/workday-adapter-endpoint.test.mjs(+1). Suite: 2522.
[1.195.0] — 2026-08-14
Prestazioni (scanner) — il rilevamento dei repost resta veloce su cronologie di scansione grandi.
Prestazioni
- Il rilevamento di annunci duplicati non degrada più a O(N²) su un grande
scan-history.tsv. Il raggruppamento dei titoli per azienda era un ciclo annidato che pagava unroleFuzzyMatchcompleto per ogni coppia; ora è un indice invertito — raggruppa le righe per titolo esatto in una passata, poi fa il match fuzzy solo tra bucket DISTINTI che condividono un token discriminante (non di base). L’output è identico — gli stessi cluster di repost — dimostrato da un test differenziale contro il vecchio algoritmo su 200+ cronologie casuali.
Note
groupRowsByTitleinserver/lib/detect-reposts.mjs(esportato per il test differenziale).tests/detect-reposts-grouping.test.mjs(+2). Suite: 2521.
[1.194.0] — 2026-08-14
Corretto (scanner) — le pagine carriere Workday con URL a segmento singolo ora vengono scansionate correttamente.
Corretto
- L’adapter Workday ora analizza gli URL carriere il cui percorso è un singolo segmento — es.
https://parsons.wd5.myworkdayjobs.com/Search,.../KBR_Careers,.../Careers. Prima il sito ripiegava suExternal, l’adapter colpiva l’endpoint CXS sbagliato e una sonda poteva sembrare sana senza restituire nulla. Ora prende il primo segmento non vuoto del percorso come sito (scartando un prefisso di lingua comeen-US); il caso documentato/en-US/Externalè invariato. (Segnalato in #255.)
Note
- Analisi strutturale del percorso in
server/lib/portals/adapters/workday.mjs.tests/workday-adapter-endpoint.test.mjs(+7). Suite: 2519.
[1.193.0] — 2026-08-14
Aggiunto (statistiche) — una scheda “Silenzio dopo il colloquio” che fa emergere i colloqui da sollecitare.
Aggiunto
- Una scheda Silenzio dopo il colloquio in
#/stats: colloqui rimasti in silenzio oltre una finestra di cortesia (30 giorni di default), unendo i tuoi colloqui attivi e il tracker — con da quanto ciascuno è in silenzio, la data dell’ultimo colloquio e il motivo. Un elenco gentile di sollecito/chiusura; solo suggerimenti, mai un’affermazione di rifiuto. Senza token.
Note
- Nuova rotta
GET /api/stats/rejection-latency(fail-soft{available:false}).tests/stats-rejection-latency-route.test.mjs(+2). +10 chiavi i18n ×17; help-hint di#/statsda 7→8 schede. Suite: 2510.
[1.192.0] — 2026-08-14
Aggiunto (cv-studio) — un controllo “Verifica i fatti del tuo CV” che scova numeri che non hai mai avuto.
Aggiunto
- Una scheda Verifica i fatti del tuo CV in
#/cv-studio: incolla un CV o una lettera su misura e verifica ogni metrica e fatto rispetto al tuo CV, profilo e two-pager reali. Ottieni un verdetto pass / warn / block più le metriche inventate, i fatti non supportati e le frasi vietate / di avviso esatte. Senza LLM; non viene scritto nulla.
Note
- Nuova rotta
POST /api/cv-studio/verify-facts: scrive il testo in un file temporaneo usa-e-getta ed esegueverify-cv-facts.mjs, fidandosi del verdetto JSON anche se lo script esce con 1 su un block.tests/cv-studio-verify-facts-route.test.mjs(+4). +15 chiavi i18n ×17. Suite: 2508.
[1.191.0] — 2026-08-14
Aggiunto (statistiche) — una scheda “Cosa imparare dopo” che ordina le competenze da imparare prima.
Aggiunto
- Una scheda Cosa imparare dopo in
#/stats: un riepilogo su tutto il tracker — le competenze mancanti che più spesso hanno affondato una bassa compatibilità, pesate (per 5−punteggio di compatibilità su ogni report) e in livelli Critical / High / Medium — più quelle già coperte dal tuo CV/profilo. Sola lettura, solo suggerimenti, senza token.
Note
- Nuova rotta
GET /api/stats/upskill(campo{ error }quando i dati sono pochi; fail-soft{available:false}).tests/stats-upskill-route.test.mjs(+3). +15 chiavi i18n ×17. Suite: 2504.
[1.190.0] — 2026-08-14
Aggiunto (tracker) — un pannello “Cronologia azienda” che ti dice quali aziende ti rispondono davvero.
Aggiunto
- Una scheda Cronologia azienda su
#/tracker: scegli un’azienda e ottieni prove in sola lettura — quanto è stata reattiva con te (in silenzio con te / misto / ha già risposto) e se lo stesso ruolo viene ripubblicato — unendo tracker, follow-up e cronologia di scansione. Nessun token; lo scanner non viene mai chiamato.
Note
- Nuova rotta
GET /api/stats/company-history[?company=](fail-soft{available:false}).tests/stats-company-history-route.test.mjs(+3). +18 chiavi i18n ×17. Suite: 2501.
[1.189.0] — 2026-08-14
Corretto (scanner) — i livelli di anzianità scritti in numeri romani ora contano anche nei titoli non latini.
Corretto
- Il classificatore di livello dietro
skip_tiersora legge un suffisso di livello in numeri romani (I / II / III / IV / V) dopo la parola del ruolo in qualsiasi scrittura — “Инженер III”, “エンジニア I”, “Ingénieur IV” — non solo dopo parole ASCII. Prima, un numero di livello dopo una parola non latina veniva ignorato e l’offerta cadeva su mid, quindiskip_tiers: [senior]o[entry]le mancava.
Note
- Lookbehind indipendente dalla scrittura in
server/lib/classify-tier.mjs; rimosso un matcherSr.duplicato morto.tests/classify-tier.test.mjs(+1). Suite: 2498.
[1.188.0] — 2026-08-14
Corretto (UI) — i pulsanti d’azione principali non sono più attaccati al sottotitolo della pagina.
Corretto
- La riga di azione / controllo principale in Riepilogo settimanale dei colloqui, Aziende finanziate, Portali, Piano di carriera e Orientamento professionale ora ha un margine superiore adeguato, così il pulsante respira sotto il sottotitolo invece di appoggiarvisi.
Note
- Guardia di regressione
tests/lead-row-top-margin.test.mjs(+5). Suite: 2497.
[1.187.0] — 2026-08-14
Corretto (scanner) — l’impostazione skip_tiers funziona di nuovo: le offerte che hai chiesto di saltare per seniority vengono scartate.
Corretto
- Un elenco
skip_tiers:inportals.yml(es.skip_tiers: [intern, entry]) è ora rispettato dalla scansione. Il titolo di ogni offerta è classificato in un livello (intern / entry / mid / senior) e scartato se il livello è nel tuo elenco. Prima la scansione applicava i filtri titolo / località / contenuto / affidabilità ma senza filtro di livello, quindiskip_tiersveniva ignorato in silenzio. I titoli senza parola di livello ricadono su mid (quindiskip_tiers: [mid]scarta anche la maggior parte delle offerte comuni), e il classificatore legge la parola di livello PIÙ A SINISTRA.
Note
- Nuovo modulo puro
server/lib/classify-tier.mjs(classifyTier+buildTierFilter), collegato alle catene di filtri degli scanner EN e RU.tests/classify-tier.test.mjs(+7). Suite: 2492.
[1.186.0] — 2026-08-14
Aggiunto (CV Studio) — un pannello “Divario di competenze”: quali competenze richieste da un’offerta il tuo CV nomina, implica o gli mancano.
Aggiunto
- Un nuovo pannello Divario di competenze in CV Studio. Scegli una descrizione di lavoro salvata e classifica ogni competenza richiesta in nominata nel tuo CV, implicita nel tuo CV o mancante — confronto di parole senza LLM, nulla viene scritto. Una nota di bassa affidabilità appare quando l’offerta non aveva una sezione requisiti chiara.
Note
- Nuovo
GET /api/jds/:name/skill-gap(il nome dell’offerta è sanificato e confermato sottojds/prima di diventare un argomento; fallback morbido a{available:false}senza lo script). +13 chiavi i18n ×17. Test:tests/jds-skill-gap-route.test.mjs(+4, incl. rifiuto del path-traversal). Suite: 2485.
[1.185.0] — 2026-08-14
Aggiunto (statistiche) — una scheda “Imbuto e velocità”: come il tuo imbuto si confronta col mercato e quanto velocemente avanzi tra le fasi.
Aggiunto
- Una nuova scheda Imbuto e velocità in Statistiche mostra i tuoi tassi di risposta e colloquio accanto agli intervalli di riferimento di mercato (con le avvertenze su campione piccolo e bias di selezione), una lista d’attesa delle candidature in corso oltre la finestra tipica di prima risposta, e i giorni mediani per fase (Candidato → Risposto → Colloquio → Offerta) — le righe lente sono censurate a destra per non distorcere le mediane. Sola lettura e zero token; legge solo il tuo tracker.
Note
- Nuovo
GET /api/stats/funnel(fallback morbido a{available:false}senza lo script). +18 chiavi i18n ×17. Test:tests/stats-funnel-route.test.mjs(+2). Suite: 2481.
[1.184.0] — 2026-08-14
Corretto (UI) — le tessere di azione rapida della Dashboard ora si allineano in una griglia uniforme.
Corretto
- Nella Dashboard (Centro di comando) un gruppo di 3 tessere veniva reso più largo di uno da 4, lasciando le sezioni con un bordo destro irregolare. Ogni gruppo usa ora colonne di uguale larghezza (4 su schermo ampio, fino a 3 / 2 / 1 al restringersi della finestra), così tutte le tessere hanno la stessa dimensione e i loro bordi destri si allineano.
Note
- Solo CSS (
.qa-grid:repeat(N, minmax(0,1fr))fisso invece diauto-fill). Protetto datests/dashboard-grid-align.test.mjs(+2). Suite: 2479.
[1.183.0] — 2026-08-14
Aggiunto (scanner) — rilevamento duplicati più intelligente: la stessa offerta ripubblicata con un link di tracciamento non compare più due volte.
Aggiunto
- Lo scanner ora riconosce un’offerta tramite una chiave URL canonica, così la stessa offerta ripubblicata con un parametro di tracciamento (
?utm_…,gclid, …), suhttpvshttps, o con una barra finale /#frammentoè trattata come l’unica offerta che è — nessuna riga duplicata nei risultati o nel pipeline e nessuna valutazione sprecata su un’offerta già vista. Offerte davvero diverse (un id funzionale mantenuto comegh_jid) contano ancora separatamente.
Note
- Nuovo
server/lib/url-key.mjs, collegato al dedup di entrambi gli scanner e allo scrittore del pipeline. Normalizza volutamente poco — non fonde mai due offerte distinte. Test:tests/url-key.test.mjs(+5),tests/parsers.test.mjs(+1). Suite: 2477 (+6).
[1.182.0] — 2026-08-14
Corretto (scanner) — le fasce salariali ora si leggono uguali in ogni lingua.
Corretto
- Gli importi salariali nelle righe di scan e tracker usano i simboli neutri ≥ e ≤ (es.
≥ 120000 EUR,≤ 90000) invece delle parole inglesi “from” / “up to”, che trapelavano non tradotte nelle interfacce non inglesi. Vale per ogni bacheca con fascia a un solo lato (Getro, Remotli, Manfred, Agentic Jobs, JustJoin, Jobicy); le fasce a due lati (100000–150000 USD) erano già neutre.
Note
- Solo visualizzazione — il filtro salari del client analizza i numeri a prescindere dal prefisso, quindi il filtraggio non cambia. Suite: 2471.
[1.181.0] — 2026-08-14
Aggiunto (scanner) — le bacheche Getro ora mostrano stipendio, tutte le località e posizioni da remoto.
Aggiunto
- Lo scanner Getro (bacheche delle reti di talenti dei fondi) ora mostra uno stipendio su ogni posizione (fascia annuale + valuta), elenca tutte le località invece della sola prima e contrassegna le posizioni da remoto. Una posizione Getro nel tuo scan e tracker porta ora lo stesso dettaglio di stipendio + località delle altre bacheche.
Note
- Solo scanner; nessuna nuova dipendenza, nessun cambiamento a route / CSP / SSRF. Test:
tests/sources-getro.test.mjs(+5). Suite: 2470 (+5).
[1.180.0] — 2026-08-14
Corretto (MEDIO, report) — l’elenco #/reports è ora una tabella e viene recuperato un punteggio reale che un segnaposto di Machine Summary nascondeva.
Corretto
- L’elenco
#/reportsè una tabella (Report · Data · Legittimità · Punteggio), non una griglia di 4 schede. Un lungo chip “Punteggio non rilevato” comprimeva la colonna del titolo quasi a zero, e l’overflow-wrap: anywheredel titolo della scheda spezzava il nome del report un carattere alla volta. Ora ogni campo ha la propria colonna, la cella del nome va a capo per parole e la tabella scorre orizzontalmente su viewport stretti (nuovo contenitore.reports-scroll). Nuova chiave i18nrep.colReport×17. - Un punteggio reale nel corpo (
**Итоговый балл:** 1.8 / 5) non è più nascosto da un segnaposto di Machine Summary (score: —). Quando il blocco## Machine Summaryportava un punteggio non numerico o fuori intervallo, occupava lo slot del punteggio analizzato e bloccava il fallback forma-valore in grassetto, così il report mostrava “Punteggio non rilevato” nonostante unX / 5reale nel corpo.parseReportHeaderora recupera la forma-valore dal corpo quando non sopravvive alcun numero utilizzabile (passo 4.5).
Note
- Solo client + parser; nessun cambiamento a route / CSP / SSRF / scrittura sul padre. Test:
tests/reports-table.test.mjs(+5),tests/report-header-locale.test.mjs(+2). Suite: 2465 (+7).
[1.179.0] — 2026-08-13
Modificato (LOW, scanner) — 20 decoder di entità HTML duplicati consolidati sul modulo condiviso (follow-up di parità, chiude la worklist).
Modificato
- 20 sorgenti di scansione avevano ciascuna il proprio
decodeEntities/decodeXmlEntities(+ un helperfromCodePoint) — copie che erano derivate (tre potevano sollevare unRangeError, corretto in v1.172.0; altre ammettevano NUL/C0 o interpretavano malea2;). Ora passano tutte per l’unicoserver/lib/html-entities.mjs(decoder sicuro secondo Char di XML 1.0), rimuovendo ~237 righe di duplicazione. Le 8 sorgenti in stile RSS hanno guadagnato la decodifica di (prima gestivano solo 5 entità); la doppia decodifica voluta di cryptocurrencyjobs è preservata tramite un alias.hhmantiene il proprio decoder (gestisce—/–, fuori dalle 6 condivise). Un nuovo test di guardia fallisce se una sorgente ricrea un decoder locale.
Note
- Refactoring che preserva il comportamento; nessun cambiamento a route / CSP / SSRF / scrittura sul padre. Test:
tests/decoder-consolidation.test.mjs(+2). Suite: 2458 (+2).
[1.178.0] — 2026-08-13
Corretto (LOW, parità col padre) — due costanti obsolete allineate al padre (PARENT-SYNC GAP #4 + #5).
Corretto
- User-Agent del browser (GAP #4) —
BROWSER_LIKE_USER_AGENT(inviato da workable/workday/oraclecloud/a16z/eightfold per superare i gate WAF/bot) portato da Chrome 131 a 151, allineando iluser-agent.mjsdel padre; una versione vecchia è più spesso bloccata. Protetto da un testChrome major ≥ 151. - FALLBACK degli stati del tracker (GAP #5) — il
FALLBACKdi ultima istanza instates.mjs(usato solo quando iltemplates/states.ymllive è illeggibile — clone nuovo / root isolata in CI) ha guadagnato gli alias di stato turchi del padre (#2615): değerlendirildi, başvuruldu, yanıt verildi, mülakat, teklif, reddedildi, iptal edildi, uygun değil, kabul edildi/işe alındı. In produzione il file live li forniva già.
Note
- Solo due costanti; nessun cambiamento a route / CSP / SSRF / scrittura sul padre. Test:
tests/http-json.test.mjs(+1) +tests/states.test.mjs(+1). Suite: 2456 (+2).
[1.177.0] — 2026-08-13
Corretto (MEDIUM, scanner) — csod (Cornerstone) restituiva 0 offerte sui tenant che proteggono l’API di ricerca con cookie di sessione (parent #2769, PARENT-SYNC GAP #1).
Corretto
- Alcuni tenant Cornerstone impostano cookie di sessione sulla home del sito carriere e rispondono
401 CSOD Unauthorizedall’API di ricerca se quei cookie non tornano insieme al token bearer anonimo.sources/csod.mjsora legge il bootstrap tramite un nuovo helperfetchResponse, costruisce un headerCookiedai suoi valoriSet-Cookie(cookieHeaderFrom— solo nome=valore, semantica jar) e lo ripete sul POST di ricerca. Solo stessa origine (host fissato +redirect:'error'), quindi i cookie di sessione non raggiungono mai terze parti; un tenant senza cookie si comporta come prima.
Note
- Nuovo
server/lib/http-json.mjs::fetchResponse(additivo; sorgenti esistenti intatte). Nessun cambiamento a route / CSP / SSRF / scrittura sul padre. Test:tests/sources-parity-v1118a.test.mjs(+1). Suite: 2454 (+1).
[1.176.0] — 2026-08-13
Corretto (MEDIUM, report) — un punteggio sotto un’etichetta in grassetto che la tabella RU non elenca mostrava ancora “Score not detected” (FIND-5).
Corretto
- Due report RU scrivevano il punteggio come
**Итоговый балл:** 1.8 / 5/**Скор:** 1.8 / 5— etichette in grassetto cheREPORT_LABELS.runon enumera (conosce solo “Оценка”/“Балл”), quindi il punteggio restava non analizzato. Invece di ampliare l’elenco di sinonimi,parseReportHeaderora ripiega sulla forma del valore: una frazione sulla rubrica /5 sotto QUALSIASI etichetta in grassetto. È indipendente dalla lingua, immune a un’intestazione (niente**, niente valore/5) e rifiuta una data come5/5/2026(lookahead negativo sul denominatore).
Note
- Solo parser del server; nessun cambiamento a route / CSP / SSRF / scrittura sul padre. Test:
tests/report-header-locale.test.mjs(+2). Suite: 2453 (+2).
[1.175.0] — 2026-08-13
Corretto (LOW, irrobustimento) — una guardia di regressione per la descrizione SEO di FIND-3 + uno strip di legittimità sicuro ai valori nulli (follow-up dell’AI-review).
Corretto
- Guardia di parità della descrizione SEO — la correzione di v1.174.0 che ha sostituito un “~55” fisso nel
meta.descdi ogni lingua con un placeholder{adapters}derivato dal registro non aveva test, quindi poteva regredire in silenzio alla prossima modifica di una locale. Il nuovotests/site-meta-desc-parity.test.mjs(isolato in CI) fallisce se uno dei 17site/src/i18n/*.jsonperde il placeholder o rifissa un conteggio, o seLanding.astrosmette di interpolarlo nelle tre metadescrizioni. - Strip di legittimità sicuro ai nulli —
stripEmphasisrestituisce''per un input nullish invece della stringa “undefined” (i campi sono inizializzati a stringa, quindi è difesa in profondità).
Note
- Test + una guardia di una riga nel parser; nessun cambiamento a route / CSP / SSRF / scrittura sul padre. Test:
tests/site-meta-desc-parity.test.mjs(+3). Suite: 2451 (+3).
[1.174.0] — 2026-08-13
Corretto (HIGH, report) — i report localizzati mostravano “Score not detected”; la descrizione SEO era obsoleta.
Corretto
- Parsing del punteggio (FIND-1) — un report non inglese il cui H1 contiene la parola dell’etichetta del punteggio (
# Оценка вакансии: <titolo>) non scambia più quel titolo per il punteggio.parseReportHeaderora si ancora all’etichetta in grassetto localizzata (**Оценка:** 1.5 / 5), salta le righe di intestazione e richiede l’etichetta adiacente ai due punti — così i report RU che mostravano “Score not detected” espongono il punteggio reale. - Chip di legittimità (FIND-2) — l’enfasi Markdown viene rimossa dal valore, così il chip mostra “High Confidence” invece di “** High Confidence”.
- Overflow del punteggio — una riga di punteggio con testo di stato in coda (“1.8, Status: Evaluated, …”) viene compattata al solo punteggio;
.score-pillottiene un limite no-wrap/overflow e la colonna del titolo può restringersi, così un chip colorato non esce mai dal bordo della card. - Descrizione SEO (FIND-3) — le descrizioni meta / OG / Twitter di cvstart.org (tutte le 17 lingue) fissavano “Scan ~55 job boards” mentre il corpo contava il registro reale (“~75”). Ora la descrizione interpola il conteggio derivato dal registro, così non deriva più.
Note
- Parser del server + render/CSS del client + i18n del sito; nessun cambiamento a route / CSP / SSRF / scrittura sul padre. Test:
tests/report-header-locale.test.mjs(+4). Suite: 2448 (+4).
[1.173.0] — 2026-08-13
Aggiunto (LOW, configurazione) — Hermes entra nell’elenco delle CLI di IA rilevate (parità con career-ops).
Aggiunto
- La scheda
#/config→ “Strumenti CLI di IA” ora rileva Hermes (Nous Research), il runtime di agente appena supportato dal progetto padre (binariohermes). L’elenco fisso inserver/lib/routes/cli-detect.mjscresce da 10 a 11 strumenti; il rilevamento resta una scansione di PATH in sola lettura (nessun binario viene mai eseguito).
Note
- Nessun cambiamento a i18n / route / CSP / SSRF / scrittura sul padre; l’elenco è fisso, mai un input. Suite: 2444 (il canary di cli-detect aggiornato da 10 a 11).
[1.172.0] — 2026-08-13
Corretto (MEDIUM, scanner) — un’entità HTML malformata poteva mandare in crash una sorgente di scansione (parità con career-ops #2150).
Corretto
- Le sorgenti
oraclecloud,gemedassaultdecodificavano le entità HTML numeriche con un debole controlloNumber.isFiniteprima diString.fromCodePoint— un riferimento superiore a0x10FFFF(es.�da un feed malformato o ostile) sollevava unRangeErrornon catturato e interrompeva l’intero parsing di quella sorgente. Un modulo condivisoserver/lib/html-entities.mjs(che rispecchia il_html-entities.mjsdel progetto padre) ora limita i riferimenti numerici all’insieme Char di XML 1.0 §2.2, cosìString.fromCodePointnon può mai sollevare eccezioni, e distingue esadecimale e decimale separatamente affinchéa2;non venga più interpretato male. Le tre sorgenti lo importano.
Note
- Nessun cambiamento per i feed validi; nessun cambiamento a JS / i18n / route / CSP / SSRF / scrittura sul padre. Il consolidamento delle ~20 copie del decoder rimaste nelle sorgenti è tracciato in
qa/PARENT-SYNC-WORKLIST-v1.26.0.md. - Test:
tests/html-entities.test.mjs(+7). Suite: 2444 (+7).
[1.171.0] — 2026-08-13
Modificato (BASSA, design-system) — token di scala tipografica + livelli z-index (D-4, primo passo). Dimensioni e impilamento erano letterali per componente.
Modificato
- Livelli z-index — introdotti token
--z-*(--z-topbar…--z-skiplink) e migrato ogni letterale z-index. Valori preservati, impilamento identico; un nuovo canarino vieta nuovi numeri magici. - Scala tipografica — rampa
--font-size-*(xs 11…2xl 28, base = Inter 15px); migrate le dimensioni centrali (nessun cambiamento visivo). I valori fuori rampa migrano in modo incrementale (docs/UX-ROADMAP.md).
Note
- Solo token CSS; nessuna modifica a comportamento/JS/i18n/route/CSP/SSRF/scrittura. Nessun cambiamento di pixel.
tests/design-tokens-scale.test.mjs(+3). Suite: 2437 (+3).
[1.170.0] — 2026-08-13
Aggiunto (BASSA) — suggerimenti ETA onesti sulle lunghe generazioni IA (P4-ETA). Le generazioni pesanti (piano di carriera ~40 s, orientamento / mercato / networking ~30 s, two-pager ~20 s) mostravano solo “Generazione…” senza percezione della durata.
Aggiunto
- Accanto a ogni pulsante di generazione lunga compare ora un suggerimento attenuato
⏱ ~Ns(come l’ETA di#/auto). Stile.eta-hintcondiviso + due chiavi generiche (common.eta~{n}s,common.etaTitle).
Note
- Solo client; nessuna modifica a route/CSP/SSRF/scrittura. +2 chiavi i18n ×17 (snapshot 1219 → 1221).
tests/generation-eta-hint.test.mjs(+2). Suite: 2434 (+2).
[1.169.0] — 2026-08-13
Aggiunto (BASSA) — anteprima PDF inline (D-5). GET /api/output/pdfs/:name forzava Content-Disposition: attachment, quindi anche il pulsante “Apri” su #/cv scaricava invece di mostrare.
Aggiunto
?inline=1serve lo STESSO file sanificato conContent-Disposition: inline, per un’👁 Anteprima in una nuova scheda; il default resta il download. Nessuna nuova route; stesse protezioni sul nome.- Il primo pulsante della lista PDF su
#/cvè ora 👁 Anteprima accanto a ⬇ Scarica.cv.openPdf“Apri” → “Anteprima” ×17.
Note
- Nessuna modifica CSP/SSRF — stesso
sanitizePathName. Una chiave i18n riformulata ×17 (snapshot 1219).tests/output-pdfs.test.mjs(+3). Suite: 2432 (+3).
[1.168.0] — 2026-08-13
Corretto (BASSA, a11y) — le righe delle checkbox rispettano ora il minimo 24×24 di WCAG 2.5.8 (D-2). Le etichette di checkbox/radio su #/scan, #/config, #/evaluate e #/cv-studio stavano in una banda di ~22 px.
Corretto
- Una regola circoscritta
label:has(> input[type="checkbox"/"radio"]) { min-height: 24px }garantisce una banda ≥24 px. Solomin-height— le etichette sono già flex, nulla si sposta;.apply-checklist(32 px) era già conforme.
Note
- Solo CSS; nessuna modifica a comportamento/JS/i18n/route/CSP/SSRF/scrittura.
tests/checkbox-target-size.test.mjs(+1). Suite: 2429 (+1).
[1.167.0] — 2026-08-13
Corretto (BASSA, design-system) — le superfici rialzate ora si distinguono dai filetti (D-3). I token --panel-2 / --surface-elev1 si risolvevano in --slate, lo stesso valore dei filetti --line / --border, senza separazione visiva.
Corretto
- Un token dedicato e consapevole del tema
--elev(#eef1f6chiaro /#1e232escuro, distinto da--slatein entrambi i temi) sostiene ora le superfici rialzate; i filetti restano su--slate. Gli altri rilievi (D-2, D-4, D-5, P4-ETA) sono backlog indocs/UX-ROADMAP.md.
Note
- Solo token CSS; nessuna modifica a comportamento/JS/i18n/route/CSP/SSRF/scrittura.
tests/elevation-token.test.mjs(+2). Suite: 2428 (+2).
[1.166.0] — 2026-08-13
Corretto (BASSA) — la terminologia della rubrica ora rispecchia i docs canonici. career-ops.org/docs descrive “cinque dimensioni più un punteggio globale olistico”, ma il web-ui, cvstart.org e il wiki dicevano “rubrica a sei dimensioni” (5 + 1 = 6, ma il lessico divergeva).
Corretto
- Adottata la formulazione dei docs — “cinque dimensioni più un punteggio globale olistico” — in modo coerente in README ×17, nel sito cvstart.org ×17, nella guida ×17, in
docs/career-ops-canonical.mde nel wiki (Home ×17 + Features).
Note
- Solo docs/marketing; nessuna modifica a codice/chiave i18n/route/CSP/SSRF/scrittura.
tests/rubric-terminology.test.mjs(+2). Suite: 2426 (+2).
[1.165.0] — 2026-08-13
Corretto (BASSA) — il termine “Two-pager” è ora coerente all’interno di ogni lingua. In arabo la barra laterale mostrava il latino “Two-pager” mentre l’<h1> era completamente localizzato — l’unica stringa latina in una navigazione RTL altrimenti speculare.
Corretto
- Decisione applicata: per lingua,
nav.twoPageretwoPager.titleconcordano sul termine (entrambi in latino o entrambi localizzati). Solo l’arabo era diviso; la sua etichetta di navigazione è ora localizzata (“الصفحتان”). Un nuovo canarino fallisce se una lingua li separa di nuovo.
Note
- Solo testo; nessuna modifica a route/CSP/SSRF/scrittura. Un valore i18n modificato (ar); nessuna nuova chiave (snapshot 1219).
tests/two-pager-term-consistency.test.mjs(+2). Suite: 2424 (+2).
[1.164.0] — 2026-08-13
Corretto (BASSA) — il placeholder della ricerca non trabocca più in nessuna lingua. “Find a company, role or URL…” veniva troncato (nowrap) quando la barra si restringeva; la metà “…or URL” non era mai visibile.
Corretto
top.search(×17) è ora il breve “Cerca o incolla una URL” (≤24 caratteri in ogni lingua), sta anche in una barra stretta e mantiene il riferimento all’URL. Il fallback inindex.htmlcorrisponde; l’aria-labelconserva il dettaglio completo.
Note
- Solo testo; nessuna modifica a route/CSP/SSRF/scrittura. Una chiave i18n riformulata ×17 (nessuna nuova; snapshot 1219).
tests/search-placeholder-fit.test.mjs(+2). Suite: 2422 (+2).
[1.163.0] — 2026-08-13
Corretto (BASSA) — l’assistente in-app “Chiedi ai docs” ora copre l’esportazione di un report in PDF. Rispondeva che la guida non lo copriva, benché #/reports/:slug abbia un pulsante 📄 Generate PDF funzionante.
Corretto
- Aggiunto un H3 “Esportare un report in PDF” sotto §10 Report in tutti i 17 bundle di aiuto (dov’è il pulsante, il file finisce in
output/*.pdf, richiede Playwright, rivedere prima di inviare). Il recupero dell’assistente ora mostra la sezione Report.
Note
- Solo docs/aiuto; nessuna modifica a codice/route/CSP/SSRF/scrittura. Soglia aiuto 112 → 113 H3 (31 H2 invariato).
tests/help-reports-pdf-section.test.mjs(+2). Suite: 2420 (+2).
[1.162.0] — 2026-08-13
Corretto (MEDIA) — il “?” di aiuto è ora un bersaglio di ≥24×24 (WCAG 2.5.8). .help-hint misurava 18×18 px con padding:0, sotto il minimo, su ogni intestazione.
Corretto
- Il box
.help-hintè ora 24×24 (il bersaglio misurabile) mentre l’anello visibile resta a 18px — disegnato da un::beforecentrato, quindi il glifo e la linea di base dell’<h1>sono invariati. Gli stati hover/attivo/focus seguono l’anello; margine 6→3px per mantenere lo spazio.
Note
- Solo CSS; nessuna modifica a JS/i18n/route/CSP/SSRF/scrittura.
tests/help-hint-target-size.test.mjs(+2). Suite: 2418 (+2).
[1.161.0] — 2026-08-13
Corretto (MEDIA) — #/reports mostra un chip “Punteggio non rilevato” invece di spazio vuoto. Dopo il parser multilingua di v1.159.0, un report senza punteggio analizzabile mostrava un’area vuota, indistinguibile da un errore.
Corretto
- La cella del punteggio ora si ramifica: con punteggio → pillola di tono; senza → chip
.score-mutedattenuato (“Punteggio non rilevato”, ×17) con tooltip “Apri il report…”. La card resta unrole="link"operabile da tastiera e la data si mostra. - Riusa il token neutro esistente; nessun colore nuovo.
Note
- Solo client; nessuna modifica a route/CSP/SSRF/scrittura. +2 chiavi i18n ×17 (snapshot 1217 → 1219). Suite: 2416 (+3).
[1.160.0] — 2026-08-13
Corretto (ALTA) — il testo sui provider non contraddice più la promessa dei 7 provider. #/config diceva che la valutazione live “usa la tua chiave Anthropic o Gemini” e che quella OpenAI “non è usata dal web UI”; la dashboard diceva “Score Anthropic-first” — falso dalla cascata a 7 provider (v1.157.0).
Corretto
config.providerModelNote(×17): ora dice che la ⚡ valutazione live gira headless con una qualsiasi delle tue sette chiavi (Anthropic · Gemini · OpenAI · Qwen · OpenRouter · GitHub Models · Hermes), ordinate automaticamente con fallback. Rimossa la frase falsa su OpenAI.dash.quick.evaluateSub(×17): neutro (“Punteggio di idoneità 0–5”).Keys: N / 5→N / 7.
Note
- Solo testo; nessuna modifica a route/CSP/SSRF/scrittura. Nessuna nuova chiave i18n (snapshot 1217). Suite: 2413 (+3).
[1.159.0] — 2026-08-13
Corretto (ALTA) — i metadati del report non dipendono più dalla lingua. I report generati in una lingua diversa dall’inglese mostravano una striscia di metadati vuota su #/reports, perché parseReportHeader riconosceva solo le etichette in grassetto in inglese.
Corretto
parseReportHeaderora analizza il blocco YAML invariante## Machine Summary(score:/legitimacy:/date:— la stessa fonte cheauto-pipelinegià legge): etichette inglesi → blocco Machine Summary → etichette localizzate (REPORT_LABELS, 17 lingue). I report in inglese restano identici byte per byte.- Analisi numerica tollerante (
1.5/5,1,5/5,1.5 из 5,4.5 out of 5); la data ripiega sul mtime del file quando manca.
Note
- Solo lettura/analisi; nessuna modifica a route, CSP, SSRF o scrittura sul padre. Nessuna nuova chiave i18n. Suite: 2410 (+8).
[1.158.0] — 2026-08-12
Corretto — due bug cosmetici di visualizzazione (un «?» che trapelava nel titolo della scheda e un conteggio dei provider errato nella landing). Solo visualizzazione; nessuna modifica a comportamento, sicurezza o flusso dei dati.
Corretto
- Il «?» di HelpHint non trapela più in
document.title. Il router derivava il titolo della scheda dalh1.textContentgrezzo, mostrando «Vacancy search?» invece di «Vacancy search».router.js::focusNewViewora clona l’intestazione, rimuove.help-hinte poi legge il testo; il «?» visibile resta intatto. - cvstart.org mostrava «17 AI providers» invece di «7». L’helper
sub()inFeatures.astroriscriveva ogni{n}con il numero di lingue (17) prima della sostituzione per scheda; ora{n}è risolto per scheda (provider → 7, lingue → 17).
Note
- Nessuna modifica a server, route, CSP, SSRF o chiavi i18n; forma di
facts.jsoninvariata. Suite: 2402 test (+1).
[1.157.0] — 2026-08-12
Corretto — le valutazioni live ora girano con QUALSIASI provider configurato, non solo Anthropic/Gemini. Un utente con solo OPENROUTER_API_KEY veniva forzato erroneamente in modalità manuale.
Corretto
- Causa radice: un pin
LLM_PROVIDERsenza chiave (es.LLM_PROVIDER=claudedainit) finiva in un vicolo cieco; ora ripiega sull’ordine auto tra i provider configurati (inselectActiveProvider+ entrambe le cascate di dispatch). - Il gating lato client (
#/deep+ viste mode-page) usa orawindow.ProviderStatus(/api/status/providers, tutti e 7) invece del probe obsoleto Anthropic/Gemini; testi riscritti (deep/eval × 17) + badge «Valutazioni live» della dashboard +config.llmProviderHint.
Note
- Nessun cambiamento di sicurezza. Suite: 2401 test (+5).
[1.156.0] — 2026-08-12
Refactor — dividere scan.js sotto il limite di dimensione (P-16) + un fix CodeQL. scan.js era di 906 righe; estratte due factory che preservano il comportamento → 648. Completa la coppia di divisioni delle view P-15/P-16.
Modificato
- Nuovi
scan/runner.js(motore di esecuzione dello scan) escan/filters.js(macchina a stati dei filtri) tramite bagctx/refs;scan.jscollega entrambi.
Corretto
- CodeQL
js/useless-assignment-to-local(#428) inconfig/tab-controller.js:let n = i;→let n;.
Note
- Refactor puro, nessun cambio di comportamento; 4 test che leggono il sorgente ripuntati. Entrambe le view grandi ora sotto 800 (P-15/P-16 fatto). Suite: 2396 test.
[1.155.0] — 2026-08-12
Refactor — dividere config.js sotto il limite di dimensione (P-15). config.js era di 1030 righe (oltre il limite di 800); estratti due moduli che preservano il comportamento, portandolo a 783.
Modificato
- Nuovi
config/field-specs.js(dati dei campi + liste di modelli) econfig/tab-controller.js(factory della barra delle schede);config.jsli referenzia, la logica di render è invariata.
Note
- Refactor puro, nessun cambio di comportamento; 6 test che leggono il sorgente sono stati ripuntati.
scan.js(906) resta com’è (già parzialmente diviso; nucleo troppo accoppiato per una divisione meccanica pulita). Suite: 2396 test.
[1.154.0] — 2026-08-12
Nuova guida — “Esegui l’intero stack nel cloud.” career-ops non ha una propria storia cloud/server, quindi ne aggiungiamo una: una ricetta passo passo per mettere la pipeline padre career-ops, questo visualizzatore career-ops-ui e il motore IA (un abbonamento Claude via Claude Code, un Hermes locale, o chiavi API) su un piccolo server sempre attivo. Arriva come Aiuto §31 in 17 lingue, una sezione del README e una pagina wiki.
Aggiunto
- Aiuto §31 “Esegui l’intero stack nel cloud” (× 17) — le tre parti, provisioning + installazione, scelta del motore, esposizione sicura (reverse proxy HTTPS + auth + le invarianti CSP/SSRF/XSS/nessun-segreto). Il bundle di aiuto cresce a 31 H2 / 112 H3.
- README — una sezione “Esegui l’intero stack nel cloud” (× 17) + una pagina Cloud-Deployment nel wiki.
Note
- Solo docs — nessuna rotta, server o modifica client; nessuna nuova chiave i18n. I 4 test di aiuto passano al contratto 31 H2 / 112 H3. Suite: 2396 test (invariata).
[1.153.0] — 2026-08-12
Scanner Jobvite migrato al feed XML pubblico (sync con il parent). Il parent ha ritirato l’API JSON di Jobvite (ora restituisce zero offerte); il source di web-ui usava lo stesso endpoint morto, quindi ogni azienda Jobvite tracciata scansionava vuota in silenzio. Porta il fix del parent (#2623): il source ora legge il feed XML pubblico per-tenant, con chiave companyEId.
Corretto
- Il source chiamava l’API JSON ritirata e restituiva zero offerte; ora chiama
https://app.jobvite.com/CompanyJobs/Xml.aspx?c={companyEId}e fa il parse dell’XML<result><job>…(CDATA + entità,detail-urlprima diapply-url).
Modificato
- Risoluzione di
companyEId: (1)company_eid:sul portale, (2) ilc=di unapi:esplicito, (3) discovery dalla pagina del board.fetchText(http-json.mjs) allega.location/.retryAfterall’errore non-ok (sola lettura, retrocompatibile).
Note
- Sicurezza — due host (
jobs.jobvite.com,app.jobvite.com) fissati daassertJobviteUrl: solo https, allowlist stretta, nessun redirect seguito. IlcompanyEIdè solo un valore?c=; conteggio dei source invariato. - Suite: 2396 test (+4).
[1.152.0] — 2026-08-12
Provider Hermes — cablaggio completato + allineamento docs. Una revisione del codice dell’integrazione Hermes di v1.151.0 ha trovato due lacune reali e quattro punti di completezza; tutti corretti qui, e il roster dei provider LLM dell’intera app è portato ai sette completi su tutte le superfici di docs e le 17 lingue.
Corretto
#/confignon poteva forzare Hermes — il menuLLM_PROVIDERelencava solo sei provider, quindi si poteva impostareHERMES_API_KEYma non forzare Hermes dalla UI. Orahermesè l’8ª opzione, e un nuovo test di parità impedisce al menu di divergere di nuovo daLLM_PROVIDERS.- Le chiavi locali corte venivano rifiutate in silenzio — la soglia di 20 caratteri di
isUsableKeyera tarata per chiavi cloud;hasHermesKeyusa ora una soglia allentata di 8 (l’esempio dei docs Hermes è di 19 caratteri).
Modificato
- Il roster dei provider è stato normalizzato ai sette completi in README (× 17), guida in-app (× 17), dict
config.llmProviderHint(× 17) edocs/sdd;hermesChatUrlcompleta un host senza percorso; il testo di fallback manuale cita Hermes.
Note
- Sicurezza invariata — nessuna nuova rotta né modifica SSRF/CSP; health/doctor guadagna una riga
HERMES_API_KEY. - Suite: 2392 test (+2).
[1.151.0] — 2026-08-12
Hermes è ora un provider LLM collegato (Phase 5) — lo spike di analisi della Phase 5 ha confermato che il Hermes di Nous Research include un API Server compatibile con OpenAI (hermes gateway → POST /v1/chat/completions), quindi career-ops-ui ora esegue valutazioni live tramite un Hermes locale esattamente come OpenAI/Qwen. Imposta HERMES_API_KEY in Impostazioni app ed entra nell’ordine auto (ultimo). Chiude l’ultimo punto aperto della roadmap — Phase 5, Shape A.
Aggiunto
- Provider LLM Hermes (Shape A) —
runHermessul client condivisorunOpenAICompatible(server/lib/openai.mjs), in entrambe le cascate (llm-dispatch.mjs+routes/llm.mjs), in coda all’ordine auto + il pinLLM_PROVIDER=hermes,/api/status/providersellm-pricing.mjs. Raggiunge una base URL locale configurabile (defaulthttp://127.0.0.1:8642/v1) con auth Bearer — è un endpoint di provider CONFIGURATO (come OpenRouter/Qwen), non un URL di offerta dell’utente, quindi non tocca il guard SSRF. - Campi
#/config—HERMES_API_KEY(segreto) +HERMES_BASE_URL+HERMES_MODEL(defaulthermes-agent), con 6 nuove chiavi i18n × 17 lingue (snapshot 1208 → 1214).
Modificato
- Lo spike di analisi è risolto:
docs/integrations/HERMES.md, l’aiuto in-app §30 (× 17), il teaser del README (× 14), la skillhermes-bridgee la roadmap passano da “pianificato / non ancora collegato” a collegato (Shape A). Shape B (un relay su misura del runtime di agente) non è stato necessario.
Note
- Sicurezza: il fetch del provider è un endpoint configurato, della stessa categoria degli altri provider compatibili con OpenAI — nessuna nuova superficie SSRF, nessuna modifica a CSP/sanitizer.
HERMES_API_KEYè unaSECRET_KEY(mai mostrata). - Test (isolati in CI, trasporto simulato):
tests/hermes-provider.test.mjs(+5); il canarino “nessun ramo Hermes” di v1.146.0 è invertito per affermare che C’È; i test di superficie dei provider aggiornati all’ordine a 7 provider. - Suite: 2390 test (+5).
[1.150.0] — 2026-08-12
Stati vuoti coerenti (rifinitura Phase 4) — ogni pannello “ancora niente qui” ora si renderizza tramite l’unico stile condiviso .empty, invece che alcune viste ridichiarino l’aspetto inline con un 40px magico. Piccola correzione di coerenza visiva; gli stati vuoti di #/activity, #/cv-studio, #/stats e #/usage ora combaciano con tutti gli altri (padding di 48px tokenizzato + bordo tratteggiato).
Modificato
#/activity,#/cv-studio,#/stats,#/usagehanno rimosso il lorostyle: { padding: '40px', textAlign: 'center', color: 'var(--foggy)' }inline sui pannelli vuoti — tutte e tre le proprietà sono già fornite dalla classe condivisa.empty(--space-7= 48px, centrato, attenuato, bordo tratteggiato). Così questi quattro si renderizzano identici agli altri ~25 pannelli.empty.- Gli override legittimi per vista (
#/dashboardwidth:100%,#/pipelineborder:none) non sono toccati — rimosse solo le ridichiarazioni puramente ridondanti.
Note
- Solo pulizia dell’uso di CSS lato client — nessuna modifica a rotta, server, chiave i18n o regole CSS (la classe
.emptyè invariata); snapshot del dizionario 1208. Verificato nel browser (il pannello vuoto di#/usagecalcola 48px di padding + bordo tratteggiato, 0 errori di console). - Il nuovo canarino
tests/empty-state-consistency.test.mjsmantiene.emptycome unica fonte di verità. La Phase 5 (provider Hermes) resta bloccata. - Suite: 2385 test (+2:
tests/empty-state-consistency.test.mjs).
[1.149.0] — 2026-08-12
Portali spostati nelle Impostazioni (Phase 4) — #/portals ora si trova nel gruppo di navigazione Setup, accanto a Impostazioni app, invece che sotto Sourcing. Dalla v1.144.0 è una superficie di configurazione (abilita/disabilita le aziende monitorate + una sonda di salute dell’ATS), non un’azione di sourcing — quindi è lì che deve stare. Solo modifica di navigazione; la pagina e la sua rotta non cambiano.
Modificato
- Elemento di navigazione
#/portals→ gruppo Setup (inpublic/index.html), collocato subito dopo Impostazioni app. Rimosso dal gruppo Sourcing (che conserva Scan / Pipeline / Auto-pipeline / Aziende finanziate). La rotta#/portals, la vista e l’etichettanav.portalsnon cambiano — si è spostata solo la posizione nella barra laterale.
Note
- Solo markup di navigazione — nessuna modifica a rotta, vista, chiave i18n o server. Verificato nel browser (0 errori di console); protetto da
tests/portals-nav-placement.test.mjs. - Suite: 2383 test (+2:
tests/portals-nav-placement.test.mjs).
[1.148.0] — 2026-08-12
Filtri di ricerca più chiari (Phase 4) — il pannello dei filtri è ora una griglia ordinata — il pannello dei filtri di #/scan è passato da un flex-wrap irregolare di riquadri rigidi di larghezza variabile a una griglia responsiva, e le azioni Applica / Reimposta ora occupano una loro riga separata e allineata a destra. Stessi filtri, stesso comportamento — solo più leggibili. Una rifinitura di design (senza parent-sync).
Modificato
- Pannello dei filtri di
#/scan→ griglia responsiva —.scan-filtersè oradisplay: gridcon colonnerepeat(auto-fill, minmax(180px, 1fr))e spaziature uniformi, così gli 11 filtri etichettati si allineano in colonne ordinate a qualsiasi larghezza invece di andare a capo in una riga irregolare. - Azioni Applica / Reimposta occupano l’intera griglia su una loro riga, separate da una sottile linea e allineate a destra. Rimosso il vecchio trucco dell’etichetta nascosta + il wrapper flex interno in
scan.js.
Note
- Solo CSS + una piccola pulizia del DOM — ogni id di filtro (
#scan-filter-*,#scan-apply) e il cablaggio diSR.render()sono invariati, quindi il flusso Playwright non è toccato. Nessuna nuova chiave i18n. - Verificato nel browser (0 errori di console); protetto da
tests/scan-filters-grid.test.mjs. - Suite: 2381 test (+3:
tests/scan-filters-grid.test.mjs).
[1.147.0] — 2026-08-12
Hermes & Telegram — la sezione di aiuto in-app + la superficie su cvstart.org (Phase 5b, parte 2) — la seconda e ultima parte del lavoro di documentazione di Hermes: la guida pratica ora vive dentro la guida di aiuto dell’app stessa, in tutte le 17 lingue, e l’assistente di documentazione integrato risponde alle domande su Hermes a partire da essa. Resta solo documentazione — il percorso del provider LLM Hermes rimane pianificato / non ancora collegato (Phase 5).
Aggiunto
- Aiuto in-app §30 “Hermes & Telegram” × 17 lingue — una nuova sezione della guida (cos’è Hermes + le due forme di integrazione; esecuzione su un server cloud; Telegram tramite Hermes + la regola “cosa NON esporre”), raggiungibile da
#/help. Il grounding didocs-assistant/DocsFabla recupera automaticamente, dato che entrambi leggonodocs/help/<lang>.md. - cvstart.org — un link alla guida di Hermes che punta al documento su GitHub.
Modificato
- Soglia del bundle di aiuto alzata 29 → 30 H2 / 105 → 108 H3 (
canonical-docs-coverage,help-ui,help-ru-config-section); §30 aggiunge 3 H3.
Note
- Ancora nulla chiama Hermes. Il nuovo canarino
tests/help-hermes-section.test.mjsverifica che ogni lingua contenga la §30 con le sue àncore indipendenti dalla lingua (docs/integrations/HERMES.md,hermes-bridge,#/help,127.0.0.1, Telegram). Il provider resta bloccato in attesa del contratto API della Phase 5. - Questo chiude il deliverable documentazione + skill della Phase 5b; l’integrazione del provider (Phase 5) resta un elemento separato e bloccato.
- Suite: 2378 test (+2:
tests/help-hermes-section.test.mjs).
[1.146.0] — 2026-08-12
Agente Hermes + Telegram — la guida di integrazione + una skill (Phase 5b, parte 1) — puoi eseguire career-ops-ui su un server cloud e collegare i suoi eventi (una scansione completata, un nuovo report, un follow-up urgente) a Telegram tramite un agente Hermes di Nous Research. Questa versione porta la documentazione di design + deployment e una skill hermes-bridge; il percorso del provider LLM Hermes resta pianificato / non ancora collegato (bloccato dallo spike sul contratto API della Phase 5). Documentazione intenzionalmente davanti al codice.
Aggiunto
docs/integrations/HERMES.md— l’analisi approfondita: le due forme di integrazione (endpoint compatibile con OpenAI vs. runtime dell’agente), il deployment su server cloud (reverse proxy + HTTPS + systemd, il contratto di sola lettura con il parent su una macchina headless), Telegram tramite Hermes, e un elenco da modello di minaccia «cosa NON esporre» (niente CV / stipendio / corpo dei report / chiavi verso il canale).- Il teaser
## Hermes agent + Telegramnel README — un breve rimando + link, nel README in inglese e replicato nei README tradotti di ogni lingua. - Una skill
hermes-bridge(.claude/skills/hermes-bridge/) che rende operativa la guida — controlli di prerequisiti e di ambito (Node ≥ 18, chiavi presenti, raggiungibilità dell’endpoint tramite il percorso sicuro da SSRF), non scrive mai segreti su disco/log, e si rifiuta di inventare un endpoint Hermes o di affermare che il provider sia collegato. - Una sezione Integrations in
docs/architecture/OVERVIEW.mdcollega alla guida.
Note
- Per ora nulla chiama Hermes. Un test canarino (
tests/hermes-docs.test.mjs) verifica i marcatori di onestà «pianificato / non ancora collegato» e chellm-dispatch.mjsnon abbia alcun ramo Hermes/Nous — quindi collegare il provider in futuro dovrà aggiornare documentazione + roadmap nella stessa modifica. - Rinviato a v1.147.0 (Phase 5b, parte 2): la sezione di aiuto in-app «Hermes & Telegram» H2 × 17 lingue e la superficie di marketing di cvstart.org.
- Suite: 2376 test (+4:
tests/hermes-docs.test.mjs).
[1.145.0] — 2026-08-12
Statistiche perspicaci (segue): un grafico ricostruibile — la scheda “Tendenza ruoli target” su #/stats ha ora un widget Crea un grafico: scegli una metrica × dimensione e si ridisegna in tempo reale. Richiesta UX dell’utente (senza parent-sync).
Aggiunto
- Grafico ricostruibile metrica × dimensione — scegli una metrica (Offerte / Stipendio mediano / Stipendio medio) e una dimensione (Per paese / Per ruolo), e il grafico a barre si ridisegna all’istante. Le metriche salariali rispettano la valuta + l’interruttore all’anno ⇄ al mese; le offerte sono un semplice conteggio.
- 8 nuove chiavi i18n × 17 lingue; snapshot 1200 → 1208.
Note
- Verificato nel browser (0 errori console). Suite: 2372 test (+2).
[1.144.0] — 2026-08-12
Impostazioni e filtri (Fase 4, parte 1): abilita/disabilita i portali seguiti — ora puoi accendere o spegnere un’azienda monitorata da #/portals, e lo scanner lo rispetta. Richiesta UX dell’utente (senza parent-sync).
Aggiunto
- Interruttore Abilita/Disabilita per azienda su
#/portals— un clic spegne un portale (lo scanner EN salta già le aziende conenabled: false, quindi un portale disabilitato esce da tutte le scansioni future) o lo riaccende, con un avviso ottimistico. POST /api/portals/toggle— una scrittura esplicita dell’utente che commuta chirurgicamente e con validazione di parsing il flagenableddi un’azienda inportals.yml(commenti, ordine e altri campi preservati). 5 nuove chiavi i18n × 17 lingue; snapshot 1195 → 1200.
Note
- La modifica allo scanner è stata zero —
en-scanner.mjsfiltra giàenabled !== false. Suite: 2370 test (+3).
[1.143.0] — 2026-08-12
Comprensibile (segue): suggerimenti ? sulle viste di lavoro principali — il ? di aiuto copre ora le nove pagine d’azione principali, in tutte le lingue. Aggiustamento UX segnalato dall’utente (senza parent-sync).
Aggiunto
- Suggerimento
?su altri 9 titoli di viste —#/scan,#/evaluate,#/cv-studio,#/tracker,#/config,#/deep,#/batch,#/auto,#/applyricevono un?inline (viaHelpHint.title) che apre un popover localizzato «cosa fa / come usarlo / cosa aspettarsi» — lo stesso componente sicuro per la CSP di v1.139.0. - 9 nuove chiavi i18n × 17 lingue (
help.hint.scan/…/apply); snapshot 1186 → 1195.
Note
- Verificato nel browser (0 errori console). Suite: 2365 test (+1).
[1.142.0] — 2026-08-12
Correzione: basta con l’archetipo di carriera “Unknown” — #/orientation ora classifica sempre tra gli otto vettori di carriera nominati invece di rispondere talvolta “Unknown” e consigliarti di “puntarci di più”. Correzione segnalata dall’utente (senza parent-sync).
Corretto
#/orientation— il prompt IA vieta ora un archetipo fuori insieme. Il modello DEVE classificare i primi tre tra esattamente gli otto vettori nominati e non mai rispondere “Unknown”/“N/A”/“dati insufficienti” né inventare un’etichetta. Con un CV scarno nomina comunque i tre più vicini con minore fiducia e dice quali prove mancano.
Note
- Solo modifica del prompt server (
buildOrientationPrompt); nessuna modifica i18n/schema. Suite: 2364 test (+1).
[1.141.0] — 2026-08-12
Statistiche perspicaci (segue): arricchimento delle aziende finanziate — #/funded è ora più visivo: loghi aziendali, un grafico per importo di finanziamento e schede con round / importo / punteggio di scoperta / azione suggerita. Aggiustamento UX segnalato dall’utente (senza parent-sync).
Modificato
#/funded— tabella piatta → griglia di schede. Ogni azienda finanziata di recente è ora una scheda con logo (derivato dal nome viaCompanyLogo, avatar a lettera come ripiego), chip di round + importo, il punteggio di scoperta e l’azione suggerita del progetto principale, oltre a link e data della notizia.- Visualizzazione dell’importo di finanziamento — un grafico a barre delle maggiori aziende per importo divulgato; gli importi in testo libero (“$120M”/“€1.5B”) vengono convertiti in grandezza da un nuovo
parseAmount. 3 nuove chiavi i18n × 17 lingue.
Note
- Ancora sola lettura su
GET /api/company-funded; descrizione e fascia salariale non sono nella fonte di finanziamento. Suite: 2363 test (+2).
[1.140.0] — 2026-08-12
Statistiche perspicaci: cifre salariali più ricche — la ripartizione salariale di “La mia pipeline” su #/stats mostra ora la media (non solo la mediana), un interruttore all’anno ⇄ al mese e una tabella min · media · mediana · max per paese. Prima parte della Fase 3. Aggiustamento UX segnalato dall’utente (senza parent-sync).
Aggiunto
- Salario medio —
RoleStats.salaryStatsora restituisceavgUsdaccanto aminUsd/medianUsd/maxUsd. La mediana resiste agli outlier, la media rivela l’asimmetria — insieme si leggono come una distribuzione. - Interruttore all’anno ⇄ al mese e una tabella min · media · mediana · max per paese nella sezione salari, guidati dai selettori di valuta e periodo. 8 nuove chiavi i18n × 17 lingue.
Note
- Le cifre derivano ancora solo da annunci con salario leggibile e sono normalizzate in USD (indicative). Suite: 2361 test (+1).
[1.139.0] — 2026-08-12
Comprensibile: suggerimenti ? — un pulsante ? riutilizzabile e sicuro per la CSP che, al clic, spiega «cosa fa / come funziona / cosa aspettarsi» nella tua lingua. Aggiustamento UX segnalato dall’utente (senza parent-sync).
Aggiunto
- Popover di suggerimento
?(window.HelpHint) — un?rotondo accanto a un titolo apre un popover leggero, adattato al tema e speculare in RTL, che mostra una spiegazione localizzata tramiteUI.md(); accessibile (role="tooltip",aria-expanded, chiusura con Esc/clic esterno, focus ripristinato) e sicuro per la CSP. ?sulle 5 schede di#/statse su 8 titoli di viste IA/analitiche (career-plan, orientamento, two-pager, networking, colloquio simulato, memoria, funded, riepilogo settimanale) — 14 nuove chiavi i18n × 17 lingue.
Note
- Tutte le viste avevano già un sottotitolo di una riga; il
?aggiunge la spiegazione più approfondita su richiesta e rende gli stati vuoti autoesplicativi. Suite: 2360 test (+4).
[1.138.0] — 2026-08-12
Generazione nella lingua della tua interfaccia — ogni generazione IA ora risponde nella lingua scelta nell’UI, più rafforzamenti dei test emersi dalla revisione. Un aggiustamento UX segnalato dall’utente (senza parent-sync).
Modificato
- Le generazioni IA ora rispettano la lingua dell’interfaccia. Con l’interfaccia in russo, spagnolo, giapponese, … il testo generato torna in quella lingua invece che sempre in inglese. La direttiva di lingua di output attraversa tutti gli endpoint di generazione — piano di carriera, orientamento, report di mercato, colloquio simulato, piano di networking, «chiedi alla documentazione», il suggerimento della nota di memoria e la bozza del two-pager. Codice e identificatori restano in inglese (es. le chiavi YAML del two-pager); solo prosa, titoli e punti vengono localizzati.
Corretto
- Guardia dei ruoli di colore CSS (
tests/css-role-tokens.test.mjs) — un canarino statico che i token alias della modalità scura di v1.137.0 non invertano mai il ruolo: i token di testo (--fg/--danger/--ok/…) mai comebackground, e quelli di superficie (--card/--panel/--line/…) mai comecolordi testo, in tutto il CSS e negli stili inline della SPA. - Auto-sonda del loader XSS di
UI.md()— il test che caricamd()daapi.jsora sondamd('<script>…')subito dopo l’estrazione e solleva un errore se manca l’escape, così un futuro taglio errato fallisce rumorosamente invece di lasciare verde la suite di sicurezza su una funzione troncata. - Guardia di scroll su
#/career-plan— loscrollIntoViewdopo la generazione viene eseguito solo se l’anteprima è ancora connessa al documento.
Note
docs/UX-ROADMAP.mdaggiornato: i suggerimenti?+ descrizioni di pagina + stati vuoti passano a v1.139.0; un provider Nous Research / Hermes — con una guida al deploy su server cloud + Telegram e uno skill Hermes — è tracciato come Fase 5 / 5b.- Suite: 2356 test (+5).
[1.137.0] — 2026-08-11
Correzioni di leggibilità e rendering — contrasto in modalità scura, etichette dei grafici e il piano di carriera. Un intervento UX segnalato da un utente (nessun parent-sync).
Corretto
- Bianco su bianco / nero su nero in modalità scura su molte schermate — quindici proprietà CSS personalizzate referenziate da diverse viste (
--fg,--panel,--panel-2,--ok,--danger,--card, …) non erano mai state dichiarate, quindi ricadevano su valori chiaro/nero hardcoded: corretto in modalità chiara, illeggibile in modalità scura (i chip di panoramica di#/pipeline, la tab attiva di#/stats, “Attivo / Chiavi” + “✓ impostato” di#/config, le sezioni di#/two-pager, il fumetto della domanda di#/mock-interview, il testo di errore). Ora sono aliasate ai veri token theme-aware, quindi seguono automaticamente il tema — 0 violazioni di contrasto WCAG-AA su tutte le 29 viste, verificato da un revisore automatico; la tab attiva di#/configè passata a uno stile con tinta più leggibile. Una guardia di regressione (tests/dark-theme-tokens.test.mjs) mantiene l’aliasing. - Le etichette dei grafici di
#/statsvenivano troncate a metà parola (“Senior Backend Engineer” → “…Enginee”) — ora vengono troncate con ellissi, mantenendo l’etichetta completa come tooltip al passaggio del mouse. #/career-planmostrava il piano generato come Markdown non elaborato — ora viene renderizzato automaticamente come testo formattato e leggibile (il Markdown modificabile resta nella textarea; il pulsante Anteprima lo attiva/disattiva).
Note
#/career-plan,#/two-pager,#/statse il digest settimanale dei colloqui non sono rotti — mostrano stati vuoti finché non generi un piano o non hai dati. Indicazioni più chiare in pagina e suggerimenti di aiuto?sono previsti come prossimo passo (docs/UX-ROADMAP.md).
[1.136.0] — 2026-08-11
Parità con il career-ops padre v1.26.x (mainline post-v1.26.0) — una nuova sorgente a zero autenticazione più un’ondata di port di qualità e robustezza ai mirror di web-ui. Il registro ora conta 79 sorgenti = 74 EN + 5 RU (ALL_ADAPTERS 74).
Aggiunto
eightfold(Eightfold AI, #2684) — bacheche di talent-acquisition tramite l’API a zero autenticazionehttps://<tenant>.eightfold.ai/api/apply/v2/jobs, fissata sull’host*.eightfold.ai(il CNAME brandizzatocareers.<company>.comè deliberatamente rifiutato); paginata con un tetto di sicurezza, errore su bacheca morta, deduplica degli URL. Sorgente + adattatore + suite CI-isolata; compare nel filtro Sorgente di#/scane sulla landing.
Corretto
- Deduplica e chiavi di ruolo sensibili a Unicode (#2569 / #2587 / #2667) — una nuova
normalizeTextKeycondivisa (NFKC, mantiene lettere/segni/cifre di qualsiasi script) sostituisce le chiavi solo-ASCII:detect-repostsora raggruppa le varianti di larghezza/punteggiatura dell’azienda (“Acme, Inc.” ≡ “Acme Inc”) e non collassa mai datori di lavoro distinti non latini, mentrerole-matcherunifica i titoli a larghezza piena e mantiene i token di ruolo non latini invece di eliminarli. fetchJsonWithRetrynon ripete più un reindirizzamento rifiutato (#2657) — una guardiaredirect:'error'che incontra un 3xx è deterministica, quindi ora non è ripetibile e fallisce rapidamente invece di consumare il budget di retry.- Gruppi AND con
+intitle_filter.positive(#2552) — un+delimitato da spazi all’interno di una voce positiva ora richiede che ogni termine appaia nel titolo, in qualsiasi ordine. oraclecloudaccetta gli apici tenant numeratioraclecloud1.com … oraclecloud99.com(#2683) — una famiglia limitata (nessuno zero iniziale, ≤ 2 cifre), mai un apice wildcard.workableirrobustita (#2675) — retry, header simil-browser e serializzazione delle richieste contro l’host dietro Cloudflare.personioricorre a uno scraping HTML quando il feed XML è disabilitato, invece di non restituire nulla.- Alias FALLBACK di
statesrisincronizzati con il padre (#2615).
Note
- Non portato (non specchiato da web-ui, o solo CLI): reply-matcher (#2672), jd-similarity (#2661), jd-skill-gap (#2686), il parsing di scan env-path (#2568) /
--flag=value(#2589), e le modifiche a cover-letter / template CV / doctor / ollama / generate-pdf. Gli avvisi HIGH webjs-yaml/nanoiderano già stati corretti in web-ui v1.135.0.
[1.135.0] — 2026-08-11
Parità con career-ops padre v1.26.0 — cinque nuove sorgenti di scansione a zero autenticazione più correzioni di correttezza a quattro bacheche già presenti in web-ui. Il registro ora conta 78 sorgenti = 73 EN + 5 RU (ALL_ADAPTERS 73).
Aggiunto
- Cinque nuove sorgenti di scansione (ciascuna con una sorgente + adattatore + suite CI-isolata; compaiono nel filtro Sorgente di
#/scane sulla landing di cvstart.org):join(JOIN) — la bacheca JOIN di un’azienda ricavata dal__NEXT_DATA__di Next.js injoin.com/companies/<slug>(host fissato, pagine limitate).getro(Getro) — bacheche di portfolio “talent-network” dei VC tramite l’API pubblica POSTapi.getro.com, paginata dal più recente; ogni lavoro è attribuito all’azienda del portfolio, non al fondo.consider(Consider) — bacheche di portfolio VC di getconsider.com tramite una POST same-origin; l’host configurabile è fissato da una guardia SSRF strutturale (solo host HTTPS pubblico).joinup(JOINUP) — la bacheca svizzera joinup.ch, che legge la pagina più recente renderizzata lato server; si blocca (fail-closed) in caso di rottura dello scraper.remotli(Remotli) — remotli.ch, ruoli da remoto presso aziende svizzere (stipendi in CHF); emette l’URL di candidatura ATS proprio del datore di lavoro, così le inserzioni incrociate vengono deduplicate.
Corretto
- a16z Speedrun non interrompe più l’intera bacheca per un intoppo transitorio — i fetch delle pagine ora passano attraverso un
fetchJsonWithRetrycondiviso (tentativi limitati solo su 429/5xx/timeout transitori, mai su un 4xx permanente), e il budget di pagina è stato ridimensionato per la pagina da 50 lavori. - arbeitsagentur è passata alla API Jobsuche v6 (
/pc/v6/jobs) — il vecchio endpoint v4 restituisce 404; la forma della risposta è stata rinominata e il filtraggio remoto ora si restringe lato server. - thehub è passata alla API v2
jobsandfeatured— le righe non portano una data di pubblicazione e sono esenti dal filtro per età. - hackernews trova ora in modo affidabile il thread mensile “Who is hiring?” filtrando la ricerca Algolia sul tag account
author_whoishiringinvece che su una query in testo libero.
Note
- Non portato (web-ui è già sicuro, assorbito dall’inoltro, o solo CLI): le chiavi di deduplicazione ruoli / corrispondenza aziende Unicode (il raggruppamento dei ripescaggi di web-ui fa già chiave sull’azienda in minuscolo semplice, quindi datori di lavoro distinti non latini non collassano mai); il segnale di latenza di rifiuto del follow-up + le rifiniture di company-funded (inoltrati in sola lettura, fail-soft); i percorsi di scansione sovrascrivibili da variabili d’ambiente e il parsing
--flag=value(web-ui esegue gli scanner in-process); il refactoring di consolidamento dello User-Agent (web-ui lo centralizza già); e le voci solo CLI (elenco contenuti non attendibili, oferta/offer-prep, doctor, modifiche ai template CV/lettera di presentazione).
[1.134.1] — 2026-08-05
Irrobustimento della validazione — correzioni emerse da un audit a livello di intero progetto.
Corretto
successfactorsnon scarta più i lavori raccolti in caso di fallimento a metà scansione (regressione nel port v1.134.0 dell’errore su bacheca morta) — il suo ciclo di paginazione non aveva alcuntry/catch, quindi un fallimento a pagina 2 o successiva (dopo il successo della pagina 1) generava un errore e scartava tutto ciò che era già stato raccolto; e se quel fallimento era un404(unostartrowfuori intervallo),en-scannermetteva in quarantena per giorni un tenant in realtà attivo. Ora rispecchiaphenom/radancy: un fallimento a pagina 0 genera ancora un errore (bacheca morta), ma un fallimento su una pagina successiva mantiene i risultati parziali.- Le chip dei filtri di
#/scansono ora azionabili da tastiera (WCAG 2.1.1) — le chip delle facet (e la chip “pulisci”) erano span con un gestore di click ma senzatabindex/ruolo, quindi gli utenti che usano la tastiera o uno screen reader non potevano raggiungerle né attivarle. Ora portanorole="button",tabindex="0",aria-pressede l’attivazione con Invio/Spazio. - Tre stringhe inglesi hardcoded sono ora localizzate — il tooltip del badge di affidabilità di
#/scan, l’intestazione della colonna di trasferimento di#/scane l’intestazione del punteggio di#/dashboarderano letterali nudi che il gate di parità i18n non poteva rilevare (non erano mai state chiavi), quindi restavano in inglese in ogni localizzazione non inglese. Orascan.trustTip+scan.col.reloc(2 nuove chiavi) più un riuso ditrack.col.score, con una guardia source-static.
[1.134.0] — 2026-08-05
Parità con career-ops padre v1.25.0.
Aggiunto
- Nuova sorgente di scansione: getManfred (
manfred) — un feed a livello di board di ruoli tech spagnoli/UE con stipendi pubblicati, dawww.getmanfred.com/api/v2/public/offers(zero autenticazione, host fissato + solo HTTPS, catalogo completo in una singola richiesta). Sorgente + adattatore + una suite CI-isolata (tests/sources-manfred.test.mjs); il registro ora conta 73 sorgenti = 68 inglesi + 5 russe (ALL_ADAPTERS68). Compare nel filtro Sorgente di#/scane sulla landing di cvstart.org.
Corretto
- Il feed a16z Speedrun si troncava silenziosamente a 50 lavori (#2404) — il feed limita una pagina a 50, ma la sorgente richiedeva
PER_PAGE = 100, quindi la paginazione si fermava dopo la pagina 1. Corretto a 50. - Le bacheche morte ora generano un errore invece di leggersi come “attive ma vuote” (#2379) —
cryptocurrencyjobs,phenom,radancy,successfactors: un fallimento di fetch in cui nessuna richiesta si risolve mai ora genera un errore (così#/portalshealth e la scansione registrano un vero fallimento), invece di ridurlo silenziosamente a un elenco vuoto; un fallimento a metà scansione dopo almeno un successo mantiene i risultati parziali. - workable ora usa l’API pubblica del widget (#5ab8425) — passato a
apply.workable.com/api/v1/widget/accounts/<slug>, che restituisce in un’unica richiesta l’elenco completo degli annunci di un account grande, così gli account grandi non vengono più troncati.
Note
- Non portato (solo CLI o non replicato da web-ui): la riscrittura delle prestazioni con bucket per titolo di detect-reposts #2389; le correzioni per chiavi aziendali Unicode (la deduplicazione del tracker di web-ui è già sicura per caratteri non latini);
scan --since;cv-facts; il passaggio di audit su template CV / PDF;doctor; la direttiva sui contenuti non attendibili delle modalità.
[1.133.1] — 2026-08-02
Corretto
#/funded(Aziende finanziate) ora mostra i risultati — due bug facevano sì che la tabella mostrasse sempre “nessuna azienda finanziata” anche quandocompany-funded.mjsdel padre restituiva un elenco completo. (1) La vista leggeva i risultati sottores.candidates, ma il padre li emette sottocompanies(ciascuna{ company, amount, round, funding: { sources: [{ source, url, observed_date }] } }); il client ora legge la chiave corretta e mappa la forma reale delle prove. (2) La tabella dei risultati passava le proprie celle aUI.el('tr', {}, …)come argomenti variadici, maUI.el(tag, attrs, children)si aspettachildrencome un singolo nodo o un array, quindi veniva renderizzata solo la prima colonna (Azienda) — ora le celle vengono passate come array. Verificato in un browser reale: 11 aziende dai quattro feed vengono renderizzate con le colonne Azienda / Segnale di finanziamento / Fonte / Data e link alle prove funzionanti, zero errori console. Un passaggio vuoto ora mostra anche la diagnostica per fonte, così un giorno di notizie tranquillo si distingue da un feed bloccato.- Guardie di regressione in
tests/parity-routes-v1133.test.mjs: lo script fittizio del padre ora emette la forma reale di outputcompanies(il fixture originale rispecchiava erroneamente la formacandidates— proprio il motivo per cui il bug è stato pubblicato con la suite verde), oltre a canarini source-statici che verificano chefunded.jsleggares.companies(maires.candidates) e costruisca le righe della tabella con figli in forma di array (+1 → 2144).
[1.133.0] — 2026-08-01
Aggiunto
- Scoperta aziende finanziate (
#/funded, parità col padre #2117) — una nuova vista di sola lettura che inoltra lo scriptcompany-funded.mjsdel career-ops padre tramiteGET /api/company-funded: un elenco a revisione preventiva di aziende recentemente finanziate, scoperte da feed pubblici di finanziamento con host fissato (TechCrunch, PR Newswire, The Guardian, Hacker News). L’inoltro esegue lo script con--json --dry-run(JSON su stdout, nessuna scrittura su file), non passa mai l’input dell’utente in--sources, applica un limite di frequenza ed è attivato dall’utente (un pulsante Scopri, mai al montaggio). Nuovo modulo di rottaserver/lib/routes/funded.mjs+public/js/views/funded.js, sotto Sourcing. - Digest settimanale dei colloqui (
#/interview-digest, parità col padre #2129/#2130) — una nuova vista di sola lettura che inoltra lo script zero-LLMweekly-digest.mjsdel padre tramiteGET /api/interview/weekly-digest: un riepilogo meccanico delle note di sessione dei colloqui — con quali aziende e in quali fasi hai fatto colloqui questa settimana, competenze ricorrenti e lacune aperte stimate al meglio. L’intervallo opzionale?from=&to=viene passato solo quando ENTRAMBI sonoYYYY-MM-DDvalidi; un intervallo vuoto è un digestavailable:truevalido. Aggiunto aserver/lib/routes/interview.mjs+public/js/views/interview-digest.js, sotto Analytics. - Entrambi gli inoltri seguono il contratto consolidato fail-soft
available:falsequando lo script del padre è assente (CI, installazioni standalone). 26 nuove chiavi i18n ×17 locale; suite CI-isolatatests/parity-routes-v1133.test.mjs(+5 → 2143).
Note
- Il career-ops padre è avanzato oltre la v1.24.0 con la pagina Follow-up Tracker dell’app web/ Next.js (#1422) e il rendering PDF lato backend (#2182) — non portati: web-ui ha già un proprio inoltro follow-up e i propri runner PDF, e l’irrobustimento sottostante di
followup-cadence.mjsarriva gratis tramite l’inoltro con shell-out. Le modifiche aset-status.mjs/tracker-utils.mjssono interne alla CLI e non vengono replicate.
[1.132.0] — 2026-07-31
Modificato
- Il sottosistema di rendering dei risultati di
#/scanè stato estratto inpublic/js/lib/scan-results.js(debito da contratto sulla dimensione dei file —public/js/views/scan.jsera cresciuto a ~1254 LOC). Il sottosistema —renderResults,buildChipRow,getRows, i builder di righe/faccette, i pittori delle opzioni e lo specchio del registroFALLBACK_SOURCES— si è spostato in una factorywindow.ScanResults.create(ctx)che chiude su un oggetto di contesto fornito dalla vista. Nessuna modifica di comportamento — le funzioni sono state spostate testualmente, le variabili di chiusura ricollegate meccanicamente actx.*;scan.jsè ora ~906 LOC (è pianificato un secondo passaggio di estrazione verso l’obiettivo di 800 LOC). - I test source-static leggono entrambi i file tramite
tests/helpers/scan-src.mjs::loadScanSrc();tests/scan-fallback-sources.test.mjsora legge lo specchio del registro dascan-results.js. - Nuovo gate di regressione in-browser —
tests/playwright-scan-filters.mjssemina undata/last-scan.jsonpredefinito e guida ogni filtro di#/scan, verificando i conteggi esatti delle righe (npm run test:e2e:browser); sono stati aggiunti id stabili per i controlli dei filtri (#scan-filter-*,#scan-apply). - Il banner “Ultima release” del README è stato snellito a un riepilogo di una riga + un link al changelog completo (il lungo muro narrativo multi-versione è stato ritirato). Applicato in tutte le 17 localizzazioni.
[1.131.2] — 2026-07-31
Modificato
app.cssdiviso in tre fogli di stile ordinati (debito da contratto sulla dimensione dei file — il singolo file era cresciuto a ~1990 LOC, ben oltre il limite rigido di 800 LOC). Ora èapp.css(~672 — accessibilità, token di design/tema, sidebar, main, pulsanti, content-shell),components.css(~595 — card, griglie, paginatore, badge, tabelle, form, log/console, markdown, selettore lingua, filtro a chip, banner di connessione) eoverlays.css(~737 — toast, cassetto notifiche, modale, varie/responsive, lo specchio[dir="rtl"], docs-fab, usage-hud), ciascuno entro il limite rigido.- Il taglio è contiguo e in ordine, quindi la cascata è identica byte per byte al file pre-divisione;
index.htmlcarica i tre come<link>ordinati. Nessuna modifica di comportamento, markup o i18n. - I test che verificano il CSS ora leggono la concatenazione tramite un helper condiviso
tests/helpers/css.mjs::loadAppCss(). Il nuovotests/css-modularization.test.mjsblocca la divisione (i file esistono · ciascuno ≤ 800 LOC · ordine dei link in index.html) → suite 2138. Verificato nel browser: tutti e tre i fogli di stile vengono analizzati e le loro regole vengono applicate.
- Il taglio è contiguo e in ordine, quindi la cascata è identica byte per byte al file pre-divisione;
[1.131.1] — 2026-07-31
Corretto
- Coerenza del pinning dell’host degli adattatori sulle due sorgenti v1.130.0 (follow-up di code review, difesa in profondità; nessuna modifica di comportamento per input validi):
- L’adattatore
a16z-speedrun-talentora rivalida l’overrideapi:/a16z-speedrun-talent:inbuildEndpoint(HTTPS + host esattospeedrun-talent-network.com) e torna al feed canonico quando la validazione fallisce — parità con l’adattatorecryptocurrencyjobs, così un valore fuori host non raggiunge mai lo slot di fetch (in precedenza si basava solo sulla guardia a tempo di fetchassertSpeedrunUrl). Il controllo dell’host esatto è ora una singolaSPEEDRUN_TALENT_HOST_REesportata, condivisa dalla guardia e dall’adattatore. - Il parser
cryptocurrencyjobs—cleanUrlora usa la stessa guardia dell’host a corrispondenza esatta diassertCryptocurrencyJobsUrle dell’override dell’adattatore (eraendsWith, che accettava sottodomini). Il parser non è mai più permissivo della guardia SSRF: un link di elementosub.cryptocurrencyjobs.coviene scartato. - +2 test → suite 2135.
- L’adattatore
[1.131.0] — 2026-07-31
Aggiunto
- Bacheca a schede di stadio (CRM) per
#/tracker(portata dalla vista/pipelinedell’app web del padre). La barra a chip del funnel + il menu a tendina di stato del tracker sono sostituiti da una striscia di schede di stadio: una scheda Tutti più una scheda per ogni stato canonico — Evaluated · Applied · Responded · Interview · Offer · Rejected · Discarded · SKIP · Hired — ciascuna con un conteggio live sull’intera cronologia, incluse le fasi a conteggio zero così l’intero funnel è sempre visibile (l’aspetto CRM). La scheda attiva guida il filtro; un secondo clic la deseleziona tornando a Tutti. Le righe mantengono la tonalità del punteggio, la legittimità e le funzioni PDF e report, e la cella dell’azienda ora mostra un logo del brand quando i loghi sono abilitati (disattivati di default → zero richieste extra).- Nuova rotta di sola lettura
GET /api/tracker/stagesrestituisce il funnel canonico (etichette in ordine) + una mappa di fusione degli alias, presa daserver/lib/states.mjs(templates/states.yml, con il fallback integrato) — così il client non codifica mai staticamente la whitelist di stato. La risposta legacy senza parametri diGET /api/trackerè invariata (solo{ rows }). - Nuova libreria client pura e testata a livello di unità
public/js/lib/tracker-stages.jsraggruppa le righe secondo gli stadi del server, tollerando grassetto markdown residuo e alias localizzati (es.aplicado→Applied). Le schede sono accessibili (role tablist/tab, aria-selected, area di tocco ≥44 px, conteggi nel nome accessibile di ciascuna scheda). Nessuna nuova chiave i18n. Suite 2133.
- Nuova rotta di sola lettura
[1.130.0] — 2026-07-31
Aggiunto
- Due nuove sorgenti di scansione portate dal career-ops del padre v1.24.0 (in-process, senza nuove dipendenze; entrambe compaiono nel filtro Sorgente di
#/scane sulla landing di cvstart.org):- a16z Speedrun (
a16z-speedrun-talent, #2231) — il feed JSON dell’intera bacheca della talent-network a16z Speedrun. Fissato all’hostspeedrun-talent-network.com, solo HTTPS, paginazione a indice 0 con un tetto di pagine, threading diq/config per azienda, fail-soft. - Cryptocurrency Jobs (
cryptocurrencyjobs) — il job board Web3cryptocurrencyjobs.co, acquisito tramite il suo feed pubblico RSS 2.0 (zero autenticazione). Decodifica delle entità XML in due passaggi, annunci solo remoti, datore di lavoro estratto dalla coda del titolo"… at <Azienda>". - Il totale del registro è ora 72 sorgenti = 67 inglesi + 5 russe (
ALL_ADAPTERS= 67 adattatori di portali inglesi).
- a16z Speedrun (
Corretto
echojobs— i ruoli ibridi restano distinguibili dal remoto (rispecchia il #2258 del padre). Un marcatorehybridcase-insensitive ora produce"<Città> · Ibrido"(o un sempliceIbridoin assenza di città) eworkplaceType: 'Hybrid', invece di essere collassato inRemoto.radancy— markup legacy TalentBrew + trasporto del frammento di risultati JSON (rispecchia il commit del padre a3e6df9), condizionato da unopts.fetchJsoniniettabile.
Note
- Non portato — funzionalità del padre solo CLI. L’ampia superficie CLI/modalità di career-ops v1.24.0 resta fuori da web-ui, che è un visualizzatore + scrittura passante sottile, non un host di modalità: le tabelle di conformità/giurisdizione, la rubrica contatti + vCard, il debrief da trascrizione colloquio / rilevamento piattaforma di chiamata, l’impostazione stato del ledger, la registrazione degli esiti, il triage a due passaggi, la similarità delle JD, lo schema versionato degli artefatti CV di candidatura, il rilevamento doctor di Playwright-MCP e
portals/fix-slugs.mjs. Le modifiche di orchestrazione della scansione che vivono nelloscan.mjsdel padre — lo scanner Playwright per Interamt.de, lo sweep completo iCIMS reverse-ATS, il filtro di idoneità paese per il remoto, il pacing delle ricerche DNS, la deduplicarltrdi StepStone e la colonna azienda normalizzata nella cronologia scansioni — non si applicano: web-ui esegue gli scanner EN/RU in-process e non passa perscan.mjs. - Già coperto. La correzione di accent-folding di
role-matcher(#2209) è stata portata in v1.127.0, quindi qui è un no-op.
[1.129.1] — 2026-07-29
Corretto
- Follow-up della revisione IA sui port web di v1.128/v1.129 (tutti consultivi, corretti all’origine): precedenza di livello in
job-facets.js(un modificatore esplicito ora batte una parola di gestione —Senior Engineering Manager→senior, primalead); il fallback instates.mjsnon è più bloccato (una lettura riuscita è memoizzata, il fallback è restituito senza cache — un padre momentaneamente non disponibile al boot viene riletto alla chiamata successiva) +console.warnsu un file presente ma malformato;score-tone.js— una riga senza punteggio è neutra (muted), non rossa;domainFromName()salta gli slug non-ASCII prima di/api/logo; +una guardia di isolamento intests/states.test.mjs. +4 test → 2073.
[1.129.0] — 2026-07-29
Aggiunto
- Faccetta Livello + colonna Età su
#/scan— la libreriajob-facets.jsrilasciata in v1.128.0 è ora collegata alla UI di scansione (prima solo logica). Un nuovo menu Livello classifica il titolo di ogni offerta in lead/staff/senior/intermedio/junior/stagista (JobFacets.seniorityFromTitle) e si autopopola da ciò che c’è nei risultati (come la faccetta Paese); i titoli senza parola di livello passano sempre. Mantenuta in ricerche salvate, Reimposta e Applica. La tabella guadagna una colonna Livello (badge) e una colonna Età senza token (oggi/Ng, daJobFacets.daysSince). 12 chiavi i18n ×17, +3 test → 2069.
[1.128.0] — 2026-07-29
Aggiunto
- Quattro soluzioni portate dalla web app propria del padre (
../web/, Next.js), reimplementate in JS vanilla/ESM, senza nuove dipendenze: (1)server/lib/states.mjsleggetemplates/states.ymldal vivo come vocabolario canonico degli stati del tracker (fallback CI) — elimina il re-sync manuale della whitelist a ogni release; POST ripiega gli alias (spagnolo/legacy) sull’etichetta canonica, il funnel di GET raggruppa per stato canonico; (2) loghi aziendali sulle righe con host ATS viadomainFromName()(~90 brand→dominio); (3)score-tone.js— tono di punteggio a 4 livelli (≥4.2/3.8/3.0 + fallback a lettera); (4)job-facets.js— facet seniority/source/days. +21 test.
Note
- Non portato (solo concetto): il livello di azioni agentiche del padre (
actions/registry.ts+api/assistant/route.ts) — progetto per quandodocs-fabdiventerà un copilota. Nessuna nuova sorgente (registro 70), nessuna modifica i18n/help.
[1.127.0] — 2026-07-29
Aggiunto
- Tre nuove sorgenti di scansione (parità career-ops v1.23.0) — il registro conta ora 70 adattatori (65 EN + 5 RU): Flowxtra (aggregatore globale senza auth), VDAB (API per parola chiave del servizio pubblico per l’impiego fiammingo) e iCIMS (portali
careers-<tenant>.icims.com, distinto dajibeapply). Inoltre Cursor torna nel roster delle CLI (parent #2115):cli-detectora rilevacursor(10 strumenti), roster ripristinato in help/README/config ×17.
Corretto
- agenticjobs è passato dallo scraping HTML all’API REST (#2167); Greenhouse recupera la città da
/officesquandolocation.nameè solo un modello di lavoro (#2104); parità role-matcher (#1933/#2164/#2009: prefisso MTS, baselineproduct, ripiegamento accenti, disaccordo sub-baseline).
Note
- Non portato. Gran parte di v1.23.0 è superficie CLI/dashboard che web-ui non usa (batch-tailor, discover-ats, modi NL/PT, temi PDF, dashboard Go, updater/doctor); gli script relayati non cambiano. VERSION del padre → 1.23.0.
[1.126.1] — 2026-07-25
Corretto
- Due punti di deriva del roster CLI che il resync di v1.126.0 ha mancato — (1) l’intro della scheda API keys di
#/config(config.providerModelNote, i18n ×17) elencava solo 7 CLI — ora Antigravity e Grok Build sono inseriti dopo OpenCode; (2) una seconda riga della tabella comparativa nella guida di aiuto (×17) e l’help del sito (costruito in CI) riportavano ancoraInside Claude Code / Codex / Cursor / Gemini CLI— il set obsoleto con Cursor — ora il roster completo. Entrambi usavano separatori slash/punto medio che i pattern dello sweep v1.126.0 non coprivano. Snapshot i18n rigenerato; la suite resta a 1969.
career-ops-ui v1.267.1 · page 4/10 · mirror of the project changelog for cvstart.org