Журнал змін
GitHubКожна значуща зміна, реліз за релізом. Той самий файл постачається в репозиторії та перекладений усіма 19 мовами.
[1.260.0] — 2026-10-26
Потяг зміцнення після ревізій: вісім злиттів (#491–#498) з раундів opus-5-5 та AI-review поверх флоту v1.259.0 — нормалізація зарплат, обмеження області дії виправлення дат звітів, стійкість геокодування, cache-busting бандла r2+r3, виправлення переповнення на #/deep — плюс два UI v2 (pipeline, reports). Кожне виправлення приїхало через червоно-зелений fix-forward.
Виправлено
- Нормалізація порівняння зарплат (CAR-143 rev-2): маркери періоду розпізнаються лише поруч із числом, до якого вони належать — «6 month contract, $120k» більше не перетворюється на річну ×12, а форми періоду RU/ES, що визнаються лише при приєднанні до числа («за месяц» поруч із сумою), перемагають випадкові слова; родина символів
$/¥розпізнається, але не відображається на валюту, доки в тому ж рядку немає явного ISO-коду (долари AUD/CA/SG більше не зараховуються як USD, а форма ¥ RMB отримує явне застереження замість конвертації в JPY з помилкою ~20×); голі числа 19xx/20xx — завжди роки; числа діапазону успадковують період сусідньої межі; профільніminimumіtarget_rangeніколи не зшиваються в один діапазон. - Обмеження області дії fixReportDate (CAR-144 rev-2): серверно закріплене виправлення дати застосовується лише до преамбули перед першим заголовком
##(звіт із заголовком-H2 отримує консервативний no-op — застаріла дата прийнятна, вигадана — ні); усередині преамбули: унікальний кандидат виправляється, інакше — унікальний жирний**Date:**, інакше — нічого (два жирні або два звичайні кандидати — нічого не перезаписується). Гейт PDF-міток FIX-5 тепер читає реальний складений словник — відсутня локаля кидає помилку замість мовчки проходити з англійським fallback. - Чип локації Habr + стійкість геокодування (CAR-142 rev-2): мета-чип Habr стає містом лише за структурних доказів (посилання на фільтр міста або іконка локації) — повідомлення про релокацію («Возможна релокация») і типи зайнятості ніколи не видають себе за локації; кеш геокодування тепер переживає рестарт (скидання на диск при
beforeExit/exit/SIGTERM+SIGINT з повторним підняттям сигналу, брудний прапорець очищається лише після успішного запису), завершення роботи обмежене в часі (засувка exit-циклу, лічильник генерацій, LRU за свіжістю), а вичерпаний бюджет геокодування відповідає 503 + Retry-After замість загального 502. - Cache-busting бандла (CAR-145 r2+r3): оболонка посилається на
/js/bundle.js?v=<content-hash>— immutable при збігу, no-store при розбіжності — чим убито 5-хвилинне вікно розгортання, у якому свіжа оболонка поєднувалася із закешованим старим бандлом. Хеш і потік читання спільно використовують один файловий дескриптор (без розірваних читань); коротке читання віддає null замість fallback-хешу зі stat, який міг отруїти ціле розгортання;make-bundleзаписує identity та компаньйони .br/.gz через temp із крапкою на початку + rename, тож убитий білд не може ані віддати напівзаписані байти, ані залишити свій тимчасовий файл. - Переповнення на #/deep (CAR-146): збережений рісьорч-бриф, у якого сама назва файлу перевищує в’юпорт телефона, більше не може витіснити
#/deepза 390px — картка обмежується своїм рядком, назва переноситься, дата лишається в одному рядку; обхід вузьких в’юпортів тепер проходить#/deepз навмисно наддовгим підсіяним фіксчуром (єдина знахідка двох прогонів remote-QA — закрита). - Зміцнення pipeline v2 (ревізія CAR-116): видалення з підтвердженням та undo (з чесним текстом undo), картки більше не вигадують компанію з хоста борду вакансій, FNV-1a id рядків, справжні масові підрахунки та охорона від повторного входу для масових операцій.
Додано
- Pipeline v2 (CAR-116): збагачені картки, групований вид і масове відхилення з undo; 17 нових ключів
pipe.*постачилися одразу перекладеними в усі 19 словників. - Reports v2 (CAR-118): живий пошук із фільтрами за смугою оцінки / легітимністю / компанією / датою, сортувальні заголовки (
aria-sort), персистентний картковий вид з кнопками дій за смугою, гістограма оцінок із фільтрацією кліком та уніфікований рендеринг «Company — Role» / «x.x/5» (window.ReportOps); 31 ключrep2.*постачилися одразу перекладеними в усі 19 словників.
Примітки
- Кожне виправлення пройшло раунд ревізії з червоно-зеленими доказами; дві HIGH-знахідки AI-review на першому r2 бандла (хеш при короткому читанні, атомарність компаньйонів) закриті r3, а раунди CAR-143/CAR-144/CAR-142 кожен повторно перевірив відкриті знахідки свого попередника.
- Навмисно не зроблено: кластерний вид мапи лишається CAR-125 (у черзі); серверне відновлення метаданих pipeline — окремий тікет; без ретроактивних виправлень для контенту, збереженого в станах v1.259.0 до rev-2.
- Тести 5988 (+165); браузерний набір — 152 пройшли / 0 провалено / 1 опційний пропуск. Джерел сканера не торкнуто (реєстр 121 = 116 EN + 5 RU · 116 адаптерів); 48 нових i18n-ключів приїхали зі своїм ×19 розкладанням всередині самих фіче-PR.
[1.259.0] — 2026-10-25
Другий потяг агентного флоту, шість виправлень: дати звітів закріплюються сервером, геокодер мапи кешує результати й читає RU-міста, довідка ×18 цитує локалізовані підписи кнопок, речення інтерфейсу перестають змішувати мови, порівняння зарплат рахуються на сервері — а JS-бандл постачається заздалегідь стиснутим (холодне завантаження 3.1 MB → 598 KB).
Виправлено
- Вигадані дати звітів (CAR-101): промпт оцінювання тепер закріплює сьогоднішню дату серверно («поле Date: МАЄ дорівнювати рівно
— скопіюй дослівно; ніколи не вигадуй»), а fixReportDateперезаписує збиту дату на сьогодні в кожному місці приймання звіту (гілка Anthropic, сабпроцес Gemini, OpenAI-сумісний хвіст, /api/deep, auto-pipeline) із попередженням + записом у журнал активності — це виправлення, а не відхилення. 349/789 звітів на проді мали вигадані дати; нові — вже не можуть. - Геокодування мапи (CAR-105): кириличні назви міст у рядках hh.ru/Habr тепер доходять до геокодера; персистентний обмежений кеш (
data/geocode-cache.json, підwithFileLock) означає, що вже розпізнане місто більше ніколи не б’є по провайдеру; виклики геокодування мають бюджет на запит, тож мапа заповнюється поступово замість блокування на 13.9 с. - Змішування мов в інтерфейсі (CAR-108): виправлено 23 витоки за ключами в 17 із 19 словників (85 значень) — «все matching», «только remote», «Открыть Scan», воронка «Evaluated → Applied…» та інше стали повноцінними локалізованими реченнями, що повторно використовують власний продуктовий словник кожної локалі.
i18n-no-latin-leaksрозширено таблицею фрагментів + пробою воронки (red→green). - Перевертання вердикту через валюту (CAR-109): порівняння зарплат обчислюється на сервері в
comp-compare.mjsі вбудовується в канонічній формі (Candidate minimum: … per YEAR (raw: …)vsJD offer: …); місячні суми ×12, форми k/к/тыс/萬 і десяткова кома нормалізуються перед порівнянням; невпевнена конвертація сама по собі більше ніколи не переверне вердикт (кейс zh-TW «€80–100k ниже минимума» мертвий). Модель ніколи не множить. - Підписи кнопок у довідці ×18 (CAR-107): локалізовані довідкові пакунки цитували англійські підписи («⚡ Run live», «Save», «Scan now»), яких інтерфейс більше не показує — 263 заміни по 18 локалях, зіставлені з вихідними кодами в’ю; docs-assistant успадковує виправлення.
Додано
- Заздалегідь стиснутий бандл + кешоване холодне завантаження (CAR-104):
make-bundle.mjsу момент релізу створюєbundle.js.br(brotli q11) +.gz; окремий middleware бандла домовляється br > gz ізVary: Accept-Encoding,public, max-age=300, must-revalidate+ ETag/304; під гейтом свіжості (компаньйон-артефакт ніколи не віддається старішим за identity-бандл), прямі запити артефактів віддають 404, CSP байт-ідентичний. Холодне завантаження: 3.1 MB → ~0.6 MB по мережі (−80%); повторні візити протягом 5 хв передають ≈0 байтів.
Примітки
- Усі виправлення прийшли з агентного флоту з червоно-зеленими доказами (13 + 28 + 1 + 30 + 7 нових кейсів у своїх сьютах) та пройшли раунди ревізій (CodeQL #579/#580/#584 виправлені в коді — тестові URL-заглушки парсяться через
new URL().host, мертвий рядок промпту викинуто). - Числа дозаповнення оцінок v1.258.0 тепер записані в CAR-129 (
updated: 22,reportScoreless: 159). - Навмисно не зроблено: без ретроактивного виправлення дат для 349 історичних звітів (дозаповнення було б ops-операцією зі згоди власника), без кластерного виду мапи (CAR-125 лишається в черзі за цим виправленням геокодування).
- Тести 5823 (+74). Джерел сканера не торкнуто (реєстр 121 = 116 EN + 5 RU · 116 адаптерів); жодних i18n-ключів (CAR-108 змінює лише значення наявних ключів).
[1.258.0] — 2026-10-25
Шість виправлень агентного флоту одним потягом: лічильник компаній на #/scan перестає змішувати всесвіти, глибокий рісьорч вимагає джерел, помилки форм підсвічують поле, стани завантаження отримують скелетони та миттєвий h1, а оцінювання кешують свій 37k-токенний префікс промпту.
Виправлено
- Лічильник «Активні компанії» на #/scan (CAR-100): чисельник рахував різні компанії по ВСІХ рядах результатів (1554 на проді — сам hh.ru дає сотні), тоді як знаменник рахував API-підкріплені відстежувані компанії (~90) — два всесвити в одному дробі. Обране парування (a): чисельник тепер рахує компанії серед рядків, які отримав сам API-підкріплений відстежуваний скан (збіг через
_entry ?? company; рядки роботодавців hh.ru/telegram відпадають); пре-скановий fallback переїхав у той самий всесвіт (раніше він міг читатися як «104/90»). - Вигадування фактів у глибокому рісьорчі (CAR-110): глибокий промпт тепер несе жорсткий контракт — кожне фактичне твердження закінчується маркером
[source: domain.com](не можеш назвати джерело → не стверджуй; вигаданий маркер гірший за чеснеunknown), невідомі факти пишуться якunknown, а звіт ОБОВ’ЯЗКОВО закінчується секцією## Data freshness. Якорі свіжості: сьогоднішня дата за UTC + опційнийurlкомпанії наPOST /api/deep(обмежений, сервером ніколи не отримується). - Помилки форм підсвічують поле (CAR-130): невдалий POST позначає перше поле форми
aria-invalid+.is-errorі фокусує його (WCAG 3.3.1); знімається при редагуванні. Підказка про мережеву помилку враховує розгортання — на loopback лишається підказка з dev-командою, а розгорнутий origin отримує «сервер не відповідає» замість порадиbash web-ui/bin/start.md(банер з’єднання ніс той самий хардкод). - Стани завантаження (CAR-112): #/map і #/tracker рендерять свій h1 миттєво (ціль фокуса роутера існувала лише після прибуття даних), а спільний хелпер скелетонів (
Skeleton.rows, aria-hidden мерехтливі рядки всередині регіонуrole="status", який повторно використовуєcommon.loading) замінює голий текст «Завантаження…» на трекері, результатах сканування та оболонці мапи;prefers-reduced-motionзупиняє пульсацію. - Кешування промпту оцінювання (CAR-111):
buildEvaluationPromptPartsрозрізає промпт на ~37k токенів на байт-стабільний префікс (CV + профіль + файли режимів + інструкції) та хвіст JD; шлях Anthropic надсилає[prefix + cache_control: ephemeral] + [JD]— префікс тарифікується за ставкою читання з кешу (≈10× дешевше) на кожному виклику після першого. Відсутній чи незбіжний префікс — безпечний відкат до байт-ідентичного старого запиту; OpenAI-сумісний каскад недоторканий (там кешування серверне).
Примітки
- Усі шість виправлень прийшли з агентного флоту з червоно-зеленими доказами (5 + 7 + 7 + 7 + 8 нових кейсів) та раундами ревізій; перші алерти CodeQL на CAR-111 (#579/#580) виправлені в коді — тестова заглушка парсить хост URL замість пошуку підрядка, а мертвий рядок повного промпту викинуто.
- Дозаповнення оцінок прогнано на проді (ендпоінт v1.256.0, зі згоди власника):
updated: 22,missingReport: 0,reportScoreless: 159— 159 рядків без оцінки вказують на звіти, чиї заголовки передують машинному блоку або несуть формати оцінки, які не розпарсити; відновлювані лише повторним оцінюванням або ширшим проходом парсера (відмічено в CAR-129). - Навмисно не зроблено: без зміни nulls-last для commute-колонки (передіснуюча примха
Infinityпри спадному сортуванні), без ретроактивного перерахунку цін старих записів usage, без підсвічування в стилі.is-errorдля форм фільтрів сканування (CAR-130 обмежений спільним хелпером сабміту mode-page + підказкою в api.js). - Тести 5749 (+36). Джерел сканера не торкнуто (реєстр 121 = 116 EN + 5 RU · 116 адаптерів); жодних i18n-ключів (підказка CAR-130 повторно використовує
conn.down; регіон скелетона —common.loading).
[1.257.0] — 2026-10-25
Два виправлення агентного флоту: вбудований помічник довідки розуміє деванагарі та тамільську, а сторінка #/usage показує фактичну вартість Hermes.
Виправлено
- Пошук у помічнику довідки для hi/ta (CAR-103): клас символів слова в keyword-токенізаторі docs-assistant був
[\p{L}\p{N}\u30fc]+— без комбінованих знаків. Кожна матра деванагарі (ा ै ्…) і тамільський знак голосної (ே ை ்…) розрізали слово на фрагменти; правило довжини відкидало короткі фрагменти (запит хінді знаходив 0 секцій), а довші слова розрубувалися в сміття, що збігалося з нерелевантними секціями. Тепер клас тримає\p{M}; шлях CJK-біграм недоторканий. hi «मैं स्कैन कैसे चलाऊं?» і ta «ஸ்கேன் எப்படி இயக்குவது?» знаходять правильні секції в поставлених бандлах. - Вартість usage для Hermes (CAR-106):
normalizeUsage()згортав usage провайдера до{in, out}і мовчки втрачав передану шлюзомcost—#/usageпоказував $0.0000 за 9.5 M токенів Hermes (рядок у таблиці цін — 0/0). Форма запису usage отримує опційний назад-суміснийcost?; агрегатор віддає перевагу переданій провайдером вартості на кожен запис і звертається до оцінки з таблиці цін, лише коли її немає; сміттєві/негативні значення відкидаються, а не вигадуються.
Примітки
- Обидва виправлення прийшли з агентного флоту з червоно-зеленими доказами (3 + 5 нових кейсів; 14/14 і 10/10 зелені; сусідні 78/78 та 61/61). Перші PR (#470/#471) несли застарілу базу worktree, що конфліктувала зі squash-злитим v1.256.0 — замінені чистими cherry-pick’ами (#472/#473).
- Навмисно не зроблено: без ретроактивного переоцінювання історичних рядків
data/llm-usage.jsonl(у них немає поля cost; вигоду отримують лише нові записи). - Тести 5713 (+8: docs-assistant ×3, usage ×5). Джерел сканера не торкнуто (реєстр 121 = 116 EN + 5 RU · 116 адаптерів); жодних i18n-ключів.
[1.256.0] — 2026-10-25
Якість даних трекера: роль більше ніколи не записується як компанія, борд вакансій ніколи не записується як роботодавець, рядки без оцінки перестають спливати першими — а одноразовий ендпоінт дозаповнює відсутні оцінки з самих звітів.
Додано
- Дозаповнення оцінок (CAR-129):
POST /api/tracker/backfill-scoresзаповнює клітинку Score у рядках, де пов’язаний звіт несе оцінку, яку вдається розпарсити (181 оцінений рядок мав звіт, але не мав оцінки). Оцінені рядки, явні сентинели (N/A), рядки без звіту та звіти без оцінки ніколи не чіпаються; перезаписується лише клітинка Score, під файловим блокуванням; форма оцінки валідується в місці запису та нормалізується до конвенції трекераX.X/5; прогін ідемпотентний і повертає{ok, updated, missingReport, reportScoreless}.
Виправлено
- Роль ніколи не компанія (CAR-128):
guessCompanyRoleвважав ЛІВУ сторону рядка-заголовка з тире компанією, але борди пишуть в обидвох порядках — «Senior Frontend Engineer - Grafana Labs» записував роль ЯК компанію (13 рядків: Grafana, CircleCI…). Тепер евристика рольових ключових слів зі словесними межами обирає сторону ролі, а тип зайнятості ніколи не стає роботодавцем («Senior Engineer - Remote (EU)» не записує нічого, замість «Remote (EU)»). - Борд вакансій ніколи не роботодавець (CAR-127): fallback за доменним слагом записував рядки hh.ru як «hh» (120 рядків). Слаги
hh/habrдолучаються до списку не-роботодавців; висновування роботодавця з середини речення («в компании X», «X ищет») тепер працює і для хостів бордів вакансій (hh.ru/kz/uz/by, habr.com) — там, де текст сторінки називає справжнього роботодавця. Дорогою знайдено першопричину: ASCII\b/\wу цих патернах взагалі ніколи не збігалися з кирилицею — мовчки мертві з v1.248.4; їх замінюють Unicode-lookbehind’и та класи\p{L}. - Рядки без оцінки сортуються останніми (CAR-129): сортування трекера за оцінкою використовувало сентинел
-1, тож за типовим висхідним сортуванням кожен рядок без оцінки спливав перед кожним оціненим. Тепер null-и сортуються останніми в обидва боки (фільтр за оцінкою зберігає свій-1— «без оцінки не проходить фільтр ≥4» — коректна семантика саме там).
Примітки
- Історичні рядки цей реліз НЕ мігрує: виправлення парсерів стосуються нових оцінювань; оцінки відновлювані через backfill-ендпоінт, тоді як 120 клітинок роботодавця «hh» потребують повторного оцінювання, а не перезапису.
- Раунд ревізії opus-5-5 включено: незаякорений патерн «X ищет» викинуто ще до випуску (будь-яке слово з великої літери перед дієсловом записалося б як роботодавець — інваріант no-fabrication), додано якорі «В…» на початку речення, а записувач backfill повторно перевіряє цільову клітинку (дублікати рядків за
#з’являються після ручних редагувань), зберігає кількість колонок короткого рядка, пошкодженого вручну, та зупиняється на першому рядку поза таблицею. Крапка всередині назви з крапкою («Сбер. IT») невиразна від крапки речення для якоря «в компании» — записується консервативний префікс («Сбер»); патерн із дієслівним якорем ловить повну форму. - Навмисно не зроблено: компаратор не винесено з
tracker.jsу бібліотеку, придатну для юніт-тестів (source-static гейт залишається; відмічено в CAR-129 як follow-up), без зміни nulls-last для commute-колонки (передіснуюча примхаInfinity-on-desc), без міграції даних для рядків «hh». - Тести 5705 (+10: 5 identity-спек у
auto-pipeline-jd-gate, 2 CAR-128, гейт сортування CAR-129, 4 backfill, включно з прив’язуванням ворожого шляху — мінус замінені кейси v1.248.x). Джерел сканера не торкнуто (реєстр 121 = 116 EN + 5 RU · 116 адаптерів); жодних i18n-ключів.
[1.255.2] — 2026-10-24
Три видимі користувачеві виправлення з live-регресії та звіту про збій на проді: збій рендеру #/scan, сліпа мовна перевірка в’єтнамської та повторювана втрата блоку summary zh-TW.
Виправлено
- Збій #/scan (CAR-99): перегенерація count-гейтів від v1.253.0 писала літерал
FALLBACK_SOURCESіз подвійною комою в кінці кожного елемента — 120 дірок (пропущених елементів) у масиві.new Set(FALLBACK_SOURCES.map((s) => s.value))у scan-в’ю тримав тодіundefined, аid.lengthкласифікатора компаній кидавTypeError: Cannot read properties of undefined (reading 'length')на кожному рендері сторінки (спіймав logbuf на проді). Дірки видалено, а новий гейт (4 спеки, перевірений на червоному) виконує файл і валить будь-яку дірку, непридатний елемент чиundefinedу Set ідентифікаторів адаптерів — текстовий гейт дрейфу цього класу не бачив. - Сліпа мовна перевірка vi (CAR-97): в’єтнамська — латиниця й не мала запису в таблиці стоп-слів live-QA, тож «vi стоп-слова» рахували англійські влучання, а в’єтнамська відповідь читалася як
report not in vi(live-регресія: vi 2, en 2). У в’єтнамської тепер власна десятислівна таблиця службових слів — чисто в’єтнамський звіт проходить, англійський досі падає. - zh-TW втрачав блок SCORE_SUMMARY (CAR-98): закривний мовний reminder казав лише, що машинний формат не можна перекладати, і ніколи — що блок summary має існувати — короткий звіт zh-TW (5983 символи) ніс блоки A–G, але повністю пропускав цей блок (рецидив класу v1.239.2). Відкривна директива та закривний reminder тепер кажуть, що блок обов’язковий, а без нього звіт недійсний.
Примітки
- Цей реліз навмисно не змінює жодного джерела сканера, лічильника реєстру (121 = 116 EN + 5 RU · 116 адаптерів) чи проводки провайдерів — це суто виправний реліз, тож count-гейти недоторкані.
- Тести 5695 (+9:
car99-fallback-holes×4,car97-vi-lang-check×4, інваріант промпту CAR-98 ×1). Браузер 143/143. Повторна live-перевірка vi/zh-TW їде з пост-деплойною регресією.
[1.255.1] — 2026-10-24
Два раунди виправлень за ревізіями: ревізія Opus в’єтнамської локалі (9 знахідок) і другий прохід ревізії виправлень CAR-91/92 (5 must-fix) — обидва закриті з червоно-зеленими тестами.
Виправлено
- Раунд ревізії vi-локалі (наступний за CAR-88): гліф хангиль (
신), що затік уproject.subtitle/project.roleHint, виправлено (độ khớp tín hiệu), із гейтом, який валить будь-який CJK/кана-гліф у значенні vi, якого немає в словнику EN; шість розбитих пробілами маркдаун-URL у docs/help/vi.md (what-is career-ops,apply for a job,set up playwrightі ще три) виправлено на дефісові справжні слаги гайда, із гейтом «без пробілів у URL». - Межі блокових заголовків оцінювання (CAR-92 r2 H1):
&,.,/,'тепер словесні символи лівої межі блокової літери —## Q&A,## Plan B,## Ph.D. requirement,## E-commerceбільше не реєструються як блоки A/B/D/E; голий терминатор кінця рядка приймається лише після блокового слова-токена (Block/блок/블록/…); ASCII-прибрано з класів словесних терминаторів, тож заголовкиE-commerce/ई-मेलвиживають. - Звіти з українською/російською нумерацією (CAR-92 r2 H2): детектор послідовності А Б В Г Д Є Ж став спільним хелпером; коли він знаходить 3+ літери послідовності на рівні заголовків, цей рівень стає референсним для різака — літерні підрозділи
### A.більше не обрізають звіт на блоці Б. - Життєвий цикл перенесених рядків (CAR-92 r2 M1): рядки, які джерело, що відмовило, переносить у наступний скан, отримують штамп
carriedSince, відпадають після 30 днів або 5 переносів підряд і додаються після свіжих рядків — замість того, щоб мовчки накопичуватися вічно. - Знаменник і легітимність gemini-synthesis (CAR-92 r2 M2) та шлях body-score сабпроцесу (M3): прочитано з файлу знахідок, виправлено з червоними тестами.
- Гейт дрейфу бандла холодного завантаження оновлено під словник vi (90 script-тегів / 87 бандлозбірних), а його невикористане тестове прив’язування видалено (CodeQL 572).
Примітки
- Формулювання лічильника адаптерів у банері «Latest» README.vi, яке позначила ревізія, живе в банері Previous і вже дзеркалить EN README байт у байт — залишили недоторканим заради паритету EN↔vi (задокументовано в тілі коміту).
- Тести: 5686 (+26:
eval-validate-opus-r2,scanner-carryover-r2, твердження vi-гейтів). Реєстр без змін: 121 джерел (116 EN + 5 RU) · 116 адаптерів.
[1.255.0] — 2026-10-24
В’єтнамська (Tiếng Việt) стає 19-ю мовою інтерфейсу — словник на 1525 ключів із повним паритетом до EN, розпізнавання vi/vi-VN, повний довідковий пакунок, дзеркала README та CHANGELOG, інтеграція в лендінг cvstart.org і 🇻🇳 у перемикачі мови.
Додано
- В’єтнамська (
vi) — 1525/1525 ключів із повним паритетом доen;detect()приймаєviтаvi-VN; в’єтнамська — LTR (доRTL_LANGSне додається). ‘Pipeline’ залишається ‘Pipeline’ — стандартне технічне запозичення, у білому списку. - Повний docs/help/vi.md (33 H2 / 125 H3), плюс дзеркала README.vi.md і CHANGELOG.vi.md та в’єтнамські рядки-скафолди в
prompts.mjs(LOCALE_NAMES+SCAFFOLD_STRINGS) — вивід LLM в’єтнамською працює з коробки. - Лендінг cvstart.org в’єтнамською —
site/src/i18n/vi.json, запис локалі в sitemap і скриншотdashboard-vi.png; перемикач мови отримує 🇻🇳 (19 позицій). - Перелічувальні гейти 18 → 19 — LANGS, проби detect, i18n-аудит, паритет changelog, блок перекладів README, locale-sweep — плюс red-first
tests/i18n-vi-locale.test.mjs(139 рядків).
Примітки
- Лендінг cvstart.org отримує vi — навмисне відхилення від прецеденту ta (той реліз тримав лендінг на 17 спеціально); задокументовано в спесі релізу.
- vi — латиниця, тож гейт no-latin-leaks не застосовується (він сторожить нелатинські локалі); латинська технічна лексика тут норма.
- Тестова підлога: 5634 → 5642 (1 пропуск — опціональний браузерний capture-тест розкладки, без змін).
[1.254.0] — 2026-10-24
Реліз зміцнення з раунду ревізії максимальною моделлю: снапшот сканера більше не втрачає рядки, одинадцать інваріантних порушень LLM-шару виправлено, двадцять три SPA-must-fix ввійшли, і Paylocity ATS долучається до сканера — реєстр тепер поставляє 121 джерел (115 EN + 5 RU) зі 116 адаптерами.
Додано
- Paylocity ATS (батько #5687): тенантні борди
recruiting.paylocity.com, мертвий борд 302 → JobNotFound відображається в 404, щоб карантин виводив запис, капmax_jobsз прапорцем усічення. Реєстр: 121 джерел = 116 EN + 5 RU · 116 адаптерів; гейти лічильників перегенеровано.
Виправлено
- Безпека снапшоту сканера (R-12): скан, що нічого не пробує, тепер ЗБЕРІГАЄ снапшот (
nothing scanned); full-replace переносить старі рядки джерел, що відмовили, — зі штампом по ентрі — з повторним прогоном через ПОТОЧНІ фільтри, приземляючись лише вfiltered. - Одинадцять інваріантів LLM-шару: мовна директива відкриває І закриває промпти deep/mode; Gemini-subprocess пересилає
--lang, не-нульовий вихід → 502, не пускає stderr клієнту, тримає 300 с і пише usage; manual-fallback несе URL вакансії; truncation і warnings бандла спливають у/api/mode; spend tier більше не обходиться клієнтом; дисконнект клієнта абортить виклик провайдера в польоті; різак повторних заголовків більше не знищує валідні звіти; екстрактори score/legitimacy узгоджені з валідатором; українська послідовність та арабська ва tightened. - Раунд ревізії Opus 5-5 НА самих виправленнях (8 must-fix): мультибуквена реєстрація різака, ширші терминатори заголовків, abort-проводка Gemini-subprocess, штампована атрибуція переносу, перенесені рядки повторно проходять поточні фільтри, рівень посилальних заголовків за числом різних букв.
- Двадцять три SPA-must-fix у sourcing-в’ю (append однопейджного пейджера, клавіатура рядків pipeline, відновлення фільтрів saved-search, скиди select source, відкат дефолту в config, течі листенерів, close SSE на терміналі, teardown scan-раннера, голий fetch, провальні-завантаження-виглядають-як-пусті, колізія в window, збереження фокусу) та assist-в’ю (мертвий help deep-link, течі help, повторне озброєння blank-overwrite у two-pager, single-flight платних прогонів batch/deep/mode, тихі кінці стрімів, вічні спінери на помилках, привид рядків провалених фільтрів, дублі docs-fab + мова).
Примітки
- Весь раунд пройшов ревізію Opus 5-5 на максимальній строгості — порти до мержу (21 знахідка, 13 виправлено) і самі фікси постфактум (8 must-fix, усі ввійшли тут з червоно-зеленими тестами).
- Тестова підлога: 5262 → 5634 (372 нові специфікації по джерелах, сканерах, LLM-шару та SPA).
[1.253.1] — 2026-10-24
Дві HIGH-знахідки безпеки з ревізії максимальною моделлю виправлено: IPv6-літерали більше не обходять SSRF-гард сканера, а escape-regex CV-маркдауну більше не бектрекає квадратично.
Виправлено
- IPv6-літерали обходили SSRF-гард (
http-json.mjs): дужковий IPv6-літерал валивdns.lookupвинятком, і гард fail-open пропускав. Тепер дужки знімаються, літерал класифікується черезisIP— без DNS і без fail-open. - ReDoS в escape-regex CV-маркдауну (
security.mjs): патерн бектрекав квадратично —<+ ~100k пробілів коштували ~16 с; тепер ~1 мс на плоскому обмеженому класі. Все, що екранував старий, екранує й новий.
Примітки
- Обидва фікси несуть червоно-зелені регресійні тести (3 нові IPv6-кейси + ReDoS з часовою межею; 5511 тестів, +7).
- Файли джерел сканера не зачеплені — гард живе в спільному транспорті.
[1.253.0] — 2026-10-24
Десять нових джерел долучаються до сканера — Atlassian, BWI, Computrabajo, Dover, Google, Kalibrr, Magneto365, Platsbanken, Tech in Asia та Zoho Recruit — реєстр тепер поставляє 120 джерел (115 EN + 5 RU) зі 115 адаптерами, а роль-матчер розділяє заголовки, що різняться лише коротким капсом-кодом.
Додано
- 10 джерел портовано з батька 1.38.0, кожне зі своїм fetch-адаптером і CI-ізольованим набором (фейковий fetchImpl, нуль мережі): Atlassian (єдний auth-free JSON-фід, iCIMS-URL, allowlist https+icims.com), BWI (ItemList JSON-LD, JSON.parse перший), Computrabajo (LATAM, декод ентіть, іспанське «remoto» = remote), Dover (DRF-пагінація по тенанту, рендер
Remote (International), UUID-валідація борд), Google Careers (перші сторінки за фразами, роботодавець картки виграє, дедуп між запитами), Kalibrr (SEA-агрегатор, фід-id як id рядка), Magneto365 (LATAM, фіксований міжсторінковий темп), Platsbanken (шведський JobTech API, вікна за ключовими словами), Tech in Asia (Algolia-ключі з білда сайту), Zoho Recruit (hidden-input борд, обмежений detail-enrichment, валюта лише ISO-4217). - Лічильники реєстру: 120 джерел = 115 EN + 5 RU · 115 адаптерів; п’ять гейтів лічильників перегенеровано з живого реєстру; фолбэк дропдауна
#/scan, лінки cvstart.org і help §17 ×18 слідують.
Виправлено
- Роль-матчер (#5688 порт): заголовки, що різняться лише коротким капсом-кодом («HR BP» vs «HR GP», «AWS» vs «GCP», навіть у змішаних скриптах), більше не склеюються фазз-матчем в одну вакансію; коди читаються з сирого заголовка з виключенням noise-слів, гард стоїть просто перед коефіцієнтом Жаккара.
- Порти пережили ревізію Opus 5-5 максимальної строгості (21 знахідка): throw на тихо-пустих бордах при дрейфі розмітки (google/bwi), преабортований скан кидає замість повернення
[], порядок decode-then-strip на LATAM-скреперах (XSS-форма strip-then-decode), https+allowlist на URL рядків atlassian, host-ассерти з відмовою портів, парс пізніх сторінок всередині try (партиали виживають), і чесність тестів (бюджети пораховані точно, контракти коерсції законтрактовані правдиво). 13 знахідок виправлено; [spec]-хвіст відстежується окремо.
Примітки
- П’ять RU залишаються web-ui-only (апстрим-адаптерів немає — навмисно); кожне EN-джерело має свій адаптер.
- Coverage-рейтча: шість найбільших портів піднято до ≥95 % branch (27 нових кейсів); оборонний мертвий код залишку задокументовано пофайлово.
- Нові джерела вибираються через
provider: <slug>у portals.yml — для наявних конфігурацій нічого не змінюється.
[1.252.1] — 2026-10-17
Банер релізу в wiki тепер перевіряється скриптом, а не ручним свіпом — регрес v1.252.0 спіймав протухлий банер лише руками — і локальна директива явно перевизначає language.output профілю та мову вакансії.
Додано
scripts/wiki-banner-check.mjs— гейт, якого вимагав регрес: читає коженHome*.mdу клоні wiki та звіряє банер зpackage.jsonі з ЖИВИМИ лічильниками реєстру (scanner adapters з портального реєстру — 105, а не протухлі 109 з банера; route modules зserver/lib/routes/; UI locales з набору CHANGELOG). Regex банера мовно-незалежний за ФОРМОЮ: локалізовані Home-сторінки перекладають і мітку, і лічильники («Текущий релиз», «Aktuel udgivelse», «адаптеров сканера», «enhedstests»), і їх числа поїхали далі (5177), ніж EN-сторінка (5245).--tests Nприймає число, яке щойно надрукував свіжийtest:ci;--check-onlyзабороняє неявний клон для офлайну. Фікстурний офлайн-тест —tests/wiki-banner-check.test.mjs.
Виправлено
- Банер wiki Home ×18 повернуто в синхрон (виходить разом з цим релізом): свіп v1.252.0 змінював бейджі й посилання, але ніколи рядок банера, і ніщо механічне на це не вказувало. Гейт вище робить це релізоблокувальною перевіркою.
- Локальна директива фіксує пріоритет мови (
buildLocaleDirective): «This UI locale overrides the profile’slanguage.outputsetting and the job posting’s own language». Власне правило 6 батька велить писати вlanguage.output— без фрази про пріоритет модель розв’язувала два суперечні вказівки непослідовно по локалях, що й був розкид мовної колонки v1.252.0. Закріпленоtests/locale-directive-language-output.test.mjsна тексті директиви (17 не-EN локалей + порожній EN).
Примітки
- Спостереження за ФОРМОЮ ta evaluate (§3, стежити): перший живий прогін v1.252.0 дав evaluate на 48 336 символів без Block G і SCORE_SUMMARY — схоже на зациклення, а не на мову (CAR-70 — про мову). Повториться на наступному релізі — окрема Linear-задача, не CAR-70.
- За FIX-промптом: батько лише для читання; перевизначення цілком живе в директиві UI.
[1.252.0] — 2026-10-17
Батьківський форк синхронізовано з career-ops-hq/career-ops — 264 апстрим-коміти, батько 1.36.0 → 1.37.0 — з захистом обох форк-дивергенцій, а деплой переустановлює робочого батька на цю базу.
Додано
- В’єтнамський (vi) ринковий режим з апстриму (
modes/vi/,tuyen-dung/ung-tuyen): BHXH/BHYT/BHTN, thử việc, lương tháng 13, trợ cấp thôi việc — 21-й ринок у таблиці режимів батька. - Провайдер борди Tech in Asia (апстрим): читає публічний Algolia-індекс
job_postings, який сам запитує UI вакансій techinasia.com — SEA-борда стартапів, ~300 живих вакансій на замірі, 273 з них індонезійські. Ключі виявляються заново на кожному прогоні з білда сайту, власне посилання роботодавця перемагає сторінку борди, а зарплата свідомо не додається (поля зарплат в індексі — прапорці, а не гроші). - Вісім нових документів сигналів легітимності Block G (
modes/oferta-signals/08–17): країна в термінології пільг, тег локації сторонньої платформи, ліцензування агентств, надмір імміграційних вимог, заборонений контент юрисдикції, ширина вилки pay-transparency, питання юристу про мінімалку, розкриття AI-скринінгу, розкриття строкового контракту, купівельна спроможність при релокації.
Змінено
- Зміцнення на боці батька, які їдуть з базою: перевірка story-provenance працює за одиницями та реченнями і тримає цифри
user-cannot-confirmпоза переговорами, ATS та interview-виводом; fact-gate CV запускається на кожному рендерері і читає переформульовані та структуровані заяви; LaTeX відкидає протухлі витягнуті слоти до запису; launchd-обхід follow-ups пропускається, коли follow-ups вимкнені; update-system утримує скан локальних правок у межах argv. - Дивергенції форка пережили мерж:
providers/telegram.mjs(форк-єдиний парсер, ADR-0001) та Unicode-lookupLOCATIONISH_REуtelegram-channel.mjs(ADR-0002). Документ борд тепер перелічує покращений рядокtelegram-channelапстриму, форк-єдиний рядокtelegram, Tech in Asia і дефолтmax_pagesу Tencent, піднятий 20 → 100.
Примітки
- Змін поведінки SPA в цьому релізі немає: це док-поїзд плюс перевстановлення батька. Деплой тепер працює на батькові 1.37.0; таблицю канонічних статусів дельта батька не торкається — статуси трекера рендеряться як раніше.
- У мержі апстриму розв’язано два конфлікти:
docs/SUPPORTED_JOB_BOARDS.md(обидва телеграм-рядки збережено) таtest-all.mjs(walkTree-версіяcopyDirSyncапстриму субсумує форковий ручний guard вкладених чекаутів за.git; список імпорту — об’єднання).
[1.251.1] — 2026-10-12
Фільтр спеціалізацій тепер матчить лише ВІДМІННІ слова ролі — стаж і загальні посадові іменники більше не матчать вакансію самі по собі — а remote-qa розділяє форму та мову, щоб дрейф не червонив A–G.
Виправлено
- Фільтр спеціалізацій викидає стаж і загальні посадові іменники зі свого матчера: «Senior PHP Developer» матчив «Senior Java Developer» через спільний Senior/Developer. Матчер залишає лише відмінні токени (PHP, Go, Platform, SRE, Backend…); роль без відмінних токенів («Software Engineer») падає на матч усієї фрази. Межі слів на місці: «Go» ніколи не матчить «Golang» і «Google».
- remote-qa розділяє форму та мову: прапор A–G судять лише за кодами блоків/SCORE_SUMMARY/looping; дрейф
report not in <locale>іде у власний прапор і колонку warnings (таблиця отримує бракуючий 4-й стовпець; коди зберігаються построчно). - Тулбар карти — чесні 50/50 на 390 px (флекс 58/42 залишав пошуку 124 px і обрізав плейсхолдер), мертве CSS-правило
.job-map-job-selectвидалено. - CodeQL 552 — невикористану змінну
OVERLAYSу design-qa тесті видалено.
Примітки
- Милі все ще відкладає власник. Мовний дрейф ta залишається ЗАДОКУМЕНТОВАНИМ обмеженням бекенду hermes (2–26 % прози між прогонами; A–G тримається): remote-qa повідомляє його як очікувану знахідку. Список загальних токенів навмисно мінімальний — розширити — рядок на мову.
[1.251.0] — 2026-10-12
Селект на карті тепер фільтрує за СПЕЦІАЛІЗАЦІЄЮ з налаштувань (config/profile.yml → target_roles.primary): вибір «Product Manager» або «Backend Engineer (Go / PHP)» залишає на карті лише вакансії, чий тайтл несе слова цієї ролі. Локатор списком вакансій з v1.250.0 видалено — це був невірний інструмент (правка власника).
Додано
- Фільтр спеціалізації: select перелічує спеціалізації з налаштувань (target_roles.primary); вибір залишає лише вакансії, чий тайтл несе один із відмінних токенів ролі (межі слів, без регістру — «Go» ніколи не матчить «Google»); Reset повертає всі.
- Локатор списком вакансій з v1.250.0 видалено — заміна: фільтр за спеціалізацією, не список вакансій (правка власника).
Змінено
- Two mobile-map height specs: the floor moves 600 -> 540 px (the toolbar takes ~90 px of the viewport).
Примітки
- Милі все ще відкладає власник. Матчинг свідомо ЛИШЕ за тайтлом словами з налаштувань: компанії чи описи затягли б у спеціалізацію нерелевантні вакансії. Вакансія поза всіма налаштованими ролями видима за «All».
[1.250.0] — 2026-10-12
Сторінка карты стає робочим інструментом: локатор вакансій над картою наводить на будь-яку видиму вакансію і відкриває її пік-картку (зокрема на десктопі), а текстовий пошук звужує карту за компанією або посадою — і те, й інше переживає перезавантаження.
Додано
- Локатор вакансій: select над картою перелічує видимі (пості фільтри) вакансії за компанією+посадою; вибір летить до кружка і відкриває пік-картку — зокрема на десктопі.
- Текстовий пошук: звужує карту за підрядком компанія+посада (порівняння без регістру; поле зберігає введений регістр), персистить у map-prefs, Reset чистить.
- FAB більше не перекриває атрибуцію на
#/mapна всіх ширинах.
Виправлено
- Два spec-и висоти мобільної карти: поріг стає 540 px (тулбар займає ~90 px вьюпорта).
Примітки
- Милі власник відкладає на наступний реліз. 4 нові i18n-ключі входять до 18 словників, зібраний снапшот перегенеровано. Локатор завжди відкриває пік (навігація знецінила б його); кружок знаходиться через зворотне посилання __job — НЕ _point, який L.Circle вже займає.
[1.249.0] — 2026-10-11
Мапа вакансій тепер ставить релевантність на перше місце: спокійна шкала від бірюзового до сірого виділяє найкращі вакансії, фільтри оцінки й часу в дорозі зберігаються, на телефоні мапа займає доступну висоту та показує картку за першим дотиком, а трекер використовує ті самі фільтр і сортування поїздки.
[1.248.8] — 2026-10-11
Паритет з батьком: форк і web-ui синхронізовані зі 115-комітною дельтою апстріму — нове джерело PageUp, URL-комірки трекера парсяться як markdown-посилання, стейт Assessment — плюс ta/ja мовна робота: зациклені звіти зрізаються і називаються, скелет заголовків тримає A–G стабільним, а частка письма рахується лише по прозі.
Додано
Виправлено
- Нове джерело PageUp (105 EN-адаптерів / 110 джерел); URL-комірки трекера парсяться як markdown-посилання (href — ключ дедуплікації); стейт Assessment приходить через states.yml.
- Зациклений звіт зрізається і називається (
report looping (block B repeated)), скелет заголовків## A) … ## G)для неанглійських. - Частка письма рахується лише по прозі (таблиці, SCORE_SUMMARY, код і URL виключено) — японський звіт з латинськими таблицями більше не флакує.
- /api/evaluate попереджає
report not in <locale>, а remote-qa друкує коди колонкою.
-
[1.248.7] — 2026-10-11
Роль із шапки звіту тепер читає реальну конвенцію батька — # Evaluation: {Company} — {Role} — замість припущення # <Роль> — <Компанія>, тож у трекер більше не потрапляє «Evaluation: <Компанія>» як роль; звіт не тією мовою попереджає замість мовчазного збереження; а балачки моделі до H1 зрізано зі збережених звітів.
Виправлено
- Локалізована мітка знімається першою (
Evaluation:,Оценка вакансии:та ще 16 варіантів); після міткиCompany — Role→ роль — права сторона; без міткиRole — Company→ ліва; частина, що називає компанію, вирішує; роль ніколи не дорівнює компанії чи слову мітки — запас це чищений рядок посту. - cleanup-plan сумує N із рядків (
— N dirty-URL line(s)), не рахує файли — файл із 5 сміттєвими URL давав 1, а не 5. - Звіт не тією мовою попереджає: промпт відкривається ТА закривається обов’язковою мовною інструкцією (весь звіт, включно з таблицями), а конвеєр перевіряє мову збереженого звіту — дрейф спливає попередженням
report not in <locale>. - Без балачок моделі до H1 — звіт починається зі своєї заголовкової лінії.
Примітки
- Не портовано і не чіпано (перевірено в регресії v1.248.6): embed-фетч, рядкова структура посту («Компания : X»), дедуп «## Rejected», відмова кореня домену до fetch, чесність recon/link-check і cleanup-режими. Мовна перевірка — попередження: звіт (вміст уже валідовано A–G + оцінка) зберігається; оператор бачить дрейф і може повторити оцінку.
[1.248.6] — 2026-10-10
Пострегресійне шліфування телеграм-ланцюга: реальний пост зберігає рядкову структуру (мітка «Компания : X» і рядок ролі знаходяться там, де вони реально є), роль у трекер приходить із шапки звіту, а не сирим рядком посту, і операційні поверхні (recon, cleanup-plan, link-check) повідомляють те, що заявляють.
Виправлено
- Пост t.me більше не склеюється в один рядок:
<br>стає переводом рядка до зняття тегів, числові HTML-сутності декодуються; шаблон мітки компанії приймає форму з пробілом перед двокрапкою. - Роль у трекері береться з шапки звіту (H1
# <Роль> — <Компанія>); рядок-запасний варіант чиститься — без хештегів, без емодзі, ≤80 символів. ## Rejectedбільше не дублюється: розділ створюється один раз; кожна відмова дописує в нього свій рядок.- Корінь домену відхиляється до запиту — сміттєвий URL більше нічого не коштує.
- Recon обчислює сигнал контракту, а не стверджує його (
honours rejected:перевіряєmarkPipelineRejectedу задеплоєному коді); лічильник міток більше не друкує «0» двічі. - cleanup-plan повідомляє числа за видами (t-role до переміщення, рядки dirty-URL, рядки test-row) замість одного
grep -c, що рахував власний рядок успіху. - link-check вважає 5xx від github.com неперевіреними — за бото-стіною, не як биті посилання.
Примітки
- Не портовано і не чіпано (перевірено в регресії v1.248.5): сам embed-фетч, серверний skip-контракт, офлайн-тести _setLookup, прийом uk-порядкових, межі слів у гейті ролі та режим cleanup-apply. Підрахунок за видами в cleanup-plan читає власні мітки виводу скрипту очищення — їх перейменування мовчки зламало б лічильники.
[1.248.5] — 2026-10-10
Зміцнення після регресії: одиночні записи t.me тепер отримують сам текст посту (embed-форма), відхилений запис покидає пул eval-таймера на боці сервера, а рідна нумерація блоків в українському звіті приймається — плюс один тест, який потай ходив у живу мережу, назавжди офлайн.
Виправлено
- Записи t.me нарешті оцінюються: звичайна сторінка одного посту віддає в HTML лише шапку каналу (~0,2 КБ — «View in Telegram» і нічого більше), модель отримувала порожній опис, і кожен телеграм-запис умирав на evaluate. Фетч тепер переписує
t.me/<канал>/<id>(і форму стрічкиt.me/s/…) в embed-форму (?embed=1&mode=tme) — той самий хост, SSRF-перевірки без змін — і бере текст посту з.tgme_widget_message_text(запасний варіант:og:description). Немає тексту віджета → відмова на кроці fetch (telegram post has no text,rejected: true) до витрати токенів LLM. - Eval-таймер перестає повторно платити за відхилені записи (серверний контракт): відхилений запис позначається в
data/pipeline.md— її рядок покидає URL-паркан і потрапляє до розділу## Rejectedз короткою причиною (ідемпотентно, без дублів). Таймер бере голі URL з паркану, тому позначений запис пропускається природно — без залежності від нутрощів скрипту таймера. Recon друкуєpipeline rejected marks: N+honours rejected: yes (server-side)замість ґрепу по скрипту. - Українські звіти знову проходять валідацію A–G: uk-модель нумерує блоки за порядком власного алфавіту (А Б В Г Д Є Ж), а прохід двійників рахував лише А/В/Е — на проді бракувало рівно блоків C, D, F, G (2/2 відтворення). Валідатор приймає українську порядкову послідовність, коли заголовки йдуть по порядку (та сама дисципліна, що абджад для ar і деванагарі для hi), а промпт оцінки для всіх локалей тепер явно вимагає латинські
A–G. - Гейт рольових ключових слів спрацьовує на слова, а не підрядки: корені
linkedin.com/іx.com/проходили гейт, боlead/itжили в службовому тексті сторінки. Ключові слова зіставляються за межами слів Unicode, а корінь домену (URL без шляху) відхиляється до виклику LLM. - Юніт-тесту більше не потрібна жива мережа: тест no-hints (
tests/auto-pipeline-jd-gate.test.mjs) підмінював HTTP-транспорт, але робив справжній DNS-запит — у CI був зеленим лише тому, що раннер мав мережу. У safe-fetch з’явився хук_setLookup()(сам резолвер замінний у тестах; продовий код його не чіпає), тест резолвить офлайн.
Додано
- Режими
cleanup-plan/cleanup-applyу deploy.yml: продова чистка QA-сміття (post-qa-cleanup) запускається на сервері без SSH з ноутбука. У лозі лише числа — ні імен файлів, ні прев’ю рядків: лог публічний, а в іменах назви компаній. Крок Verify для cleanup-режимів пропускається.
Примітки
- Не портовано: компілятор локацій за межами слів і таблиця штатів USPS з батька (задокументовано з v1.248.1). Контракт пропуску відхилених записів тримається на сервері (розмітка в data/pipeline.md), а не на скрипті таймера — той поза репозиторієм і свідомо не правиться.
[1.248.4] — 2026-10-10
Продовження регрес-раунду: eval-таймер більше ні за яких умов не архівує безіменні Telegram-заглушки як звіти, QA-очищення більше не видаляє справжні Telegram-вакансії, атрибуція карти знову видима, а дашборд-чіпи читаються з ≥ 4.5:1.
Виправлено
- Ланцюг ролі працює для кириличних постів: список ключових слів отримав разработчик/программист/инженер/аналитик/менеджер/руководитель/тимлид/лид/дизайнер/тестировщик/QA/архитектор/специалист/директор/DevOps/SRE (старий був лише EN: 0 з 40 реальних постів отримували роль), консультується шапка звіту (
parseReportHeader— модель пише роль у H1), остання інстанція — перший осмислений рядок посту. - Ворота ідентичності переїхали ДО виклику LLM: запис без компанії та без зачіпки ролі падає на кроці fetch (
rejected: true) — токени не витрачаються на оцінку, яку все одно було б відхилено. t.me/s/<канал>/<id>більше не називає компанію «S» — сегмент веб-прев’ю пропускається; обидві форми посилань резолвляться в канал.- Самоябели «Компания: X» на Telegram-постах враховуються (і «…в компании X» всередині речення для мессенджер-хостів).
Додано
- Фейли auto-pipeline несуть
rejected: trueу SSE-помилці — серверний eval-таймер використовує флаг, щоб пропустити запис pipeline замість повторів кожні 2 години.
Примітки
- Не портовано: батьківський компілятор локацій з word-boundary та USPS-таблиця (документовано з v1.248.1).
- Блок «Translations» у README генерується з одного канонічного списку, з паритет-тестом у CI (17 посилань на файл, своя не лінкується) — ar/pl/uk отримали блок; дрейф роздільників
|/·усунуто.
[1.248.3] — 2026-10-10
Продовження регрес-раунду: eval-таймер більше за жодних умов не оформити безіменні Telegram-заглушки у звіти, QA-очищення більше не видалить справжні Telegram-вакансії, атрибуція карти знову видима, а дашборд-чіпи читаються з ≥ 4.5:1.
Виправлено
- Auto-pipeline валідує ДО збереження: звіт без оцінки 0–5 або без блоків A–G завершує ран на кроці evaluate (
rejected: trueу SSE-навантаженні) — ні файла звіту, ні рядка трекера. t.me-записи, що проходили довжину-гейт v1.248.2, після того релізу дали ще два сміттєвих звіти; тепер безіменні записи відхиляються зcompany/role not identifiable. guessCompanyRoleбільше не вигадує компанію з мессенджер-хостів —t.me,telegram.me,vk.com,linkedin.comта інші в EMPTY_DOMAINS: Telegram-пост падає на ім’я каналу, а безіменний запис відхиляється замістьunknown-role.post-qa-cleanupспершу читає оцінку з шапки: авто-пайплайн вирізує блок SCORE_SUMMARY перед збереженням, тож стара перевірка лише-блоку маркувала кожен збережений Telegram-пост сміттям — на проді 8 з 41 мають оцінку в шапці, і--applyвидалив би їх. Оцінка з шапки (черезparseReportHeader) тепер первинний сигнал, блок — запасний. Dry-run друкує причину по кожному файлу.- Атрибуція Leaflet видима на #/map — docs-FAB її перекривав (порушення ліцензії OSM); кут піднято на 96px.
- Дашборд-чіпи читаються з ≥ 4.5:1 — підписи
dash-chipі варіант--manualпереведено на--foggy-strong(було 4.31:1).
Додано
- Фейли auto-pipeline несуть
rejected: trueу SSE-помилці — серверний eval-таймер використовує флаг, щоб пропустити запис pipeline замість повторних спроб кожні 2 години.
Примітки
- Дрейф ar/ja не повторився на перезапуску (див. v1.248.2).
- Саме очищення проду досі чекає явного «так» користувача (CAR-61c); з цим фіксом очищення більше не може видалити справжні Telegram-вакансії.
[1.248.2] — 2026-10-10
Регрес-раунд: eval-таймер більше не перетворює сміття pipeline на «Last evaluation», розбір SCORE виживає у реальних форматах, а трекер нарешті говорить вашою мовою.
Виправлено
- Звіти з порожнім JD більше не пишуться. Ворота JD авто-пайплайну піднялися з 50 до 200 символів до виклику LLM — сторінки-заглушки (
example.com/qa-v167-…) завершуються SSE-помилкою + попередженням у консоль замість звіту…-t-role-<ts>.md, який дашборд показував як «Last evaluation» (на проді 42 таких файли). - Розбір SCORE_SUMMARY виживає у реальних форматах: «4,2/5», «4.2 / 5», «4.2», «SCORE :» — все валідно; кома з 1–2 цифрами — десяткова, з 3 — тисячні (старий код читав «4,200» як 4.2 і пропускав ворота 0–5). Провал пише в лог лише рядок
SCORE:— ніколи вміст звіту (логи CI публічні). - Статуси трекера перекладені: таби стадій і бейджі рендерять локалізовані підписи в усіх 18 локалях; канонічне значення в даних, фільтрах і URL не чіпається.
- Чесний ETA: «⏱ ~30s» став «~2–4 хв» у evaluate/advisor/ринковому звіті (реальний діапазон 86–285 с), а підзаголовок evaluate читається A–G.
- Доступність: лічильник API-keys у config і лічильник pipeline тепер ≥ 4.5:1 (було 4.31:1); чип фасету з порожнім значенням більше не рендериться без доступного імені.
- UI-полірування: кнопка Delete збережених пошуків виглядає деструктивною лише при вибраному пошуку (інакше disabled); картка EMAIL профілю не обрізається на 1440 px; у пігулці live-evals з’явився відступ іконка/текст включно з RTL; docs-FAB не перекриває атрибуцію Leaflet; «?» help не переноситься під табами stats; usage отримав повітря між підзаголовком і табами; health тепер каже career-ops-ui.
Додано
scripts/post-qa-cleanup.mjs: dry-run за замовчуванням,--applyз автоматичними бекапами (*.bak-<ts>, звіти переносяться доqa/cleanup-backup-<ts>/): рядки ZZ-QA-TEST у трекері, звіти*-t-role-*без валідного SCORE, записиexample.comу pipeline.
Примітки
- Дрейф ar/ja з регрес-раунду не повторився на перезапуску (обидва: lang ✓ + A–G ✓) — запис ta у language-check і посилений парсер SCORE йдуть як defense-in-depth.
- Саме очищення проду чекає явного «так» користувача: спершу dry-run-план, потім
--apply.
[1.248.1] — 2026-10-09
location_filter отримує батьківські тири always_allow та block_hard (CAR-60) — мультикраїновий рядок локації виживає при блок-листі сусідів, а явний жорсткий блок більше не можна врятувати.
Виправлено
always_allowсильніше заblock: приblock: [romania, poland, croatia]іalways_allow: [united kingdom]workable-merged комірка «London, United Kingdom · Romania · Poland · Croatia» проходить — домашній регіон є в опціях (споживчий кейс workable #4806 тепер у батьківській формі).block_hardсильніше заalways_allow: странові терміни, які не можна врятувати збігом always_allow десь у рядку.strict: trueвраховуєblock_hardяк обмежувальний тир; секціяlocation_filterу docs/help документує обидва тири ×18.
Примітки
- Свідомо не перенесено цього разу: батьківський компілятор локаційних ключів із word-boundary та його таблиця штатів USPS (веб-морда тримає case-insensitive substring; таблиця служить US-центричним конфігам — відкладіть під CAR-60, якщо знадобиться). Перехідна allow-list форма споживчого тесту workable лишається другим вердиктом поруч із новою батьківською.
[1.248.0] — 2026-10-09
Паритет з батьком: п’ять апстрім-фіксів провайдерів/liveness перенесено — борди, що переїхали на нові API, працюють далі, а банер закритої вакансії на окремому рядку більше не пропускається.
Виправлено
- Gupy слідує за API на
portal.gupy.ioі шле імена фільтрів, які API реально читає (workplaceType/type) — старі плюральні імена мовчки ігнорувалися;countryбільше не надсилається (сконфігурована країна малює warning з ім’ям запису). Конфіденційні роботодавці дропаються за точним збігом ярлика («Confidencial Seguros» виживає), аmax_pagesобмежений 100 — API відповідає 400 після offset 10 000, і truncation-warning тепер радить звузити ключові слова. - Workable зберігає всі краї мультикраїнової вакансії: сіблінги під одним job-URL згортаються в один рядок («London, United Kingdom · Romania · Poland · Croatia») замість перемоги першої країни.
- Liveness: банер на своєму рядку — не частина рядка вище — «Sign in if you want to apply» / «Get notified when this position is filled» під вакансією читається як клауза закриття, а справжнє «until filled» через рядки досі читається активним.
- Radancy читає сіблінг-елемент
job-locationу legacy-розмітці картки (розкладки родини Barclays кладуть локацію після</a>) — рядки без власної локації залишаються порожніми, а не займають локацію сусіда. - SmartRecruiters fallback-посилання зберігають сконфігурований slug — вакансія з відсутнім/недовіреним
refсинтезує публічний URL зі slug тенанта, ніколи з відображуваного імені.
Додано
- No-ports задокументовано: апстрім-фікс
_httpproxyfetchImpl(у веб-морди немає проксі-гілки — порт зламав би identity-перевірку DNS-rebinding-гварда й додав undici-залежність, якої репо свідомо уникає); апстрімніverify-ats(+226 рядків) іcv-facts(веб-морда реалізує власний ATS-скоринг і CV-парсинг); батьківський дашборд Go TUI та CLI-скрипти; перестановка імпортів у Teamtailor (косметика).
Примітки
- Відома прогалина як follow-up: батьківський tier
always_allowв location-фільтрі (споживчий тест workable виживає завдяки йому UK-блок) — в location-фільтрі веб-морди цього tier немає; порт виразив той самий вердикт споживача через allow-list тест. Заведено окремою задачею.
[1.247.0] — 2026-10-09
Хвіст дизайн-свипу CAR-49…58 закрито — всі решта minor/polish-знахідки плюс темна карта, яку минулий реліз навмисно відклав.
Виправлено
- Карта рендерить справжній темний режим. Зум/шари/атрибуція потемніли ще у v1.246.0; тепер і тайли слідують темі: темний режим малює OSM-шар із CSS-інверсією (без вендорних ключів), а keyed-пресет Carto-dark доступний через
MAP_TILE_DARK_URL(його хост додано до дозволу CSPimg-src). Шар перемонтується наживо при зміні теми. - Жовті callout’и мають темні варіанти (
config,batch, hero-попередження) через токен-механіку v1.246.0 — і попутно спійманий регрес:.callout aзабивав.btn-primary; тепер скоупиться:not(.btn). - URL в інфо-картці apply більше не обрізається посеред слова.
- Кнопки заголовка CV — одна іконочна система (текст; мікс 📁/📄/💾 прибрано).
- Таби статусів трекера в одному регістрі — sentence case з локальними особливостями (CJK/ar/hi/ta без родових форм).
- Іконки топбара — єдиний stroke-SVG набір — мікс 🔔/🌙/🩺 прибрано; кнопка теми малює sun/moon через CSS.
- Картка Pipeline на дашборді позбулася рожевого highlight’у (кільце лише hover/focus).
- Видалення збережених пошуків явно деструктивне (
.btn-danger, а не нейтральний ghost). - У preview pipeline з’явився справжній порожній стан (заголовок + підказка + CTA, патерн трекера).
- Мета часу/вартості — один патерн:
UI.pageMetaмалює ⏱ + вартість одним рядком під заголовком в’ю (evaluate/deep/auto/mode-page/orientation). - Auto-pipeline позбувся ✨ — єдиний декорований h1 повернувся на спільний лівий край.
- Batch docs URL — інлайн-посилання.
- interview-digest / orientation мають спроєктовані порожні стани (заголовок + підказка + CTA).
- Export career-plan неактивний, доки план порожній, дії — в один рядок.
- stats: дублюючий заголовок «Market report» прибрано, плейсхолдер регіону вміщується (коротший ×18, фікс-ширина).
- Config більше не тікає абсолютними шляхами — серверний
displayPath()показує~/…або голе ім’я файлу. - Хелпер LLM_PROVIDER — три короткі рядки (×18 +
pre-line) замість 150-символьної mono-стіни. - EMAIL-картка профілю тримає адресу в один рядок (ellipsis; перенос лише ≤480px).
- Текст Health більше не ламає слова («Run buttons» лишається цілим; overflow-wrap anywhere).
- Інлайн-код у help виживає в RTL (LTR-ізоляція, nowrap, скрол).
- Пігулка «Live evals» у hero більше не стикається в ru/ar (flex-wrap, нестисливі іконки).
Додано
- Пресет темних тайлів (
MAP_TILE_DARK_URL+ attribution-env) задокументований у коді; додання до CSPimg-srcпокриває хост Carto.
Примітки
- Дефолт темних тайлів — CSS-інверсія OSM: keyless Carto зачинив базові мапи за API-ключ (перевірено запитом), тож keyed-пресет — опція, а не дефолт.
[1.246.0] — 2026-10-09
Усі десять топ-знахідок сеньйор-дизайн проходу CAR-48 виправлено — включно з блокером і всіма сьома мажорами.
Виправлено
- Apply dark: посилання на інфо-картці знову видиме. Картка тримала світлу поверхню в темному режимі; посилання «Need Playwright? See …» сиділо на ≈1.0:1. Theme-aware
.callout--info(токени surface/border/text/link) піднімає темне посилання до ≈7.3:1. - RTL більше не перевертає числові композиції. Score-пігулки («1.5 / 5»), бенди звітів («≥ 4.5», «< 3.5») і дашбордне «/ 5.0» отримують напрямлену ізоляцію (LTR-span’и); ar-словник там, де прозі краще, переписує діапазони словами.
- CV-markdown лишається LTR. Textarea і прев’ю рендеряться
dir="ltr"арабською; обв’язка навколо залишається RTL. - Колонка DATE у звітах більше ніколи не переносить ISO-дату (nowrap + табличні цифри).
- Сайдбар закінчується над USAGE HUD (резерв скроллпорту + fade) — останній пункт навігації більше не їде під панель; RTL-дзеркалання враховано.
- Мобільний скан @390: «Save search» повністю видимий — ряд контролов переноситься і стискається замість обрізання на краї.
- Контроли карти слідують темній темі (zoom, шари, атрибуція). Самі тайли навмисно не чіпали: їх origin закріплений серверним CSP
img-src. - Chat FAB більше не перекриває інтерактив — скрол-паддінг сторінок розчищає його (кнопки Disable у Portals, score-пігулка у Reports, інпут two-pager, атрибуція Leaflet).
- Журнал активності розмовляє мовою інтерфейсу: 22 слаги дій локалізовано ×18 локалей (з fallback на сирий id для невідомих) і чіпи фільтрів перекладаються; колонка TARGET залишається — її заповнює auto-pipeline.
- Форми assessments отримують справжні лейбли (sentence case, зв’язка
htmlFor/id) замість полів лише з placeholder’ами.
Примітки
- Не в цьому релізі: темні тайли карти (потрібне розширення CSP
img-src— серверна правка, навмисно не клієнтська); темні варіанти жовтих callout’ів (config,batch) — механіка токенів уже готова для продовження.
[1.245.0] — 2026-10-09
Тамільська (தமிழ்) приєднується як 18-а локаль інтерфейсу — перша нова мова після хвилі з 17 локалей, з повним паритетом ключів.
Додано
- Тамільська (
ta) — 1449/1449 ключів із повним паритетом доen, без загублених плейсхолдерів, у хінді-стилі indic-tech-конвенцій (провайдери, моделі, шляхи та команди лишаються латиницею).detect()приймаєta,ta-IN,ta-LK; тамільська — LTR (закріплено тестом). - Повний docs/help/ta.md (33 H2 / 125 H3, лічильники джерел звірені з живим реєстром, канаркові якорі Hermes, дослівний YAML), плюс дзеркала README.ta.md і CHANGELOG.ta.md.
- Перелічувальні ворота 17 → 18: LANGS, detect-проби, i18n-аудит, changelog-паритет, RTL-страж, CI
langs— заодно підтягуєhi, якого раніше не було в CI-списку.
Примітки
- Лендінг cvstart.org поки лишається на 17 локалях — переклад лендінгу окреме завдання; синк контенту сайту вже віддає docs/help/ta.md і ta-changelog.
report-header.jsREPORT_LABELS залишається на 17 — парсинг тамільських звітів прийде разом із генерацією тамільських звітів у батьківському репо.
[1.244.2] — 2026-10-08
Полірування сторінки сканування: лаунчер, рядок стану, панель репостів і фільтри перероблені в один консистентний хром — і таблицю результатів більше не можна розтягнути даними бордів.
Виправлено
- Клітинки зарплати показують лише грошовий діапазон. Деякі борди кладуть увесь текст пільг у поле зарплати — видима клітинка тепер показує лише грошову частину, а текст переміщує в підказку (раніше: 6-рядкова клітинка, що розтягувала кожен рядок).
- Рядки з порожнім заголовком показують компанію (потім нейтральне тире) замість гігантської порожньої клітинки.
- Бейдж seniority ніколи не переноситься у своїй колонці фіксованої ширини.
- Рядки без оцінки показують притлумлене «◎ —» із підказкою, що порівняння з two-pager не знайшло збігів (раніше: нічого, що виглядало зламаним поруч з оціненими рядками).
Додано
- Редизайн хрому: картка-лаунчер (вирівняний ряд контролів, домінантна primary-кнопка), рядок стану терміналу з крапкою стану (бездія/виконання/готово/помилка) підтримує reduced-motion, панель репостів має стелю висоти зі sticky-заголовком (датасет із 1,560 кластерів рендерив панель 155,000 px), і блок фільтрів як рівномірна адаптивна сітка з вирівняним футером.
Примітки
- Решта #/scan ідентична v1.244.1 — змінилися лише полірування хрому та чотири виправлення вище. Тести 5045 → 5068 unit.
[1.244.1] — 2026-10-08
Виправлено
- Scan icons announce real values. The boost/fit/score icons’ accessible names leaked the raw dict templates (“Fit: {band}”, “Boosted by {by}”) —
{band}/{by}/{score}are now substituted (band word, booster keyword, numeric score). [HIGH a11y, caught live in the v1.244.0 regression round] - Title pipes split correctly. A 4-segment title like «C++ | Rust | Go Developer | Onsite» no longer lifts “Go Developer” out as a “country” — the split demands the segment resolves to a real country (conservative
Countries.detectCountry). - Meta dates render for every row.
daysSinceaccepted date-only ISO, but the API serves full timestamps for ~83% of dated rows — those rows showed no date segment.
Примітки
- Решта #/scan ідентична v1.244.0 — змінилися лише три виправлення вище.
- Свідомо не зроблено: стан фільтрів у hash URL, навігація рядків клавіатурою та парсинг
5 lakhs INR(існуючі хвости).
career-ops-ui v1.267.1 · page 2/12 · mirror of the project changelog for cvstart.org