Changelog

GitHub

Todas as mudanças relevantes, versão por versão. O mesmo arquivo é publicado no repositório e traduzido nos 19 idiomas.

[1.198.0] — 2026-08-15

Adicionado — as retentativas de varredura agora usam recuo exponencial, jitter e respeitam o Retry-After de um limitador de taxa.

Adicionado

  • Quando um quadro de vagas limita a taxa ou falha brevemente (HTTP 429 / 5xx) durante a varredura, a retentativa agora espera com recuo exponencial + jitter em vez de um atraso curto fixo — assim um quadro ocupado não é martelado no mesmo ritmo e retentativas concorrentes não colidem em sincronia. Um Retry-After do quadro é respeitado (mas limitado, para que um Retry-After: 86400 hostil não trave a varredura inteira). Erros permanentes (404, redirecionamentos recusados) continuam falhando de imediato — sem mudança.

Notas

  • Novos parseRetryAfterMs() e o puro computeRetryDelayMs() em server/lib/http-json.mjs; fetchJson agora captura .retryAfter em uma resposta não-ok e fetchJsonWithRetry recebe um maxDelayMs opcional (padrão 8000). tests/http-json.test.mjs (+9). Conjunto: 2536.

[1.197.0] — 2026-08-14

Adicionado — acompanhe um quadro de vagas de fundo Getro apenas pela careers_url; o id da coleção se resolve sozinho.

Adicionado

  • Um quadro Getro acompanhado (b2venture, Earlybird, Point Nine, …) não precisa mais de um getro_collection numérico procurado à mão. Informe a própria careers_url do quadro e o id se resolve sozinho a partir dessa página na primeira varredura — um único GET seguro contra SSRF lê o network.id numérico direto dos dados embutidos da página. Um getro_collection explícito ainda prevalece e pula a busca por completo.

Notas

  • Novos httpsCareersUrl(), extractCollectionId() e o assíncrono resolveCollectionId() em server/lib/sources/getro.mjs; a página do quadro é buscada via safeGet (DNS fixado, tamanho limitado), e o id resolvido continua preso ao host api.getro.com por assertGetroUrl. O adaptador agora corresponde a uma entrada provider: getro que carrega uma careers_url https mesmo sem id. tests/sources-getro.test.mjs (+8). Conjunto: 2527.

[1.196.0] — 2026-08-14

Corrigido (segurança) — o adaptador do Workday valida um endpoint api pelo hostname, não por substring.

Corrigido

  • Um valor api: do Workday no portals.yml agora só é aceito quando seu hostname é myworkdayjobs.com (ou um subdomínio .myworkdayjobs.com). A verificação antiga era uma correspondência de substring, então qualquer URL que apenas contivesse a string — ex. https://example.com/?x=myworkdayjobs.com — passava e seria usada como endpoint. Endpoints reais do Workday não são afetados. (Reportado pelo CodeQL, #443.)

Notas

  • Novo isWorkdayApi() analisa a URL e verifica o host em server/lib/portals/adapters/workday.mjs. tests/workday-adapter-endpoint.test.mjs (+1). Suíte: 2522.

[1.195.0] — 2026-08-14

Desempenho (scanner) — a detecção de reposts continua rápida em históricos de scan grandes.

Desempenho

  • A detecção de vagas duplicadas não degrada mais para O(N²) num scan-history.tsv grande. O agrupamento de títulos por empresa era um laço aninhado que pagava um roleFuzzyMatch completo em cada par; agora é um índice invertido — agrupa linhas por título exato numa passada, depois faz correspondência difusa só sobre buckets DISTINTOS que compartilham um token discriminante (não base). A saída é idêntica — os mesmos clusters de repost — comprovado por um teste diferencial contra o algoritmo antigo em 200+ históricos aleatórios.

Notas

  • groupRowsByTitle em server/lib/detect-reposts.mjs (exportado para o teste diferencial). tests/detect-reposts-grouping.test.mjs (+2). Suíte: 2521.

[1.194.0] — 2026-08-14

Corrigido (scanner) — páginas de vagas do Workday com URL de segmento único agora são escaneadas corretamente.

Corrigido

  • O adaptador do Workday agora analisa URLs de vagas cujo path é um único segmento — ex. https://parsons.wd5.myworkdayjobs.com/Search, .../KBR_Careers, .../Careers. Antes, o site caía para External, então o adaptador batia no endpoint CXS errado e uma sondagem podia parecer saudável sem retornar nada. Agora usa o primeiro segmento não vazio do path como site (descartando um prefixo de idioma como en-US); o caso documentado /en-US/External não muda. (Reportado em #255.)

Notas

  • Análise estrutural do path em server/lib/portals/adapters/workday.mjs. tests/workday-adapter-endpoint.test.mjs (+7). Suíte: 2519.

[1.193.0] — 2026-08-14

Adicionado (estatísticas) — uma aba “Silêncio após a entrevista” que revela entrevistas que merecem um lembrete.

Adicionado

  • Uma aba Silêncio após a entrevista em #/stats: entrevistas que ficaram em silêncio além de uma janela de cortesia (30 dias por padrão), unindo suas entrevistas ativas e o tracker — com há quanto cada uma está em silêncio, a data da última entrevista e o motivo. Uma lista suave de lembrete/fechamento; apenas sugestões, nunca afirma rejeição. Sem tokens.

Notas

  • Nova rota GET /api/stats/rejection-latency (fail-soft {available:false}). tests/stats-rejection-latency-route.test.mjs (+2). +10 chaves i18n ×17; help-hint de #/stats de 7→8 abas. Suíte: 2510.

[1.192.0] — 2026-08-14

Adicionado (cv-studio) — um controle “Verifique os fatos do seu CV” que pega números que você nunca teve.

Adicionado

  • Um cartão Verifique os fatos do seu CV em #/cv-studio: cole um CV ou carta adaptados e verifique cada métrica e fato afirmado contra seu CV, perfil e two-pager reais. Você recebe um veredicto pass / warn / block mais as métricas inventadas, fatos sem respaldo e frases proibidas / de aviso exatas. Sem LLM; nada é escrito.

Notas

  • Nova rota POST /api/cv-studio/verify-facts: escreve o texto em um arquivo temporário descartável e roda verify-cv-facts.mjs, confiando no veredicto JSON mesmo que o script saia com 1 num block. tests/cv-studio-verify-facts-route.test.mjs (+4). +15 chaves i18n ×17. Suíte: 2508.

[1.191.0] — 2026-08-14

Adicionado (estatísticas) — uma aba “O que aprender a seguir” que ordena as habilidades a aprender primeiro.

Adicionado

  • Uma aba O que aprender a seguir em #/stats: um resumo de todo o tracker — as habilidades que mais afundaram uma baixa aderência, ponderadas e em níveis Critical / High / Medium — mais as que seu CV/perfil já cobre. Somente leitura, apenas sugestões; sem tokens.

Notas

  • Nova rota GET /api/stats/upskill (com campo { error } quando há poucos dados; fail-soft {available:false}). tests/stats-upskill-route.test.mjs (+3). +15 chaves i18n ×17. Suíte: 2504.

[1.190.0] — 2026-08-14

Adicionado (tracker) — um painel “Histórico da empresa” que mostra quais empresas realmente respondem.

Adicionado

  • Um cartão Histórico da empresa em #/tracker: escolha uma empresa e veja evidência somente-leitura — quão responsiva ela foi com você (em silêncio com você / misto / respondeu antes) e se a mesma vaga é republicada — juntando seu tracker, follow-ups e histórico de scan. Sem tokens; o scanner do projeto nunca é chamado.

Notas

  • Nova rota GET /api/stats/company-history[?company=] (fail-soft {available:false}). tests/stats-company-history-route.test.mjs (+3). +18 chaves i18n ×17. Suíte: 2501.

[1.189.0] — 2026-08-14

Corrigido (scanner) — níveis de senioridade em algarismos romanos agora contam também em títulos não latinos.

Corrigido

  • O classificador de nível por trás de skip_tiers agora lê um sufixo de nível em algarismos romanos (I / II / III / IV / V) após a palavra do cargo em qualquer alfabeto — “Инженер III”, “エンジニア I”, “Ingénieur IV” — não apenas após palavras ASCII. Antes, um número de nível após uma palavra não latina era ignorado e a vaga caía para mid, então skip_tiers: [senior] ou [entry] não as filtrava.

Notas

  • Lookbehind independente de alfabeto em server/lib/classify-tier.mjs; removido um matcher Sr. duplicado morto. tests/classify-tier.test.mjs (+1). Suíte: 2498.

[1.188.0] — 2026-08-14

Corrigido (UI) — os botões de ação principais não ficam mais colados no subtítulo da página.

Corrigido

  • A linha de ação / controle principal em Resumo semanal de entrevistas, Empresas financiadas, Portais, Plano de carreira e Orientação de carreira agora tem uma margem superior adequada, então o botão respira abaixo do subtítulo em vez de encostar nele.

Notas

  • Guarda de regressão tests/lead-row-top-margin.test.mjs (+5). Suíte: 2497.

[1.187.0] — 2026-08-14

Corrigido (scanner) — a opção skip_tiers voltou a funcionar: as vagas que você pediu para pular por senioridade são descartadas.

Corrigido

  • Uma lista skip_tiers: em portals.yml (ex.: skip_tiers: [intern, entry]) agora é respeitada no scan. O título de cada vaga é classificado em um nível (intern / entry / mid / senior) e descartado se o nível estiver na sua lista. Antes o scan aplicava os filtros de título / localização / conteúdo / confiança mas sem filtro de nível, então skip_tiers era ignorado silenciosamente. Títulos sem palavra de nível caem em mid (então skip_tiers: [mid] também descarta a maioria das vagas comuns), e o classificador lê a palavra de nível MAIS À ESQUERDA.

Notas

  • Novo server/lib/classify-tier.mjs puro (classifyTier + buildTierFilter), ligado às cadeias de filtros dos scanners EN e RU. tests/classify-tier.test.mjs (+7). Suíte: 2492.

[1.186.0] — 2026-08-14

Adicionado (CV Studio) — um painel “Lacuna de habilidades”: quais das habilidades exigidas de uma vaga seu CV cita, insinua ou está faltando.

Adicionado

  • Um novo painel Lacuna de habilidades no CV Studio. Escolha uma descrição de vaga salva e ele classifica cada habilidade exigida em citada no seu CV, insinuada no seu CV ou faltando — comparação de palavras sem IA, nada é gravado. Uma nota de baixa confiança aparece quando a vaga não tinha uma seção clara de requisitos.

Notas

  • Novo GET /api/jds/:name/skill-gap (o nome da vaga é saneado e confirmado sob jds/ antes de virar argumento; degrada suave para {available:false} sem o script). +13 chaves i18n ×17. Testes: tests/jds-skill-gap-route.test.mjs (+4, incl. rejeição de path-traversal). Suíte: 2485.

[1.185.0] — 2026-08-14

Adicionado (estatísticas) — uma aba “Funil e velocidade”: como seu funil se compara ao mercado e com que rapidez você avança entre etapas.

Adicionado

  • Uma nova aba Funil e velocidade em Estatísticas mostra suas taxas de resposta e entrevista ao lado de faixas de referência do mercado (com as ressalvas de amostra pequena e viés de seleção mantidas), uma lista de espera de candidaturas em andamento além da janela típica de primeira resposta, e dias medianos por etapa (Candidatado → Respondido → Entrevista → Oferta), com linhas lentas censuradas à direita para não enviesar as medianas. Somente leitura e sem tokens; lê apenas o seu próprio rastreador.

Notas

  • Novo GET /api/stats/funnel (degrada suave para {available:false} sem o script). +18 chaves i18n ×17. Testes: tests/stats-funnel-route.test.mjs (+2). Suíte: 2481.

[1.184.0] — 2026-08-14

Corrigido (UI) — os blocos de ação rápida do Painel agora se alinham em uma grade uniforme.

Corrigido

  • No Painel (Centro de comando), um grupo de 3 blocos ficava mais largo que um de 4, deixando as seções com a borda direita irregular. Agora cada grupo usa colunas de largura igual (4 em tela larga, caindo para 3 / 2 / 1 conforme a janela estreita), então todos os blocos têm o mesmo tamanho e suas bordas direitas se alinham.

Notas

  • Apenas CSS (.qa-grid: repeat(N, minmax(0,1fr)) fixo em vez de auto-fill). Protegido por tests/dashboard-grid-align.test.mjs (+2). Suíte: 2479.

[1.183.0] — 2026-08-14

Adicionado (scanner) — detecção de duplicados mais inteligente: a mesma vaga re-listada com um link de rastreamento não aparece mais duas vezes.

Adicionado

  • O scanner agora reconhece uma vaga por uma chave de URL canônica, então a mesma vaga re-listada com um parâmetro de rastreamento (?utm_…, gclid, …), por http vs https, ou com barra final / #fragmento é tratada como a única vaga que é — sem linha duplicada nos resultados ou no pipeline, e sem avaliação desperdiçada numa vaga já vista. Vagas realmente diferentes (um id funcional mantido como gh_jid) continuam contando separadamente.

Notas

  • Novo server/lib/url-key.mjs, ligado ao dedup dos dois scanners e ao escritor do pipeline. Sub-normaliza de propósito: nunca funde duas vagas distintas. Testes: tests/url-key.test.mjs (+5), tests/parsers.test.mjs (+1). Suíte: 2477 (+6).

[1.182.0] — 2026-08-14

Corrigido (scanner) — as faixas salariais agora aparecem iguais em todos os idiomas.

Corrigido

  • Os valores de salário nas linhas de scan e rastreador usam os símbolos neutros ≥ e ≤ (ex.: ≥ 120000 EUR, ≤ 90000) em vez das palavras inglesas “from” / “up to”, que vazavam sem tradução para interfaces não inglesas. Vale para todo quadro com faixa de um lado só (Getro, Remotli, Manfred, Agentic Jobs, JustJoin, Jobicy); faixas de dois lados (100000–150000 USD) já eram neutras.

Notas

  • Apenas exibição — o filtro de salário do cliente analisa os números independentemente do prefixo, então a filtragem não muda. Suíte: 2471.

[1.181.0] — 2026-08-14

Adicionado (scanner) — os quadros do Getro agora mostram salário, todas as localizações e vagas remotas.

Adicionado

  • O scanner do Getro (quadros de redes de talentos de fundos) agora mostra um valor de salário em cada vaga (faixa anual + moeda), lista todas as localizações em vez de apenas a primeira e marca vagas remotas. Uma vaga do Getro no seu scan e rastreador agora traz o mesmo detalhe de salário + localização que os outros quadros.

Notas

  • Apenas scanner; sem nova dependência, sem mudança de rota / CSP / SSRF. Testes: tests/sources-getro.test.mjs (+5). Suíte: 2470 (+5).

[1.180.0] — 2026-08-14

Corrigido (MÉDIO, relatórios) — a lista #/reports agora é uma tabela e uma pontuação real que um marcador do Machine Summary escondia é recuperada.

Corrigido

  • A lista #/reports é uma tabela (Relatório · Data · Legitimidade · Pontuação), não uma grade de 4 cartões. Um chip longo de “Pontuação não detectada” espremia a coluna do título quase a zero, e o overflow-wrap: anywhere do título do cartão quebrava o nome do relatório caractere por caractere. Agora cada campo tem sua própria coluna, a célula do nome quebra por palavras e a tabela rola horizontalmente em telas estreitas (novo contêiner .reports-scroll). Nova chave i18n rep.colReport ×17.
  • Uma pontuação real no corpo (**Итоговый балл:** 1.8 / 5) não é mais escondida por um marcador do Machine Summary (score: —). Quando o bloco ## Machine Summary trazia uma pontuação não numérica ou fora do intervalo, ele ocupava o espaço da pontuação analisada e bloqueava o fallback de forma-valor em negrito, então o relatório exibia “Pontuação não detectada” apesar de um X / 5 real no corpo. parseReportHeader agora recupera a forma-valor do corpo sempre que nenhum número utilizável sobrevive (passo 4.5).

Notas

  • Apenas cliente + analisador; sem mudança de rota / CSP / SSRF / escrita no pai. Testes: tests/reports-table.test.mjs (+5), tests/report-header-locale.test.mjs (+2). Suíte: 2465 (+7).

[1.179.0] — 2026-08-13

Alterado (LOW, scanner) — 20 decodificadores de entidades HTML duplicados consolidados no módulo compartilhado (follow-up de paridade, fecha o worklist).

Alterado

  • 20 fontes de varredura tinham cada uma seu próprio decodeEntities/decodeXmlEntities (+ um helper fromCodePoint) — cópias que haviam divergido (três podiam lançar RangeError, corrigido em v1.172.0; outras admitiam NUL/C0 ou faziam parse errado de &#1a2;). Agora todas passam pelo único server/lib/html-entities.mjs (decodificador seguro conforme XML 1.0 Char), removendo ~237 linhas de duplicação. As 8 fontes tipo RSS ganharam a decodificação de   (antes só tratavam 5 entidades); a dupla decodificação deliberada do cryptocurrencyjobs é preservada por um alias. hh mantém seu decodificador (trata —/–, fora dos 6 compartilhados). Um novo teste-guarda falha se alguma fonte recriar um decodificador local.

Notas

  • Refatoração que preserva o comportamento; sem mudança de rota / CSP / SSRF / escrita no pai. Testes: tests/decoder-consolidation.test.mjs (+2). Suíte: 2458 (+2).

[1.178.0] — 2026-08-13

Corrigido (LOW, paridade com o pai) — duas constantes desatualizadas foram atualizadas para igualar ao pai (PARENT-SYNC GAP #4 + #5).

Corrigido

  • User-Agent do navegador (GAP #4) — BROWSER_LIKE_USER_AGENT (enviado por workable/workday/oraclecloud/a16z/eightfold para passar filtros WAF/bot) subiu de Chrome 131 para 151, igualando o user-agent.mjs do pai; uma versão antiga é mais propensa a ser bloqueada. Protegido por um teste Chrome major ≥ 151.
  • FALLBACK de estados do tracker (GAP #5) — o FALLBACK de último recurso em states.mjs (usado só quando o templates/states.yml ao vivo é ilegível — clone novo / raiz isolada de CI) ganhou os aliases de status em turco do pai (#2615): değerlendirildi, başvuruldu, yanıt verildi, mülakat, teklif, reddedildi, iptal edildi, uygun değil, kabul edildi/işe alındı. Em produção o arquivo ao vivo já os fornecia.

Notas

  • Apenas duas constantes; sem mudança de rota / CSP / SSRF / escrita no pai. Testes: tests/http-json.test.mjs (+1) + tests/states.test.mjs (+1). Suíte: 2456 (+2).

[1.177.0] — 2026-08-13

Corrigido (MEDIUM, scanner) — csod (Cornerstone) retornava 0 vagas em inquilinos que protegem a API de busca com cookies de sessão (parent #2769, PARENT-SYNC GAP #1).

Corrigido

  • Alguns inquilinos do Cornerstone definem cookies de sessão na página inicial do site de carreiras e respondem 401 CSOD Unauthorized na API de busca a menos que esses cookies voltem junto com o token bearer anônimo. sources/csod.mjs agora lê o bootstrap com um novo helper fetchResponse, monta um cabeçalho Cookie a partir dos valores Set-Cookie (cookieHeaderFrom — só nome=valor, semântica de jar) e o reenvia no POST de busca. Apenas mesma origem (host fixado + redirect:'error'), então cookies de sessão nunca chegam a terceiros; um inquilino sem cookies se comporta como antes.

Notas

  • Novo server/lib/http-json.mjs::fetchResponse (aditivo; fontes existentes intactas). Sem mudança de rota / CSP / SSRF / escrita no pai. Testes: tests/sources-parity-v1118a.test.mjs (+1). Suíte: 2454 (+1).

[1.176.0] — 2026-08-13

Corrigido (MEDIUM, relatórios) — uma pontuação sob um rótulo em negrito que a tabela RU não lista ainda mostrava “Score not detected” (FIND-5).

Corrigido

  • Dois relatórios RU escreviam a pontuação como **Итоговый балл:** 1.8 / 5 / **Скор:** 1.8 / 5 — rótulos em negrito que REPORT_LABELS.ru não enumera (só conhece “Оценка”/“Балл”), então a pontuação ficava sem parse. Em vez de ampliar a lista de sinônimos, parseReportHeader agora recorre à forma do valor: uma fração sobre a rubrica /5 sob QUALQUER rótulo em negrito. É independente de idioma, imune a um cabeçalho (sem **, sem valor /5) e rejeita uma data como 5/5/2026 (lookahead negativo no denominador).

Notas

  • Apenas o parser do servidor; sem mudança de rota / CSP / SSRF / escrita no pai. Testes: tests/report-header-locale.test.mjs (+2). Suíte: 2453 (+2).

[1.175.0] — 2026-08-13

Corrigido (LOW, robustez) — uma guarda de regressão para a descrição SEO do FIND-3 + um strip de legitimidade seguro a nulos (follow-up da AI-review).

Corrigido

  • Guarda de paridade da descrição SEO — a correção de v1.174.0 que trocou um “~55” fixo no meta.desc de cada idioma por um placeholder {adapters} derivado do registro não tinha teste, então podia regredir em silêncio na próxima edição de um idioma. O novo tests/site-meta-desc-parity.test.mjs (isolado de CI) falha se algum dos 17 site/src/i18n/*.json perder o placeholder ou refixar uma contagem, ou se Landing.astro parar de interpolá-lo nas três metadescrições.
  • Strip de legitimidade seguro a nulos — stripEmphasis retorna '' para entrada nula em vez da string “undefined” (os campos são inicializados como string, então é defesa em profundidade).

Notas

  • Teste + uma guarda de uma linha no parser; sem mudança de rota / CSP / SSRF / escrita no pai. Testes: tests/site-meta-desc-parity.test.mjs (+3). Suíte: 2451 (+3).

[1.174.0] — 2026-08-13

Corrigido (HIGH, relatórios) — relatórios localizados mostravam “Score not detected”; a descrição SEO estava desatualizada.

Corrigido

  • Parsing da pontuação (FIND-1) — um relatório não inglês cujo H1 contém a palavra do rótulo de pontuação (# Оценка вакансии: <título>) não confunde mais esse título com a pontuação. parseReportHeader agora se ancora no rótulo em negrito localizado (**Оценка:** 1.5 / 5), pula linhas de cabeçalho e exige o rótulo junto aos dois-pontos — então relatórios RU que mostravam “Score not detected” exibem a pontuação real.
  • Chip de legitimidade (FIND-2) — a ênfase Markdown é removida do valor, então o chip mostra “High Confidence” em vez de “** High Confidence”.
  • Estouro da pontuação — uma linha de pontuação com texto de status extra (“1.8, Status: Evaluated, …”) é compactada para apenas a pontuação; .score-pill ganha um limite de não-quebra/overflow e a coluna do título pode encolher, então um chip colorido nunca ultrapassa a borda do card.
  • Descrição SEO (FIND-3) — as descrições meta / OG / Twitter do cvstart.org (todos os 17 idiomas) fixavam “Scan ~55 job boards” enquanto o corpo contava o registro real (“~75”). A descrição agora interpola a contagem do registro, então não desvia de novo.

Notas

  • Parser do servidor + render/CSS do cliente + i18n do site; sem mudança de rota / CSP / SSRF / escrita no pai. Testes: tests/report-header-locale.test.mjs (+4). Suíte: 2448 (+4).

[1.173.0] — 2026-08-13

Adicionado (LOW, configuração) — Hermes entra no roster de CLIs de IA detectadas (paridade com career-ops).

Adicionado

  • A aba #/config → “Ferramentas CLI de IA” agora detecta Hermes (Nous Research), o novo runtime de agente suportado pelo projeto pai (binário hermes). server/lib/routes/cli-detect.mjs amplia sua lista fixa de 10 para 11 ferramentas; a detecção continua sendo uma varredura de PATH somente leitura (nenhum binário é executado).

Notas

  • Sem mudança de i18n / rota / CSP / SSRF / escrita no pai; o roster é uma lista fixa, nunca entrada. Suíte: 2444 (o canário do cli-detect atualizado de 10 para 11).

[1.172.0] — 2026-08-13

Corrigido (MEDIUM, scanner) — uma entidade HTML malformada podia derrubar uma fonte de varredura (paridade com career-ops #2150).

Corrigido

  • As fontes oraclecloud, gem e dassault decodificavam entidades HTML numéricas com uma verificação Number.isFinite insuficiente antes de String.fromCodePoint — uma referência acima de 0x10FFFF (ex.: &#99999999; de um feed malformado ou malicioso) lançava um RangeError não capturado e abortava toda a análise daquela fonte. Um módulo compartilhado server/lib/html-entities.mjs (espelho do _html-entities.mjs do projeto pai) agora restringe as referências numéricas ao conjunto Char do XML 1.0 §2.2, então String.fromCodePoint nunca lança, e distingue hexadecimal de decimal separadamente para que &#1a2; não seja mais interpretado errado. As três fontes o importam.

Notas

  • Sem mudança para feeds válidos; sem mudança de JS / i18n / rota / CSP / SSRF / escrita no pai. A consolidação das ~20 cópias restantes do decodificador está registrada em qa/PARENT-SYNC-WORKLIST-v1.26.0.md.
  • Testes: tests/html-entities.test.mjs (+7). Suíte: 2444 (+7).

[1.171.0] — 2026-08-13

Alterado (BAIXA, design-system) — tokens de escala tipográfica + camadas z-index (D-4, primeiro passo). Tamanhos e empilhamento eram literais por componente.

Alterado

  • Camadas z-index — adicionados tokens --z-* (--z-topbar … --z-skiplink) e migrado cada literal z-index. Valores preservados, empilhamento idêntico; um novo canário proíbe novos números mágicos.
  • Escala tipográfica — rampa --font-size-* (xs 11 … 2xl 28, base = Inter 15px); migrados os tamanhos centrais (sem mudança visual). Valores fora da rampa migram incrementalmente (docs/UX-ROADMAP.md).

Notas

  • Apenas token CSS; sem mudança de comportamento/JS/i18n/rota/CSP/SSRF/escrita. Sem mudança de pixels. tests/design-tokens-scale.test.mjs (+3). Conjunto: 2437 (+3).

[1.170.0] — 2026-08-13

Adicionado (BAIXA) — dicas de ETA honestas em gerações IA longas (P4-ETA). Gerações pesadas (plano de carreira ~40 s, orientação / mercado / networking ~30 s, two-pager ~20 s) mostravam apenas “Gerando…” sem indicar a duração.

Adicionado

  • Cada botão de geração longa agora exibe uma dica ⏱ ~Ns ao lado, como a ETA de #/auto. Estilo .eta-hint compartilhado + duas chaves genéricas (common.eta ~{n}s, common.etaTitle).

Notas

  • Apenas cliente; sem mudança de rota/CSP/SSRF/escrita. +2 chaves i18n ×17 (snapshot 1219 → 1221). tests/generation-eta-hint.test.mjs (+2). Conjunto: 2434 (+2).

[1.169.0] — 2026-08-13

Adicionado (BAIXA) — pré-visualização de PDF inline (D-5). GET /api/output/pdfs/:name forçava Content-Disposition: attachment, então até o botão “Abrir” de #/cv baixava em vez de exibir.

Adicionado

  • ?inline=1 serve o MESMO arquivo saneado com Content-Disposition: inline, para uma 👁 Pré-visualização em nova aba; o padrão continua download. Sem nova rota; mesmas proteções de nome.
  • O primeiro botão da lista de PDFs em #/cv agora é 👁 Pré-visualizar ao lado de ⬇ Baixar. cv.openPdf alterado para “Pré-visualizar” ×17.

Notas

  • Sem mudança de CSP/SSRF — mesmo sanitizePathName. Uma chave i18n reformulada ×17 (snapshot 1219). tests/output-pdfs.test.mjs (+3). Conjunto: 2432 (+3).

[1.168.0] — 2026-08-13

Corrigido (BAIXA, a11y) — as linhas de checkbox agora atendem ao mínimo 24×24 do WCAG 2.5.8 (D-2). Os rótulos de checkbox/radio em #/scan, #/config, #/evaluate e #/cv-studio ficavam numa faixa de ~22 px.

Corrigido

  • Uma regra restrita label:has(> input[type="checkbox"/"radio"]) { min-height: 24px } garante uma faixa ≥24 px. Apenas min-height — os rótulos já são flex, nada se desloca; .apply-checklist (32 px) já cumpria.

Notas

  • Apenas CSS; sem mudança de comportamento/JS/i18n/rota/CSP/SSRF/escrita. tests/checkbox-target-size.test.mjs (+1). Conjunto: 2429 (+1).

[1.167.0] — 2026-08-13

Corrigido (BAIXA, design-system) — superfícies elevadas agora se separam das linhas (D-3). Os tokens --panel-2 / --surface-elev1 resolviam para --slate, igual às linhas --line / --border, sem separação visual.

Corrigido

  • Um token temático dedicado --elev passa a sustentar as superfícies elevadas (#eef1f6 claro / #1e232e escuro, distinto de --slate); as linhas ficam em --slate. Os achados restantes (D-2, D-4, D-5, P4-ETA) ficam como backlog em docs/UX-ROADMAP.md.

Notas

  • Apenas token CSS; sem mudança de comportamento/JS/i18n/rota/CSP/SSRF/escrita. tests/elevation-token.test.mjs (+2). Conjunto: 2428 (+2).

[1.166.0] — 2026-08-13

Corrigido (BAIXA) — a terminologia da rubrica agora espelha os docs canônicos. career-ops.org/docs descreve “cinco dimensões mais uma pontuação global holística”, mas o web-ui, o cvstart.org e o wiki diziam “rubrica de seis dimensões” (5 + 1 = 6, mas o vocabulário não batia).

Corrigido

  • Adotada a redação dos docs — “cinco dimensões mais uma pontuação global holística” — em README ×17, no site cvstart.org ×17, no guia de ajuda ×17, em docs/career-ops-canonical.md e no wiki (Home ×17 + Features).

Notas

  • Apenas docs/marketing; sem mudança de código/chave i18n/rota/CSP/SSRF/escrita. tests/rubric-terminology.test.mjs (+2). Conjunto: 2426 (+2).

[1.165.0] — 2026-08-13

Corrigido (BAIXA) — o termo “Two-pager” agora é coerente em cada idioma. Em árabe a barra lateral mostrava o latino “Two-pager” enquanto o <h1> estava localizado — a única string latina numa navegação RTL de resto espelhada.

Corrigido

  • Decisão aplicada: por idioma, nav.twoPager e twoPager.title concordam no termo (ambos em latim ou ambos localizados). Só o árabe estava dividido; seu rótulo de navegação agora está localizado (“الصفحتان”). Um novo canário falha se algum idioma voltar a dividi-los.

Notas

  • Apenas texto; sem mudança de rota/CSP/SSRF/escrita. Um valor i18n alterado (ar); sem novas chaves (snapshot 1219). tests/two-pager-term-consistency.test.mjs (+2). Conjunto: 2424 (+2).

[1.164.0] — 2026-08-13

Corrigido (BAIXA) — o placeholder da busca não transborda mais em nenhum idioma. “Find a company, role or URL…” era cortado (nowrap) quando a barra encolhia; a metade “…or URL” nunca aparecia.

Corrigido

  • top.search (×17) agora é o curto “Buscar ou colar URL” (≤24 caracteres em cada idioma), cabe até numa barra estreita e mantém a referência a URL. O fallback em index.html combina; o aria-label preserva o detalhe completo.

Notas

  • Apenas texto; sem mudança de rota/CSP/SSRF/escrita. Uma chave i18n reformulada ×17 (sem novas; snapshot 1219). tests/search-placeholder-fit.test.mjs (+2). Conjunto: 2422 (+2).

[1.163.0] — 2026-08-13

Corrigido (BAIXA) — o assistente “Pergunte aos docs” agora cobre exportar um relatório para PDF. Antes respondia que o guia não cobria, apesar do controle 📄 Generate PDF em #/reports/:slug.

Corrigido

  • Adicionado um H3 “Exportar um relatório para PDF” em §10 Relatórios nos 17 pacotes de ajuda (onde fica o botão, que o arquivo vai para output/*.pdf, precisa do Playwright, revisar antes de enviar). A recuperação do assistente agora mostra a seção Relatórios.

Notas

  • Apenas docs/ajuda; sem mudança de código/rota/CSP/SSRF/escrita. Limite de ajuda 112 → 113 H3 (31 H2 inalterado). tests/help-reports-pdf-section.test.mjs (+2). Conjunto: 2420 (+2).

[1.162.0] — 2026-08-13

Corrigido (MÉDIA) — o “?” de ajuda agora é um alvo de ≥24×24 (WCAG 2.5.8). .help-hint media 18×18 px com padding:0, abaixo do mínimo, em cada cabeçalho.

Corrigido

  • A caixa de .help-hint agora é 24×24 (o alvo mensurável) enquanto o anel visível continua 18px via um ::before centralizado — o glifo e a linha de base do <h1> não mudam. Estados hover/ativo/foco acompanham o anel; margem 6→3px para manter o espaço.

Notas

  • Apenas CSS; sem mudança de JS/i18n/rota/CSP/SSRF/escrita. tests/help-hint-target-size.test.mjs (+2). Conjunto: 2418 (+2).

[1.161.0] — 2026-08-13

Corrigido (MÉDIA) — #/reports mostra um chip “pontuação não detectada” em vez de espaço vazio. Após o analisador multi-idioma da v1.159.0, um relatório sem pontuação analisável mostrava uma área vazia, indistinguível de uma falha.

Corrigido

  • A célula de pontuação agora se ramifica: com pontuação → pílula de tom; sem pontuação → chip .score-muted com “Pontuação não detectada” (×17) e dica “Abra o relatório…”. O cartão continua um role="link" operável por teclado e a data se mantém.
  • Reutiliza o token neutro existente; sem cor nova.

Notas

  • Apenas cliente; sem mudança de rota/CSP/SSRF/escrita. +2 chaves i18n ×17 (snapshot 1217 → 1219). Conjunto: 2416 (+3).

[1.160.0] — 2026-08-13

Corrigido (ALTA) — o texto sobre provedores não contradiz mais a promessa de 7 provedores. #/config dizia que a avaliação ao vivo “usa sua chave Anthropic ou Gemini” e que a do OpenAI “não é usada pelo web UI”; o painel dizia “Score Anthropic-first” — falso desde a cascata de 7 provedores (v1.157.0).

Corrigido

  • config.providerModelNote (×17): agora diz que a ⚡ avaliação ao vivo headless funciona com qualquer uma das suas sete chaves (Anthropic · Gemini · OpenAI · Qwen · OpenRouter · GitHub Models · Hermes), ordenadas automaticamente com fallback. Frase falsa sobre OpenAI removida.
  • dash.quick.evaluateSub (×17): neutro quanto ao provedor (“Pontuação de adequação 0–5”). Keys: N / 5 → N / 7.

Notas

  • Apenas texto; sem mudança de rota/CSP/SSRF/escrita. Sem novas chaves i18n (snapshot 1217). Conjunto: 2413 (+3).

[1.159.0] — 2026-08-13

Corrigido (ALTA) — os metadados do relatório não dependem mais do idioma. Relatórios gerados em idioma diferente do inglês mostravam uma faixa de metadados vazia em #/reports, porque parseReportHeader só reconhecia rótulos em negrito em inglês.

Corrigido

  • parseReportHeader agora analisa o bloco YAML invariante ## Machine Summary (score: / legitimacy: / date:, a mesma fonte que o auto-pipeline já lê): rótulos em inglês → bloco Machine Summary → rótulos localizados (REPORT_LABELS, 17 idiomas). Relatórios em inglês ficam byte a byte idênticos.
  • Análise numérica tolerante (1.5/5, 1,5/5, 1.5 из 5, 4.5 out of 5); a data recorre ao mtime do arquivo quando ausente.

Notas

  • Apenas leitura/análise; sem mudança de rota, CSP, SSRF ou escrita no pai. Sem novas chaves i18n. Conjunto: 2410 (+8).

[1.158.0] — 2026-08-12

Corrigido — dois bugs cosméticos de exibição (uma «?» vazando no título da aba e uma contagem de provedores errada na landing). Apenas visual; sem mudança de comportamento, segurança ou fluxo de dados.

Corrigido

  • A «?» do HelpHint não vaza mais para document.title. O router derivava o título da aba do h1.textContent bruto, então a aba mostrava «Vacancy search?» em vez de «Vacancy search». router.js::focusNewView agora clona o cabeçalho, remove .help-hint e lê o texto; a «?» visível do cabeçalho fica intacta.
  • cvstart.org mostrava «17 AI providers» em vez de «7». O helper sub() de Features.astro reescrevia todo {n} com a contagem de idiomas (17) antes da substituição por cartão; agora {n} é resolvido por cartão (provedores → 7, idiomas → 17).

Notas

  • Sem mudança de servidor, rota, CSP, SSRF ou chaves i18n; forma de facts.json inalterada. Conjunto: 2402 testes (+1).

[1.157.0] — 2026-08-12

Corrigido — as avaliações ao vivo agora rodam com QUALQUER provedor configurado, não só Anthropic/Gemini. Um usuário com apenas OPENROUTER_API_KEY era forçado erroneamente ao modo manual.

Corrigido

  • Causa raiz: um pin LLM_PROVIDER sem chave (ex.: LLM_PROVIDER=claude do init) travava; agora recorre à ordem auto entre os provedores configurados (em selectActiveProvider + ambas as cascatas de despacho).
  • O gating do cliente (#/deep + views mode-page) agora usa window.ProviderStatus (/api/status/providers, os 7) em vez do probe obsoleto de Anthropic/Gemini; textos reescritos (deep/eval × 17) + selo «Evals ao vivo» do dashboard + config.llmProviderHint.

Notas

  • Sem mudanças de segurança. Suíte: 2401 testes (+5).

[1.156.0] — 2026-08-12

Refactor — dividir scan.js sob o limite de tamanho (P-16) + um fix do CodeQL. scan.js tinha 906 linhas; duas fábricas que preservam o comportamento foram extraídas → 648. Completa o par de divisões de views P-15/P-16.

Alterado

  • Novos scan/runner.js (motor de execução do scan) e scan/filters.js (máquina de estado de filtros), via sacos ctx/refs; scan.js conecta ambos.

Corrigido

  • CodeQL js/useless-assignment-to-local (#428) em config/tab-controller.js: let n = i; → let n;.

Notas

  • Refactor puro, sem mudança de comportamento; 4 testes que leem o fonte foram repontados. Ambas as views grandes agora sob 800 (P-15/P-16 feito). Suíte: 2396 testes.

[1.155.0] — 2026-08-12

Refactor — dividir config.js sob o limite de tamanho (P-15). config.js tinha 1030 linhas (acima do limite de 800); dois módulos que preservam o comportamento foram extraídos, deixando-o em 783.

Alterado

  • Novos config/field-specs.js (dados de campos + listas de modelos) e config/tab-controller.js (fábrica da barra de abas); config.js os referencia, a lógica de render não muda.

Notas

  • Refactor puro, sem mudança de comportamento; 6 testes que leem o fonte foram repontados. scan.js (906) fica como está (já parcialmente dividido; núcleo acoplado demais para uma divisão mecânica limpa). Suíte: 2396 testes.

[1.154.0] — 2026-08-12

Novo guia — “Rodar todo o stack na nuvem.” O career-ops não traz história própria de nuvem/servidor, então adicionamos uma: um passo a passo para pôr o pipeline pai career-ops, este visualizador career-ops-ui e o motor de IA (uma assinatura Claude via Claude Code, um Hermes local, ou chaves de API) num servidor pequeno sempre ligado. Chega como Ajuda §31 nos 17 idiomas, uma seção do README e uma página wiki.

Adicionado

  • Ajuda §31 “Rodar todo o stack na nuvem” (× 17) — as três partes, provisionar + instalar, escolher motor e expor com segurança (proxy reverso HTTPS + auth + as invariantes CSP/SSRF/XSS/sem-segredos). O bundle de ajuda cresce para 31 H2 / 112 H3.
  • README — uma seção “Rode todo o stack na nuvem” (× 17) + uma página Cloud-Deployment na wiki.

Notas

  • Somente docs — sem rota, servidor ou mudança de cliente; sem nova chave i18n. Os 4 testes da ajuda passam ao contrato 31 H2 / 112 H3. Suíte: 2396 testes (inalterada).

[1.153.0] — 2026-08-12

O scanner do Jobvite migrou para o feed XML público (sync com o pai). O pai aposentou a API JSON do Jobvite (agora retorna zero vagas); o source do web-ui usava esse mesmo endpoint morto, então qualquer empresa Jobvite rastreada escaneava vazia em silêncio. Porta o fix do pai (#2623): agora lê o feed XML público por inquilino, com chave companyEId.

Corrigido

  • O source pedia a API JSON aposentada e retornava zero vagas; agora pede https://app.jobvite.com/CompanyJobs/Xml.aspx?c={companyEId} e faz parse do XML <result><job>… (CDATA + entidades, detail-url sobre apply-url).

Alterado

  • Resolução de companyEId: (1) company_eid: no portal, (2) o c= de um api: explícito, (3) descoberta pela página do board. fetchText (http-json.mjs) anexa .location/.retryAfter ao erro non-ok (somente leitura, retrocompatível).

Notas

  • Segurança — dois hosts fixados (jobs.jobvite.com, app.jobvite.com) via assertJobviteUrl: só https, allowlist estrita, nenhum redirecionamento seguido. O companyEId é só um valor ?c=; contagem de sources inalterada.
  • Suíte: 2396 testes (+4).

[1.152.0] — 2026-08-12

Provedor Hermes — fiação concluída + atualização de docs. Uma revisão de código da integração do Hermes da v1.151.0 encontrou duas falhas reais e quatro itens de completude; todos corrigidos aqui, e o roster de provedores LLM de todo o app é elevado aos sete completos em todas as superfícies de docs e nos 17 idiomas.

Corrigido

  • #/config não conseguia forçar o Hermes — o menu LLM_PROVIDER listava só seis provedores, então dava para definir HERMES_API_KEY mas não forçar o Hermes pela UI. Agora hermes é a 8.ª opção, e um novo teste de paridade impede o menu de divergir de LLM_PROVIDERS.
  • Chaves locais curtas eram rejeitadas em silêncio — o piso de 20 caracteres de isUsableKey foi calibrado para chaves de nuvem; hasHermesKey usa agora um piso relaxado de 8 (o exemplo dos docs do Hermes tem 19 caracteres).

Alterado

  • O roster de provedores foi normalizado para os sete completos em README (× 17), ajuda no app (× 17), dict config.llmProviderHint (× 17) e docs/sdd; hermesChatUrl completa um host sem caminho; o texto de fallback manual cita o Hermes.

Notas

  • Segurança inalterada — sem rota nova nem mudança de SSRF/CSP; health/doctor ganha uma linha HERMES_API_KEY.
  • Suíte: 2392 testes (+2).

[1.151.0] — 2026-08-12

Hermes agora é um provedor de LLM conectado (Phase 5) — o spike de escopo da Phase 5 confirmou que o Hermes da Nous Research inclui um API Server compatível com OpenAI (hermes gateway → POST /v1/chat/completions), então o career-ops-ui agora roda avaliações ao vivo por um Hermes local igual a OpenAI/Qwen. Defina HERMES_API_KEY em Configurações do app e ele entra na ordem auto (por último). Encerra o último item aberto do roadmap — Phase 5, Shape A.

Adicionado

  • Provedor LLM Hermes (Shape A) — runHermes no cliente compartilhado runOpenAICompatible (server/lib/openai.mjs), em ambas as cascatas (llm-dispatch.mjs + routes/llm.mjs), na cauda da ordem auto + o pin LLM_PROVIDER=hermes, /api/status/providers e llm-pricing.mjs. Alcança uma base URL local configurável (padrão http://127.0.0.1:8642/v1) com auth Bearer — é um endpoint de provedor CONFIGURADO (como OpenRouter/Qwen), não uma URL de vaga do usuário, então não passa pelo guard SSRF.
  • Campos em #/config — HERMES_API_KEY (secreto) + HERMES_BASE_URL + HERMES_MODEL (padrão hermes-agent), com 6 novas chaves i18n × 17 idiomas (snapshot 1208 → 1214).

Alterado

  • O spike de escopo foi resolvido: docs/integrations/HERMES.md, a ajuda integrada §30 (× 17), a chamada do README (× 14), a skill hermes-bridge e o roadmap passam de “planejado / ainda não conectado” para conectado (Shape A). Shape B (um relay sob medida do runtime de agente) não foi necessário.

Notas

  • Segurança: o fetch do provedor é um endpoint configurado, da mesma categoria dos outros provedores compatíveis com OpenAI — sem nova superfície SSRF, sem mudança de CSP/sanitizador. HERMES_API_KEY é uma SECRET_KEY (nunca exibida).
  • Testes (isolados em CI, transporte simulado): tests/hermes-provider.test.mjs (+5); o canário “sem ramo Hermes” da v1.146.0 é invertido para afirmar que ESTÁ conectado; testes de superfície de provedores atualizados para a ordem de 7 provedores.
  • Suíte: 2390 testes (+5).

[1.150.0] — 2026-08-12

Estados vazios consistentes (polimento da Phase 4) — cada painel de “ainda não há nada” agora usa o único estilo compartilhado .empty, em vez de algumas telas redeclararem o visual inline com um 40px mágico. Pequena correção de consistência visual; os estados vazios de #/activity, #/cv-studio, #/stats e #/usage agora combinam com todos os outros (padding de 48px tokenizado + borda tracejada).

Alterado

  • #/activity, #/cv-studio, #/stats, #/usage removeram seu style: { padding: '40px', textAlign: 'center', color: 'var(--foggy)' } inline nos painéis vazios — as três propriedades já vêm da classe compartilhada .empty (--space-7 = 48px, centralizado, esmaecido, borda tracejada). Assim, esses quatro renderizam idênticos aos ~25 outros painéis .empty.
  • Sobreposições legítimas por tela (#/dashboard width:100%, #/pipeline border:none) não foram tocadas — só as redeclarações puramente redundantes saíram.

Notas

  • Apenas limpeza de uso de CSS no cliente — sem mudança de rota, servidor, chave i18n ou regras CSS (a classe .empty não muda); snapshot do dicionário 1208. Verificado no navegador (o painel vazio de #/usage calcula 48px de padding + borda tracejada, 0 erros de console).
  • O novo canário tests/empty-state-consistency.test.mjs mantém .empty como fonte única de verdade. A Phase 5 (provedor Hermes) segue bloqueada.
  • Suíte: 2385 testes (+2: tests/empty-state-consistency.test.mjs).

[1.149.0] — 2026-08-12

Portais movidos para Configurações (Phase 4) — #/portals agora fica no grupo de navegação Setup, ao lado de Configurações do app, em vez de sob Sourcing. Desde a v1.144.0 é uma superfície de configuração (ativar/desativar empresas monitoradas + uma sonda de saúde do ATS), não uma ação de sourcing — então é onde ele pertence. Apenas mudança de navegação; a página e a rota não mudam.

Alterado

  • Item de navegação #/portals → grupo Setup (em public/index.html), colocado logo após Configurações do app. Removido do grupo Sourcing (que mantém Scan / Pipeline / Auto-pipeline / Empresas financiadas). A rota #/portals, a visão e o rótulo nav.portals não mudam — só a posição na barra lateral.

Notas

  • Apenas marcação de navegação — sem rota, visão, chave i18n ou mudança de servidor. Verificado no navegador (0 erros de console); protegido por tests/portals-nav-placement.test.mjs.
  • Suíte: 2383 testes (+2: tests/portals-nav-placement.test.mjs).

[1.148.0] — 2026-08-12

Filtros de busca mais claros (Phase 4) — o painel de filtros agora é uma grade organizada — o painel de filtros do #/scan saiu de um flex-wrap irregular de caixas rígidas de largura variável para uma grade responsiva, e as ações Aplicar / Redefinir agora ficam em sua própria linha separada e alinhada à direita. Mesmos filtros, mesmo comportamento — só mais fáceis de ler. Um retoque de design (sem parent-sync).

Alterado

  • Painel de filtros do #/scan → grade responsiva — .scan-filters agora é display: grid com colunas repeat(auto-fill, minmax(180px, 1fr)) e espaçamento uniforme, de modo que os 11 filtros rotulados se alinham em colunas organizadas em qualquer largura em vez de quebrarem em uma linha irregular.
  • Ações Aplicar / Redefinir ocupam toda a grade em sua própria linha, separadas por uma linha fina e alinhadas à direita. Removido o antigo truque de rótulo oculto + o wrapper flex interno em scan.js.

Notas

  • Apenas CSS + uma pequena limpeza do DOM — cada id de filtro (#scan-filter-*, #scan-apply) e a ligação do SR.render() não mudam, então o fluxo do Playwright fica intacto. Sem novas chaves i18n.
  • Verificado no navegador (0 erros de console); protegido por tests/scan-filters-grid.test.mjs.
  • Suíte: 2381 testes (+3: tests/scan-filters-grid.test.mjs).

[1.147.0] — 2026-08-12

Hermes & Telegram — a seção de ajuda integrada + superfície no cvstart.org (Phase 5b, parte 2) — a segunda e última parte do trabalho de documentação do Hermes: o tutorial agora vive dentro do próprio guia de ajuda do app, em todos os 17 idiomas, e o assistente de documentação integrado responde a perguntas sobre o Hermes a partir dele. Ainda é apenas documentação — o caminho do provedor LLM Hermes permanece planejado / ainda não conectado (Phase 5).

Adicionado

  • Ajuda integrada §30 “Hermes & Telegram” × 17 idiomas — uma nova seção do guia (o que é o Hermes + as duas formas de integração; execução em um servidor na nuvem; Telegram via Hermes + a regra “o que NÃO expor”), acessível a partir de #/help. O grounding do docs-assistant / DocsFab a captura automaticamente, já que ambos leem docs/help/<lang>.md.
  • cvstart.org — um link para o guia do Hermes apontando para o documento no GitHub.

Alterado

  • Limite do pacote de ajuda elevado 29 → 30 H2 / 105 → 108 H3 (canonical-docs-coverage, help-ui, help-ru-config-section); §30 adiciona 3 H3.

Notas

  • Ainda nada chama o Hermes. O novo canário tests/help-hermes-section.test.mjs garante que cada idioma inclui a §30 com suas âncoras independentes de idioma (docs/integrations/HERMES.md, hermes-bridge, #/help, 127.0.0.1, Telegram). O provedor continua bloqueado à espera do contrato de API da Phase 5.
  • Isso encerra o entregável de documentação + skill da Phase 5b; a integração do provedor (Phase 5) continua sendo um item separado e bloqueado.
  • Suíte: 2378 testes (+2: tests/help-hermes-section.test.mjs).

[1.146.0] — 2026-08-12

Agente Hermes + Telegram — o guia de integração + uma skill (Phase 5b, parte 1) — você pode rodar o career-ops-ui em um servidor na nuvem e conectar seus eventos (uma varredura concluída, um novo relatório, um follow-up urgente) ao Telegram através de um agente Hermes da Nous Research. Esta versão entrega a documentação de design + implantação e uma skill hermes-bridge; o caminho do provedor LLM Hermes permanece planejado / ainda não conectado (bloqueado pelo spike de contrato de API da Phase 5). Documentação antes do código, por design.

Adicionado

  • docs/integrations/HERMES.md — o mergulho profundo: as duas formas de integração (endpoint compatível com OpenAI vs. runtime de agente), a implantação em servidor na nuvem (reverse proxy + HTTPS + systemd, o contrato somente leitura com o parent em uma máquina headless), Telegram via Hermes, e uma lista de modelo de ameaças «o que NÃO expor» (sem CV / salário / corpo de relatórios / chaves para o canal).
  • ## Hermes agent + Telegram como chamada no README — um link breve com indicação, no README em inglês e espelhado nos READMEs traduzidos de cada idioma.
  • Uma skill hermes-bridge (.claude/skills/hermes-bridge/) que operacionaliza o guia — checagens de pré-requisito e de escopo (Node ≥ 18, chaves presentes, alcançabilidade do endpoint pelo caminho seguro contra SSRF), nunca grava segredos em disco/logs, e se recusa a inventar um endpoint do Hermes ou afirmar que o provedor está conectado.
  • Uma seção Integrations em docs/architecture/OVERVIEW.md linka o guia.

Notas

  • Nada chama o Hermes ainda. Um teste canário (tests/hermes-docs.test.mjs) garante os marcadores de honestidade «planejado / ainda não conectado» e que llm-dispatch.mjs não tem nenhuma ramificação Hermes/Nous — então conectar o provedor depois precisará atualizar a documentação + o roadmap na mesma mudança.
  • Adiado para v1.147.0 (Phase 5b, parte 2): a seção H2 «Hermes & Telegram» da ajuda integrada × 17 idiomas e a superfície de marketing do cvstart.org.
  • Suíte: 2376 testes (+4: tests/hermes-docs.test.mjs).

[1.145.0] — 2026-08-12

Estatísticas úteis (cont.): um gráfico reconstruível — a aba “Tendência de cargos-alvo” em #/stats agora tem um widget Criar um gráfico: escolha uma métrica × dimensão e ele se recompõe ao vivo. Pedido de UX do usuário (sem parent-sync).

Adicionado

  • Gráfico reconstruível métrica × dimensão — escolha uma métrica (Vagas / Salário mediano / Salário médio) e uma dimensão (Por país / Por cargo) e o gráfico de barras se recompõe na hora. Métricas salariais respeitam a moeda + a alternância por ano ⇄ por mês; vagas são uma contagem simples.
  • 8 novas chaves i18n × 17 idiomas; snapshot 1200 → 1208.

Notas

  • Verificado no navegador (0 erros de console). Suíte: 2372 testes (+2).

[1.144.0] — 2026-08-12

Configurações e filtros (Fase 4, parte 1): ativar/desativar portais monitorados — agora você pode ligar ou desligar uma empresa vigiada em #/portals, e o scanner respeita isso. Pedido de UX do usuário (sem parent-sync).

Adicionado

  • Botão Ativar/Desativar por empresa em #/portals — um clique desliga um portal (o scanner EN já ignora empresas com enabled: false, então um portal desativado sai de todos os scans futuros) ou o liga novamente, com um aviso otimista.
  • POST /api/portals/toggle — uma escrita explícita do usuário que altera cirurgicamente e com validação de parse o flag enabled de uma empresa em portals.yml (comentários, ordem e demais campos preservados). 5 novas chaves i18n × 17 idiomas; snapshot 1195 → 1200.

Notas

  • A mudança no scanner foi zero — en-scanner.mjs já filtra enabled !== false. Suíte: 2370 testes (+3).

[1.143.0] — 2026-08-12

Compreensível (cont.): dicas ? nas telas de trabalho principais — o ? de ajuda agora cobre as nove páginas de ação principais, em todos os idiomas. Ajuste de UX reportado pelo usuário (sem parent-sync).

Adicionado

  • Dica de ajuda ? em mais 9 títulos de telas — #/scan, #/evaluate, #/cv-studio, #/tracker, #/config, #/deep, #/batch, #/auto, #/apply recebem um ? embutido (via HelpHint.title) que abre um popover localizado de «o que faz / como usar / o que esperar» — o mesmo componente seguro para CSP da v1.139.0.
  • 9 novas chaves i18n × 17 idiomas (help.hint.scan/…/apply); snapshot 1186 → 1195.

Notas

  • Verificado no navegador (0 erros de console). Suíte: 2365 testes (+1).

[1.142.0] — 2026-08-12

Correção: fim do arquétipo profissional “Unknown” — #/orientation agora sempre classifica entre os oito vetores de carreira nomeados em vez de às vezes responder “Unknown” e recomendar “dobrar a aposta” nele. Correção reportada pelo usuário (sem parent-sync).

Corrigido

  • #/orientation — o prompt de IA agora proíbe um arquétipo fora do conjunto. O modelo DEVE classificar os três primeiros entre exatamente os oito vetores nomeados e nunca responder “Unknown”/“N/A”/“dados insuficientes” nem inventar um rótulo. Com um currículo escasso, ainda nomeia os três mais próximos com menor confiança e diz que evidência falta, em vez de recusar.

Notas

  • Apenas mudança de prompt do servidor (buildOrientationPrompt); sem mudanças de i18n/esquema. Suíte: 2364 testes (+1).

[1.141.0] — 2026-08-12

Estatísticas úteis (cont.): enriquecimento de empresas financiadas — #/funded agora é mais visual: logos de empresa, um gráfico por valor de financiamento e cartões com rodada / valor / pontuação de descoberta / ação sugerida. Ajuste de UX reportado pelo usuário (sem parent-sync).

Alterado

  • #/funded — tabela plana → grade de cartões. Cada empresa recém-financiada é agora um cartão com logo (derivado do nome via CompanyLogo, com avatar de letra como fallback), chips de rodada + valor, a pontuação de descoberta e a ação sugerida do projeto pai, além do link e da data da notícia.
  • Visualização do valor de financiamento — um gráfico de barras das maiores empresas por valor divulgado; valores em texto livre (“$120M”/“€1.5B”) são convertidos em magnitude por um novo parseAmount. 3 novas chaves i18n × 17 idiomas.

Notas

  • Continua somente leitura sobre GET /api/company-funded; descrição e faixa salarial não estão na fonte de financiamento. Suíte: 2363 testes (+2).

[1.140.0] — 2026-08-12

Estatísticas úteis: números salariais mais ricos — o detalhamento salarial de “Meu pipeline” em #/stats agora mostra a média (não só a mediana), uma alternância por ano ⇄ por mês e uma tabela mín · média · mediana · máx por país. Primeira parte da Fase 3. Ajuste de UX reportado pelo usuário (sem parent-sync).

Adicionado

  • Salário médio (média) — RoleStats.salaryStats agora retorna avgUsd junto a minUsd/medianUsd/maxUsd. A mediana resiste a outliers; a média revela a assimetria, então juntas se leem como uma distribuição.
  • Alternância por ano ⇄ por mês e uma tabela mín · média · mediana · máx por país na seção salarial, com os seletores de moeda e período. 8 novas chaves i18n × 17 idiomas.

Notas

  • Os números ainda vêm só de anúncios com salário legível, normalizados para USD (indicativos). Suíte: 2361 testes (+1).

[1.139.0] — 2026-08-12

Compreensível: dicas de ajuda ? — um botão ? reutilizável e seguro para CSP que, ao clicar, explica «o que faz / como funciona / o que esperar» no seu idioma. Ajuste de UX reportado pelo usuário (sem parent-sync).

Adicionado

  • Popover de dica de ajuda ? (window.HelpHint) — um ? redondo ao lado de um título abre um popover leve, adaptado ao tema e espelhado em RTL, que exibe uma explicação localizada via UI.md(); acessível (role="tooltip", aria-expanded, fecha com Escape ou clique fora, foco restaurado) e seguro para CSP.
  • ? nas 5 abas de #/stats e em 8 títulos de telas de IA/analytics (career-plan, orientação, two-pager, networking, entrevista simulada, memória, funded, resumo semanal) — 14 novas chaves i18n × 17 idiomas.

Notas

  • Todas as telas já tinham um subtítulo de uma linha; o ? acrescenta a explicação mais detalhada sob demanda e faz os estados vazios se explicarem sozinhos. Suíte: 2360 testes (+4).

[1.138.0] — 2026-08-12

Geração no idioma da sua interface — cada geração por IA agora responde no idioma escolhido na interface, além de reforços de teste vindos da revisão. Ajuste de UX reportado pelo usuário (sem parent-sync).

Alterado

  • As gerações por IA agora respeitam o idioma da interface. Com a interface em russo, espanhol, japonês, … o texto gerado volta nesse idioma em vez de sempre em inglês. A diretiva de idioma de saída passa por todos os endpoints de geração — plano de carreira, orientação, relatório de mercado, entrevista simulada, plano de networking, «pergunte à documentação», a sugestão de nota de memória e o rascunho do two-pager. Código e identificadores permanecem em inglês (ex.: as chaves YAML do two-pager); só a prosa, títulos e itens são localizados.

Corrigido

  • Guarda de papel de cor CSS (tests/css-role-tokens.test.mjs) — um canário estático de que os tokens-alias do modo escuro da v1.137.0 nunca invertem o papel: tokens de texto (--fg/--danger/--ok/…) nunca como background, e os de superfície (--card/--panel/--line/…) nunca como color de texto, em todo o CSS e nos estilos inline da SPA.
  • Auto-sonda do carregador XSS do UI.md() — o teste que carrega md() de api.js agora testa md('<script>…') logo após a extração e lança erro se o escape faltar, para que um corte errado futuro falhe ruidosamente em vez de deixar a suíte de segurança verde.
  • Guarda de scroll em #/career-plan — o scrollIntoView pós-geração só roda se a prévia ainda estiver conectada ao documento.

Notas

  • docs/UX-ROADMAP.md atualizado: as dicas de ajuda ? + descrições de página + estados vazios passam para v1.139.0; um provedor Nous Research / Hermes — com guia de deploy em servidor cloud + Telegram e um skill de Hermes — é registrado como Fase 5 / 5b.
  • Suíte: 2356 testes (+5).

[1.137.0] — 2026-08-11

Correções de legibilidade e renderização — contraste no modo escuro, rótulos de gráficos e o plano de carreira. Um ajuste de UX reportado por usuário (sem parent-sync).

Corrigido

  • Branco-sobre-branco / preto-sobre-preto no modo escuro em muitas telas — quinze propriedades CSS customizadas que várias views referenciavam (--fg, --panel, --panel-2, --ok, --danger, --card, …) nunca haviam sido declaradas, então recaíam em valores fixos claros/pretos: tudo bem no modo claro, ilegível no modo escuro (os chips de visão geral do #/pipeline, a aba ativa do #/stats, “Ativo / Chaves” + “✓ definido” do #/config, as seções do #/two-pager, o balão de pergunta do #/mock-interview, texto de erro). Agora elas são vinculadas por alias aos tokens reais que respeitam o tema, então seguem o tema automaticamente — 0 falhas de contraste WCAG-AA em todas as 29 views, verificado por um auditor automatizado; a aba ativa do #/config passou a usar um estilo tintado legível. Uma guarda de regressão (tests/dark-theme-tokens.test.mjs) mantém esse alias.
  • Os rótulos de gráfico do #/stats eram cortados no meio da palavra (“Senior Backend Engineer” → “…Enginee”) — agora eles recebem elipse (…), mantendo o rótulo completo como tooltip ao passar o mouse.
  • O #/career-plan exibia o plano gerado como Markdown bruto — agora ele é renderizado automaticamente como texto formatado e legível (o Markdown editável permanece na caixa de texto; o botão Visualizar alterna entre os dois).

Notas

  • #/career-plan, #/two-pager, #/stats e o resumo semanal de entrevistas não estão quebrados — eles exibem estados vazios até que você gere um plano / tenha dados. Orientações mais claras na tela e dicas de ajuda ? estão planejadas para os próximos passos (docs/UX-ROADMAP.md).

[1.136.0] — 2026-08-11

Paridade com o career-ops pai v1.26.x (mainline pós-v1.26.0) — uma nova fonte zero-auth, além de uma onda de portes de qualidade e robustez para os trechos de código que o web-ui espelha. O registro agora soma 79 fontes = 74 em inglês + 5 russas (ALL_ADAPTERS 74).

Adicionado

  • eightfold (Eightfold AI, #2684) — boards de aquisição de talentos via a API zero-auth https://<tenant>.eightfold.ai/api/apply/v2/jobs, com host fixado em *.eightfold.ai (o CNAME de marca careers.<company>.com é deliberadamente rejeitado); paginado com um limite de segurança, lançamento de erro para board morto (dead-board-throw) e deduplicação de URL. Fonte + adaptador + suíte isolada para CI; aparece no filtro de Fonte do #/scan e na landing.

Corrigido

  • Chaves de deduplicação e de cargo com suporte a Unicode (#2569 / #2587 / #2667) — uma nova normalizeTextKey compartilhada (NFKC, mantém letras/marcas/dígitos de qualquer script) substitui as chaves somente-ASCII: o detect-reposts agora agrupa variantes de empresa com diferenças de largura/pontuação (“Acme, Inc.” ≡ “Acme Inc”) e nunca colapsa empregadores distintos não latinos, enquanto o role-matcher normaliza títulos em largura total (full-width) e preserva tokens de cargo não latinos em vez de eliminá-los.
  • fetchJsonWithRetry não tenta mais novamente um redirecionamento recusado (#2657) — uma guarda redirect:'error' que encontra um 3xx é determinística, então agora é não repetível (non-retryable) e falha rapidamente em vez de consumir o orçamento de novas tentativas.
  • Grupos AND em title_filter.positive (#2552) — um + delimitado por espaço dentro de uma entrada positiva agora exige que todos os termos apareçam no título, em qualquer ordem.
  • oraclecloud passa a aceitar os domínios-raiz numerados de tenant oraclecloud1.com … oraclecloud99.com (#2683) — uma família limitada (sem zero à esquerda, ≤ 2 dígitos), nunca um domínio-raiz com wildcard.
  • workable reforçado (#2675) — novas tentativas, cabeçalhos que imitam um navegador, e serialização de requisições contra o host protegido por Cloudflare.
  • personio recorre a uma extração (scrape) de HTML quando o feed XML está desabilitado, em vez de retornar vazio.
  • Aliases FALLBACK de states resincronizados com o pai (#2615).

Notas

  • Não portado (não espelhado pelo web-ui, ou exclusivo de CLI): reply-matcher (#2672), jd-similarity (#2661), jd-skill-gap (#2686), os caminhos de varredura por variável de ambiente do scan (#2568) / o parsing de --flag=value (#2589), e as mudanças em cover-letter / template de CV / doctor / ollama / generate-pdf. Os avisos HIGH de js-yaml/nanoid para a web já haviam sido corrigidos no web-ui v1.135.0.

[1.135.0] — 2026-08-11

Paridade com o career-ops pai v1.26.0 — cinco novas fontes de varredura zero-auth, além de correções de precisão em quatro boards que o web-ui já possui. O registro agora soma 78 fontes = 73 em inglês + 5 russas (ALL_ADAPTERS 73).

Adicionado

  • Cinco novas fontes de varredura (cada uma com fonte + adaptador + suíte isolada para CI; aparecem no filtro de Fonte do #/scan e na landing do cvstart.org):
    • join (JOIN) — o board JOIN de uma empresa a partir do __NEXT_DATA__ do Next.js em join.com/companies/<slug> (host fixado, com limite de páginas).
    • getro (Getro) — boards de portfólio de “rede de talentos” de VCs via a API pública POST api.getro.com, paginada da mais recente para a mais antiga; cada vaga é atribuída à empresa do portfólio, não ao fundo.
    • consider (Consider) — boards de portfólio de VCs do getconsider.com via um POST de mesma origem; o host configurável é fixado por uma guarda estrutural de SSRF (somente host HTTPS público).
    • joinup (JOINUP) — o board suíço joinup.ch, lendo a página mais recente renderizada no servidor (SSR); falha fechada (fail-closed) em caso de quebra do scraper.
    • remotli (Remotli) — remotli.ch, vagas remotas em empresas suíças (salários em CHF); emite a própria URL de candidatura do ATS do empregador, para que listagens cruzadas sejam deduplicadas.

Corrigido

  • a16z Speedrun não aborta mais o board inteiro em uma falha transitória — as buscas de página agora passam por um fetchJsonWithRetry compartilhado (novas tentativas limitadas apenas para 429/5xx/timeout transitórios, nunca para um 4xx permanente), e o orçamento de páginas foi redimensionado para a página de 50 vagas.
  • arbeitsagentur migrado para a API Jobsuche v6 (/pc/v6/jobs) — o endpoint v4 antigo retorna 404; o formato da resposta foi renomeado e a filtragem remota agora é restringida no lado do servidor.
  • thehub migrado para a API v2 jobsandfeatured — as linhas não trazem data de publicação e são isentas do filtro de idade.
  • hackernews agora encontra a thread mensal “Who is hiring?” de forma confiável, filtrando a busca no Algolia pela tag de conta author_whoishiring, em vez de uma consulta em texto livre.

Notas

  • Não portado (o web-ui já é seguro, absorvido via retransmissão, ou é exclusivo de CLI): as chaves de deduplicação de vaga / correspondência de empresa com suporte a Unicode (o agrupamento de repostagens do web-ui já usa uma chave de empresa em minúsculas simples, então empregadores distintos não latinos nunca colapsam); o sinal de latência de rejeição do follow-up + os ajustes do company-funded (retransmitidos somente leitura, fail-soft); os caminhos de varredura configuráveis por variável de ambiente e o parsing de --flag=value (o web-ui roda os scanners em processo); a refatoração de consolidação do User-Agent (o web-ui já a centraliza); e os itens exclusivos de CLI (lista de conteúdo não confiável, oferta/offer-prep, doctor, mudanças de template de carta de apresentação/CV).

[1.134.1] — 2026-08-05

Fortalecimento de validação — correções reveladas por uma auditoria completa do projeto.

Corrigido

  • successfactors não descarta mais as vagas coletadas em uma falha no meio da varredura (regressão no port do lançamento de erro para boards mortos da v1.134.0) — seu loop de paginação não tinha try/catch, então uma falha na página 2 ou posterior (depois do sucesso da página 1) lançava erro e descartava tudo o que já havia sido coletado; e se essa falha fosse um 404 (um startrow fora do intervalo), o en-scanner colocaria em quarentena um tenant ativo como morto por dias. Agora espelha phenom/radancy: uma falha na página 0 ainda lança erro (board morto), mas uma falha em página posterior preserva os resultados parciais.
  • Os chips de filtro do #/scan agora são operáveis por teclado (WCAG 2.1.1) — os chips de faceta (e o chip “limpar”) eram spans com um manipulador de clique, mas sem tabindex/role, então usuários de teclado e de leitor de tela não conseguiam alcançá-los ou alterná-los. Agora eles têm role="button", tabindex="0", aria-pressed e ativação por Enter/Espaço.
  • Três strings fixas em inglês agora estão localizadas — o tooltip do selo de confiança do #/scan, o cabeçalho da coluna de realocação do #/scan e o cabeçalho de pontuação do #/dashboard eram literais isoladas que o gate de paridade de i18n não conseguia detectar (nunca foram chaves), então permaneceram em inglês em todos os idiomas não ingleses. Agora com scan.trustTip + scan.col.reloc (2 novas chaves) e reaproveitamento de track.col.score, com uma guarda estática de fonte.

[1.134.0] — 2026-08-05

Paridade com o career-ops pai v1.25.0.

Adicionado

  • Nova fonte de varredura: getManfred (manfred) — um feed de todo o board com vagas de tecnologia da Espanha/UE com salários publicados, a partir de www.getmanfred.com/api/v2/public/offers (zero-auth, host fixado + somente HTTPS, catálogo completo em uma única requisição). Fonte + adaptador + uma suíte isolada para CI (tests/sources-manfred.test.mjs); o registro agora soma 73 fontes = 68 em inglês + 5 russas (ALL_ADAPTERS 68). Aparece no filtro Fonte de #/scan e na landing do cvstart.org.

Corrigido

  • O feed da a16z Speedrun estava truncando silenciosamente em 50 vagas (#2404) — o feed limita uma página a 50, mas a fonte solicitava PER_PAGE = 100, então a paginação parava depois da página 1. Corrigido para 50.
  • Boards mortos agora lançam erro em vez de serem lidos como “ativos, porém vazios” (#2379) — cryptocurrencyjobs, phenom, radancy, successfactors: uma falha de busca em que nenhuma requisição jamais se resolve agora lança um erro (para que a saúde de #/portals e a varredura registrem uma falha real), em vez de a engolir e resultar numa lista vazia; uma falha no meio da varredura, após pelo menos um sucesso, preserva os resultados parciais.
  • workable agora usa a API pública do widget (#5ab8425) — trocado para apply.workable.com/api/v1/widget/accounts/<slug>, que retorna a lista completa de vagas de uma conta grande em uma única requisição, então contas grandes não são mais truncadas.

Notas

  • Não portado (exclusivo de CLI ou não espelhado pelo web-ui): a reescrita de performance com bucketing por título do detect-reposts #2389; as correções de chave de empresa Unicode (a deduplicação do próprio tracker do web-ui já é segura para não-Latin); scan --since; cv-facts; a auditoria do template de CV / PDF; doctor; a diretiva de conteúdo não confiável dos modos.

[1.133.1] — 2026-08-02

Corrigido

  • #/funded (Empresas financiadas) agora renderiza os resultados — dois bugs faziam a tabela sempre mostrar “nenhuma empresa financiada”, mesmo quando o company-funded.mjs do pai retornava uma lista completa. (1) A view lia os resultados em res.candidates, mas o pai os emite em companies (cada uma { company, amount, round, funding: { sources: [{ source, url, observed_date }] } }); o cliente agora lê a chave correta e mapeia o formato real da evidência. (2) A tabela de resultados passava suas células para UI.el('tr', {}, …) como varargs, mas UI.el(tag, attrs, children) espera children como um único nó ou um array, então só a primeira coluna (Empresa) era renderizada — as células agora são passadas como um array. Verificado em um navegador real: 11 empresas nos quatro feeds são renderizadas com as colunas Empresa / Sinal de financiamento / Fonte / Data e links de evidência funcionais, zero erros no console. Uma passagem vazia agora também exibe os diagnósticos por fonte, para que um dia de notícias parado seja distinguível de um feed bloqueado.
  • Guardas de regressão em tests/parity-routes-v1133.test.mjs: o script falso do pai agora emite o formato real de saída companies (a fixture original espelhava o formato errado candidates — motivo exato pelo qual o bug foi lançado em verde), além de canários estáticos de fonte que garantem que funded.js lê res.companies (nunca res.candidates) e constrói as linhas da tabela com filhos em array (+1 → 2144).

[1.133.0] — 2026-08-01

Adicionado

  • Descoberta de empresas financiadas (#/funded, paridade com o pai #2117) — uma nova view somente leitura que retransmite o company-funded.mjs do career-ops pai via GET /api/company-funded: uma lista para revisão prévia de empresas recém-financiadas, descobertas a partir de feeds públicos de financiamento com host fixado (TechCrunch, PR Newswire, The Guardian, Hacker News). A retransmissão executa o script com --json --dry-run (JSON para stdout, sem escrita de arquivos), nunca encaminha entrada do usuário para --sources, aplica limite de taxa e é acionada pelo usuário (um botão Descobrir, nunca ao montar a página). Novo módulo de rota server/lib/routes/funded.mjs + public/js/views/funded.js, em Sourcing.
  • Resumo semanal de entrevistas (#/interview-digest, paridade com o pai #2129/#2130) — uma nova view somente leitura que retransmite o weekly-digest.mjs zero-LLM do pai via GET /api/interview/weekly-digest: uma consolidação mecânica das notas de sessão de entrevista — quais empresas e rodadas você teve entrevistas nesta semana, competências recorrentes e lacunas em aberto (best-effort). O intervalo opcional ?from=&to= só é encaminhado quando AMBOS os valores são YYYY-MM-DD válidos; um intervalo vazio é um resumo available:true válido. Adicionado a server/lib/routes/interview.mjs + public/js/views/interview-digest.js, em Analytics.
  • Ambas as retransmissões seguem o contrato consolidado de fail-soft available:false para quando o script do pai estiver ausente (CI, instalações standalone). 26 novas chaves de i18n ×17 locales; suíte isolada para CI tests/parity-routes-v1133.test.mjs (+5 → 2143).

Notas

  • O career-ops pai avançou além da v1.24.0 com a página Follow-up Tracker (#1422) do app web/ em Next.js e a renderização de PDF no backend (#2182) — não portadas: o web-ui já tem sua própria retransmissão de follow-up e seus próprios runners de PDF, e o robustecimento subjacente do followup-cadence.mjs chega de graça via a retransmissão por shell-out. As mudanças em set-status.mjs / tracker-utils.mjs são internas à CLI e não são espelhadas.

[1.132.0] — 2026-07-31

Alterado

  • Subsistema de renderização de resultados do #/scan extraído para public/js/lib/scan-results.js (amortização do contrato de tamanho de arquivo — public/js/views/scan.js havia crescido para ~1254 LOC). O subsistema — renderResults, buildChipRow, os construtores de linha/facetas, os pintores de opções e o espelho do registro FALLBACK_SOURCES — passa para uma fábrica window.ScanResults.create(ctx) que fecha sobre um objeto de contexto fornecido pela view. Nenhuma mudança de comportamento — as funções foram movidas literalmente (verbatim) e suas variáveis de closure foram religadas para ctx.*; scan.js agora tem ~906 LOC (uma segunda passada de extração rumo à meta de 800 LOC está planejada).
  • Novo portão de regressão no navegador — tests/playwright-scan-filters.mjs semeia um data/last-scan.json fixo e conduz cada filtro de #/scan, verificando contagens exatas de linhas, de modo que a extração é validada contra o comportamento real do navegador.
  • Banner do README enxugado — a longa narrativa por versão de “Última versão” é aposentada em favor de um resumo de uma linha + um link para o changelog completo (este arquivo).

[1.131.2] — 2026-07-31

Alterado

  • app.css dividido em três folhas de estilo ordenadas (débito do contrato de tamanho de arquivo — o arquivo único havia crescido para ~1990 LOC, bem além da meta rígida de 800 LOC). Agora são app.css (~672 — a11y, tokens de design/tema, sidebar, main, botões, content-shell), components.css (~595 — cards, grids, paginador, badges, tabelas, formulários, log/console, markdown, seletor de idioma, filtro de chips, banner de conexão) e overlays.css (~737 — toast, gaveta de notificações, modal, misc/responsivo, o espelho [dir="rtl"], docs-fab, usage-hud), cada uma dentro do limite rígido.
    • O corte é contíguo e na mesma ordem, então a cascata é byte a byte idêntica ao arquivo pré-split; index.html carrega as três como <link>s ordenados. Nenhuma mudança de comportamento, markup ou i18n.
    • Os testes que verificam CSS agora leem a concatenação via um helper compartilhado tests/helpers/css.mjs::loadAppCss(). O novo tests/css-modularization.test.mjs trava o split (arquivos existem · cada um ≤ 800 LOC · ordem dos links no index.html) → suíte 2138. Verificado no navegador: as três folhas de estilo são interpretadas (parse) e suas regras se aplicam.

[1.131.1] — 2026-07-31

Corrigido

  • Consistência de fixação de host (host-pinning) dos adaptadores nas duas fontes da v1.130.0 (desdobramentos de revisão de código, defesa em profundidade; sem mudança de comportamento para entradas válidas):
    • O adaptador a16z-speedrun-talent agora revalida o override api: / a16z-speedrun-talent: em buildEndpoint (HTTPS + host exato speedrun-talent-network.com) e recai para o feed canônico quando a validação falha — paridade com o adaptador cryptocurrencyjobs, de modo que um valor de host diferente nunca alcança o slot de busca (antes dependia só da guarda assertSpeedrunUrl em tempo de busca). A checagem de host exato agora é uma única SPEEDRUN_TALENT_HOST_RE exportada, compartilhada pela guarda e pelo adaptador.
    • O parser cryptocurrencyjobs — cleanUrl agora usa a mesma guarda de host por correspondência exata que assertCryptocurrencyJobsUrl e o override do adaptador (antes era endsWith, que aceitava subdomínios). O parser nunca é mais permissivo que a guarda de SSRF: um link de item sub.cryptocurrencyjobs.co é descartado.
    • +2 testes → suíte 2135.

[1.131.0] — 2026-07-31

Adicionado

  • Board de abas de estágio (CRM) do #/tracker (portado da view /pipeline do web app do pai). A barra de chips de funil + dropdown de status do tracker são substituídos por uma faixa de abas de estágio: uma aba All mais uma aba por status canônico — Evaluated · Applied · Responded · Interview · Offer · Rejected · Discarded · SKIP · Hired — cada uma mostrando uma contagem ao vivo do histórico completo, incluindo estágios com contagem zero para que o funil completo esteja sempre visível (o visual CRM). A aba ativa comanda o filtro; clicar nela de novo limpa de volta para All. As linhas mantêm o tom de score, legitimidade, PDF e as ações de relatório, e a célula da empresa agora mostra a marca da empresa (logo) quando os logos estão habilitados (desabilitado por padrão → zero requisições extras).
    • Nova rota somente leitura GET /api/tracker/stages retorna o funil canônico (rótulos em ordem) + um mapa de dobra de aliases, extraído de server/lib/states.mjs (templates/states.yml, com o fallback embutido) — assim o cliente nunca fixa a whitelist de status. A resposta legada de GET /api/tracker sem parâmetros permanece inalterada (só { rows }).
    • Nova lib de cliente pura e testada por unidade public/js/lib/tracker-stages.js agrupa as linhas nos estágios do servidor, tolerando negrito markdown perdido e aliases localizados (ex.: aplicado → Applied). As abas são acessíveis (role tablist/tab, aria-selected, área de toque ≥44 px, contagens no nome acessível de cada aba). Sem novas chaves i18n. Suíte 2133.

[1.130.0] — 2026-07-31

Adicionado

  • Duas novas fontes de varredura portadas do career-ops pai v1.24.0 (em processo, sem novas dependências; ambas aparecem no filtro Fonte de #/scan e na landing do cvstart.org):
    • a16z Speedrun (a16z-speedrun-talent, #2231) — o feed JSON de todo o board da rede de talentos do a16z Speedrun. Fixado no host speedrun-talent-network.com, somente HTTPS, paginação indexada a partir de 0 com limite de páginas, q/config encadeados por empresa, fail-soft.
    • Cryptocurrency Jobs (cryptocurrencyjobs) — o board de vagas Web3 cryptocurrencyjobs.co, ingerido via seu feed RSS 2.0 público (zero-auth). Decodificação de entidades XML em duas passagens, vagas somente remotas, empregador extraído da cauda do título "… at <Company>".
    • O registro agora soma 72 fontes = 67 em inglês + 5 russas (ALL_ADAPTERS = 67 adaptadores de portais em inglês).

Corrigido

  • echojobs — vagas híbridas continuam distinguíveis do remoto (espelha o #2258 do pai). Um marcador hybrid sem distinção de maiúsculas/minúsculas agora produz "<City> · Hybrid" (ou um Hybrid isolado quando não há cidade) e workplaceType: 'Hybrid', em vez de ser colapsado em Remote.
  • radancy — markup legado do TalentBrew + transporte via fragmento JSON de resultados (espelha o commit a3e6df9 do pai), condicionado a um opts.fetchJson injetável.

Notas

  • Não portado — recursos do pai exclusivos de CLI. A ampla superfície de CLI/modos da career-ops v1.24.0 permanece fora do web-ui, que é um visualizador + escrita fina, não um host de modos: as tabelas de compliance/jurisdição, a agenda de contatos + vCard, o transcript-debrief de entrevista / detecção de plataforma de chamada, o set-status do ledger, o registro de outcome, a triagem em duas passagens, o jd-similarity, o schema versionado de artefato de CV de candidatura, a detecção do Playwright-MCP no doctor, e portals/fix-slugs.mjs. Mudanças de orquestração de varredura que vivem no scan.mjs do pai — o scanner Playwright do Interamt.de, a varredura reversa completa do iCIMS, o filtro de elegibilidade remota por país, o espaçamento de lookups de DNS, a deduplicação rltr do StepStone e a coluna de empresa normalizada no histórico de varredura — não se aplicam: o web-ui roda os scanners EN/RU em processo e não invoca scan.mjs.
  • Já coberto. A correção de dobra de acentos do role-matcher (#2209) foi portada na v1.127.0, então é um no-op aqui.

[1.129.1] — 2026-07-29

Corrigido

  • Desdobramentos da revisão de IA sobre os ports web de v1.128/v1.129 (todos consultivos, corrigidos na origem): precedência de nível em job-facets.js (um modificador explícito agora vence uma palavra de gestão — Senior Engineering Manager → senior, antes lead); o fallback de states.mjs não é mais fixado (uma leitura bem-sucedida é memoizada, o fallback volta sem cache, então um pai momentaneamente indisponível no boot é relido depois) e avisa (console.warn) num arquivo presente mas malformado; score-tone.js — uma linha sem pontuação é neutra (muted), não vermelha; domainFromName() ignora slugs não-ASCII antes do /api/logo; +uma verificação de isolamento em tests/states.test.mjs. +4 testes → 2073.

[1.129.0] — 2026-07-29

Adicionado

  • Faceta de nível + coluna de idade em #/scan — a lib job-facets.js da v1.128.0 agora está integrada na UI de varredura (antes só lógica). Um novo dropdown Nível classifica o título de cada vaga em lead/staff/sênior/pleno/júnior/estagiário (JobFacets.seniorityFromTitle) e se autopreenche com o que há nos resultados —como a faceta de País—; títulos sem palavra de nível sempre passam. Persiste em buscas salvas, Redefinir e Aplicar. A tabela ganha uma coluna Nível (badge) e uma coluna Idade sem tokens (hoje / Nd, de JobFacets.daysSince). 12 chaves i18n ×17, +3 testes → 2069.

[1.128.0] — 2026-07-29

Adicionado

  • Quatro soluções portadas do próprio web app do pai (../web/, Next.js), reimplementadas em JS vanilla/ESM, sem novas dependências: (1) server/lib/states.mjs lê templates/states.yml ao vivo como vocabulário canônico de status do tracker (com fallback para CI) — elimina o re-sync manual da whitelist a cada release; POST dobra aliases (espanhol/legado) ao rótulo canônico, o funnel do GET agrupa por status canônico; (2) logos de empresa em linhas com host ATS via domainFromName() (~90 marcas→domínio) antes do avatar de letra; (3) score-tone.js — tom de pontuação de 4 níveis (≥4.2/3.8/3.0 + fallback de letra); (4) job-facets.js — facetas seniority/source/days. +21 testes.

Notas

  • Não portado (só conceito): a camada de ações agênticas do pai (actions/registry.ts + api/assistant/route.ts) — blueprint para quando docs-fab virar copiloto. Sem novas fontes (registro 70), sem mudanças i18n/help.

[1.127.0] — 2026-07-29

Adicionado

  • Três novas fontes de varredura (paridade com career-ops v1.23.0) — o registro agora traz 70 adaptadores (65 EN + 5 RU): Flowxtra (agregador global sem auth), VDAB (API por palavra-chave do serviço público de emprego flamengo) e iCIMS (portais careers-<tenant>.icims.com, distinto de jibeapply). Além disso Cursor volta ao roster de CLIs (parent #2115): cli-detect agora detecta cursor (10 ferramentas) e o roster é restaurado em help/README/config ×17.

Corrigido

  • agenticjobs migrou de scraping HTML para a API REST (#2167); Greenhouse recupera a cidade do endpoint /offices quando location.name é só um modelo de trabalho (#2104); paridade do role-matcher (#1933/#2164/#2009: prefixo MTS, base product, dobra de acentos, desacordo sub-baseline).

Notas

  • Não portado. A maior parte da v1.23.0 é superfície CLI/dashboard sem uso no web-ui (batch-tailor, discover-ats, modos NL/PT, temas de PDF, dashboard Go, updater/doctor); os scripts relayados não exigem mudança. VERSION do pai → 1.23.0.

[1.126.1] — 2026-07-25

Corrigido

  • Dois pontos de desvio do roster de CLI que o resync da v1.126.0 perdeu — (1) o intro da aba API keys de #/config (config.providerModelNote, i18n ×17) listava só 7 CLIs — agora Antigravity e Grok Build são inseridos após OpenCode; (2) uma segunda linha de tabela comparativa no guia de ajuda (×17) e o help do site (construído no CI) ainda diziam Inside Claude Code / Codex / Cursor / Gemini CLI — o conjunto obsoleto com Cursor — agora o roster completo. Ambos usavam separadores barra/ponto-médio que os padrões da varredura v1.126.0 não cobriam. Snapshot i18n regenerado; suíte permanece em 1969.

[1.126.0] — 2026-07-25

Adicionado

  • A aba de ferramentas AI CLI agora detecta as 8 CLIs de primeira classe do career-ops — o roster de #/config foi sincronizado com o docs/SUPPORTED_CLIS.md do pai: server/lib/routes/cli-detect.mjs ganha Grok Build CLI (grok) e Kimi CLI (kimi), e a Antigravity agora testa primeiro seu binário canônico agy. A varredura de PATH somente-leitura agora reporta 9 ferramentas; continua sem executar nenhum binário encontrado.

Alterado

  • Ressincronização da documentação com career-ops.org/docs — cada superfície de docs foi reconciliada com as páginas vivas do pai (as 31 lidas). O roster canônico de assistentes IA (help ×17 + README ×17) lista agora as 8 CLIs de primeira classe — Claude Code, Codex, OpenCode, Antigravity CLI, Grok Build CLI, Qwen Code, Kimi, GitHub Copilot CLI — mais Gemini CLI (wrapper legado). Os pacotes de ajuda mantêm sua estrutura de 29 H2 / 105 H3.

[1.125.4] — 2026-07-23

Alterado

  • dependências do site (dependabot #151–#153) — sharp 0.34.5→0.35.3, svgo 4.0.1→4.0.2, fast-uri (dev) 3.1.3→3.1.4 em site/; build Astro verde, sem impacto em SPA/servidor.

Notas

  • Varredura de paridade com o pai (career-ops 37d17ec..254764a, pós-v1.22.0) — nada a portar: o guard de linha errada do set-status (#2108) é só CLI (no web-ui as linhas do tracker são selecionadas explicitamente e nenhuma rota chama set-status.mjs), o Risk Summary dos modos localizados (#2109) toca arquivos modes/<lang>/ que o web-ui nunca lê (apenas modes/*.md de nível superior), a verificação de manifesto do update-system (#2111) é só do atualizador, e o resto são docs do pai (README turco, SIGNATURES ×4, SCRIPTS.md, acentos es). O VERSION do pai continua 1.22.0 — parentVersion inalterado.

career-ops-ui v1.267.1 · page 4/9 · mirror of the project changelog for cvstart.org