Журнал изменений
GitHubКаждое значимое изменение, релиз за релизом. Тот же файл публикуется в репозитории и переведён на все 19 языков.
[1.34.0] — 2026-05-17
WS5 — экран Auto-pipeline в один клик (#/auto). Модалка повышена до отдельной линкуемой страницы. Один клик: валидация→загрузка→оценка→сохранение отчёта→трекер (SSE). Доступный stepper (aria-current, live-region), deep-link на отчёт/трекер, ручной режим без ключа, линкуется #/auto?url=…&go=1. Пункт в сайдбаре; кнопка ✨ дашборда ведёт сюда. i18n 14 ключей ×8. help §1 ×8 + README ×8. +8 тестов. 589 → 597. Подробно — CHANGELOG.md.
[1.33.0] — 2026-05-17
WS4 — аудит паритета с career-ops 1.8.0 + location_filter. В родительском scan.mjs появился location_filter (#570); in-process сканеры web-ui не делегируют ему, поэтому не флоу. Новый server/lib/location-filter.mjs повторяет семантику дословно; подключён в оба сканера. help §5 ×8. +8 тестов. 581 → 589. Остальной дельта классифицирован в PARENT-PARITY.md. Подробно — CHANGELOG.md.
[1.32.0] — 2026-05-17
Вкладка Profile в #/config — сырой YAML-блоб → форма по полям (WS1). 3 сворачиваемые секции (Кандидат / Нарратив / Компенсация), 14 скалярных путей. Сохранение по полям мёржит в config/profile.yml: архетипы, proof points и кастомные ключи сохраняются нетронутыми. Raw-YAML escape-hatch сохранён в Advanced (хранит комментарии). 23 i18n-ключа ×8. +7 тестов (вкл. инвариант выживания неизвестных ключей). 574 → 581. Подробно — CHANGELOG.md.
[1.31.0] — 2026-05-17
Синхронизация с career-ops 1.8.0 — #/batch отдаёт --model + --start-from. Родительский проект 1.7.1 → 1.8.0; в batch-runner.sh добавлены --model NAME (#504) и --start-from N. web-ui выводит их в #/batch (поля Модель и Старт с #) с серверной defense-in-depth валидацией (charset для model, целое 1..100000 для start-from). i18n ×8. +7 тестов. 567 → 574. Полностью — в CHANGELOG.md.
[1.30.0] — 2026-05-14
Paginator на #/scan — заменяет truncation «первые 200 из N» из v1.12.
До v1.30 таблица результатов скана жёстко обрезалась на первых 200 отфильтрованных строках, внизу шла подпись «Показаны первые 200 из N». Строки 201..N были недоступны из UI — приходилось руками сужать title_filter.positive в portals.yml. v1.30.0 заменяет cap на UI.paginate (тот же хелпер что в #/tracker / #/reports / #/activity). PAGE_SIZE = 200 сохраняет прежнюю плотность; сортировка boost-to-top стабильна между страницами (сортируем ПОЛНЫЙ набор и только потом режем на страницы); автореsет на page 1 при смене любого фильтра. Устаревший i18n-ключ scan.shownTop удалён (×8 локалей). 9 новых кейсов в tests/scan-paginator.test.mjs (7 статических канарий + 1 логическая таблица с 6 граничными кейсами + 1 расчёт summary). 558 → 567 unit + acceptance (+9 новых). Полные подробности в CHANGELOG.md.
[1.29.2] — 2026-05-14
Hot-fix: 🌐 Scan с source=both запускал только EN-фазу. RU-фаза тихо терялась.
SSE-клиент (public/js/api.js:156) закрывал EventSource на ПЕРВОМ событии done, но сервер в source=both шлёт по одному done на фазу. RU-фаза стартовала и сразу же отменялась. Фикс: сервер маркирует каждый done полем final: true|false; клиент закрывает поток только когда final !== false. Обратная совместимость — продюсеры одиночных фаз без final закрываются как раньше. 547 → 558 unit + acceptance (+11 новых). Полные подробности в CHANGELOG.md.
[1.29.1] — 2026-05-14
Подробный пользовательский гайд по настройке 5 русских порталов в help-bundle §5, во всех 8 локалях.
Новая ### подсекция «Настройка русских порталов — подробное руководство» внутри §5 (Portals & sources): инвентарная таблица 5 источников с auth и гео-ограничениями, пошаговый поиск и редактирование portals.yml, полный YAML-пример с 5 источниками, коллизия с negative-list’ом и пример исправления, как временно отключить отдельный источник, как проверить через 🌐 Scan + SSE-лог. §17 (релиз v1.29.0) описывает девелоперский флоу; §5 в v1.29.1 — пользовательский флоу. 540 → 547 unit + acceptance (+7 новых). Полные подробности в CHANGELOG.md.
[1.29.0] — 2026-05-14
Сканер русских порталов расширен с 2 до 5 источников. Registry + динамический dropdown. Новая секция §17 «Как добавить новый источник».
- 3 новых RU-адаптера:
Trudvsem(open-data API «Работа в России», без auth, без гео-гейта),GetMatchиGeekJob(HTML-скрейп с defensive-парсером —[]при неудачном парсе, никогда не throw на здоровом 200). - Source registry в
server/lib/sources/registry.mjs— единый источник правды, который читают dispatcher + endpoint + dropdown. До v1.29 список жил жёстко-захардкоженным в трёх местах. - Новый endpoint
GET /api/scan/sourcesсCache-Control: max-age=60— SPA при mount’е#/scanдинамически перестраивает dropdown. - Новая §17 во всех 8 локалях: «Как добавить новый источник для скана» (template адаптера, запись в registry, dispatcher, мокированный тест,
portals.yml). - Дефолт
russian_portals.sourcesменяется с["hh", "habr"]на 5 источников; если у тебя вportals.ymlуже явно перечисленыsources:, добавь три новых руками. - Тесты: 520 → 540 (+20). Полные подробности в
CHANGELOG.md.
[1.28.1] — 2026-05-14
Hot-fix: router 404 на хэшах с ?query. Строку HH_USER_AGENT убрали из health.
До v1.28.1 Router.go('/evaluate?url=…') производил hash, чей первый сегмент по split('/') был литералом "evaluate?url=…", который никогда не совпадал с зарегистрированной ветвью → __not_found__ (404). Однострочный фикс: hash.split('?')[0] перед разбиением имени. Закрывает оба известных клика: #/pipeline → ▶ и «App settings → Modes». Опциональная строка HH_USER_AGENT убрана из /api/health (подсказка про 403-вне-РФ остаётся в help-bundle §16 и в stderr самого ru-scanner). 515 → 520 unit + acceptance (+5 новых). Полные подробности в CHANGELOG.md.
[1.28.0] — 2026-05-14
Выравнивание документации + контрол --max-retries N на #/batch. Закрывает два открытых issue, поднятых в qa/QA-PROMPT-docs-vs-app.md.
- Issue #2 — на
#/batchпоявился числовой input «Max retries» (1–10), активный только когда отмечен «Retry failed». Сервер парсит черезparseIntи валидирует 1≤N≤10; значения вне диапазона тихо отбрасываются, без--retry-failedфлаг не пробрасывается вовсе. 7 кейсов вtests/batch-max-retries.test.mjs. 2 новых i18n-ключа × 8 локалей. - Issue #1 — список AI-CLI в 8 help-bundle и 8 README выровнен под канон career-ops.org/docs (Claude Code · Codex · OpenCode · Qwen CLI), с локализованной строкой: «другие Claude-совместимые CLI работают через тот же интерфейс slash-команд». Бюллет «Multi-CLI» в README про shim-файлы web-ui сохранён без изменений (это другая поверхность). 2 новых канария в
tests/canonical-docs-coverage.test.mjs. - 506 → 515 unit + acceptance (+ 9 новых). Playwright 32/32 без изменений. Полные подробности в
CHANGELOG.md.
[1.27.0] — 2026-05-14
Косметика + a11y: устранён дубль #/dashboard в сайдбаре.
В сайдбаре логотип бренда (<a class="logo" href="#/dashboard">) и первый пункт навигации указывали на один и тот же маршрут. Скринридеры дважды произносили «Dashboard», а у клавиатурных пользователей появлялся лишний tab-stop. Блок логотипа теперь — обычный <div class="logo">; пункт навигации остаётся единственной ссылкой на #/dashboard. 506 / 506 unit + 32 / 32 Playwright — без изменений. Подробности в CHANGELOG.md.
[1.26.1] — 2026-05-14
Hot-fix WCAG 2.5.5 — восстановлен минимум min-height: 44px на .btn.
В v1.26.0 декларация min-height: 44px в .btn отсутствовала, header-кнопки рендерились на 39-41 px (нарушение WCAG 2.5.5). v1.26.1 восстанавливает 44-пиксельный пол + flex-shrink: 0 + line-height: 1.2. 502 → 506 unit, Playwright 32/32 без изменений. Детали в CHANGELOG.md.
[1.26.0] — 2026-05-14
Пирамида тестов + line-покрытие ≥ 93 %.
Внедрена 4-уровневая пирамида тестов (unit → functional → acceptance → e2e) согласно backlog’у v1.25. Добавлены 22 новых теста, покрывающие крупнейшие пробелы из v1.25 (jds.mjs 61.64 % → 100 %, ветки отказа в auto-pipeline). Появилась директория tests/acceptance/ для тестов пользовательских сценариев через несколько endpoint’ов. 480 → 502 unit + acceptance, Playwright 32/32 без изменений. Полные детали в CHANGELOG.md и docs/architecture/TESTING.md.
[1.25.0] — 2026-05-14
Короткое замыкание ручного режима авто-пайплайна + косметика дашборда + добивка паритета CHANGELOG. Релиз закрывает G-014 (авто-пайплайн игнорировал mode: 'manual'), G-012 (рассинхрон CHANGELOG — 6 локалей отставали на два релиза) и косметический баг с двойным глифом ✨ ✨ на дашборде. G-003 (переименование README.cn.md) де-факто уже закрыт — в репозитории присутствует только README.zh-CN.md. G-005 (перестройка блоков отчётов A-G → A-F) требует согласованного коммита в родительском проекте и переносится дальше.
🛡️ G-014 — короткое замыкание mode: 'manual' в авто-пайплайне
fix(auto-pipeline): G-014 — honour mode:'manual' short-circuit(server/lib/routes/auto-pipeline.mjs:158-195) — до v1.25 маршрут всегда обращался к LLM. Передачаmode: 'manual'(по аналогии с/api/evaluateначиная с v1.10.2) тихо игнорировалась, запрос висел 1–3 минуты на Anthropic. Теперь обработчик:- Принимает
modeИevalModeради обратной совместимости. Любое из значений'manual'запускает короткое замыкание. - Эмитит все 5 этапов SSE со
status: 'done'/status: 'skipped'. Без fetch. Без вызова LLM. Без $0.05 за запрос. - Полезная нагрузка
doneнесёт{ mode: 'manual', prompt: <buildEvaluationPrompt scaffold>, message }— SPA может отрендерить её так же, как уже существующую карточку ручного промпта/api/evaluate.
- Принимает
- Закрывает риск DoS на
HOST=0.0.0.0: ранее, даже когдаllmRateLimitограничивал 10 запросов в 60 с с IP, 10 атакующих × 10 запросов = $50/мин расхода Anthropic. Короткое замыкание срабатывает до того, как декремент лимита запросов учтётся для реального вызова. - Тесты —
tests/auto-pipeline-manual-mode.test.mjs: 3 теста подтверждают (1)mode: 'manual'возвращает ответ менее чем за 2 с со всеми 5 ключами этапов, (2) даже при заданномANTHROPIC_API_KEYкороткое замыкание всё равно срабатывает (исходный симптом), (3) старые вызовы сevalMode: 'manual'продолжают работать.
📝 G-012 — добивка паритета CHANGELOG (6 локалей × 2 пропущенных релиза)
docs(changelog): backfill v1.23.0, v1.24.0, v1.24.1, v1.25.0 in 6 lagging locales— до v1.25 только EN содержал v1.23–v1.24; RU отставал на один релиз, остальные 6 — на два. v1.25 запускает параллельных переводческих агентов (по образцу цикла v1.23), которые добавляют все четыре записи вCHANGELOG.{es,pt-BR,ko-KR,ja,zh-CN,zh-TW}.md. RU получает v1.24.0 + v1.24.1 + v1.25.0 (v1.23.0 уже присутствовал с цикла v1.23).feat(ci): scripts/check-changelog-parity.mjs gate— падает сборка, если самая свежая запись CHANGELOG любой локали старше канонического EN. Подключено вnpm run test:ci. Текущий рассинхрон G-012 поймал бы себя сам в момент пересечения границы EN.
✨ Косметика — устранение двойного глифа на дашборде
fix(dashboard): dedup ✨ glyph in auto-pipeline button label(public/js/lib/i18n-dict.js:219) —dash.autoPipelineнёс ведущий✨в строке каждой локали, Иpublic/js/views/dashboard.js:58добавлял ещё один✨во вьюхе. Результат: кнопка рендерилась как✨ ✨ Auto-pipeline …. v1.25 убирает ведущий глиф из значения DICT каждой локали; префикс вьюхи остаётся единственным источником. Тот же проход аудита прошёлся по остальному i18n-бандлу — других случаев двойного глифа не обнаружено.
🚫 Перенесено на следующий релиз
- G-005 — перестройка блоков отчёта A-G → A-F согласно каноническому career-ops.org/docs — требует согласованного коммита в родительском проекте
santifer/career-ops(переписатьmodes/oferta.mdтак, чтобы он эмитил A=Role, B=CV-match, C=Strategy, D=Comp, E=Personalization, F=STAR — отказаться от C-Risks/G-Legitimacy как отдельных блоков). v1.25.0 поставляет сторону web-ui, готовую к новой схеме (reports.jsпринимает произвольные буквы блоков с v1.13). Закладывается в следующее релизное окно, когда родитель и потомок смогут выйти вместе. - G-003 — переименование
README.cn.md→README.zh-CN.md— проверено при подготовке v1.25: в репозитории уже присутствуетREADME.zh-CN.md(никакого осиротевшегоREADME.cn.mdнигде в worktree). Находка G-003 устарела.
🧪 Тесты
- 477 → 480 unit (+3 из PR-B
auto-pipeline-manual-mode.test.mjs). - 32/32 Playwright без изменений.
npm run test:ciтеперь прогоняетnpm test+check-no-also-leftovers.mjs+check-changelog-parity.mjs.
Верификация
$ npm run test:ci
# 480 / 480
# ✓ no .also( leftovers in views/
# ✓ CHANGELOG parity: all 8 locales at v1.25.0
# G-014 — manual mode returns < 2 s even with ANTHROPIC_API_KEY set:
$ ANTHROPIC_API_KEY=sk-ant-test PORT=4317 npm start &
$ sleep 3
$ time curl -sS -X POST -H 'Content-Type: application/json' \
-d '{"url":"https://job-boards.greenhouse.io/anthropic/jobs/x","mode":"manual"}' \
http://127.0.0.1:4317/api/auto-pipeline | head -20
# real 0m0.1xx s (was 1-3 min)
# event: start … event: step (×5) … event: done {"mode":"manual","prompt":"…"}
# G-012 — every locale CHANGELOG carries the v1.25.0 entry:
$ grep -c '^## \[1.25.0\]' CHANGELOG*.md
# 8 files, each → 1
# Cosmetic — dashboard glyph:
$ grep "dash.autoPipeline" public/js/lib/i18n-dict.js
# No leading ✨ in any locale value (view supplies the single glyph)
Несовместимые изменения
Нет. mode: 'manual' подключается опционально; старые вызовы с evalMode: 'manual' продолжают работать без изменений.
Вне рамок (v1.26+)
| Пункт | Примечания |
|---|---|
| G-005 — перестройка блоков отчёта A-F | Требует согласованного коммита в родительском проекте (santifer/career-ops переписывает modes/oferta.md). |
| Живое выполнение визуальных подтестов QA-сценария 31 | Требуют браузерного агента (Claude Cowork). Частично покрыты Playwright smoke. |
Превышение i18n-dict.js целевого лимита в 400 LOC |
Фикстура переводов — освобождена политикой. Разделение добавило бы HTTP-запросов без сборщика. |
[1.24.1] — 2026-05-14
Hot-fix: падение #/config во всех 8 локалях (G-015).
🚑 Критический hot-fix
fix(config): G-015 — replace removed Element.prototype.also call in config.js(public/js/views/config.js:371) — v1.22.0 N-2 удалил глобальный monkey-patchElement.prototype.alsoи перевёлcv.jsна шаблон со свободными выражениями, но пропустилconfig.js. В итоге#/configпадал при первом обращении в каждой локали с ошибкойc(...).also is not a function. v1.24.1 применяет тот же шаблон миграции, что и вcv.js:188-201— вынести дерево вconst root = c(...), выполнить блок активации отдельно и затемreturn root;.
🛡️ Шлюз CI
feat(ci): scripts/check-no-also-leftovers.mjs sweep— обходит каждый файл подpublic/js/views/и валит сборку на любом вызове.also((ссылки в комментариях допустимы). Подключено в новый скриптnpm run test:ci. Будущий откат удаления monkey-patch не сможет тихо вернуть ту же регрессию.
🧪 Тесты
test: tests/config-view-syntax.test.mjs— три страховки:- парсинг
config.jsчерезnode:vm.Script(ловит регрессии уровня синтаксиса без Playwright) - проверка, что
.also(не выживает вне комментариев - проверка, что якоря миграции
const root = c(...)/return root;на месте
- парсинг
- 474 → 477 unit (+3) + 32/32 Playwright без изменений.
Верификация
$ npm run test:ci
# 477 / 477
# ✓ no .also( leftovers in views/
# Browser smoke:
$ open http://127.0.0.1:4317/#/config
# → renders normally, no "is not a function" card. Every locale equivalent.
Вне рамок (перенесено на v1.25)
- G-014, G-012, G-005, G-003 — см. запись v1.25.0 ниже единым пакетом.
[1.24.0] — 2026-05-14
Обновление глубины содержимого справочного бандла + живое выполнение QA-сценария 31 + сквозной перевод RU CHANGELOG. Релиз закрывает оба пункта, перенесённых таблицей «Out of scope» из v1.23.0 на v1.24: полное обновление глубины содержимого всех 8 справочных бандлов из 5 канонических URL career-ops.org/docs (с v1.11.x присутствовало только покрытие URL) и живое выполнение QA-сценария 31 на запущенном сервере (числилось как «требует браузерного агента + LLM-учётных данных» — оказалось, что 6/6 подтестов достижимы через curl + grep, и только визуальные подтесты требуют браузера).
📖 Обновление глубины содержимого справочного бандла
docs(help): refresh en.md from 5 canonical career-ops.org/docs URLs(docs/help/en.md) — до v1.24 EN-бандл содержал 1113 строк и перечислял 5 канонических URL во front-matter, но не раскрывал их в теле. v1.24 загружает все 5 URL через WebFetch и углубляет соответствующие H2-разделы:- About career-ops (front-matter) — добавлены принципы (суверенитет данных, AI-agnostic, контроль со стороны человека), блок «What career-ops is NOT», расширена инвентаризация концепций с 6 до 10 строк (добавлены Proof points, JD store, Interview-prep, Batch additions).
- §5 Portals — добавлен канонический bootstrap
cp templates/portals.example.yml portals.yml, уточнены обязательные и опциональные поля каждой записиtracked_companies. - §7 Scan — добавлено примечание «no AI tokens consumed» для варианта A, перечень последующих команд (
apply/contacto/deep/tracker). - §14 Apply checklist — разделён на режим SPA-чеклиста, режим Manual-vs-Playwright-assisted и полный CLI-сценарий (канонические 8 пронумерованных шагов от
/career-ops apply <company>доSubmitted.с авто-переходомEvaluated → Applied); подсекция batch evaluate получила таблицу схемы TSV + документацию всех 4 флагов +merge-tracker.mjs --dry-run; подсекция Playwright Setup перечисляет команды установки, регистрацию MCP, альтернативу.claude/settings.local.json, примечание о headless-режиме по умолчанию.
- Сохранён паритет 16 H2-разделов (CI-тест
help-ui.test.mjs::section-parityутверждает ровно 16 H2-разделов во всех 8 локалях). - Каждый из 5 канонических URL встречается ≥ 2 раз в бандле (CI-тест
canonical-docs-coverage.test.mjsэто обеспечивает). Счётчики на URL после v1.24:what-is-career-ops× 4,scan-job-portals× 5,apply-for-a-job× 3,batch-evaluate-offers× 5,set-up-playwright× 3. docs(help): translate the v1.24 deepening to 7 non-EN locales— запущены 7 параллельных переводческих агентов. Каждая целевая локаль (es / pt-BR / ko-KR / ja / ru / zh-CN / zh-TW) получает обновлённый бандл, который зеркалит структуру EN раздел в раздел, сохраняет дословно блоки кода, URL, пути к файлам и метки кнопок (📁 Upload CV / 🌐 Scan now / ▶ Evaluate / 📄 Generate PDF / 💾 Save), а также английские аббревиатуры (CSP, SSRF, TOCTOU, WCAG, ATS, JD, SSE, REST, API), и переводит углубление на нативный технический стиль публикационного уровня на целевом языке.
🧪 QA-сценарий 31 — живое выполнение (6/6 PASS)
-
docs(qa): append last-verified live-execution log to qa/claude-cowork-browser-test-prompt.md— до v1.24 сценарий 31 был задокументирован, но ни разу не прогонялся на живом сервере (был отложен как «требует браузерного агента + LLM-учётных данных»). v1.24 прогнал все 6 подтестов наhttp://127.0.0.1:4317:Sub Описание Статус 31.1 Пороги баллов в справочных бандлах ✅ PASS (4.5 × 3, 4.0 × 9, 3.5 × 6 упоминаний в docs/help/en.md)31.2 Эндпоинты сценария сканирования ✅ PASS ( /api/stream/scan-{en,ru}+/api/scan-ru/config→ 404;/api/scan/regional/config→ 200)31.3 Чеклист /api/apply-helper✅ PASS (тело содержит career-ops apply+ предупреждениеauto-submit)31.4 Эндпоинт /api/batch✅ PASS (ключи [exists, runnerExists, raw, rows, additions])31.5 Доступность Playwright ✅ PASS ( /api/healthсообщаетPlaywright (parent node_modules) ok: true, value: installed)31.6 Покрытие URL справочного бандла (5 URL × 8 локалей) ✅ PASS (40 / 40 ✓) Чисто визуальные подтесты (требуют браузер) помечены отдельно в QA-промпте — они остаются выполнимыми через Claude Cowork или
npm run test:e2e:browser.
🌐 RU CHANGELOG end-to-end (продолжение M-9)
docs(translate): CHANGELOG.ru.md retry agent — full body translation(CHANGELOG.ru.md) — релиз v1.23.0 вышел с переводческим агентом для RU CHANGELOG ещё в работе (он один раз упал с ошибкой сокета и был перезапущен). v1.24 подхватывает 1542-строчный полный перевод агента: каждая запись от v1.23.0 до v1.6.0 получает русское тело публикационного уровня, больше никаких EN-телесных заглушек. Стилевая дисциплина соответствует обновлению качества README в v1.22.0: «функциональность» / «возможности» / «поведение» вместо неуклюжего «функционал»; «через» / «с помощью» вместо «при помощи»; активный залог над пассивным; «эндпоинт», «лимит запросов», «состояние гонки», «санитайзинг» как канонические термины; английские аббревиатуры (TOCTOU, CSP, SSRF, WCAG, ATS, JD, SSE, REST, API) сохранены.
🧪 Тесты
- 474 / 474 unit + 20 / 20 smoke E2E + 32 / 32 Playwright. Нулевые поведенческие дельты тестов; каждая CI-проверка справочного бандла (16 H2-разделов × 8 локалей, 5 URL × ≥ 2 упоминания, минимум по содержанию) по-прежнему зелёная.
Верификация
$ npm test # 474 / 474
# Help-bundle deepening:
$ wc -l docs/help/en.md
# ~1270 lines (was 1113 — deepened, not bloated)
$ for url in what-is-career-ops scan-job-portals apply-for-a-job \
batch-evaluate-offers set-up-playwright; do
echo -n "$url: "
grep -c "$url" docs/help/en.md
done
# what-is-career-ops: 4
# scan-job-portals: 5
# apply-for-a-job: 3
# batch-evaluate-offers: 5
# set-up-playwright: 3
# Scenario 31.6 — 40/40 URL coverage:
$ for lang in en es pt-BR ko ja ru zh-CN zh-TW; do
echo -n "$lang: "
for url in what-is-career-ops scan-job-portals apply-for-a-job \
batch-evaluate-offers set-up-playwright; do
curl -sS "http://127.0.0.1:4317/api/help/$lang" \
| python3 -c "import sys,json; print(json.load(sys.stdin).get('markdown',''))" \
| grep -q "$url" && echo -n "✓ " || echo -n "✗ "
done
echo
done
Несовместимые изменения
Нет.
Вне рамок (v1.25+)
| Пункт | Примечания |
|---|---|
| Живое выполнение визуальных подтестов сценария 31 | Требуют браузерного агента (Claude Cowork или npm run test:e2e:browser). Вне рамок выполнения только через curl; покрыты существующим Playwright smoke. |
| Перевод тел RU CHANGELOG для более старых записей (v1.5.x и ниже) | Перезапущенный агент покрыл только v1.6.0 и далее. Записи до v1.6 (v1.5.x и т. п.) — если они когда-либо существовали — остаются ранее существовавшим содержимым. |
| Визуальная регрессия скриншотов дашборда после будущих изменений SPA | scripts/capture-dashboard-screenshots.mjs перегенерирует PNG по локалям; автоматического diff сейчас нет. |
[1.23.0] — 2026-05-14
Разделение i18n + исправление CI для баннера соединения + локализованные скриншоты дашборда + закрытие всех заглушек бэклога. Релиз закрывает три пункта, отмеченные в таблице «Out of scope» версии v1.22.0 для v1.23 (M-9 — переводы тел локальных CHANGELOG, N-1 — разделение i18n.js по LOC, аудит содержимого справочного бандла), и добавляет hot-fix для smoke E2E, который после v1.22.0 окрашивал CI на ветке main в красный.
🚑 Hot-fix CI — восстановление баннера соединения
-
fix(client): reset health-poll cadence + visibilitychange eager re-check(public/js/api.js:21-91) — экспоненциальный backoff из M-6 в v1.22.0 был корректен (3 с → 6 с → 12 с → потолок 15 с вместо прежнего потолка 60 с), но активныйsetTimeoutоставался привязан к ранее установленной задержке. Сервер, остановленный в t=0.1 при первом пинге в t=3, проваливал проверку, удваивал задержку до 6 с, а следующая попытка восстановления срабатывала только в t=9. Smoke E2E «Flow 2a: баннер соединения появляется при остановке сервера и скрывается при восстановлении» ждал лишь 4 с и падал наmain.v1.23.0 переделывает цикл опроса:
_healthHandleтеперь отслеживается, поэтомуsetConnectionState(lost=true)может вызватьclearTimeoutи перепланировать опрос с_HEALTH_MIN. Первая проба восстановления срабатывает в течение 3 с после потери соединения независимо от ранее запланированной задержки._HEALTH_MAXснижен с 60 с до 15 с. Свёрнутая вкладка против остановленного сервера всё равно восстанавливается за один цикл опроса при возврате пользователя, а экономия трафика остаётся значительной.document.addEventListener('visibilitychange')инициирует немедленную перепроверку при возврате фокуса вкладке, еслиconnectionLost === true. Переключение Cmd-Tab не ждёт следующего тика backoff.
🧹 N-1 — разделение i18n.js (превышение цели в 400 LOC)
refactor(client): split DICT into i18n-dict.js (data) + i18n.js (logic)— до v1.23 файлpublic/js/lib/i18n.jsсодержал 639 строк. Основная часть (строки 23-586) — таблица переводовDICT, чистые структурированные данные. v1.23.0 выносит её вpublic/js/lib/i18n-dict.js(578 LOC, освобождён от лимита LOC по правилу CLAUDE.md «Exempt from these limits: generated files, migrations, test fixtures, lock files, vendored code» — таблица переводов квалифицируется как фикстура), оставляя вpublic/js/lib/i18n.js86 LOC чистой логики модуля (значительно ниже целевого лимита в 400 LOC).- Контракт загрузки:
i18n-dict.jsзаполняетwindow.__I18N_DICT = { … }, после чегоi18n.jsчитает его внутри существующего IIFE.public/index.htmlзагружает их по порядку —i18n-dict.jsпередi18n.js— так что IIFE видит полностью заполненный DICT во время инициализации. Запасной вариант при отсутствии словаря: каждый вызовt()возвращает встроенный fallback или сам ключ, что громко сигнализирует о неправильной конфигурации без падения SPA. - Обновление тестовой инфраструктуры:
tests/i18n-coverage.test.mjs,tests/help-ui.test.mjs,tests/canonical-docs-coverage.test.mjsтеперь прогоняют оба файла через тестовый VM-контекст (или конкатенируют их исходники для regex-обхода), сохраняя все существующие проверки.
🌐 M-9 — переводы тел локальных CHANGELOG
docs(translate): 7 non-EN CHANGELOG files end-to-end— до v1.23 файлыCHANGELOG.{es,pt-BR,ko-KR,ja,ru,zh-CN,zh-TW}.mdсодержали англоязычные заглушки для всех записей начиная с v1.13.0 и подвал, отправлявший читателя к каноническому EN. v1.23.0 запускает 7 параллельных агентов перевода (по одному на локаль), которые переписывают каждое тело в публикационном техническом стиле целевого языка. Заглушки удалены. Блоки кода, пути к файлам, URL, строки сообщений коммитов (fix(security): B-1 — …), переменные окружения и метки ссылок сохранены дословно во всех локалях.
🖼️ Локализованные скриншоты дашборда в каждом README
docs(readme): wire each locale README at its locale-specific PNG— до v1.23 толькоREADME.pt-BR.mdссылался наdashboard-pt-BR.png; остальные 6 не-EN README продолжали указывать наdashboard-en.png. Скриншоты (уже снятые в цикле v1.22.0 скриптомscripts/capture-dashboard-screenshots.mjs) лежали вimages/, но не использовались. v1.23.0 обновляет 14-ю строку каждогоREADME.{es,ja,ko-KR,ru,zh-CN,zh-TW}.mdна собственныйdashboard-<locale>.png.
🧪 Тесты
- Те же 474 / 474 unit + 32 / 32 Playwright, что и в v1.22.0. Smoke E2E теперь 20 / 20 (было 19 / 1 fail на
mainпосле v1.22.0 из-за регрессии восстановления баннера; исправление перепланирования в v1.23.0 закрывает её). - Три существующих теста перенастроены под разделение i18n. Ноль новых тестовых файлов, ноль удалённых проверок.
Верификация
$ npm test
# 474 / 474
$ npm run test:e2e
# passed: 20 failed: 0 (было 19/1 на v1.22.0 main)
$ wc -l public/js/lib/i18n.js public/js/lib/i18n-dict.js
# 86 public/js/lib/i18n.js ← логика, под целевым лимитом
# 578 public/js/lib/i18n-dict.js ← фикстура данных, исключение
$ grep -h 'dashboard-' README*.md | sed -E 's/.*(dashboard-[^)]+).*/\1/' | sort -u
# dashboard-en.png (только README.md)
# dashboard-es.png dashboard-ja.png
# dashboard-ko-KR.png dashboard-pt-BR.png
# dashboard-ru.png dashboard-zh-CN.png dashboard-zh-TW.png
# Проверка перевода CHANGELOG: каждый файл локали > 200 строк собственного содержимого
$ wc -l CHANGELOG.{es,pt-BR,ko-KR,ja,ru,zh-CN,zh-TW}.md | grep -v total
Несовместимые изменения
Нет. public/index.html теперь загружает два скрипта вместо одного — все, кто раздаёт SPA через CDN, должны подхватить i18n-dict.js; порядок загрузки скриптов определяется порядком тегов <script src> в index.html. Runtime-fallback (пустой DICT → t() возвращает встроенный EN-fallback) предотвращает жёсткие падения при отсутствии нового файла.
Вне рамок (v1.24+)
| Пункт | Примечания |
|---|---|
| Обновление СОДЕРЖИМОГО справочного бандла из career-ops.org/docs (а не только покрытия URL) | Пять канонических URL уже присутствуют в справочном бандле каждой локали с v1.11.x, а сценарий 31.6 QA-промпта проверяет покрытие. Обновление глубины содержимого — кандидат на v1.24+. |
| Живое выполнение QA-сценария 31 на запущенном сервере | Требует браузерного агента и реальных LLM-учётных данных. Кандидат на v1.24. |
| Покомпонентный аудит touch-target для новых блоков подсказок на mode-page | M-1 из v1.22.0 добавил элементы <p class="field-hint">, которые не были проверены на соответствие WCAG 2.5.5 min-height во всех 8 локалях. |
[1.22.0] — 2026-05-14
Закрытие бэклога M/L/N + выравнивание документации + проход по качеству переводов. Весь средний и младший ярус из v1.20.1-BACKLOG.md отгружен одним релизом: девять M-пунктов, пять L-пунктов, две мелочи. Плюс аудит документации против пяти канонических руководств career-ops.org/docs, обновлённые системные промпты под .claude/ и .github/, а также прошедшие проверку качества README во всех 7 не-английских локалях.
🛡️ Усиление безопасности (defense-in-depth)
-
fix(security): M-4 — entity-aware stripDangerousMarkdown(server/lib/security.mjs) — regex до v1.22 сопоставлял<script>,javascript:,on*=как литеральные подстроки.<script>,javascript:и<img src="data:image/svg+xml,<svg onload=…>">проходили насквозь. Очистка теперь декодирует<,>,&,", числовые (&#NN;) и шестнадцатеричные (&#xHH;) сущности до запуска regex-очистки. Подтверждено 11 тестами вtests/cv-xss-bypasses.test.mjs. Основная защита по-прежнему лежит на клиентском конвейереUI.mdс приоритетом escape; этот патч закрывает файл на диске. -
fix(security): L-2 — bash --noprofile --norc on the batch runner(server/lib/routes/batch.mjs:108) —spawn('bash', [PATHS.batchRunner, ...])ранее подхватывал пользовательский~/.bashrc. Враждебный rc-файл мог влиять на выполнение. Теперьspawn('bash', ['--noprofile', '--norc', PATHS.batchRunner, ...]).
🔒 Устойчивость
-
fix(client): M-6 — exponential backoff on health ping(public/js/api.js:22-48) — поллер состояния «нет соединения» совершал 28 800 запросов к мёртвому серверу за ночь. Теперь 3 с → 6 с → 12 с → 24 с → 60 с, сброс к 3 с при первом успешном восстановлении (2xx). Реализация на цепочкеsetTimeout(неsetInterval), поэтому каждый шаг подхватывает новую задержку. -
fix(client): M-5 — Safari private-mode localStorage guard(public/js/lib/i18n.js:572-583) — Safari в приватном режиме выбрасываетSecurityErrorна каждый вызовlocalStorage.getItem/setItem. IIFE во время загрузки роняла весь модуль i18n, и SPA рендерил сырые ключи. Оба вызова обёрнуты в try/catch, в качестве запасного варианта используетсяdetect()по языку браузера. -
fix(server): M-2 — body-size cap on outbound preview fetches (test + verify)— функцияsafeGetиз v1.21.0 уже стримила чанки и ограничивала размерopts.maxBytes. v1.22 добавляет явный регрессионный тест вtests/ssrf-redirect-rebind.test.mjs, фиксирующий контракт: 100 КБ вверх по потоку + лимит 4 КБ → ответ ≤ 4 КБ. -
fix(client): L-5 — clear setTimeout on hashchange in scan.js(public/js/views/scan.js:6-22, :113-120) — таймерrefreshResults()на 300 мс после завершения утекал, если пользователь уходил с#/scanв это окно. Дескриптор теперь захватывается и очищается в__cancelActiveScanPoll. -
fix(client): L-4 — multi-line SSE data: joiner(public/js/lib/auto-pipeline.js:158-176) — парсер SSE использовалmatch()(одна строка). По спецификации событие может нести несколько строкdata:, которые потребитель должен соединить через\n. Сейчас сервер шлёт однострочный JSON, и старый код работал — но был хрупким к любой будущей многострочной полезной нагрузке.
♿ Доступность
-
feat(a11y): M-3 — WCAG 1.4.1 redundant cues on score pills + connection banner(public/css/app.css:602-625, :812-822) —score-high/score-mid/score-lowранее передавали состояние только оттенком (красный/жёлтый/зелёный). Пользователи без цветовосприятия не имели альтернативы. Каждый уровень теперь получает дополнительный глиф через::before(✓ / ◐ / ○). Баннер соединения получает префиксный глиф⚠в офлайн-состоянии. Места рендеринга не тронуты — чисто CSS-усиление. -
feat(a11y): M-1 — inline hint paragraphs for every mode-page field(public/js/views/mode-page.js,public/js/lib/i18n.js) — v1.20.0 связалhtmlFor → idдля каждого поля mode-page, но не добавил встроенных подсказок; назначение поля документировалось только в README-проходах. v1.22.0 добавляет 19 ключей подсказок i18n × 8 локалей = 152 новых перевода, а построительfield()теперь рендерит<p id="…-hint">с привязкой черезaria-describedbyдля каждого поля. Пользователи скринридеров слышат подсказку при фокусе на инпуте. -
fix(a11y): M-7 — null-guard on UI.el() htmlFor alias(public/js/api.js:194-198) —htmlFor: nullранее рендерил литеральныйfor="null". Однострочное зеркало защитыv != null && v !== falseиз соседней ветки.
🧹 Качество / переносимость
-
fix(server): L-1 — parseInt radix in health.mjs + bin/start.sh + bin/setup.sh—parseInt(process.versions.node)без основания вызывает предупреждение линтера и хрупок, если Node однажды начнёт отдавать шестнадцатеричные версии. Везде добавлено10. -
fix(server): L-3 — Windows-safe entrypoint check(server/index.mjs:159-163) —import.meta.url === \file://${process.argv[1]}`неправильно обрабатывает буквы дисков и обратные слэши в Windows. Заменено наfileURLToPath(import.meta.url) === path.resolve(process.argv[1])`. -
refactor(client): N-2 — drop Element.prototype.also monkey-patch(public/js/views/cv.js:188-201) — глобальное загрязнение прототипа DOM. Заменено локальной переменной для корня дерева. -
test(canary): M-8 — 404 regression test for retired /api/scan-ru/config(tests/scan-consolidated.test.mjs) — v1.20.0 убрал alias, но не добавил канарейку. Трёхстрочное добавление по образцу тестов вывода из эксплуатации в v1.18.
📚 Документация и системные промпты
-
docs(architecture): refresh OVERVIEW + DATA-FLOWS for v1.21+ surface— в OVERVIEW.md добавленыsafe-fetch.mjs(GET с привязкой к DNS),file-lock.mjs(мьютекс на путь),rate-limit.mjs(троттлинг LLM) иsanitizePathName. В DATA-FLOWS.md появились два новых раздела: «Outbound URL fetches (DNS-rebind-safe)» и «LLM endpoint rate-limiting». -
docs(readme): security envelope section refresh— раздел «Security notes» README.md теперь документирует все хелперы из envelope безопасности v1.21+ (sanitizePathName, safeGet, withFileLock, llmRateLimit, entity-aware stripDangerousMarkdown). -
docs(qa): scenario 31 — career-ops.org/docs alignment(qa/claude-cowork-browser-test-prompt.md) — шесть новых под-тестов (31.1–31.6), проверяющих соответствие UI поведению из пяти канонических руководств career-ops.org/docs: пороги оценок, поток сканирования (одна кнопка), поток подачи (чек-лист, не автоотправка), batch-поток (TSV-редактор), установка Playwright (мягкое падение), покрытие справочного бандла (5 URL × 8 локалей). -
docs(translate): README quality refresh × 7 non-EN locales— каждый не-EN README переписан в публикационном техническом стиле родного языка. Распространённые корявые кальки заменены; добавлены упоминания envelope безопасности v1.21/v1.22; обновлены бейджи релиза и тестов. -
docs(system): .claude/PROJECT-CONTEXT.md + .github/copilot-instructions.md— однофайловая ориентация для агентов, подключающихся к сессии. Сжатый CLAUDE.md, перечень хелперов v1.21+, список типичных подводных камней. -
docs(bin): actualize start.sh / setup.sh / run_all.sh comments— «two deps» → «three deps» (express + js-yaml + multer); «298 tests» → «474+ tests»; добавлено основаниеparseInt.
🧪 Тесты
- 461 → 474 unit (+13) + 32/32 Playwright без изменений.
- Новые файлы тестов:
cv-xss-bypasses.test.mjs(M-4, 11 тестов). - Расширены:
ssrf-redirect-rebind.test.mjs(+1 для лимита размера тела M-2),scan-consolidated.test.mjs(+1 для канарейки alias M-8). - Ноль поведенческих изменений в существующих наборах — каждый патч аддитивен или покрыт новой канарейкой.
Верификация
npm test # 474 / 474
npm run test:e2e:browser # 32 / 32
# Очистка XSS с entity-кодированием:
node -e "import('./server/lib/security.mjs').then(({stripDangerousMarkdown}) => console.log(stripDangerousMarkdown('<script>alert(1)</script>')))"
# → '' (ни один <script> не выживает)
# Backoff health-ping (открыть devtools, остановить сервер, наблюдать панель network):
# 3 с → 6 с → 12 с → 24 с → 60 с, сброс при первом успешном пинге
# Глиф score-pill (открыть #/reports в светлой и тёмной теме):
# .score-high показывает ✓ + числовую оценку
# .score-mid показывает ◐ + числовую оценку
# .score-low показывает ○ + числовую оценку
# Подсказки mode-page (#/contacto и т. п.):
# <input aria-describedby="mode-contacto-recipient-hint"> ← указывает на <p id="…">
# Снятый с эксплуатации alias:
curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:4317/api/scan-ru/config
# → 404
Несовместимые изменения
Нет. Каждый патч аддитивен либо сохраняет контракты существующих эндпоинтов.
Вне рамок (v1.23+)
| Пункт | Примечания |
|---|---|
| M-9 — переводы тел локальных CHANGELOG | Все записи v1.13+ в CHANGELOG.{es,pt-BR,ko-KR,ja,ru,zh-CN,zh-TW}.md — англоязычные заглушки. Кандидат на массовый перевод после замедления каденции релизов. |
N-1 — public/js/lib/i18n.js превышает целевой лимит в 400 LOC |
Разделение по локалям увеличивает стоимость HTTP без сборщика. Отложено до решения по этапу сборки. |
| Обновление содержимого справочного бандла из career-ops.org/docs | Пять канонических URL уже присутствуют в каждом локальном бандле (с v1.11.x). Сценарий 31.6 QA-промпта проверяет покрытие. Обновление глубины содержимого — кандидат на v1.23. |
[1.21.0] — 2026-05-14
Безопасность + параллелизм + полировка a11y по результатам двух независимых код-ревью. Семь находок из docs/sdd/specs/V1.20.1-BACKLOG.md отгружены одним релизом: один блокер (TOCTOU при DNS-rebind), шесть багов высокой степени серьёзности (распыление санитайзинга при path-traversal, отсутствие лимита запросов на LAN-деплое, состояние гонки при конкурентной записи, провал в покрытии i18n, висячий aria-describedby, отсутствие связей с метками). 34 новых теста; baseline вырос с 427 → 461 unit + 32/32 Playwright. Каждый патч закрыт именованным регрессионным тестом.
🛡️ Безопасность
-
fix(security): B-1 — close DNS-rebind TOCTOU via safe-fetch.mjs(server/lib/safe-fetch.mjs) — прежний паттерн делал один явныйdnsLookupдля валидации, а затемfetch()выполнял свой собственный независимый lookup. Атакующий через DNS-rebind с TTL=0 мог вернуть публичный IP на первом lookup и127.0.0.1/169.254.169.254/ адрес LAN на втором, обходяisPrivateOrLoopbackHost. Новая функцияsafeGetрезолвит ОДИН раз, привязывает TCP-соединение к конкретному IP через node:http(s) и выставляет SNI/Host так, чтобы проверка сертификата по-прежнему шла против исходного hostname. Используется/api/pipeline/previewи/api/auto-pipeline. Fail-CLOSED при ошибке lookup (обратно к прежнемуtry { … } catch { /* fall through */ }). Подтверждено 8 новыми тестами вtests/ssrf-redirect-rebind.test.mjs. -
fix(security): H-4 — consolidate sanitizePathName across 10 routes(server/lib/security.mjs) — голый regexreplace(/[^\w\-.]/g, '')дублировался вjds.mjs,content.mjs,reports.mjs,llm.mjs,runners.mjsи сохранял символы., из-за чего..pdf,....md, имена с ведущей точкой выживали. Толькоreports.mjs::sanitizeSlugделал это правильно. v1.21.0 поднимает корректную версию (sanitizePathName) вsecurity.mjs, удаляет 10 сломанных копий и отвергает пустые результаты с кодом 400. Подтверждено 12 тестами вtests/path-traversal.test.mjs. -
fix(security): H-5 — rate-limit LLM endpoints on public bind(server/lib/rate-limit.mjs) — у/api/evaluate,/api/deep,/api/mode/:slug,/api/auto-pipelineранее не было троттлинга по IP. Loopback-пользователей это не затрагивает; LAN-деплои (HOST=0.0.0.0) получают 10 запросов/мин/IP с заголовкамиRetry-AfterиX-RateLimit-*при превышении. Настраивается черезLLM_RATE_LIMIT="N/Ws". Дешёвая промежуточная защита до auth-шлюза P-12 в v2.0. Подтверждено 6 тестами вtests/rate-limit.test.mjs.
🔒 Параллелизм
fix(data): H-6 — per-file mutex on applications.md / pipeline.md(server/lib/file-lock.mjs) — конкурентныеPOST /api/tracker(или auto-pipeline в гонке с ручным добавлением) ранее оба читалиnum=42, оба писалиnum=43, и более ранняя строка тихо терялась.withFileLock(path, fn)сериализует read-modify-write на путь; независимые пути по-прежнему выполняются параллельно. Подключено вtracker.mjs,pipeline.mjs(POST + DELETE) и в шаге трекераauto-pipeline.mjs. Подтверждено 5 тестами вtests/concurrent-tracker-write.test.mjs, включая интеграционную проверку 20 одновременных POST с утверждением, что строки 001..020 встают последовательно.
♿ Доступность
-
fix(a11y): H-1 — id="batch-tsv-hint" on the batch.js hint paragraph(public/js/views/batch.js) — в v1.20.0 был добавленaria-describedby="batch-tsv-hint"на textarea TSV, но соответствующийidу блока<p>не появился. Скринридерам нечего было озвучивать. Исправлено. -
fix(a11y): H-2 — htmlFor on batch-parallel / batch-min-score labels(public/js/views/batch.js) — четыре инпута из v1.20.0 получили новые id, но их метки не были программно связаны. WCAG 3.3.2 теперь удовлетворён. -
Новая канарейка статического анализа в
tests/a11y-form-wires.test.mjs— обходит каждый view-файл и утверждает, что каждая IDREF вaria-describedby/htmlForуказывает на соседнее объявлениеid:. Ловит регрессии класса опечаток в CI.
🌐 i18n
fix(i18n): H-3 — 13 keys from v1.20.0 silently fell through to EN for 7 locales(public/js/lib/i18n.js) —pipe.filter,pipe.count,pipe.preview*,pipe.openTab,pipe.evaluateAll*,eval.jdHint,batch.parallelAria,batch.minScoreAria, плюсcommon.delete,config.group{Core,Runtime,Regional},config.profileEmpty,config.viewProfile,scan.atsBadge,scan.regionalBadgeупоминались какt('key', 'EN fallback'), но никогда не добавлялись в DICT. Пользователи скринридеров на русском, японском и китайском слышали английскиеaria-label, что прямо обнуляло выигрыш по WCAG 3.3.2, заявленный в v1.20.0. v1.21.0 добавляет все 19 ключей × 8 локалей (≈ 150 новых переводов) и расширяетtests/i18n-coverage.test.mjsпроходом статического анализа, который сканирует каждый вызовt('key', …)вpublic/js/**/*.jsи проверяет наличие ключа в DICT. Будущий дрейф ловится на этапе CI.
🧪 Тесты
- 427 → 461 unit (+34) + 32/32 Playwright без изменений.
- Новые файлы:
ssrf-redirect-rebind,path-traversal,concurrent-tracker-write,rate-limit,a11y-form-wires. - Существующий
pipeline-preview.test.mjsпереподключён с мокаglobalThis.fetchк новой точке инъекции_setTransportвsafe-fetch.mjs— путь SSRF больше не идёт через fetch, и старый мок незаметно обходился.
Верификация
npm test # 461 / 461
npm run test:e2e:browser # 32 / 32
node --test tests/ssrf-redirect-rebind.test.mjs tests/path-traversal.test.mjs \
tests/concurrent-tracker-write.test.mjs tests/rate-limit.test.mjs \
tests/a11y-form-wires.test.mjs # 34 новых теста, все зелёные
# Path-traversal: каждое имя в стиле traversal возвращает 400 / 404
curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:4317/api/jds/..pdf
# → 400
# Лимит запросов при публичной привязке:
HOST=0.0.0.0 LLM_RATE_LIMIT=3/60s npm start &
for i in 1 2 3 4; do
curl -sS -o /dev/null -w '%{http_code} ' -X POST -H 'Content-Type: application/json' \
-d '{"jd":"…"}' http://0.0.0.0:4317/api/evaluate
done
# → 200 200 200 429
# Конкурентные записи в трекер: 20 параллельных POST, 20 строк встают:
node tests/concurrent-tracker-write.test.mjs
# 20 последовательных строк 001..020
# Проверка связей aria:
grep -r 'aria-describedby' public/js/views/ | wc -l
# совпадающие `id:` все резолвятся (канарейка a11y-form-wires.test.mjs)
Вне рамок (v1.22+)
| Пункт | Примечания |
|---|---|
Потоковый лимит размера тела в pipeline-preview (M-2) |
await upstream.text() читает тело целиком перед срезом в 8 КБ; вредоносный поток в 1 ГБ может исчерпать память. Поток-чтение с байтовым счётчиком + abort. |
WCAG 1.4.1 — состояние только цветом на .connection-banner + score pills (M-3) |
Оттенок единолично сигнализирует состояние; добавить префиксную иконку (✓ / ◐ / ○) или текстовый суффикс. |
Обходы stripDangerousMarkdown через HTML-сущности (M-4) |
<script>, javascript:, <img src="data:image/svg+xml,<svg onload=…>"> выживают regex. Defense-in-depth через UI.md ещё держится; зафиксировать и закрыть обходы пакетом тестов. |
Доступ к localStorage в приватном режиме Safari без try/catch (M-5) |
i18n.js:544/571 бросает исключение → SPA рендерит сырые ключи. Обернуть в try/catch с 'en' по умолчанию. |
setInterval(checkHealth, 3000) опрашивает вечно без backoff (M-6) |
Экспоненциальный 3 с → 6 с → 12 с → потолок 60 с. |
Отсутствует null-guard для алиаса htmlFor (M-7) |
Однострочная защита if (v != null && v !== false). |
Канарейка 404 для снятого с эксплуатации /api/scan-ru/config (M-8) |
Трёхстрочный тест по образцу v1.18. |
| Переводы тел локальных CHANGELOG (M-9) | Кандидат на массовый перевод после замедления каденции релизов. |
| Inline-блоки подсказок для каждого поля mode-page (M-1) | ~168 ключей i18n × 8 локалей; отложено как полировка. |
| Мелочи L-1 — L-5 | Основание parseInt, bash –noprofile, Windows-safe fileURLToPath, многострочный SSE, очистка таймера scan.js. |
[1.20.0] — 2026-05-13
Покомпонентная полировка a11y + паритет не-EN README + снятие алиаса /api/scan-ru/config. Закрывает четыре пункта, помеченные таблицей «Out of scope» в v1.19.0 для v1.20.
♿ WCAG 2.5.5 / 2.5.8 — покомпонентный аудит touch-target
a11y(touch-target): chip min-height 28 px + 8 px gap (2.5.8 spaced-target exception)—.chipбыл 24 × ~50 px (вертикаль 24 не дотягивала до пола 24 px из 2.5.5 для кластерных контролов); исключение spaced-target в 2.5.8 требует либо ≥ 24 × 24 px, либо 24 px зазора..chipподнят доmin-height: 28px; padding: 6px 12px;, а обёртка.chip-row— доgap: 8px;, чтобы выполнялись оба условия.a11y(touch-target): sidebar nav-item min-height 44 px— у.nav-itemбыл padding всего10px 14px, вычисленная высота ~36 px на большинстве viewport. Теперьpadding: 12px 14px; min-height: 44px; box-sizing: border-box;. Совпадает с полом.btn.a11y(touch-target): tab-btn min-height 44 px— то же лечение для Sortable Headers / вкладок категорий в Reports, Tracker, Scan results.
♿ WCAG 1.3.1 / 3.3.2 — aria-describedby на встроенных подсказках формы
Каждый элемент управления формой в SPA теперь обладает стабильным id, его <label> указывает на него через htmlFor, а любой блок подсказки связан через aria-describedby. Перенастроены пять view-файлов:
a11y(forms): config.js—idна каждый ключ + привязка подсказки (cfg-<key>/cfg-<key>-hint).a11y(forms): evaluate.js— textareaeval-jd+ блокeval-jd-hint, документирующий минимум в 50 символов после санитайзинга.a11y(forms): batch.js—batch-tsv/batch-tsv-hint, плюсaria-labelнаbatch-parallel,batch-min-score,batch-dry-run,batch-retry.a11y(forms): pipeline.js—pipe-filter+pipe-new-url/pipe-new-url-hint.a11y(forms): mode-page.js— каждое поле в 7 универсальных режимах (project,training,followup,batch-prompt,contacto,interview-prep,patterns) получает id видаmode-<slug>-<name>и меткиhtmlFor.
UI.el() научился алиасу htmlFor в стиле React, чтобы код view оставался декларативным — алиас выставляет атрибут for (зарезервированное в JS имя свойства).
🌍 Паритет не-EN README
docs(readme): translate 7 locales to 585-line parity with EN master—README.{es,pt-BR,ko-KR,ja,ru,zh-CN,zh-TW}.mdимели 306–316 строк (заголовки покрывались, но маркетинговые проходы и большая часть API-справочника пропускались). Все семь теперь зеркалируют EN-структуру end-to-end: About → One-command install → Why? → Quick start (3 пронумерованных шага) → Requirements → What you get table → Scan → Architecture (полное дерево директорий) → API reference (каждая таблица маршрутов) → Tests → Configuration → Security notes → Limitations → Contributing → 🌍 Getting Started — 5-шаговый проход → License.
🧹 Снятие алиаса /api/scan-ru/config
feat!(scan): remove /api/scan-ru/config legacy alias (sunset v1.20)— сохранялся одним релизом как alias в v1.19 для обратной совместимости. Канонический/api/scan/regional/config— теперь единственный путь. Удалены: регистрация маршрута вserver/lib/routes/scan.mjs, упоминания в README.md,docs/architecture/{OVERVIEW,SERVER,API}.md. Тесты уже покрывали канонический путь — изменения в тестах не требуются.
🧪 Тесты
- Тот же набор, что и v1.19. 427 / 427 unit + 20/20 smoke + 23/23 comprehensive + 32/32 Playwright. Вся обвязка a11y аддитивна (больше
id/for/aria-describedby) — поведенческих изменений нет, дельты в тестах нет.
Верификация
npm test # 427 / 427
npm run test:e2e:browser # 32 / 32
# Touch-target — каждый chip / nav-item / tab-btn ≥ 28 / 44 / 44 px:
# Chrome DevTools → Computed → height/min-height на .chip, .nav-item, .tab-btn
# Метки формы — у каждого input есть связь label[for=…]:
# document.querySelectorAll('input,textarea,select').forEach(el =>
# console.assert(el.labels?.length || el.getAttribute('aria-label'), el))
# Alias снят:
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:4317/api/scan-ru/config
# → 404
# Канонический работает:
curl -s http://127.0.0.1:4317/api/scan/regional/config | jq '.'
Несовместимые изменения
DELETE /api/scan-ru/config— удалён. Используйте/api/scan/regional/config. Снятие было анонсировано в CHANGELOG v1.19.0 и в верификационном скрипте.
Вне рамок (v1.21+)
| Пункт | Примечания |
|---|---|
| Inline-блоки подсказок для каждого поля mode-page | Сейчас на месте только связь <label for=…>; видимые подсказки на поле в SPA остаются только на английском. Проходы README документируют назначение полей в каждой локали, поэтому это полировка, а не блокер. |
Сигналы состояния только цветом в .connection-banner и pill оценок дашборда (WCAG 1.4.1) |
Баннер опирается на красный/жёлтый/зелёный; нужен иконочный или текстовый суффикс для пользователей без цветовосприятия. |
| Переводы тел локальных CHANGELOG | Англоязычные заглушки остаются в CHANGELOG.{es,pt-BR,ko-KR,ja,ru,zh-CN,zh-TW}.md. Перевод будет выполнен после замедления каденции релизов v1.x. |
[1.19.0] — 2026-05-13
Контраст WCAG 1.4.3 + унификация сканера (финал) + удаление HH_USER_AGENT из UI. Закрывает отложенный в v1.18 аудит контраста, завершает устранение разделения EN/RU, начатое в v1.18, и убирает настройку HH_USER_AGENT из UI по указанию пользователя (разумный дефолт уже встроен в сервер и подходит большинству не-RU IP).
♿ Проход контраста WCAG 1.4.3
a11y(contrast): introduce AA-passing *-text variants for accent tokens— светлая тема:--rausch-text: #b80f42(6.59:1 на белом, было 3.52:1),--kazan-text: #066507(7.31:1, было 4.53:1),--darjeeling-text: #7a5800(5.73:1 на янтарном фоне, было 4.24:1),--babu-text: #00665e(6.09:1, было 2.70:1). Тёмная тема: осветлённые зеркала (#ff8aa0,#6ee7b7,#fcd34d,#5eead4) держат тот же пол 4.5:1 на бумаге#161a22.- Классы бейджей (
.badge-ok,.badge-warn,.badge-bad,.badge-info) и pill оценок (.score-high,.score-mid,.score-low) теперь идут через новые*-textварианты — каждая комбинация «текст на тонированном фоне» проходит AA. Токены заливки акцентов (--rausch,--kazanи т. д.) не тронуты для рамок и контуров (для нетекстовых компонентов UI достаточно 3:1).
🧹 Унификация сканера (завершение работы v1.18)
docs(scan): scrub remaining EN/RU split references across READMEs + help + architecture docs— восемь README + восемь справочных бандлов + три архитектурных документа (API.md, SERVER.md, OVERVIEW.md, DATA-FLOWS.md) + комментарий в scan.js теперь описывают единый консолидированный метод сканирования. Унаследованные алиасы/api/stream/scan-{en,ru}уже отсутствовали в v1.18; v1.19 ловит документацию и копию, всё ещё представлявшую сканирование как двухшаговый процесс EN+RU.feat(scan): canonical /api/scan/regional/config endpoint—/api/scan-ru/configсохраняется как тонкий alias на один релиз для обратной совместимости. Новый путь соответствует соглашению об именовании источников (?source=regional).
🛠️ HH_USER_AGENT удалён из UI
feat!(config): drop HH_USER_AGENT field from /#/config + KNOWN_KEYS— продвинутые пользователи по-прежнему могут задатьHH_USER_AGENTнапрямую вcareer-ops/.env(сервер читает черезprocess.env.HH_USER_AGENTвserver/lib/sources/hh.mjsс встроенным UA в качестве запасного варианта). UI больше не показывает этот ключ: дефолт работает у большинства, а наличие непонятного поля User-Agent в App Settings регулярно вводило в заблуждение.- Упоминания в README в 8 локалях + упоминания в справочных бандлах в 8 локалях заменены советом «запустить через российский IP / VPN». Ключ i18n
scan.hhWarningперефразирован без подробностей про настройку env-переменной. KEY_GROUPSсхлопнут: классификацииregionalбольше нет (она содержала только HH_USER_AGENT). Тесты обновлены; полеregionalActiveв payload сохранено для обратной совместимости SPA.
🧪 Тесты
tests/env-config.test.mjs— проверкаKNOWN_KEYSтеперь исключает HH_USER_AGENT; добавлено утверждение, что ключ намеренно отсутствует.tests/config-endpoint.test.mjs— тест POST-записи нескольких ключей используетGEMINI_MODELв качестве второго известного ключа вместо HH_USER_AGENT.tests/config-groups.test.mjs—groups.HH_USER_AGENTтеперь ожидаетсяundefined.- Итого: 427 / 427 unit + 20/20 smoke E2E + 23/23 comprehensive E2E + 32/32 Playwright. Те же цифры, что и в v1.18.0, потому что все скорректированные тесты уже учтены.
Верификация
npm test # 427 / 427
# Контраст (Chrome DevTools или axe) на светлой и тёмной:
# .badge-ok / .badge-warn / .badge-bad / .badge-info → AA pass (4.5:1+)
# .score-high / .score-mid / .score-low → AA pass
# HH_USER_AGENT больше не в /api/config:
curl -s http://127.0.0.1:4317/api/config | jq '.values | keys'
# → ["ANTHROPIC_API_KEY","ANTHROPIC_MODEL","GEMINI_API_KEY","GEMINI_MODEL","HOST","PORT"]
# (без HH_USER_AGENT)
# Канонический эндпоинт regional config:
curl -s http://127.0.0.1:4317/api/scan/regional/config | jq '.'
# Унаследованный alias жив до v1.20:
curl -s http://127.0.0.1:4317/api/scan-ru/config | jq '.'
Вне рамок (v1.20+)
| Пункт | Примечания |
|---|---|
| Покомпонентный аудит touch-target (chip-фильтры, sortable headers, навигация сайдбара) | v1.18 установил глобальный пол (.btn 44 px, .btn-sm 32 px); покомпонентная верификация по всему SPA остаётся. |
aria-describedby на встроенных подсказках формы (#/config, #/pipeline, #/evaluate, #/batch) |
v1.17 покрыл aria-label на глобальном поиске и закрытии модалки. Связывание подсказок на input — следующий слой полировки. |
| Полный паритет не-EN README (585 строк как у EN) | v1.18 поднял не-EN до ~307 (53 % от EN). Маркетинговые «Quick start» + «🌍 Getting Started» проходы остаются только на английском. |
Удалить унаследованный alias /api/scan-ru/config |
Снятие запланировано на v1.20. Канонический /api/scan/regional/config — целевая миграция. |
[1.18.0] — 2026-05-13
Консолидация эндпоинтов сканирования + проход WCAG 2.2 AA + завершение длинного хвоста i18n. Снимает с эксплуатации унаследованные алиасы /api/stream/scan-{en,ru} (окно Sunset 2026-10-01 перенесено на v1.18 по указанию пользователя). Доводит не-EN README до ~307 строк и переводит оставшиеся RU-телесные записи v1.16.0 + v1.17.0 в CHANGELOG для 6 локалей.
🚪 Несовместимые изменения
feat!(scan): retire legacy /api/stream/scan-{en,ru} aliases— устаревшие SSE-эндпоинты EN/RU удалены. Каждый потребитель идёт через консолидированный/api/stream/scan?source=ats|regional|both(живёт с v1.12.0). У унаследованных путей с v1.15.0 были заголовки Deprecation + Sunset (RFC 8594); окно миграции закрыто. Внешние интеграции на старых путях получают чистый 404 вместо тихой маршрутизации в catch-all SPA.
♿ Доступность (проход WCAG 2.2 AA)
- WCAG 2.4.1 Bypass Blocks — новая ссылка Skip to main content как первый фокусируемый элемент на каждой странице. Визуально скрыта через
.skip-linkдо получения фокуса, при Tab от загрузки страницы вытаскивается в верхний-левый угол. - WCAG 2.4.7 Focus Visible — глобальный стиль
*:focus-visible. Фокусные кольца на клик мыши выключены, на нажатие Tab — включены (стандартный паттерн WAI-ARIA AP). Кнопка закрытия модалки (×) получает кольцо фокуса повышенной контрастности. - WCAG 2.5.5 Target Size — минимум 44×44 px touch-target на
.skip-link..btn-smсохраняетmin-height: 32px(в сочетании с межстрочным интервалом удовлетворяет исключению AAA «24×24 + spacing» для компактных контролов строк таблиц). - WCAG 3.1.1 Language of Page —
<html lang="en">исправлен сlang="ru"(JS-бутстрап i18n уже переопределял это при загрузке, но SSR-дефолт теперь совпадает с дефолтной локалью SPA). - WCAG 1.3.1 Info & Relationships —
#contentполучаетtabindex="-1", чтобы цель skip-link фокусировалась чисто. (Роли ARIA + ловушка фокуса были добавлены ещё в v1.17.)
📚 Длинный хвост i18n
docs(i18n): v1.16.0 + v1.17.0 CHANGELOG translated in 6 locales— записи, ранее остававшиеся с русским телом вCHANGELOG.{es,pt-BR,ko-KR,ja,zh-CN,zh-TW}.md, теперь на родном языке. Счётчик кириллических символов по локали упал 79 → 42 → 23 (оставшиеся 23 — технические inline-ссылки вроде путей к файлам и заголовок мультиязычных ссылок, намеренно).docs(readme): expand non-EN READMEs with Why / Requirements / Features / Configuration / Contributing— каждый не-EN README вырос с 240 → ~307 строк. Теперь покрывает те же немаркетинговые разделы, что и 585-строчный EN. Полный паритет 1:1 (маркетинговые проходы) пока отложен.
🛠️ Прочее
docs(api): consolidated scan endpoint in API.md + DATA-FLOWS.md + README.md— таблица API-справки теперь перечисляет только/api/stream/scan?source=…. Раздел Scan в README объясняет удаление разделения EN/RU в v1.18.0.fix(scan.js): drop stale comment about deprecated aliases being live— комментарий диспетчераrunScanAllв SPA теперь отражает консолидированную реальность.
🧪 Тесты
tests/scan-consolidated.test.mjs::F-018 backwards compatпереписан — два прежних утверждения «унаследованный эндпоинт ещё работает» теперь проверяют, что запросы к/api/stream/scan-{en,ru}возвращают 404 (а не маршрутизируются в catch-all SPA).- Итого: 427 / 427 unit + 20/20 smoke E2E + 23/23 comprehensive E2E + 32/32 Playwright (счётчик не изменился; +2 новых корректных утверждения о удалении legacy заменяют +2 утверждений о «legacy ещё работает»).
Верификация
npm test # 427 / 427
npm run test:e2e:full # 23 / 23
# Снятие legacy-эндпоинтов:
curl -sI http://127.0.0.1:4317/api/stream/scan-en | head -1 # → HTTP/1.1 404
curl -sI http://127.0.0.1:4317/api/stream/scan-ru | head -1 # → HTTP/1.1 404
# Консолидированный эндпоинт:
curl -sN 'http://127.0.0.1:4317/api/stream/scan?source=ats&dryRun=1' | head -5
# → event: start
# → data: {"script":"en-scanner","writeFiles":false,…}
# Skip-link (a11y):
curl -s http://127.0.0.1:4317/ | grep -c 'class="skip-link"' # → 1
# Запасной html lang:
curl -s http://127.0.0.1:4317/ | grep -c 'html lang="en"' # → 1
Вне рамок (v1.19+)
| Пункт | Примечания |
|---|---|
| Полный паритет не-EN README (585 строк как у EN) | v1.18 поднял не-EN до ~307 (53 % от EN). Маркетинговые «Why?» / «Quick start» проходы остаются только на английском. |
| Аудит цветового контраста (WCAG 1.4.3 AA — текст 4.5:1, крупный текст 3:1) | v1.18 закрыл структурную a11y; токенный контраст в светлой и тёмной палитрах остаётся к проверке. |
| Аудит touch-target по каждому интерактивному элементу | v1.18 установил пол (.btn: 44 px, .btn-sm: 32 px); покомпонентная проверка (chip-фильтры, навигация сайдбара, sortable headers) остаётся. |
[1.17.0] — 2026-05-13
Релиз полировки + a11y + исправления CI. Закрывает все 9 follow-up из списка
v1.16.0: проверка smoke в браузере, правда бейджей README,
обновление покрытия, проявление lastWorkdayFallback в SPA, полный пересчёт E2E,
сценарии Playwright для auto-pipeline, проход аудита a11y,
сжатие исторических CHANGELOG в 6 локалях и расширение
не-EN README разделами Architecture / API / Security / Tests.
🐛 Исправления
fix(e2e): smoke + comprehensive suites re-aligned with v1.16 UX— изменение v1.16 Cmd+K Enter → AutoPipeline modal приводило к тому, чтоsearch.press('Enter')в e2e-тестах открывал модалку, перехватывавшую последующие клики. Тесты теперь используютShift+Enterдля устаревшего пути быстрого добавления, что соответствует документированному в v1.16 разделению. Также обновляет итерацию batch-режима в comprehensive E2E для использования/#/batch-prompt(устаревший slug, введённый PR-H в v1.15). Это был провал CI на push v1.16.0 — Playwright e2e таймаутил 30 с на ожидании кликов, перехваченных backdrop.fix(mode-page): batch-prompt route → modes/batch.md via serverSlug— v1.15 переименовала legacy mode slug вbatch-prompt, но серверныйPOST /api/mode/:slugпосле этого искалmodes/batch-prompt.md, которого не существует. Новое полеserverSlugразводит хеш маршрута и имя файла режима родителя.chore: bump deprecation messages from v1.16.0 to v1.17.0— тексты deprecation для scan-en/scan-ru и баннер deprecation batch-prompt ссылались на прошлую версию.
✨ Возможности
feat(scan): 🔒 Workday CAPTCHA chip in Active Companies card— серверный экспортlastWorkdayFallbackиз PR-7 v1.16 теперь потребляется SPA./api/scan-resultsвозвращает снапшот;#/scanрендерит warn-карточку над Active Companies, когда тенант Workday сорвался в fallback («🔒 Workday tenant blocked — fallback: use /career-ops scan (Playwright)»). Новый экспортерgetLastWorkdayFallback()исключает неоднозначность live-binding ESM. 2 новых ключа i18n × 8 локалей.
♿ Доступность
a11y: ARIA roles + focus management pass on critical surfaces—index.html: атрибутыroleна<aside>(navigation),<header>(banner),<section id="content">(main),<div id="modal">(dialog с aria-modal/aria-labelledby),<div id="toast">+#conn-banner(status с aria-live),<div class="searchbar">(search).#sidebar-toggleполучаетaria-controls="sidebar"+aria-expanded, синхронизируемый JS при open/close.#global-searchполучает визуально-скрытую<label>и явныйaria-label, проявляющий подсказку шортката Cmd+K.- Закрытие модалки (×) получает
aria-label="Close dialog". - Декоративные backdrop получают
aria-hidden="true". - Ловушка фокуса в модалке —
UI.modal()запоминает владельца клика, фокусирует первый не-close фокусируемый элемент при открытии и циклит Tab/Shift+Tab внутри модалки.UI.closeModal()возвращает фокус предыдущему владельцу. - Новый утилитарный класс
.visually-hiddenвpublic/css/app.css(стандартный паттерн WAI-ARIA AP).
📚 Документация
docs(readme): badge truth across 8 READMEs— бейдж тестов284 / 379 / 360→ 427; бейдж релизаv1.9.1 / v1.13.0→ v1.16.0, затем → v1.17.0 через v1.17 bump. Цели ссылок релиза обновлены.docs(readme): expand 7 non-EN READMEs with reference sections— каждый вырос с 170 → ~240 строк за счёт новых разделов Architecture / API reference / Security notes / Tests / A11y / Limitations / License на родном языке. Пока не на полном паритете 585 строк с EN, но покрывает все ключевые немаркетинговые поверхности.docs(changelog): condense pre-v1.12 entries in 6 locales— длинные RU-телесные записи v1.11.x + v1.10.x, протекавшие в не-EN/не-RU CHANGELOG, заменены на компактную сводку «Earlier releases» на родном языке каждой локали. Детальная история остаётся вCHANGELOG.md(EN).
🛠️ Инструментарий
coverage: refresh numbers— последняя опубликованная цифра — 95.46 % line / 84.06 % branch (REVIEW v1.13.0). Baseline v1.17: 94.14 % line / 82.98 % branch / 93.20 % function. Лёгкое снижение от новых путей ошибок в auto-pipeline + reports-write; по-прежнему значительно выше пола 80 % из CLAUDE.md.
🧪 Тесты
- Итого: 427 / 427 unit + 20/20 smoke E2E + 23/23 comprehensive
E2E + 32 / 32 Playwright (было 28; +4 новых сценария auto-pipeline:
кнопка открывает модалку, Cmd+K paste запускает модалку,
невалидный URL блокирует шаг 1, обрамление SSE-событий
POST /api/auto-pipeline). - E2E-сет перенастроен под UX v1.16.0 (Shift+Enter quick-add, /#/batch-prompt для устаревшего режима).
Верификация
# Локально:
npm test # 427 / 427
npm run test:e2e # 20 / 20
npm run test:e2e:full # 23 / 23
npm run test:e2e:browser # 32 / 32
# Smoke в браузере (на уровне страницы):
curl -s http://127.0.0.1:4317/api/scan-results | jq '.workdayFallback'
# null, когда fallback Workday не происходил; {apiUrl, reason, at} после 4xx.
# Точечная проверка a11y:
node -e "
const c = require('cheerio').load(require('fs').readFileSync('public/index.html','utf8'));
['banner','navigation','main','dialog','status','search'].forEach(r =>
console.log(r, c('[role=' + r + ']').length));
"
# Каждая роль должна появляться ≥1.
# Проверка шлюза CI: workflow dashboard-screenshots поднимает /tmp
# скаффолд, регенерирует PNG, сравнивает с закоммиченными — зелёный, когда
# images/dashboard-*.png актуальны для отрендеренного SPA.
Вне рамок (v1.18+)
| Пункт | Примечания |
|---|---|
| Перевести запись v1.16.0 в не-EN CHANGELOG | Сейчас с русским телом (~30 строк × 6 локалей = 180 строк). Было вне явных рамок пользователя для v1.11.x/v1.10.x. |
| Полный паритет не-EN README (585 строк как у EN) | v1.17 поднял не-EN до ~240; маркетинговые «Why?» / «Quick start» проходы остаются только на английском. |
| Родительский коммит для канонического промпта A-F | Переписывание santifer/career-ops::modes/oferta.md всё ещё требуется upstream (жёсткое правило CLAUDE.md #1). |
| Полный аудит WCAG 2.2 AA | v1.17 покрыл структурный ARIA + ловушку фокуса; покомпонентный контраст / порядок Tab остаётся к проверке. |
career-ops-ui v1.267.1 · page 11/13 · mirror of the project changelog for cvstart.org