Changelog
GitHubTodas 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-Afterdo quadro é respeitado (mas limitado, para que umRetry-After: 86400hostil não trave a varredura inteira). Erros permanentes (404, redirecionamentos recusados) continuam falhando de imediato — sem mudança.
Notas
- Novos
parseRetryAfterMs()e o purocomputeRetryDelayMs()emserver/lib/http-json.mjs;fetchJsonagora captura.retryAfterem uma resposta não-ok efetchJsonWithRetryrecebe ummaxDelayMsopcional (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_collectionnumérico procurado à mão. Informe a própriacareers_urldo quadro e o id se resolve sozinho a partir dessa página na primeira varredura — um único GET seguro contra SSRF lê onetwork.idnumérico direto dos dados embutidos da página. Umgetro_collectionexplícito ainda prevalece e pula a busca por completo.
Notas
- Novos
httpsCareersUrl(),extractCollectionId()e o assíncronoresolveCollectionId()emserver/lib/sources/getro.mjs; a página do quadro é buscada viasafeGet(DNS fixado, tamanho limitado), e o id resolvido continua preso ao hostapi.getro.comporassertGetroUrl. O adaptador agora corresponde a uma entradaprovider: getroque carrega umacareers_urlhttps 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 noportals.ymlagora 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 emserver/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.tsvgrande. O agrupamento de títulos por empresa era um laço aninhado que pagava umroleFuzzyMatchcompleto 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
groupRowsByTitleemserver/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 paraExternal, 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 comoen-US); o caso documentado/en-US/Externalnã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#/statsde 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 rodaverify-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_tiersagora 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ãoskip_tiers: [senior]ou[entry]não as filtrava.
Notas
- Lookbehind independente de alfabeto em
server/lib/classify-tier.mjs; removido um matcherSr.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:emportals.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ãoskip_tiersera ignorado silenciosamente. Títulos sem palavra de nível caem em mid (entãoskip_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.mjspuro (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 sobjds/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 deauto-fill). Protegido portests/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, …), porhttpvshttps, 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 comogh_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 ooverflow-wrap: anywheredo 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 i18nrep.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 Summarytrazia 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 umX / 5real no corpo.parseReportHeaderagora 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 helperfromCodePoint) — cópias que haviam divergido (três podiam lançarRangeError, corrigido em v1.172.0; outras admitiam NUL/C0 ou faziam parse errado dea2;). Agora todas passam pelo únicoserver/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.hhmanté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 ouser-agent.mjsdo pai; uma versão antiga é mais propensa a ser bloqueada. Protegido por um testeChrome major ≥ 151. - FALLBACK de estados do tracker (GAP #5) — o
FALLBACKde último recurso emstates.mjs(usado só quando otemplates/states.ymlao 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 Unauthorizedna API de busca a menos que esses cookies voltem junto com o token bearer anônimo.sources/csod.mjsagora lê o bootstrap com um novo helperfetchResponse, monta um cabeçalhoCookiea partir dos valoresSet-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 queREPORT_LABELS.runão enumera (só conhece “Оценка”/“Балл”), então a pontuação ficava sem parse. Em vez de ampliar a lista de sinônimos,parseReportHeaderagora 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 como5/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.descde 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 novotests/site-meta-desc-parity.test.mjs(isolado de CI) falha se algum dos 17site/src/i18n/*.jsonperder o placeholder ou refixar uma contagem, ou seLanding.astroparar de interpolá-lo nas três metadescrições. - Strip de legitimidade seguro a nulos —
stripEmphasisretorna''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.parseReportHeaderagora 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-pillganha 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áriohermes).server/lib/routes/cli-detect.mjsamplia 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,gemedassaultdecodificavam entidades HTML numéricas com uma verificaçãoNumber.isFiniteinsuficiente antes deString.fromCodePoint— uma referência acima de0x10FFFF(ex.:�de um feed malformado ou malicioso) lançava umRangeErrornão capturado e abortava toda a análise daquela fonte. Um módulo compartilhadoserver/lib/html-entities.mjs(espelho do_html-entities.mjsdo projeto pai) agora restringe as referências numéricas ao conjunto Char do XML 1.0 §2.2, entãoString.fromCodePointnunca lança, e distingue hexadecimal de decimal separadamente para quea2;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
⏱ ~Nsao lado, como a ETA de#/auto. Estilo.eta-hintcompartilhado + 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=1serve o MESMO arquivo saneado comContent-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
#/cvagora é 👁 Pré-visualizar ao lado de ⬇ Baixar.cv.openPdfalterado 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. Apenasmin-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
--elevpassa a sustentar as superfícies elevadas (#eef1f6claro /#1e232eescuro, distinto de--slate); as linhas ficam em--slate. Os achados restantes (D-2, D-4, D-5, P4-ETA) ficam como backlog emdocs/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.mde 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.twoPageretwoPager.titleconcordam 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 emindex.htmlcombina; oaria-labelpreserva 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-hintagora é 24×24 (o alvo mensurável) enquanto o anel visível continua 18px via um::beforecentralizado — 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-mutedcom “Pontuação não detectada” (×17) e dica “Abra o relatório…”. O cartão continua umrole="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
parseReportHeaderagora analisa o bloco YAML invariante## Machine Summary(score:/legitimacy:/date:, a mesma fonte que oauto-pipelinejá 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 doh1.textContentbruto, então a aba mostrava «Vacancy search?» em vez de «Vacancy search».router.js::focusNewViewagora clona o cabeçalho, remove.help-hinte lê o texto; a «?» visível do cabeçalho fica intacta. - cvstart.org mostrava «17 AI providers» em vez de «7». O helper
sub()deFeatures.astroreescrevia 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.jsoninalterada. 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_PROVIDERsem chave (ex.:LLM_PROVIDER=claudedoinit) travava; agora recorre à ordem auto entre os provedores configurados (emselectActiveProvider+ ambas as cascatas de despacho). - O gating do cliente (
#/deep+ views mode-page) agora usawindow.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) escan/filters.js(máquina de estado de filtros), via sacosctx/refs;scan.jsconecta ambos.
Corrigido
- CodeQL
js/useless-assignment-to-local(#428) emconfig/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) econfig/tab-controller.js(fábrica da barra de abas);config.jsos 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-urlsobreapply-url).
Alterado
- Resolução de
companyEId: (1)company_eid:no portal, (2) oc=de umapi:explícito, (3) descoberta pela página do board.fetchText(http-json.mjs) anexa.location/.retryAfterao erro non-ok (somente leitura, retrocompatível).
Notas
- Segurança — dois hosts fixados (
jobs.jobvite.com,app.jobvite.com) viaassertJobviteUrl: só https, allowlist estrita, nenhum redirecionamento seguido. OcompanyEIdé 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
#/confignão conseguia forçar o Hermes — o menuLLM_PROVIDERlistava só seis provedores, então dava para definirHERMES_API_KEYmas não forçar o Hermes pela UI. Agorahermesé a 8.ª opção, e um novo teste de paridade impede o menu de divergir deLLM_PROVIDERS.- Chaves locais curtas eram rejeitadas em silêncio — o piso de 20 caracteres de
isUsableKeyfoi calibrado para chaves de nuvem;hasHermesKeyusa 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) edocs/sdd;hermesChatUrlcompleta 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) —
runHermesno cliente compartilhadorunOpenAICompatible(server/lib/openai.mjs), em ambas as cascatas (llm-dispatch.mjs+routes/llm.mjs), na cauda da ordem auto + o pinLLM_PROVIDER=hermes,/api/status/providersellm-pricing.mjs. Alcança uma base URL local configurável (padrãohttp://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ãohermes-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 skillhermes-bridgee 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é umaSECRET_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,#/usageremoveram seustyle: { 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 (
#/dashboardwidth:100%,#/pipelineborder: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
.emptynão muda); snapshot do dicionário 1208. Verificado no navegador (o painel vazio de#/usagecalcula 48px de padding + borda tracejada, 0 erros de console). - O novo canário
tests/empty-state-consistency.test.mjsmantém.emptycomo 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 (empublic/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ótulonav.portalsnã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-filtersagora édisplay: gridcom colunasrepeat(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 doSR.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 dodocs-assistant/DocsFaba captura automaticamente, já que ambos leemdocs/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.mjsgarante 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 + Telegramcomo 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.mdlinka 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 quellm-dispatch.mjsnã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 comenabled: 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 flagenabledde uma empresa emportals.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.mjsjá filtraenabled !== 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,#/applyrecebem um?embutido (viaHelpHint.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 viaCompanyLogo, 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.salaryStatsagora retornaavgUsdjunto aminUsd/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 viaUI.md(); acessível (role="tooltip",aria-expanded, fecha com Escape ou clique fora, foco restaurado) e seguro para CSP. ?nas 5 abas de#/statse 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 comobackground, e os de superfície (--card/--panel/--line/…) nunca comocolorde texto, em todo o CSS e nos estilos inline da SPA. - Auto-sonda do carregador XSS do
UI.md()— o teste que carregamd()deapi.jsagora testamd('<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— oscrollIntoViewpós-geração só roda se a prévia ainda estiver conectada ao documento.
Notas
docs/UX-ROADMAP.mdatualizado: 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#/configpassou 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
#/statseram 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-planexibia 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,#/statse 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-authhttps://<tenant>.eightfold.ai/api/apply/v2/jobs, com host fixado em*.eightfold.ai(o CNAME de marcacareers.<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#/scane na landing.
Corrigido
- Chaves de deduplicação e de cargo com suporte a Unicode (#2569 / #2587 / #2667) — uma nova
normalizeTextKeycompartilhada (NFKC, mantém letras/marcas/dígitos de qualquer script) substitui as chaves somente-ASCII: odetect-repostsagora agrupa variantes de empresa com diferenças de largura/pontuação (“Acme, Inc.” ≡ “Acme Inc”) e nunca colapsa empregadores distintos não latinos, enquanto orole-matchernormaliza títulos em largura total (full-width) e preserva tokens de cargo não latinos em vez de eliminá-los. fetchJsonWithRetrynão tenta mais novamente um redirecionamento recusado (#2657) — uma guardaredirect:'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. oraclecloudpassa a aceitar os domínios-raiz numerados de tenantoraclecloud1.com … oraclecloud99.com(#2683) — uma família limitada (sem zero à esquerda, ≤ 2 dígitos), nunca um domínio-raiz com wildcard.workablereforçado (#2675) — novas tentativas, cabeçalhos que imitam um navegador, e serialização de requisições contra o host protegido por Cloudflare.personiorecorre a uma extração (scrape) de HTML quando o feed XML está desabilitado, em vez de retornar vazio.- Aliases FALLBACK de
statesresincronizados 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 dejs-yaml/nanoidpara 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
#/scane na landing do cvstart.org):join(JOIN) — o board JOIN de uma empresa a partir do__NEXT_DATA__do Next.js emjoin.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 POSTapi.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
fetchJsonWithRetrycompartilhado (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
successfactorsnã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 tinhatry/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 um404(umstartrowfora do intervalo), oen-scannercolocaria em quarentena um tenant ativo como morto por dias. Agora espelhaphenom/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
#/scanagora 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 semtabindex/role, então usuários de teclado e de leitor de tela não conseguiam alcançá-los ou alterná-los. Agora eles têmrole="button",tabindex="0",aria-pressede 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#/scane o cabeçalho de pontuação do#/dashboarderam 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 comscan.trustTip+scan.col.reloc(2 novas chaves) e reaproveitamento detrack.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 dewww.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_ADAPTERS68). Aparece no filtro Fonte de#/scane 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#/portalse 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 ocompany-funded.mjsdo pai retornava uma lista completa. (1) A view lia os resultados emres.candidates, mas o pai os emite emcompanies(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 paraUI.el('tr', {}, …)como varargs, masUI.el(tag, attrs, children)esperachildrencomo 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ídacompanies(a fixture original espelhava o formato erradocandidates— motivo exato pelo qual o bug foi lançado em verde), além de canários estáticos de fonte que garantem quefunded.jslêres.companies(nuncares.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 ocompany-funded.mjsdo career-ops pai viaGET /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 rotaserver/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 oweekly-digest.mjszero-LLM do pai viaGET /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ãoYYYY-MM-DDválidos; um intervalo vazio é um resumoavailable:trueválido. Adicionado aserver/lib/routes/interview.mjs+public/js/views/interview-digest.js, em Analytics. - Ambas as retransmissões seguem o contrato consolidado de fail-soft
available:falsepara quando o script do pai estiver ausente (CI, instalações standalone). 26 novas chaves de i18n ×17 locales; suíte isolada para CItests/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.mjschega de graça via a retransmissão por shell-out. As mudanças emset-status.mjs/tracker-utils.mjssão internas à CLI e não são espelhadas.
[1.132.0] — 2026-07-31
Alterado
- Subsistema de renderização de resultados do
#/scanextraído parapublic/js/lib/scan-results.js(amortização do contrato de tamanho de arquivo —public/js/views/scan.jshavia crescido para ~1254 LOC). O subsistema —renderResults,buildChipRow, os construtores de linha/facetas, os pintores de opções e o espelho do registroFALLBACK_SOURCES— passa para uma fábricawindow.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 paractx.*;scan.jsagora 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.mjssemeia umdata/last-scan.jsonfixo 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.cssdividido 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ãoapp.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) eoverlays.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.htmlcarrega 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 novotests/css-modularization.test.mjstrava 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.
- O corte é contíguo e na mesma ordem, então a cascata é byte a byte idêntica ao arquivo pré-split;
[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-talentagora revalida o overrideapi:/a16z-speedrun-talent:embuildEndpoint(HTTPS + host exatospeedrun-talent-network.com) e recai para o feed canônico quando a validação falha — paridade com o adaptadorcryptocurrencyjobs, de modo que um valor de host diferente nunca alcança o slot de busca (antes dependia só da guardaassertSpeedrunUrlem tempo de busca). A checagem de host exato agora é uma únicaSPEEDRUN_TALENT_HOST_REexportada, compartilhada pela guarda e pelo adaptador. - O parser
cryptocurrencyjobs—cleanUrlagora usa a mesma guarda de host por correspondência exata queassertCryptocurrencyJobsUrle o override do adaptador (antes eraendsWith, que aceitava subdomínios). O parser nunca é mais permissivo que a guarda de SSRF: um link de itemsub.cryptocurrencyjobs.coé descartado. - +2 testes → suíte 2135.
- O adaptador
[1.131.0] — 2026-07-31
Adicionado
- Board de abas de estágio (CRM) do
#/tracker(portado da view/pipelinedo 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/stagesretorna o funil canônico (rótulos em ordem) + um mapa de dobra de aliases, extraído deserver/lib/states.mjs(templates/states.yml, com o fallback embutido) — assim o cliente nunca fixa a whitelist de status. A resposta legada deGET /api/trackersem parâmetros permanece inalterada (só{ rows }). - Nova lib de cliente pura e testada por unidade
public/js/lib/tracker-stages.jsagrupa 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.
- Nova rota somente leitura
[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
#/scane 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 hostspeedrun-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 Web3cryptocurrencyjobs.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).
- a16z Speedrun (
Corrigido
echojobs— vagas híbridas continuam distinguíveis do remoto (espelha o #2258 do pai). Um marcadorhybridsem distinção de maiúsculas/minúsculas agora produz"<City> · Hybrid"(ou umHybridisolado quando não há cidade) eworkplaceType: 'Hybrid', em vez de ser colapsado emRemote.radancy— markup legado do TalentBrew + transporte via fragmento JSON de resultados (espelha o commit a3e6df9 do pai), condicionado a umopts.fetchJsoninjetá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-statusdo 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, eportals/fix-slugs.mjs. Mudanças de orquestração de varredura que vivem noscan.mjsdo 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çãorltrdo 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 invocascan.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, anteslead); o fallback destates.mjsnã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 emtests/states.test.mjs. +4 testes → 2073.
[1.129.0] — 2026-07-29
Adicionado
- Faceta de nível + coluna de idade em
#/scan— a libjob-facets.jsda 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, deJobFacets.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.mjslêtemplates/states.ymlao 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 viadomainFromName()(~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 quandodocs-fabvirar 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 dejibeapply). Além disso Cursor volta ao roster de CLIs (parent #2115):cli-detectagora detectacursor(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
/officesquandolocation.nameé só um modelo de trabalho (#2104); paridade do role-matcher (#1933/#2164/#2009: prefixo MTS, baseproduct, 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 diziamInside 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
#/configfoi sincronizado com odocs/SUPPORTED_CLIS.mddo pai:server/lib/routes/cli-detect.mjsganha Grok Build CLI (grok) e Kimi CLI (kimi), e a Antigravity agora testa primeiro seu binário canônicoagy. 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) —
sharp0.34.5→0.35.3,svgo4.0.1→4.0.2,fast-uri(dev) 3.1.3→3.1.4 emsite/; 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 doset-status(#2108) é só CLI (no web-ui as linhas do tracker são selecionadas explicitamente e nenhuma rota chamaset-status.mjs), o Risk Summary dos modos localizados (#2109) toca arquivosmodes/<lang>/que o web-ui nunca lê (apenasmodes/*.mdde nível superior), a verificação de manifesto doupdate-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 —parentVersioninalterado.
career-ops-ui v1.267.1 · page 4/9 · mirror of the project changelog for cvstart.org