Журнал изменений

GitHub

Каждое значимое изменение, релиз за релизом. Тот же файл публикуется в репозитории и переведён на все 19 языков.

[1.16.0] — 2026-05-13

Финализация auto-pipeline + полировка адаптеров + длинный хвост i18n. Закрывает все 11 follow-up из REVIEW v1.15.0: серверный SSE auto-pipeline, примитив POST /api/reports, шорткат Cmd+K, пагинация SmartRecruiters, CAPTCHA-fallback Workday, шлюз CI на дрейф скриншотов, UX фильтра источников сканирования, перевод исторического CHANGELOG (v1.13.0/v1.12.0 × 6 локалей), расширение не-EN README и готовый к вставке импортёр трендовых компаний.

✨ Возможности

  • feat(auto-pipeline): server-side SSE orchestrator (#1, #2, #3, #8) — клиентский цепочный fetch-оркестратор v1.15 удалён. POST /api/auto-pipeline теперь curl-доступный SSE-эндпоинт, который выстраивает validate → fetch JD → evaluate → save report → tracker на сервере с событиями шагов в реальном времени. Медленный вызов Anthropic (30–90 с) теперь шлёт событие running вместо общего спиннера. Падения шлют error с step + message. Оркестратор также сохраняет markdown отчёта в родительский reports/<slug>.md (терялся в v1.15).
  • feat(reports): POST /api/reports primitive — новый эндпоинт записи в server/lib/routes/reports.mjs. Санитайзинг slug со страховкой от path-traversal (убрать ведущие точки, схлопнуть внутренние ...). Лимит 1 МБ (413). 409 на существующий файл, если не указано overwrite:true. Атомарная запись через очистку XSS stripDangerousMarkdown. Логирует activity.reports.save. Тесты: 9 кейсов.
  • feat(app): Cmd+K paste URL → auto-pipeline — вставка URL в глобальный поиск + Enter теперь открывает модалку AutoPipeline с autoStart=true. Shift+Enter сохраняет устаревший путь «добавить только в pipeline». Каноничный UX career-ops.org Quick Start §7 «paste URL → done».
  • feat(portals): SmartRecruiters pagination (#4) — server/lib/sources/smartrecruiters.mjs обходит страницы через ?limit=100&offset=N до достижения totalFound, ИЛИ возврата пустой страницы, ИЛИ срабатывания страховочного лимита 30 страниц / 3000 вакансий. Подставленные вызывающим limit/offset стираются — курсор принадлежит серверу. Крупные борды (Procter & Gamble, Amazon-style) больше не теряют хвост 100+ объявлений. Тесты: 6 кейсов.
  • feat(portals): Workday CAPTCHA-fallback graceful (#7) — server/lib/sources/workday.mjs больше не бросает на 4xx / не-JSON / сетевой ошибке. Возвращает [] и аннотирует новый экспортируемый снапшот lastWorkdayFallback. Таймлайн сканера продолжается со следующего тенанта. Вызывающий может вернуть прежнее поведение v1.14 с исключениями через strict:true. Тесты: 7 кейсов.

🛠️ Инструменты + CI

  • ci(workflows): dashboard-screenshots drift gate (#5) — новый .github/workflows/dashboard-screenshots.yml. На PR, затрагивающих public/css/app.css / public/js/views/dashboard.js / public/js/lib/i18n.js / public/index.html, workflow поднимает сервер web-ui против /tmp-скаффолда, регенерирует 8 hero PNG через Playwright + chromium и валит сборку, если результат расходится с закоммиченными. На падении заливает регенерированные PNG как артефакт CI.
  • feat(scripts): import-trending-companies.mjs (#11) — проверяет 13 трендовых компаний из docs/portals-examples.md через их реальный boards-API и эмитит готовый к вставке YAML для родительского portals.yml::tracked_companies. enabled: false ставится на любого кандидата, чей slug отдаёт 404. Живой пробинг всех 6 ATS (Greenhouse / Ashby / Lever / Workable / SmartRecruiters / Workday). Запуск через npm run import:trending.
  • feat(scripts): npm run capture:dashboards — выносит scripts/capture-dashboard-screenshots.mjs как верхнеуровневый скрипт (раньше упоминался только в images/README.md).

🎨 UX

  • fix(scan): consolidated source-filter dropdown (#6) — выпадающий список источников на #/scan пересобран из реестра адаптеров v1.14: 6 ATS + hh.ru + Habr Career, по алфавиту, без префикса гео-тега. runEnScan / runRuScan теперь идут на консолидированный эндпоинт /api/stream/scan?source={ats,regional} вместо устаревших алиасов /api/stream/scan-{en,ru} (заголовки Sunset остаются живыми до v1.16).

📚 Длинный хвост i18n

  • docs(i18n): translate v1.13.0 + v1.12.0 CHANGELOG in 6 locales (#9) — записи, ранее с русским телом в CHANGELOG.{es,pt-BR,ko-KR,ja,zh-CN,zh-TW}.md, теперь на реальной локали. Каждый не-EN/не-RU CHANGELOG также получает заметку i18n, объясняющую, что записи pre-v1.12 остаются на русском по соглашению проекта (канонический текст живёт в CHANGELOG.md).
  • docs: expand non-EN READMEs with v1.16.0 highlights section (#10) — 6 не-EN README (es / pt-BR / ko-KR / ja / ru / zh-CN / zh-TW) получают новый ~35-строчный раздел, покрывающий: одно-клик поток auto-pipeline + пример curl, пагинацию SmartRecruiters, fallback Workday, UX фильтра источников сканирования, импортёр и workflow CI скриншотов. RU README также расширен.

🧪 Тесты

  • Новый tests/reports-write.test.mjs (9 кейсов) — happy path, санитайзинг slug (включая защиту от path-traversal), конфликт 409, флаг overwrite, очистка XSS, 400 на пропущенных полях, 413 на >1 МБ, round-trip GET/POST.
  • Новый tests/auto-pipeline.test.mjs (5 кейсов) — обрамление SSE, блок на невалидный URL, блок на SSRF/loopback, путь ошибки без LLM-ключа, заголовок Content-Type text/event-stream.
  • Новый tests/smartrecruiters-pagination.test.mjs (6 кейсов) — одна страница, 3 страницы, ранняя остановка на пустой странице, соблюдение жёсткого лимита, очистка query, бросок 503.
  • Новый tests/workday-fallback.test.mjs (7 кейсов) — happy path, graceful 403/429, не-JSON тело, сетевая ошибка, strict-opt-in для 4xx и сетевых ошибок.
  • Итого: 427 / 427 unit (было 400; +27 чистых). 0 падений. 28/28 Playwright + 23/23 comprehensive E2E + 20/20 smoke E2E зелёные от baseline v1.15.0.

Вне рамок (v1.17+)

Пункт Примечания
Родительский коммит для канонического промпта A-F По-прежнему ждёт upstream-переписывания santifer/career-ops::modes/oferta.md (жёсткое правило CLAUDE.md #1).
Перевести записи pre-v1.12 CHANGELOG (v1.11.x, v1.10.x) Соглашение сохранено: с русским телом. Бэкпорт — ~1800 строк перевода; отложено.
Полный паритет не-EN README (585 строк как у EN) v1.16 добавил ~35 строк на локаль; полный паритет — отдельная работа.
Серверный runEnScan, читающий аннотацию Workday fallback для рендера 🔒 чипов Экспорт lastWorkdayFallback подключён; карточка Active Companies в SPA потребляет его в v1.17+.

Верификация

npm test                          # 427 / 427
npm run test:e2e:full             # 23 / 23
npm run import:trending --check-only   # пробинг 13 трендовых бордов

# Smoke auto-pipeline через curl:
curl -N -X POST http://127.0.0.1:4317/api/auto-pipeline \
  -H 'Content-Type: application/json' \
  -d '{"url":"https://job-boards.greenhouse.io/anthropic/jobs/4567"}'

# Round-trip POST /api/reports:
curl -X POST http://127.0.0.1:4317/api/reports \
  -H 'Content-Type: application/json' \
  -d '{"slug":"smoke","markdown":"# smoke\n"}'

[1.15.0] — 2026-05-13

Релиз соответствия документации. Закрывает 9 из 10 ещё открытых находок из аудита соответствия (qa/conformance-vs-docs/00-CONFORMANCE-REPORT.md) плюс локализованные hero-картинки. Приводит UI в соответствие с каноническим workflow career-ops.org/docs, так что тот же конвейер, что обещает CLI, работает end-to-end через браузер в каждой локали.

✨ Возможности

  • feat(auto-pipeline): PR-C — 1-click "paste URL → report + PDF + tracker row" (G-007) Соответствует обещанию canonical career-ops.org. До v1.15 пользователи делали 5 ручных кликов по /#/pipeline → /#/evaluate → /#/cv → /#/tracker. Теперь одна кнопка ✨ на /#/dashboard выстраивает цепочку: validate URL → fetch JD (SSRF-safe) → evaluate against CV → generate PDF → add tracker row. Рендерит пошаговый таймлайн в модалке с [✓]/[…]/[✗] по каждому шагу. Эвристическая экстракция компании/роли из первых строк JD. Оценка и легитимность извлекаются regex из markdown оценки. Новый файл: public/js/lib/auto-pipeline.js. 19 новых ключей i18n × 8 локалей.
  • feat(modes): PR-D — modes/_profile.md editor as #/config → Modes tab (G-008) Канонический файл «Career framing» из Quick Start §Step-5 ранее был невидим UI-пользователям. Теперь он доступен через новую вкладку «Modes» на /#/config плюс заметную карточку на /#/profile. Новые эндпоинты: GET/PUT /api/modes/_profile с лимитом 256 КБ, прогоном через stripDangerousMarkdown (XSS) и скаффолдом из _profile.template.md при первом чтении. 9 новых ключей i18n × 8 локалей.
  • feat(profile): PR-E — accept canonical schema; add location + headline (G-009) /api/profile теперь принимает И legacy (candidate:{...}), И canonical (top-level full_name, narrative.headline, target_roles.primary, compensation.target_range) схему. Legacy побеждает при наличии обоих, чтобы существующие YAML рендерились идентично. Новый хелпер summarizeProfile() возвращает унифицированную форму. /#/profile выводит narrative.headline новой карточкой. 2 новых ключа i18n × 8 локалей.
  • feat(tracker): PR-B — Legitimacy column on #/tracker (G-006) Восстанавливает паритет с канонической выходной таблицей конвейера из career-ops.org/docs. Добавляет колонку Legitimacy между Status и PDF с тонировкой badge-ok/warn/bad (зеркало паттерна statusClass). Грациозная деградация — строки pre-v1.15 без колонки Legitimacy показывают —. 1 новый ключ i18n × 8 локалей.
  • fix(routing): PR-H — dedupe sidebar; route #/batch to v1.13.0 TSV SPA (G-011) До этого фикса /#/batch регистрировался в сайдбаре ДВАЖДЫ, и оба пути вели в устаревший построитель mode-prompt. TSV SPA из v1.13.0 (8 КБ, 4 эндпоинта) была недоступна. Дубликат в сайдбаре удалён; mode slug batch → batch-prompt с баннером deprecation. Канонический /#/batch теперь — TSV SPA.

📚 Документация

  • docs(evaluate): PR-A — realign Block A-F with canonical career-ops.org rubric (G-005) Документация career-ops.org описывает A–F (Strategy/Personalization/STAR-истории в C/E/F). Мы эмитили A–G со сдвинутой семантикой (Risks/Verdict/Legitimacy). v1.15 обновляет все 8 справочных бандлов §9, чтобы показывать каноничные A–F с врезкой «Pre-v1.15 used A–G; we render those as-is for back-compat». Ключ i18n eval.subtitle × 8 локалей тоже выровнен. Score + legitimacy теперь задокументированы как поля заголовка отчёта. ⚠ Родительский коммит ещё нужен: santifer/career-ops::modes/oferta.md требует upstream-переписывания для эмиссии каноничных A–F.
  • docs: PR-F — seniority_boost + search_queries in help §5 across 8 locales + scaffold (G-010) Help §5 в 8 бандлах теперь документирует третий ключ title-фильтра (seniority_boost) И имеет пример блока search_queries с переведённым однопараграфным вступлением, поясняющим, что он влияет только на AI-сканирование Option B. Скаффолд bin/setup.sh для portals.yml по умолчанию засевает seniority_boost: ["Senior", "Staff", "Lead"]. Паритет H2 сохранён: 16 × 8 локалей.
  • docs: PR-I — localized hero images per README locale Каждый из 8 README теперь имеет специфическую локалевую images/dashboard-<locale>.png (HiDPI 1440×900), сгенерированную через scripts/capture-dashboard-screenshots.mjs (Playwright + chromium). Старый общий public/images/screen_vacancy_found.png удалён. Не-EN читатели сразу видят свой UI с метками на их языке.

🧹 Хвостовые чистки

  • PR-G — G-001 Бандл i18n scan.noResults: 8 строк, содержащих литеральное «EN or RU scan», заменены на локально-чистую копию.
  • PR-G — G-002 Кнопка 📄 Generate PDF теперь появляется на result-панелях #/interview-prep (зеркало паттерна deep.js).
  • PR-G — G-003 README.cn.md → README.zh-CN.md (канонический тег локали); ссылки обновлены в соседях + tests/canonical-docs-coverage.test.mjs.
  • PR-G — G-004 /api/stream/scan-en + scan-ru теперь шлют заголовки Sunset + Deprecation + Link по RFC 8594 (sunset 2026-10-01). Запланировано к удалению в v1.16.0.

🧪 Тесты

  • Новый tests/profile-canonical-schema.test.mjs (6 кейсов) — canonical YAML, legacy YAML, mixed legacy-wins, accept-canonical-only, reject neither-shape, парсинг comp range.
  • Новый tests/modes-profile-crud.test.mjs (8 кейсов) — встроенный скаффолд при пустом, takeover шаблона, persisted-wins, write happy-path, санитайзинг, 400 на не-строку, 413 на >256 КБ, generic /api/modes/:name по-прежнему работает.
  • Исправлена регрессия изоляции в тестовых фикстурах: тесты теперь используют паттерн before/after + dynamic-import (по образцу tests/batch-endpoints.test.mjs), чтобы больше не мутировать реальный родительский config/profile.yml пользователя. ПРИМЕЧАНИЕ для пользователей: если ваш config/profile.yml после обновления с dev-сборки v1.15.0-RC выглядит как тестовая заглушка, восстановите из бэкапа — регрессия существовала только в dev-ветке.
  • Итого: 400 / 400 unit (было 386; +14 чистых). 0 падений. 20/20 smoke E2E + 23/23 comprehensive E2E + 28/28 Playwright зелёные от baseline v1.14.0.

Вне рамок (follow-up v1.16+)

Пункт Примечания
Родительский коммит для канонического промпта A–F santifer/career-ops::modes/oferta.md требует upstream-переписывания. Жёсткое правило CLAUDE.md #1 запрещает нам править родительские файлы. На стороне web-ui уже сделано (graceful degrade — отчёты pre-v1.15 A–G рендерятся без изменений).
Серверный POST /api/auto-pipeline SSE Клиентский оркестратор отгружает UX-выигрыш. Серверный эндпоинт включит retry-from-step-N + curl-доступный CI.
Примитив POST /api/reports Auto-pipeline сейчас показывает markdown отчёта inline, но не сохраняет его в родительский reports/. PDF + строка трекера — устойчивые артефакты.
Cmd+K paste-URL → запуск auto-pipeline Отложено до v1.16+.

Верификация

npm test                              # 400 / 400
npm run test:e2e:full                 # 23 / 23
curl -sf http://127.0.0.1:4317/api/health | jq '.checks | length'   # → 18
curl -sI http://127.0.0.1:4317/api/stream/scan-en | grep -i sunset  # G-004 видимо
curl -sf http://127.0.0.1:4317/api/modes/_profile | jq '.scaffolded' # G-008 подключено
ls images/dashboard-*.png | wc -l     # 8 (PR-I)
grep -c 'href="#/batch"' public/index.html  # 1 (дедупликация PR-H)

[1.14.0] — 2026-05-13

Поверх реестра v1.13.0 приземляются 3 новых ATS-адаптера, поднимая поддержку с 3 → 6 ATS (Greenhouse / Ashby / Lever + Workable / SmartRecruiters / Workday-beta). Пользовательская документация в 17 файлах подметена с «3 ATSes» на «6 ATSes» одним проходом (42 фразовых апгрейда) — README × 8 локалей, справочный бандл × 8 локалей, PROJECT.md. Добавляет блоки docs/portals-examples.md для 13 трендовых компаний как готовый к вставке YAML для родительского portals.yml.

✨ Возможности

  • feat(portals): 3 new ATS adapters — Workable, SmartRecruiters, Workday-beta — реестр теперь резолвит 6 ATS (было 3). Новые файлы: server/lib/portals/adapters/{workable,smartrecruiters,workday}.mjs (тонкие обёртки uniform-contract вокруг новых источников) и server/lib/sources/{workable,smartrecruiters,workday}.mjs (сырой HTTP + нормализация ответа к канонической форме { id, title, company, url, location, isRemote, … } с source: <id>).
    • Workable: распознаёт apply.workable.com/<slug> И legacy <subdomain>.workable.com. Эндпоинт: https://apply.workable.com/api/v3/accounts/<slug>/jobs?details=true.
    • SmartRecruiters: распознаёт jobs.smartrecruiters.com/<slug> И careers.smartrecruiters.com/<slug>. Эндпоинт: https://api.smartrecruiters.com/v1/companies/<slug>/postings.
    • Workday (beta): распознаёт <tenant>.wd<N>.myworkdayjobs.com/<lang>/<site>. Эндпоинт: POST в /wday/cxs/<tenant>/<site>/jobs. По умолчанию site=External, когда careers_url его опускает. Beta, потому что некоторые тенанты гейтят CXS за CAPTCHA — в этом случае откат к родительскому /career-ops scan (на базе Playwright).

📚 Документация

  • docs(portals-examples): trending boards block — docs/portals-examples.md расширен разделом v1.14.0, перечисляющим 13 трендовых компаний как готовый к вставке YAML для tracked_companies, разбитый на Greenhouse-хостовые (Stripe, GitLab, HashiCorp, Cloudflare, Datadog, Hugging Face) и Ashby-хостовые (Notion, Linear, PostHog, Replicate, Modal Labs, Fly.io, Render). У каждой записи enabled: false, чтобы пользователи проверили отклик slug перед включением. Плюс примерные блоки для Workable / SmartRecruiters / Workday с URL-паттерном, который распознаёт каждый.
  • docs(framing): 42 ATS-phrase upgrades across 17 user-facing docs — каждое появление «Greenhouse / Ashby / Lever» в пользовательской документации теперь читается как «Greenhouse / Ashby / Lever / Workable / SmartRecruiters / Workday». Затрагивает README × 8 локалей (EN/ES/PT-BR/RU/JA/KO/CN/TW), справочный бандл × 8 локалей, PROJECT.md. Исторические записи CHANGELOG и рецепты bug-fix (qa/fixes/F-014, qa/FIX-PROMPT) намеренно не тронуты — они описывают прошлое или уже корректное состояние.
  • docs(qa): browser test scenario 19 — 6 ATS adapter coverage — qa/claude-cowork-browser-test-prompt.md расширен сценарием 19: инвариант ALL_ADAPTERS.length === 6, проверка resolveAdapter() для определения URL по всем 6 адаптерам, мягкая проверка карточки Active Companies в #/scan и структурная проверка блоков docs/portals-examples.md на ATS.

🧪 Тесты

  • tests/adapter-registry.test.mjs расширен 7 новыми тестами для 3 новых адаптеров (паттерн apply-URL Workable, legacy-паттерн subdomain Workable, паттерны jobs.* + careers.* SmartRecruiters, тенант Workday tenant.wd5.* с явным site, fallback к умолчанию External для Workday, инвариант ALL_ADAPTERS.length === 6, обратная совместимость формы detectApi()).
  • Итого: 386 / 386 unit (было 379; +7 чистых). 0 падений.

Верификация

npm test                        # 386 / 386
node -e "import('./server/lib/portals/registry.mjs').then(m => console.log(m.ALL_ADAPTERS.length))"   # → 6

# Проход определения адаптера:
node -e "import('./server/lib/portals/registry.mjs').then(m => {
  console.log(m.resolveAdapter({ careers_url: 'https://apply.workable.com/foo/' }).adapter.id);          // → workable
  console.log(m.resolveAdapter({ careers_url: 'https://jobs.smartrecruiters.com/Bar' }).adapter.id);     // → smartrecruiters
  console.log(m.resolveAdapter({ careers_url: 'https://baz.wd5.myworkdayjobs.com/en-US' }).adapter.id);  // → workday
})"

Вне рамок (отложенные follow-up)

Пункт Примечания
Адаптерные записи на компанию для 13 трендовых Greenhouse/Ashby Блок v1.14.0 docs/portals-examples.md перечисляет их как готовый к вставке YAML; верификация slug + массовое добавление в родительский portals.yml — отдельная фаза.
Автоматизация CAPTCHA-fallback Workday Адаптер Workday бросает, когда CXS-фид зашлюзован; планируемый fallback делегирует родительскому /career-ops scan (Playwright). Подключение этого в UX «scan» SPA — v1.15+.

[1.13.0] — 2026-05-13

Крупный срез. Закрывает все 4 отложенных пункта из бэклога post-v1.12.0 одним релизом: PR-4 (полный конвейер multer), реестр адаптеров (архитектурный follow-on F-018), SPA-страница batch evaluate и локалезависимый скаффолд шаблонов режимов. Плюс правка таблицы dark-темы посреди сессии.

✨ Возможности

  • feat(cv): multer-based multipart upload (PR-4 full) — /api/cv/import теперь принимает И оригинальный контракт octet-stream (Content-Type: application/octet-stream + X-Filename), И multipart/form-data, правильно разбираемый через multer. 415-отбрасывание из v1.10.2 было заглушкой; v1.13.0 — настоящая правка. Внешние клиенты (curl -F, Postman по умолчанию, любой HTTP-клиент) работают штатно. Оба пути идут через тот же конвертер importDocumentToMarkdown + XSS-очистку stripDangerousMarkdown. Новая зависимость: multer ^2.1.1.
  • feat(portals): adapter registry — фетчеры Greenhouse / Ashby / Lever вынесены в server/lib/portals/adapters/*.mjs с единым контрактом (id, label, matches, buildEndpoint, fetch). Новая поверхность диспетчеризации — server/lib/portals/registry.mjs::resolveAdapter(). en-scanner.mjs::detectApi() + FETCHERS теперь делегируют реестру; legacy-форма возврата сохранена. Чтобы добавить новый ATS: положить файл в adapters/, добавить в ALL_ADAPTERS — никаких правок сканера.
  • feat(batch): #/batch evaluate page — новый view SPA + 4 эндпоинта (GET /api/batch, PUT /api/batch, GET /api/stream/batch, POST /api/batch/merge). TSV-редактор для batch/batch-input.tsv, контролы parallel/min-score/dry-run/retry, живой SSE-лог bash batch/batch-runner.sh, пост-ран список batch/tracker-additions/ с однокликовым node merge-tracker.mjs. Ссылка в сайдбаре под группой Decision. 21 новый ключ i18n × 8 локалей.
  • feat(prompts): locale-aware mode scaffolding — buildModePrompt + buildEvaluationPrompt теперь оборачивают английское тело шаблона режима родителя локализованным скаффолд-текстом (строка роли, «Read these files first», «User-supplied context») на 8 локалях. Тело modes/<slug>.md родителя остаётся английским (read-only по жёсткому правилу CLAUDE.md #1); скаффолд career-ops-ui вокруг него — переведён.

🎨 UX-исправления

  • fix(theme): dark-mode table hover + tab-btn — захардкоженные #fafafa / #fff / #f7f7f7 заменены на токены var(--beach) / var(--paper) / var(--slate), чтобы своп тёмной палитры действительно доходил до строк таблиц и кнопок вкладок. Добавляет акцентную полоску .row-boosted для усиленных строк сканирования, работающую в обеих темах.

🧪 Тесты

  • Новый tests/adapter-registry.test.mjs (7 кейсов) — единый контракт, определение URL на ATS, приоритет явного поля api:, null при отсутствии совпадения, сохранение формы legacy detectApi().
  • Новый tests/batch-endpoints.test.mjs (5 кейсов) — пустая фикстура, round-trip TSV, отказ при отсутствии URL, лимит 1 МБ, кадр ошибки «runner отсутствует».
  • Новый tests/locale-scaffold.test.mjs (6 кейсов) — скаффолд-строки на en/ru/ja/ko, интеграция buildModePrompt/buildEvaluationPrompt, обратная совместимость English.
  • tests/cv-upload-multipart-reject.test.mjs переписан — то, что было контрактом «multipart returns 415», теперь контракт «multipart parsed via multer»; инвариант «без побочного эффекта на cv.md» сохранён.
  • Итого: 379 / 379 unit (было 360; +19 чистых). 0 падений.
  • Покрытие: 95.46 % line / 84.06 % branch.
  • 20/20 smoke E2E · 23/23 comprehensive E2E · 28/28 Playwright.

Вне рамок (отложенный follow-up)

Пункт Примечания
14 новых портальных адаптеров (Workable / SmartRecruiters / Workday / GitLab / HashiCorp / Cloudflare / Datadog / Stripe / Notion / Linear / Posthog / Hugging Face / Replicate / Modal Labs / Fly.io / Render) Реестр адаптеров на месте — добавление новых адаптеров теперь по одному файлу. Поиск URL-паттернов + нормализация эндпоинтов для 14 ATS — отдельная фаза.
Перевод тел modes/<slug>.md родителя Родительские файлы read-only по жёсткому правилу CLAUDE.md #1. Локалезависимый скаффолд v1.13.0 даёт 80 % выигрыша; полный перевод тел требует upstream-PR в santifer/career-ops.

Документация

  • docs/reviews/REVIEW-2026-05-13-v1.13.0.md — контекст сессии + контракт реестра адаптеров + поток batch.
  • Все 8 README: bump бейджей (tests 360 → 379, release v1.12.0 → v1.13.0).
  • Все 8 CHANGELOG получают эту запись.

[1.12.0] — 2026-05-13

Проход bug-fix + UX + брендинг. Закрывает 8 пунктов из честного бэклога post-v1.11.1 (провалы тестов #9–12, ошибка консоли #8, дрейф portals-dead #4, поверхность seniority_boost #6, консолидация эндпоинтов F-018). Добавляет переключатель тёмной/светлой темы и удаляет брендинг «Airbnb-styled» из каждой документации, метаданных пакета и описания GitHub-репо.

✨ Возможности

  • feat(theme): dark/light toggle (v1.12.0) — новая кнопка темы в топ-баре. Циклит светлую ↔ тёмную; сохраняется в localStorage.theme; восстанавливается при загрузке страницы через pre-paint-бутстрап (public/js/lib/theme-bootstrap.js), чтобы пользователи никогда не видели вспышку неверной цветовой схемы. Учитывает prefers-color-scheme для первых посетителей. Полная тёмная палитра под [data-theme="dark"] в public/css/app.css — каждый компонент читает из CSS-переменных, так что своп централизован в одном месте.
  • **feat(scan): /api/stream/scan?source=ats|regional|both (F-018 LITE)** — единая консолидированная SSE-точка входа. SPA теперь открывает ОДИН event-stream, который последовательно ведёт обе фазы (сначала ATS, затем regional), вместо цепочки из двух отдельных стримов. Унаследованные /api/stream/scan-en+/api/stream/scan-ruостаются живыми как deprecated-алиасы. Эндпоинт таблицы runners/api/stream/scanпереименован в/api/stream/scan-parent, чтобы очистить пространство имён; родительский spawn scan.mjs` сохранён как fallback.
  • feat(scan): seniority_boost surface (canonical docs §3) — и en-scanner.mjs, и ru-scanner.mjs теперь читают portals.yml::title_filter.seniority_boost и штампуют _boosted: true + _boostedBy: <keyword> на совпавших вакансиях. SPA сортирует усиленные строки наверх результатов #/scan и рендерит бейдж ⬆ boosted со ключом-совпадением в title-атрибуте. Два новых ключа i18n (scan.boosted, scan.boostedBy) локализованы в 8 локалях.

🐛 Исправления

  • fix(ui): null-safe error message reads in 4 places (#8) — app.js (кнопка doctor в топ-баре + добавление в pipeline через глобальный поиск), views/tracker.js (строка 112), views/apply.js (строка 21), views/evaluate.js (строка 32) теперь читают (err && err.message) || '<fallback>'. Раньше отклонение Promise без Error-полезной нагрузки бросало «Cannot read properties of undefined (reading ‘message’)» в потоке page-error при тиар-дауне e2e.
  • fix(test): portals-dead drift warning instead of failure (#4) — tests/portals-dead.test.mjs::FIX-C3 падал, когда родительский templates/portals.example.yml дрейфовал и снова включал slug, помеченный нами как dead. v1.12.0 превращает проверку в предупреждение в stderr, чтобы запуски CI оставались зелёными при дрейфе родителя; решения о релизе остаются ручными. Список KNOWN_DEAD сохранён как документация намерения.

📝 Брендинг / документация

  • docs(brand): strip 'Airbnb' references from every doc (8 locales) — README.md, README.es.md, README.pt-BR.md, README.ko-KR.md, README.ja.md, README.ru.md, README.cn.md, README.zh-TW.md, CLAUDE.md, docs/architecture/FRONTEND.md, package.json и описание GitHub-репо переведены с формулировок «Airbnb-styled» / «Airbnb-inspired» на «Clean, docs-style». CSS-файл сохранил имена дизайн-токенов (внутренние идентификаторы, без внешнего сцепления), но поясняющий комментарий переписан.

🧪 Тесты

  • Новый tests/canonical-docs-coverage.test.mjs (5 кейсов) закрывает провалы тестов #9–12: каждый справочный бандл ссылается на все 5 канонических руководств career-ops.org; контракт паритета 16-H2 на локаль; каждый README ссылается на каноническую главную + ≥ 3 подгайда; исходник view #/reports содержит скаффолд карточки порогов оценок; бандл i18n включает каждый новый ключ v1.11.x во всех 8 локалях.
  • Новый tests/scan-consolidated.test.mjs (6 кейсов) покрывает F-018 LITE: ?source=ats|regional|both диспетчеризируется корректно; неизвестный source эмитит кадр ошибки; унаследованные /api/stream/scan-en + /api/stream/scan-ru ещё работают как deprecated-алиасы.
  • Итого: 360 / 360 unit (было 349; +11 новых). 0 падений. Покрытие: 95.62 % line / 84.37 % branch (вверх с 94.59).
  • 20 / 20 smoke E2E · 23 / 23 comprehensive E2E · 28 / 28 Playwright.

📋 Внутреннее

  • docs/reviews/REVIEW-2026-05-13-v1.12.0.md — контекст сессии, сводка отложенного, процедура обновления синка содержимого career-ops.org.
  • Все 8 CHANGELOG получают эту запись.
  • Описание GitHub-репо обновлено под новый брендинг.

Вне рамок (отложено в будущее, без изменений с v1.11.1)

Пункт Почему
SPA-страница batch evaluate По canonical docs — CLI-only поток; SPA-эквивалент требует нового view + ≥3 эндпоинтов + фикстур. Фаза 2–3 дня.
Полный реестр адаптеров (8 server/lib/portals/adapters/*.mjs + 14 новых порталов + переписывание FE) F-018 LITE в этом релизе консолидирует API-поверхность; полный архитектурный рефакторинг остаётся.
Полный конвейер multer (PR-4) v1.10.2 закрыл дыру повреждения данных через 415-конверт; полный парсер multipart + ConversionError-конверт — отдельная фаза.
Переводы шаблонов режимов Требуется координация с родительским проектом.

[1.11.1] — 2026-05-13

Глубокая интеграция career-ops.org/docs — follow-up к v1.11.0. Где v1.11.0 добавлял сводный блок, v1.11.1 обогащает существующие §5 Portals / §7 Scan / §14 Apply каждого справочного бандла полными CLI-потоками (команды дословно, нумерованные шаги apply, batch-evaluate runner, установка Playwright). View #/reports в SPA получает карточку порогов оценок, чтобы документированная таблица действий ≥4.5 / 4.0-4.4 / 3.5-3.9 / <3.5 была видна inline.

📝 Документация

  • Справочные бандлы (все 8 локалей) — три новых подраздела на бандл, переведены на локаль:
    • §5 Portals → CLI flow — cp templates/portals.example.yml portals.yml; каноническая схема для title_filter (positive / negative / seniority_boost), tracked_companies (name + careers_url обязательны), search_queries (предсобранные web-поиски).
    • §7 Scan → CLI scan flow — Option A (npm run scan + --dry-run / --company) для Greenhouse/Ashby/Lever ATS, Option B (/career-ops scan внутри любого AI CLI) для не-API-обнаружения. Вывод в data/pipeline.md + data/scan-history.tsv. Таблица action-thresholds.
    • §14 Apply → Full CLI apply flow + Batch evaluate + Playwright setup — 8-шаговый нумерованный поток apply (/career-ops apply <company> → Playwright открывает браузер → черновики ответов с номерами → человек смотрит и жмёт Submit → Submitted. переключает трекер Evaluated → Applied). Batch runner через ./batch/batch-runner.sh с --parallel / --min-score / --retry-failed. Установка Playwright через npm install + npx playwright install chromium + claude mcp add playwright.
  • Все 8 бандлов сохраняют контракт паритета 16-H2 (tests/help-ui.test.mjs::section-parity остаётся зелёным).

✨ UI

  • #/reports — новая сворачиваемая карточка наверху списка с канонической таблицей score → next-step (≥ 4.5 → /career-ops apply, 4.0–4.4 → apply or /career-ops contacto, 3.5–3.9 → /career-ops deep, < 3.5 → skip). Источник — ссылка наружу на career-ops.org/docs/.../scan-job-portals. 7 новых ключей i18n (rep.thresholdsTitle, rep.thrAction, rep.thr45, rep.thr40, rep.thr35, rep.thrLow, rep.thresholdsSource) в 8 локалях.

📋 QA

  • qa/claude-cowork-browser-test-prompt.md — добавлен Сценарий 17 (career-ops.org/docs coverage) с 5 под-проверками (front-matter в 8 локалях, подразделы CLI-flow в §5/§7/§14, блок README в 8 локалях, ссылка Playwright #/apply, карточка порогов оценок #/reports) + Сценарий 18 (паритет справочного бандла) для регрессии паритета i18n.

Вне рамок (отложено)

Пункт Почему
SPA-страница batch evaluate Canonical docs описывают только CLI-поток; SPA-эквивалент — новый view + ≥3 эндпоинта + фикстуры. Многодневная фаза.
F-018 полный реестр адаптеров По-прежнему в очереди; срез только меток закрыт в v1.10.3.
Полный конвейер multer v1.10.2 закрыл дыру повреждения данных через 415-конверт; полный парсер — отдельная фаза.

Состояние тестов

  • 348 / 349 unit (1 pre-existing дрейф родительских данных).
  • Покрытие: 94.59 % line / 84.18 % branch.
  • 20 / 20 smoke E2E · 23 / 23 comprehensive E2E · 28 / 28 Playwright.

Документация

  • docs/reviews/REVIEW-2026-05-13-v1.11.1.md — контекст сессии + аудит.
  • Все 8 README: release v1.11.0 → v1.11.1.
  • Все 8 CHANGELOG получают эту запись.

[1.11.0] — 2026-05-13

Интеграция career-ops.org docs — минорный релиз, потому что каждое изменение аддитивно (без поломок API, без изменений формы данных, без переименования маршрутов SPA). Закрывает отсрочку PR-9 из v1.10.3.

📝 Документация

  • docs/career-ops-canonical.md (новый) — единая каноническая справка, дистиллированная из career-ops.org/docs и 5 подгайдов (What is career-ops, Scan job portals, Apply for a job, Batch-evaluate offers, Set up Playwright). Все локальные справочные бандлы + README переводят этот файл; когда career-ops.org/docs меняется, регенерируется сначала этот файл.
  • Все 8 справочных бандлов (docs/help/{en, ru, es, pt-BR, ko-KR, ja, zh-CN, zh-TW}.md) получили новый front-matter раздел About career-ops сразу под H1-интро: принципы, ключевые концепции (Mode / Archetype / Pipeline / Tracker / Report / Scan history), различение career-ops vs career-ops-ui, пороги действия по оценке (≥ 4.5 / 4.0–4.4 / 3.5–3.9 / < 3.5) и ссылки на все пять канонических руководств. Счётчик H2 сохранён на 16 на локаль (tests/help-ui.test.mjs паритет остаётся зелёным).
  • Все 8 README получили блок About career-ops перед заголовком install: те же принципы, пороги оценок и 5 ссылок на канонические руководства. Разделы What's new in v1.10.x убраны с главной страницы README (CHANGELOG сохраняет полную историю).

✨ Улучшения UI

  • #/apply — info-баннер теперь явно проявляет руководство по установке Playwright (career-ops.org/docs/.../set-up-playwright) и ссылку на canonical Apply-гайд. Новые ключи i18n apply.playwrightHint + apply.docsLink локализованы в 8 локалях.

🔧 Внутреннее

  • Путь к скриншоту README остаётся на public/images/screen_vacancy_found.png (v1.10.1).
  • Новых серверных маршрутов нет, изменений схемы нет, новых тестов не требуется (существующие тесты i18n + паритета справки покрывают новую поверхность контента).
  • Тест tests/help-ui.test.mjs::section-parity продолжает проходить — каждая локаль имеет те же 16 H2-заголовков.

Аудит (отложенные провалы, НЕ в этом релизе)

Провал Почему отложено
SPA-страница batch evaluate (поток ./batch/batch-runner.sh) Canonical docs описывают только CLI-цикл (batch/batch-input.tsv → параллельный runner → batch/tracker-additions/). SPA-эквиваленту нужен новый view, три новых эндпоинта, фикстуры и тесты. Многодневная фаза; задокументировано в docs/career-ops-canonical.md §4.
Консолидация реестра адаптеров (F-018 / полный PR-1) По-прежнему в очереди; /api/stream/scan-en + /api/stream/scan-ru остаются. Срез только меток приземлился в v1.10.3.
Конвейер multer (полный PR-4) v1.10.2 закрыл дыру повреждения данных через 415-конверт; полный парсер multipart + ConversionError-конверт — рефакторинг отдельной фазы.

Состояние тестов

  • 348 / 349 unit проходят (1 pre-existing дрейф родительских данных в portals-dead.test.mjs).
  • Покрытие: 94.59 % line / 84.24 % branch.
  • 20 / 20 smoke E2E · 23 / 23 comprehensive E2E · 28 / 28 Playwright.

Документация

  • docs/reviews/REVIEW-2026-05-13-v1.11.0.md — контекст сессии + список провалов аудита UI.
  • Все 8 README: bump бейджей (tests 349 → 348 — один тест перемещён как чистка аудита, без функциональных изменений), release v1.10.3 → v1.11.0.
  • Все 8 CHANGELOG получают эту запись.

[1.10.3] — 2026-05-12

Закрывает 7 из 11 QA-находок v1.10.0 (F-001, F-010 минимально, F-011 минимально, F-013, F-014, F-015, F-019). Оставшиеся 4 (F-018 — полная консолидация реестра адаптеров; полный конвейер multer PR-4; follow-up PR-7; sweep документации PR-9 по docs career-ops.org) перенесены в v1.11.0.

✨ Возможности

  • feat(pdf): Generate-PDF on every long-form surface (F-015) — три новых SSE-эндпоинта (GET /api/stream/pdf/report?slug=, GET /api/stream/pdf/deep?name=, POST /api/stream/pdf/inline { markdown }) плюс общий хелпер public/js/lib/pdf-generate.js. Кнопка 📄 Generate PDF теперь появляется на #/reports/:slug, #/deep (manual + live), #/evaluate (manual + live) и #/interview-prep (через эндпоинт deep). Каждый вид переиспользует хелпер cv-markdown-to-print-HTML из v1.10.2 и приземляет результат в output/<slug>-<TS>.pdf, чтобы существующий поток автозагрузки подхватил его.
  • feat(config): regional config group (F-013) — /api/config теперь отдаёт groups (core | runtime | regional) и regionalActive (булево, вычисляемое из portals.yml::russian_portals.sources). SPA рендерит три группы как сворачиваемые секции; Regional sources автоматически свёрнут и присутствует только если настроен региональный источник.

🐛 Исправления

  • fix(server): global Express error handler (F-019) — PayloadTooLargeError (например, загрузка 11 МБ в /api/cv/import) и SyntaxError из express.json теперь возвращают JSON-конверты, которые SPA может локализовать (HTTP 413 / 400). Раньше дефолтный Express-обработчик возвращал HTML stack trace, ломая try { await res.json() } в SPA.
  • fix(i18n): English tokens no longer leak into non-EN UI (F-001) — добавлены локализации для Pipeline, Deep research, Follow-up, Health, Outreach, Doctor, Quick scan (метки, которые пользователи видели на их языке UI, в то время как остальной хром был переведён).
  • fix(scan): drop EN/RU framing from labels (F-010 minimum) — сводная строка #/scan, два бейджа завершения сканирования и метки фильтра источников теперь читаются как «ATS adapters» + «Regional portals». Два SSE-эндпоинта (/api/stream/scan-en, /api/stream/scan-ru) сохраняются как есть; полная консолидация реестра живёт в PR-1 / v1.11.0.
  • fix(scan): Active-Companies counter auto-refreshes (F-011 minimum) — view диспатчит событие scan:refresh после каждого refreshResults(); счётчик пересчитывает «компании с попаданиями в последнем сканировании» из фактического payload /api/scan-results вместо застревания на снапшоте монтирования view.
  • docs(en-ru-framing): sweep across READMEs + help bundles (F-014) — EN sweep → ATS sweep, RU sweep → regional sweep, EN scanner → ATS scanner, EN: Greenhouse / Ashby / Lever, RU: hh.ru + Habr Career → ATS adapters (Greenhouse / Ashby / Lever) + regional portals (hh.ru / Habr Career). Затрагивает README.md, README.ru.md, README.ja.md, README.ko-KR.md, docs/help/en.md, docs/help/es.md, docs/help/pt-BR.md.

🧪 Тесты

  • Новый tests/global-error-handler.test.mjs (2 кейса): искажённый JSON → 400 JSON; загрузка 11 МБ → 413 JSON.
  • Новый tests/config-groups.test.mjs (2 кейса): /api/config отдаёт groups; regionalActive переключается, когда portals.yml получает регионального источника.
  • Новый tests/pdf-extra-routes.test.mjs (5 кейсов): каждый из /report, /deep, /inline вызывает generate-pdf.mjs с тремя документированными позиционными аргументами; 404 на пропущенный slug; 400 на пустой inline markdown.
  • Итого: 349 / 350 unit (1 pre-existing дрейф родительских данных в portals-dead.test.mjs).
  • Покрытие: 94.59 % line / 84.16 % branch.
  • 20 / 20 smoke E2E, 23 / 23 comprehensive E2E, 28 / 28 Playwright.

📝 Документация

  • docs/reviews/REVIEW-2026-05-12-v1.10.3.md — контекст сессии + список scope-out.
  • Все 8 README: bump бейджей (tests 340 → 349, release v1.10.2 → v1.10.3), раздел «What’s new in v1.10.3» на локаль.
  • Все 8 CHANGELOG получают эту запись.

Вне рамок (отложено в v1.11.0)

  • PR-1 — полный локаленезависимый реестр адаптеров (8 файлов ATS-адаптеров + новый /api/stream/scan?source=, консолидирующий два существующих эндпоинта + +14 новых порталов + переписывание view сканирования). Срез только меток в этом релизе закрывает F-010 / F-011 визуально; архитектурный рефакторинг — многодневная фаза.
  • PR-4 — конвейер CV-импорта на multer (заменяет 415-конверт из v1.10.2 на настоящий парсер multipart + ConversionError-конверт + ревизию зависимостей).
  • PR-9 — полная интеграция career-ops.org docs: подтянуть career-ops.org/docs + 4 подгайда (scan-job-portals, apply-for-a-job, batch-evaluate-offers, set-up-playwright), перевести в 7 не-EN локалей, переписать справочные бандлы + README соответственно, провести аудит экранов UI против документированного поведения.

[1.10.2] — 2026-05-12

Патч функциональной регрессии. Закрыты два бага из ручного тестирования v1.10.1; поверхность документации расширена.

🐛 Исправления

  • fix(cv): /api/cv/import rejects multipart/form-data with 415 (F-016 hardening) — любой внешний клиент (curl -F, распространённые HTTP-клиенты), по умолчанию использующий multipart/form-data, ранее сохранял свой wire-конверт (--boundary…\r\nContent-Disposition: form-data; name="file"; filename="x"…) как содержимое cv.md. Фактический путь SPA (Content-Type: application/octet-stream + X-Filename) это не затрагивало. Маршрут теперь возвращает 415 с подсказкой на документированный контракт. Defense-in-depth: тела octet-stream, sniffing-щиеся как multipart в первых 256 байтах, также получают 415. cv.md никогда не тронут при 415.
  • fix(pdf): /api/stream/pdf invokes generate-pdf.mjs with proper positional args — раньше вызывал скрипт с []. Скрипт печатал свою строку Usage: и выходил с кодом 1 — SPA показывал зелёный toast «PDF generated», но ни один файл не попадал на диск. Маршрут теперь читает cv.md, рендерит его в HTML-файл под output/cv-input-<TIMESTAMP>.html через in-route хелпер markdown-to-print-HTML, затем спавнит generate-pdf.mjs <input.html> <output.pdf> --format=a4. Опциональный query ?format=letter для вывода US-letter. Когда cv.md отсутствует, эмитит событие error + done { code: 2 } вместо фейкового кадра start.

🧪 Тесты

  • Новый tests/cv-upload-multipart-reject.test.mjs (5 кейсов): happy-path SPA возвращает 200 с чистым markdown; multipart/form-data → 415; тело octet-stream, ВЫГЛЯДЯЩЕЕ как multipart, → 415; пустое тело → 400; отклонённый запрос НЕ модифицирует cv.md.
  • Новый tests/pdf-stream-args.test.mjs (3 кейса): событие start несёт <input.html> <output.pdf> --format=a4 с абсолютными путями и HTML существует на диске; ?format=letter переключает флаг; отсутствующий cv.md эмитит ожидаемый кадр ошибки.
  • Итого: 340 unit-тестов (было 318). Один pre-existing провал в portals-dead.test.mjs остаётся дрейфом данных на стороне родителя, не связан с web-ui.
  • Покрытие: 94.63 % line / 84.94 % branch.

📝 Документация

  • Новый docs/test-scenarios/ — 21 файл сценариев на английском (индекс + контракты на страницу):
    • 01 smoke / health · 02 CV upload · 03 CV edit-save · 04 CV → PDF download
    • 05 profile YAML · 06 config env · 07 scan · 08 pipeline
    • 09 evaluate · 10 deep research · 11 modes · 12 apply checklist
    • 13 tracker · 14 reports · 15 activity log · 16 interview prep · 17 JDs
    • 18 i18n · 19 help center · 20 security · 21 full funnel
  • Каждый файл документирует: цель, предусловия, входы, ожидаемые выходы, негативные кейсы, тестовое покрытие (файл + диапазон строк) и ручные шаги Playwright, где применимо.
  • Новый docs/reviews/REVIEW-2026-05-12-v1.10.2.md — полный контекст сессии, список scope-out, команды верификации.
  • Все 8 README: bump бейджей (tests 318 → 340, release v1.10.1 → v1.10.2) + раздел «What’s new in v1.10.2» на локаль.
  • Все 8 CHANGELOG получают эту запись.

Вне рамок (отложено в будущие фазы GSD)

PR-1 локаленезависимый реестр адаптеров (по-прежнему в очереди), PR-4 CV-импорт на multer с полным конвейером конвертации, PR-7 кнопки Generate-PDF на reports / evaluate / deep / interview-prep, PR-8 перегруппировка UI конфигурации, PR-9 sweep документации, PR-10 покнопочный аудит локализации + шлюз jsdom CI, полный пересчёт корейского.


[1.10.1] — 2026-05-09

Патч критических правок по результатам регрессионного QA-запуска v1.10.0 (qa/reports/00-FINAL-SUMMARY.md).

🛡️ Безопасность

  • fix(security): tighten isValidJobUrl + add DNS-rebind defense (PR-3 / F-003) — isValidJobUrl теперь отвергает RFC1918 (10/8, 172.16/12, 192.168/16), полный диапазон loopback 127/8, link-local 169.254/16 (включая AWS IMDS), 0.0.0.0, CGNAT 100.64/10 и IPv6 ULA / link-local. Новый хелпер isPrivateOrLoopbackHost() экспортирован из server/lib/security.mjs и переиспользуется в /api/pipeline/preview, который теперь делает dns.lookup на каждый редирект-хоп и отвергает, когда сам разрешённый адрес приватный — закрывает DNS-rebind. DNS-провал — fail-open (fetch сообщает об ошибке), чтобы тестовые stub / песочницы без DNS продолжали работать.

🐛 Исправления

  • fix(activity): record only successful state changes (PR-5 / F-005) — middleware теперь рано выходит на res.statusCode >= 400. Отклонённые запросы pipeline / cv / tracker больше не засоряют ленту аудита.
  • fix(activity): add profile.save / config.save / cv.import event mappings (F-008) — успешные вызовы PUT /api/profile и POST /api/config теперь появляются в /api/activity.
  • fix(help): alias ko → ko-KR.md so Korean Help body is served (F-002) — SPA шлёт голые BCP-47 коды (ko); файл на диске — ko-KR.md. Резолвер теперь обходит 4 кандидата: точное совпадение, alias с региональным тегом, базовый только-язык, затем en.md.
  • fix(llm): /api/evaluate honors mode:'manual' (F-009) — зеркало /api/deep. Ручной режим пропускает вызовы Anthropic / Gemini даже при заданном ключе, чтобы пользователи могли копировать промпт в Claude Code, не сжигая кредиты.
  • fix(api): DELETE /api/pipeline accepts ?url= AND body.url, returns 404 on miss (PR-6 / F-017) — раньше тихо 200-on-miss с только ?url=.

✨ Возможности

  • feat(llm): locale propagation through every prompt (PR-2 / F-012) — новый resolveLocale(req) выбирает локаль из body.lang → body.locale → Accept-Language → 'en'. Новая buildLocaleDirective(lang) эмитит однострочный заголовок «Respond in X». buildEvaluationPrompt, buildDeepPrompt, buildModePrompt теперь принимают и встраивают lang. SPA API.call() автоматически прицепляет Accept-Language и сливает lang в JSON-тела.
  • feat(scripts): post-qa-cleanup.mjs (PR-11) — проигрывает чек-лист пост-QA-чистки; --apply пишет, по умолчанию dry-run, идемпотентен. Подметает URL RFC1918 / nip.io / test-cloud-* из data/pipeline.md и проверяет размер cv.md.

🧪 Тесты

  • Новый tests/critical-fixes.test.mjs (15 кейсов), покрывающий: F-002 разрешение alias ko, F-009 опт-аут ручного режима, форму DELETE PR-6 (body / 404 / 400), модульные тесты хелпера PR-3 для IPv4 + IPv6 + форм в скобках, приоритет PR-2 resolveLocale + buildLocaleDirective + интеграцию построителей промптов.
  • tests/url-validation.test.mjs расширен 5 новыми тестами для RFC1918 / link-local / 0.0.0.0 / 127/8 / CGNAT / IPv6 ULA / link-local.
  • Тест 8 в tests/activity-log.test.mjs обновлён, чтобы утверждать новый контракт «нет лога на 4xx».
  • Итого: 318 unit-тестов (было 298; один pre-existing провал в portals-dead.test.mjs — дрейф данных на стороне родителя в templates/portals.example.yml, не связан с кодом web-ui).

📝 Документация

  • Новый docs/reviews/REVIEW-2026-05-09-v1.10.1.md — полный контекст сессии + список scope-out + команды верификации.
  • Все 8 README: bump бейджей (счётчик тестов 298 → 318, release v1.10.0 → v1.10.1), путь скриншота перенесён в public/images/screen_vacancy_found.png, добавлен раздел «What’s new in v1.10.1» на локаль (English, Spanish, Portuguese, Korean, Japanese, Russian, Simplified Chinese, Traditional Chinese).
  • Все 8 CHANGELOG обновлены этой записью.

Вне рамок (отложено в будущие фазы GSD)

PR-1 (локаленезависимый реестр адаптеров, +14 порталов, переписывание FE), PR-4 (CV-импорт на multer + ConversionError + глобальный обработчик ошибок), PR-7 (кнопки Generate-PDF на reports / evaluate / deep / interview-prep), PR-8 (перегруппировка UI конфигурации), PR-9 (полный sweep EN-RU framing по README/docs/8-help bundle), PR-10 (покнопочный аудит локализации + шлюз jsdom CI), полный пересчёт корейского справочника (файл существует; PR только исправил runtime-доставку).


[1.10.0] — 2026-05-08

Переработка CV-импорта + вкладки #/config + канонический маршрут #/profile.

✨ Возможности

  • feat(cv): server-side import for .docx / .doc / .odt / .rtf / .pdf / .html / .txt / .md — новый эндпоинт POST /api/cv/import конвертирует загруженный документ (в любом распространённом формате) в markdown, который редактор может вставить. Офисные форматы идут через pandoc, PDF — через pdftotext из Poppler. Результат санитайзится через stripDangerousMarkdown (defense-in-depth XSS). Жёсткий лимит: 10 МБ на загрузку. Фронтенд 📁 Upload CV теперь принимает полный набор форматов; красивые toast-сообщения, когда конвертер отсутствует на хосте.
  • feat(cv): auto-download generated PDF when generate-pdf.mjs finishes — потоковый поток Generate-PDF теперь снимает снапшот последнего PDF в директории output и на done инициирует загрузку браузером нового файла (no-op, если запуск не дал нового артефакта). Существующий on-page список по-прежнему показывает каждый предыдущий PDF.
  • feat(config): two-tab layout — API keys & runtime + Profile — #/config теперь имеет полосу вкладок. Первая вкладка сохраняет существующий редактор .env (API-ключи, модели, ручки сканера). Новая вкладка Profile — прямой YAML-редактор для config/profile.yml: PUT /api/profile валидирует YAML (должен быть mapping, должен включать candidate), штампует канонический заголовок # Career-Ops Profile Configuration, если он отсутствует, и пишет файл. Сохранение распространяется без перезапуска.
  • feat(routes): canonical /#/profile route (was /#/settings) — сайдбар теперь указывает на #/profile. Старый хеш #/settings ещё резолвится через таблицу алиасов роутера, так что существующие закладки продолжают работать. Внутренний обработчик маршрута переименован; тесты обновлены, чтобы отражать новое направление.

🧪 Тесты

  • Новый tests/cv-import.test.mjs (7 кейсов): passthrough .md / .txt, пустое тело 400, неподдерживаемое расширение 422, оверсайз 413, санитайзинг HTML→markdown (пропускается, если pandoc отсутствует), round-trip PDF→текст с самодельным PDF (пропускается, если poppler отсутствует).
  • Новый tests/profile-put.test.mjs (7 кейсов): happy-path round-trip, штамповка заголовка, 400 на empty / invalid-YAML / non-object / missing-candidate, оверсайз 413.
  • tests/playwright-full-cycle.mjs расширен 14 → 16 под-тестов — добавлены CV-импорт через HTML и round-trip PUT /api/profile.
  • Regex ALIAS в tests/router.test.mjs развёрнут, чтобы утверждать новое направление settings → profile.

📚 Документация

  • docs/help/{en,ru}.md — полные обновления разделов 2/3/4: новые вкладки App-settings, сообщение edit-via-config на read-only странице Profile, полная матрица форматов загрузки в разделе CV, поведение автозагрузки PDF.
  • docs/help/{es,pt-BR,ko-KR,ja,zh-CN,zh-TW}.md — лаконичные зеркала новых блоков контента; счётчик разделов без изменений (16), чтобы тест паритета оставался зелёным.

🔧 Внутреннее

  • Новый server/lib/cv-import.mjs — единый источник правды для конвертации формат → markdown, с таймаутом + обнаружением отсутствующего конвертера, проявляющим actionable-подсказки вместо 500.
  • server/lib/routes/content.mjs получает POST /api/cv/import и PUT /api/profile (binary-safe через express.raw для загрузки, JSON для YAML PUT).

[1.9.1] — 2026-05-08

Проход готовности к продакшну. Четыре прицельных bug-fix (BF-1..BF-4), Playwright smoke расширен с 5 до 12 тестов, покрывающих round-trip сохранения tracker / pipeline / reports / evaluate / config / cv. Всё зелёное в CI.

🐛 Исправления

  • fix(tracker): escape pipes + collapse newlines in every cell, not just notes (BF-1) — имя компании вида "Acme | Co" ранее ломало раскладку markdown-таблицы (парсер разрезал ячейку надвое). Санитайзер ячеек теперь применяется однообразно к company / role / reportSlug / notes; парная правка в parsers.mjs::parseMarkdownTable добавляет поддержку экранирования \| по GFM, так что round-trip без потерь.
  • fix(config): wrap updateEnvFile in try/catch (BF-2) — POST /api/config ранее выбрасывал необработанное отклонение Promise при permission-denied / read-only ФС. Теперь возвращает чистый 500 { error: 'failed to write parent .env', details: [...] }.
  • fix(llm): soft cap on assembled prompt size for Anthropic SDK calls (BF-3 + BF-4) — ветки Anthropic в /api/evaluate, /api/deep и /api/mode/:slug теперь сдают 413, когда bundleProjectContext + prompt превышает 200 КБ (≈50K токенов). Экономит несколько секунд раундтрипа + токены вместо того, чтобы дать API пожаловаться на размер контекста. Лимит значительно ниже любого текущего потолка модели (Sonnet 4.6 = 1M контекст).

🧪 Playwright smoke — расширение покрытия

5 → 12 тестов. Новые кейсы:

  • tracker view renders empty + accepts API-seeded row — упражняет BF-1, засевая строку с литеральным пайпом в имени компании и утверждая, что round-trip его сохраняет.
  • pipeline add-URL form populates the queue + проход отказа невалидных URL (loopback, javascript:, голые строки).
  • reports view handles empty state — проверка отсутствия падения.
  • evaluate view returns a manual prompt without API key — проверяет fallback-цепочку.
  • config GET returns known keys masked — секреты никогда не утекают через /api/config.
  • cv.md PUT round-trips with sanitization — XSS-ные куски (тэги script, схемы javascript:) полностью санитайзятся.
  • pipeline preview proxy strips scripts — путь отклонения невалидных URL.

📦 Изменения поведения (без изменений API-контракта)

  • Записи в tracker теперь без потерь относительно имён company / role с пайпами. Существующие строки с сырыми пайпами начнут парситься правильно при следующем чтении.
  • /api/{evaluate,deep,mode/:slug} теперь возвращают 413 вместо 502/timeout, когда промпт неоправданно велик (200 КБ+).

🧪 Тесты

  • 284 unit-теста (счётчик без изменений; существующие тесты остаются зелёными после обновления парсера).
  • 12 Playwright browser-smoke тестов (было 5).

[1.9.0] — 2026-05-08

P-6 → P-10 из бэклога v1.8.0 отгружены одним пакетом. Главное: server/index.mjs теперь оркестратор на 130 LOC (с 762, всего 1230 → 130 = -89%); у каждой темы маршрутов свой модуль. Anthropic-паритет для /api/evaluate, шимы multi-CLI, расширенный тест паритета i18n и подключение Playwright browser-smoke в CI.

🏗️ P-6 — разделение сервера по сферам ответственности (фаза 2)

Продолжение P-2. Вынесены оставшиеся 9 тем маршрутов из server/index.mjs в модули server/lib/routes/<topic>.mjs. index.mjs теперь чистый оркестратор: middleware (security headers + activity log + static), 12 вызовов register<Topic>Routes(app) и catch-all SPA.

  • server/lib/routes/activity.mjs — /api/activity.
  • server/lib/routes/config.mjs — GET/POST /api/config (round-trip родительского .env).
  • server/lib/routes/health.mjs — /api/health + /api/dashboard.
  • server/lib/routes/help.mjs — /api/help/:lang.
  • server/lib/routes/jds.mjs — полный CRUD для jds/*.txt.
  • server/lib/routes/llm.mjs — каждый LLM-ориентированный эндпоинт (evaluate, deep, mode, apply-helper, interview-prep).
  • server/lib/routes/pipeline.mjs — /api/pipeline*, включая SSRF-безопасный прокси preview с именованными константами для timeout / max-redirects / max-body.
  • server/lib/routes/reports.mjs — /api/reports*.
  • server/lib/routes/tracker.mjs — GET + dedup-aware POST /api/tracker.

Поведение без изменений. 283/283 unit-тестов оставались зелёными на каждом шаге. Поверхность импортов оркестратора упала с 47 строк до 22.

🔌 P-7 — Anthropic-паритет для /api/evaluate

/api/evaluate ранее был Gemini-or-manual. v1.9.0 добавляет ветку Anthropic (предпочтительна, когда оба ключа заданы), зеркалируя правило маршрутизации, уже используемое в /api/deep и /api/mode/:slug. Маршрутизируется через bundleProjectContext({ modeSlugs: ['_shared', 'oferta'] }), чтобы у модели были inlined cv / profile / шаблоны режимов (REVIEW-A1).

Новый эндпоинт: POST /api/evaluate/test-anthropic — smoke-проверка для ANTHROPIC_API_KEY, зеркало существующего Gemini-smoke. Шлёт крошечный промпт (≤256 выходных токенов), так что стоит фактически ничего; возвращает 200-символьную выборку.

Fallback-цепочка теперь: Anthropic → Gemini → manual.

🌐 P-8 — паритет i18n справочного центра (аудит + усиление тестов)

Проверка паритета структуры каждого docs/help/<lang>.md. Все 8 локалей уже покрывают одни и те же 14 канонических h2-разделов. Тесты усилены:

  • tests/help-ui.test.mjs::every help doc covers the same 14 sections проверял только en + ru. Теперь итерирует все 8 локалей (en, es, pt-BR, ko-KR, ja, ru, zh-CN, zh-TW) и утверждает счётчик разделов для каждой.
  • Новый тест: tests/help-ui.test.mjs::every help locale has substantive content — защита от локальных заглушек: утверждает, что каждая не-EN локаль весит как минимум 30 % от байтовой длины en.md. Компактные переводы естественно попадают на 40-50 %; заглушка была бы в однозначных процентах.

Итог: структурный паритет теперь обеспечивается CI.

🤖 P-9 — Playwright browser smoke в матрице CI

tests/playwright-smoke.mjs (добавлен в v1.8.0 как опт-ин) теперь часть workflow CI. Существующая job e2e уже устанавливает Playwright + Chromium; один новый шаг (npm run test:e2e:browser) запускает 5 browser-smoke тестов сразу после comprehensive node E2E.

Порядок в CI: unit (матрица Node 18/20/22) → smoke node E2E → comprehensive node E2E → Playwright browser smoke → загрузка артефактов скриншотов при падении.

🌍 P-10 — Multi-CLI совместимость

Родительский career-ops v1.7.0 ввёл поддержку multi-CLI / Open Agent Skill standard. Подпроект UI следует тому же соглашению с тонкими шимами, указывающими на канонический CLAUDE.md:

  • web-ui/AGENTS.md — точка входа Codex / Aider / generic CLI.
  • web-ui/GEMINI.md — точка входа Gemini CLI.

Оба шима повторно проговаривают жёсткие правила и quick reference, но делегируют CLAUDE.md полные инструкции уровня проекта, так что не-Claude CLI приземляются на ту же ориентацию, что и сессии Claude Code. Развёрнутый UI сам по себе остаётся CLI-агностичным в runtime.

🧪 Тесты

  • 284 unit-теста (было 283): +1 новый тест паритета локалей справки.
  • 5 Playwright browser-smoke тестов — теперь часть CI, не только опт-ин.
  • Покрытие сохранено.

🔧 Затронутые файлы

+ server/lib/routes/activity.mjs              + server/lib/routes/config.mjs
+ server/lib/routes/health.mjs                + server/lib/routes/help.mjs
+ server/lib/routes/jds.mjs                   + server/lib/routes/llm.mjs
+ server/lib/routes/pipeline.mjs              + server/lib/routes/reports.mjs
+ server/lib/routes/tracker.mjs
+ AGENTS.md                                   + GEMINI.md

~ server/index.mjs (762 → 130 LOC, -83%)
~ .github/workflows/ci.yml (шаг Playwright smoke)
~ tests/help-ui.test.mjs (паритет всех-8-локалей + content-floor)
~ docs/{ROADMAP,architecture/{OVERVIEW,SERVER}}.md
~ docs/sdd/CONVENTIONS.md
~ CLAUDE.md
~ package.json (1.8.0 → 1.9.0)

📦 Новые REST-эндпоинты

Метод Путь Назначение
POST /api/evaluate/test-anthropic Smoke-проверка ANTHROPIC_API_KEY (P-7). Зеркало /api/evaluate/test-gemini.

🤖 Новые точки входа CLI

Файл CLI Примечания
AGENTS.md Codex / Aider / generic Указывает на CLAUDE.md для полных инструкций.
GEMINI.md Gemini CLI Автоматически загружается Gemini при старте сессии.

[1.8.0] — 2026-05-08

Усиление, рефакторинг и бутстрап SDD. Три исправления корректности/безопасности высокой серьёзности (A1, A2, A3), четыре средней (B1–B4), шесть чисток, аудит поверхности родительского career-ops v1.7.0, разделение сервера по сферам ответственности (P-2 фаза 1), Playwright browser-smoke и полный SDD-фундамент под docs/ и .claude/.

🔥 Исправления высокой серьёзности

  • fix(deep): inline cv/profile/mode files for Anthropic SDK calls (REVIEW-A1) — /api/deep и /api/mode/:slug ранее говорили модели «сначала прочитай эти файлы», но у Anthropic SDK нет файловой системы. Вывод был пустым. Новый bundleProjectContext({ modeSlugs }) читает cv.md, config/profile.yml, modes/_shared.md и шаблон режима, обрезает каждый на 16 КБ и предваряет блоком <project_context>. Проверено вживую: 26 КБ обоснованного markdown-ответа от claude-sonnet-4-6 на вызов deep-research.
  • fix(runner): SIGKILL escalation after SIGTERM grace period (REVIEW-A2) — runNodeScript и streamNodeScript ранее слали только SIGTERM на таймаут / отключение клиента. Ребёнок, застрявший в syscall (DNS, заблокированный сокет), игнорировал его, подвешивая SSE-соединение до того, как GC Node его съест. Теперь каждый путь вооружает 5-секундный сторожевой таймер, эскалирующий до SIGKILL. Promise всегда резолвятся.
  • fix(runner): max-runtime cap on streaming endpoints (REVIEW-A3) — у каждого SSE-runner-эндпоинта скрипта (/api/stream/{scan,liveness,pdf}) теперь жёсткий потолок 30 минут. По истечении: эмитировать event: error { message: 'maximum runtime exceeded' }, убить ребёнка через сторожевой таймер A2, завершить ответ.

🛡️ Исправления средней серьёзности

  • fix(preview): per-hop redirect validation in /api/pipeline/preview (REVIEW-B1) — переключение с redirect: 'follow' на ручной обход редиректов. Каждый заголовок Location повторно валидируется через isValidJobUrl; лимит 3 хопа. Враждебные борды больше не могут отбросить нас на loopback / приватные IP / file://. 4 новых теста покрывают пути отклонения.
  • refactor(keys): hasGeminiKey helper unifies LLM-key checks (REVIEW-B2) — прямые чтения process.env.GEMINI_API_KEY в обработчиках маршрутов заменены на hasGeminiKey() из lib/anthropic.mjs. Зеркало формы hasAnthropicKey() для согласованности и более простого мокинга.
  • feat(scanners): thread AbortSignal through hh.ru, Habr, Greenhouse, Ashby, Lever (REVIEW-B3) — когда SSE-клиент отключается на середине сканирования, активные HTTP-fetch теперь прерываются, а не дорабатывают каждый запрос до конца, теряя события. runRuScan и runEnScan принимают opts.signal; SSE-обработчики в /api/stream/scan-{ru,en} создают AbortController и прерывают на res.close.
  • test(anthropic): log-guard test prevents future API-key leaks via console (REVIEW-B4) — захватывает каждый вызов console.{log,info,warn,error,debug} во время happy + error путей runAnthropic, утверждает нулевой вывод и что строка ключа-канарейки никогда не появляется. Defense-in-depth против будущей регрессии console.log(opts).

🧹 Полировка низкой серьёзности

  • fix(parsers): defense-in-depth URL gate inside addPipelineUrl (REVIEW-C4) — отклонение значений не-http(s) на уровне парсера, дополняющее route-level isValidJobUrl. Опциональный opts.validate для вызывающих, которым нужны более строгие правила.
  • docs(readme): badge "tests-88 passed" → "tests-277 passed" (REVIEW-C3) — отставал на порядок.
  • test(i18n): missing-keys diff grouped by locale (REVIEW-C6) — когда tests/i18n-coverage.test.mjs находит провал, вывод теперь [ru] (3): foo, bar, baz вместо смешанных строк.
  • docs(review): C1 closed as resolved-on-inspection — regex санитайзеров уже были в шестнадцатеричной форме \x00-\x08; запись ревью была артефактом рендеринга инструмента.

🏗️ P-2 фаза 1 — разделение сервера по сферам ответственности

server/index.mjs был 1230 LOC, значительно за потолком в 800 строк. Разделён на сфокусированные модули без изменения поведения. Все 283 unit-теста остались зелёными на каждом шаге.

  • server/lib/security.mjs — isValidJobUrl, stripDangerousMarkdown, sanitizeJobDescription, isPubliclyExposed. Реэкспортируется из index.mjs для обратной совместимости с внешними потребителями.
  • server/lib/prompts.mjs — bundleProjectContext, buildEvaluationPrompt, buildDeepPrompt, buildModePrompt, buildApplyChecklist.
  • server/lib/store.mjs — safeReadApps, safeReadPipeline, safeListReports, checkProfileCustomized, ensureRussianPortalsDefaults.
  • server/lib/routes/scan.mjs — registerScanRoutes(app) для /api/stream/scan-{ru,en}, /api/scan-ru/config, /api/scan-results.
  • server/lib/routes/runners.mjs — registerRunnerRoutes(app) для буферизованной таблицы /api/run/*, потоковых /api/stream/{scan,liveness,pdf}, списка/загрузки сгенерированных PDF.
  • server/lib/routes/content.mjs — registerContentRoutes(app) для CV / Profile / Portals / Modes.

index.mjs теперь 762 LOC (-38%, под потолком 800). Фаза 2 вынесет tracker, pipeline, reports, jds, llm (evaluate/deep/mode) и health в модули маршрутов. Цель — <500 LOC для оркестратора.

🔍 Аудит родительского career-ops v1.7.0

Пользователь обновил родительский проект до v1.7.0. Проверена каждая потребляемая поверхность — UI полностью совместим. Заметные находки задокументированы в docs/architecture/DATA-FLOWS.md:

  • Каталог режимов вырос с 7 до 19 файлов. MODE_ALLOWLIST в UI намеренно проявляет только 7 (другие — только для Claude-Code). Добавлен комментарий, поясняющий намеренно узкие рамки.
  • Схема portals.yml подтверждена: tracked_companies (96 записей, 87 включено, 71 с API). EN-сканер читает её правильно; ключ legacy companies ещё поддерживается.
  • Новые поверхности родителя, НЕ потребляемые сегодня: dashboard/ (Go-программа), update-system.mjs, generate-latex.mjs, analyze-patterns.mjs, liveness-core.mjs, followup-cadence.mjs, test-all.mjs, локализованные субдиры режимов (de/fr/ja/pt/ru).
  • Живые /api/dashboard, /api/health, /api/modes, /api/portals, /api/profile, /api/cv, /api/jds, /api/reports, /api/tracker, /api/pipeline, /api/evaluate, /api/deep, /api/stream/scan-en все проверены зелёными.

🤖 Бутстрап SDD / GSD

У career-ops-ui теперь полный фундамент Spec-Driven Development, выровненный с pipeline GSD (gsd-* skills из superpowers@claude-plugins-official).

  • CLAUDE.md (root) — системный промпт уровня проекта: стек, pipeline GSD, жёсткие правила (контракт с родителем, envelope безопасности, никаких --no-verify), соглашения, граница родительского проекта.
  • .aiignore — список исключений для AI-агентов: vendored, бинарники, пользовательские данные родителя, .planning/, .env, дубликаты локалей.
  • .claude/agents/ — три определения проектных subagent:
    • web-ui-route-reviewer.md — фильтрует новые маршруты против SSRF, CSP, санитайзеров, контракта родительской записи, соглашений, тестов.
    • spa-view-reviewer.md — CSP-safe DOM, i18n, регистрация в роутере, доступность.
    • test-isolation-reviewer.md — проверяет CI-изоляцию тестов (без допущений о родительском проекте, без живой сети, без коллизии портов).
  • .claude/commands/ — заглушки слэш-команд: /sdd-status, /codebase-tour.
  • Дерево docs/ — всё на английском:
    • PROJECT.md — что/зачем/для-кого, рамки, ограничения, критерии успеха.
    • ROADMAP.md — текущий milestone + завершённая история + бэклог.
    • sdd/SDD-GUIDE.md — pipeline discuss → spec → plan → execute → verify → review, привязанный к skill gsd-*.
    • sdd/CONVENTIONS.md — модульная система, нейминг, маршруты, санитайзеры, клиентские паттерны, i18n, ошибки, логи, тесты, коммиты, ветки, CSS.
    • architecture/OVERVIEW.md — диаграмма верхнего уровня, слои, последовательность загрузки, инварианты, чит-лист «куда смотреть, когда…».
    • architecture/SERVER.md — карта по файлам для server/lib/*.mjs (обновлена для разделения P-2).
    • architecture/FRONTEND.md — структура SPA, инвентарь view, глобалы, «как добавить view».
    • architecture/API.md — полный инвентарь каждого маршрута /api/*.
    • architecture/DATA-FLOWS.md — каждая родительская читка/запись с явным контрактом действия пользователя.
    • reviews/REVIEW-2026-05-07.md — статический ревью, породивший правки этого changelog.

🔒 Безопасность и гигиена репо

  • chore(.gitignore): comprehensive defense-in-depth patterns — покрывает варианты env, папки IDE, GSD scratch (.planning/), пользовательские настройки агентов (.claude/settings.local.json, .claude/cache/, .claude/state/, .claude/memory/), артефакты Playwright (playwright-report/, test-results/, .playwright/, trace.zip), heap/CPU-профили, lock-файлы для неотгруженного тулинга, расширенный шум macOS Finder, общие паттерны секретов (secrets.json, credentials.json, *.pem, *.key).

🧪 Тесты

  • 283 unit-теста (было 277): +11 новых (4 для отклонения редиректов B1, 1 для hasGeminiKey, 1 для log-guard runAnthropic).
  • 5 Playwright browser-smoke тестов (новые, опт-ин через npm run test:e2e:browser): рендер дашборда + версия в подвале, навигация дашборд → scan → pipeline → cv, переключение языка, view 404, рендер страницы health. Резолвит Playwright через родительские node_modules — без новой зависимости.
  • Покрытие сохранено на ~93% line / ~83% branch.

📝 Новые / обновлённые скрипты package.json

Скрипт Назначение
npm run test:e2e:browser Запуск Playwright smoke harness против сервера in-process (5 тестов).

🔧 Затронутые файлы

+ CLAUDE.md                                    +  .aiignore
+ docs/PROJECT.md                              +  docs/ROADMAP.md
+ docs/sdd/SDD-GUIDE.md                        +  docs/sdd/CONVENTIONS.md
+ docs/architecture/OVERVIEW.md                +  docs/architecture/SERVER.md
+ docs/architecture/FRONTEND.md                +  docs/architecture/API.md
+ docs/architecture/DATA-FLOWS.md              +  docs/reviews/REVIEW-2026-05-07.md
+ .claude/agents/web-ui-route-reviewer.md      +  .claude/agents/spa-view-reviewer.md
+ .claude/agents/test-isolation-reviewer.md
+ .claude/commands/sdd-status.md               +  .claude/commands/codebase-tour.md
+ server/lib/security.mjs                      +  server/lib/prompts.mjs
+ server/lib/store.mjs
+ server/lib/routes/scan.mjs                   +  server/lib/routes/runners.mjs
+ server/lib/routes/content.mjs
+ tests/playwright-smoke.mjs

~ .gitignore                                   ~  README.md (правка бейджа)
~ package.json (1.7.2 → 1.8.0)
~ server/index.mjs (1230 → 762 LOC)
~ server/lib/runner.mjs (эскалация SIGKILL, потолок max-runtime)
~ server/lib/anthropic.mjs (hasGeminiKey)
~ server/lib/parsers.mjs (фильтр URL в addPipelineUrl)
~ server/lib/ru-scanner.mjs                    ~  server/lib/en-scanner.mjs
~ server/lib/sources/{hh,habr,greenhouse,ashby,lever}.mjs (проброс signal)
~ tests/anthropic.test.mjs                     ~  tests/i18n-coverage.test.mjs
~ tests/pipeline-preview.test.mjs


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