Журнал змін
GitHubКожна значуща зміна, реліз за релізом. Той самий файл постачається в репозиторії та перекладений усіма 19 мовами.
[1.214.0] — 2026-08-25
Виправлено — описи вакансій Greenhouse, Ashby і Recruitee тепер потрапляють до контент-фільтра, тож фільтри за словами / країною / візою реально збігаються з цими дошками; а “Associate <старший титул>” (включно з академічними званнями) більше не відносять до entry-level.
Виправлено
- Контент-фільтри тепер працюють на Greenhouse, Ashby і Recruitee. Ці дошки тепер несуть повний текст кожної вакансії як її опис, тож
content_filter(або фільтр за словами країни / візи) збігається з тілом вакансії, а не пропускає дошку наосліп. Ashby і Recruitee віддають тіло в списковому payload безкоштовно; Greenhouse уже це робив. Усі три очищаються одним спільним конвеєром HTML→текст. - “Associate Director” / “Associate Professor” — це senior, а не entry-level. Класифікатор рівня (використовує
skip_tiers:) вважав слово “associate” міткою entry-level, навіть коли воно передує старшому титулу. Тепер він читає “Associate <старший іменник>” — включно з академічними званнями (Associate Professor, Associate Dean) — як senior, зберігаючи справжні junior-варіанти (Associate Attorney, Associate Editor) як entry-level, і досі відносить провідний “Intern, Associate Dean” до стажування.
Примітки
- Набір тестів: 2736. Джерела сканування без змін — 82. У черзі на потім: опціональне збагачення описів SmartRecruiters (запит деталі на кожну вакансію), пропуск дощок
aggregator: trueу detect-reposts і префікси заголовків із якірним словомstem:/word:— кожне опціональне налаштування, а не зміна поведінки за замовчуванням. Захист від DNS-rebinding досі в черзі на окремий реліз безпеки.
[1.213.0] — 2026-08-22
Додано — MyCareersFuture, національний банк вакансій Сінгапуру, як джерело сканування. Виправлено — вакансії Greenhouse тепер несуть повний текст, щоб контент-фільтри працювали, а віддалені вакансії Ashby більше не ховаються за локацією «лише місто».
Додано
- MyCareersFuture (Сінгапур) (mycareersfuture.gov.sg) — нове джерело сканування без токенів для національного державного банку вакансій Сінгапуру (Workforce Singapore). Виберіть його у фільтрі Джерело на
#/scanабо додайте компанію зprovider: mycareersfutureта необовʼязковим спискомkeywords(інакше беруться цільові ролі з профілю, як у Job Bank). Читає публічний пошуковий API, з привʼязкою до хоста, без ключа.
Виправлено
- Вакансії Greenhouse тепер можна фільтрувати за контентом. Дошки Greenhouse запитуються з повним тілом вакансії, декодованим у простий текст як опис — тож
content_filter(або фільтр за словами країни/візи), що читає опис, тепер справді збігається з вакансіями Greenhouse, а не пропускає їх наосліп. - Віддалені вакансії Ashby більше не відсікаються фільтром міста. Ashby тримає модель роботи (Remote/Hybrid/Onsite) окремо від міста офісу, тож повністю віддалена вакансія все ще читалася як, наприклад, «San Francisco» — і фільтр локації, що блокує це місто, приховував доступну вакансію. Тепер за віддаленої роботи до локації додається «Remote», а
workplaceTypeважливіший за застарілий прапорецьisRemote, щоб офісно-прив’язану гібридну вакансію не позначили хибно.
Примітки
- Джерела сканування: 82 (77 англійських + 5 російських). Набір тестів: 2724. Захист від DNS-rebinding (перевірка розвʼязаної адреси хоста перед підключенням) відкладено в окремий реліз — йому потрібен власний дизайн для web-ui, а не прямий порт.
[1.212.1] — 2026-08-21
Виправлено — лендинг cvstart.org недорахував джерела сканера (показував 80 і губив Job Bank (Канада)); тепер знову збігається з 81 у застосунку, а збірка сайту гучно падає, якщо ці числа розходяться.
Виправлено
- Лічильник «Джерела вакансій» на лендингу знову синхронний із застосунком. Після v1.212.0 cvstart.org показував 80 дощок, і бракувало нового чипа Job Bank (Канада), тоді як застосунок, випадний список сканування та довідка перелічували 81. Лендинг будує список, завантажуючи живий реєстр сканера, і одне джерело не завантажилося в тій збірці через те, як воно підтягувало YAML-залежність — тож було мовчки відкинуте. Тепер Job Bank завантажує цю залежність ліниво, так само як решта застосунку робить під час скану, тому він завжди присутній.
- Збірка сайту тепер відмовляється публікувати неузгоджений лічильник джерел. Якщо реєстр перелічить менше джерел, ніж є на диску (ознака джерела, що не завантажилося), збірка падає з чітким повідомленням замість тихої публікації хибного числа.
Примітки
- Поведінка застосунку не змінилася — у сканері завжди були всі 81 джерело; зачепило лише лендинг. Джерела сканування: 81 (76 англійських + 5 російських) — без змін. Набір тестів: 2687.
[1.212.0] — 2026-08-21
Додано — Job Bank (Канада), федеральна національна дошка вакансій. Вилучено — EchoJobs (його фід тепер за бот-захистом). Виправлено — дошки на Consider знову повертають результати, а мульти-локаційні вакансії Lever більше не приховують половину локацій.
Додано
- Job Bank (Канада) (jobbank.gc.ca) — нове джерело сканування без токенів для федеральної національної служби зайнятості Канади, високонавантаженої дошки, яку погано покриває будь-який агрегатор. Виберіть його у фільтрі Джерело на
#/scanабо додайте компанію зprovider: jobbankcaта необовʼязковим спискомkeywords(інакше беруться цільові ролі з вашого профілю). Читає публічний ATOM-фід, з привʼязкою до хоста, без ключа.
Вилучено
- EchoJobs — виведено. Його публічний фід тепер за бот-захистом і нічого не повертає, тож тримати його — лише марнувати слот сканування.
Виправлено
- Дошки на Consider знову повертають результати. Consider тепер вимагає анонімний handshake (GET, що задає сесійну куку + CSRF-токен) перед прийняттям пошукового запиту; без нього запит мовчки відхилявся, і дошка виглядала порожньою.
- Мульти-локаційні вакансії Lever більше не приховують половину локацій. Lever кладе одне основне місто в
location, а решту — уallLocations; читання лише основного робило вакансію, відкриту в Барселоні І Монтевідео, «лише-Барселона» (і її помилково відсікав фільтр локацій). Тепер обидва обʼєднуються.
Примітки
- Мʼякший темп між сторінками (250 мс замість 150) на посторінкових дошках — із ввічливості до одногостових careers-сайтів. Джерела сканування: 81 (76 англійських + 5 російських) — без змін (Job Bank додано, EchoJobs вилучено). Набір тестів: 2685.
[1.211.0] — 2026-08-19
Додано — Yourator, тайванська tech-дошка вакансій. Виправлено — акцентовані сутності в заголовках/назвах компаній тепер декодуються всюди, а компанія з акцентом у назві більше не позначається помилково.
Додано
- Yourator (yourator.co) — нове джерело сканування без токенів для tech- та digital-ринку вакансій Тайваню. Виберіть його у фільтрі Джерело на
#/scanабо додайте компанію зprovider: yourator. Воно читає публічний JSON API (без ключа, без браузера), обходить усі сторінки дошки й видає справжнє посилання роботодавця (його власний ATS) з вирізаними трекінг-параметрами.
Виправлено
- Акцентовані іменовані сутності тепер декодуються всюди. Спільний HTML-декодер отримав латинські літери Latin-1 (
é→ é,ç→ ç, …), тож європейська дошка, що пишеDéveloppeurчиFrançais, більше не лишає цей літерал у заголовку, трекері чи згенерованому документі. (Великі лишаються великими —Éце É, а не é, — а пошук на кшталт&constructor;тепер розвʼязується в себе.) - Компанія з акцентом у назві більше не позначається помилково за перебування на власному домені. «Işık» тепер згортається в «isik» і збігається з isik.com.tr; «Société Générale» збігається з societegenerale.com. Стара перевірка видаляла акцентовані літери замість згортати їх до ASCII-основи.
Примітки
- Джерела сканування: 81 (76 англійських + 5 російських). Набір тестів: 2667.
[1.210.1] — 2026-08-19
Виправлено — заголовки вакансій і назви компаній з «&» чи лапками з Habr Career більше не надходять спотвореними.
Виправлено
- Джерело Habr Career тепер декодує HTML-сутності в заголовку та назві компанії, перш ніж вони підуть далі. Серверні картки надходять екранованими («Changellenge >>», «Demand Forecasting & Inventory Optimization», «ООО "М-ТЕХ"»), тож недекодований «&» тихо не проходив ваш же фільтр заголовка з «&» — саме той симптом, який минулий реліз закрив на п’яти інших дошках, — а назви компаній доїжджали до трекера й звітів кривими. Декодування сутностей тепер завершене на всіх шести уражених джерелах.
Примітки
- Набір тестів: 2644.
[1.210.0] — 2026-08-19
Додано — Senjob, перша африканська дошка вакансій сканера (Сенегал); точніше зіставлення заголовків ще на п’яти дошках.
Додано
- Senjob (senjob.com) — нове джерело сканування без токенів для Сенегалу, перша африканська дошка сканера. Виберіть його у фільтрі Джерело на
#/scanабо додайте компанію зprovider: senjob. Воно читає публічний список звичайним HTTP (без ключа, без браузера), прив’язує кожен запит до senjob.com і — оскільки парсить HTML — вважає список, що раптом дав порожньо, зламаною дошкою (видимою помилкою), а не країною без вакансій.
Виправлено
- Заголовки з «&» більше не втрачають вакансії на п’яти дошках — на beesite, Cornerstone (csod), Hacker News «Who is hiring», Phenom і TKMS заголовки надходять з HTML-екрануванням, тож екранований «&» у посаді на кшталт “R&D Engineer” не проходив ваше ж ключове слово “r&d”, і вакансія тихо зникала (вето “sales & marketing” теж не спрацьовувало). Тепер заголовки — і локації Phenom — декодуються перед фільтруванням.
Примітки
- Джерела сканування: 80 (75 англійських + 5 російських). Набір тестів: 2643.
[1.209.0] — 2026-08-17
Додано — вбудована довідка тепер описує, як записати підсумок відгуку, а «Запитати довідку» підкаже, де це.
Додано
- У довідці Трекера (§11) усіма 17 мовами зʼявився розділ «Записати підсумок», що проводить по кнопці Підсумок: оберіть, що сталося (відмова / оффер / найнято / відхилено / ігнор / пройшов на співбесіду), перегляньте, що буде зроблено, і запишіть — це фіксує результат, архівує надіслані вами резюме та супровідний лист і синхронізує Статус рядка за вас. Плаваючий помічник «Запитати довідку» читає цей посібник, тож тепер веде вас до цієї кнопки, а не лише пропонує правити Статус вручну.
Примітки
- Кожен довідковий бандл тепер 31 H2 / 119 H3 (було 118); вартові паритету оновлено. Лише документація — без змін коду й поведінки. Набір: 2625.
[1.208.2] — 2026-08-16
Виправлено — на телефоні кнопки сповіщень і теми більше не лежать поверх поля пошуку.
Виправлено
- У v1.208.1 кнопки верхньої панелі перестали накривати заголовок сторінки, але на вузькому — хоч і не найвужчому — екрані, особливо в мовах із довшими підписами, уся панель усе одно втискалася в один рядок, тож кнопки 🔔 і 🌙 могли лягти поверх поля пошуку. Тепер кнопки дій (сповіщення, тема, Діагностика, Відкрити Scan) на телефоні завжди переходять на власний другий рядок на всю ширину, тож поле пошуку видно повністю і ніщо не перекривається.
Примітки
- На телефоні кнопки дій панелі переходять на другий рядок на всю ширину, прибираючи крихку смугу «майже повного рядка», де розкладка розподіляла залишок відʼємного місця як накладання. Playwright-вартовий тепер відтворює точний тригер — локаль із довгими підписами в смузі 565–640px — і перевіряє, що елементи панелі не ділять пікселі. Набір: 2621.
[1.208.1] — 2026-08-16
Виправлено — на телефоні кнопки верхньої панелі більше не накривають сторінку.
Виправлено
- У v1.208.0 кнопки верхньої панелі (Діагностика, Відкрити Scan, сповіщення, тема) переносилися на другий рядок на вузьких екранах, але панель зберігала фіксовану висоту, тож перенесений рядок вилазив і лягав поверх заголовка сторінки. Тепер панель росте під свої рядки, а контент іде під нею.
Примітки
- Фіксована
heightпанелі сталаmin-height, тож вона росте під контент на будь-якій ширині (десктоп без змін). Playwright-вартовий тепер також перевіряє, що панель не вилазить поверх сторінки. Набір: 2621.
[1.208.0] — 2026-08-16
Виправлено — застосунок тепер вміщається на екрані телефона: горизонтальне прокручування зникло.
Виправлено
- На вузькому екрані весь застосунок їхав убік — верхня панель, таблиці, статті довідки й вкладки налаштувань вилазили за правий край. Тепер кожна сторінка вміщається на будь-якій ширині: кнопки верхньої панелі переходять на другий рядок, широкі таблиці й блоки коду прокручуються всередині своєї рамки, довідка ставить зміст над статтею, ряди кнопок і вкладок переносяться, а довгі шляхи чи URL переносяться, а не розтягують сторінку.
Примітки
- Причина — класична пастка flex/grid min-width: auto плюс кілька необгорнутих широких елементів; виправлено через
min-width: 0на grid-елементах,overflow-wrapна markdown/заголовках, прокручувану markdown-таблицю та перебудову сітки довідки на мобільному брейкпоінті. Playwright-вартовий перевіряє 0 горизонтального переповнення на 375 px на основних маршрутах.tests/playwright-smoke.mjs. Набір: 2621.
[1.207.2] — 2026-08-16
Виправлено — AI-плани та профілі карʼєрної орієнтації більше не показуються як сирий дамп коду.
Виправлено
- Деякі моделі загортають усю відповідь в код-огорожу
markdown …. Коли так ставалося, план розвитку і профіль орієнтації показувалися моноширинним блоком коду замість документа із заголовками та списками. Тепер обгортальну огорожу знято — лише якщо вона охоплює всю відповідь і мова явноmarkdown/md, тож справжня відповідь наpython/js/без мови ``` лишається недоторканою.
Примітки
- Оброблено один раз у спільному кроці очищення LLM (
cleanLlmMarkdown), тож виграють усі AI-маршрути, а вкладені блоки коду всередині відповіді зберігаються.tests/llm-output.test.mjs(+3). Набір: 2621.
[1.207.1] — 2026-08-16
Виправлено — на маленьких телефонах лендинг більше не вилазить убік.
Виправлено
- На вузькому телефоні перший екран — заголовок, вступний рядок і термінал встановлення — міг обрізатися по правому краю, бо довга команда встановлення та колонки верстки не стискалися під екран. Тепер усе вміщається на будь-якій ширині; команда встановлення прокручується всередині свого термінала.
Примітки
- Також зміцнено нестабільну E2E-перевірку, яка могла падати через тимчасовий 404 ресурсу: тепер вона ігнорує нешкідливий мережевий шум (favicon / зʼєднання / невдалий ресурс), як і сусідні перевірки, але й далі ловить справжні помилки скриптів. Поведінка застосунку не змінилася. Набір: 2618.
[1.207.0] — 2026-08-15
Додано — записуйте підсумок відгуку прямо з трекера.
Додано
- У кожному рядку трекера зʼявилася дія Підсумок: оберіть, що сталося (відмова, офер отримано, найнятий, офер відхилено, немає відповіді, пройшов на співбесіду), додайте необовʼязкову примітку, перегляньте підсумковий статус і запишіть. Запис архівує надіслані артефакти CV та супровідного листа й синхронізує трекер до канонічного стану — одна детермінована дія замість ручного редагування.
Примітки
- Нова
POST /api/outcomeретранслює CLI підсумків:dryRun:true— перегляд лише для читання (знаходить рядок, повідомляє підсумковий стан, нічого не пише); реальний виклик записує. Безпека запису: тип підсумку обмежено відомим набором, а кожне текстове поле відхиляється за керуючих символів до виклику (аргументи-масив, spawn — без оболонки).tests/outcome-route.test.mjs. Набір: 2618.
[1.206.0] — 2026-08-15
Документація — вбудований довідник тепер описує п’ять найновіших функцій усіма 17 мовами.
Додано
- Вбудований довідник — і асистент «Запитати довідку», що відповідає на його основі — тепер описує п’ять нещодавно доданих функцій: Доктор налаштування (Налаштування — перевіряє CV і профіль на прогалини та залишені демодані), Пошук ATS-дощок (Портали — автоматично знаходить карʼєрну дошку компанії), перевірку «ще відкрита?» (Трекер — чи вакансія ще відкрита), підказку «повторно використати попереднє CV?» (CV Studio — сигналізує, коли вже адаптоване CV пасує до нової вакансії) та Журнал навичок (Аналітика — запис оцінок самоперевірки). П’ять нових підрозділів, перекладених усіма 17 мовами.
Примітки
- Структура довідника зросла до 31 H2 / 118 H3, паритет гарантовано для кожної мови. Оновлено довідкову документацію:
docs/architecture/API.mdописує п’ять маршрутів цих функцій, а лічильники маршрутів/версії уCLAUDE.mdтаdocs/sdd/CONVENTIONS.mdактуальні (36 модулів маршрутів). Набір: 2610.
[1.205.0] — 2026-08-15
Додано — журнал навичок для запису результатів тестів/оцінювань.
Додано
- Новий Журнал навичок (Аналітика → Журнал навичок) дає змогу записати самооцінку — компанія, платформа, навичка, оцінка % та необовʼязкова примітка — додається до
data/assessments.tsv, зі списком минулих записів (нові згори). Без токенів, детерміновано; формат файлу веде CLI батьківського проєкту.
Примітки
- Новий
GET /api/assessments(ретранслює типовий список JSON зassessment-log.mjs; мʼяка відмова{available:false}) +POST /api/assessments(явний запис: поля передаються як аргументи-масив доassessment-log.mjs add). Безпека запису: будь-яке текстове поле з керуючими символами відхиляється (TAB зламав би стовпець, перенесення рядка вставило б рядок) → 400 до запису; оцінка/поріг 0–100, довжини обмежені.tests/assessments-route.test.mjs. Набір: 2610.
[1.204.0] — 2026-08-15
Додано — панель «Доктор налаштування» в Налаштуваннях, що позначає незаповнені чи демо-дані CV/профілю.
Додано
- Налаштування → Доктор налаштування тепер без токенів перевіряє ваші
cv.mdіconfig/profile.ymlта перелічує блокуючі проблеми (відсутні файли/поля) і попередження (залишені демо/шаблонні дані, зашиті метрики) — щоб виявити неповне налаштування, перш ніж воно послабить скани й адаптацію. Лише читання; перезапуск одним кліком.
Примітки
- Новий route лише для читання
GET /api/cv-sync-checkретранслюєcv-sync-check.mjsз батьківського проєкту, який друкує текст + код виходу (без--json); route легко структурує його стабільні рядкиERROR:/WARN:у{ok, errors[], warnings[]}— успіх визначає банер, а не код виходу. М’яка відмова{available:false}в автономних установках.tests/cv-sync-check-route.test.mjs. Набір: 2602.
[1.203.0] — 2026-08-15
Додано — підказку «повторно використати попереднє резюме?» у CV Studio.
Додано
- Коли ви відкриваєте збережену вакансію в CV Studio, застосунок тепер порівнює її з іншими вашими збереженими вакансіями (детерміноване перекриття слів, без токенів) і підказує, чи найближча схожа настільки, щоб повторно використати те адаптоване резюме, використати його з правками чи готувати нове — щоб не створювати з нуля для ролі, над якою ви вже працювали.
Примітки
- Новий route лише для читання
GET /api/jds/:name/reuseретранслюєjd-similarity.mjsз батьківського проєкту (перекриття Жаккара + перевірка сеніорності; JSON{decision, score, reason}) по разу на кожну попередню вакансію (fan-out обмежено 25, береться найкраще); м’яка відмова{available:false}, якщо скрипта чи попередніх вакансій немає.tests/jd-similarity-reuse-route.test.mjs. Набір: 2594.
[1.202.0] — 2026-08-15
Додано — знайдіть ATS-дошку компанії просто на #/portals і почніть відстежувати її.
Додано
- На #/portals введіть назву компанії — застосунок перевірить Greenhouse, Ashby і Lever на її публічну дошку (без LLM, без браузера) і покаже дошки, які існують і зараз мають ≥1 вакансію. Один клік додає обрану дошку до компаній, за якими стежить сканер. Перевірка лише для читання; запис у
portals.ymlвідбувається тільки після натискання Додати.
Примітки
- Новий
server/lib/discover-ats.mjs(проба слага з фіксованим хостом і перевіркою charset черезsafeGetіз закріпленим DNS, ≤12 проб на запит) +POST /api/portals/discover(лише читання) таPOST /api/portals/track(явний запис:withFileLock+ текстова вставка + перевірка повторного парсингу + атомарна заміна; лише відомі ATS-хости, ідемпотентно). Повторно використовує реєстр адаптерів сканера. i18n ×17.tests/discover-ats-resolver.test.mjs+tests/discover-ats-route.test.mjs. Набір: 2588.
[1.201.0] — 2026-08-15
Виправлено — трекер із локалізованими або альтернативними заголовками стовпців більше не відображається порожнім.
Виправлено
- Якщо ваш
data/applications.mdвикористовує неанглійські чи альтернативні заголовки — іспанськіempresa/puesto/estado/fecha/enlace, абоposition/stage/link— трекер читав їх за неправильними ключами й показував порожні стовпці Компанія / Роль / Статус / Дата / Посилання. Тепер такі заголовки згортаються до канонічних імен полів, і трекер відображається правильно. Англомовний трекер обробляється точно як раніше.
Примітки
- Нова карта
HEADER_ALIASESі згортка нормалізації вparseApplications(server/lib/parsers.mjs); невідомі або вже канонічні заголовки проходять без змін.tests/tracker-header-aliases.test.mjs. Набір: 2563.
[1.200.0] — 2026-08-15
Додано — перевірка «ще активна?» для вакансій на ATS одним кліком у трекері.
Додано
- На #/tracker відгук, чий URL — вакансія Greenhouse / Lever / Ashby / Workday / SmartRecruiters, тепер має кнопку «Ще активна?». Один клік запитує публічний JSON самого ATS — без токенів, без браузера — і показує Активна / Закрита / Невідомо, щоб знаходити мертві вакансії, не відкриваючи кожну. Консервативно за замовчуванням: лише явний 404/410 читається як Закрита; усе неоднозначне лишається Невідомо (ніколи хибної Закрита).
Примітки
- Нові
server/lib/liveness-core.mjs+liveness-api.mjsі route лише для читанняGET /api/liveness?url=(без записів, без LLM). Безпечно щодо SSRF: URL проходитьisValidJobUrl, далі до API ATS звертаємось лише черезsafeGet(із закріпленим DNS), з фіксованим хостом і перевіреними сегментами.tests/liveness-core.test.mjs+tests/liveness-route.test.mjs. Набір: 2557.
[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
Змінено (НИЗЬКЕ, дизайн-система) — токени типографічної шкали + шари 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
Додано (НИЗЬКЕ) — чесні підказки 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
Додано (НИЗЬКЕ) — 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
Виправлено (НИЗЬКЕ, 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
Виправлено (НИЗЬКЕ, дизайн-система) — підняті поверхні тепер відділяються від розділювачів (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
Виправлено (НИЗЬКЕ) — термінологія рубрики тепер збігається з канонічною документацією. 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
Виправлено (НИЗЬКЕ) — термін «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
Виправлено (НИЗЬКЕ) — плейсхолдер пошуку у верхній панелі більше не переповнюється жодною мовою. «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
Виправлено (НИЗЬКЕ) — вбудований асистент «Запитати документацію» тепер охоплює експорт звіту в 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
Виправлено (СЕРЕДНЄ) — «?»-підказка тепер ≥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
Виправлено (СЕРЕДНЄ) — #/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).
Виправлено
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).
career-ops-ui v1.267.1 · page 5/12 · mirror of the project changelog for cvstart.org