Changelog
GitHubOgni cambiamento rilevante, release dopo release. Lo stesso file è distribuito nel repository ed è tradotto in tutte le 19 lingue.
[1.253.1] — 2026-10-24
Due riscontri HIGH di sicurezza dalla revisione col modello massimo sono corretti: gli URL letterali IPv6 non aggirano più il guardia SSRF dello scanner, e la regex di escape del markdown CV non fa più backtracking quadratico.
Corretto
- I letterali IPv6 aggiravano la guardia SSRF (
http-json.mjs): un letterale IPv6 tra parentesi faceva lanciaredns.lookupe la guardia fail-open. Ora le parentesi si tolgono e il letterale si classifica viaisIP— senza DNS, senza fail-open. - ReDoS nell’escape del markdown CV (
security.mjs): il pattern faceva backtracking quadratico —<+ ~100k spazi costavano ~16 s; ora ~1 ms con una classe piatta limitata. Tutto ciò che si escapeva prima si escape ancora.
Note
- Entrambe le correzioni portano test rosso→verde (3 nuovi casi IPv6 + ReDoS con limite temporale; 5511 test, +7).
- I file sorgente dello scanner non sono toccati — la guardia vive nel trasporto condiviso.
[1.253.0] — 2026-10-24
Dieci nuove fonti di offerte entrano nello scanner — Atlassian, BWI, Computrabajo, Dover, Google, Kalibrr, Magneto365, Platsbanken, Tech in Asia e Zoho Recruit — il registro ora fornisce 120 fonti (115 EN + 5 RU) con 115 adattatori, e il role matcher tiene separati i titoli che differiscono solo per un breve codice in maiuscolo.
Aggiunto
- 10 fonti portate dal parent 1.38.0, ciascuna con il suo adattatore fetch-walk e una suite isolata CI (fetchImpl finto, zero rete): Atlassian (singolo feed JSON senza auth, URL iCIMS, allowlist https+icims.com), BWI (ItemList JSON-LD, JSON.parse prima), Computrabajo (LATAM, decodifica entità, «remoto» spagnolo letto remote), Dover (paginazione DRF per tenant, rendering
Remote (International), board validati UUID), Google Careers (prime pagine per frase, il datore della carta vince, dedup tra query), Kalibrr (aggregatore SEA, id del feed come id di riga), Magneto365 (LATAM, ritmo inter-pagina fisso), Platsbanken (API JobTech svedese, finestre per parola chiave), Tech in Asia (chiavi Algolia scoperte dal build del sito), Zoho Recruit (board hidden-input, arricchimento dettaglio limitato, valuta solo ISO-4217). - Conteggio registro: 120 fonti = 115 EN + 5 RU · 115 adattatori; i cinque gate di conteggio rigenerati dal registro vivo; il fallback del dropdown
#/scan, i link cvstart.org e help §17 ×18 seguono.
Corretto
- role-matcher (port #5688): i titoli che differiscono solo per un breve codice in maiuscolo («HR BP» vs «HR GP», «AWS» vs «GCP», anche in scritture miste) non vengono più accostati per fuzzy come la stessa posizione; i codici si leggono dal titolo grezzo con esclusione delle parole-rumore, e il guard sta subito prima del rapporto di Jaccard.
- I port sono sopravvissuti a una revisione Opus 5-5 di rigore massimo (21 reperti): throw sui board silenziosamente vuoti per deriva di markup (google/bwi), una scansione pre-abortita lancia invece di restituire
[], l’ordine decode-then-strip sugli scraper LATAM (la forma XSS strip-then-decode), https+allowlist sugli URL di riga atlassian, assert di host che rifiutano porte, parse delle pagine tardive dentro il try (i parziali sopravvivono) e onestà dei test (budget contati esattamente, contratti di coercizione conficcati veritieri). 13 reperti corretti; la coda [spec] tracciata a parte.
Note
- I cinque RU restano web-ui-only (nessun adattatore upstream — di proposito); ogni fonte EN ha il suo adattatore.
- Ratchet di copertura: i sei port maggiori alzati a ≥95 % branch (27 nuovi casi); il resto del codice morto difensivo è documentato per file.
- Le nuove fonti si selezionano con
provider: <slug>in portals.yml — nulla cambia per le configurazioni esistenti.
[1.252.1] — 2026-10-17
Il banner di versione della wiki ora è verificato da uno script invece che da una passata manuale — la regressione v1.252.0 l’ha colto solo a mano — e la direttiva locale prevale esplicitamente sul language.output del profilo e sulla lingua dell’annuncio.
Aggiunto
scripts/wiki-banner-check.mjs— il gate richiesto dalla regressione: legge ogniHome*.mdin un clone della wiki e confronta il banner conpackage.jsone con i contatori VIVI del registro (scanner adapters dal registro dei portali — 105, non i 109 scaduti del banner; route modules daserver/lib/routes/; UI locales dall’insieme CHANGELOG). La regex del banner è language-agnostic per FORMA: le pagine Home localizzate traducono etichetta e contatori («Текущий релиз», «Aktuel udgivelse», «адаптеров сканера», «enhedstests») e i loro numeri erano derivati più (5177) della pagina EN (5245).--tests Nprende il numero che iltest:ciappena eseguito ha stampato;--check-onlyvieta il clone implicito per l’uso offline. Test di fixture offline intests/wiki-banner-check.test.mjs.
Corretto
- Il banner wiki Home ×18 torna in sincrono (spinto con questa release): la passata v1.252.0 cambiava badge e link ma mai la riga del banner, e nulla di meccanico lo segnalava. Il gate sopra lo rende controllo bloccante della release.
- La direttiva locale fissa la priorità della lingua (
buildLocaleDirective): «This UI locale overrides the profile’slanguage.outputsetting and the job posting’s own language». La stessa regola 6 del padre comanda di scrivere inlanguage.output— senza la frase di priorità il modello risolveva le due istruzioni contraddittorie in modo incoerente per locale, che era la dispersione della colonna lingua in v1.252.0. Conficcato datests/locale-directive-language-output.test.mjssul testo della direttiva (17 locale non-EN + EN vuoto).
Note
- Osservazione sulla FORMA del ta evaluate (§3, tenere d’occhio): la prima esecuzione live della v1.252.0 ha prodotto un evaluate di 48 336 caratteri senza Block G e senza SCORE_SUMMARY — a forma di loop, non di lingua (CAR-70 è lingua). Se si ripete al prossimo release, avrà un suo issue Linear, separato da CAR-70.
- Secondo il FIX-PROMPT: il parent resta in sola lettura; l’override vive interamente nella direttiva della UI.
[1.252.0] — 2026-10-17
Il fork del parent è sincronizzato con career-ops-hq/career-ops — 264 commit upstream, parent 1.36.0 → 1.37.0 — entrambe le divergenze del fork difese, e il deploy riaggancia il parent in esecuzione a quella base.
Aggiunto
- Modalità di mercato vietnamita (vi) dall’upstream (
modes/vi/,tuyen-dung/ung-tuyen): BHXH/BHYT/BHTN, thử việc, lương tháng 13, trợ cấp thôi việc — il 21° mercato nella tabella delle modalità del parent. - Provider della board Tech in Asia (upstream): legge l’indice Algolia pubblico
job_postingsche la UI lavori di techinasia.com interroga essa stessa — una board SEA di startup, ~300 posizioni vive al campionamento, 273 indonesiane. Le chiavi vengono riscoperte a ogni run dal build del sito, il link di candidatura del datore di lavoro batte la pagina della board, e lo stipendio resta volutamente non allegato (i campi stipendio dell’indice sono flag, non denaro). - Otto nuovi documenti sui segnali di legittimità Block G (
modes/oferta-signals/08–17): paese nella terminologia dei benefit, tag di localizzazione di piattaforma terza, licenze delle agenzie, eccesso di requisiti migratori, contenuti vietati dalla giurisdizione, ampiezza della forbice pay-transparency, domanda dell’avvocato sul salario minimo, divulgazione dello screening AI, divulgazione del contratto a termine, potere d’acquisto alla ricollocazione.
Cambiato
- Irrobustimenti lato parent che viaggiano con la base: il controllo story-provenance lavora per unità e frase e tiene le cifre
user-cannot-confirmfuori da negoziazione, ATS e output di intervista; il fact-gate del CV gira su ogni renderer e legge affermazioni riformulate e strutturate; LaTeX rifiuta gli slot estratti scaduti prima di scrivere; la scansione launchd dei follow-ups viene saltata quando i follow-ups sono spenti; update-system mantiene la sua scansione delle modifiche locali entro il limite argv. - Le divergenze del fork sono sopravvissute al merge:
providers/telegram.mjs(parser esclusivo del fork, ADR-0001) e ilLOCATIONISH_REin lookaround Unicode intelegram-channel.mjs(ADR-0002). Il documento delle board ora elenca la rigatelegram-channelmigliorata dell’upstream, la rigatelegramesclusiva del fork, Tech in Asia e ilmax_pagespredefinito di Tencent alzato 20 → 100.
Note
- Nessun cambio di comportamento dello SPA in questa versione: è il treno di documentazione più il riaggancio del parent. Il deploy ora gira sul parent 1.37.0; la tabella degli stati canonici non è toccata dal delta del parent — gli stati del tracker si rendono esattamente come prima.
- Il merge upstream ha risolto due conflitti:
docs/SUPPORTED_JOB_BOARDS.md(entrambe le righe telegram conservate) etest-all.mjs(ilcopyDirSyncbasato su walkTree dell’upstream subsume il guard manuale.gitper i checkout annidati del fork; la lista degli import è l’unione).
[1.251.1] — 2026-10-12
Il filtro di specializzazione ora matcha solo le parole DISTINTIVE del ruolo — anzianità e sostantivi generici non matchano più una posizione da soli — e remote-qa separa forma da lingua così che una deriva non segni mai A–G in rosso.
Corretto
- Il filtro di specializzazione elimina anzianità e sostantivi generici dal suo matcher: «Senior PHP Developer» matchava «Senior Java Developer» tramite il Senior/Developer condiviso. Il matcher mantiene solo token distintivi (PHP, Go, Platform, SRE, Backend…); un ruolo senza token distintivi («Software Engineer») ripiega sulla corrispondenza dell’intera frase. I confini di parola restano: «Go» non matcha mai «Golang» né «Google».
- remote-qa separa forma da lingua: il flag A–G è giudicato solo dai codici blocchi/SCORE_SUMMARY/looping; una deriva
report not in <locale>va al proprio flag e alla colonna warnings (la tabella guadagna la 4a colonna mancante; i codici sono salvati per riga). - La barra della mappa è un vero 50/50 a 390 px (la divisione 58/42 lasciava la ricerca a 124 px e ne troncava il placeholder), e la regola CSS morta
.job-map-job-selectè rimossa. - CodeQL 552 — la variabile inutilizzata
OVERLAYSnel test design-qa rimossa.
Note
- Le miglia restano rinviate dal proprietario. La deriva linguistica ta rimane una limitazione DOCUMENTATA del backend hermes (2–26 % di prosa tra le esecuzioni; A–G regge): remote-qa la segnala come reperto atteso. La lista dei token generici è volutamente minimale — estenderla costa una riga per lingua.
[1.251.0] — 2026-10-12
Il select della mappa ora filtra per SPECIALIZZAZIONE dalle impostazioni (config/profile.yml → target_roles.primary): scegli «Product Manager» o «Backend Engineer (Go / PHP)» e la mappa mantiene solo le posizioni il cui titolo porta le parole di quel ruolo. Il localizzatore a lista di v1.250.0 è rimosso — era lo strumento sbagliato (correzione del proprietario).
Aggiunto
- Filtro per specializzazione: il select elenca le specializzazioni dalle impostazioni (target_roles.primary); sceglierne una mantiene solo le posizioni il cui titolo porta uno dei token distintivi del ruolo (ai confini di parola, senza maiuscole — «Go» non matcha mai «Google»); il Reset ripristina tutto.
- Il localizzatore a lista di v1.250.0 è rimosso — la sostituzione: filtro per specializzazione, non lista di posizioni (correzione del proprietario).
Cambiato
- Two mobile-map height specs: the floor moves 600 -> 540 px (the toolbar takes ~90 px of the viewport).
Note
- Le miglia restano rinviate dal proprietario. Il matching è deliberatamente SOLO TITOLO con le parole delle impostazioni: aziende o descrizioni trarrebbero posizioni non correlate nella specializzazione. Una posizione fuori da tutti i ruoli configurati resta visibile sotto «All».
[1.250.0] — 2026-10-12
La pagina della mappa diventa operativa: un selettore di posizioni sopra la mappa vola verso qualsiasi posizione visibile e apre la sua scheda (desktop incluso), e una ricerca testuale restringe la mappa per azienda o titolo — entrambe sopravvivono al ricaricamento.
Aggiunto
- Selettore di posizioni: un select sopra la mappa elenca le posizioni visibili ordinate per azienda+titolo; sceglierne una vola al cerchio e apre la scheda — anche su desktop.
- Ricerca testuale: restringe la mappa per azienda+titolo, persiste in map-prefs, Reset la cancella.
- Il FAB non copre l’attribuzione su
#/mapa tutte le larghezze.
Corretto
- Due specifiche di altezza mobile: il minimo va a 540 px (la barra occupa ~90 px del viewport).
Note
- Le miglia sono rimandate dal proprietario al prossimo rilascio. Le 4 nuove chiavi i18n entrano in tutti i 18 dizionari e lo snapshot assemblato viene rigenerato. Il selettore apre sempre l’anteprima (navigarne via ne vanificherebbe lo scopo); il cerchio si trova tramite un riferimento inverso __job — NON _point, che L.Circle possiede già.
[1.249.0] — 2026-10-11
La mappa delle offerte ora privilegia la pertinenza: una scala calma dal verde petrolio al grigio evidenzia le offerte migliori, i filtri per punteggio e tragitto restano memorizzati, il telefono usa una mappa a tutta altezza con anteprima al tocco e il tracker condivide filtro e ordinamento del tragitto.
[1.248.8] — 2026-10-11
Parità con il genitore: il fork e web-ui si sincronizzano con il delta di 115 commit dell’upstream — una nuova fonte PageUp, le celle URL del tracker analizzate come link markdown, lo stato Assessment — più il lavoro linguistico ta/ja: i report in loop vengono tagliati e nominati, lo scheletro dei titoli mantiene A–G stabile, e la quota di scrittura legge solo la prosa.
Aggiunto
Corretto
- Nuova fonte PageUp (105 adattatori EN / 110 fonti); le celle URL del tracker vengono analizzate come link markdown (l’href è la chiave di dedup); lo stato Assessment arriva via states.yml.
- Un report in loop viene tagliato e nominato (
report looping (block B repeated)), scheletro di titoli## A) … ## G)per il non-inglese. - La quota di scrittura legge solo la prosa (tabelle, SCORE_SUMMARY, codice e URL esclusi) — un report giapponese con tabelle latine non fluttua più.
- /api/evaluate avvisa
report not in <locale>e remote-qa stampa i codici in una colonna.
-
[1.248.7] — 2026-10-11
Il ruolo dall’intestazione del report ora legge la vera convenzione del genitore — # Evaluation: {Company} — {Role} — invece di assumere # <Ruolo> — <Azienda>, quindi il tracker non porta più «Evaluation:
Corretto
- L’etichetta localizzata viene rimossa per prima (
Evaluation:,Оценка вакансии:e altre 16 varianti); dopo l’etichettaCompany — Role→ il ruolo è il lato destro; senza etichettaRole — Company→ il sinistro; la parte che nomina l’azienda decide; il ruolo non è mai l’azienda né una parola di etichetta — il ripiego è la riga del post pulita. - cleanup-plan somma gli N delle righe (
— N dirty-URL line(s)), non conta i file — un file con 5 URL sporchi dava 1, non 5. - Un report nella lingua sbagliata avvisa: il prompt apre E chiude con l’istruzione obbligatoria di lingua (tutto il report, tabelle incluse), e la pipeline valida la lingua del report salvato — la deriva appare come avviso
report not in <locale>. - Niente chiacchiericcio del modello prima dell’H1 — il report inizia alla sua riga di intestazione.
Note
- Non portato e intoccato (verificato nella regressione v1.248.6): l’embed-fetch, la struttura a righe del post («Компания : X»), la deduplicazione «## Rejected», il rifiuto della radice di dominio prima del fetch, l’onestà di recon/link-check e le modalità cleanup. La validazione della lingua è un avviso — il report (contenuto già validato A–G + punteggio) viene salvato; l’operatore vede la deriva e può ripetere la valutazione.
[1.248.6] — 2026-10-10
Lucidatura post-regressione della catena Telegram: un post reale mantiene la sua struttura di righe (l’etichetta «Компания : X» e la riga del ruolo vengono trovate dove realmente sono), il ruolo del tracker arriva dall’intestazione del report invece che da una riga grezza del post, e le superfici operative (recon, cleanup-plan, link-check) riportano ciò che dichiarano di riportare.
Corretto
- Un post t.me non si incolla più in una riga:
<br>diventa un a-capo prima della rimozione dei tag e le entità HTML numeriche vengono decodificate; il pattern dell’etichetta azienda accetta la forma con spazio prima dei due punti. - Il ruolo del tracker viene dall’intestazione del report (H1
# <Ruolo> — <Azienda>); la riga di fallback viene pulita — senza hashtag, senza emoji, ≤80 caratteri. ## Rejectednon si duplica più: la sezione viene creata una volta; ogni rifiuto vi aggiunge la propria riga.- La radice di dominio viene rifiutata prima della richiesta — un URL spazzatura non costa più nulla.
- Recon calcola il segnale del contratto invece di asserirlo (
honours rejected:verificamarkPipelineRejectednel codice deployato); il contatore dei marchi non stampa più «0» due volte. - cleanup-plan riporta numeri per categoria (t-role da spostare, righe dirty-URL, righe test-row) invece di un singolo
grep -cche contava la propria riga di successo. - link-check tratta i 5xx di github.com come non verificati — dietro il muro dei bot, non come link rotti.
Note
- Non portato e intoccato (verificato nella regressione v1.248.5): l’embed-fetch stesso, il contratto di salto in pipeline, i test offline _setLookup, l’accettazione degli ordinali uk, i confini di parola nel filtro del ruolo e la modalità cleanup-apply. Il conteggio per categorie di cleanup-plan legge le etichette di output dello script stesso — rinominarle romperebbe silenziosamente i contatori.
[1.248.5] — 2026-10-10
Indurimento post-regressione: le voci di post singolo t.me ora recuperano il testo stesso del post (forma embed), una voce rifiutata lascia il pool del timer di valutazione lato server, e la numerazione nativa dei blocchi in un report ucraino viene accettata — inoltre un test che usava segretamente la rete live è offline per sempre.
Corretto
- Le voci t.me finalmente vengono valutate: la pagina di un singolo post serve solo l’intestazione del canale in HTML puro (~0,2 KB — «View in Telegram» e nient’altro), il modello riceveva una descrizione vuota e ogni voce Telegram moriva in valutazione. Il fetch ora riscrive
t.me/<canale>/<id>(e la forma feedt.me/s/…) nella forma embed (?embed=1&mode=tme) — stesso host, validazione SSRF invariata — e legge il post da.tgme_widget_message_text(fallback:og:description). Nessun testo del widget → rifiuto al passaggio fetch (telegram post has no text,rejected: true) prima di spendere token LLM. - Il timer di valutazione smette di pagare di nuovo per le voci rifiutate (contratto lato server): una voce rifiutata ora viene contrassegnata in
data/pipeline.md— la sua riga lascia la recinzione degli URL e atterra in una sezione## Rejectedcon un motivo breve (idempotente, mai duplicato). Il timer prende URL nudi dalla recinuzione, quindi una voce contrassegnata viene saltata naturalmente — senza dipendere dalle viscere dello script del timer. Recon stampapipeline rejected marks: N+honours rejected: yes (server-side)invece di setacciare lo script. - I report ucraini superano di nuovo la validazione A–G: un modello uk numera i blocchi secondo l’ordine del proprio alfabeto (А Б В Г Д Є Ж), e il passaggio dei lookalike contava solo А/В/Е — esattamente i blocchi C, D, F, G mancavano in produzione (2/2 riproduzioni). Il validatore ora accetta la sequenza ordinale ucraina quando i titoli appaiono in ordine (la stessa disciplina di abjad per ar e devanagari per hi), e il prompt di valutazione per ogni locale ora richiede esplicitamente
A–Glatine. - Il cancello delle parole chiave di ruolo scatta su parole, non sottostringhe: le radici
linkedin.com/ex.com/passavano il cancello perchélead/itvivevano nel testo dell’interfaccia della pagina. Le parole chiave ora corrispondono ai confini di parole Unicode, e una radice di dominio (URL senza percorso) viene rifiutata prima della chiamata LLM. - Un test unitario non ha più bisogno della rete live: il test no-hints (
tests/auto-pipeline-jd-gate.test.mjs) sostituiva il trasporto HTTP ma eseguiva comunque una vera query DNS — verde in CI solo perché il runner aveva connettività. safe-fetch ha guadagnato un hook_setLookup()(il risolutore stesso è sostituibile nei test; il codice di produzione non lo sovrascrive mai), e il test risolve offline.
Aggiunto
- Modalità workflow
cleanup-plan/cleanup-applyin deploy.yml: la pulizia dei rifiuti QA in produzione (post-qa-cleanup) gira lato server senza SSH da un laptop. Il log riporta solo numeri — nessun nome di file o anteprime di righe, perché il log è pubblico e i nomi dei file contengono nomi di aziende. Il passaggio Verify viene saltato per le modalità di pulizia.
Note
- Non portato: il compilatore di localizzazioni a confini di parola e la tabella degli stati USPS del genitore (documentato da v1.248.1). Il contratto di salto delle voci rifiutate vive lato server (contrassegno in data/pipeline.md), non nello script del timer — è fuori dal repository e volutamente non modificato.
[1.248.4] — 2026-10-10
Il proseguimento del round di regressione: la catena di risoluzione del ruolo per le voci Telegram — un post cirillico con una vera offerta ora produce un report nominato invece di un file unknown-role rifiutato — e la chiamata LLM si attiva solo per le voci che vale la pena valutare.
Corretto
- La catena del ruolo funziona per i post cirillici: l’elenco di parole chiave ha guadagnato разработчик/программист/инженер/аналитик/менеджер/руководитель/тимлид/лид/дизайнер/тестировщик/QA/архитектор/специалист/директор/DevOps/SRE (il vecchio era solo EN: 0 post reali su 40 ottenevano un ruolo), l’intestazione del report è consultata (
parseReportHeader— il modello scrive il ruolo nell’H1), e la prima riga sensata del post è l’ultima risorsa. - Il gate d’identità si è spostato PRIMA della chiamata LLM: una voce senza società e senza traccia di ruolo fallisce al passo fetch (
rejected: true) — niente token bruciati per una valutazione che sarebbe stata rifiutata comunque. t.me/s/<canale>/<id>non nomina più la società «S» — il segmento di anteprima è saltato; entrambe le forme di link risolvono al canale.- Le auto-etichette «Компания: X» nei post Telegram sono onorate (e «…в компании X» a metà frase per gli host di messaggistica).
Aggiunto
- I fallimenti dell’auto-pipeline portano
rejected: truenell’errore SSE — il timer di valutazione lato server usa il flag per saltare la voce del pipeline invece di riprovarla ogni 2 ore.
Note
- Non portato: il compilatore di parole chiave di localizzazione a confini di parola del padre e la tabella USPS (documentato dal v1.248.1).
- Il blocco «Translations» del README è generato da un’unica lista canonica, con un test di parità in CI (17 link per file, il proprio mai linkato) — ar/pl/uk hanno ottenuto il blocco; la deriva dei separatori
|/·è eliminata.
[1.248.3] — 2026-10-10
Proseguimento del round di regressione: il timer di valutazione non può più in nessun caso archiviare spazzatura Telegram senza nome come report, la pulizia QA non cancella più vere offerte Telegram, l’attribuzione della mappa è di nuovo visibile e le chip della dashboard leggono ≥ 4.5:1.
Corretto
- L’auto-pipeline valida PRIMA di salvare: un report senza punteggio 0–5 o senza blocchi A–G termina la corsa al passo evaluate (
rejected: truenel payload SSE) — nessun file di report, nessuna riga tracker. Le voci t.me che passavano la porta di lunghezza v1.248.2 hanno prodotto altri due rifiuti dopo quella versione; ora le voci senza nome sono rifiutate concompany/role not identifiable. guessCompanyRolenon inventa più una società dagli host di messaggistica —t.me,telegram.me,vk.com,linkedin.come affini sono in EMPTY_DOMAINS: un post Telegram retrocede sul nome del canale, e una voce senza nome viene rifiutata invece di essere archiviata comeunknown-role.post-qa-cleanuplegge prima il punteggio dall’intestazione: l’auto-pipeline rimuove il blocco SCORE_SUMMARY prima di salvare, così la vecchia verifica solo-summary segnava ogni offerta Telegram salvata come spazzatura — in produzione 8 su 41 portano un punteggio nell’intestazione e--applyle avrebbe cancellate. Il punteggio d’intestazione (viaparseReportHeader) è ora il segnale primario; il blocco è la riserva. Il dry-run stampa il motivo per ogni file.- L’attribuzione Leaflet è visibile su #/map — il FAB docs la copriva (violazione della licenza OSM); l’angolo è sollevato di 96px.
- Le chip della dashboard superano 4.5:1 — le etichette
dash-chipe la variante--manualpassano a--foggy-strong(prima 4.31:1).
Aggiunto
- I fallimenti dell’auto-pipeline portano
rejected: truenell’errore SSE — il timer di valutazione lato server usa il flag per saltare la voce del pipeline invece di riprovarla ogni 2 ore.
Note
- La deriva ar/ja non si è ripetuta alla re-esecuzione (vedi v1.248.2).
- La pulizia della produzione attende ancora un sì esplicito dell’utente (CAR-61c); con questa correzione la pulizia non può più cancellare le vere offerte Telegram.
[1.248.2] — 2026-10-10
Round di regressione: il timer di valutazione non trasforma più spazzatura del pipeline in «Last evaluation», il parsing di SCORE sopravvive ai formati reali e il tracker parla finalmente la tua lingua.
Corretto
- I report con JD vuoto non vengono più scritti. Il gate JD dell’auto-pipeline è salito da 50 a 200 caratteri prima della chiamata LLM — le pagine segnaposto (
example.com/qa-v167-…) finiscono in errore SSE + avviso in console invece di un report…-t-role-<ts>.mdche la dashboard mostrava come «Last evaluation» (42 file in produzione). - Il parsing di SCORE_SUMMARY sopravvive ai formati reali: «4,2/5», «4.2 / 5», «4.2», «SCORE :» validano tutti; una virgola con 1–2 cifre è decimale, con 3 è migliaia (il vecchio codice leggeva «4,200» come 4.2 e passava il gate 0–5). Un fallimento registra solo la riga
SCORE:— mai il contenuto del report (i log CI sono pubblici).
Aggiunto
scripts/post-qa-cleanup.mjs: dry-run per impostazione predefinita,--applycon backup automatici (*.bak-<ts>, report spostati inqa/cleanup-backup-<ts>/): righe ZZ-QA-TEST del tracker, report*-t-role-*senza SCORE valido, vociexample.comdel pipeline.
Note
- La deriva ar/ja del round di regressione non si è ripetuta alla re-esecuzione (entrambi: lang ✓ + A–G ✓) — la voce ta del language-check e il parsing SCORE irrobustito vanno come defense-in-depth.
- La pulizia della produzione attende un sì esplicito dell’utente: prima il piano in dry-run, poi
--apply.
[1.248.1] — 2026-10-09
location_filter ottiene i livelli always_allow e block_hard del padre (CAR-60) — una stringa di localizzazione multipaese sopravvive a un elenco di blocco dei fratelli, e un blocco duro esplicito non può più essere salvato.
Corretto
always_allowbatteblock: conblock: [romania, poland, croatia]ealways_allow: [united kingdom], la cella fusa di workable «London, United Kingdom · Romania · Poland · Croatia» passa — la regione d’origine è un’opzione (il caso consumatore di workable #4806, ora in forma padre).block_hardbattealways_allow: termini di livello paese che non devono mai essere salvati da un hit always_allow altrove nella stringa.strict: trueora contablock_hardcome livello restrittivo; la sezionelocation_filterdi docs/help documenta entrambi i livelli ×18.
Note
- Non portato questa volta di proposito: il compilatore di parole chiave di localizzazione a confini di parola del padre e la sua tabella degli stati USPS (web-ui mantiene la corrispondenza per sottostringa; la tabella serve configurazioni centrate sugli USA — archiviare sotto CAR-60 se serve). La forma allow-list del test consumatore di workable resta un secondo verdetto accanto alla nuova forma padre.
[1.248.0] — 2026-10-09
Parità col padre: cinque correzioni a monte di provider/liveness portate — le board che hanno spostato l’API continuano a funzionare e un banner di chiusura su riga propria non viene più perso.
Corretto
- Gupy segue l’API verso
portal.gupy.ioe invia i nomi dei filtri che l’API legge davvero (workplaceType/type) — i vecchi nomi plurali venivano ignorati in silenzio;countrynon viene più inviato (un paese configurato disegna un avviso con il nome della voce). I datori confidenciali cadono per corrispondenza esatta dell’etichetta («Confidencial Seguros» sopravvive) emax_pagesha un tetto di 100 — l’API risponde 400 oltre l’offset 10.000, e l’avviso di troncamento ora suggerisce di restringere le parole chiave. - Workable conserva tutti i paesi di una offerta multipaese: i fratelli sotto lo stesso URL di offerta si ripiegano in una riga («London, United Kingdom · Romania · Poland · Croatia») invece di lasciar vincere il primo paese.
- Liveness: un banner su riga propria non fa parte della riga sopra — «Sign in if you want to apply» / «Get notified when this position is filled» sotto un’offerta si legge come la clausola di chiusura che è, mentre un genuino «until filled» a cavallo di righe si legge ancora attivo.
- Radancy legge un elemento fratello
job-locationnel markup legacy delle card (i layout della famiglia Barclays mettono la localizzazione dopo</a>) — le righe senza localizzazione propria restano vuote invece di prendere in prestito quella del vicino. - I link di fallback di SmartRecruiters conservano lo slug configurato — un’offerta senza
refaffidabile sintetizza il suo URL pubblico dallo slug del tenant, mai dal nome visualizzato.
Aggiunto
- No-port documentati: la correzione a monte
_httpproxyfetchImpl(web-ui non ha un ramo proxy — portarla annullerebbe il controllo d’identità della guardia DNS-rebinding e aggiungerebbe una dipendenza undici che il repo evita di proposito);verify-atsa monte (+226 righe) ecv-facts(web-ui implementa il proprio scoring ATS e il parsing CV); la dashboard Go TUI del padre e i suoi script CLI; un riordino degli import in Teamtailor (cosmetico).
Note
- Lacuna nota come follow-up: il tier
always_allowdel filtro di localizzazione del padre (il test consumatore di workable sopravvive a un blocco UK grazie a esso) non esiste in web-ui; il port ha espresso lo stesso verdetto consumatore tramite un test allow-list. Archiviato come modifica separata.
[1.247.0] — 2026-10-09
La coda della scansione design CAR-49…58 è chiusa — tutti i reperti minori e di polish rimanenti, più la mappa scura che la versione precedente aveva rimandato di proposito.
Corretto
- La mappa renderizza un vero tema scuro. Zoom/livelli/attribuzione sono diventati scuri in v1.246.0; ora seguono anche le tessere: il modo scuro disegna il layer OSM con un’inversione CSS (senza chiavi di fornitore) e un preset Carto-dark con chiave è disponibile via
MAP_TILE_DARK_URL(il suo host è ammesso nel CSPimg-src). Il layer si rimonta in diretta al cambio tema. - I callout gialli hanno varianti scure (
config,batch, avviso hero) con la meccanica a token di v1.246.0 — e in corsa è stata colta una regressione:.callout asoffocava.btn-primary; ora con ambito:not(.btn). - L’URL della scheda info di Apply non si tronca più a metà parola.
- I pulsanti d’intestazione del CV usano un unico sistema di icone (testo; fuori il miscuglio 📁/📄/💾).
- Le tab di stato del tracker hanno un’unica cassa — caratteri da frase, con adattamenti per locale (CJK/ar/hi/ta senza forme di genere).
- Le icone della barra superiore sono un solo set SVG a tratto — fuori il miscuglio 🔔/🌙/🩺; il pulsante tema disegna sole/luna via CSS.
- La card Pipeline della dashboard perde il suo evidenziato rosa (anello solo hover/focus).
- Eliminare le ricerche salvate appare davvero distruttivo (
.btn-danger, non un ghost neutro). - L’anteprima di pipeline ha un vero stato vuoto (titolo + suggerimento + CTA, schema tracker).
- Il meta tempo/costo segue un unico schema:
UI.pageMetadisegna ⏱ + costo su una riga sotto il titolo della vista (evaluate/deep/auto/mode-page/orientation). - Auto-pipeline perde il suo ✨ — l’unico h1 decorato dell’app torna sul bordo sinistro condiviso.
- L’URL dei docs di batch è un link in linea.
- interview-digest / orientation hanno stati vuoti disegnati (titolo + suggerimento + CTA).
- L’export di career-plan è disabilitato senza piano, azioni su una sola riga.
- stats: via il titolo duplicato «Market report» e il placeholder di regione sta in riga (testo più corto ×18, larghezza fissa).
- Config non perde più percorsi assoluti — il
displayPath()lato server mostra~/…o il nome nudo del file. - L’helper di LLM_PROVIDER sono tre righe brevi (×18 +
pre-line), non un muro mono di 150 caratteri. - La card EMAIL del profilo tiene l’indirizzo su una riga (ellipsis; a capo solo ≤480px).
- Il testo di Health non spezza più le parole («Run buttons» intero; overflow-wrap anywhere).
- Il codice inline del help sopravvive all’RTL (isolato LTR, nowrap, scorrevole).
- La pill «Live evals» dell’hero non collide più in ru/ar (flex-wrap, icone non riducibili).
Aggiunto
- Preset di tessere scure (
MAP_TILE_DARK_URL+ attribuzione) documentato nel codice; l’ampliamento del CSPimg-srccopre l’host Carto.
Note
- Il default delle tessere scure è un’inversione CSS di OSM — Carto senza chiave ha chiuso le basemaps dietro una API key (verificato con richiesta); il preset con chiave è un opt-in, non il default.
[1.246.0] — 2026-10-09
Tutti i dieci reperti principali della scansione senior-design CAR-48 sono corretti — bloccante e tutti e sette i major inclusi.
Corretto
- Apply scuro: il link della scheda info è di nuovo visibile. La scheda conservava la superficie chiara in modalità scura; il link «Need Playwright? See …» era a ≈1.0:1. Il
.callout--infoa token di tema (surface/border/text/link) porta il link scuro a ≈7.3:1. - RTL non ribalta più le composizioni numeriche. Le pillole di punteggio («1.5 / 5»), le fasce dei report («≥ 4.5», «< 3.5») e il «/ 5.0» della dashboard ricevono isolamento direzionale (span LTR); il dizionario ar riformula in parole gli intervalli dove la prosa lo chiede.
- Il markdown del CV resta LTR. Textarea e anteprima si rendono con
dir="ltr"in arabo; il telaio attorno resta RTL. - La colonna DATE dei report non spezza mai più una data ISO (nowrap + cifre tabulari).
- La sidebar termina sopra l’HUD di utilizzo (riserva dello scrollport + dissolvenza) — l’ultima voce di navigazione non scorre più sotto il pannello; specchiato per RTL.
- Scan mobile @390: «Save search» interamente visibile — la riga di controlli va a capo e si comprime invece di tagliarsi al bordo.
- I controlli della mappa seguono il tema scuro (zoom, livelli, attribuzione). Le tessere restano volutamente invariate: la loro origine è fissata lato server dal CSP
img-src. - Il FAB della chat non copre più contenuto interattivo — il padding di scorrimento lo libera (pulsanti Disable di Portals, pill di punteggio di Reports, input del two-pager, attribuzione Leaflet).
- Il log attività parla la lingua dell’UI: 22 slug d’azione localizzati ×18 locale (fallback sull’id grezzo per gli sconosciuti) e le chip di filtro tradotte; la colonna TARGET resta — la riempie auto-pipeline.
- I form di assessments hanno etichette vere (caratteri da frase, legame
htmlFor/id) invece di soli segnaposto.
Note
- Non in questa release: tessere mappa scure (richiede un ampliamento del CSP
img-src— lato server, volutamente non lato client); varianti scure dei callout gialli (config,batch) — la meccanica a token è pronta per un seguito.
[1.245.0] — 2026-10-09
Il tamil (தமிழ்) entra come 18ª lingua dell’interfaccia — la prima nuova lingua dall’ondata delle 17, con parità completa delle chiavi.
Aggiunto
- Tamil (
ta): 1449/1449 chiavi in piena parità conen, nessun segnaposto perso, convenzioni indic-tech in stile hi (provider, nomi di modello, percorsi e comandi restano in caratteri latini).detect()accettata,ta-IN,ta-LK; il tamil è LTR (bloccato da test). - docs/help/ta.md completo (33 H2 / 125 H3, conteggi sorgenti verificati contro il registro attivo, ancore canary Hermes, YAML letterale), più i specchi README.ta.md e CHANGELOG.ta.md.
- Gate di enumerazione 17 → 18: LANGS, sonde detect, audit i18n, parità dei changelog, guardia RTL,
langsdel CI — che recupera anchehi, finora assente dall’elenco CI.
Note
- La landing di cvstart.org resta per ora a 17 lingue — tradurla è un’attività a parte; la sincronizzazione dei contenuti del sito pubblica già docs/help/ta.md e il changelog ta.
- REPORT_LABELS di
report-header.jsresta a 17 — il parsing dei rapporti tamil arriverà con la generazione dei rapporti tamil nel repo padre.
[1.244.2] — 2026-10-08
La rifinitura della pagina di scansione: launcher, barra di stato, pannello repost e filtri ridisegnati come un chrome coerente — e la tabella dei risultati non può più essere allungata dai dati dei board.
Corretto
- Le celle dello stipendio mostrano solo la forbice monetaria. Alcuni board mettono tutta la descrizione dei benefit nel campo stipendio — la cella visibile ora mostra solo la parte economica e sposta il testo nel tooltip (prima: una cella di 6 righe che stirava ogni riga).
- Le righe con titolo vuoto mostrano l’azienda (poi un trattino neutro) invece di una cella gigante vuota.
- Il badge di seniority non va mai a capo nella sua colonna a larghezza fissa.
- Le righe senza punteggio mostrano un «◎ —» attenuato con tooltip che spiega che il confronto two-pager non ha trovato corrispondenze (prima: nulla, sembrava rotto accanto alle righe con punteggio).
Aggiunto
- Refonte du chrome: card launcher (riga di controlli allineata, pulsante primario dominante), barra di stato del terminale con punto di stato (idle/running/done/errore) rispetta reduced-motion, pannello dei repost con tetto di altezza e header sticky (un dataset di 1,560 cluster renderizzava un pannello da 155,000 px), e il blocco filtri come griglia responsiva uniforme con footer allineato.
Note
- Tutto il resto di #/scan è identico a v1.244.1 — sono cambiati solo la rifinitura del chrome e le quattro correzioni sopra. Test 5045 → 5068 unit.
[1.244.1] — 2026-10-08
Corretto
- Scan icons announce real values. The boost/fit/score icons’ accessible names leaked the raw dict templates (“Fit: {band}”, “Boosted by {by}”) —
{band}/{by}/{score}are now substituted (band word, booster keyword, numeric score). [HIGH a11y, caught live in the v1.244.0 regression round] - Title pipes split correctly. A 4-segment title like «C++ | Rust | Go Developer | Onsite» no longer lifts “Go Developer” out as a “country” — the split demands the segment resolves to a real country (conservative
Countries.detectCountry). - Meta dates render for every row.
daysSinceaccepted date-only ISO, but the API serves full timestamps for ~83% of dated rows — those rows showed no date segment.
Note
- Tutto il resto di #/scan è identico a v1.244.0 — sono cambiati solo i tre fix sopra.
- Deliberatamente non fatto: stato dei filtri nell’hash URL, navigazione delle righe da tastiera e il parsing di
5 lakhs INR(follow-up esistenti).
[1.244.0] — 2026-10-08
I risultati della scansione si leggono come un elenco, non come un muro: un annuncio = due righe, i segnali sono icone e una scansione da 700 righe è una tabella impaginata invece di una pagina da 37,000 px.
Corretto
- Anatomia della riga. Ogni annuncio viene reso come una riga delimitata a due linee: linea 1 il titolo, linea 2
company · location · source · date · work-type. Il badge di potenziamento, la fascia di title-fit e il punteggio di corrispondenza sono diventati icone con nomi accessibili localizzati e tooltip (i lettori di schermo leggono «Соответствие: сильное» / “Fit: strong”, non un muro di parole); i badge di affidabilità e di trasferta si sono fusi nella stessa barra. Seniority e stipendio restano colonne ausiliarie solo desktop. - Igiene del titolo. Un segmento finale
| Germany | Remoteviene separato dal titolo e portato nella riga meta — consapevole di Unicode, e solo quando la coda è un vero marcatore di tipo di lavoro e il segmento centrale ha la forma di un luogo (le barre legittime come “C++ | Rust | Go Developer” non vengono mai separate). - Niente più allargamento orizzontale. Layout di tabella fisso,
min-width: 0, titolo e meta ellissizzati su una riga con il testo completo nel tooltip: zero overflow della pagina e zero scorrimento interno della tabella a 1440 px e 390 px (la vecchia pagina traboccava di 675 px alla larghezza del telefono). - Impaginazione. I risultati vengono resi 50 per pagina (25/50/100/200 selezionabili) — una scansione da 700 righe ora è alta ~4,600 px invece di ~37,000 px, e il DOM contiene ~1,000 nodi invece di 5,500+.
Aggiunto
- Test contrattuali di layout: la riga a due linee, i nomi accessibili delle icone (en + ru), il budget di nodi dell’impaginazione e la guardia su conteggio/ordine del title-fit girano nella suite browser — il redesign non può regredire in silenzio.
Note
- Test 5036 → 5045 unitari (la suite browser ha guadagnato i contratti di layout), media di copertura 98.14 % righe / 89.34 % rami (soglia 96/86).
- Ciò che lo scanner trova, le formule di punteggio e
last-scan.jsonrestano intatti — il title-fit resta un’annotazione. - Non incluso in questa versione: lo stato dei filtri nell’URL-hash e la navigazione da tastiera tra le righe (follow-up); l’analisi di
5 lakhs INR(preesistente).
[1.243.2] — 2026-10-08
Corretto
- Versione deployabile ritagliata come v1.243.2. Il tag git v1.243.1 è stato ri-puntato dopo la pubblicazione, e il
git fetch --tagsdel server rifiuta correttamente di sovrascrivere un tag esistente — quindi v1.243.1 non è mai stato deployabile. Questo tag porta l’albero identico (il fix del window-listener della modale su base v1.243.0) più questo incremento di versione. Null’altro è cambiato; il tarball npm 1.243.1 conteneva già il fix.
Note
- Il codice dell’app è identico a v1.243.1 — nuovo è solo il tag deployabile (il
git fetch --tagsdel server ha rifiutato la v1.243.1 ri-puntata). - Deliberatamente non fatto: forzare l’aggiornamento del tag lato server (i tag pubblicati non si spostano mai).
[1.243.1] — 2026-10-08
Corretto
- La modale «Registra esito» ora si chiude davvero alla navigazione. La guardia v1.243.0 ascoltava
hashchangesudocument, dove quell’evento non scatta mai — la correzione è arrivata morta e la modale continuava a bloccare la vista successiva (trovata dal vivo in produzione nel round di regressione v1.243.1). La guardia ascolta suwindow; il test dispatcha anche lì ed è stato verificato rosso contro il codice rotto.
[1.243.0] — 2026-10-08
Lo strato del browser tiene al sicuro il tuo lavoro e parla la tua lingua: nessuna perdita di modifiche non salvate, nessun fallimento silenzioso, nessun inglese silenzioso in un’interfaccia tradotta, contrasto AA ovunque.
Corretto
- Le modifiche non salvate sopravvivono ai re-render. L’editor del CV mantiene un buffer sporco e lo ripristina a ogni render — Annulla, un cambio di lingua o navigare verso CV Studio ora chiedono conferma invece di scartare; il piano di carriera conserva il proprio testo generato allo stesso modo. I fallimenti di caricamento in Config/CV Studio/career-plan/memory/two-pager mostrano un errore e disabilitano Salva invece di sovrascrivere in silenzio il file con campi vuoti.
- La modale “Registra esito” non sopravvive più alla navigazione. Aprirla su
#/trackere cliccare un’altra vista la lasciava bloccare l’intera pagina (emersa nella regressione a due banchi di test della v1.242.1) — ora si chiude al cambio di rotta; anche le viste sorelle della scansione fermano i loro flussi SSE e staccano i listener alla navigazione. - L’analisi di stipendi e parole chiave legge ogni sistema di scrittura. I chip delle parole chiave usano confini di parola Unicode (cirillico,
c++,.netfiltrano correttamente invece di azzerare le righe); un unico parser degli stipendi condiviso corregge$182.9K - $240K,от 100 000 руб,100 тыс. руб.,50 000 Kč(prima 50M USD) e la semantica degli intervalli ($100-150K); un must-have “Germania o Paesi Bassi” non conta più sia come soddisfatto sia come violato. - I flussi si assestano esattamente una volta. Una connessione SSE caduta consegnava l’errore due volte e poteva lasciare Genera disabilitato per sempre; ogni stato terminale (done/error/close/throw, incluso un processo figlio ucciso) si assesta su un unico percorso e riabilita il pulsante. L’auto-pipeline scarica l’ultimo frame bufferizzato invece di buttarlo via.
- Privacy e correttezza nei percorsi minori. Il corpo del bug report non porta più l’URL dell’offerta valutata né gli slug dei report; la privacy del CV maschera i link nudi
linkedin.com/in/…, i nomi in tutte maiuscole e smette di alterare le date UE; la risoluzione del paese prende la corrispondenza più lunga e si rifiuta di indovinare quando sono citati due paesi (“Cambridge, MA” non finisce più → Regno Unito); i download dei file conservano i nomi in cirillico. - Router e a11y. Il link di salto al contenuto non finisce più in 404 su
ontent; la voce di navigazione attiva portaaria-current="page"; un solo<main>/un solo<nav>; Escape chiude il cassetto mobile (che ora è davvero fuori dall’ordine di tabulazione quando è chiuso);scroll-padding-toptiene il focus visibile oltre le barre sticky; il banner sticky non copre più la topbar. - Il contrasto è AA in entrambi i temi — i toast scuri erano bianco su bianco (1.10:1): toast, pulsanti, banner e link su
--rauschora passano per token invarianti rispetto al tema, sotto la guardia di test contrattuali WCAG calcolati (≥ 4.5:1). - Tutto il resto del giro: evaluate mostra l’avviso di troncamento; memory/config/two-pager si rifiutano di salvare sopra un caricamento fallito; scan smette di lasciare orfano il suo SSE; i risultati della scansione classificano i board basati su API dal registro del server invece che da una regex fuori sincrono; il “Procedi con cautela” del tracker è un avviso, non un badge-red; le statistiche aggregano gli stati localizzati; i grafici a barre si leggono correttamente in RTL; il FAB della documentazione non copre più il contenuto sui telefoni;
GET /app.jsrisponde 404 invece della shell dell’indice;GET /api/runnerselenca le sue azioni.
Aggiunto
- Il danese entra nell’auto-rilevamento — un browser
da-DKriceve ora l’interfaccia danese (il dizionario era completo, era il rilevamento a non vederlo);zh-Hant*mappa sul cinese tradizionale.cv-diagnostics— la checklist del curriculum — è completamente localizzata (28 chiavidiag.*× 17). Aggiunte 26 etichette di field-spec; rimosse 31 chiavi morte; gli elenchi dei provider non affermano più che Hermes sia “ultimo nell’ordine automatico”; le aria-label del docs-fab/del menu sono localizzate.
Note
- Test 4812 → 5036 unitari, browser 118 + nuovi contratti di navigazione/contrasto, media di copertura 98.15 % righe / 89.34 % rami (soglia 96/86).
- Non incluso in questa versione: il redesign di
#/scan(Fase 4 — v1.244.0, spec redatta); una chiave di dizionario dedicataexport.docxFailed(il fallimento riusacommon.error);5 lakhs INRsi analizza ancora come 5 (preesistente).
[1.242.1] — 2026-10-08
Corretto
- L’archivio di release non contiene più i symlink
node_modules. Il tag git v1.242.0 portava un symlinknode_modulesnella radice esite/node_modules(ungit add -Agenerico li ha fatti passare oltre i pattern di ignore con barra finale), facendo fallire l’estrazione tar del deploy sul server — il deploy è tornato indietro in sicurezza e la produzione è rimasta su 1.241.1. Entrambi i symlink sono fuori dal tracciamento e i pattern di ignore ampliati; il tarball npm non è mai stato interessato (npm escludenode_modulesal packing).
Note
- Il codice dell’app è identico a v1.242.0 — solo packaging. I contenuti sulla correttezza delle fonti vanno in produzione con questo deploy (v1.242.0 non è mai arrivato in produzione: il deploy server è tornato indietro dopo il fallimento tar).
- Deliberatamente non fatto: ri-taggare v1.242.0 (npm ha già pubblicato 1.242.0; il tarball del registro è pulito — npm esclude
node_modulesal packing).
[1.242.0] — 2026-10-07
Lo strato dati dello scanner è di nuovo affidabile: le fonti morte tornano vive, una risposta malformata non può più fingersi un board vuoto e un URL ostile di un board non può più reindirizzare una scansione.
Corretto
- Le fonti morte tornano vive. justjoin (l’inviluppo dell’API è cambiato in
{data, meta.next.cursor}) legge l’intero catalogo con un percorso per cursore e i nuovi campi salariali; nofluffjobs risponde 400 senzasalaryCurrency— viene inviata la query richiesta dal genitore e i risultati vengono paginati (pageTo). himalayas e jobicy vedevano solo le ~20 offerte più recenti di oltre 100k — ora entrambe percorrono l’intero catalogo (paginazione per offset / cursore con tetti di pagine). - Un 200 con la forma sbagliata genera un errore già alla pagina 1. ~40 fonti leggevano risposte malformate o challenge come “0 offerte” e la scansione “riusciva” in silenzio. Le nuove guardie di
server/lib/sources/_shape.mjs(requireArray/requireContainer/requireObject) rendono il fallimento rumoroso in ogni famiglia; un fallimento a una pagina successiva conserva le pagine già raccolte; la paginazione si ferma sulla lunghezza grezza della pagina, non sul conteggio filtrato. - SSRF: i controlli dei fornitori per sottostringa sostituiti da pin di host esatti analizzati. lever (
clever.comcontienelever.co), greenhouse (5 host + i legacy host path-slugboards[.eu].greenhouse.io), ashby, smartrecruiters, workable, gem REST, ibm, arbeitsagentur, hecklerkoch (solo www), workingnomads (http://169.254.169.254era raggiungibile oltre la guardia DNS), remoteok, remotive, rss, successfactors.redirect:'error'ovunque; gli URL delle offerte devono esserehttps:sull’host fissato (javascript:/data:/ fuori host scartati). - Una singola voce errata in
portals.ymlnon può più interrompere un’intera scansione.resolveAdapterintercetta le eccezioni degli adattatori,detectApiè incapsulato per azienda, gli adattatori che lanciano restituiscononull; gli errori di fetch di comeet oscurano?token=prima che finiscano nei log o nei record di quarantena. - Paginazione e freschezza. workday percorre i tenant oltre le 100 offerte (pagine per offset,
MAX_PAGES); telegram conserva i post più RECENTI e pagina con?before=invece di tenere in silenzio i più vecchi; tencent prosegue quando mancaCount; taleo analizza le tabelle con intestazioni francesi; avature si ferma sulla lunghezza grezza della pagina; date in UTC (amazon con lo scarto di un giorno a est di UTC, torre, workingnomads, trudvsem); entità HTML decodificate (geekjob, getmatch, hh); mycareersfuture emette la forma completa dell’offerta a 12 campi. - Rilevamento del remoto inasprito. arbeitsagentur, vdab, rippling, teamtailor: la percentuale di contratto (“Verpleegkundige (100%)”,
bundesweit) e la parola “distributed” (“Distributed Systems Engineer”) non etichettano più Remote ruoli in sede.
Aggiunto
tests/adapters-pin-coverage.test.mjscompleta la copertura dei rami dei pin di host introdotta dai pin.
Note
- Test 4419 → 4812 unitari, browser 118. Media di copertura 98.15 % righe / 89.36 % rami (soglia 96 / 86).
- Non incluso in questa versione: i risultati lato client dello stesso giro di revisione (Fase 3 — viste, librerie, CSS/a11y, i18n);
sources/habr.mjsnon ha ancora un file di test dedicato (esenzione dal cricchetto).
[1.241.1] — 2026-10-07
Le valutazioni dal vivo funzionano end to end in ogni lingua, la mappa delle offerte posiziona gli annunci lunghi con più sedi e un deploy non interrompe più una scansione in corso.
Corretto
- Le valutazioni vedono tutto il metodo. Il budget di contesto (144 KB) tagliava ancora la fine di
modes/oferta.mduna volta inseriti CV e profilo, quindi ogni valutazione dal vivo perdeva gli ultimi blocchi. Il budget ora è 192 KB e il limite morbido del prompt 256 KB, definiti una sola volta inllm-dispatch.mjs. - Report nella lingua scelta. L’istruzione sulla lingua ora apre il prompt oltre a chiuderlo, e le parole comuni (ruolo, team, design…) sono scritte nella lingua del report; restano in inglese solo nomi, tecnologie e citazioni testuali. Risolve i report in hindi e giapponese che erano in gran parte in inglese.
- Le valutazioni lente non finiscono più in HTTP 502. Il timeout della valutazione è 300 s (prima era quello predefinito del provider, 180 s).
- Lettere dei blocchi in hindi. Un report che scrive le lettere A–G in devanagari (
## ए),## बी)…## जी)) viene riconosciuto, eSCORE:accetta cifre locali (३.८,٣٫٨,3.8) e la virgola decimale. - Mappa delle offerte: sedi lunghe. Una cella come “Berlin Office · Berlin · Germany · Bosnia · …” (oltre 300 caratteri) veniva rifiutata con HTTP 400 e l’annuncio restava senza marcatore. Ora si usa il primo luogo reale (
·,;,|,/, a capo; i segmenti “Remote …”, “(HQ)” e “Office” vengono saltati); solo una query oltre i 2000 caratteri viene rifiutata. - Deploy e scansione oraria. Il deploy sospende i timer di scansione e di valutazione automatica, attende la fine di una scansione in corso (fino a 15 min), riavvia e riattiva sempre i timer — anche in caso di rollback.
Aggiunto
- Mappa delle offerte su cvstart.org in tutte le 17 lingue, e nei README in inglese e russo.
- Screenshot della mappa in tutte le lingue: uno per lingua (
scripts/capture-map-screenshots.mjs), in tutti i 17 README, su cvstart.org e in una nuova sottosezione della guida, «Come usare la mappa» (§33, ×17).
Note
- Test 4404 → 4419, browser 118. QA remota (tutte le route, sito, link e LLM dal vivo × 17 lingue) verde in produzione.
- Non incluso in questa versione: valutazioni in streaming; il timeout di 300 s resta un limite rigido (un modello locale lento può ancora raggiungerlo) e il ponte Hermes mantiene la chiamata senza streaming.
[1.241.0] — 2026-10-07
Release di consolidamento nata da una revisione del codice con 28 agenti (~190 problemi verificati): chiuse le classi sicurezza, crash del processo e perdita di dati, più una mappa delle offerte contribuita da @bullitt186.
Aggiunto
- Mappa delle offerte (
#/map) — le offerte della pipeline, dell’ultima scansione e del tracker su una mappa OpenStreetMap, geocodificate tramite Nominatim con uno User-Agent identificativo, una coda da 1 richiesta/s, back-off e una cache su disco. Leaflet è servito da questo server (nessuna CDN); la CSP aggiunge soltanto l’origine dei tile inimg-src. Contributo di @bullitt186 (#381). - Protezione delle richieste — il server rifiuta un
Hostche non serve (DNS rebinding), le scritture cross-site e i GET con effetti collaterali (/api/stream/*,/api/run/*,/api/geocode). Un reverse proxy che inoltra un nome pubblico lo elenca inALLOWED_HOSTS; il workflow di deploy scrive quel drop-in di systemd a partire dall’URL pubblico. - Soglia di copertura —
npm run test:coverage:gate(scripts/coverage-report.mjs) stampa la copertura di righe/rami per file e fallisce sotto una baseline a cricchetto.
Corretto
- Una richiesta malformata non ferma più il server. Express 4 ignorava gli handler asincroni rifiutati, quindi un campo di tipo errato in
/api/deep,/api/auto-pipeline,/api/career-plan,/api/trackero un byte NUL in/api/statsterminava il processo; ora sono errori JSON 400 e qualsiasi altro rifiuto diventa un 500. - Una scansione interrotta o fallita non cancella più gli ultimi risultati.
last-scan.jsonviene conservato quando una scansione viene fermata o tutte le fonti falliscono, e una scansione di una sola azienda unisce i risultati invece di sostituirli. Una singola voce errata inportals.ymlnon interrompe più l’intera scansione. - Le valutazioni vedono tutto il metodo. I file di modalità venivano tagliati a 16 KB, quindi
oferta.md(92 KB) perdeva i blocchi C–G; i limiti per file ora sono 128 KB per le modalità e 64 KB per il CV, e un taglio viene segnalato. Le risposte vuote o troncate falliscono invece di scrivere un report vuoto. - SSRF: i salti di reindirizzamento vengono ricontrollati per destinazioni private, ogni risposta DNS viene verificata, la scadenza della scansione copre il corpo, le chiavi
*_BASE_URLvanno solo all’host del fornitore (Hermes/Ollama: loopback o LAN), pandoc viene eseguito con--sandbox. - Blocchi dell’event loop: due regex quadratiche (rimozione dell’HTML, sanificazione del markdown) sostituite da scanner lineari.
- Perdita di dati: l’eliminazione di una riga della pipeline conservava la retribuzione delle altre righe;
$&in un URL corrompevapipeline.md; i salvataggi di mock-interview, networking e deep-research sovrascrivevano file precedenti; la cronologia dei colloqui conservava i primi 40 turni invece degli ultimi. - Livello LLM: auto-pipeline e deep research rispettano
LLM_PROVIDERe ogni provider tramite il dispatch condiviso, registrano l’utilizzo e segnalano il troncamento; evaluate/test di Gemini non salvano più un report; OpenAI usamax_completion_tokense unOPENAI_MODELsolo Codex viene inviato comegpt-5; la descrizione dell’offerta è racchiusa come dato non attendibile. - PDF di report, deep e inline passano
--skip-fact-check(la verifica dei fatti del CV bloccava qualsiasi report con numeri); il formato di pagina del profilo non viene più sovrascritto. - Scanner: la quarantena è indicizzata per URL (correggere un URL lo fa riprovare), la deduplicazione nella stessa esecuzione per URL canonico, le voci vuote delle liste non corrispondono più a tutto, scritture atomiche, le righe di cronologia
skipped_*non sono «già viste». - Controllo dei processi: l’escalation a SIGKILL non scattava mai; le esecuzioni batch terminano l’intero gruppo di processi alla disconnessione.
- CI e release: la CI ora esegue le soglie di parità del changelog, dei workflow e dell’i18n; permessi dei workflow con privilegi minimi; segreti di produzione solo nell’ambiente
production; il pacchetto npm ora includedocs/help/. - Test: nessun test tocca più la rete né il vero progetto genitore (un
CAREER_OPS_ROOTesplicito viene sempre rispettato); gli algoritmi copiati sono stati sostituiti dal codice di produzione.
Note
- Test 4056 → 4404, browser 118. Baseline di copertura del server: righe 96,7 %, rami 86,3 % (
scripts/coverage-baseline.json). - Le specifiche ora si trovano in un’unica cartella,
docs/sdd/specs/; il piano del programma èdocs/sdd/PLAN.md, la matrice invariante→sogliadocs/sdd/TRACEABILITY.md. - Nota per l’operatore: dietro un reverse proxy imposta
ALLOWED_HOSTS=<host pubblico>; un URL base di Ollama/Hermes su un host pubblico ora viene rifiutato.
[1.240.0] — 2026-10-06
Parità con il genitore — career-ops main @ 62905981 (VERSION 1.35.0, 177 commit upstream da b39931e). Sei nuove fonti — 103 → 109 (104 EN + 5 RU) —, correzioni rispecchiate in otto provider esistenti e il formato checklist della pipeline, contributo di @bullitt186.
Aggiunto
- Sei nuove fonti — 103 → 109 (104 EN + 5 RU), 98 → 104 adattatori EN.
- ADP Workforce Now (
workforcenow.adp.comconcid+ccId) — paginazione$skipin base alle righe effettivamente restituite; una risposta senza néjobRequisitionsnémetagenera un errore invece di passare per un board vuoto; arricchimento dei dettagli opt-in (adpWorkforcenow.fetchDetails, default 25, massimo 100). - Gupy (
employability-portal.gupy.io) — scansione per parole chiave sull’intero board, dalla più recente con arresto anticipato susince_days;workplaceTypeviene letto come la stringa singolare in pt-BR, così le offerte ibride non si perdono; le pagine carriera riservate vengono scartate. - JazzHR (
*.applytojob.com) — parser delle card con ripiego su ancore nude, così un restyling parziale produce comunque offerte; link di annunci con zero card analizzate generano un errore, un board davvero vuoto restituisce[]. - Startup Jobs (
startup.jobs/feeds/jobs) — RSS, una sola richiesta; un corpo malformato, HTML o troncato genera un errore invece di restituire[](la correzione del genitore dopo la revisione post-merge,08fe5d06); CDATA e commenti vengono mascherati prima dell’associazione delle voci. - Taleo (
<tenant>.taleo.net,tre.taleo.netrifiutato) — GET della shell, poi POSTsearchjobs; una shell senza id del portale genera un errore come board privato; colonne riconosciute dall’intestazione. - UKG Pro / UltiPro (
recruiting[N].ultipro.{com,ca}) —Top=50+Skip,totalCountmai considerato come condizione di arresto, tetto di 100 pagine (limitato a 1500), pagine di dettaglio opt-in analizzate con una scansione delle parentesi graffe attenta alle stringhe.
- ADP Workforce Now (
Corretto
- Pipeline nel formato checklist del genitore — il career-ops upstream scrive
data/pipeline.mdcome sezioni## Pending/## Processeddi righe- [ ] url | …, che la UI leggeva come 0 in sospeso e a cui scriveva aggiungendo un blocco di codice delimitato in fondo al file, dove il genitore non guarda mai.parsePipeline,addPipelineUrleremovePipelineUrlora leggono e scrivono quella forma (deduplica anche rispetto alle righe già elaborate; compenso come segmento etichettatonote: comp …); il formato a blocco delimitato è invariato. Contributo di @bullitt186 (#380). - Paese integrato nella località — Ashby (principale
address.postalAddress), Breezy (country.name) e Recruitee (locationpiatto) aggiungono il paese a meno che il nome non lo contenga già come parola intera, così un ruolo remoto con sede principale nel Regno Unito e secondaria negli Stati Uniti non viene più scartato da un filtro di località. - MokaHR — l’host regionale
hire-r1.mokahr.comè accettato (la lista di consentiti è esattamenteapp.mokahr.com+hire-r1.mokahr.com), il POST all’API e i link delle offerte usano l’host proprio del board, e unsuccess:falsein-band ora genera un errore invece di passare per un board vuoto. - Avature — le card
article--jobsvengono analizzate (titolo dall’ancora dell’intestazione, località dallo spanicon-address). - SAP SuccessFactors — un URL di ricerca salvata
/go/<Category>/<id>[/<offset>]/viene risolto alla radice del tenant; prima raggiungeva…/tile-search-results/, che risponde 200 con zero tile. - Deutsche Bahn — db.jobs svuota qualsiasi pagina di risultati che contiene un’offerta che non sa rendere; la scansione ora ordina per
pubExternalDate_tdtcon 1000 per pagina (massimo 5 pagine), si ferma sul conteggio grezzo delle ancore dei risultati e genera un errore se manca l’intestazione dei risultati o ci sono link di offerte non analizzati, invece di restituire un board troncato. - HigherEdJobs — una pagina di verifica Incapsula servita con HTTP 200 veniva analizzata come zero voci e passava per un board vuoto; ora genera un errore.
- Un test leggeva il vero progetto genitore su una macchina di sviluppo —
tests/reports-list-cache.test.mjsimpostavaCAREER_OPS_ROOTsu una cartella temporanea senzacv.md/portals.yml, chepaths.mjsignora, quindi in locale contava i report del genitore (6 invece di 3) mentre la CI, senza il genitore accanto, passava.
Note
- Test 3782 → 4056. Nuove suite:
sources-adp-workforcenow,sources-gupy,sources-jazzhr,sources-startup-jobs,sources-taleo,sources-ultipro,sources-location-country-fold,sources-deutschebahn-blanked-page,sources-higheredjobs-challenge-page, piùparsers-pipeline-checklistdalla #380. - Genitore: il fork è stato portato all’
mainupstream (144 → 0 commit indietro) con le quattro divergenze dell’ADR-0002 verificate intatte; il ripristino upstream delLOCATIONISH_REcirillico non è arrivato. - Già rilasciati nella v1.239.0, non ripetuti: AppliTrack, NEOGOV, Red Rover, SchoolSpring, OCC Mundial, board embed di Greenhouse, Workday
myworkdaysite.com. - Non portato, con le relative motivazioni: egress con proxy fidato (
CAREER_OPS_TRUST_PROXY_EGRESS,_http.mjs/_ip-guard.mjs/_dns-cache.mjs) — web-ui scansiona tramite il proprio trasportohttp-json.mjs; il registro delle colonne discan-history.tsve la deduplica delle richieste sensibile alla lingua — web-ui legge le posizioni 1–12, che non si sono spostate, e deduplica per URL; SmartRecruitersrequisitionId/language— nessun consumatore nella forma delle offerte di web-ui; GemisoCountry→ nome del paese — richiede la tabella alpha-3 da 249 voci del genitore, rimandato; modifiche solo CLI (modalità, updater, doctor, payload PDF/ATS, script del tracker, dashboard Go, modalità Singapore).
[1.239.4] — 2026-09-29
Patch dalla regressione dal vivo della v1.239.3.
Corretto
- Le valutazioni in coreano e cinese tradizionale segnalavano la mancanza del riepilogo del punteggio. Quei report arrivano a 10–11 mila caratteri e il riepilogo viene per ultimo, quindi finiva oltre il limite di 8192 token di output. Le valutazioni (e la pipeline automatica) ora consentono 16384 token e, se un fornitore taglia comunque la risposta, il primo avviso lo dice invece di elencare i blocchi mancanti.
- Il controllo dal vivo considerava non tedesca una breve risposta in tedesco (3 parole vuote tedesche, nessuna inglese). Una risposta a elenco può contenerne poche; senza alcuna parola inglese, ora ne bastano tre.
- Test 3778 → 3782.
Note
- Le route di ricerca approfondita e delle modalità mantengono il limite di 8192 token: le loro risposte non lo raggiungono nelle esecuzioni dal vivo.
[1.239.3] — 2026-09-29
Patch dalla regressione dal vivo della v1.239.2.
Corretto
- Le valutazioni in arabo fallivano ancora il controllo A–G dopo che la v1.239.2 aveva corretto giapponese e hindi. Uno scheletro mascherato dei titoli (nessun testo del report nel log) ha mostrato il motivo: il report arabo scrive la lettera di blocco in arabo, in ordine abjad, qualunque cosa chieda il prompt. Il controllo ora accetta أ ب ج د هـ و ز come A–G quando la lettera è isolata all’inizio di un titolo, come già accettava i sosia cirillici; le stesse lettere dentro una parola araba non contano.
- Il controllo dal vivo stampa questo scheletro mascherato quando il controllo A–G fallisce, così il prossimo caso si diagnostica dal log.
- Test 3776 → 3778.
Note
- La nota della v1.239.2 diceva che il controllo non sarebbe stato allentato; per l’arabo ora lo è, perché tre esecuzioni dal vivo hanno mostrato che il solo prompt non mantiene la lettera latina.
[1.239.2] — 2026-09-29
Patch dalla regressione dal vivo della v1.239.1.
Corretto
- Una valutazione in arabo traduceva le lettere di blocco e una in giapponese ometteva il riepilogo del punteggio. La v1.239.1 ripeteva solo la lingua nell’ultima riga del prompt. Ora quella riga dice anche che la lettera latina A–G di ogni titolo e il blocco
---SCORE_SUMMARY---(marcatori e chiavi in inglese) non si traducono, e chiede la scrittura propria della lingua. - Test 3775 → 3776.
Note
- Il controllo del report non viene allentato per accettare lettere o marcatori tradotti: è il prompt a mantenere stabile il formato.
[1.239.1] — 2026-09-29
Patch dalla regressione dal vivo della v1.239.0.
Corretto
- Le valutazioni in giapponese e ucraino arrivavano quasi tutte in inglese. L’istruzione sulla lingua era in cima al prompt, prima di decine di kilobyte di file di progetto e annuncio in inglese. Ora il prompt ripete la lingua nell’ultima riga.
- Il controllo dal vivo segnalava una scansione fallita quando il server ne stava già eseguendo un’altra (la scansione oraria, ripetuta subito dopo il deploy). Che il server rifiuti una seconda scansione è corretto; il controllo ora attende e riprova, fino a 30 minuti.
- Test 3774 → 3775.
Note
- La scansione singola del server non viene allentata: una seconda scansione è ancora rifiutata con
SCAN_BUSY. È cambiato solo il controllo.
[1.239.0] — 2026-09-29
Parità con career-ops @ b39931e: cinque nuove fonti e tre correzioni replicate.
Aggiunto
- Cinque nuove fonti (98 → 103; adattatori in inglese 93 → 98): AppliTrack (
applitrack.com/<distretto>), NEOGOV (SchoolJobs / GovernmentJobs), Red Rover (jobs.redroverk12.com/org/<id>) e SchoolSpring (<distretto>.schoolspring.com) per le assunzioni nelle scuole e nel settore pubblico, più OCC Mundial, il più grande sito di lavoro del Messico (provider: occesplicito con query di ricerca). Ognuna è vincolata al proprio host, solo HTTPS, e rifiuta i reindirizzamenti. - Test 3500 → 3774.
Corretto
- Le bacheche Greenhouse incorporate (
job-boards.greenhouse.io/embed/job_board?for=<bacheca>) puntavano a una bacheca chiamata “embed” e fallivano; ora la bacheca viene da?for=, e solo su un vero host Greenhouse. - Le bacheche Workday su
myworkdaysite.com(/recruiting/<tenant>/<sito>) non venivano riconosciute; ora vengono scansionate, con i link degli annunci sullo stesso host. - Un nome di report con parentesi (
042-acme-(berlin)-….md) veniva tagliato alla prima)nel tracker; ora il nome resta intero.
Note
- Le modifiche del padre solo per la CLI non vengono portate — modelli PDF e CV, lo scaffolder, l’esecutore batch, l’importazione di career-profile, la copertura delle parole chiave ATS, le traduzioni delle modalità colloquio, l’host CLI
pi: web-ui non li esegue. - Aprire un report del genere dal tracker richiede ancora che la route dei report accetti le parentesi nel nome del file; quella route non cambia in questa release.
[1.238.3] — 2026-09-27
Le pagine restano reattive durante una scansione e i log della QA remota non rivelano più l’host di produzione.
Corretto
- Dashboard e report impiegavano secondi a caricare e andavano in timeout durante una scansione.
/api/reportse/api/dashboardleggevano e analizzavano tutti i report a ogni richiesta — circa 3,7 s sul server di produzione, bloccando ogni altra richiesta; con una scansione in corso, i caricamenti arrivavano al timeout di 30 s. Ora le intestazioni analizzate sono in cache per file e si aggiornano quando il file cambia, quindi elencare costa un controllo per report (500 report: 430 ms → 3 ms in locale). - Un rilievo della QA remota poteva stampare parte dell’host di produzione in un log pubblico. GitHub oscura un segreto solo quando compare per intero, e un messaggio tagliato a 180 caratteri finiva a metà dell’host. Ora rilievi e report interi vengono oscurati prima del taglio.
- Ogni valutazione con l’IA veniva segnalata come malformata. Il prompt non chiedeva mai il blocco
---SCORE_SUMMARY---richiesto dal controllo del report (gli script di valutazione del progetto padre lo chiedono), e un report tradotto traduceva anche la parola “Block”, così il controllo non trovava le sezioni. Ora il prompt chiede il riepilogo e la lettera del blocco in ogni titolo, il controllo accetta una parola tradotta prima della lettera e il blocco di riepilogo viene rimosso prima che il report venga mostrato o salvato.
Aggiunto
- Test 3488 → 3500.
Note
- Lo scanner gira ancora dentro il processo del server; questa release rimuove l’altro grande blocco ma non sposta la scansione in un worker.
[1.238.2] — 2026-09-27
Correzione dei 502 che il server di produzione mostrava ancora durante la scansione oraria.
Corretto
- Pagine e risorse restituivano 502 durante una scansione. Node chiude una connessione keep-alive inattiva dopo 5 s; Caddy riusa le connessioni inattive fino a 2 minuti. Mentre una scansione nel processo teneva occupato il server, Node chiudeva socket su cui Caddy aveva appena inviato una richiesta, e Caddy rispondeva 502 connection reset by peer (62 volte in una scansione, con il server sano). Ora il server mantiene le connessioni inattive per 125 s — più del proxy — quindi chiude sempre prima il proxy.
Aggiunto
deploy.yml→verifymostra perché il proxy ha risposto 502 (tipi di errore, indirizzi mascherati), i 502 al minuto dall’ultimo riavvio e l’esito della scansione oraria.release.ymlpuò creare un tag mancante quando viene avviato a mano, così si può pubblicare una release dove i tag non possono essere inviati.- Test 3486 → 3488.
Note
- La scansione gira ancora dentro il processo del server e può tenerlo occupato per secondi; spostarla in un worker non fa parte di questa patch.
[1.238.1] — 2026-09-27
Patch di stabilità dopo la prima regressione dal vivo del server di produzione: una scansione alla volta e correzioni del sito.
Corretto
- Due scansioni insieme mandavano in crash il server. Ogni
GET /api/stream/scaneseguiva lo scanner completo nel processo del server senza protezione; una scansione avviata dalla UI si è sovrapposta al timer orario e il processo è morto con JavaScript heap out of memory (due volte, con errori 502 nel frattempo). Ora una scansione alla volta: una seconda richiesta riceve unerrorSSE con codiceSCAN_BUSYe non parte nulla. Il workflow di deploy alza anche il limite di memoria del server (heap_mb, predefinito 448 MB). /helpe/changelogdi cvstart.org scorrevano di lato sui telefoni (e/changelogja/zh anche su desktop): il codice lungo e l’indice ora vanno a capo.- Mancava l’hindi nelle alternative di lingua della sitemap; il controllo i18n del sito ora confronta la sitemap con il registro delle lingue.
- La Guida §19 chiedeva di aggiungere una chiave a «tutti e 8» i file di lingua — sono 17.
Aggiunto
- Regressione remota (
.github/workflows/remote-qa.yml): ogni percorso × ogni lingua dell’app distribuita (sola lettura), ogni pagina × lingua × telefono/desktop di cvstart.org, ogni link dei README e della wiki e, su richiesta, una scansione reale e chiamate IA dal vivo in ogni lingua. - Workflow di deploy del server (
.github/workflows/deploy.yml):recon,deploy(con rollback esatto) everify(smoke test e report di stabilità). - Test 3481 → 3486.
Note
- Il consumo di memoria dello scanner in sé non viene ridotto in questa versione: la protezione impedisce due scansioni insieme e il drop-in alza il tetto. Profilare una vera scansione di 98 fonti richiede la rete di produzione.
- Il timer orario
career-ops-scannon viene modificato;reconora mostra quali comandi esegue.
[1.238.0] — 2026-09-27
Parità con il genitore — career-ops main @ 993085ce (VERSION 1.34.0, 182 commit da de2224a9). Quattro nuove fonti, dieci correzioni di provider rispecchiate, una falla SSRF a livello di scanner chiusa, e un suggerimento gratuito di aderenza del titolo sui risultati di scan.
Aggiunto
- Quattro nuove fonti — 94 → 98 (93 EN + 5 RU), 89 → 93 adattatori EN.
- Eploy (
provider: eploy) — legge la sitemap/live-jobs.xmldel tenant su qualsiasi host brandizzato (solo HTTPS; letterali IP,localhost,*.local,*.internale host a etichetta singola vengono rifiutati). Una risposta che non è una sitemap (una pagina di login) genera un errore invece di passare come zero offerte. Le pagine di dettaglio sono opt-in (eploy: { fetchDetails, detailLimit }). - HiringRoom (
*.hiringroom.com) — prima JSON-LD/portal/jobs, poi le card legacy di/jobscome fallback; gli URL di annuncio inhttp://nel payload vengono ricostruiti in HTTPS. - PeopleSoft Candidate Gateway — rilevato dal percorso
/psc/{site}/EMPLOYEE/HRMS/c/HRS_HRAM_FL.HRS_CG_SEARCH_FL.GBLsu qualsiasi host brandizzato. Segue da sé i redirect di sessione same-origin di PeopleSoft (massimo 10 hop, ogni hop riancorato all’origine del tenant e controllato contro indirizzi privati), riproduce il modulo “carica altro” e marca una scansione parziale (peoplesoftIncomplete) invece di segnalarla come completa. - PrevueAPS (
*.prevueaps.ca) — risolve l’id del sito dalla pagina delle offerte, poi legge l’API JSON.
- Eploy (
- Suggerimento di aderenza del titolo sulle righe di
#/scan. Ogni risultato porta un chip approssimativo di aderenza forte / correlata / debole che confronta il titolo dell’annuncio con i ruoli target inconfig/profile.yml(target_roles.primary+ nomi di archetipo). È una stima gratuita basata su parole chiave: non filtra, non riordina né cambia mai i conteggi, e il tooltip lo dichiara. Calcolato quando i risultati vengono serviti, quindi una modifica al profilo si riflette al refresh successivo. Tradotto ×17. Rispecchia la #3261 del genitore.
Corretto
- Sicurezza — la protezione contro il DNS-rebinding non veniva mai eseguita in una scansione reale.
http-json.mjscontrollava gli indirizzi risolti solo quando riceveva ilfetchglobale nudo, ma entrambi gli scanner iniettanomakeTimeoutFetch(), quindi ogni scansione saltava la protezione. Una fonte puntata su un hostname che risolveva a10.xo127.0.0.1veniva effettivamente recuperata. La protezione ora viene eseguita all’interno dello stessomakeTimeoutFetch, messa in gara con il timeout della scansione così che un resolver bloccato non possa bloccare una scansione. Trovato nella revisione di questa release. - Sicurezza — gli indirizzi IPv6 che incorporano un indirizzo IPv4 non venivano trattati come privati.
http://[::ffff:127.0.0.1]/(che il parser di URL riscrive come[::ffff:7f00:1]) superavaisValidJobUrl. Sia la forma puntata che quella esadecimale di::ffff:a.b.c.d/::a.b.c.dora vengono classificate in base all’indirizzo incorporato. Dal_ip-guard.mjsdel genitore. - Rippling è passato all’API board v2 (
ats.rippling.com/api/v2/board/<slug>/jobs), con paginazione (10 pagine di default, massimo 50), un blocco host sui link degli annunci e validazione dello slug. Un URL API incollato ora produce il suo slug reale invece diapi. - Recruitee — le offerte multi-sede elencano ogni sede (
A · B), gli annunci demo(Sample)e le righe senza titolo vengono scartati, e un’offerta viene mantenuta quando almeno uno dei suoi due URL è valido. - Pinpoint — gli annunci demo seminati (il video demo duplicato) vengono scartati.
- Canali Telegram — una prima riga con più
|non viene più letta comeTitolo | Azienda, e vengono riconosciute più parole di ruolo. IlLOCATIONISH_REdel fork con supporto cirillico resta intatto (ADR-0002). - Workday — un 422/401/403 sulla prima pagina viene sondato una volta; la pagina di manutenzione o il redirect di outage di Workday marca il board come morto (messo in quarantena in modalità strict). Le richieste CXS e di dettaglio ora rifiutano i redirect.
- BambooHR — un
/careers/listreindirizzato viene smistato tramite il feed embed pubblico: vuoto significa un board attivo senza posizioni aperte, qualsiasi altra cosa è un board morto. - Heckler & Koch / Rheinmetall — un
heckler-koch.com/rheinmetall.comnudo viene riscritto inwww.così che il trasporto senza redirect lo raggiunga comunque. Ashby rifiuta i redirect. beesite scandisce le pagine con ildelay()annullabile. - I filtri sui titoli appiattiscono gli accenti su entrambi i lati (scanner EN e RU), quindi
Développeurviene intercettato da un negativodeveloppeur. - Il matching aziendale del cooldown comprende nomi non latini e forme societarie (marcatori CJK/coreani, İ turca puntata). I nomi CJK prima collassavano in una chiave vuota e non facevano mai match.
location_filter.strict: truefallisce in modo chiuso su una posizione vuota quando è impostata una lista di permessi o blocchi.- Liveness — nuovi controlli tramite API pubblica per gli embed Greenhouse (
gh_jid), Arbeitsagentur e We Work Remotely; SmartRecruiters ora decide in base al flagactivedell’annuncio. - PeopleSoft — un’azione di form relativa “carica altro” viene risolta accanto alla pagina di ricerca, non alla radice dell’origine.
Note
- Test 3210 → 3481. Nuove suite:
sources-eploy,sources-hiringroom,sources-peoplesoft,sources-prevueaps,title-fit,fetch-timeout-dns-guard,cooldown-company-match-unicode,liveness-api-more-rungs,ru-scanner-title-accent-fold. - Non portato, con le relative motivazioni: bordi di parola Unicode nel filtro di localizzazione (il filtro di web-ui è basato su sottostringa, quindi non c’è un bordo da correggere — un port completo e deliberato del filtro di localizzazione del genitore resta lavoro futuro); dedup delle richieste per stesso titolo (web-ui dedup solo per URL); avvisi di ripubblicazione degli aggregatori, ambito dei domini in blacklist, ricevute zero-non-verificato e la migrazione di
scan-runs.tsv(web-ui non ha nessuna di queste superfici); lo split per faccette e i tag di troncamento di Workday (web-ui recupera una pagina per board);enrichDatedi BambooHR (nessun hook di arricchimento data); il refactoring dello sleep di_http.mjsdel genitore su ~20 provider (web-ui usa già undelayannullabile condiviso); updater, doctor, PDF, tracker/merge-tracker,validate-profile,discover-new-companies, l’app Next.js inweb/, i bot GitHub — esclusivi della CLI o inoltrati altrove. - Mantenuto deliberatamente diverso dal genitore: Heckler & Koch / Rheinmetall restano solo HTTPS (nessun upgrade
http→https); un board Workday morto in una scansione normale restituisce comunque[]invece di far fallire la scansione.
[1.237.1] — 2026-09-22
Patch da una verifica QA esterna su v1.237.0. La correzione di Jobstreet è stata confermata dall’esterno per la prima volta: sugli host AU, NZ, HK e MY la vecchia forma /id/job/<id> restituisce un 404 pulito e la nuova /job/<id> raggiunge una rotta reale protetta da Cloudflare; su id.jobstreet.com vale il contrario. I link persi erano reali. Sono stati trovati due difetti, entrambi in ciò che v1.237.0 stesso aveva aggiunto.
Corretto
- L’etichetta del link al libro era localizzata, ma la sua destinazione no. Tutti i 17 locale della SPA e tutti i 17 locale di cvstart.org puntavano il link nel footer di Agentic Coding Design Patterns all’edizione inglese, mentre l’unica etichetta tradotta — russa, «Паттерны агентного кодинга» — prometteva un libro russo. Un nuovo applicatore
data-i18n-href(stessa forma didata-i18n-title, limitato a valori assolutihttps://) e una chiavefooter.patternsUrlinvianoruall’edizione russa e ogni altro locale a quella inglese. Il test di regressione verifica l’invariante in entrambe le direzioni: un titolo tradotto implica un URL localizzato. - SEEK sta migrando gli host, e il trasporto rifiuta i redirect.
www.seek.com.au→au.seek.comewww.seek.co.nz→nz.seek.com: le pagine di dettaglio fanno già 301, ed entrambe le coppie di host servono oggi l’API di ricerca v5 con 200. Poichéhttp-json.mjsimpostaredirect:'error'(una difesa SSRF che vale la pena mantenere), il giorno in cui il percorso dell’API iniziasse a reindirizzare, una voce AU/NZ sull’host vecchio fallirebbe — e con solo gli hostwww.in allowlist nessuno potrebbe migrare.au.seek.comenz.seek.comsono ora inALLOWED_JOBSTREET_HOSTS; gli host vecchi restano, quindi la configurazione esistente e i link già costruiti continuano a funzionare. I sosia (au.seek.com.evil.com,au-seek.com) sono ancora rifiutati. - Wiki: la riga Agentic Practices nella mappa della documentazione della Home era l’unica su dodici a non fare l’escape di
|dentro[[…|…]], quindi GitHub ha tagliato la cella — link morto, descrizione nascosta. Un solo carattere.
Note
- Conteggi invariati a 94 sorgenti (89 EN + 5 RU), 89 adattatori. Test 3201 → 3210 (+5 locale del link nel footer, +4 host SEEK).
- Non chiudibile dall’esterno, registrato invece che dichiarato: Oracle Cloud (Akamai ha bloccato entrambe le sonde REST), il pin Personio e
appendWorkType(serve una voce inportals.yml, e l’interfaccia non ha un modo per eliminarne una), e i casi di stringa di liveness (nessuna pagina controllabile; 40 sonde live hanno dato 12 live, 28 inconcludenti, 0 scadenze false). - Help ×17 ora elenca entrambe le forme di host per
AU-MaineNZ-Main. Struttura dei titoli invariata a 32 H2 / 122 H3.
[1.237.0] — 2026-09-21
Parità con il padre — career-ops main @ 93c4302b (VERSION 1.33.0, 75 commit tratti da career-ops-hq/main). Nessuna sorgente nuova questa volta: quattro correzioni replicate, e due di esse qui perdevano silenziosamente le offerte.
Corretto
Jobstreet / SEEK — ogni offerta australiana, neozelandese, singaporiana, malese e di Hong Kong puntava a una pagina morta. parseJobstreetItem costruiva l’URL di dettaglio come ${baseUrl}/id/job/${id} in modo incondizionato. Il segmento /id/ non è un percorso — è il prefisso di locale indonesiano, ed esiste solo sugli host indonesiani. Ogni altro host della piattaforma SEEK risponde 404 su /id/job/<id>. web-ui aveva già aggiunto www.seek.com.au, www.seek.co.nz, sg.jobstreet.com, my.jobstreet.com e hk.jobsdb.com a ALLOWED_JOBSTREET_HOSTS, quindi la scansione restituiva volentieri quelle offerte — ciascuna puntando a un 404. Il percorso ora è determinato dall’host: ID_LOCALE_HOSTS (id.jobstreet.com, www.jobstreet.co.id, jobstreet.co.id) mantiene /id/job/, tutto il resto ottiene /job/, e un origin non interpretabile ricade sul percorso comune invece di sollevare un’eccezione. Due dei nostri stessi test avevano codificato il difetto — asserivano hk.jobsdb.com/id/job/… e www.jobstreet.com/id/job/… — e sono stati corretti.
Oracle Cloud — una pagina corta a metà elenco terminava la scansione e lasciava cadere la coda della bacheca. La condizione di arresto era listLen === 0 || listLen < PAGE_SIZE, ma ORC restituisce meno righe del limite a metà elenco perché alcune righe vengono filtrate lato server. Misurato a monte su American Express: TotalJobsCount 454, pagine da 200 / 199 / 54 — il 199 veniva letto come la fine e le ultime 54 offerte, il 12% della bacheca, non venivano mai recuperate. Ora una pagina vuota termina sempre la scansione; quando il tenant riporta un totale, la paginazione continua finché offset + PAGE_SIZE >= total; solo un tenant senza un totale con cui verificare termina su una pagina corta. Questo è coerente con la convenzione più ampia secondo cui un’API «può restituire meno risultati del numero richiesto … anche se non alla fine della collezione» (Google AIP-158). hasMore resta deliberatamente ignorato — alcuni tenant restituiscono hasMore:false su ogni pagina di una bacheca da 7000 offerte. Con le pagine corte che non terminano più la scansione, MAX_PAGES è ora l’unico argine contro un tenant che riporta un totale fasullo, quindi è coperto da un proprio test.
Liveness — «This role is closed» non veniva riconosciuto, e una scadenza falsa è permanente. Il pattern era /this job (listing )?is closed/i, che non intercetta le formulazioni altrettanto comuni con role e position; su una bacheca a monte è stato misurato che 111 su 111 offerte incerte usavano «role». Ampliato a /this (?:job|role|position)(?: listing)? is closed\b(?!-)/i. Il finale \b(?!-) è una guardia per gli aggettivi composti: \b da solo respinge closedown ma corrisponde comunque a una prosa reale come “This role is closed-loop control of the platform”, ed è il lookahead a fermarlo.
Liveness — job expired ora si trova in un livello debole che non può prevalere su un pulsante Candidati visibile. HARD_EXPIRED_PATTERNS viene verificato prima del test sul controllo di candidatura, quindi qualsiasi cosa vi sia collocata prevale su un’offerta viva. /\bjob expired\b/i scattava per errore su quattro forme reali di pagine vive — una voce di un carosello «Similar jobs», un chip di filtro «Hide job expired», una FAQ nel footer, e la prosa closed-loop di cui sopra — perché il classificatore riceve il testo dell’intera pagina. Ora vive in SOFT_EXPIRED_PATTERNS, verificato dopo il controllo di candidatura e prima dell’euristica di pagina-elenco, restituendo expired_body_soft; un semplice banner «JOB EXPIRED» senza controllo di candidatura scade comunque. Questa direzione conta perché un expired falso viene scritto nella cronologia delle scansioni come skipped_expired e poi filtra via per deduplicazione un’offerta reale da ogni scansione successiva, indefinitamente, a meno che scan_history.recheck_after_days non sia impostato.
Aggiunto
Personio — un personio: <slug> esplicito fissa il tenant. Molte aziende incorporano la propria bacheca Personio come iframe su una pagina carriere personalizzata, quindi careers_url punta al dominio proprio dell’azienda mentre il feed vive su <slug>.jobs.personio.de; senza un pin quelle bacheche non risolvono a nulla. Lo slug è limitato nel set di caratteri da un’allowlist ancorata (/^[a-z0-9][a-z0-9-]{0,62}$/i) e l’URL risultante viene comunque instradato attraverso il parsing new URL() esistente, il controllo HTTPS e il filtro host PERSONIO_HOST_RE — il pin non è mai un modo per raggiungere un altro host. Uno slug che porta ., /, @, :, .., spazi, un - iniziale, o che supera il limite di 63 caratteri per etichetta DNS, ricade sul comportamento odierno di api/careers_url. A differenza del padre, la regex host di web-ui distingue tra maiuscole e minuscole, quindi il pin viene normalizzato tramite new URL() — AcmeGroup risolve in acmegroup.jobs.personio.de invece di costruire un host che la propria allowlist respingerebbe poi.
Jobstreet — appendWorkType opzionale aggiunge come suffisso al titolo i tipi di impiego dell’offerta, ad es. Strategy Consultant [Part time], così i filtri sul titolo e il triage possono vedere il tipo di impiego al momento della scansione. Disattivato di default; un workTypes mancante o vuoto non ha effetto.
Note
I conteggi di sorgenti e adattatori restano invariati: 94 sorgenti (89 EN + 5 RU), 89 adattatori EN. Questa release sposta solo il conteggio dei test, 3164 → 3201 (+13 jobstreet, +7 Oracle Cloud, +9 liveness, +8 Personio).
Il padre ha eliminato providers/telegram.mjs, consolidando su telegram-channel. web-ui registra entrambi come sorgenti visibili all’utente, quindi l’eliminazione non viene seguita: farebbe scendere il registro da 94 a 93 e romperebbe silenziosamente chiunque abbia configurato telegram. Da rivedere solo come una rimozione deliberata e annunciata.
jd-skill-gap.mjs ha ricevuto una forma non contratta dell’intestazione «what we are looking for» e una rimozione dei marcatori in grassetto. web-ui inoltra quello script invece di rispecchiarlo, quindi la correzione arriva con il padre distribuito e non richiede alcuna modifica qui.
Non portato, con le motivazioni: lib/latex-escape.mjs, browser-extract.mjs, openai-eval.mjs, update-system.mjs e lo scaffolder (web-ui non li rispecchia né li esegue in shell); la TUI della dashboard Go e il form di configurazione di web/ (superfici separate); doctor.mjs e analyze-patterns.mjs (relay in sola lettura il cui fail-soft assorbe la modifica).
La divergenza cirillica \p{L} del fork in providers/telegram-channel.mjs è sopravvissuta intatta al merge upstream ed è stata verificata byte per byte contro una copia pre-merge.
[1.236.0] — 2026-09-18
Parità con il padre — career-ops main @ 6a9c84c (VERSION 1.33.0, 128 commit). Due nuove sorgenti, e tre correzioni replicate — due delle quali qui perdevano silenziosamente le offerte.
Aggiunto
Due sorgenti per lo scanner, 92 → 94 (89 EN + 5 RU). Python.org Jobs (pythonorg) legge il feed pubblico della bacheca; l’attribuzione del datore di lavoro è obbligatoria, quindi una riga il cui titolo non nomina alcun datore di lavoro viene scartata invece di essere completata con il nome della voce del portale. Generalist World (generalist-world) esegue lo scraping delle schede della bacheca e fallisce rumorosamente su un markup che non riesce a interpretare — una scheda la cui forma è cambiata solleva un’eccezione invece di leggersi come una bacheca vuota, che è la modalità di fallimento che rende inaffidabile una sorgente ottenuta tramite scraping. Entrambe compaiono nel filtro Sorgente di #/scan, entrambe hanno l’host fissato (regex ancorata, solo HTTPS), ed entrambe sono guidate end-to-end da suite a trasporto fittizio (43 e 36 casi).
Corretto
Un ruolo Workday aperto in più città veniva scartato da un filtro sede. L’endpoint delle liste di Workday risponde a un’offerta multi-sede con un conteggio dove ogni altra offerta porta una sede: "53 Locations". Non è una sede, e buildLocationFilter confronta per sottostringa senza distinzione tra maiuscole e minuscole — quindi un ruolo aperto ad Austin e in altre 52 città non corrispondeva a nessuna voce allow: [austin] e veniva scartato, mentre lo stesso ruolo elencato singolarmente passava. Misurato a monte su tre tenant: 53 su 291 offerte erano segnaposto, quindi questo è il caso ordinario, non uno marginale. Le sedi reali dell’offerta esistono solo nel documento CXS di dettaglio, quindi ogni segnaposto ora costa una GET aggiuntiva verso di esso, con un tetto di 200 per annuncio — oltre il tetto il segnaposto viene lasciato visibile invece che falsificato, perché un’offerta non risolta non deve mai leggersi come risolta. Un dettaglio fallito o senza sede lascia l’offerta esattamente come restituita dalla lista: una "53 Locations" visibile è onesta su ciò che non si è potuto risolvere, mentre una sede svuotata passerebbe silenziosamente ogni filtro. La stessa GET porta anche startDate, quindi un’offerta arricchita viene datata esattamente invece che con la prosa relativa dell’endpoint delle liste ("Posted 30+ Days Ago" → undefined); tutto ciò che non è un semplice YYYY-MM-DD viene lasciato invariato invece di essere passato a Date.parse, il cui fallback non-ISO dipende dall’implementazione.
Un livello dichiarato non fa più collassare due requisizioni in un unico repost. "Insurance Specialist II" è la dichiarazione stessa del datore di lavoro che questa requisizione non è sorella dell’altra — una fascia retributiva, un ambito e un numero di requisizione diversi sotto un unico titolo di base. Il tokenizzatore non riusciva a vederlo: w.length > 3 scarta ogni numero romano fino a VIII e ogni cifra singola, quindi "Insurance Specialist" e "Insurance Specialist II" venivano tokenizzati in modo identico, ottenevano un rapporto di Jaccard perfetto, e il rilevatore di repost segnalava il secondo come una ripubblicazione del primo. role-matcher.mjs di web-ui non aveva alcuna gestione dei livelli, quindi questo porta l’intero meccanismo del padre: i livelli vengono estratti ripiegando romano e arabo su un unico numero ("Nurse II" e "Nurse 2" sono la stessa dichiarazione — e quella coppia prima differiva erroneamente), due livelli dichiarati devono concordare, e un livello presente solo su un lato è trattato come una riscrittura libera a meno che ciascun titolo non porti anche una parola che manca all’altro, il che costituisce due disaccordi e non una decorazione.
Una bacheca Avature fissata a un filtro percorreva comunque l’intera bacheca globale. L’interfaccia di ricerca di Avature genera parametri di faccetta opachi (?42386=[812132] è un filtro paese), e un operatore che fissa una bacheca ristretta in portals.yml si aspetta che la scansione percorra quella. resolveSearch ricostruiva l’URL come ${origin}${path} e scartava del tutto la query string — quindi ogni richiesta, prima inclusa, percorreva l’intera bacheca del tenant: più pagine, più richieste, e offerte che l’operatore aveva deliberatamente filtrato via. Le faccette ora accompagnano ogni richiesta paginata, con il nostro jobOffset applicato per ultimo, così che una faccetta omonima vagante non possa mai dirottare la paginazione.
Sicurezza
devalue 5.8.1 → 5.9.2 (in modo transitivo tramite astro), che chiude l’unico avviso Dependabot aperto — una denial of service su input malformato. Dipendenza solo del sito; nessun codice a runtime modificato.
Note
Non portato, ciascuno con la propria motivazione. Il padre ha dato a lever un timeout di 30s e a manfred un timeout di 25s contro il suo default condiviso di 10s — un no-port: il trasporto di scansione di web-ui consente già 60s (DEFAULT_SCAN_TIMEOUT_MS), quindi entrambe le bacheche hanno qui più margine di quanto la correzione conceda là. Anche il prefisso careers- raddoppiato di iCIMS non può verificarsi qui: web-ui prende l’origin del tenant direttamente da api/careers_url e non costruisce mai un host a partire da un nome di tenant; il suo nuovo percorso di fallback-host è una funzionalità per la configurazione più permissiva del padre, non la correzione di un difetto. deutschebahn e manfred hanno ottenuto il retry-on-transient-abort, che il tetto di 60s di web-ui rende molto meno urgente — annotato invece che portato, così un futuro timeout su una scansione lunga ha già una prima cosa nota da provare. Tutto il resto del delta di 128 commit è CLI, verificatore, dashboard o web/ (il frontend proprio del padre): liveness-browser, check-jd-archive, verify-ats/verify-portals/verify-cv-facts, update-system, set-status, audit-portals, discover-ats, scaffolder, test-all, manifest dei plugin.
Divergenze del fork riverificate dopo il pull: la correzione cirillica \p{L} in providers/telegram-channel.mjs, il providers/telegram.mjs proprio del fork, hermes in web/src/lib/clis.ts. L’upstream career-ops-hq/main è completamente unito (0 commit mancanti). 3066 → 3164 test; la guida ora dichiara 94 adattatori (89 EN + 5 RU) in tutte e 17 le lingue, e la struttura dei titoli resta invariata a 32 H2 / 122 H3.
career-ops-ui v1.267.1 · page 2/10 · mirror of the project changelog for cvstart.org