Historial de cambios
GitHubCada cambio relevante, versión a versión. El mismo archivo se publica en el repositorio y está traducido a los 19 idiomas.
[1.208.2] — 2026-08-16
Corregido — en el móvil los botones de notificaciones y tema ya no quedan sobre el cuadro de búsqueda.
Corregido
- La v1.208.1 evitó que los botones de la barra superior se solaparan con el título de la página, pero en un móvil estrecho —aunque no el más estrecho— y sobre todo en idiomas con etiquetas largas, toda la barra seguía apretándose en una sola fila, así que los botones 🔔 y 🌙 podían quedar sobre el cuadro de búsqueda. Ahora los botones de acción (notificaciones, tema, Diagnóstico, Abrir Scan) siempre pasan a su propia segunda fila a lo ancho en el móvil, así el cuadro de búsqueda se ve completo y nada se solapa.
Notas
- En el móvil los botones de acción de la barra pasan a una segunda fila a todo el ancho, eliminando la frágil franja de “fila casi llena” donde el diseño repartía el espacio negativo sobrante como solape. Un guard de Playwright reproduce ahora el detonante exacto —un idioma de etiquetas largas en la franja 565–640px— y comprueba que los controles de la barra nunca compartan píxeles. Conjunto: 2621.
[1.208.1] — 2026-08-16
Corregido — en el móvil los botones de la barra superior ya no se solapan con la página.
Corregido
- La v1.208.0 pasaba los botones de la barra superior (Diagnóstico, Abrir Scan, notificaciones, tema) a una segunda fila en pantallas estrechas, pero la barra mantenía una altura fija, así que la fila envuelta se salía y quedaba sobre el título de la página. Ahora la barra crece para acoger sus filas y el contenido fluye debajo.
Notas
- La
heightfija de la barra pasó amin-height, así crece con su contenido en cualquier ancho (el escritorio no cambia). Un guard de Playwright ahora también comprueba que la barra no se salga sobre la página. Conjunto: 2621.
[1.208.0] — 2026-08-16
Corregido — la app ya cabe en la pantalla de un móvil: se acabó el desplazamiento lateral.
Corregido
- En pantallas estrechas toda la app se desplazaba de lado —la barra superior, las tablas, los artículos de ayuda y las pestañas de ajustes se salían por el borde derecho. Ahora cada página cabe en cualquier ancho: los botones de la barra superior pasan a una segunda fila, las tablas y bloques de código anchos se desplazan dentro de su propia caja, la ayuda apila su índice sobre el artículo, las filas de botones/pestañas se ajustan, y las rutas o URL largas se parten en lugar de estirar la página.
Notas
- La causa era la clásica trampa min-width: auto de flex/grid más algún elemento ancho sin envolver; resuelto con
min-width: 0en los ítems de grid,overflow-wrapen el markdown/títulos, una tabla de markdown desplazable y el apilado de la cuadrícula de ayuda en el punto móvil. Un guard de Playwright verifica 0 desbordamiento horizontal a 375 px en las rutas principales.tests/playwright-smoke.mjs. Conjunto: 2621.
[1.207.2] — 2026-08-16
Corregido — los planes de IA y los perfiles de orientación ya no se muestran como un volcado de código.
Corregido
- Algunos modelos envuelven toda su respuesta en una valla de código
markdown …. Cuando pasaba, el plan de desarrollo y el perfil de orientación aparecían como un bloque monoespaciado en vez de un documento con encabezados y listas. Ahora se quita esa valla envolvente —solo cuando envuelve toda la respuesta y es explícitamentemarkdown/md, así que una respuesta real depython/js/``` sin lenguaje se deja intacta.
Notas
- Resuelto una sola vez en el paso compartido de limpieza de LLM (
cleanLlmMarkdown), así todas las rutas de IA se benefician, y los bloques de código internos sobreviven.tests/llm-output.test.mjs(+3). Conjunto: 2621.
[1.207.1] — 2026-08-16
Corregido — la página de inicio ya no se desborda de lado en móviles pequeños.
Corregido
- En un móvil estrecho el hero —el titular, la línea de introducción y la terminal de instalación— podía quedar recortado por el borde derecho porque un comando de instalación largo y las columnas del diseño no se encogían a la pantalla. Ahora caben en cualquier ancho; el comando de instalación se desplaza dentro de su propia terminal.
Notas
- También se reforzó una comprobación E2E inestable que podía fallar por un 404 transitorio de un recurso: ahora ignora el ruido de red benigno (favicon / conexión / recurso fallido) como las comprobaciones hermanas, sin dejar de detectar errores reales de script. Sin cambios en el comportamiento de la aplicación. Conjunto: 2618.
[1.207.0] — 2026-08-15
Añadido — registra el resultado de una candidatura directamente desde el seguimiento.
Añadido
- Cada fila del seguimiento tiene una acción Resultado: elige qué pasó (rechazado, oferta recibida, contratado, oferta rechazada, sin respuesta, avanzó a entrevista), añade una nota opcional, previsualiza el estado resultante y regístralo. Registrar archiva los artefactos del CV y la carta enviados y sincroniza el seguimiento al estado canónico — una acción determinista en vez de editar el seguimiento a mano.
Notas
- Nueva
POST /api/outcomeretransmite la CLI de resultados:dryRun:truees una vista previa de solo lectura (localiza la fila, informa del estado resultante, no escribe nada); una llamada real lo registra. Seguridad de escritura: el tipo de resultado se limita al conjunto conocido y todo campo de texto se rechaza si tiene caracteres de control antes del shell-out (argumentos como array, spawn — sin shell).tests/outcome-route.test.mjs. Conjunto: 2618.
[1.206.0] — 2026-08-15
Documentación — la guía de ayuda integrada ahora cubre las cinco funciones más recientes, en los 17 idiomas.
Añadido
- La guía de ayuda integrada —y el asistente «Pregunta a la ayuda» que responde a partir de ella— ahora documenta cinco funciones recientes: Doctor de configuración (Ajustes — revisa tu CV y perfil en busca de huecos y datos de ejemplo olvidados), Descubrir tableros ATS (Portales — encuentra automáticamente el portal de empleo de una empresa), la comprobación «¿sigue activa?» (Seguimiento — si una oferta sigue abierta), la sugerencia «¿reutilizar un CV anterior?» (CV Studio — avisa cuando un CV ya adaptado encaja con un nuevo puesto) y el Registro de habilidades (Analítica — registra puntuaciones de autoevaluación). Cinco subsecciones nuevas, traducidas a los 17 idiomas.
Notas
- La estructura de la guía crece a 31 H2 / 118 H3, con paridad garantizada en cada idioma. Documentación de referencia actualizada:
docs/architecture/API.mddocumenta las cinco rutas de estas funciones, y los contadores de rutas/versión enCLAUDE.mdydocs/sdd/CONVENTIONS.mdestán al día (36 módulos de ruta). Conjunto: 2610.
[1.205.0] — 2026-08-15
Añadido — un Registro de habilidades para anotar resultados de pruebas/evaluaciones.
Añadido
- Un nuevo Registro de habilidades (Analítica → Registro de habilidades) te permite anotar una autoevaluación — empresa, plataforma, habilidad, puntuación % y una nota opcional — que se añade a
data/assessments.tsv, con una lista de entradas anteriores (más recientes primero). Sin tokens, determinista; el formato lo gestiona la CLI del proyecto padre.
Notas
- Nueva
GET /api/assessments(relaya la lista JSON por defecto deassessment-log.mjs; tolerante{available:false}) +POST /api/assessments(escritura explícita: campos pasados como args de array aassessment-log.mjs add). Seguridad de escritura: se rechaza cualquier campo con caracteres de control (un TAB rompería una columna, un salto de línea inyectaría una fila) → 400 antes de escribir; puntuación/umbral limitados a 0–100; longitudes acotadas.tests/assessments-route.test.mjs. Conjunto: 2610.
[1.204.0] — 2026-08-15
Añadido — un panel “Doctor de configuración” en Ajustes que detecta un CV/perfil incompleto o con datos de ejemplo.
Añadido
- Ajustes → Doctor de configuración ahora hace una comprobación sin tokens de tu
cv.mdyconfig/profile.ymly lista los problemas bloqueantes (archivos/campos faltantes) y avisos (datos de ejemplo residuales, métricas fijas) — para detectar una configuración incompleta antes de que debilite tus escaneos y adaptaciones. Solo lectura; se re-ejecuta con un clic.
Notas
- Nueva ruta de solo lectura
GET /api/cv-sync-checkque relayacv-sync-check.mjsdel proyecto padre, que imprime texto + un código de salida (sin--json); la ruta parsea ligeramente sus líneas establesERROR:/WARN:en{ok, errors[], warnings[]}— decide el banner, no el código de salida. Tolerante a fallos{available:false}en instalaciones autónomas.tests/cv-sync-check-route.test.mjs. Conjunto: 2602.
[1.203.0] — 2026-08-15
Añadido — una pista “¿reutilizar un CV anterior?” en CV Studio.
Añadido
- Al abrir una oferta guardada en CV Studio, la app ahora la compara con tus otras ofertas guardadas (solapamiento de palabras determinista, cero tokens) y te dice si la más parecida es suficiente para reutilizar ese CV adaptado, reutilizarlo con retoques o adaptar uno nuevo — para no regenerar desde cero un puesto que ya trabajaste.
Notas
- Nueva ruta de solo lectura
GET /api/jds/:name/reuseque relayajd-similarity.mjsdel proyecto padre (solapamiento Jaccard + guarda de seniority; JSON{decision, score, reason}) una vez por oferta previa (fan-out limitado a 25, gana la mejor); tolerante a fallos{available:false}si falta el script o las ofertas previas.tests/jd-similarity-reuse-route.test.mjs. Conjunto: 2594.
[1.202.0] — 2026-08-15
Añadido — descubre el tablero ATS de una empresa desde #/portals y empieza a seguirlo.
Añadido
- En #/portals, escribe el nombre de una empresa y la app sondea Greenhouse, Ashby y Lever por su tablero público — cero LLM, sin navegador — y muestra los tableros que existen y listan ≥1 empleo. Un clic añade el tablero elegido a las empresas que vigila tu escáner. El sondeo es de solo lectura; la escritura en
portals.ymlocurre solo al pulsar Añadir.
Notas
- Nuevo
server/lib/discover-ats.mjs(sondeo de slug con host fijo y charset validado víasafeGetcon DNS fijado, ≤12 sondeos/petición) +POST /api/portals/discover(solo lectura) yPOST /api/portals/track(escritura explícita:withFileLock+ empalme de texto + reverificación + renombrado atómico; solo hosts ATS conocidos, idempotente). Reutiliza el registro de adaptadores del escáner. i18n ×17.tests/discover-ats-resolver.test.mjs+tests/discover-ats-route.test.mjs. Conjunto: 2588.
[1.201.0] — 2026-08-15
Corregido — un seguimiento con encabezados de columna localizados o variantes ya no se muestra en blanco.
Corregido
- Si tu
data/applications.mdusa encabezados no ingleses o variantes —empresa/puesto/estado/fecha/enlace, oposition/stage/link— el seguimiento los leía con las claves equivocadas y mostraba columnas Empresa / Puesto / Estado / Fecha / Enlace en blanco. Ahora esos encabezados se pliegan a los nombres de campo canónicos y el seguimiento se muestra bien. Un seguimiento en inglés se procesa igual que antes.
Notas
- Nuevo mapa
HEADER_ALIASESy un plegado de normalización enparseApplications(server/lib/parsers.mjs); los encabezados desconocidos o ya canónicos pasan sin cambios.tests/tracker-header-aliases.test.mjs. Conjunto: 2563.
[1.200.0] — 2026-08-15
Añadido — comprobación de un clic “¿sigue activa?” para empleos en ATS desde tu seguimiento.
Añadido
- En #/tracker, una candidatura cuya URL sea una publicación de Greenhouse / Lever / Ashby / Workday / SmartRecruiters muestra ahora un botón “¿Sigue activa?”. Un clic consulta el JSON público del propio ATS — cero tokens, sin navegador — y muestra Activa / Expirada / Desconocida, para detectar vacantes muertas sin abrir cada una. Conservador por diseño: solo un 404/410 definitivo se lee como Expirada; lo ambiguo queda Desconocida (nunca una falsa Expirada).
Notas
- Nuevos
server/lib/liveness-core.mjs+liveness-api.mjsy una ruta de solo lecturaGET /api/liveness?url=(sin escrituras, sin LLM). Segura ante SSRF: la URL pasa porisValidJobUrly luego la API del ATS se alcanza solo mediantesafeGet(DNS fijado) con host fijo y segmentos de ruta validados.tests/liveness-core.test.mjs+tests/liveness-route.test.mjs. Conjunto: 2557.
[1.199.0] — 2026-08-15
Corregido — las tablas anchas ahora se desplazan lateralmente en vez de cortarse.
Corregido
- En la página Scan (y en el resto de tablas — Seguimiento, Estadísticas, Uso, Panel) una tabla más ancha que la ventana quedaba recortada sin barra de desplazamiento, dejando inaccesibles las últimas columnas. Ahora las tablas anchas muestran una barra de desplazamiento horizontal cuando hace falta, así toda columna sigue siendo accesible en cualquier ancho.
Notas
.table-wrapenpublic/css/components.csspasó deoverflow: hiddenaoverflow-x: auto(replica el contenedor.reports-scroll); se conserva el borde redondeado.tests/table-wrap-scroll.test.mjs. Conjunto: 2540.
[1.198.0] — 2026-08-15
Añadido — los reintentos de escaneo ahora usan retroceso exponencial, jitter y respetan el Retry-After de un limitador de tasa.
Añadido
- Cuando un tablero de empleo limita la tasa o falla brevemente (HTTP 429 / 5xx) durante un escaneo, el reintento ahora espera con retroceso exponencial + jitter en lugar de un retardo corto fijo — así un tablero ocupado no recibe golpes al mismo ritmo y los reintentos concurrentes no vuelven a colisionar al unísono. Un
Retry-Afterdel tablero se respeta (pero acotado, para que unRetry-After: 86400hostil no bloquee todo el escaneo). Los errores permanentes (404, redirecciones rechazadas) siguen fallando de inmediato — sin cambios.
Notas
- Nuevos
parseRetryAfterMs()y el purocomputeRetryDelayMs()enserver/lib/http-json.mjs;fetchJsonahora captura.retryAfteren una respuesta no-ok yfetchJsonWithRetryacepta unmaxDelayMsopcional (por defecto 8000).tests/http-json.test.mjs(+9). Conjunto: 2536.
[1.197.0] — 2026-08-14
Añadido — sigue un tablero de empleo de un fondo Getro solo con su careers_url; el id de la colección se resuelve solo.
Añadido
- Un tablero Getro seguido (b2venture, Earlybird, Point Nine, …) ya no necesita un
getro_collectionnumérico buscado a mano. Dale el propiocareers_urldel tablero y el id se resuelve solo desde esa página en el primer escaneo — un único GET seguro contra SSRF lee elnetwork.idnumérico directamente de los datos incrustados de la página. Ungetro_collectionexplícito sigue teniendo prioridad y evita la descarga por completo.
Notas
- Nuevos
httpsCareersUrl(),extractCollectionId()y el asíncronoresolveCollectionId()enserver/lib/sources/getro.mjs; la página del tablero se descarga a través desafeGet(DNS fijado, tamaño acotado), y el id resuelto sigue anclado al hostapi.getro.comporassertGetroUrl. El adaptador ahora coincide con una entradaprovider: getroque lleva uncareers_urlhttps aunque no tenga id.tests/sources-getro.test.mjs(+8). Conjunto: 2527.
[1.196.0] — 2026-08-14
Corregido (seguridad) — el adaptador de Workday valida un endpoint api por su hostname, no por una subcadena.
Corregido
- Un valor
api:de Workday enportals.ymlahora se acepta solo cuando su hostname esmyworkdayjobs.com(o un subdominio.myworkdayjobs.com). La comprobación anterior era una coincidencia de subcadena, así que cualquier URL que solo contuviera la cadena — p. ej.https://example.com/?x=myworkdayjobs.com— pasaba y se habría usado como endpoint. Los endpoints reales de Workday no se ven afectados. (Reportado por CodeQL, #443.)
Notas
- Nuevo
isWorkdayApi()analiza la URL y comprueba el host enserver/lib/portals/adapters/workday.mjs.tests/workday-adapter-endpoint.test.mjs(+1). Suite: 2522.
[1.195.0] — 2026-08-14
Rendimiento (escáner) — la detección de reposts sigue siendo rápida en historiales de escaneo grandes.
Rendimiento
- La detección de publicaciones duplicadas ya no degrada a O(N²) en un
scan-history.tsvgrande. El agrupamiento de títulos por empresa era un bucle anidado que pagaba unroleFuzzyMatchcompleto en cada par; ahora es un índice invertido — agrupa filas por título exacto en una pasada, luego hace coincidencia difusa solo sobre buckets DISTINTOS que comparten un token discriminante (no base). La salida es idéntica — los mismos clústeres de repost — probado con un test diferencial contra el algoritmo anterior en 200+ historiales aleatorios.
Notas
groupRowsByTitleenserver/lib/detect-reposts.mjs(exportado para el test diferencial).tests/detect-reposts-grouping.test.mjs(+2). Suite: 2521.
[1.194.0] — 2026-08-14
Corregido (escáner) — las páginas de empleo de Workday con una URL de un solo segmento ahora se escanean correctamente.
Corregido
- El adaptador de Workday ahora analiza las URLs de empleo cuyo path es un solo segmento — p. ej.
https://parsons.wd5.myworkdayjobs.com/Search,.../KBR_Careers,.../Careers. Antes, el sitio caía aExternal, así que el adaptador golpeaba el endpoint CXS equivocado y una prueba podía parecer sana sin devolver nada. Ahora toma el primer segmento no vacío del path como sitio (descartando un prefijo de idioma comoen-US); el caso documentado/en-US/Externalno cambia. (Reportado en #255.)
Notas
- Análisis estructural del path en
server/lib/portals/adapters/workday.mjs.tests/workday-adapter-endpoint.test.mjs(+7). Suite: 2519.
[1.193.0] — 2026-08-14
Añadido (estadísticas) — una pestaña “Silencio tras la entrevista” que muestra entrevistas que merecen un recordatorio.
Añadido
- Una pestaña Silencio tras la entrevista en
#/stats: entrevistas que quedaron en silencio más allá de una ventana de cortesía (30 días por defecto), uniendo tus entrevistas activas y tu tracker — con cuánto lleva cada una en silencio, la fecha de la última entrevista y el motivo. Una lista suave de recordatorio/cierre; solo sugerencias, nunca afirma un rechazo. Sin tokens.
Notas
- Nueva ruta
GET /api/stats/rejection-latency(fail-soft{available:false}).tests/stats-rejection-latency-route.test.mjs(+2). +10 claves i18n ×17; help-hint de#/statsde 7→8 pestañas. Suite: 2510.
[1.192.0] — 2026-08-14
Añadido (cv-studio) — un control “Verifica los datos de tu CV” que caza números que nunca tuviste.
Añadido
- Una tarjeta Verifica los datos de tu CV en
#/cv-studio: pega un CV o carta adaptados y comprueba cada métrica y dato afirmado contra tu CV, perfil y two-pager reales. Obtienes un veredicto pass / warn / block más las métricas inventadas, los datos sin respaldo y las frases prohibidas / de advertencia exactas. Sin LLM; no se escribe nada.
Notas
- Nueva ruta
POST /api/cv-studio/verify-facts: escribe el texto en un archivo temporal desechable y ejecutaverify-cv-facts.mjs, confiando en el veredicto JSON aunque el script salga con 1 en un block.tests/cv-studio-verify-facts-route.test.mjs(+4). +15 claves i18n ×17. Suite: 2508.
[1.191.0] — 2026-08-14
Añadido (estadísticas) — una pestaña “Qué aprender a continuación” que ordena las habilidades a aprender primero.
Añadido
- Una pestaña Qué aprender a continuación en
#/stats: un resumen de todo el tracker — las habilidades que más a menudo hundieron una baja compatibilidad, ponderadas y por niveles Critical / High / Medium — más las que ya cubre tu CV/perfil. Solo lectura, solo sugerencias; sin tokens.
Notas
- Nueva ruta
GET /api/stats/upskill(con campo{ error }si hay pocos datos; fail-soft{available:false}).tests/stats-upskill-route.test.mjs(+3). +15 claves i18n ×17. Suite: 2504.
[1.190.0] — 2026-08-14
Añadido (tracker) — un panel “Historial de empresa” que te dice qué empresas realmente te responden.
Añadido
- Una tarjeta Historial de empresa en
#/tracker: elige una empresa y obtén evidencia de solo lectura — cómo de receptiva ha sido contigo (en silencio contigo / mixto / respondió antes) y si el mismo puesto se republica — combinando tu tracker, seguimientos e historial de escaneo. Sin tokens; nunca se llama al escáner del proyecto.
Notas
- Nueva ruta
GET /api/stats/company-history[?company=](fail-soft{available:false}).tests/stats-company-history-route.test.mjs(+3). +18 claves i18n ×17. Suite: 2501.
[1.189.0] — 2026-08-14
Corregido (escáner) — los niveles de antigüedad en números romanos ahora cuentan también en títulos no latinos.
Corregido
- El clasificador de niveles detrás de
skip_tiersahora lee un sufijo de nivel en números romanos (I / II / III / IV / V) tras la palabra del puesto en cualquier alfabeto — “Инженер III”, “エンジニア I”, “Ingénieur IV” — no solo tras palabras ASCII. Antes, un número de nivel tras una palabra no latina se ignoraba y la oferta caía a mid, así queskip_tiers: [senior]o[entry]no las filtraba.
Notas
- Lookbehind independiente del alfabeto en
server/lib/classify-tier.mjs; eliminado un matcherSr.duplicado muerto.tests/classify-tier.test.mjs(+1). Suite: 2498.
[1.188.0] — 2026-08-14
Corregido (UI) — los botones de acción principales ya no quedan pegados al subtítulo de la página.
Corregido
- La fila de acción / control principal en Resumen semanal de entrevistas, Empresas financiadas, Portales, Plan de carrera y Orientación profesional ahora tiene un margen superior adecuado, así el botón respira bajo el subtítulo en lugar de pegarse a él.
Notas
- Guardia de regresión
tests/lead-row-top-margin.test.mjs(+5). Suite: 2497.
[1.187.0] — 2026-08-14
Corregido (escáner) — la opción skip_tiers vuelve a funcionar: se descartan las publicaciones que pediste omitir por antigüedad.
Corregido
- Una lista
skip_tiers:enportals.yml(p. ej.skip_tiers: [intern, entry]) ahora se respeta en el escaneo. El título de cada publicación se clasifica en un nivel de antigüedad (intern / entry / mid / senior) y se descarta si su nivel está en tu lista. Antes el escaneo aplicaba los filtros de título / ubicación / contenido / confianza pero sin filtro de nivel, así queskip_tiersse ignoraba en silencio. Los títulos sin palabra de nivel caen en mid (asískip_tiers: [mid]también descarta la mayoría de publicaciones normales), y el clasificador lee la palabra de nivel MÁS A LA IZQUIERDA.
Notas
- Nuevo
server/lib/classify-tier.mjspuro (classifyTier+buildTierFilter), conectado a las cadenas de filtros de los escáneres EN y RU.tests/classify-tier.test.mjs(+7). Suite: 2492.
[1.186.0] — 2026-08-14
Añadido (CV Studio) — un panel “Brecha de habilidades”: cuáles de las habilidades requeridas de un empleo nombra tu CV, insinúa o le faltan.
Añadido
- Un nuevo panel Brecha de habilidades en CV Studio. Elige una descripción de empleo guardada y clasifica cada habilidad requerida en nombrada en tu CV, insinuada en tu CV o faltante — comparación de palabras sin IA, sin escribir nada. Aparece una nota de baja confianza cuando la oferta no tenía una sección clara de requisitos.
Notas
- Nuevo
GET /api/jds/:name/skill-gap(el nombre de la oferta se sanea y se confirma bajojds/antes de ser un argumento; degradación suave a{available:false}sin el script). +13 claves i18n ×17. Pruebas:tests/jds-skill-gap-route.test.mjs(+4, incl. rechazo de path-traversal). Suite: 2485.
[1.185.0] — 2026-08-14
Añadido (estadísticas) — una pestaña “Embudo y velocidad”: cómo se compara tu embudo con el mercado y con qué rapidez avanzas entre etapas.
Añadido
- Una nueva pestaña Embudo y velocidad en Estadísticas muestra tus tasas de respuesta e entrevista junto a rangos de referencia del mercado (con las advertencias de muestra pequeña y sesgo de selección), una lista de espera de postulaciones en curso pasadas la ventana típica de primera respuesta, y días medianos por etapa (Postulado → Respondido → Entrevista → Oferta), con las filas lentas censuradas por la derecha para que no sesguen las medianas. Solo lectura y sin tokens; lee solo tu propio seguimiento.
Notas
- Nuevo
GET /api/stats/funnel(degradación suave a{available:false}sin el script). +18 claves i18n ×17. Pruebas:tests/stats-funnel-route.test.mjs(+2). Suite: 2481.
[1.184.0] — 2026-08-14
Corregido (UI) — los mosaicos de acción rápida del Panel ahora se alinean en una cuadrícula pareja.
Corregido
- En el Panel (Centro de mando), un grupo de 3 mosaicos se veía más ancho que uno de 4, dejando las secciones con un borde derecho irregular. Ahora cada grupo usa columnas de igual ancho (4 en pantalla amplia, bajando a 3 / 2 / 1 al estrecharse la ventana), así todos los mosaicos son del mismo tamaño y sus bordes derechos se alinean.
Notas
- Solo CSS (
.qa-grid:repeat(N, minmax(0,1fr))fijo en vez deauto-fill). Protegido portests/dashboard-grid-align.test.mjs(+2). Suite: 2479.
[1.183.0] — 2026-08-14
Añadido (escáner) — detección de duplicados más inteligente: el mismo empleo re-publicado con un enlace de seguimiento ya no aparece dos veces.
Añadido
- El escáner ahora reconoce un puesto por una clave de URL canónica, así que el mismo empleo re-publicado con un parámetro de seguimiento (
?utm_…,gclid, …), porhttpvshttps, o con una barra final /#fragmentose trata como el único puesto que es — sin fila duplicada en tus resultados o pipeline, y sin evaluación desperdiciada en un empleo ya visto. Los puestos realmente distintos (un id funcional conservado comogh_jid) siguen contando por separado.
Notas
- Nuevo
server/lib/url-key.mjs, conectado al dedup de ambos escáneres y al escritor de pipeline. Sub-normaliza a propósito: nunca fusiona dos puestos distintos. Pruebas:tests/url-key.test.mjs(+5),tests/parsers.test.mjs(+1). Suite: 2477 (+6).
[1.182.0] — 2026-08-14
Corregido (escáner) — los rangos salariales ahora se leen igual en todos los idiomas.
Corregido
- Las cifras de salario en las filas de escaneo y seguimiento usan los símbolos neutrales ≥ y ≤ (p. ej.
≥ 120000 EUR,≤ 90000) en lugar de las palabras inglesas “from” / “up to”, que se filtraban sin traducir a interfaces no inglesas. Se aplica a cada tablero con rango de un solo lado (Getro, Remotli, Manfred, Agentic Jobs, JustJoin, Jobicy); los rangos de dos lados (100000–150000 USD) ya eran neutrales.
Notas
- Solo visualización — el filtro de salario del cliente analiza los números sin importar el prefijo, así que el filtrado no cambia. Suite: 2471.
[1.181.0] — 2026-08-14
Añadido (escáner) — los tableros de Getro ahora muestran salario, todas las ubicaciones y puestos remotos.
Añadido
- El escáner de Getro (tableros de redes de talento de fondos) ahora muestra una cifra de salario en cada puesto (rango anual + moneda), lista todas las ubicaciones en lugar de solo la primera y etiqueta los puestos remotos. Un puesto de Getro en tu escaneo y seguimiento ahora tiene el mismo detalle de salario + ubicación que los demás tableros.
Notas
- Solo escáner; sin nueva dependencia, sin cambios de ruta / CSP / SSRF. Pruebas:
tests/sources-getro.test.mjs(+5). Suite: 2470 (+5).
[1.180.0] — 2026-08-14
Corregido (MEDIO, informes) — la lista #/reports ahora es una tabla y se recupera una puntuación real que un marcador de posición de Machine Summary ocultaba.
Corregido
- La lista
#/reportses una tabla (Informe · Fecha · Legitimidad · Puntuación), no una cuadrícula de 4 tarjetas. Un chip largo de “Puntuación no detectada” reducía la columna del título casi a cero, y eloverflow-wrap: anywheredel título de la tarjeta rompía el nombre del informe carácter por carácter. Ahora cada campo tiene su propia columna, la celda del nombre se ajusta por palabras y la tabla se desplaza horizontalmente en pantallas estrechas (nuevo contenedor.reports-scroll). Nueva clave i18nrep.colReport×17. - Una puntuación real en el cuerpo (
**Итоговый балл:** 1.8 / 5) ya no queda oculta por un marcador de posición de Machine Summary (score: —). Cuando el bloque## Machine Summarytraía una puntuación no numérica o fuera de rango, ocupaba el hueco de la puntuación analizada y bloqueaba el respaldo de forma-valor en negrita, por lo que el informe mostraba “Puntuación no detectada” pese a unX / 5real en el cuerpo.parseReportHeaderahora recupera la forma-valor del cuerpo cuando no sobrevive ningún número utilizable (paso 4.5).
Notas
- Solo cliente + analizador; sin cambios de ruta / CSP / SSRF / escritura al padre. Pruebas:
tests/reports-table.test.mjs(+5),tests/report-header-locale.test.mjs(+2). Suite: 2465 (+7).
[1.179.0] — 2026-08-13
Cambiado (LOW, escáner) — se consolidaron 20 decodificadores de entidades HTML duplicados en el módulo compartido (seguimiento de paridad, cierra el worklist).
Cambiado
- 20 fuentes de escaneo tenían cada una su propio
decodeEntities/decodeXmlEntities(+ un helperfromCodePoint) — copias que habían derivado (tres podían lanzarRangeError, corregido en v1.172.0; otras admitían NUL/C0 o interpretaban mala2;). Ahora todas pasan por el únicoserver/lib/html-entities.mjs(decodificador seguro según XML 1.0 Char), eliminando ~237 líneas de duplicación. Las 8 fuentes tipo RSS ganaron la decodificación de (antes solo manejaban 5 entidades); se preserva la doble decodificación deliberada de cryptocurrencyjobs mediante un alias.hhmantiene su decodificador (maneja—/–, fuera de las 6 compartidas). Una nueva prueba de guardia falla si alguna fuente vuelve a crear un decodificador local.
Notas
- Refactor que preserva el comportamiento; sin cambios de ruta / CSP / SSRF / escritura al padre. Pruebas:
tests/decoder-consolidation.test.mjs(+2). Suite: 2458 (+2).
[1.178.0] — 2026-08-13
Corregido (LOW, paridad con el padre) — se actualizaron dos constantes obsoletas para igualar al padre (PARENT-SYNC GAP #4 + #5).
Corregido
- User-Agent del navegador (GAP #4) —
BROWSER_LIKE_USER_AGENT(que envían workable/workday/oraclecloud/a16z/eightfold para pasar filtros WAF/bot) subió de Chrome 131 a 151, igualando eluser-agent.mjsdel padre; una versión vieja es más propensa a ser bloqueada. Protegido por una pruebaChrome major ≥ 151. - FALLBACK de estados del tracker (GAP #5) — el
FALLBACKde último recurso enstates.mjs(usado solo cuando eltemplates/states.ymlen vivo no es legible — clon nuevo / raíz aislada de CI) ganó los alias de estado en turco del padre (#2615): değerlendirildi, başvuruldu, yanıt verildi, mülakat, teklif, reddedildi, iptal edildi, uygun değil, kabul edildi/işe alındı. En producción el archivo en vivo ya los proveía.
Notas
- Solo dos constantes; sin cambios de ruta / CSP / SSRF / escritura al padre. Pruebas:
tests/http-json.test.mjs(+1) +tests/states.test.mjs(+1). Suite: 2456 (+2).
[1.177.0] — 2026-08-13
Corregido (MEDIUM, escáner) — csod (Cornerstone) devolvía 0 empleos en inquilinos que protegen la API de búsqueda con cookies de sesión (parent #2769, PARENT-SYNC GAP #1).
Corregido
- Algunos inquilinos de Cornerstone fijan cookies de sesión en la página de inicio del sitio de carreras y responden
401 CSOD Unauthorizeden la API de búsqueda salvo que esas cookies vuelvan junto al token portador anónimo.sources/csod.mjsahora lee el bootstrap con un nuevo helperfetchResponse, arma una cabeceraCookiecon sus valoresSet-Cookie(cookieHeaderFrom— solo nombre=valor, semántica de jar) y la reenvía en el POST de búsqueda. Solo mismo origen (host fijado +redirect:'error'), así las cookies de sesión nunca llegan a un tercero; un inquilino sin cookies se comporta igual que antes.
Notas
- Nuevo
server/lib/http-json.mjs::fetchResponse(aditivo; fuentes existentes sin afectar). Sin cambios de ruta / CSP / SSRF / escritura al padre. Pruebas:tests/sources-parity-v1118a.test.mjs(+1). Suite: 2454 (+1).
[1.176.0] — 2026-08-13
Corregido (MEDIUM, informes) — una puntuación bajo una etiqueta en negrita que la tabla RU no lista seguía mostrando “Score not detected” (FIND-5).
Corregido
- Dos informes RU escribían la puntuación como
**Итоговый балл:** 1.8 / 5/**Скор:** 1.8 / 5— etiquetas en negrita queREPORT_LABELS.runo enumera (solo conoce “Оценка”/“Балл”), así que la puntuación quedaba sin analizar. En vez de ampliar la lista de sinónimos,parseReportHeaderahora recurre a la forma del valor: una fracción sobre la rúbrica /5 bajo CUALQUIER etiqueta en negrita. Es independiente del idioma, inmune a un encabezado (sin**, sin valor/5) y rechaza una fecha como5/5/2026(lookahead negativo en el denominador).
Notas
- Solo el analizador del servidor; sin cambios de ruta / CSP / SSRF / escritura al padre. Pruebas:
tests/report-header-locale.test.mjs(+2). Suite: 2453 (+2).
[1.175.0] — 2026-08-13
Corregido (LOW, endurecimiento) — una guardia de regresión para la descripción SEO de FIND-3 + un strip de legitimidad a prueba de nulos (seguimiento de la AI-review).
Corregido
- Guardia de paridad de la descripción SEO — el arreglo de v1.174.0 que cambió un “~55” fijo en el
meta.descde cada idioma por un marcador{adapters}derivado del registro no tenía prueba, así que podía regresar en silencio en la próxima edición de un idioma. El nuevotests/site-meta-desc-parity.test.mjs(aislado de CI) falla si alguno de los 17site/src/i18n/*.jsonpierde el marcador o vuelve a fijar un recuento, o siLanding.astrodeja de interpolarlo en las tres metadescripciones. - Strip de legitimidad a prueba de nulos —
stripEmphasisdevuelve''para una entrada nula en lugar de la cadena “undefined” (los campos se inicializan como cadena, así que es defensa en profundidad).
Notas
- Prueba + una guarda de una línea en el analizador; sin cambios de ruta / CSP / SSRF / escritura al padre. Pruebas:
tests/site-meta-desc-parity.test.mjs(+3). Suite: 2451 (+3).
[1.174.0] — 2026-08-13
Corregido (HIGH, informes) — los informes localizados mostraban “Score not detected”; la descripción SEO estaba desactualizada.
Corregido
- Análisis de puntuación (FIND-1) — un informe no inglés cuyo H1 contiene la palabra de la etiqueta de puntuación (
# Оценка вакансии: <título>) ya no confunde ese título con la puntuación.parseReportHeaderahora se ancla en la etiqueta en negrita localizada (**Оценка:** 1.5 / 5), omite las líneas de encabezado y exige la etiqueta junto a sus dos puntos — así los informes RU que decían “Score not detected” muestran su puntuación real. - Chip de legitimidad (FIND-2) — se elimina el énfasis Markdown del valor, de modo que el chip muestra “High Confidence” en vez de “** High Confidence”.
- Desbordamiento de puntuación — una línea de puntuación con texto de estado extra (“1.8, Status: Evaluated, …”) se compacta a solo la puntuación;
.score-pillgana un tope de no-ajuste/desbordamiento y la columna del título puede encogerse, así un chip de color nunca se sale de la tarjeta. - Descripción SEO (FIND-3) — las descripciones meta / OG / Twitter de cvstart.org (los 17 idiomas) fijaban “Scan ~55 job boards” mientras el cuerpo contaba el registro real (“~75”). Ahora la descripción interpola el recuento del registro, así no vuelve a desviarse.
Notas
- Analizador del servidor + render/CSS del cliente + i18n del sitio; sin cambios de ruta / CSP / SSRF / escritura al padre. Pruebas:
tests/report-header-locale.test.mjs(+4). Suite: 2448 (+4).
[1.173.0] — 2026-08-13
Añadido (LOW, configuración) — Hermes se suma al roster de CLIs de IA detectadas (paridad con career-ops).
Añadido
- La pestaña
#/config→ “Herramientas CLI de IA” ahora detecta Hermes (Nous Research), el nuevo entorno de agente compatible del proyecto padre (binariohermes).server/lib/routes/cli-detect.mjsamplía su lista fija de 10 a 11 herramientas; la detección sigue siendo un escaneo de PATH de solo lectura (nunca se ejecuta ningún binario).
Notas
- Sin cambios en i18n / rutas / CSP / SSRF / escritura al padre; el roster es una lista fija, nunca entrada. Suite: 2444 (el canario de cli-detect se actualizó de 10 a 11).
[1.172.0] — 2026-08-13
Corregido (MEDIUM, escáner) — una entidad HTML mal formada podía bloquear una fuente de escaneo (paridad con career-ops #2150).
Corregido
- Las fuentes
oraclecloud,gemydassaultdecodificaban entidades HTML numéricas con una comprobaciónNumber.isFiniteinsuficiente antes deString.fromCodePoint— una referencia por encima de0x10FFFF(p. ej.�en un feed mal formado o malicioso) lanzaba unRangeErrorno capturado y abortaba todo el análisis de esa fuente. Un módulo compartidoserver/lib/html-entities.mjs(espejo del_html-entities.mjsdel proyecto padre) ahora restringe las referencias numéricas al conjunto Char de XML 1.0 §2.2, de modo queString.fromCodePointnunca puede lanzar, y distingue hexadecimal de decimal por separado para quea2;ya no se interprete mal. Las tres fuentes lo importan.
Notas
- Sin cambios para feeds válidos; sin cambios en JS / i18n / rutas / CSP / SSRF / escritura al padre. La consolidación de las ~20 copias restantes del decodificador se rastrea en
qa/PARENT-SYNC-WORKLIST-v1.26.0.md. - Pruebas:
tests/html-entities.test.mjs(+7). Suite: 2444 (+7).
[1.171.0] — 2026-08-13
Cambiado (BAJA, sistema de diseño) — tokens de escala tipográfica + capas z-index (D-4, primer paso). Los tamaños y el apilamiento eran literales por componente.
Cambiado
- Capas z-index — se añadieron tokens
--z-*(--z-topbar…--z-skiplink) y se migró cada literal z-index a ellos. Los valores se conservan, el apilamiento es idéntico; un nuevo canario prohíbe nuevos números mágicos. - Escala tipográfica — rampa
--font-size-*(xs 11…2xl 28, base = Inter 15px); se migraron los tamaños centrales (sin cambio visual). Los valores fuera de la rampa migran de forma incremental (docs/UX-ROADMAP.md).
Notas
- Solo token CSS; sin cambios de comportamiento/JS/i18n/ruta/CSP/SSRF/escritura. Sin cambios de píxeles.
tests/design-tokens-scale.test.mjs(+3). Conjunto: 2437 (+3).
[1.170.0] — 2026-08-13
Añadido (BAJA) — indicaciones de ETA honestas en generaciones IA largas (P4-ETA). Las generaciones pesadas (plan de carrera ~40 s, orientación / mercado / networking ~30 s, two-pager ~20 s) mostraban solo “Generando…” sin indicar la duración.
Añadido
- Cada botón de generación larga muestra ahora una pista
⏱ ~Nsal lado, como la ETA de#/auto. Estilo.eta-hintcompartido + dos claves genéricas (common.eta~{n}s,common.etaTitle).
Notas
- Solo cliente; sin cambios de ruta/CSP/SSRF/escritura. +2 claves i18n ×17 (snapshot 1219 → 1221).
tests/generation-eta-hint.test.mjs(+2). Conjunto: 2434 (+2).
[1.169.0] — 2026-08-13
Añadido (BAJA) — vista previa de PDF en línea (D-5). GET /api/output/pdfs/:name forzaba Content-Disposition: attachment, así que incluso el botón “Abrir” de #/cv descargaba en vez de mostrar.
Añadido
?inline=1sirve el MISMO archivo saneado conContent-Disposition: inline, para una 👁 Vista previa en una pestaña nueva; el valor por defecto sigue siendo descarga. Sin nueva ruta; se aplican los mismos guardas de nombre.- El primer botón de la lista de PDFs en
#/cves ahora 👁 Vista previa junto a ⬇ Descargar.cv.openPdfcambiado a “Vista previa” ×17.
Notas
- Sin cambios de CSP/SSRF — mismo
sanitizePathName. Una clave i18n reformulada ×17 (snapshot 1219).tests/output-pdfs.test.mjs(+3). Conjunto: 2432 (+3).
[1.168.0] — 2026-08-13
Corregido (BAJA, a11y) — las filas de casilla cumplen ahora el mínimo de 24×24 de WCAG 2.5.8 (D-2). Las etiquetas de casilla/radio en #/scan, #/config, #/evaluate y #/cv-studio estaban en una banda de ~22 px.
Corregido
- Una regla acotada
label:has(> input[type="checkbox"/"radio"]) { min-height: 24px }garantiza una banda ≥24 px. Solomin-height— las etiquetas ya son flex, no se desplaza nada;.apply-checklist(32 px) ya cumplía.
Notas
- Solo CSS; sin cambios de comportamiento/JS/i18n/ruta/CSP/SSRF/escritura.
tests/checkbox-target-size.test.mjs(+1). Conjunto: 2429 (+1).
[1.167.0] — 2026-08-13
Corregido (BAJA, sistema de diseño) — las superficies elevadas ahora se separan de las líneas divisorias (D-3). Los tokens --panel-2 / --surface-elev1 se resolvían como --slate, igual que las líneas --line / --border, sin separación visual.
Corregido
- Un token temático dedicado
--elevrespalda ahora las superficies elevadas (#eef1f6claro /#1e232eoscuro, distinto de--slate); las líneas siguen en--slate. Los hallazgos restantes (D-2, D-4, D-5, P4-ETA) quedan como backlog endocs/UX-ROADMAP.md.
Notas
- Solo token CSS; sin cambios de comportamiento/JS/i18n/ruta/CSP/SSRF/escritura.
tests/elevation-token.test.mjs(+2). Conjunto: 2428 (+2).
[1.166.0] — 2026-08-13
Corregido (BAJA) — la terminología de la rúbrica ahora refleja los docs canónicos. career-ops.org/docs describe “cinco dimensiones más una puntuación global holística”, pero el web-ui, cvstart.org y el wiki decían “rúbrica de seis dimensiones” (5 + 1 = 6, pero el vocabulario no coincidía).
Corregido
- Se adoptó la redacción de los docs — “cinco dimensiones más una puntuación global holística” — en README ×17, el sitio cvstart.org ×17, la guía de ayuda ×17,
docs/career-ops-canonical.mdy el wiki (Home ×17 + Features).
Notas
- Solo docs/marketing; sin cambios de código/clave i18n/ruta/CSP/SSRF/escritura.
tests/rubric-terminology.test.mjs(+2). Conjunto: 2426 (+2).
[1.165.0] — 2026-08-13
Corregido (BAJA) — el término “Two-pager” ahora es coherente en cada idioma. En árabe la barra lateral mostraba el latino “Two-pager” mientras el <h1> estaba localizado — la única cadena latina en una navegación RTL por lo demás reflejada.
Corregido
- Decisión aplicada: por idioma,
nav.twoPagerytwoPager.titlecoinciden en el término (ambos en latín o ambos localizados). Solo el árabe estaba dividido; su etiqueta de navegación ahora está localizada (“الصفحتان”). Un nuevo canario falla si algún idioma vuelve a dividirlos.
Notas
- Solo texto; sin cambios de ruta/CSP/SSRF/escritura. Un valor i18n cambiado (ar); sin nuevas claves (snapshot 1219).
tests/two-pager-term-consistency.test.mjs(+2). Conjunto: 2424 (+2).
[1.164.0] — 2026-08-13
Corregido (BAJA) — el marcador de posición de la búsqueda ya no se desborda en ningún idioma. “Find a company, role or URL…” se recortaba (nowrap) al encoger la barra; la mitad “…or URL” nunca se veía.
Corregido
top.search(×17) ahora es el corto “Buscar o pegar una URL” (≤24 caracteres en cada idioma), cabe incluso en una barra estrecha y mantiene la referencia a URL. El fallback enindex.htmlcoincide; elaria-labelconserva el detalle completo.
Notas
- Solo texto; sin cambios de ruta/CSP/SSRF/escritura. Una clave i18n reformulada ×17 (sin nuevas; snapshot 1219).
tests/search-placeholder-fit.test.mjs(+2). Conjunto: 2422 (+2).
[1.163.0] — 2026-08-13
Corregido (BAJA) — el asistente “Pregunta a los docs” ya cubre exportar un informe a PDF. Antes respondía que la guía no lo cubría, pese a existir el control 📄 Generate PDF en #/reports/:slug.
Corregido
- Se añadió un H3 “Exportar un informe a PDF” en §10 Informes en los 17 paquetes de ayuda (dónde está el botón, que el archivo va a
output/*.pdf, requiere Playwright, revisar antes de enviar). La recuperación del asistente ahora muestra la sección Informes.
Notas
- Solo docs/ayuda; sin cambios de código/ruta/CSP/SSRF/escritura. Umbral de ayuda 112 → 113 H3 (31 H2 sin cambios).
tests/help-reports-pdf-section.test.mjs(+2). Conjunto: 2420 (+2).
[1.162.0] — 2026-08-13
Corregido (MEDIA) — el “?” de ayuda ahora es un objetivo de ≥24×24 (WCAG 2.5.8). .help-hint medía 18×18 px con padding:0, por debajo del mínimo, en cada encabezado.
Corregido
- La caja de
.help-hintahora es 24×24 (el objetivo medible) mientras el anillo visible sigue en 18px mediante un::beforecentrado — el glifo y la línea base del<h1>no cambian. Estados hover/activo/foco se mueven con el anillo; margen 6→3px para conservar el espacio.
Notas
- Solo CSS; sin cambios de JS/i18n/ruta/CSP/SSRF/escritura.
tests/help-hint-target-size.test.mjs(+2). Conjunto: 2418 (+2).
[1.161.0] — 2026-08-13
Corregido (MEDIA) — #/reports muestra un chip “puntuación no detectada” en vez de espacio vacío. Tras el analizador multi-idioma de v1.159.0, un informe sin puntuación analizable mostraba un área vacía, indistinguible de un fallo.
Corregido
- La celda de puntuación ahora se ramifica: con puntuación → píldora de tono; sin puntuación → chip
.score-mutedcon “Puntuación no detectada” (×17) y una pista “Abre el informe…”. La tarjeta sigue siendo unrole="link"operable por teclado y la fecha se mantiene. - Reutiliza el token neutro existente; sin color nuevo.
Notas
- Solo cliente; sin cambios de ruta/CSP/SSRF/escritura. +2 claves i18n ×17 (snapshot 1217 → 1219). Conjunto: 2416 (+3).
[1.160.0] — 2026-08-13
Corregido (ALTA) — el texto sobre proveedores ya no contradice la promesa de 7 proveedores. #/config decía que la evaluación en vivo “usa tu clave de Anthropic o Gemini” y que la de OpenAI “no la usa el web UI”; el panel decía “Score Anthropic-first” — falso desde la cascada de 7 proveedores (v1.157.0).
Corregido
config.providerModelNote(×17): ahora dice que la ⚡ evaluación en vivo headless funciona con cualquiera de tus siete claves (Anthropic · Gemini · OpenAI · Qwen · OpenRouter · GitHub Models · Hermes), con orden automático y reserva. Eliminada la frase falsa sobre OpenAI.dash.quick.evaluateSub(×17): neutral respecto al proveedor (“Puntuación de ajuste 0–5”).Keys: N / 5→N / 7.
Notas
- Solo texto; sin cambios de ruta/CSP/SSRF/escritura. Sin nuevas claves i18n (snapshot 1217). Conjunto: 2413 (+3).
[1.159.0] — 2026-08-13
Corregido (ALTA) — los metadatos del informe ya no dependen del idioma. Los informes generados en un idioma distinto del inglés mostraban una franja de metadatos vacía en #/reports (sin puntuación, fecha ni legitimidad), porque parseReportHeader solo reconocía las etiquetas en negrita en inglés.
Corregido
parseReportHeaderahora analiza el bloque YAML invariante## Machine Summary(clavesscore:/legitimacy:/date:, la misma fuente que ya usaauto-pipeline): etiquetas en inglés → bloque Machine Summary → etiquetas localizadas (REPORT_LABELS, los 17 idiomas). Los informes en inglés quedan byte a byte idénticos.- Análisis numérico tolerante:
1.5/5,1.5 / 5,1,5/5,1.5 из 5,4.5 out of 5. La fecha recurre al mtime del archivo si el cuerpo no la tiene.
Notas
- Solo lectura/análisis; sin cambios de ruta, CSP, SSRF ni escritura en el padre. Sin nuevas claves i18n. Conjunto: 2410 (+8).
[1.158.0] — 2026-08-12
Corregido — dos fallos cosméticos de visualización (una «?» filtrada en el título de la pestaña y un recuento de proveedores incorrecto en la landing). Solo visual; sin cambios de comportamiento, seguridad ni flujo de datos.
Corregido
- La «?» de HelpHint ya no se filtra en
document.title. El router derivaba el título de la pestaña delh1.textContentsin procesar, así que la pestaña mostraba «Vacancy search?» en vez de «Vacancy search».router.js::focusNewViewahora clona el encabezado, elimina.help-hinty luego lee el texto; la «?» visible del encabezado no se toca. - cvstart.org mostraba «17 AI providers» en lugar de «7». El helper
sub()deFeatures.astroreescribía todos los{n}con el número de idiomas (17) antes de la sustitución por tarjeta; ahora{n}se resuelve por tarjeta (proveedores → 7, idiomas → 17).
Notas
- Sin cambios de servidor, ruta, CSP, SSRF ni claves i18n; forma de
facts.jsonsin cambios. Conjunto: 2402 pruebas (+1).
[1.157.0] — 2026-08-12
Corregido — las evaluaciones en vivo ahora se ejecutan con CUALQUIER proveedor configurado, no solo Anthropic/Gemini. Un usuario con solo OPENROUTER_API_KEY era forzado erróneamente al modo manual.
Corregido
- Causa raíz: un pin
LLM_PROVIDERsin clave (p. ej.LLM_PROVIDER=claudedeinit) llevaba a un callejón sin salida; ahora recurre al orden auto entre los proveedores configurados (enselectActiveProvider+ ambas cascadas de despacho). - El gating del cliente (
#/deep+ vistas mode-page) ahora usawindow.ProviderStatus(/api/status/providers, los 7) en vez del sondeo obsoleto de Anthropic/Gemini; textos reformulados (deep/eval × 17) + insignia «Evals en vivo» del dashboard +config.llmProviderHint.
Notas
- Sin cambios de seguridad. Conjunto: 2401 pruebas (+5).
[1.156.0] — 2026-08-12
Refactor — dividir scan.js bajo el límite de tamaño (P-16) + un arreglo de CodeQL. scan.js tenía 906 líneas; se extrajeron dos fábricas que preservan el comportamiento → 648. Completa el par de divisiones de vistas P-15/P-16.
Cambiado
- Nuevos
scan/runner.js(motor de ejecución del escaneo) yscan/filters.js(máquina de estado de filtros), vía bolsasctx/refs;scan.jsconecta ambos.
Corregido
- CodeQL
js/useless-assignment-to-local(#428) enconfig/tab-controller.js:let n = i;→let n;.
Notas
- Refactor puro, sin cambio de comportamiento; 4 tests que leen el fuente se repuntaron. Ambas vistas grandes ahora bajo 800 (P-15/P-16 hecho). Conjunto: 2396 pruebas.
[1.155.0] — 2026-08-12
Refactor — dividir config.js bajo el límite de tamaño (P-15). config.js tenía 1030 líneas (sobre el límite de 800); se extrajeron dos módulos que preservan el comportamiento, dejándolo en 783.
Cambiado
- Nuevos
config/field-specs.js(datos de campos + listas de modelos) yconfig/tab-controller.js(fábrica de la barra de pestañas);config.jslos referencia, la lógica de render no cambia.
Notas
- Refactor puro, sin cambio de comportamiento; 6 tests que leen el fuente se repuntaron.
scan.js(906) se deja como está (ya parcialmente dividido; núcleo demasiado acoplado para una división mecánica limpia). Conjunto: 2396 pruebas.
[1.154.0] — 2026-08-12
Nueva guía — “Ejecutar todo el stack en la nube.” career-ops no trae historia propia de nube/servidor, así que se añade una: una receta paso a paso para poner el pipeline padre career-ops, este visor career-ops-ui y el motor de IA (una suscripción de Claude vía Claude Code, un Hermes local, o claves de API) en un servidor pequeño siempre encendido. Llega como Ayuda §31 en los 17 idiomas, una sección del README y una página wiki.
Añadido
- Ayuda §31 “Ejecutar todo el stack en la nube” (× 17) — las tres partes, aprovisionar + instalar, elegir motor y exponer con seguridad (proxy inverso HTTPS + auth + las invariantes CSP/SSRF/XSS/sin-secretos). El bundle de ayuda crece a 31 H2 / 112 H3.
- README — una sección “Ejecuta todo el stack en la nube” (× 17) + una página Cloud-Deployment en la wiki.
Notas
- Solo docs — sin ruta, servidor ni cambio de cliente; sin nueva clave i18n. Los 4 tests de la ayuda pasan al contrato 31 H2 / 112 H3. Conjunto: 2396 pruebas (sin cambios).
[1.153.0] — 2026-08-12
El scanner de Jobvite migró al feed XML público (sync con el padre). El padre retiró la API JSON de Jobvite (ahora devuelve cero empleos); el source de web-ui usaba ese mismo endpoint muerto, así que cualquier empresa Jobvite rastreada quedaba vacía en silencio. Porta el fix del padre (#2623): ahora lee el feed XML público por inquilino, con clave companyEId.
Corregido
- El source pedía la API JSON retirada y devolvía cero empleos; ahora pide
https://app.jobvite.com/CompanyJobs/Xml.aspx?c={companyEId}y parsea el XML<result><job>…(CDATA + entidades,detail-urlsobreapply-url).
Cambiado
- Resolución de
companyEId: (1)company_eid:en el portal, (2) elc=de unapi:explícito, (3) descubrimiento por la página del board.fetchText(http-json.mjs) adjunta.location/.retryAfteral error non-ok (solo lectura, retrocompatible).
Notas
- Seguridad — dos hosts fijados (
jobs.jobvite.com,app.jobvite.com) víaassertJobviteUrl: solo https, allowlist estricta, ninguna redirección seguida. ElcompanyEIdes solo un valor?c=; recuento de sources sin cambios. - Conjunto: 2396 pruebas (+4).
[1.152.0] — 2026-08-12
Proveedor Hermes — cableado completado + actualización de docs. Una revisión de código de la integración de Hermes de v1.151.0 encontró dos fallos reales y cuatro puntos de completitud; todos corregidos aquí, y el roster de proveedores LLM de toda la app se lleva a los siete completos en todas las superficies de docs y los 17 idiomas.
Corregido
#/configno podía forzar Hermes — el desplegableLLM_PROVIDERlistaba solo seis proveedores, así que se podía definirHERMES_API_KEYpero no forzar Hermes desde la UI. Ahorahermeses la 8.ª opción, y una nueva prueba de paridad impide que el desplegable vuelva a desviarse deLLM_PROVIDERS.- Claves locales cortas se rechazaban en silencio — el umbral de 20 caracteres de
isUsableKeyestaba calibrado para claves de nube;hasHermesKeyusa ahora un umbral relajado de 8 (el ejemplo de los docs de Hermes tiene 19 caracteres).
Cambiado
- El roster de proveedores se normalizó a los siete completos en README (× 17), la ayuda en app (× 17), el dict
config.llmProviderHint(× 17) ydocs/sdd;hermesChatUrlcompleta un host sin ruta; el texto de reserva manual nombra a Hermes.
Notas
- Seguridad sin cambios — sin ruta nueva ni cambio SSRF/CSP; health/doctor gana una fila
HERMES_API_KEY. - Conjunto: 2392 pruebas (+2).
[1.151.0] — 2026-08-12
Hermes ya es un proveedor de LLM conectado (Phase 5) — el spike de alcance de Phase 5 confirmó que Hermes de Nous Research incluye un API Server compatible con OpenAI (hermes gateway → POST /v1/chat/completions), así que career-ops-ui ahora ejecuta evaluaciones en vivo a través de un Hermes local igual que OpenAI/Qwen. Define HERMES_API_KEY en Ajustes de la app y entra en el orden auto (el último). Cierra el último punto abierto del roadmap — Phase 5, Shape A.
Añadido
- Proveedor LLM Hermes (Shape A) —
runHermessobre el cliente compartidorunOpenAICompatible(server/lib/openai.mjs), en ambas cascadas (llm-dispatch.mjs+routes/llm.mjs), en la cola del orden auto + el pinLLM_PROVIDER=hermes,/api/status/providersyllm-pricing.mjs. Alcanza una base URL local configurable (por defectohttp://127.0.0.1:8642/v1) con auth Bearer — es un endpoint de proveedor CONFIGURADO (como OpenRouter/Qwen), no una URL de empleo del usuario, así que no pasa por el guard SSRF. - Campos en
#/config—HERMES_API_KEY(secreto) +HERMES_BASE_URL+HERMES_MODEL(por defectohermes-agent), con 6 nuevas claves i18n × 17 locales (snapshot 1208 → 1214).
Cambiado
- Se resuelve el spike de alcance:
docs/integrations/HERMES.md, la ayuda integrada §30 (× 17), el aviso del README (× 14), la skillhermes-bridgey el roadmap pasan de “planificado / aún sin conectar” a conectado (Shape A). Shape B (un relay de runtime de agente a medida) no fue necesario.
Notas
- Seguridad: el fetch del proveedor es un endpoint configurado, de la misma categoría que los otros proveedores compatibles con OpenAI — sin nueva superficie SSRF, sin cambios de CSP/sanitizador.
HERMES_API_KEYes unaSECRET_KEY(nunca se muestra). - Pruebas (aisladas en CI, transporte simulado):
tests/hermes-provider.test.mjs(+5); el canario “sin rama Hermes” de v1.146.0 se invierte para afirmar que SÍ está conectado; las pruebas de superficie de proveedores actualizadas al orden de 7 proveedores. - Conjunto: 2390 pruebas (+5).
[1.150.0] — 2026-08-12
Estados vacíos consistentes (pulido de Phase 4) — cada panel de “aún no hay nada” ahora se renderiza con el único estilo compartido .empty, en lugar de que algunas vistas redeclaren el aspecto en línea con un 40px mágico. Pequeña corrección de consistencia visual; los estados vacíos de #/activity, #/cv-studio, #/stats y #/usage ahora coinciden con todos los demás (relleno de 48px tokenizado + borde discontinuo).
Cambiado
#/activity,#/cv-studio,#/stats,#/usageeliminaron sustyle: { padding: '40px', textAlign: 'center', color: 'var(--foggy)' }en línea en los paneles vacíos — las tres propiedades ya las aporta la clase compartida.empty(--space-7= 48px, centrado, atenuado, borde discontinuo). Así esos cuatro se renderizan idénticos a los ~25 otros paneles.empty.- Las anulaciones legítimas por vista (
#/dashboardwidth:100%,#/pipelineborder:none) no se tocan — solo se quitaron las redeclaraciones puramente redundantes.
Notas
- Solo limpieza de uso de CSS en el cliente — sin cambios de ruta, servidor, clave i18n ni reglas CSS (la clase
.emptyno cambia); snapshot del diccionario 1208. Verificado en el navegador (el panel vacío de#/usagecalcula 48px de relleno + borde discontinuo, 0 errores de consola). - El nuevo canario
tests/empty-state-consistency.test.mjsmantiene.emptycomo única fuente de verdad. Phase 5 (proveedor Hermes) sigue bloqueada. - Conjunto: 2385 pruebas (+2:
tests/empty-state-consistency.test.mjs).
[1.149.0] — 2026-08-12
Portales movidos a Ajustes (Phase 4) — #/portals ahora vive en el grupo de navegación Setup, junto a Ajustes de la app, en lugar de bajo Sourcing. Desde v1.144.0 es una superficie de configuración (activar/desactivar empresas seguidas + una sonda de salud del ATS), no una acción de sourcing — así que es donde corresponde. Cambio solo de navegación; la página y su ruta no cambian.
Cambiado
- Elemento de navegación
#/portals→ grupo Setup (enpublic/index.html), colocado justo después de Ajustes de la app. Eliminado del grupo Sourcing (que conserva Scan / Pipeline / Auto-pipeline / Empresas financiadas). La ruta#/portals, la vista y la etiquetanav.portalsno cambian — solo se movió la posición en la barra lateral.
Notas
- Solo marcado de navegación — sin ruta, vista, clave i18n ni cambios de servidor. Verificado en el navegador (0 errores de consola); protegido por
tests/portals-nav-placement.test.mjs. - Conjunto: 2383 pruebas (+2:
tests/portals-nav-placement.test.mjs).
[1.148.0] — 2026-08-12
Filtros de búsqueda más claros (Phase 4) — el panel de filtros ahora es una cuadrícula ordenada — el panel de filtros de #/scan pasó de un flex-wrap irregular de cajas rígidas de ancho variable a una cuadrícula responsiva, y las acciones Aplicar / Restablecer ahora ocupan su propia fila separada y alineada a la derecha. Mismos filtros, mismo comportamiento — solo más fáciles de leer. Un retoque de diseño (sin parent-sync).
Cambiado
- Panel de filtros de
#/scan→ cuadrícula responsiva —.scan-filtersahora esdisplay: gridcon columnasrepeat(auto-fill, minmax(180px, 1fr))y espaciado uniforme, de modo que los 11 filtros con etiqueta se alinean en columnas ordenadas a cualquier ancho en lugar de envolverse en una fila irregular. - Acciones Aplicar / Restablecer abarcan toda la cuadrícula en su propia fila, separadas por una línea fina y alineadas a la derecha. Se eliminó el viejo truco de etiqueta oculta + el envoltorio flex interno en
scan.js.
Notas
- Solo CSS + una pequeña limpieza del DOM — cada id de filtro (
#scan-filter-*,#scan-apply) y el cableado deSR.render()no cambian, así que el flujo de Playwright queda intacto. Sin nuevas claves i18n. - Verificado en el navegador (0 errores de consola); protegido por
tests/scan-filters-grid.test.mjs. - Conjunto: 2381 pruebas (+3:
tests/scan-filters-grid.test.mjs).
[1.147.0] — 2026-08-12
Hermes & Telegram — la sección de ayuda integrada + superficie en cvstart.org (Phase 5b, parte 2) — la segunda y última parte del trabajo de documentación de Hermes: el tutorial ahora vive dentro de la propia guía de ayuda de la app, en los 17 idiomas, y el asistente de documentación integrado responde preguntas sobre Hermes a partir de ella. Sigue siendo solo documentación — la vía del proveedor LLM Hermes permanece planificada / aún sin conectar (Phase 5).
Añadido
- Ayuda integrada §30 “Hermes & Telegram” × 17 idiomas — una nueva sección de la guía (qué es Hermes + las dos formas de integración; ejecución en un servidor en la nube; Telegram vía Hermes + la regla “qué NO exponer”), accesible desde
#/help. El grounding dedocs-assistant/DocsFabla recoge automáticamente, ya que ambos leendocs/help/<lang>.md. - cvstart.org — un enlace a la guía de Hermes que apunta al documento en GitHub.
Cambiado
- Umbral del paquete de ayuda elevado 29 → 30 H2 / 105 → 108 H3 (
canonical-docs-coverage,help-ui,help-ru-config-section); §30 añade 3 H3.
Notas
- Todavía nada llama a Hermes. El nuevo canario
tests/help-hermes-section.test.mjsverifica que cada idioma incluye la §30 con sus anclas independientes del idioma (docs/integrations/HERMES.md,hermes-bridge,#/help,127.0.0.1, Telegram). El proveedor sigue bloqueado a la espera del contrato de API de Phase 5. - Esto cierra el entregable de documentación + skill de Phase 5b; la integración del proveedor (Phase 5) sigue siendo un elemento aparte y bloqueado.
- Conjunto: 2378 pruebas (+2:
tests/help-hermes-section.test.mjs).
[1.146.0] — 2026-08-12
Agente Hermes + Telegram — la guía de integración + una skill (Phase 5b, parte 1) — puedes ejecutar career-ops-ui en un servidor en la nube y conectar sus eventos (un escaneo terminado, un informe nuevo, un seguimiento urgente) a Telegram a través de un agente Hermes de Nous Research. Esta versión entrega la documentación de diseño + despliegue y una skill hermes-bridge; la vía del proveedor LLM Hermes sigue siendo planificada / aún no conectada (bloqueada por el spike de contrato de API de la Phase 5). Documentación por delante del código, a propósito.
Añadido
docs/integrations/HERMES.md— el análisis a fondo: las dos formas de integración (endpoint compatible con OpenAI vs. runtime de agente), el despliegue en servidor en la nube (reverse proxy + HTTPS + systemd, el contrato de solo lectura con el parent en una máquina headless), Telegram vía Hermes, y una lista de modelo de amenazas «qué NO exponer» (sin CV / salario / cuerpos de informes / claves hacia el canal).## Hermes agent + Telegramcomo aviso en el README — un enlace breve con puntero, en el README en inglés y reflejado en los README traducidos de cada idioma.- Una skill
hermes-bridge(.claude/skills/hermes-bridge/) que operacionaliza la guía — comprobaciones de prerrequisitos y de alcance (Node ≥ 18, claves presentes, alcanzabilidad del endpoint por la vía segura contra SSRF), nunca escribe secretos en disco/logs, y se niega a inventar un endpoint de Hermes o afirmar que el proveedor está conectado. - Una sección Integrations en
docs/architecture/OVERVIEW.mdenlaza la guía.
Notas
- Nada llama a Hermes todavía. Una prueba canario (
tests/hermes-docs.test.mjs) verifica las marcas de honestidad «planificado / aún no conectado» y quellm-dispatch.mjsno tiene ninguna rama Hermes/Nous — así que conectar el proveedor más adelante deberá actualizar la documentación + el roadmap en el mismo cambio. - Aplazado a v1.147.0 (Phase 5b, parte 2): la sección H2 «Hermes & Telegram» de la ayuda integrada × 17 idiomas y la superficie de marketing de cvstart.org.
- Suite: 2376 pruebas (+4:
tests/hermes-docs.test.mjs).
[1.145.0] — 2026-08-12
Estadísticas útiles (cont.): un gráfico reconstruible — la pestaña “Tendencia de roles objetivo” en #/stats ahora tiene un widget Crear un gráfico: elige una métrica × dimensión y se recompone en vivo. Solicitud de UX del usuario (sin parent-sync).
Añadido
- Gráfico reconstruible métrica × dimensión — elige una métrica (Vacantes / Salario mediano / Salario medio) y una dimensión (Por país / Por rol) y el gráfico de barras se recompone al instante. Las métricas salariales respetan la moneda + el interruptor por año ⇄ por mes; las vacantes son un simple conteo.
- 8 nuevas claves i18n × 17 idiomas; snapshot 1200 → 1208.
Notas
- Verificado en el navegador (0 errores de consola). Suite: 2372 pruebas (+2).
[1.144.0] — 2026-08-12
Ajustes y filtros (Fase 4, parte 1): activar/desactivar portales seguidos — ahora puedes encender o apagar una empresa vigilada desde #/portals, y el escáner lo respeta. Solicitud de UX del usuario (sin parent-sync).
Añadido
- Interruptor Activar/Desactivar por empresa en
#/portals— un clic apaga un portal (el escáner EN ya omite las empresas conenabled: false, así que un portal desactivado desaparece de todos los escaneos futuros) o vuelve a encenderlo, con un aviso optimista. POST /api/portals/toggle— una escritura explícita del usuario que cambia quirúrgicamente y con validación de parseo el flagenabledde una empresa enportals.yml(comentarios, orden y demás campos se conservan). 5 nuevas claves i18n × 17 idiomas; snapshot 1195 → 1200.
Notas
- El cambio en el escáner fue cero —
en-scanner.mjsya filtraenabled !== false. Suite: 2370 pruebas (+3).
[1.143.0] — 2026-08-12
Comprensible (cont.): pistas ? en las vistas de trabajo principales — el ? de ayuda ahora cubre las nueve páginas de acción principales, en todos los idiomas. Ajuste de UX reportado por el usuario (sin parent-sync).
Añadido
- Pista de ayuda
?en 9 títulos de vistas más —#/scan,#/evaluate,#/cv-studio,#/tracker,#/config,#/deep,#/batch,#/auto,#/applyreciben un?en línea (víaHelpHint.title) que abre un popover localizado de «qué hace / cómo usarlo / qué esperar» — el mismo componente seguro para CSP de v1.139.0. - 9 nuevas claves i18n × 17 idiomas (
help.hint.scan/…/apply); snapshot 1186 → 1195.
Notas
- Verificado en el navegador (0 errores de consola). Suite: 2365 pruebas (+1).
[1.142.0] — 2026-08-12
Corrección: se acabó el arquetipo profesional “Unknown” — #/orientation ahora siempre clasifica entre los ocho vectores de carrera nombrados en lugar de responder a veces “Unknown” y recomendarte “redoblar” en él. Corrección reportada por el usuario (sin parent-sync).
Corregido
#/orientation— el prompt de IA ahora prohíbe un arquetipo fuera del conjunto. El modelo DEBE clasificar los tres primeros entre exactamente los ocho vectores nombrados y nunca responder “Unknown”/“N/A”/“datos insuficientes” ni inventar una etiqueta. Con un CV escaso, igual nombra los tres más cercanos con menor confianza y dice qué evidencia falta, en vez de declinar.
Notas
- Solo cambio de prompt del servidor (
buildOrientationPrompt); sin cambios de i18n/esquema. Suite: 2364 pruebas (+1).
[1.141.0] — 2026-08-12
Estadísticas útiles (cont.): enriquecimiento de empresas financiadas — #/funded es ahora más visual: logos de empresa, un gráfico por importe de financiación y tarjetas con ronda / importe / puntuación de descubrimiento / acción sugerida. Ajuste de UX reportado por el usuario (sin parent-sync).
Cambiado
#/funded— tabla plana → cuadrícula de tarjetas. Cada empresa recién financiada es ahora una tarjeta con logo (derivado del nombre víaCompanyLogo, con avatar de letra como respaldo), chips de ronda + importe, la puntuación de descubrimiento y la acción sugerida del proyecto padre, más el enlace y la fecha de la noticia.- Visualización del importe de financiación — un gráfico de barras de las mayores empresas por importe divulgado; los importes de texto libre (“$120M”/“€1.5B”) se analizan a una magnitud con un nuevo
parseAmount. 3 nuevas claves i18n × 17 idiomas.
Notas
- Sigue siendo solo lectura sobre
GET /api/company-funded; descripción y rango salarial no están en la fuente de financiación. Suite: 2363 pruebas (+2).
[1.140.0] — 2026-08-12
Estadísticas útiles: cifras salariales más ricas — el desglose salarial de “Mi pipeline” en #/stats ahora muestra la media (no solo la mediana), un cambio por año ⇄ por mes y una tabla mín · media · mediana · máx por país. Primer tramo de la Fase 3. Ajuste de UX reportado por el usuario (sin parent-sync).
Añadido
- Salario medio (media) —
RoleStats.salaryStatsahora devuelveavgUsdjunto aminUsd/medianUsd/maxUsd. La mediana resiste los valores atípicos; la media revela el sesgo, así que juntas se leen como una distribución. - Cambio por año ⇄ por mes y una tabla mín · media · mediana · máx por país en la sección salarial, con los selectores de moneda y periodo. 8 nuevas claves i18n × 17 idiomas.
Notas
- Las cifras siguen derivándose solo de anuncios con salario legible y normalizadas a USD (indicativas). Suite: 2361 pruebas (+1).
[1.139.0] — 2026-08-12
Comprensible: pistas de ayuda ? — un botón ? reutilizable y seguro para CSP que, al hacer clic, explica «qué hace / cómo funciona / qué esperar» en tu idioma. Ajuste de UX reportado por el usuario (sin parent-sync).
Añadido
- Popover de pista de ayuda
?(window.HelpHint) — un?redondo junto a un encabezado abre un popover ligero, adaptado al tema y reflejado en RTL, que muestra una explicación localizada medianteUI.md(); accesible (role="tooltip",aria-expanded, cierre con Escape o clic fuera, foco restaurado) y seguro para CSP. ?en las 5 pestañas de#/statsy en 8 títulos de vistas de IA/analítica (career-plan, orientación, two-pager, networking, entrevista simulada, memoria, funded, resumen semanal) — 14 nuevas claves i18n × 17 idiomas.
Notas
- Todas las vistas ya tenían un subtítulo de una línea; el
?añade la explicación más detallada bajo demanda y hace que los estados vacíos se expliquen por sí solos. Suite: 2360 pruebas (+4).
[1.138.0] — 2026-08-12
Generación en el idioma de tu interfaz — cada generación con IA ahora responde en el idioma que has elegido en la interfaz, además de refuerzos de pruebas surgidos de la revisión. Ajuste de UX reportado por el usuario (sin parent-sync).
Cambiado
- Las generaciones con IA ahora respetan el idioma de la interfaz. Con la interfaz en ruso, español, japonés, … el texto generado vuelve en ese idioma en lugar de siempre en inglés. La directiva de idioma de salida se propaga por todos los endpoints de generación — plan de carrera, orientación, informe de mercado, entrevista simulada, plan de networking, «pregunta a la documentación», la sugerencia de nota de memoria y el borrador del two-pager. El código y los identificadores siguen en inglés (p. ej. las claves YAML del two-pager); solo se localiza la prosa, los títulos y las viñetas.
Corregido
- Guardia de rol de color CSS (
tests/css-role-tokens.test.mjs) — un canario estático que verifica que los tokens alias del modo oscuro de v1.137.0 nunca invierten su rol: los tokens de texto (--fg/--danger/--ok/…) nunca se usan comobackground, y los de superficie (--card/--panel/--line/…) nunca comocolorde texto, en todo el CSS y los estilos inline de la SPA. - Auto-sonda del cargador XSS de
UI.md()— el test que cargamd()desdeapi.jsahora pruebamd('<script>…')justo tras la extracción y lanza un error si falta el escape, de modo que un futuro corte erróneo falla ruidosamente en vez de dejar verde la suite de seguridad. - Guardia de scroll en
#/career-plan— elscrollIntoViewtras generar solo se ejecuta si la vista previa sigue conectada al documento.
Notas
docs/UX-ROADMAP.mdactualizado: las pistas de ayuda?+ descripciones de página + estados vacíos pasan a v1.139.0; un proveedor Nous Research / Hermes — con una guía de despliegue en servidor cloud + Telegram y un skill de Hermes — se registra como Fase 5 / 5b.- Suite: 2356 pruebas (+5).
[1.137.0] — 2026-08-11
Correcciones de legibilidad y renderizado — contraste en modo oscuro, etiquetas de gráficos y el plan de carrera. Una revisión de UX motivada por un reporte de usuario (sin parent-sync).
Corregido
- Modo oscuro en blanco-sobre-blanco / negro-sobre-negro en muchas pantallas — quince propiedades personalizadas de CSS que varias vistas referenciaban (
--fg,--panel,--panel-2,--ok,--danger,--card, …) nunca se habían declarado, así que recurrían a valores fijos claros/negros: correcto en modo claro, ilegible en modo oscuro (los chips de resumen de#/pipeline, la pestaña activa de#/stats, “Active / Keys” + “✓ set” de#/config, las secciones de#/two-pager, la burbuja de pregunta de#/mock-interview, el texto de error). Ahora son alias de los tokens reales que sí responden al tema, así que lo siguen automáticamente — 0 fallos de contraste WCAG-AA en las 29 vistas, verificado por un auditor automatizado; la pestaña activa de#/configpasó a un estilo de insignia con tinte legible. Una protección de regresión (tests/dark-theme-tokens.test.mjs) mantiene ese alias. - Las etiquetas de los gráficos de
#/statsse cortaban a mitad de palabra («Senior Backend Engineer» → «…Enginee») — ahora se truncan con elipsis y conservan la etiqueta completa como tooltip al pasar el cursor. #/career-planmostraba el plan generado como Markdown sin procesar — ahora se renderiza automáticamente como texto legible y con formato (el Markdown editable permanece en el área de texto; el botón Vista previa lo alterna).
Notas
#/career-plan,#/two-pager,#/statsy el resumen semanal de entrevistas no están rotos — muestran estados vacíos hasta que generas un plan o tienes datos. Se planea para la próxima entrega una guía en pantalla más clara y pistas de ayuda?(docs/UX-ROADMAP.md).
[1.136.0] — 2026-08-11
Paridad con career-ops padre v1.26.x (posterior a v1.26.0 en la rama principal) — una nueva fuente sin autenticación más una ronda de mejoras de calidad y robustez portadas a los espejos de web-ui. El registro ahora son 79 fuentes = 74 en inglés + 5 en ruso (ALL_ADAPTERS 74).
Añadido
eightfold(Eightfold AI, #2684) — boards de captación de talento vía la API sin autenticaciónhttps://<tenant>.eightfold.ai/api/apply/v2/jobs, anclada a host a*.eightfold.ai(el CNAME de marcacareers.<company>.comse rechaza deliberadamente); paginada con un límite de seguridad, lanzamiento de error ante board muerto, y deduplicación de URL. Fuente + adaptador + suite aislada para CI; aparece en el filtro de Fuente de#/scany en la landing.
Corregido
- Deduplicación y claves de rol con reconocimiento Unicode (#2569 / #2587 / #2667) — una nueva
normalizeTextKeycompartida (NFKC, conserva letras/marcas/dígitos de cualquier alfabeto) sustituye a las claves solo-ASCII:detect-repostsahora agrupa variantes de ancho/puntuación del nombre de empresa (“Acme, Inc.” ≡ “Acme Inc”) y nunca colapsa empleadores no latinos distintos, mientras querole-matcherpliega títulos en ancho completo y conserva los tokens de rol no latinos en lugar de borrarlos. fetchJsonWithRetryya no reintenta una redirección rechazada (#2657) — una protecciónredirect:'error'que encuentra un 3xx es determinista, así que ahora es no reintentable y falla rápido en lugar de agotar el presupuesto de reintentos.- Grupos AND de
title_filter.positive(#2552) — un+delimitado por espacios dentro de una entrada positiva ahora exige que todos los términos aparezcan en el título, en cualquier orden. oraclecloudacepta los apex de tenant numeradosoraclecloud1.com … oraclecloud99.com(#2683) — una familia acotada (sin cero inicial, ≤ 2 dígitos), nunca un apex comodín.workablereforzado (#2675) — reintentos, cabeceras similares a un navegador, y serialización de peticiones frente al host tras Cloudflare.personiorecurre a un scraping HTML cuando el feed XML está desactivado, en lugar de devolver nada.- Los alias FALLBACK de
statesresincronizados con el padre (#2615).
Notas
- No portado (no reflejado por web-ui, o exclusivo de CLI): reply-matcher (#2672), jd-similarity (#2661), jd-skill-gap (#2686), el parseo de scan env-path (#2568) /
--flag=value(#2589), y los cambios de cover-letter / plantilla de CV / doctor / ollama / generate-pdf. Los avisos HIGH dejs-yaml/nanoiddel lado web ya se habían corregido en web-ui v1.135.0.
[1.135.0] — 2026-08-11
Paridad con career-ops padre v1.26.0 — cinco nuevas fuentes de escaneo sin autenticación más correcciones de precisión en cuatro boards que web-ui ya incorpora. El registro ahora son 78 fuentes = 73 en inglés + 5 en ruso (ALL_ADAPTERS 73).
Añadido
- Cinco nuevas fuentes de escaneo (cada una con una fuente + adaptador + una suite aislada para CI; aparecen en el filtro de Fuente de
#/scany en la landing de cvstart.org):join(JOIN) — el board JOIN de una empresa a partir del__NEXT_DATA__de Next.js enjoin.com/companies/<slug>(anclado a host, con límite de páginas).getro(Getro) — boards de “red de talento” de portfolios de VC vía la API pública POST deapi.getro.com, paginada de más reciente a más antiguo; cada empleo se atribuye al empleador del portfolio, no al fondo.consider(Consider) — boards de portfolio de VC de getconsider.com vía un POST del mismo origen; el host configurable está anclado por una protección SSRF estructural (solo host HTTPS público).joinup(JOINUP) — el board suizo joinup.ch, que lee la página más reciente renderizada en el servidor (SSR); falla cerrado ante una rotura del scraper.remotli(Remotli) — remotli.ch, puestos remotos en empresas suizas (salarios en CHF); emite la propia URL de aplicación del ATS del empleador para que las publicaciones cruzadas se deduplican.
Corregido
- a16z Speedrun ya no aborta todo el board ante un fallo transitorio — las peticiones de página ahora pasan por un
fetchJsonWithRetrycompartido (reintentos acotados solo ante 429/5xx/timeout transitorios, nunca ante un 4xx permanente), y el presupuesto de páginas se ha redimensionado para la página de 50 empleos. - arbeitsagentur se ha migrado a la API Jobsuche v6 (
/pc/v6/jobs) — el antiguo endpoint v4 devuelve 404; la forma de la respuesta se renombró y el filtrado remoto ahora se acota en el servidor. - thehub se ha migrado a la API v2
jobsandfeatured; las filas no llevan fecha de publicación y están exentas del filtro de antigüedad. - hackernews encuentra el hilo mensual “Who is hiring?” de forma fiable filtrando la búsqueda de Algolia por la etiqueta de cuenta
author_whoishiringen lugar de una consulta de texto libre.
Notas
- No portado (web-ui ya es seguro, absorbido por el relé, o exclusivo de CLI): las claves de deduplicación de roles / coincidencia de empresas en Unicode (la agrupación de repost de web-ui ya usa como clave el nombre de la empresa en minúsculas planas, así que los empleadores distintos no latinos nunca colapsan); la señal de latencia de rechazo de followup + los ajustes de company-funded (relevados de solo lectura, con degradación segura); las rutas de escaneo configurables por entorno y el parseo
--flag=value(web-ui ejecuta los escáneres en proceso); la refactorización de consolidación del User-Agent (web-ui ya la centraliza); y los elementos exclusivos de CLI (el listado de contenido no confiable, oferta/offer-prep, doctor, cambios de plantilla de carta/CV).
[1.134.1] — 2026-08-05
Endurecimiento de validación — correcciones detectadas por una auditoría integral del proyecto.
Corregido
successfactorsya no descarta los empleos extraídos ante un fallo a mitad de escaneo (regresión introducida en el port de “lanzar error en board muerto” de v1.134.0) — su bucle de paginación no teníatry/catch, así que un fallo en la página 2 o posterior (tras el éxito de la página 1) lanzaba un error y descartaba todo lo ya recolectado; y si ese fallo era un404(unstartrowfuera de rango),en-scannerponía en cuarentena durante días a un tenant en realidad activo, como si estuviera muerto. Ahora imita aphenom/radancy: un fallo en la página 0 sigue lanzando error (board muerto), pero un fallo en una página posterior conserva los resultados parciales.- Los chips de filtro de
#/scanya se pueden operar con teclado (WCAG 2.1.1) — los chips de faceta (y el chip “borrar”) eranspancon un manejador de clic pero sintabindexni rol, así que los usuarios de teclado y lector de pantalla no podían alcanzarlos ni activarlos. Ahora llevanrole="button",tabindex="0",aria-pressedy activación con Intro/Espacio. - Tres cadenas en inglés codificadas de forma fija ya están localizadas — el tooltip de la insignia de confianza de
#/scan, la cabecera de columna de reubicación de#/scany la cabecera de puntuación de#/dashboarderan literales sueltos que la puerta de paridad i18n no podía detectar (nunca fueron claves), así que permanecían en inglés en todos los idiomas que no fueran el inglés. Ahora sonscan.trustTip+scan.col.reloc(2 claves nuevas) más una reutilización detrack.col.score, con una comprobación estática de código fuente.
[1.134.0] — 2026-08-05
Paridad con career-ops padre v1.25.0.
Añadido
- Nueva fuente de escaneo: getManfred (
manfred) — un feed de todo el board de puestos tecnológicos españoles/UE con salarios publicados, desdewww.getmanfred.com/api/v2/public/offers(sin autenticación, anclado a host + solo HTTPS, catálogo completo en una sola petición). Fuente + adaptador + una suite aislada para CI (tests/sources-manfred.test.mjs); el registro ahora son 73 fuentes = 68 en inglés + 5 en ruso (ALL_ADAPTERS68). Aparece en el filtro de Fuente de#/scany en la landing de cvstart.org.
Corregido
- El feed de a16z Speedrun truncaba silenciosamente a 50 empleos (#2404) — el feed limita una página a 50, pero la fuente solicitaba
PER_PAGE = 100, así que la paginación se detenía tras la primera página. Corregido a 50. - Los boards muertos ahora lanzan un error en vez de leerse como “en vivo pero vacíos” (#2379) —
cryptocurrencyjobs,phenom,radancy,successfactors: un fallo de descarga en el que ninguna petición llega a resolverse ahora lanza un error (de modo que la salud de#/portalsy el escaneo registran un fallo real), en vez de silenciarlo como una lista vacía; un fallo a mitad de escaneo tras al menos un éxito conserva los resultados parciales. - workable ahora usa la API pública de widget (#5ab8425) — cambiado a
apply.workable.com/api/v1/widget/accounts/<slug>, que devuelve el listado completo de publicaciones de una cuenta grande en una sola petición, de modo que las cuentas grandes ya no se truncan.
Notas
- No portado (exclusivo de CLI o no reflejado en web-ui): la reescritura de rendimiento por agrupación de títulos de detect-reposts #2389; las correcciones de clave de empresa Unicode (la deduplicación del tracker propio de web-ui ya es segura para no-latinos);
scan --since;cv-facts; la pasada de auditoría de plantilla de CV / PDF;doctor; la directiva de contenido no confiable de los modos.
career-ops-ui v1.267.1 · page 4/9 · mirror of the project changelog for cvstart.org