Журнал изменений
GitHubКаждое значимое изменение, релиз за релизом. Тот же файл публикуется в репозитории и переведён на все 19 языков.
[1.199.0] — 2026-08-15
Исправлено — широкие таблицы теперь прокручиваются вбок, а не обрезаются.
Исправлено
- На странице Scan (и во всех остальных таблицах — Трекер, Статистика, Использование, Дашборд) таблица шире окна обрезалась без полосы прокрутки, и до правых колонок было не добраться. Теперь широкие таблицы показывают горизонтальную полосу прокрутки по мере необходимости, так что любая колонка доступна при любой ширине экрана.
Примечания
.table-wrapвpublic/css/components.cssпереключён сoverflow: hiddenнаoverflow-x: auto(как у существующего контейнера.reports-scroll); скруглённая рамка сохранена.tests/table-wrap-scroll.test.mjs. Набор: 2540.
[1.198.0] — 2026-08-15
Добавлено — повторы при сканировании теперь используют экспоненциальную задержку, джиттер и учитывают Retry-After от ограничителя частоты.
Добавлено
- Когда доска вакансий на короткое время ограничивает частоту или отдаёт ошибку (HTTP 429 / 5xx) во время сканирования, повтор теперь ждёт с экспоненциальной задержкой + джиттером вместо фиксированной короткой паузы — так занятую доску не долбят с одним и тем же интервалом, а одновременные повторы не сталкиваются синхронно. Присланный доской
Retry-Afterучитывается (но с ограничением, чтобы враждебныйRetry-After: 86400не застопорил всё сканирование). Постоянные ошибки (404, отклонённые редиректы) по-прежнему падают сразу — без изменений.
Примечания
- Новые
parseRetryAfterMs()и чистаяcomputeRetryDelayMs()вserver/lib/http-json.mjs;fetchJsonтеперь захватывает.retryAfterпри не-ok ответе, аfetchJsonWithRetryпринимает необязательныйmaxDelayMs(по умолчанию 8000).tests/http-json.test.mjs(+9). Набор: 2536.
[1.197.0] — 2026-08-14
Добавлено — отслеживайте доску вакансий VC-фонда Getro только по её careers_url; id коллекции определяется автоматически.
Добавлено
- Отслеживаемой доске Getro (b2venture, Earlybird, Point Nine, …) больше не нужен вручную найденный числовой
getro_collection. Укажите собственныйcareers_urlдоски, и id определится автоматически с этой страницы при первом сканировании — один защищённый от SSRF GET читает числовойnetwork.idпрямо из встроенных данных страницы. Явныйgetro_collectionпо-прежнему имеет приоритет и полностью пропускает загрузку.
Примечания
- Новые
httpsCareersUrl(),extractCollectionId()и асинхронныйresolveCollectionId()вserver/lib/sources/getro.mjs; страница доски загружается черезsafeGet(закреплённый DNS, ограниченный размер), а определённый id по-прежнему привязан к хостуapi.getro.comчерезassertGetroUrl. Адаптер теперь сопоставляется с записьюprovider: getro, несущей httpscareers_url, даже без id.tests/sources-getro.test.mjs(+8). Набор: 2527.
[1.196.0] — 2026-08-14
Исправлено (безопасность) — адаптер Workday проверяет эндпоинт api по имени хоста, а не по подстроке.
Исправлено
- Значение
api:Workday вportals.ymlтеперь принимается только если его имя хоста —myworkdayjobs.com(или поддомен.myworkdayjobs.com). Старая проверка была поиском подстроки, поэтому любой URL, просто содержащий строку — напр.https://example.com/?x=myworkdayjobs.com— проходил и мог быть использован как эндпоинт. Реальные эндпоинты Workday не затронуты. (Сообщено CodeQL, #443.)
Примечания
- Новый
isWorkdayApi()парсит URL и проверяет хост (server/lib/portals/adapters/workday.mjs).tests/workday-adapter-endpoint.test.mjs(+1). Набор: 2522.
[1.195.0] — 2026-08-14
Производительность (сканер) — обнаружение переоткрытий остаётся быстрым на больших историях сканов.
Производительность
- Обнаружение дублей больше не деградирует в O(N²) на большом
scan-history.tsv. Группировка заголовков по компании была вложенным циклом, платящим полныйroleFuzzyMatchна каждую пару; теперь это инвертированный индекс — за один проход бакетируем строки по точному заголовку, затем нечёткое сравнение только между РАЗНЫМИ бакетами, делящими различающий (небазовый) токен. Вывод идентичен — те же кластеры переоткрытий — доказано дифференциальным тестом против старого алгоритма на 200+ случайных историях.
Примечания
groupRowsByTitleвserver/lib/detect-reposts.mjs(экспортирован для диф-теста).tests/detect-reposts-grouping.test.mjs(+2). Набор: 2521.
[1.194.0] — 2026-08-14
Исправлено (сканер) — карьерные страницы Workday с URL из одного сегмента теперь сканируются корректно.
Исправлено
- Адаптер Workday теперь разбирает карьерные URL, путь которых состоит из одного сегмента — напр.
https://parsons.wd5.myworkdayjobs.com/Search,.../KBR_Careers,.../Careers. Раньше сайт сваливался вExternal, адаптер бил не в тот CXS-эндпоинт, и проба могла выглядеть здоровой, ничего не возвращая. Теперь берётся первый непустой сегмент пути как сайт (отбрасывая языковой префикс вродеen-US); документированный случай/en-US/Externalне меняется. (Сообщено в #255.)
Примечания
- Структурный разбор пути в
server/lib/portals/adapters/workday.mjs.tests/workday-adapter-endpoint.test.mjs(+7). Набор: 2519.
[1.193.0] — 2026-08-14
Добавлено (статистика) — вкладка «Тишина после интервью», показывающая интервью, которым стоит напомнить.
Добавлено
- Вкладка Тишина после интервью в
#/stats: интервью, замолчавшие после «вежливого» окна (по умолчанию 30 дней), объединяя активные интервью и трекер — с тем, как долго каждое молчит, датой последнего интервью и причиной. Мягкий список для напоминания/закрытия; лишь рекомендации, никогда не заявление об отказе. Без токенов.
Примечания
- Новый релей
GET /api/stats/rejection-latency(fail-soft{available:false}).tests/stats-rejection-latency-route.test.mjs(+2). +10 ключей i18n ×17; help-hint#/statsс 7→8 вкладок. Набор: 2510.
[1.192.0] — 2026-08-14
Добавлено (cv-studio) — гейт «Проверка фактов в CV», ловящий числа, которых у вас не было.
Добавлено
- Карточка Проверка фактов в CV в
#/cv-studio: вставьте адаптированное CV или письмо и сверьте каждую метрику и факт с вашими реальными CV, профилем и two-pager. Вы получаете вердикт pass / warn / block плюс точные придуманные метрики, необоснованные факты и запрещённые/предупреждающие фразы — чтобы сгенерированное резюме не заявило число, которого у вас нет. Без LLM; ничего не пишется.
Примечания
- Новый релей
POST /api/cv-studio/verify-facts: пишет текст в одноразовый временный файл и запускаетverify-cv-facts.mjs, доверяя JSON-вердикту, хотя скрипт выходит с 1 на block.tests/cv-studio-verify-facts-route.test.mjs(+4). +15 ключей i18n ×17. Набор: 2508.
[1.191.0] — 2026-08-14
Добавлено (статистика) — вкладка «Что учить дальше», ранжирующая навыки для изучения.
Добавлено
- Вкладка Что учить дальше в
#/stats: свод по всему трекеру — недостающие навыки, что чаще всего топили низкое соответствие, с весами (по 5−оценке соответствия в каждом отчёте) и грейдами Critical / High / Medium — плюс навыки, уже покрытые вашим CV/профилем. Только чтение, лишь рекомендации, без токенов.
Примечания
- Новый релей
GET /api/stats/upskill(поле{ error }при нехватке данных; fail-soft{available:false}).tests/stats-upskill-route.test.mjs(+3). +15 ключей i18n ×17. Набор: 2504.
[1.190.0] — 2026-08-14
Добавлено (трекер) — панель «История компании», показывающая, какие компании реально вам отвечают.
Добавлено
- Карточка История компании на
#/tracker: выберите компанию и получите свидетельства только для чтения — насколько она отвечала вам (молчит в ответ / смешанно / отвечала раньше) и переоткрывается ли одна и та же вакансия — из трекера, follow-up и истории сканов. Без токенов; сканер не вызывается.
Примечания
- Новый релей
GET /api/stats/company-history[?company=](fail-soft{available:false}).tests/stats-company-history-route.test.mjs(+3). +18 ключей i18n ×17. Набор: 2501.
[1.189.0] — 2026-08-14
Исправлено (сканер) — уровни грейда, записанные римскими цифрами, теперь учитываются и в нелатинских заголовках.
Исправлено
- Классификатор грейда за
skip_tiersтеперь читает суффикс уровня римскими цифрами (I / II / III / IV / V) после слова должности в любой письменности — «Инженер III», «エンジニア I», «Ingénieur IV» — не только после ASCII-слов. Раньше цифра уровня после нелатинского слова игнорировалась и вакансия сваливалась в mid, поэтомуskip_tiers: [senior]или[entry]их пропускал.
Примечания
- Не зависящий от письменности lookbehind в
server/lib/classify-tier.mjs; удалён мёртвый дубль-матчерSr..tests/classify-tier.test.mjs(+1). Набор: 2498.
[1.188.0] — 2026-08-14
Исправлено (UI) — основные кнопки действий больше не прижаты вплотную к подзаголовку страницы.
Исправлено
- Ряд основного действия / управления на страницах Еженедельный дайджест интервью, Профинансированные компании, Порталы, Карьерный план и Карьерная ориентация теперь имеет верхний отступ, поэтому кнопка «дышит» под подзаголовком, а не упирается в него.
Примечания
- Регресс-гард
tests/lead-row-top-margin.test.mjs(+5). Набор: 2497.
[1.187.0] — 2026-08-14
Исправлено (сканер) — настройка skip_tiers снова работает: вакансии, которые вы просили пропускать по грейду, отбрасываются.
Исправлено
- Список
skip_tiers:вportals.yml(напр.skip_tiers: [intern, entry]) теперь учитывается сканом. Заголовок каждой вакансии классифицируется по грейду (intern / entry / mid / senior) и отбрасывается, если его грейд в списке. Раньше скан применял фильтры заголовка / локации / контента / доверия, но не имел фильтра по грейду, поэтомуskip_tiersмолча игнорировался. Заголовки без слова уровня попадают в mid (поэтомуskip_tiers: [mid]отбрасывает и большинство обычных вакансий), а классификатор читает САМОЕ ЛЕВОЕ слово уровня.
Примечания
- Новый чистый модуль
server/lib/classify-tier.mjs(classifyTier+buildTierFilter), подключён к цепочкам фильтров EN- и RU-сканеров.tests/classify-tier.test.mjs(+7). Набор: 2492.
[1.186.0] — 2026-08-14
Добавлено (CV-студия) — панель «Разрыв навыков»: какие из требуемых навыков вакансии ваше CV называет, подразумевает или не имеет.
Добавлено
- Новая панель Разрыв навыков в CV-студии. Выберите сохранённую вакансию — и каждый требуемый навык распределяется на названо в вашем CV, подразумевается в CV или отсутствует — сравнение слов без LLM, ничего не записывается. Если в вакансии не было чёткого раздела требований, появляется отметка о низкой уверенности.
Примечания
- Новый
GET /api/jds/:name/skill-gap(имя вакансии проходит path-санитизацию и проверку наличия подjds/, прежде чем стать аргументом; мягкий откат к{available:false}без скрипта). +13 ключей i18n ×17. Тесты:tests/jds-skill-gap-route.test.mjs(+4, включая отказ на path-traversal). Набор: 2485.
[1.185.0] — 2026-08-14
Добавлено (статистика) — вкладка «Воронка и скорость»: как ваша воронка соотносится с рынком и как быстро вы двигаетесь между стадиями.
Добавлено
- Новая вкладка Воронка и скорость в Статистике показывает вашу долю ответов и долю интервью рядом с рыночными ориентирами (с оговорками про малую выборку и смещение отбора), список ожидания активных откликов сверх типичного окна первого ответа и медианные дни по стадиям (Отклик → Ответ → Интервью → Оффер) — медленные строки цензурируются справа, чтобы не смещать медианы. Только чтение, ноль токенов; читает только ваш трекер.
Примечания
- Новый
GET /api/stats/funnel(мягкий откат к{available:false}без скрипта). +18 ключей i18n ×17. Тесты:tests/stats-funnel-route.test.mjs(+2). Набор: 2481.
[1.184.0] — 2026-08-14
Исправлено (UI) — плитки быстрых действий на Дашборде теперь выстраиваются в ровную сетку.
Исправлено
- На Дашборде (Командном центре) группа из 3 плиток рисовалась шире, чем из 4, из-за чего секции стояли с рваным правым краем. Теперь каждая группа использует колонки одинаковой ширины (4 на широком экране, с понижением до 3 / 2 / 1 по мере сужения окна), так что все плитки одного размера и их правые края выровнены.
Примечания
- Только CSS (
.qa-grid: фиксированноеrepeat(N, minmax(0,1fr))вместоauto-fill). Защищеноtests/dashboard-grid-align.test.mjs(+2). Набор: 2479.
[1.183.0] — 2026-08-14
Добавлено (сканер) — умнее детект дубликатов: одна и та же вакансия, перевыложенная со ссылкой-трекером, больше не показывается дважды.
Добавлено
- Сканер теперь распознаёт вакансию по каноническому URL-ключу, поэтому та же роль, перевыложенная с параметром-трекером (
?utm_…,gclid, …), поhttpvshttpsили с хвостовым слэшем /#фрагментом, считается одной вакансией — без дубля в результатах скана и в пайплайне и без впустую потраченной оценки уже виденной вакансии. Действительно разные вакансии (сохранённый функциональный id вродеgh_jid) по-прежнему считаются отдельно.
Примечания
- Новый
server/lib/url-key.mjs, подключён к дедупу обоих сканеров и к записи пайплайна. Намеренно недо-нормализует — никогда не сливает две разные вакансии. Тесты:tests/url-key.test.mjs(+5),tests/parsers.test.mjs(+1). Набор: 2477 (+6).
[1.182.0] — 2026-08-14
Исправлено (сканер) — диапазоны зарплат теперь одинаково читаются на всех языках.
Исправлено
- Суммы зарплат в строках скана и трекера используют локале-нейтральные символы ≥ и ≤ (напр.
≥ 120000 EUR,≤ 90000) вместо английских слов “from” / “up to”, которые попадали без перевода в неанглийский интерфейс. Касается всех досок с односторонним диапазоном (Getro, Remotli, Manfred, Agentic Jobs, JustJoin, Jobicy); двусторонние диапазоны (100000–150000 USD) и так были нейтральны.
Примечания
- Только отображение — клиентский фильтр зарплаты разбирает числа независимо от префикса, поэтому фильтрация не меняется. Набор: 2471.
[1.181.0] — 2026-08-14
Добавлено (сканер) — доски Getro теперь показывают зарплату, все локации и удалённые вакансии.
Добавлено
- Сканер Getro (доски венчурных talent-сетей) теперь показывает зарплату по каждой вакансии (годовой диапазон + валюта), перечисляет все локации вместо только первой и отмечает удалённые вакансии. Вакансия Getro в скане и трекере теперь несёт те же данные о зарплате и локации, что и остальные доски.
Примечания
- Только сканер; без новых зависимостей, без изменений маршрутов / CSP / SSRF. Тесты:
tests/sources-getro.test.mjs(+5). Набор: 2470 (+5).
[1.180.0] — 2026-08-14
Исправлено (СРЕДНЕ, отчёты) — список #/reports теперь таблица, и восстановлена реальная оценка, которую скрывал плейсхолдер Machine Summary.
Исправлено
- Список
#/reports— это таблица (Отчёт · Дата · Легитимность · Оценка), а не сетка из 4 карточек. Длинный чип «Оценка не найдена» сжимал колонку заголовка почти до нуля, иoverflow-wrap: anywhereв заголовке карточки переносил имя отчёта по одному символу. Теперь у каждого поля своя колонка, ячейка имени переносится по словам, а на узком экране таблица прокручивается по горизонтали (новый контейнер.reports-scroll). Новый ключ i18nrep.colReport×17. - Реальная оценка в теле (
**Итоговый балл:** 1.8 / 5) больше не скрывается плейсхолдером Machine Summary (score: —). Когда блок## Machine Summaryсодержал нечисловую или выходящую за диапазон оценку, она занимала слот разобранной оценки и блокировала запасной разбор по форме-значению в жирном, поэтому отчёт показывал «Оценка не найдена», хотя в теле был реальныйX / 5. ТеперьparseReportHeaderвосстанавливает форму-значение из тела, если пригодного числа не осталось (шаг 4.5).
Примечания
- Только клиент + парсер; без изменений маршрутов / CSP / SSRF / записи в родителя. Тесты:
tests/reports-table.test.mjs(+5),tests/report-header-locale.test.mjs(+2). Набор: 2465 (+7).
[1.179.0] — 2026-08-13
Изменено (LOW, сканер) — 20 дублирующихся декодеров HTML-сущностей сведены к общему модулю (следствие паритета, закрывает worklist).
Изменено
- 20 скрейпинг-источников имели каждый свой
decodeEntities/decodeXmlEntities(+ хелперfromCodePoint) — копии, которые дрейфовали (три могли броситьRangeError, исправлено в v1.172.0; остальные пропускали NUL/C0 или неверно разбиралиa2;). Теперь все проходят через единыйserver/lib/html-entities.mjs(декодер, безопасный по XML 1.0 Char), убрано ~237 строк дублирования. 8 RSS-подобных источников получили декодирование (раньше знали только 5 сущностей); намеренное двойное декодирование cryptocurrencyjobs сохранено через алиас.hhоставляет свой декодер (обрабатывает—/–, вне общих 6). Новый гард-тест падает, если источник снова заведёт локальный декодер.
Примечания
- Рефакторинг с сохранением поведения; без изменений маршрутов / CSP / SSRF / записи в родителя. Тесты:
tests/decoder-consolidation.test.mjs(+2). Набор: 2458 (+2).
[1.178.0] — 2026-08-13
Исправлено (LOW, паритет с родителем) — две устаревшие константы обновлены под родителя (PARENT-SYNC GAP #4 + #5).
Исправлено
- User-Agent браузера (GAP #4) —
BROWSER_LIKE_USER_AGENT(его шлют workable/workday/oraclecloud/a16z/eightfold для прохождения WAF/бот-фильтров) поднят с Chrome 131 до 151 под родительскийuser-agent.mjs; устаревшую сборку чаще блокируют. Защищено тестомChrome major ≥ 151. - FALLBACK статусов трекера (GAP #5) — резервный
FALLBACKвstates.mjs(используется только когда живойtemplates/states.ymlнечитаем — свежий клон / изолированный CI-корень) получил турецкие алиасы статусов из родителя (#2615): değerlendirildi, başvuruldu, yanıt verildi, mülakat, teklif, reddedildi, iptal edildi, uygun değil, kabul edildi/işe alındı. В проде живой файл уже их давал.
Примечания
- Только две константы; без изменений маршрутов / CSP / SSRF / записи в родителя. Тесты:
tests/http-json.test.mjs(+1) +tests/states.test.mjs(+1). Набор: 2456 (+2).
[1.177.0] — 2026-08-13
Исправлено (MEDIUM, сканер) — csod (Cornerstone) возвращал 0 вакансий у арендаторов, закрывающих search-API сессионными куками (parent #2769, PARENT-SYNC GAP #1).
Исправлено
- Некоторые арендаторы Cornerstone выставляют сессионные куки на bootstrap-странице карьерного сайта и отвечают
401 CSOD Unauthorizedна search-API, пока эти куки не вернутся вместе с анонимным bearer-токеном.sources/csod.mjsтеперь читает bootstrap через новый хелперfetchResponse, собирает заголовокCookieиз значенийSet-Cookie(cookieHeaderFrom— только name=value, семантика jar) и повторяет его в POST поиска. Только тот же origin (хост закреплён +redirect:'error'), так что сессионные куки не уйдут третьей стороне; арендатор без кук ведёт себя как раньше.
Примечания
- Новый
server/lib/http-json.mjs::fetchResponse(аддитивно; существующие источники не затронуты). Без изменений маршрутов / CSP / SSRF / записи в родителя. Тесты:tests/sources-parity-v1118a.test.mjs(+1). Набор: 2454 (+1).
[1.176.0] — 2026-08-13
Исправлено (MEDIUM, отчёты) — балл под жирной меткой, которой нет в RU-таблице, всё ещё показывал «Score not detected» (FIND-5).
Исправлено
- Два RU-отчёта записывали балл как
**Итоговый балл:** 1.8 / 5/**Скор:** 1.8 / 5— жирные метки, которых нет вREPORT_LABELS.ru(знает только «Оценка»/«Балл»), поэтому балл не разбирался. Вместо наращивания списка синонимовparseReportHeaderтеперь откатывается к форме значения: дробь по рубрике /5 под ЛЮБОЙ жирной меткой. Язык-независимо, невосприимчиво к заголовку (нет**, нет значения/5) и отвергает дату вроде5/5/2026(негативный просмотр вперёд по знаменателю).
Примечания
- Только парсер на сервере; без изменений маршрутов / CSP / SSRF / записи в родителя. Тесты:
tests/report-header-locale.test.mjs(+2). Набор: 2453 (+2).
[1.175.0] — 2026-08-13
Исправлено (LOW, укрепление) — регресс-гард для SEO-описания из FIND-3 + null-безопасная очистка легитимности (по следам AI-ревью).
Исправлено
- Гард паритета SEO-описания — правка v1.174.0, заменившая жёстко зашитое «~55» в
meta.descкаждого языка на плейсхолдер{adapters}из реестра, не имела теста и могла молча регрессировать при следующем редактировании локали. Новый CI-изолированныйtests/site-meta-desc-parity.test.mjsпадает, если любой из 17site/src/i18n/*.jsonтеряет плейсхолдер или снова зашивает число, либо еслиLanding.astroперестаёт подставлять его во все три meta-описания. - Null-безопасная очистка легитимности —
stripEmphasisвозвращает''для nullish-входа вместо строки «undefined» (поля инициализируются строкой, так что это защита в глубину).
Примечания
- Тест + однострочный гард в парсере; без изменений маршрутов / CSP / SSRF / записи в родителя. Тесты:
tests/site-meta-desc-parity.test.mjs(+3). Набор: 2451 (+3).
[1.174.0] — 2026-08-13
Исправлено (HIGH, отчёты) — локализованные отчёты показывали «Score not detected»; SEO-описание устарело.
Исправлено
- Разбор оценки (FIND-1) — отчёт на не-английском, чей H1 содержит слово-метку оценки (
# Оценка вакансии: <название>), больше не принимает это название за оценку.parseReportHeaderтеперь якорится на локализованной жирной метке (**Оценка:** 1.5 / 5), пропускает строки-заголовки и требует, чтобы метка стояла вплотную к двоеточию — поэтому RU-отчёты, показывавшие «Score not detected», отображают реальную оценку. - Чип легитимности (FIND-2) — из значения убираются markdown-звёздочки, так что чип показывает «High Confidence», а не «** High Confidence».
- Переполнение оценки — строка оценки с хвостовым статусным текстом («1.8, Status: Evaluated, …») сжимается до самой оценки; у
.score-pillпоявляется ограничение без переноса/overflow, а колонка заголовка карточки может сжиматься — так цветной чип не вылезает за край карточки. - SEO-описание (FIND-3) — meta / OG / Twitter-описания cvstart.org (все 17 языков) жёстко содержали «Scan ~55 job boards», тогда как тело страницы считало реальный реестр («~75»). Теперь описание подставляет число из реестра, так что рассинхрон не повторится.
Примечания
- Парсер на сервере + рендер/CSS клиента + i18n сайта; без изменений маршрутов / CSP / SSRF / записи в родителя. Тесты:
tests/report-header-locale.test.mjs(+4). Набор: 2448 (+4).
[1.173.0] — 2026-08-13
Добавлено (LOW, настройки) — Hermes добавлен в список обнаруживаемых AI-CLI (паритет с career-ops).
Добавлено
- Вкладка
#/config→ «Инструменты AI CLI» теперь определяет Hermes (Nous Research) — новую поддерживаемую родительским проектом среду выполнения агента (бинарникhermes). Фиксированный список вserver/lib/routes/cli-detect.mjsрасширяется с 10 до 11 инструментов; определение остаётся сканированием PATH только для чтения (ни один бинарник не запускается).
Примечания
- Без изменений i18n / маршрутов / CSP / SSRF / записи в родителя; список — фиксированный, не ввод. Набор: 2444 (канарейка cli-detect обновлена 10 → 11).
[1.172.0] — 2026-08-13
Исправлено (MEDIUM, сканер) — некорректная HTML-сущность могла обрушить источник сканирования (паритет с career-ops #2150).
Исправлено
- Источники
oraclecloud,gemиdassaultдекодировали числовые HTML-сущности со слабой проверкойNumber.isFiniteпередString.fromCodePoint— ссылка выше0x10FFFF(например,�из некорректного или враждебного фида) бросала непойманныйRangeErrorи прерывала весь разбор этого источника. Общий модульserver/lib/html-entities.mjs(зеркало_html-entities.mjsродительского проекта) теперь ограничивает числовые ссылки набором Char XML 1.0 §2.2, поэтомуString.fromCodePointне может бросить, и сопоставляет шестнадцатеричные и десятичные раздельно, так чтоa2;больше не разбирается неверно. Три источника импортируют его.
Примечания
- Для корректных фидов поведение не меняется; без изменений JS / i18n / маршрутов / CSP / SSRF / записи в родителя. Консолидация оставшихся ~20 копий декодера в источниках отслеживается в
qa/PARENT-SYNC-WORKLIST-v1.26.0.md. - Тесты:
tests/html-entities.test.mjs(+7). Набор: 2444 (+7).
[1.171.0] — 2026-08-13
Изменено (LOW, дизайн-система) — токены типографической шкалы + слои z-index (D-4, первый шаг). Размеры и наложение были литералами в каждом компоненте.
Изменено
- Слои z-index — добавлены токены
--z-*(--z-topbar…--z-skiplink), и все литералы z-index мигрированы на них. Значения сохранены, порядок наложения идентичен; новый канареечный тест запрещает новые «магические» числа. - Типографическая шкала — рампа
--font-size-*(xs 11…2xl 28, base = Inter 15px); мигрированы основные размеры (без визуальных изменений). Значения вне рампы мигрируют постепенно (docs/UX-ROADMAP.md).
Примечания
- Только CSS-токен; без изменений поведения/JS/i18n/маршрута/CSP/SSRF/записи. Без пиксельных изменений.
tests/design-tokens-scale.test.mjs(+3). Набор: 2437 (+3).
[1.170.0] — 2026-08-13
Добавлено (LOW) — честные подсказки ETA для долгих AI-генераций (P4-ETA). Тяжёлые генерации (карьерный план ~40 с, ориентация / рынок / нетворкинг ~30 с, two-pager ~20 с) показывали только «Генерация…» без указания длительности.
Добавлено
- Рядом с каждой кнопкой долгой генерации теперь показывается приглушённая подсказка
⏱ ~N с(как ETA на#/auto). Общий стиль.eta-hint+ два обобщённых ключа (common.eta~{n}s,common.etaTitle).
Примечания
- Только клиент; без изменений маршрута/CSP/SSRF/записи. +2 ключа i18n ×17 (снапшот 1219 → 1221).
tests/generation-eta-hint.test.mjs(+2). Набор: 2434 (+2).
[1.169.0] — 2026-08-13
Добавлено (LOW) — inline-превью PDF (D-5). GET /api/output/pdfs/:name принудительно ставил Content-Disposition: attachment, поэтому даже кнопка «Открыть» на #/cv скачивала файл, а не показывала его.
Добавлено
?inline=1отдаёт ТОТ ЖЕ проверенный файл сContent-Disposition: inline, и браузер рендерит его в новой вкладке как 👁 Предпросмотр. По умолчанию (без параметра) — по-прежнему скачивание. Новых маршрутов нет; те же проверки имени.- Первая кнопка в списке PDF на
#/cvтеперь 👁 Предпросмотр рядом с ⬇ Скачать.cv.openPdf«Открыть» → «Предпросмотр» ×17.
Примечания
- Без изменений CSP/SSRF — тот же
sanitizePathName. Один существующий ключ i18n переписан ×17 (снапшот 1219).tests/output-pdfs.test.mjs(+3). Набор: 2432 (+3).
[1.168.0] — 2026-08-13
Исправлено (LOW, a11y) — строки чекбоксов теперь соответствуют минимуму 24×24 WCAG 2.5.8 (D-2). Метки чекбоксов/радио на #/scan, #/config, #/evaluate и #/cv-studio были в полосе ~22 px.
Исправлено
- Ограниченное правило
label:has(> input[type="checkbox"/"radio"]) { min-height: 24px }гарантирует полосу ≥24 px. Толькоmin-height— метки уже flex, ничего не смещается;.apply-checklist(32 px) уже соответствовал.
Примечания
- Только CSS; без изменений поведения/JS/i18n/маршрута/CSP/SSRF/записи.
tests/checkbox-target-size.test.mjs(+1). Набор: 2429 (+1).
[1.167.0] — 2026-08-13
Исправлено (LOW, дизайн-система) — приподнятые поверхности теперь отделяются от разделителей (D-3). Токены --panel-2 / --surface-elev1 разрешались в --slate — то же значение, что и у разделителей --line / --border — без визуального отделения.
Исправлено
- Отдельный тематический токен
--elev(светлая#eef1f6/ тёмная#1e232e, отличается от--slateв обеих темах) теперь стоит за приподнятыми поверхностями; разделители остаются на--slate. Остальные находки (D-2, D-4, D-5, P4-ETA) занесены в бэклогdocs/UX-ROADMAP.md.
Примечания
- Только CSS-токен; без изменений поведения/JS/i18n/маршрута/CSP/SSRF/записи.
tests/elevation-token.test.mjs(+2). Набор: 2428 (+2).
[1.166.0] — 2026-08-13
Исправлено (LOW) — терминология рубрики теперь совпадает с канонической документацией. career-ops.org/docs описывает «пять измерений плюс целостную общую оценку», но web-ui, cvstart.org и вики говорили «шестимерная рубрика» (5 + 1 = 6, но формулировка расходилась).
Исправлено
- Принята формулировка документации — «пять измерений плюс целостная общая оценка» — единообразно в README ×17, на сайте cvstart.org ×17, в справке ×17, в
docs/career-ops-canonical.mdи в вики (Home ×17 + Features).
Примечания
- Только docs/маркетинг; без изменений кода/ключа i18n/маршрута/CSP/SSRF/записи.
tests/rubric-terminology.test.mjs(+2). Набор: 2426 (+2).
[1.165.0] — 2026-08-13
Исправлено (LOW) — термин «Two-pager» теперь согласован внутри каждой локали. В арабском боковая панель показывала латинское «Two-pager», тогда как <h1> был полностью локализован — единственная латинская строка в зеркальной RTL-навигации.
Исправлено
- Решение зафиксировано: для каждой локали
nav.twoPagerиtwoPager.titleиспользуют один термин (оба на латинице или оба локализованы). Разъезжался только арабский; его пункт навигации теперь локализован («الصفحتان»). Новый канареечный тест падает, если какая-либо локаль снова их разведёт.
Примечания
- Только текст; без изменений маршрута/CSP/SSRF/записи. Изменено 1 значение i18n (ar); новых ключей нет (снапшот 1219).
tests/two-pager-term-consistency.test.mjs(+2). Набор: 2424 (+2).
[1.164.0] — 2026-08-13
Исправлено (LOW) — плейсхолдер поиска в верхней панели больше не переполняется ни на одном языке. «Find a company, role or URL…» обрезался (nowrap), когда строка поиска сжималась; половина «…or URL» была не видна.
Исправлено
top.search(×17) теперь короткий «Поиск или вставьте URL» (≤24 символов на каждом языке), помещается даже в узкой строке и сохраняет подсказку про URL. Резервный плейсхолдер вindex.htmlсовпадает;aria-labelсохраняет полное описание.
Примечания
- Только текст; без изменений маршрута/CSP/SSRF/записи. Один существующий ключ i18n переписан ×17 (новых нет; снапшот 1219).
tests/search-placeholder-fit.test.mjs(+2). Набор: 2422 (+2).
[1.163.0] — 2026-08-13
Исправлено (LOW) — встроенный ассистент «Спросить документацию» теперь охватывает экспорт отчёта в PDF. Раньше он отвечал, что руководство это не описывает, хотя на #/reports/:slug есть рабочая кнопка 📄 Generate PDF.
Исправлено
- Во все 17 бандлов справки под §10 «Отчёты» добавлен H3 «Экспорт отчёта в PDF» (где кнопка, что файл попадает в
output/*.pdf, нужен Playwright, проверить перед отправкой). Поиск ассистента теперь выдаёт раздел «Отчёты».
Примечания
- Только docs/справка; без изменений кода/маршрута/CSP/SSRF/записи. Гейт справки 112 → 113 H3 (31 H2 без изменений).
tests/help-reports-pdf-section.test.mjs(+2). Набор: 2420 (+2).
[1.162.0] — 2026-08-13
Исправлено (MEDIUM) — «?»-подсказка теперь ≥24×24 (WCAG 2.5.8). .help-hint был 18×18 px с padding:0 — ниже минимума, на каждом заголовке.
Исправлено
- Бокс
.help-hintтеперь 24×24 (измеримая цель), а видимое кольцо остаётся 18px — рисуется центрированным::before, так что глиф и базовая линия<h1>не меняются. Состояния hover/активное/фокус следуют за кольцом; отступ 6→3px сохраняет зазор.
Примечания
- Только CSS; без изменений JS/i18n/маршрута/CSP/SSRF/записи.
tests/help-hint-target-size.test.mjs(+2). Набор: 2418 (+2).
[1.161.0] — 2026-08-13
Исправлено (MEDIUM) — #/reports показывает чип «Оценка не распознана» вместо пустого места. После языко-независимого парсера v1.159.0 отчёт без разбираемой оценки показывал пустую область — неотличимо от сбоя.
Исправлено
- Ячейка оценки теперь ветвится: есть оценка → тональная плашка; нет оценки → приглушённый чип
.score-muted(«Оценка не распознана», ×17) с подсказкой «Откройте отчёт…». Карточка остаётсяrole="link"с управлением с клавиатуры, дата отображается. - Переиспользует существующий нейтральный токен; без нового цвета.
Примечания
- Только клиент; без изменений маршрута/CSP/SSRF/записи. +2 ключа i18n ×17 (снапшот 1217 → 1219). Набор: 2416 (+3).
[1.160.0] — 2026-08-13
Исправлено (HIGH) — текст о провайдерах больше не противоречит обещанию 7 провайдеров. #/config утверждал, что живая оценка «использует ключ Anthropic или Gemini», а ключ OpenAI «веб-интерфейс не использует»; на дашборде было «Anthropic-first scoring» — ложь после каскада 7 провайдеров (v1.157.0), и противоречие на том же экране (чип Active: OpenRouter).
Исправлено
config.providerModelNote(×17): теперь сказано, что ⚡ живая оценка headless работает на любом из семи ключей провайдеров (Anthropic · Gemini · OpenAI · Qwen · OpenRouter · GitHub Models · Hermes), с автопорядком и откатом. Ложная фраза про OpenAI удалена.dash.quick.evaluateSub(×17): нейтрально («Оценка соответствия 0–5»).Keys: N / 5→N / 7.
Примечания
- Только текст; без изменений маршрута/CSP/SSRF/записи. Новых ключей i18n нет (снапшот 1217). Набор: 2413 (+3).
[1.159.0] — 2026-08-13
Исправлено (HIGH) — метаданные отчёта больше не зависят от языка. Отчёты на не-английском языке показывали пустую полосу метаданных на #/reports (без оценки, даты и легитимности), потому что parseReportHeader распознавал только английские жирные метки.
Исправлено
parseReportHeaderтеперь разбирает языко-независимый YAML-блок## Machine Summary(score:/legitimacy:/date:— тот же источник, что уже читаетauto-pipeline). Приоритет: английские метки → блок Machine Summary → локализованные метки (REPORT_LABELS, 17 локалей). Английские отчёты остаются байт-в-байт идентичными.- Толерантный разбор числа (
1.5/5,1,5/5,1.5 из 5,4.5 out of 5); дата откатывается к mtime файла, если в теле её нет.
Примечания
- Только чтение/разбор; без изменений маршрута, CSP, SSRF и записи в родитель. Новых ключей i18n нет. Набор: 2410 (+8).
[1.158.0] — 2026-08-12
Исправлено — два косметических бага отображения («?» протекала в заголовок вкладки и неверное число провайдеров на лендинге). Только отображение; без изменений поведения, безопасности и потока данных.
Исправлено
- «?» из HelpHint больше не протекает в
document.title. Роутер брал заголовок вкладки из необработанногоh1.textContent, из-за чего вкладка показывала «Vacancy search?» вместо «Vacancy search». Теперьrouter.js::focusNewViewклонирует заголовок, удаляет.help-hintи лишь затем читает текст; видимая «?» в заголовке не затрагивается. - cvstart.org показывал «17 AI providers» вместо «7». Хелпер
sub()вFeatures.astroзаменял все{n}числом локалей (17) до покарточной подстановки; теперь{n}разрешается покарточно (провайдеры → 7, языки → 17).
Примечания
- Без изменений сервера, маршрута, CSP, SSRF и ключей i18n; форма
facts.jsonне изменилась. Набор: 2402 теста (+1).
[1.157.0] — 2026-08-12
Исправлено — живые оценки теперь выполняются на ЛЮБОМ настроенном провайдере, а не только Anthropic/Gemini. Пользователь с одним лишь OPENROUTER_API_KEY ошибочно принудительно переводился в ручной режим.
Исправлено
- Первопричина: пин
LLM_PROVIDERбез ключа (например,LLM_PROVIDER=claudeизinit) заходил в тупик; теперь происходит откат к auto-порядку среди настроенных провайдеров (вselectActiveProvider+ обеих каскадах диспетчеризации). - Клиентский гейтинг (
#/deep+ вью mode-page) теперь используетwindow.ProviderStatus(/api/status/providers, все 7) вместо устаревшей проверки Anthropic/Gemini; переформулированы подсказки (deep/eval × 17) + значок «Живые оценки» на дашборде +config.llmProviderHint.
Примечания
- Без изменений безопасности. Набор: 2401 тестов (+5).
[1.156.0] — 2026-08-12
Рефакторинг — разбить scan.js под лимит размера (P-16) + фикс CodeQL. scan.js был 906 строк; извлечены две сохраняющие поведение фабрики → 648. Завершает пару разбиений вью P-15/P-16.
Изменено
- Новые
scan/runner.js(движок выполнения скана) иscan/filters.js(машина состояний фильтров) через bag-объектыctx/refs;scan.jsсвязывает оба.
Исправлено
- CodeQL
js/useless-assignment-to-local(#428) вconfig/tab-controller.js:let n = i;→let n;.
Примечания
- Чистый рефакторинг, без изменения поведения; 4 теста, читающих исходник, перенаправлены. Оба крупных вью теперь под 800 (P-15/P-16 готово). Набор: 2396 тестов.
[1.155.0] — 2026-08-12
Рефакторинг — разбить config.js под лимит размера (P-15). config.js был 1030 строк (сверх лимита 800); извлечены два модуля, сохраняющих поведение, — теперь 783.
Изменено
- Новые
config/field-specs.js(данные полей + списки моделей) иconfig/tab-controller.js(фабрика панели вкладок);config.jsссылается на них, логика рендера не меняется.
Примечания
- Чистый рефакторинг, без изменения поведения; 6 тестов, читающих исходник, перенаправлены.
scan.js(906) оставлен как есть (уже частично разбит; ядро слишком связано для чистого механического разбиения). Набор: 2396 тестов.
[1.154.0] — 2026-08-12
Новый гайд — «Запуск всего стека в облаке». У career-ops нет собственного рассказа про облако/сервер, поэтому добавлен: пошаговый рецепт, как разместить родительский пайплайн career-ops, этот вьюер career-ops-ui и движок ИИ (подписка Claude через Claude Code, локальный Hermes или ключи API) на небольшом постоянно включённом сервере. Поставляется как Справка §31 на 17 языках, раздел README и вики-страница.
Добавлено
- Справка §31 «Запуск всего стека в облаке» (× 17) — три части, подготовка + установка, выбор движка и безопасная публикация (HTTPS обратный прокси + аутентификация + инварианты CSP/SSRF/XSS/без-секретов). Бандл справки растёт до 31 H2 / 112 H3.
- README — раздел «Запуск всего стека в облаке» (× 17) + вики-страница Cloud-Deployment.
Примечания
- Только документация — без маршрутов, сервера и изменений клиента; без новых i18n-ключей. 4 теста справки переведены на контракт 31 H2 / 112 H3. Набор: 2396 тестов (без изменений).
[1.153.0] — 2026-08-12
Сканер Jobvite переведён на публичный XML-фид (синк с родителем). Родитель вывел из эксплуатации JSON API Jobvite (теперь ноль вакансий); source в web-ui использовал тот же мёртвый эндпоинт, так что любая отслеживаемая компания Jobvite молча сканировалась пустой. Порт родительского фикса (#2623): теперь читается публичный по-тенантный XML-фид с ключом companyEId.
Исправлено
- source обращался к выведенному JSON API и возвращал ноль вакансий; теперь он обращается к
https://app.jobvite.com/CompanyJobs/Xml.aspx?c={companyEId}и парсит XML<result><job>…(CDATA + сущности,detail-urlпредпочтительнееapply-url).
Изменено
- Разрешение
companyEId: (1)company_eid:в портале, (2) параметрc=явногоapi:, (3) дискавери по странице борда.fetchText(http-json.mjs) добавляет.location/.retryAfterк non-ok ошибке (только чтение, обратно совместимо).
Примечания
- Безопасность — два хоста (
jobs.jobvite.com,app.jobvite.com) закреплены черезassertJobviteUrl: только https, строгий allowlist, редиректы не следуются.companyEId— только значение?c=; число sources не изменилось. - Набор: 2396 тестов (+4).
[1.152.0] — 2026-08-12
Провайдер Hermes — проводка завершена + актуализация документации. Код-ревью интеграции Hermes из v1.151.0 нашло два реальных пробела и четыре пункта полноты; всё исправлено здесь, а список LLM-провайдеров по всему приложению приведён к полным семи на всех поверхностях документации и в 17 языках.
Исправлено
#/configне мог форсировать Hermes — выпадающий списокLLM_PROVIDERперечислял лишь шесть провайдеров, поэтомуHERMES_API_KEYможно было задать, но не форсировать Hermes из UI. Теперьhermes— 8-й вариант, а новый тест паритета не даёт списку снова разойтись сLLM_PROVIDERS.- Короткие self-hosted ключи молча отклонялись — порог в 20 символов у
isUsableKeyбыл рассчитан на облачные ключи;hasHermesKeyтеперь использует смягчённый порог в 8 (пример в документации Hermes — 19 символов).
Изменено
- Список провайдеров нормализован до полных семи в README (× 17), встроенной справке (× 17), словаре
config.llmProviderHint(× 17) иdocs/sdd;hermesChatUrlдостраивает хост без пути; текст ручного фолбэка называет Hermes.
Примечания
- Безопасность без изменений — новых маршрутов и правок SSRF/CSP нет; в health/doctor добавлена строка
HERMES_API_KEY. - Набор: 2392 тестов (+2).
[1.151.0] — 2026-08-12
Hermes теперь подключённый LLM-провайдер (Phase 5) — спайк Phase 5 подтвердил, что Hermes от Nous Research включает OpenAI-совместимый API Server (hermes gateway → POST /v1/chat/completions), так что career-ops-ui выполняет живые оценки через локальный Hermes так же, как OpenAI/Qwen. Задайте HERMES_API_KEY в Настройках приложения — он входит в auto-порядок (последним). Закрывает последний открытый пункт роадмапа — Phase 5, Shape A.
Добавлено
- LLM-провайдер Hermes (Shape A) —
runHermesповерх общего клиентаrunOpenAICompatible(server/lib/openai.mjs), в обеих каскадах (llm-dispatch.mjs+routes/llm.mjs), в хвосте auto-порядка + пинLLM_PROVIDER=hermes,/api/status/providersиllm-pricing.mjs. Обращается к настраиваемому локальному base URL (по умолчаниюhttp://127.0.0.1:8642/v1) с Bearer-аутентификацией — это НАСТРОЕННЫЙ эндпоинт провайдера (как OpenRouter/Qwen), а не пользовательский URL вакансии, поэтому SSRF-guard не задействуется. - Поля в
#/config—HERMES_API_KEY(секрет) +HERMES_BASE_URL+HERMES_MODEL(по умолчаниюhermes-agent), 6 новых ключей i18n × 17 локалей (snapshot 1208 → 1214).
Изменено
- Спайк разрешён:
docs/integrations/HERMES.md, встроенная справка §30 (× 17), тизер README (× 14), скиллhermes-bridgeи roadmap переходят с «запланировано / пока не подключено» на подключено (Shape A). Shape B (кастомный relay агентного runtime) не понадобился.
Примечания
- Безопасность: запрос провайдера — это настроенный эндпоинт, той же категории, что и другие OpenAI-совместимые провайдеры — без новой SSRF-поверхности, без изменения CSP/санитайзера.
HERMES_API_KEY— этоSECRET_KEY(никогда не отображается). - Тесты (изолированы в CI, заглушка транспорта):
tests/hermes-provider.test.mjs(+5); канарейка «нет ветки Hermes» из v1.146.0 инвертирована, чтобы утверждать, что она ЕСТЬ; тесты набора провайдеров обновлены до порядка из 7 провайдеров. - Набор: 2390 тестов (+5).
[1.150.0] — 2026-08-12
Единообразные пустые состояния (полировка Phase 4) — каждая панель «пока ничего нет» теперь отрисовывается единым общим стилем .empty, вместо того чтобы некоторые представления переобъявляли вид инлайн с магическим 40px. Небольшое исправление визуальной согласованности; пустые состояния #/activity, #/cv-studio, #/stats и #/usage теперь совпадают со всеми остальными (токенизированный отступ 48px + пунктирная рамка).
Изменено
#/activity,#/cv-studio,#/stats,#/usageубрали свой инлайнstyle: { padding: '40px', textAlign: 'center', color: 'var(--foggy)' }на пустых панелях — все три свойства уже даёт общий класс.empty(--space-7= 48px, по центру, приглушённо, пунктирная рамка). Теперь эти четыре отрисовываются идентично ~25 другим панелям.empty.- Правомерные переопределения по представлению (
#/dashboardwidth:100%,#/pipelineborder:none) не тронуты — убраны только чисто избыточные переобъявления.
Примечания
- Только чистка использования CSS на клиенте — без изменения маршрута, сервера, ключа i18n или правил CSS (сам класс
.emptyне менялся); snapshot словаря 1208. Проверено в браузере (пустая панель#/usageвычисляет 48px отступа + пунктирную рамку, 0 ошибок консоли). - Новый канареечный тест
tests/empty-state-consistency.test.mjsдержит.emptyединственным источником истины. Phase 5 (провайдер Hermes) остаётся заблокированной. - Набор: 2385 тестов (+2:
tests/empty-state-consistency.test.mjs).
[1.149.0] — 2026-08-12
Порталы перенесены в Настройки (Phase 4) — #/portals теперь находится в группе навигации Setup рядом с Настройками приложения, а не под Sourcing. С v1.144.0 это экран настроек (включение/отключение отслеживаемых компаний + проверка состояния ATS), а не действие сорсинга — значит, ему здесь и место. Изменение только навигации; страница и её маршрут не меняются.
Изменено
- Элемент навигации
#/portals→ группа Setup (вpublic/index.html), сразу после Настроек приложения. Убран из группы Sourcing (в ней остаются Scan / Pipeline / Auto-pipeline / Профинансированные компании). Маршрут#/portals, представление и подписьnav.portalsне меняются — сдвинулась только позиция в боковой панели.
Примечания
- Только разметка навигации — без изменения маршрута, представления, ключа i18n или сервера. Проверено в браузере (0 ошибок консоли); защищено
tests/portals-nav-placement.test.mjs. - Набор: 2383 тестов (+2:
tests/portals-nav-placement.test.mjs).
[1.148.0] — 2026-08-12
Аккуратные фильтры сканирования (Phase 4) — панель фильтров теперь опрятная сетка — панель фильтров #/scan перешла с рваной flex-обёртки из жёстких блоков переменной ширины на адаптивную сетку, а действия «Применить / Сбросить» теперь занимают собственную отдельную строку с выравниванием вправо. Фильтры те же, поведение то же — просто читать удобнее. Дизайнерская правка (без parent-sync).
Изменено
- Панель фильтров
#/scan→ адаптивная сетка —.scan-filtersтеперьdisplay: gridсо столбцамиrepeat(auto-fill, minmax(180px, 1fr))и равномерными отступами, так что 11 подписанных фильтров выстраиваются в аккуратные колонки при любой ширине, а не рвано переносятся в ряд. - Действия «Применить / Сбросить» занимают всю ширину сетки в собственной строке, отделены тонкой линией и выровнены вправо. Удалён старый приём со скрытой меткой + внутренняя flex-обёртка в
scan.js.
Примечания
- Только CSS + небольшая чистка DOM — все id фильтров (
#scan-filter-*,#scan-apply) и связкаSR.render()не менялись, так что поток Playwright не затронут. Новых ключей i18n нет. - Проверено в браузере (0 ошибок консоли); защищено
tests/scan-filters-grid.test.mjs. - Набор: 2381 тестов (+3:
tests/scan-filters-grid.test.mjs).
[1.147.0] — 2026-08-12
Hermes & Telegram — раздел встроенной справки + поверхность на cvstart.org (Phase 5b, часть 2) — вторая и последняя часть работы над документацией Hermes: инструкция теперь живёт внутри собственной справки приложения на всех 17 языках, и встроенный ассистент документации отвечает на вопросы о Hermes на её основе. Всё ещё только документация — путь LLM-провайдера Hermes остаётся запланированным / пока не подключённым (Phase 5).
Добавлено
- Встроенная справка §30 «Hermes & Telegram» × 17 языков — новый раздел (что такое Hermes + две формы интеграции; запуск на облачном сервере; Telegram через Hermes + правило «что НЕ раскрывать»), доступный из
#/help. Grounding уdocs-assistant/DocsFabподхватывает его автоматически, так как оба читаютdocs/help/<lang>.md. - cvstart.org — ссылка на руководство по Hermes с переходом на документ в GitHub.
Изменено
- Порог пакета справки поднят 29 → 30 H2 / 105 → 108 H3 (
canonical-docs-coverage,help-ui,help-ru-config-section); §30 добавляет 3 H3.
Примечания
- Hermes по-прежнему никто не вызывает. Новый канареечный тест
tests/help-hermes-section.test.mjsпроверяет, что во всех языках есть §30 с языконезависимыми якорями (docs/integrations/HERMES.md,hermes-bridge,#/help,127.0.0.1, Telegram). Провайдер остаётся заблокированным до контракта API из Phase 5. - Это закрывает поставку документация + skill для Phase 5b; интеграция провайдера (Phase 5) остаётся отдельным заблокированным пунктом.
- Набор: 2378 тестов (+2:
tests/help-hermes-section.test.mjs).
[1.146.0] — 2026-08-12
Агент Hermes + Telegram — руководство по интеграции + skill (Phase 5b, часть 1) — вы можете запустить career-ops-ui на облачном сервере и связать его события (завершённый скан, новый отчёт, срочный follow-up) с Telegram через агента Hermes от Nous Research. Этот релиз добавляет документацию по дизайну + развёртыванию и skill hermes-bridge; путь провайдера LLM Hermes остаётся запланированным / пока не подключённым (заблокирован спайком по контракту API Phase 5). Документация опережает код осознанно.
Добавлено
docs/integrations/HERMES.md— подробный разбор: две формы интеграции (OpenAI-совместимый endpoint против агентного runtime), развёртывание на облачном сервере (reverse proxy + HTTPS + systemd, контракт «только чтение» с parent на headless-машине), Telegram через Hermes, и список модели угроз «что НЕЛЬЗЯ раскрывать» (никаких CV / зарплат / тел отчётов / ключей в канал).- Тизер
## Hermes agent + Telegramв README — короткая ссылка-указатель в англоязычном README, отражённая во всех полностью переведённых локализованных README. - Skill
hermes-bridge(.claude/skills/hermes-bridge/), реализующий руководство на практике — проверки предпосылок и границ (Node ≥ 18, наличие ключей, доступность endpoint через SSRF-безопасный путь), никогда не пишет секреты на диск/в логи и отказывается выдумывать endpoint Hermes или заявлять, что провайдер подключён. - Раздел Integrations в
docs/architecture/OVERVIEW.mdссылается на руководство.
Примечания
- Hermes пока никто не вызывает. Канареечный тест (
tests/hermes-docs.test.mjs) проверяет маркеры честности «запланировано / пока не подключено» и то, что вllm-dispatch.mjsнет ветки Hermes/Nous — так что подключение провайдера позже должно обновить документацию + roadmap в том же изменении. - Отложено до v1.147.0 (Phase 5b, часть 2): раздел справки в приложении «Hermes & Telegram» H2 × 17 языков и маркетинговая страница cvstart.org.
- Набор: 2376 тестов (+4:
tests/hermes-docs.test.mjs).
[1.145.0] — 2026-08-12
Полезная статистика (продолжение): перестраиваемый график — на вкладке «Тренд целевых ролей» на #/stats появился виджет Построить график: выберите метрику × измерение, и он перерисовывается вживую. Запрос UX от пользователя (без parent-sync).
Добавлено
- Перестраиваемый график метрика × измерение — выберите метрику (Вакансии / Медианная зарплата / Средняя зарплата) и измерение (По странам / По ролям), и столбчатый график мгновенно перерисовывается. Зарплатные метрики учитывают валюту + переключатель год ⇄ месяц; вакансии — простой счёт.
- 8 новых ключей i18n × 17 локалей; snapshot 1200 → 1208.
Примечания
- Проверено в браузере (0 ошибок консоли). Набор: 2372 тестов (+2).
[1.144.0] — 2026-08-12
Настройки и фильтры (Фаза 4, часть 1): включение/отключение отслеживаемых порталов — теперь можно включать и выключать отслеживаемую компанию на #/portals, и сканер это учитывает. Запрос UX от пользователя (без parent-sync).
Добавлено
- Переключатель Включить/Отключить по компании на
#/portals— один клик отключает портал (EN-сканер уже пропускает компании сenabled: false, так что отключённый портал выпадает из всех будущих сканов) или включает обратно, с оптимистичным тостом. POST /api/portals/toggle— явная пользовательская запись, которая хирургически и с проверкой парсинга переставляет флагenabledкомпании вportals.yml(комментарии, порядок и остальные поля сохраняются). 5 новых ключей i18n × 17 локалей; snapshot 1195 → 1200.
Примечания
- Изменений в сканере — ноль:
en-scanner.mjsуже фильтруетenabled !== false. Набор: 2370 тестов (+3).
[1.143.0] — 2026-08-12
Понятность (продолжение): подсказки ? на основных рабочих страницах — аффорданс ? теперь охватывает девять главных страниц действий, на всех языках. Правка UX по репорту пользователя (без parent-sync).
Добавлено
- Подсказка
?ещё на 9 заголовках —#/scan,#/evaluate,#/cv-studio,#/tracker,#/config,#/deep,#/batch,#/auto,#/applyполучили встроенный?(черезHelpHint.title), открывающий локализованное «что это / как использовать / какой результат» — тот же CSP-безопасный компонент из v1.139.0. - 9 новых ключей i18n × 17 локалей (
help.hint.scan/…/apply); snapshot 1186 → 1195.
Примечания
- Проверено в браузере (0 ошибок консоли). Набор: 2365 тестов (+1).
[1.142.0] — 2026-08-12
Исправление: больше нет «Unknown» карьерного архетипа — #/orientation теперь всегда ранжирует из восьми названных карьерных векторов, а не отвечает иногда «Unknown» и не советует «удвоить ставку» на нём. Правка по репорту пользователя (без parent-sync).
Исправлено
#/orientation— промпт ИИ теперь запрещает архетип вне набора. Модель ОБЯЗАНА ранжировать топ-3 ровно из восьми названных векторов и никогда не отвечать «Unknown»/«N/A»/«недостаточно данных» и не выдумывать метку. При скудном резюме она всё равно называет три ближайших с меньшей уверенностью и указывает, каких доказательств не хватает.
Примечания
- Только изменение серверного промпта (
buildOrientationPrompt); без изменений i18n/схемы. Набор: 2364 тестов (+1).
[1.141.0] — 2026-08-12
Полезная статистика (продолжение): обогащение раздела профинансированных компаний — #/funded стал нагляднее: логотипы компаний, график по сумме финансирования и карточки с раундом / суммой / оценкой находки / рекомендуемым действием. Правка UX по репорту пользователя (без parent-sync).
Изменено
#/funded— плоская таблица → сетка карточек. Каждая недавно профинансированная компания теперь карточка с логотипом (по названию черезCompanyLogo, запасной вариант — буквенный аватар), чипами раунда + суммы, оценкой находки и рекомендуемым действием от родительского проекта, а также ссылкой на новость о финансировании и датой.- Визуализация суммы финансирования — горизонтальный график топ-компаний по раскрытой сумме; свободный текст сумм (“$120M”/“€1.5B”) парсится в величину новым
parseAmount. 3 новых ключа i18n × 17 локалей.
Примечания
- По-прежнему только чтение через
GET /api/company-funded; описания и вилки зарплат в ленте финансирования нет. Набор: 2363 тестов (+2).
[1.140.0] — 2026-08-12
Полезная статистика: более богатые данные по зарплатам — разбивка зарплат во вкладке «Мой пайплайн» на #/stats теперь показывает среднее (не только медиану), переключатель в год ⇄ в месяц и таблицу мин · сред · медиана · макс по странам. Первый кусок Фазы 3. Правка UX по репорту пользователя (без parent-sync).
Добавлено
- Среднее (mean) значение зарплаты —
RoleStats.salaryStatsтеперь возвращаетavgUsdвместе сminUsd/medianUsd/maxUsd. Медиана устойчива к выбросам, среднее показывает перекос — вместе они читаются как распределение. - Переключатель в год ⇄ в месяц и таблица мин · сред · медиана · макс по странам в разделе зарплат, с учётом выбора валюты и периода. 8 новых ключей i18n × 17 локалей.
Примечания
- Цифры по-прежнему берутся только из вакансий с распознаваемой зарплатой и нормализуются к USD (ориентировочно). Набор: 2361 тестов (+1).
[1.139.0] — 2026-08-12
Понятность: подсказки ? — переиспользуемая CSP-безопасная кнопка ?, которая по клику объясняет «что это / как работает / чего ожидать» на вашем языке. Правка UX по репорту пользователя (без parent-sync).
Добавлено
- Всплывающая подсказка
?(window.HelpHint) — круглая?рядом с заголовком открывает лёгкое, зависящее от темы и зеркалируемое в RTL всплывающее окно, которое отрисовывает локализованное пояснение черезUI.md(); доступно (role="tooltip",aria-expanded, закрытие по Escape/клику вне, возврат фокуса) и CSP-безопасно. ?на 5 вкладках#/statsи на 8 заголовках ИИ/аналитических экранов (career-plan, ориентация, two-pager, нетворкинг, мок-интервью, память, funded, еженедельная сводка) — 14 новых ключей i18n × 17 локалей.
Примечания
- У всех экранов уже был однострочный подзаголовок;
?добавляет более глубокое пояснение по требованию и делает пустые состояния самоочевидными. Набор: 2360 тестов (+4).
[1.138.0] — 2026-08-12
Генерация на языке интерфейса — каждая AI-генерация теперь отвечает на языке, выбранном в интерфейсе, плюс усиление тестов по итогам ревью. Правка UX по репорту пользователя (без parent-sync).
Изменено
- AI-генерации теперь учитывают язык интерфейса. Если интерфейс на русском, испанском, японском и т. д. — сгенерированный текст возвращается на этом языке, а не всегда на английском. Директива языка вывода проброшена во все эндпоинты генерации — карьерный план, ориентацию, отчёт по рынку, мок-интервью, план нетворкинга, «спросить документацию», предложение заметки памяти и черновик two-pager. Код и идентификаторы остаются на английском (например, ключи YAML в two-pager); локализуются только проза, заголовки и пункты.
Исправлено
- Страж CSS-ролей цвета (
tests/css-role-tokens.test.mjs) — статический канарей, что алиас-токены тёмной темы из v1.137.0 никогда не меняют роль: текстовые токены (--fg/--danger/--ok/…) никогда не используются какbackground, а токены поверхностей (--card/--panel/--line/…) — как текстовыйcolor, во всём CSS и inline-стилях SPA. - Самопроверка XSS-загрузчика
UI.md()— тест, который через vm загружаетmd()изapi.js, теперь сразу после извлечения проверяетmd('<script>…')и бросает ошибку, если экранирование пропало, — так будущий сбой нарезки падает громко, а не оставляет security-набор зелёным на усечённой функции. - Страж прокрутки на
#/career-plan—scrollIntoViewпосле генерации выполняется только если превью всё ещё присоединено к документу.
Примечания
docs/UX-ROADMAP.mdобновлён: подсказки?+ описания страниц + пустые состояния теперь v1.139.0; провайдер Nous Research / Hermes — с гайдом по развёртыванию на облачном сервере + Telegram и скиллом Hermes — отслеживается как Фаза 5 / 5b.- Набор: 2356 тестов (+5).
[1.137.0] — 2026-08-11
Исправления читаемости и отображения — контраст в тёмной теме, подписи графиков и карьерный план. Правка UX по репорту пользователя (без parent-sync).
Исправлено
- Белый текст на белом / чёрный на чёрном в тёмной теме на многих экранах — пятнадцать пользовательских CSS-свойств, на которые ссылались несколько видов (
--fg,--panel,--panel-2,--ok,--danger,--card, …), никогда не были объявлены, поэтому они откатывались на захардкоженные светлые/чёрные значения: в светлой теме всё было нормально, а в тёмной — нечитаемо (чипы обзора на#/pipeline, активная вкладка на#/stats, «Активный / Ключей» + “✓ set” на#/config, разделы#/two-pager, пузырь вопроса на#/mock-interview, текст ошибок). Теперь они псевдонимизированы на настоящие тема-зависимые токены, поэтому автоматически следуют за темой — 0 нарушений контраста WCAG-AA во всех 29 видах, подтверждено автоматическим аудитором; активная вкладка на#/configперешла на читаемый затонированный стиль. Регрессионная защита (tests/dark-theme-tokens.test.mjs) удерживает их псевдонимизированными. - Подписи графиков на
#/statsобрывались посреди слова («Senior Backend Engineer» → «…Enginee») — теперь они обрезаются с эллипсисом, а полная подпись сохраняется в tooltip при наведении. #/career-planпоказывал сгенерированный план как необработанный Markdown — теперь он автоматически отрисовывается как форматированный, читаемый текст (редактируемый Markdown остаётся в текстовом поле; кнопка «Предпросмотр» переключает его отображение).
Примечания
#/career-plan,#/two-pager,#/statsи еженедельный дайджест интервью не сломаны — они показывают пустые состояния до тех пор, пока вы не сгенерируете план / не появятся данные. Более понятные подсказки на странице и хелп-подсказки?запланированы следующим шагом (docs/UX-ROADMAP.md).
[1.136.0] — 2026-08-11
Паритет с родительским career-ops v1.26.x (после мейнлайна v1.26.0) — один новый источник сканирования без авторизации, а также волна портов качества и надёжности в код, зеркалируемый web-ui. Реестр теперь насчитывает 79 источников = 74 EN + 5 RU (ALL_ADAPTERS 74).
Добавлено
eightfold(Eightfold AI, #2684) — доски по найму талантов через API без авторизацииhttps://<tenant>.eightfold.ai/api/apply/v2/jobs, закреплённый по хосту*.eightfold.ai(фирменный CNAMEcareers.<company>.comнамеренно отклоняется); с пагинацией и защитным лимитом, dead-board-throw, дедупликацией URL. Источник + адаптер + CI-изолированный сьют; появляется в фильтре Source на#/scanи на лендинге.
Исправлено
- Unicode-совместимые ключи дедупликации и сопоставления ролей (#2569 / #2587 / #2667) — новая общая
normalizeTextKey(NFKC, сохраняет буквы/диакритику/цифры любой письменности) заменяет ключи, работавшие только с ASCII:detect-repostsтеперь группирует варианты компаний с разной шириной символов/пунктуацией («Acme, Inc.» ≡ «Acme Inc») и никогда не схлопывает разных не-латинских работодателей в один, аrole-matcherсворачивает заголовки полной ширины и сохраняет не-латинские токены роли, вместо того чтобы их стирать. fetchJsonWithRetryбольше не повторяет запрос при отклонённом редиректе (#2657) — срабатывание guard’аredirect:'error'на 3xx детерминировано, поэтому теперь оно не подлежит повтору и приводит к быстрому отказу вместо расхода бюджета повторов.- AND-группы в
title_filter.positive(#2552) — разделённое пробелами+внутри позитивной записи теперь требует, чтобы каждый термин встречался в заголовке, в любом порядке. oraclecloudпринимает пронумерованные apex-домены тенантовoraclecloud1.com … oraclecloud99.com(#2683) — ограниченное семейство (без ведущего нуля, ≤ 2 цифр), никогда не wildcard-apex.workableукреплён (#2675) — повторы, заголовки, имитирующие браузер, и сериализация запросов против хоста, защищённого Cloudflare.personioоткатывается на HTML-скрейп, когда XML-фид отключён, вместо того чтобы возвращать пустой результат.- Алиасы FALLBACK в
statesпересинхронизированы с родителем (#2615).
Примечания
- Не портировано (не зеркалируется в web-ui или относится только к CLI): reply-matcher (#2672), jd-similarity (#2661), jd-skill-gap (#2686), переопределяемые через env пути скана (#2568) / разбор
--flag=value(#2589), а также изменения cover-letter / CV-template / doctor / ollama / generate-pdf. HIGH-уязвимостиjs-yaml/nanoidв веб-компоненте уже были исправлены в web-ui v1.135.0.
career-ops-ui v1.267.1 · page 6/13 · mirror of the project changelog for cvstart.org