Журнал змін
GitHubКожна значуща зміна, реліз за релізом. Той самий файл постачається в репозиторії та перекладений усіма 19 мовами.
[1.267.1] — 2026-11-04
Виправлено
- Ремонт релізного поїзда (гейти CI v1.267.0) — ланцюжок лічильників тестів у PROJECT-CONTEXT більше не рветься на голові v1.267.0 (формат стрілки, який матчить eval-грейдер), QA-промпти реорганізовано (один живий промпт на реліз, v1.266.0 архівовано), а пофайлове покриття піднято над ratchet 98/90: applicantpro salary-рендерер втрачає недосяжний fallthrough (salaryRangeOf доводимо видає undefined, {min}, {max} або {min,max}) із закріпленими односторонніми гілками ≥/≤ через fetch-потік, а сьют board-response-cache закріплює no-store-байпас, 304 від викликача проти незбереженої дошки та витіснення на MAX_ENTRIES.
- Тести: живий лічильник — 7395 (сьюти хвилі v1.266.0/1.267.0 несуть більше спеків, ніж ранні примітки; ланцюг переприв’язано до виміряного рівня: 7392 після хвилі + 3 гейт-тести в цьому патчі); coverage 98.51 line / 90.35 branch.
Примітки
-
Тег v1.267.0 залишається вказувати на червоний мерж-коміт — не переприв’язується (сервер відмовляє переприв’язаним тегам); v1.267.1 — деплой-носій.
-
Патч-ремонт поїзда v1.267.0: гейти CI зелені, дерево коду суттєво не змінювалося.
[1.267.0] — 2026-11-04
Додано
- Чотири нові джерела сканування — GermanTechJobs, Darwinbox, Keka, ApplicantPro/isolved (реєстр 125 → 129 = 124 EN + 5 RU · 124 адаптери) — друга хвиля parent-sync проти career-ops @
7d8feb6d(87 апстрім-комітів). GermanTechJobs (#6346): бордовий RSS наgermantechjobs.de/rss(~626 елементів одним запитом); роботодавець — останній сегмент" @ "заголовка; заголовок без нього відкидається і ніколи не записується на борд; лише явний вибір черезprovider:. Darwinbox (#6416): безавтентифікаційний JSONalljobsза<tenant>.darwinbox.in, id компанії береться з careers URL, посторінковий POST-обхід (50/сторінку,max_pages20/200) зі зупинкою на короткій сторінці, прочитаною з власної кількості рядків джерела. Keka (#6199): два запити — оболонка careers ізredirect:'manual', скрипт якої називає UUID фіду (невідомий тенант відповідає 302 наTenantNotFound.htmlі читається як іменована 404), далі весь відкритий інвентар одним масивом embed-jobs. ApplicantPro / isolved (#6252): числовийdomainIdіз bootstrap, потім{success, data:{jobs, jobCount}};jobCount > rowsпопереджає, що список неповний; opt-inapplicantpro: { fetchDetails: true }збагачує з JSON-LD у темпі 250 мс. - Повторне використання відповідей бордів через ETag (#6105) — у межах одного скану відповідь борду, отримана за останні 15 хвилин, використовується як є, а старіші ревалідуються з
If-None-Match(304 зберігає тіло); ключ кешу покриває метод + URL + credential-заголовки + тіло, накладні читання згортаються на in-flight promise, аCAREER_OPS_HTTP_CACHE=0/_TTL_MSце перевизначають. У пам’яті на кожен прогін скану (у web-ui немає on-disk контракту кешу) — зафіксоване відхилення.
Виправлено
- Дати на чотирьох бордах (#6274) — remoteok, remotive, workingnomads та IBM тепер видають дату публікації, тож фільтр «Posted within» працює й там; naive-часову мітку remotive нормалізовано до UTC (її per-machine баг з віком був живий), а
_sourceIBM тепер справді запитуєdcdate. - Помилково написаний ключ portals.yml відхиляється (#6107) — EN-скан тепер кидає іменовану помилку (файл + ключ + підказка найближчого ключа, відстань Левенштейна ≤ 2) до будь-якого запиту, замість того, щоб мовчки ігнорувати
titel_filter:. - getro продовжує обхід повз старий закріплений пін (#6406) — закріплена протухла вакансія як і раніше відкидається, але більше не завершує пагінацію; обхід закінчується на справжній короткій сторінці.
Примітки
- Свідомо не зроблено: seen-фільтр RU-сканера не є мовно-чутливим, і його шлях парсингу portals не проходить ґейт на помилково написані ключі (hand-off записані у спекі хвилі — жоден RU-борд ще не видає per-language рядків); remoteok лишає свій рядок дати з offset замість нормалізації до UTC (його жива форма детермінована). Тести 7096 → 7243; Playwright 173 pass / 0 fail / 1 opt-in skip; реєстр 129 = 124 EN + 5 RU · 124 адаптери (усі лічильні ґейти регенеровані з живого реєстру).
[1.266.0] — 2026-11-01
Додано
- Мовні версії Personio більше не з’їдають одна одну (CAR-223: батьківські #4789 + #5044 + #5285) — відкладений стек, виконаний як окремий реліз. Бік скану (#4789):
scan_history.dedup_include_language: true(строгий opt-in — у стані OFF поведінка побайтно ідентична) дає мовним версіям однієї вакансії вижити в dedup через механізм варіантів у seen-history (languageFormsFromUrl/recordUrlVariant/isUnseenLanguageVersion); канонічний ключ викидає параметриlanguage/lang/locale. preferred_languages (#5285): упорядковує рядки мовних версій вакансії preferred-first (стабільно, порядок провайдера при нічиїй) — незалежно від перемикача (OFF лишає preferred-версію, ON лишає всі версії в цьому порядку), з портомvalidatePreferredLanguagesз validate-portals. Fan-out personio (#5044): сторінка careers береться без примусового?language=en(власний?language=xxтенанта шанується) і видає один рядок на мовну версію зlanguage+ URL?language=<code>;dedupIncludeLanguageдодає per-language запити (стеля 5, із паузами); фолбеки /xml і job-link без змін. recruitee отримує таку саму видачуlanguage+preferred_languages(opts або запис). Спільні хелпери живуть уserver/lib/language-preferences.mjs.
Примітки
- Записані hand-off, свідомо не зроблено: seen-фільтр RU-сканера не є мовно-чутливим, і жоден RU-борд ще не видає per-language рядків; дзеркало
url-key.jsна клієнті та обв’язка валідації в portals-маршруті лишаються відкритими. Тести 7096 → 7203; реєстр без змін 125 = 120 EN + 5 RU · 120 адаптерів.
[1.265.0] — 2026-11-01
Додано
- Два нові джерела сканування — Apple і Bulldogjob (реєстр 123 → 125 = 120 EN + 5 RU · 120 адаптерів) — хвиля parent-sync проти career-ops @
fe857ecf. Apple (jobs.apple.com, #5496 + #6196): сайт — React Router SSR-застосунок без придатного JSON API, тож джерело читає hydration-блоб звичайного пошукового маршруту (window.__staticRouterHydrationData); виявлення веде запис (apple.queriesз сортуванням за релевантністю + осіapple.teams, без дефолтів — запис без обох кидає помилку), сторінки обмеженіmax_pages(дефолт 2, стеля 50) з паузою 200 мс, кожен сегмент URL кодується surrogate-безпечно, а вакансія, яку Apple мітилить як PIPE, несе в snippet підказку «може бути talent pipeline» — фільтрації за нею немає. Bulldogjob (bulldogjob.pl, #6253): польський tech-борд теж не має JSON API — листинги несуть__NEXT_DATA__(50/сторінку,totalCount);careers_urlзапису — це збудований користувачем пошук сайту (фільтри живуть у шляху), кожен запит несе явний/page,N, а обхід зупиняється на короткій сторінці, заявленому total чиmax_pages(дефолт 25, стеля 100) з тегом обрізання на часткових бордах. - Рework пагінації IBM (#6273) — фіксований обхід на 600 записів стає
max_pages(дефолт 20, стеля 300, перевизначення на записі) з паузою 200 мс між сторінками та per-page retry черезfetchJsonWithRetry; непошкодена логіка: впала перша сторінка кидає виняток, впала пізніша зберігає зібране і називає сторінку, і лише повна сторінка на власній стелі запису попереджає «raise max_pages».
Виправлено
- justjoin датує пропозиції за
lastPublishedAt(#6254) —publishedAtкандидатського API зсувається при кожному оновленні від роботодавця, тож більшість борду несла сьогоднішню дату іmax_posting_age_daysпропускав тижневої давності пропозиції; тепер перемагає рання дата публікації з фолбеком, коли її немає або вона не парситься. - jobvite складає рядки, що ділять один posting URL (#6127) — публічний фід повертає рядок
<job>на місто для одного detail URL; парсер лишав перше місто, а решту лічив дубликатами. Тепер сиблінги вносять лише локації, з’єднані·у порядку фіду, перший title/date зберігається, remote перераховується на об’єднаній локації. - Liveness: ранг UltiPro (#6090) — UKG не має per-posting JSON-ендпоінту і відповідає 200 на закриті, тож усі 57 закритих UltiPro-вакансій читалися uncertain. Сторінка деталю вкладає запис:
OpportunityIsClosedtrue/false → expired/active, notice «opportunity unavailable» → expired, решта лишається браузеру. Банер Built In «Sorry, this job was removed» став hard-expired (#6336) — сторінка тримає повний JD і APPLY-контрол; тепер перемагає банер, а сусідній бейдж «Job removed» без «sorry» не матчиться.
Примітки
- Свідомо не зроблено: parent-бік половини фіксу jobvite про location-компілятор scan.mjs (web-ui документує USPS-компілятор як no-port) і поле
noteбатька для PIPE-підказки Apple (в job-shape web-ui немаєnote; вона їде уsnippet). Тести 7096 → 7135 (+39); Playwright 173 pass / 0 fail / 1 opt-in skip; coverage gate ≥ 98/89; реєстр 125 = 120 EN + 5 RU · 120 адаптерів (усі лічильні ґейти регенеровані з живого реєстру).
[1.264.3] — 2026-11-01
Виправлено
- Хвиля загартування rate-limit (review LOW №1–3 з
REVIEW-2026-10-31-v1.264.1) —writeRateLimit()(express-rate-limit@7.5.1, 60 req/min route×IP, 429 JSON + Retry-After) тепер покриває усі 47 мутувальних ендпоінтів (було 2): замінено 9 неправильно скоуплених гардовllmRateLimit(/api/config— перезапис.env!,PUT /api/batch,/api/batch/merge, outcome, followup/seed, assessments, stats/snapshot, mock-interview/save, networking/save), 3 гарди складено перед LLM-бакетом на дорогих шляхах (/api/evaluate,/api/deep,/api/auto-pipeline), а 33 голі реєстрації обв’язано (редактори CV/profile/modes, усі записи трекера, pipeline POST/restore/DELETE, jds, 7/api/run/*однією правкою циклу, posting-guard, Playwright-PDF inline, portals toggle/health/track, discover, reports, export/docx, two-pager, career-plan, memory і три DELETE-маршрути)./api/apply-helperлишається голим в allowlist (чисто локальне обчислення, закріплено); 16 чисто-LLM маршрутів за дизайном тримають умовний LLM-бакет. Сторож повноти (tests/rate-limit-coverage.test.mjs, red-first при 27 голих) завалює CI на будь-якій майбутній нетротльованій мутувальній реєстрації. TRUST_PROXY— opt-in + повторно заряджені валідації (review LOW №1–2) —app.set('trust proxy', N)приймає envTRUST_PROXYяк кількість хопів (ніколи булевеtrue— воно ключувало б за лівий, керований зловмисником кінець XFF); за замовчуванням не задано, тож loopback зберігає задокументовану per-route деградацію з ключем за сокетом.validate: falseстає прицільноюwriteValidateConfig(): виключено лишеxForwardedForHeader(XFF від Caddy без trust proxy ігнорується свідомо), всі інші валідації — зокремаtrustProxy— знову гучні, тож майбутня неправильна конфігураціяtrust proxy: trueвиявиться якERR_ERL_PERMISSIVE_TRUST_PROXY, а не мовчанням. ВласнийkeyGeneratorвидалено свідомо: express-rate-limit прив’язує свої proxy-валідації до генератора ключів за замовчуванням, а per-route ізоляція не змінилася (одинMemoryStoreна виклик фабрики).- Стан копіювання на cvstart.org скидається через 2 с (CAR-141) — стан «Скопировано ✓» тепер тримається 2 секунди, як вимагає приймальний критерій (було 1.5 с), red-first-пін у
tests/site-copy-feedback.test.mjs. Це закриває останній критерій CAR-141, доступний агентам; оновлення демо-відео лишається відкладеним за рішенням власника (2026-11-01).
Змінено
- Бандли довідки називають 19 мов (×19) — речення §19 «Localizing the app» і §32 «Run it from OpenWorker» більше не відстають на дві локалі: кожен бандл тепер називає 19 з தமிழ் і Tiếng Việt у власному стилі переліку (ar вживає прописане «التسع عشرة»), а власний залишок «cả-17» у §32
vi.mdтеж виправлено. Гейти структури (33 H2 / 125+ H3) стабільні побайтно. - Гігієна стану —
AGENTS.mdкаже v1.264.2 / 7082 tests (було v1.86.x / 1566);PLAN.mdпозначає релізи 1–7 Shipped, а його абзац про ship-train каже docs ×19; коментарStatsBar.astroтепер описує, що насправді міститьfacts.adapters(123 = загальна кількість sources, історична назва ключа збережена для споживачів); шапкаi18n.jsкаже 19 мов; QA-промпт v1.264.2 несе справжнє покриття (98.45/90.05) і арифметику Playwright (176 = 174 harness + 1 opt-in skip + 2 script-level suites).
Примітки
- Свідомо не робимо: хвиля parent-sync за записаним списком портів лишається в BACKLOG — форк мусить спершу синхронізуватися за апстрім
a9476866(7 із 10 комітів списку портів ще не в локальному дереві батька; IBM/UltiPro/Bulldogjob/apple оцінені в S–M, personio language-dedup відкладено до власного релізу). Перезапис демо-відео CAR-141 свідомо відкладений власником («видео не надо пока», 2026-11-01). - Tests 7082 → 7096 (+9 сторож покриття + 5 trust-proxy); Playwright 173 pass / 0 fail / 1 opt-in skip (+2 піни шеврона всередині наявних кейсів); покриття 98.46 рядків / 90.10 гілок; evals 14/0/1skip; реєстр без змін 123 = 118 EN + 5 RU · 118 adapters.
[1.264.2] — 2026-11-01
Виправлено
- Заголовки груп сайдбару приєднуються до контракту обрізання (CAR-219) — підписи перемикачів груп (
.nav-v2-toggle > span) тепер обрізаються з ellipsis і несуть локалізований тултіп через той самий механізмsyncLabelTitles(), що й елементи навігації з CAR-203; шеврон ніколи не стискається, перемикач лишається клікабельним, RTL не ламається. Red-first-пін уtests/nav-v2.test.mjs(+1 → 7082 tests).
Змінено
- Текст cvstart.org постачається всіма 19 мовами (CAR-220) —
meta.helpDesc,helpTeaser.c1.descіfaq.a2перекладені в кожній локалі сайту (у v1.264.1 вони були свідомо лише в EN); GitHub Pages уже вивіз оновлений текст на cvstart.org. - Гігієна беклогу + рев’ю (CAR-221/222) — шість пунктів «port when merged upstream» закрито: усі сім апстрім-PR змержені, чотири пункти вже портовані, два живуть на боці батька. Рев’ю-документ
docs/reviews/REVIEW-2026-10-31-v1.264.1.mdз’являється в репозиторії (0 HIGH / 0 MED, три LOW-хвости записані в BACKLOG), а список портів parent-sync зафіксовано (6 PORT / 3 VERIFY / прогалина з джерелом apple; апстрім-хвіст 118 комітів @ VERSION 1.38.0).
Примітки
- Виправлення версійного штампу дзеркала журналу змін (
92a05fcf) увійшло в цей тег: кожне пагіноване дзеркало на cvstart.org тепер несе версію релізу, що згенерував його, тож грейдерsite-build-after-fanoutнарешті виконує свою справжню логіку замість того, щоб пропускатися. - Свідомо не робимо: тротлінг решти мутируючих POST (review LOW №3) і зв’язка бакетів із trust-proxy — записано в BACKLOG для наступної хвилі загартування. CAR-141 (демо-відео) лишається за власником.
- Tests 7081 → 7082 (пін CAR-219); Playwright 176; реєстр без змін 123 = 118 EN + 5 RU · 118 adapters.
[1.264.1] — 2026-10-31
Виправлено
- Пігулки сайдбару акуратно обрізають довгі локалізовані підписи (CAR-203) — span із підписом навігації тепер повноцінний учасник flex-розкладки (
min-width: 0+overflow: hidden+text-overflow: ellipsis+nowrapуv2-nav.css), аsyncLabelTitles()уnav-v2.jsвіддзеркалює локалізований підпис у тултіп через той самий ключdata-i18n(нуль нових i18n-ключів) — довгий «Профинансированные компании» знову читається при наведенні, замість того щоб жорстко обрізатисяoverflow: hiddenрядка. Ціль дотику 44 px, активне зафарбування та RTL-дзеркало не торкнуті; red-first-пін уtests/nav-v2.test.mjs. - Одна стеля оцінювання 480 с для всіх локалей (CAR-218) — remote-QA v1.264.0 зловила перехідні оцінювання
502 · 300 s · 0 chнаesіzh-CN(прицільний повторний запуск пройшов, але стіна була реальною).EVAL_TIMEOUT_MSтепер480_000, аevalTimeoutMs(lang)повертає його безумовно —GEMINI_EVAL_TIMEOUT_MSі auto-pipeline успадковують. Локале-залежний токенний бюджет із CAR-174 (16384 / 32768) лишається єдиним розподілом за локаллю.
Змінено
- FAQ на cvstart.org несе точні кількості джерел (CAR-217) —
{sources} job-board sources: {sourcesEn} international boards plus {sourcesRu} Russian onesпідставляється зfacts.json, який генерується з живого реєстру (123 = 118 EN + 5 RU), тож числа більше ніколи не розсинхронізуються; локалі, які досі на старішому формулюванні~{adapters}, зберігають округлення черезadaptersRounded. Попутно — покращення англійського тексту (meta.helpDesc,helpTeaser.c1.desc,faq.a2називають командне меню, групований пайплайн із undo та канбан-трекер).
Примітки
- Свідомо не робимо: покращення тексту site i18n (
meta.helpDesc/helpTeaser.c1.desc/faq.a2) цього релізу лишаються лише в EN — інші локалі зберігають коректні, але кругліші формулювання до наступної док-хвилі (їхнійfaq.a7отримує плейсхолдери точних кількостей там, де перекладено). CAR-141 (демо-відео) лишається за власником; замінену гілкуcar136/m141видалено, її ідеї записано на Linear-таску (відновлювано через тегarchive/car136-m141). - Tests 7041 → 7081 (пін CAR-203 + переписані піни CAR-218); Playwright 176; реєстр без змін 123 = 118 EN + 5 RU · 118 adapters.
[1.264.0] — 2026-10-31
Додано
- Inline-редагування статусу в трекері — бейдж статусу кожного рядка
#/trackerстав компактним<select>канонічних стадій (зGET /api/tracker/stages, жодного захардкодженого whitelist’у).POST /api/tracker/statusвикликає канонічнийset-status.mjsбатька під локомapplications.md(whitelist станів черезstates.mjs, проброс--dry-run/--note, fail-soft 502 із санітизованою деталлю, spawn через argv-масив). Успіх оновлює рядок на місці (tone-клас + лічильники вкладок, фокус зберігається); невдача відкочує значення і показує тост. Рядки, які релей не може адресувати — вручну вписаний невідомий статус, backfill-сентинел#— лишають read-only бейдж (LOW-фікси route-review; ревʼюер: жодного HIGH/MED). - Попередження сервера на кожній AI-сторінці —
evaluate,deep,auto-pipeline(сторінка і модалка),career-plan,market,cv-studio(humanize + tailor) тепер показують масивwarningsсервера (обрізання за вихідними токенами, дрейф мови, корекції дат) смугоюbadge-warn(role="status"); модалка auto-pipeline більше не втрачає попередження з done-фрейму. Новий ключllm.truncatedNotice×19. - Хвиля покриття (+111 тестів) —
routes/map.mjs100/94.9,geocode.mjs96.3/92.9,content.mjs99/84.9,networking.mjs96.4/89.1,sources/habr.mjs100/98.2 (новіtests/cov-*-routes.test.mjs×3,tests/sources-habr.test.mjs); нижня межа за гілками втримана на ≥ 90 %. - Бандл закриття Linear CAR-191…216 (
docs/reviews/2026-10-30-linear-car191-216-closure-bundle.md) — готовий до вставки close-out для власника з SHA комітів і коментарями-доказами по кожному CAR.
Виправлено
- Скан-джерело Trudvsem — порожня сторінка (
results: {}, зокрема «за кінцем») більше не вбиває весь скан (чисті нуль рядків); конверти помилок API (HTTP-200 зstatus: 500) кидають виняток із текстом самого API; пагінація йде за record-зміщеннями (було за індексом сторінок — сторінки мовчки дублювалися, ~52 вакансії замість сотень); обхід зупиняється до зони відмови API на глибоких зміщеннях (offset × limit ≥ 10000). Перевірено живим end-to-end. - habr — заголовки/компанії очищаються від тегів після декодування сутностей (паритет із hh.mjs):
<script>у заголовку картки більше не доїжджає до рядків скану як жива розмітка; легітимний>>у заголовках виживає. - PUT /api/profile (raw-шлях) — блок
candidate:без непорожньогоfull_nameвідхиляється, як і на шляху злиття полів (раніше зберігавсяfull_name: null). - Правдивість хінтів —
.docприбрано зі списку accept завантаження CV і зcv.uploadHint×19 (парсер його ніколи не підтримував);config.openaiModelHint×19 називаєgpt-5(сервер відображає будь-який*-codex-id на нього);config.anthropicModelHint×19 і дефолт Anthropic тепер віддзеркалюють тири батька — дефолтclaude-sonnet-5-5, premiumclaude-opus-5-5. - Посилання на доки Claude Code →
code.claude.com(старий URLdocs.anthropic.com/en/docs/claude-codeпадає TypeErrors у links-гейті). - help ×19: видалено булет Row-density (перемикач покинув UI в v1.263.0) і полагоджено 18 родин зламаних
**bold**-спанів — плюс клас витоку рендера виправлено ×19 (другий порожній рядок після кожної таблиці перед абзацем; глиф pipe поза bold-комірками): 0 літеральних**у рендері в усіх 19 бандлах довідки.
Примітки
- Дефолтна модель Anthropic змінилася на
claude-sonnet-5-5(стандартний тир батька). Деплої, що покладалися на старий мовчазний дефолт (claude-sonnet-4-6), оновлюються автоматично — запінітьANTHROPIC_MODELу.env, щоб лишитися на попередній. - Свідомо не робимо: два LOW-зауваження рев’ю лишені відкритими намірно (inline-селект не дзеркалить деградацію
movesReadyканбана — чесний тост на кожну невдачу є обраною поведінкою; гілкаcode: 'timeout'лишається покритою контрактом fake-writer, а не тестом >30 с). CAR-141 (демо-відео) лишається за власником. - help ×19 отримали пост-релізний прохід прозою (булет щільності видалено, клас витоку рендера виправлено) на
mainпісля виходу v1.263.0 — цей реліз перший тег, який це везе. - Також у цьому тезі: інфраструктура чіпа підтвердженості (парсери звітів + бейджі перегляду), раннер fix-report-links (
POST /api/runners/report-links, картка «Виправити» на#/health) та оцінка/підтвердження вартості батчу із записом usage — потрапили на main між зрізом гілки й тегом.
Tests 6905 → 7041 (+136) · Playwright 176 (173 pass / 1 opt-in skip) · покриття 98.5 % рядків / 90.1 % гілок · реєстр без змін: 123 sources = 118 EN + 5 RU · 118 adapters.
[1.263.0] — 2026-10-30
Додано
- Шар полірування анімацій — новий
motion.css(останній аркуш застосунку): появи модалок/поповерів (scale .97 + згасання, 130–180 ms), spring-поява тостів + м’який стан відходу, spring-натискання перемикача теми, scale(.97) при натисканні чипів/табів, stagger-інфраструктура (кроки 50 ms, кап 12), згасання.page-enterпри зміні маршруту (роутер), переходи focus-halo на чипах/табах. Кожне правило безпечне заprefers-reduced-motion(рух падає до none, opacity/колір лишаються). - Коректність на тач-пристроях — 68 правил hover-підйому не були захищені: тач-пристрої лишалися з «завислою» піднятою карткою після тапу. Усі transform-ховери тепер обгорнуті в
@media (hover:hover)(нейтралізатор motion.css), а підйом картки застосовується лише до справді інтерактивних карток (.card:has(a),a.card,button.card,[role=button].card) — більше жодної хибної аффордансності на статичних картках. - A11y: кожен контроль має програмне ім’я. Новий хелпер
A11yDumpдає імена всім 16 readonly-дампам виводу LLM; 12 полів лише з плейсхолдером (CV Studio ×6, config ×2, пошук у скані, селект оцінки в трекері, нотатка outcome, редактор плану, URL авто-пайплайна, баг-репорт) отрималиaria-label; гейтa11y-form-wiresтепер вимагає цього для всього класу (було 26 безіменних місць, стало 0 + allowlist із задокументованими причинами).
Виправлено
- #/scan: липка шапка таблиці більше не малюється поверх пошуку в топбарі після прокрутки під нього (z збігався з топбаром, перемагав пізніший DOM — тепер token-derived нижче нього); інпути «Зарплата от/до» знову нормального розміру (правило
.v2-scan-bar .inputflex витікало в колонковий flex поповера і задавало flex-basis 200 px як ВИСОТУ — тепер>-скоуп + захист поповера); кнопка перемикання щільності видалена рішенням власника (обв’язка збережена, i18n-ключі видалені ×19). - Загартування deploy-воркфлоу (F1–F6): інпут
providerвалідується за набором символів до будь-якого склеювання в shell; діагностика hermes редагує значення (регістронезалежний фільтр ключів + маскування значень + обрізання до 80 знаків на всіх трьох стоках, маскуються і?token=); крок hermes поважаєinputs.app_dir; public_url пропускається лише по https; анти-даунгрейд-сторож має 6 виконуваних пінів (рефи без дефолтів, сторожа до Deploy, строгий regex рефів). - Хвіст рев’ю, red-first: round-trip бекслеша при CSV-імпорті (повторний імпорт знову дедуплікується), стріп
<head>у cv-voice 4827 ms → 13 ms,statedYearsMinобчислюється ліниво, TOCTOU capture у posting-guard, wildcard MiniMax CN, пропуск deel-токенів, size-гарди імпорт-пікера до читання. Імпорт-модалка: зведений nav-гард, file-інпут видаляється на cancel, гардоване читання дублів,numчерез reduce. - Батч UX від власника: вертикальний ритм hero дашборда (inline-діти склеювалися в один рядковий бокс); зсув активного рядка TOC довідки на −8 px (порядковий inline gutter + колонка номерів); мобільний drawer (видима кнопка ✕, скидання scrollTop, фантомне згасання –paper обмежене
.has-hud); сторінка docs-assistant більше не переповнюється при довгому URL у відповіді (ланцюг gridmin-width+overflow-wrap: anywhere); «Недавні» пояснені (хінт) + очищаються; Save у career-plan зводиться знову після невдалого завантаження; URL-prefill в evaluate більше не спалює LLM-виклик на плейсхолдері вставки; Enter в auto-pipeline поважає IME-композицію. - Контраст (WCAG-аудит, 296 пар): градієнт docs-fab 2.43 → 6.4:1, пара toast.success → ok-soft (1.75 → AA у темній), фліп banner btn-dark →
--ink, світлі варіанти scan-logbtn/reposts-badge/fab-chip/close, і межі контролов форм до 3:1 (--cv3-input-line, WCAG 1.4.11 — було 1.2:1). - Візуальний свіп: порожній стан трекера — картка на всю ширину (раніше обрізалося всередині таблиці 760 px при 390); рядок консолі скану еліпсифікується замість напівобрізання; плаваючий чат-FAB більше не накриває контент при 390 (ціль 44 px, inset 12); порожній стан «Application statuses» на дашборді тримається патерну v2-empty; хінт «?» у stats отримав роздільник + ціль 44 px; таб Input у career-plan пояснює, куди падає результат; markdown §1 довідки (18 зламаних bold-спанів).
Змінено
- Акцент: синій → багряно-червоний (прохання власника, в колір логотипа, hue 15) — 5 cv3-токенів × 3 тематичні блоки; текст бульбашки чату
docs-assistantіде через--accent-ink(був літеральний #fff, 2.39:1 у темній); акцентна заливка funded іде через--accent-soft. Danger лишається hue 25 зі своїми обробками. - Доки: лічильник у таблиці тестування README ×17 (дрейф ери v1.248), §1 довідки + 18 markdown-спанів, «118» у довідці fr/it/ta, вендорські ціни CHECKED_ON 2026-10-30, факти HANDOFF/CONTEXT/TRACEABILITY.
Примітки
- Свідомо не робимо: переробка свіпу скелетонів лишається за бортом (контракт car112 закріплює поточне згасання дослівно); кросфейд іконки перемикача теми лишається заміною
display(його спостерігає Playwright-контракт theme-toggle); scale для.btn:activeне дублювався (володіє app.css); stagger — лише інфраструктура, плитки дашборда візьмуть його пізніше; проза довідки ×19 (згадка щільності, markdown §1, «118») їде наступним фан-аутом. - Повні докази remote-QA: прицільний повторний запуск
jaзелений (376 s / A–G ✓) на v1.262.4; en/it зелені раніше, в еру токен-щільності.
Tests 6869 → 6905 (+36) · Playwright 176 · покриття 98.4 % рядків / 89.9 % гілок · реєстр без змін: 123 sources = 118 EN + 5 RU · 118 adapters.
[1.262.4] — 2026-10-30
Виправлено
jaдолучається до token-dense локалей оцінювань (ja→ стеля 32768 токенів + годинник 480 с). П’ятилокальний прицільний повторний запуск на проді v1.262.3 розділив чотири нічні тайм-аути502 · 300 s · 0 chна перехідні та детерміновані: ar (250 с), ko (291 с) і zh-CN (259 с) пройшли свої повторні запуски, разовий мовний дрейф ru не повторився — алеjaдвічі врізалася в стандартну стелю з нульовим виводом (повний запуск ×19 40981578549 та прицільний 41012725839). Той самий клас форми, що it/pl. Red-first-пін:evalMaxTokens('ja') = 32768,evalTimeoutMs('ja') = 480_000, інші локалі зберігають 300 с.
Примітки
- Свідомо одноцільовий патч: фікс
ja— єдина зміна, яку несе цей реліз, — більше він нічого не несе. ar/ko/zh-CN залишаються на стандартній стелі за сьогоднішніми даними (вони проходять; нічні збої були перехідними збоями навантаження провайдера), а дрейф ru виявився разовим поколінневим флейком.
Tests 6868 → 6869 (+1) · Playwright 176 · реєстр без змін: 123 sources = 118 EN + 5 RU · 118 adapters.
[1.262.3] — 2026-10-30
Виправлено
plдолучається до token-dense локалей оцінювань (pl→ стеля 32768 токенів + годинник 480 с). Повна remote-QA на проді v1.262.1 спіймала польське оцінювання, що вмирає на стандартній стелі, —502 · openrouter · 300 s · 0 ch— а прицільний повторний запуск, відправлений з v1.262.2, відтворив це точно (запуски 40960568766 та 40968958062): 18/19 локалей пройшли A–G,itфінішувала на 302 с всередині token-dense годинника 480 с, аplдвічі врізалася в стіну з нульовим виводом. Детермінований тайм-аут, а не флейк, — той самий клас форми, що перевівitу v1.262.1. Red-first-пін:evalMaxTokens('pl') = 32768,evalTimeoutMs('pl') = 480_000, 18 стандартних локалей зберігають 300 с.
Примітки
- Свідомо одноцільовий патч: фікс
pl— єдина зміна, яку несе цей реліз, — більше він нічого не несе; вміст review-tail v1.262.2 вийшов окремо.
Tests 6867 → 6868 (+1) · Playwright 176 · реєстр без змін: 123 sources = 118 EN + 5 RU · 118 adapters.
[1.262.2] — 2026-10-30
Виправлено
- Хвіст wave-рев’ю — 7 знахідок низької серйозності, кожна red-first. Round-trip бекслеша CSV-імпорту:
\у комірці записувався подвоєним і зчитувався подвоєним, тож повторний імпорт того самого CSV пропускав дедуп company+role і міг створити дублікат рядка — читач тепер розекранює\\після розбиття (round-trip запис→читання без втрат, повторний імпорт дедуплікується). Стрип<head>у cv-voice був квадратичним на незакритих тегах — 4827 мс → 13 мс на вході 64 КБ, яким маршрут обмежується.statedYearsMinганяв повний парсер років для кожної вакансії кожного джерела навіть тоді, колиyears_minне був налаштований — тепер ліниво, секунди CPU на великий скан зникли. Захоплення, видалене між readdir і statSync, ламало лістинг posting-guard (TOCTOU — файли, що зникли, пропускаються). Дефолт MiniMax CN матчився лише з точнимapi.minimax.cn— будь-який базовий URL*.minimax.cnтепер отримує CN-модель.$-токени Deel, що не є hex-референсами, більше не просочуються в сніпет як літерали. Пікер імпорту перевіряєfile.sizeДОfile.text()— серверна стеля 512 КБ відповідала 413 лише після того, як браузер розпарсив можливо величезний файл — з локалізованою бульбашкоюimport.tooLarge×19. - Загартування deploy-воркфлоу (рев’ю воркфлоу F1–F6).
providerбув єдиним входом, що інтерполювався у віддалений shell без валідації кодування —'у значенні виривався з лапок і запускав довільний код на проді; тепер вайтліст^[A-Za-z0-9_-]+$перед будь-яким склеюванням, а grep сторожі ключа більше не може фальшиво пройти. Крок діагностики hermes друкував сирі journalctl + дампи unit у публічний лог; його фільтр пропускав великі*_API_KEY=і нічого не маскував — тепер спільний регістронезалежний redact + маскування значень + обрізання до 80 знаків на всіх трьох стоках, рядки запитів?token=теж маскуються. Крок hermes ігнорувавinputs.app_dirі хардкодив/opt/career-ops/src/.env.public_urlдіставався до curl без перевірки https (ін’єкція опції через початковий-). А сторожа анти-даунгрейду — фікс інциденту v1.238.0 — не мала виконуваного гейту: шість нових пінів у ci-workflows (refs не мають дефолтів, сторожа запускається перед Deploy, строга regex для refs). - Хвіст import-модалки (рев’ю SPA). Модалка озброюється тим самим nav-гардом, що й модалка outcome — догуляння геть більше не залишає її сиротою поверх іншого в’ю. Прихований file input видаляється на
cancel, а не лише наchange— повторні закриття більше не витікають input’ами в document.body.plan.duplicatesчитається через гардовану форму у прев’ю. Розподілnumтрекера використовуєreduceзамістьMath.max(...spread)— spread упирається в ліміт аргументів приблизно на 100 тис. рядків.
Змінено
- Документація: пункт «118 EN adapters» довідки тепер несе лічильник у fr/it/ta (решта 15 локалей вже мали його). Вендорські ціни на cvstart.org перевірені повторно проти офіційних сторінок прайсингу — всі чотири без змін, CHECKED_ON → 2026-10-30. Пост-релізний sweep дрейфу виправив 42 застарілі рядки лічильників/версій по README ×19, CONTEXT.md, CLAUDE.md і мануалах SDD (залишки ер 109/104 та v1.262.0).
Примітки
- Свідомо не робимо: нативний
window.confirmу cv-studio (ера CAR-179) лишається — конвертація вUI.confirmце follow-up, а не пункт патча; тултіп шаблонних фраз<mark title>лишається лише тултіпом (допустимий мінімум a11y);remote-qa.ymlтримаєcancel-in-progress: true— повторний dispatch свідомо витісняє running-регресію. - Повна remote-QA на проді v1.262.1: маршрути ×19 ✓ · сайт ×19 ✓ · посилання ✓ · живий LLM 18/19 локалей A–G ✓ —
plвпіймав одиночний тайм-аут провайдера502 · 300 s · 0 ch(не token-dense територія; прицільний повторний запуск їде з цим релізом).
Тести 6867 (6864 pass / 0 fail / 3 skip, +26 від v1.262.1) · Playwright 176 · покриття 98.42 % рядків / 89.94 % гілок · реєстр без змін: 123 джерела = 118 EN + 5 RU · 118 адаптерів.
[1.262.1] — 2026-10-29
Патч італійських оцінювань: it долучається до token-dense мов (стеля 32768 токенів + годинник 480 с) після того, як ера OpenRouter спіймала її на обрізанні звіту просто перед блоком SCORE_SUMMARY — а інструментарій перемикання провайдера, який зробив діагноз можливим, виходить в deploy-воркфлоу.
Виправлено
- Оцінювання
itобрізане перед SCORE_SUMMARY (хвіст CAR-190): remote-QA 40928864980 (перший запуск на поповнених кредитах) — оцінюванняitтривало 264 с і повернулося з 15 045 знаками приtok ~15635на стелі 16 384: блоки A–G цілі,---SCORE_SUMMARY---зник. Той самий клас форми, що принісtaїї стелю в v1.261.1 (CAR-174).itдолучається доTOKEN_DENSE_EVAL_LOCALES(ta,hi→ta,hi,it): і бюджет 32 768 токенів, і годинник 480 с тепер застосовуються, а/api/evaluate+ auto-pipeline лишаються на єдиному джерелі істини вllm.mjs. - Звіт
enбез блоків — разовий поколінневий флейк: той самий запуск побачив одне оцінюванняen, яке повернулося мовно правильним без жодного з блоків A–G; прицільний перезапуск пройшов чисто (21 091 знак, A–G ✓). До token-dense набору не додається — один флейк не історія.
Додано
- Deploy-воркфлоу:
mode=hermesіmode=provider(ops-хвіст CAR-190):mode=hermesперезапускає шлюз Hermes і друкує повну діагностику (unit-файл із вилученими секретами, слухачі до/після, журнал sudo, зондування per-port, власний погляд застосунку наHERMES_BASE_URL, баланс кредитів OpenRouter);mode=providerперемикаєLLM_PROVIDERзі сторожею наявності ключа (відмовляє, коли*_API_KEYцільового провайдера відсутній) і перезапускає застосунок — так прод переїхав наopenrouter, коли кредити Hermes вичерпалися на льоту. Сторожа ref deploy-режиму валідується лише дляmode=deploy, тож dispatch-и recon/verify знову працюють (зміна required-inputs ери v1.261.3 їх замовила).
Примітки
- Свідомо не робимо:
enне додається до token-dense набору — один звіт без блоків це поколінневий флейк, не історія обсягу. - Свідомо не робимо: посилена ta-директива мови не поширюється на
it— мовного дрейфу в італійській не спостерігалося. - Свідомо не робимо: запис нового демо-відео UI-v2 лишається за власником (CAR-141) — лендінг тепер усе одно вбудовує через
youtube-nocookie.com. - Тести 6841 → 6841 на релізній гілці.
[1.262.0] — 2026-10-29
Мінор з upstream-функціями: борд Deel приєднується до сканера (реєстр 123), CSV чужого трекера імпортується прямо в #/tracker, перевірку цілісності posting-guard і перевірку голосу CV доставлено як релеї/лінти, сканер вчиться фільтру років досвіду, а кнопки Apply ще сімома мовами перестають читатися як відсутні — плюс замінено виведену з експлуатації дефолтну модель MiniMax, і репости більше не фабрикуються між джерелами.
Додано
- Джерело борду Deel (CAR-184): реєстр 123 джерела = 118 EN + 5 RU · 118 адаптерів — автоматично розпізнає tenant-борди
jobs.deel.com/<slug>(Klarna та інші); Next.js flight-пейлоад несе весь каталог в одному запиті (посади, локації, формат роботи, дати, опис), заборонений шлях/api/ніколи не торкається, tenant, що тримає кар’єру в іншому місці, відповідає редиректом, який джерело відхиляє пойменовано, а компенсацію свідомо не маплено (суми без інтервалу виплат були б здогадкою); гейти лічильників ×5 + help §17 ×19. - CSV-імпорт трекера (CAR-181):
POST /api/tracker/import-csvіз dry-run-прев’ю та заблокованим commit — лапки RFC-4180, дати у формах ISO / день-спочатку / назва-місяця, етапи чужого трекера згортаються через канонічні стани (невідомий етап осідає як Evaluated з нотаткою, ніколи не викидається), дедуп за нормалізованим URL вакансії, прочитаним із комірки markdown-посилання або колонки Notes, ліміти 512 КБ / 2000 рядків;#/trackerотримує кнопку Import CSV із модалкою прев’ю, i18n ×19. - Релей posting guard (CAR-182):
POST /api/runners/posting-guard— детермінована zero-LLM брама цілісності JD з батька, розв’язується номером звіту через правило префікса захопленьjds/(з паддінгом чи без, порівнюється як число) або явним санітизованим шляхом; дитина отримує лише argv, стелю 30 с і fail-soft-контракт; дія «Posting guard» на#/reports/:slugрендерить знахідки, i18n ×19. - Перевірка голосу CV (CAR-188): 27 шаблонних фраз із резюме (
passionate about,proven track record,leveraged,spearheaded,delve…) плюс тире-евристики (діапазон років на кшталт 2022–2024 — не ознака;20–30%— ознака) як read-onlyPOST /api/cv-studio/voice-checkі картка#/cv-studio, що підсвічує точні збіги; чекліст Tailor зростає до шести інваріантів; i18n ×19. - Скан-фільтр років досвіду (CAR-180): парсер jd-years (65 формулювань, при сумніві ніколи не відкидає) читає заявлений мінімум з описів вакансій, нова опція
years_min:у portals пропускає кваліфікаційно нижчі вакансії якskipped_experience(спостережний статус історії, що ніколи не пришпилює URL), аyearsMinїде в снапшоті скану на майбутній UI. - Кнопки Apply ще сімома мовами (CAR-185/189): в’єтнамська
Nộp đơn ứng tuyển, болгарськаКандидатствай/Кандидатстване/Външно кандидатстване(іменник заанкерований до початку мітки — посилання на статті-поради не Apply), нідерландськаSolliciteer/Solliciteren, іспанська¡Apúntame!/Postularse(мітки, не дієслово — FAQ-посилання більше не заважають), китайська申请职位/投递(заанкеровано до цілого контролу) — живі вакансії на цих бордах перестають читатися якno_apply_control.
Виправлено
- Дефолтна модель MiniMax (CAR-187): виведену з експлуатації дефолтну
MiniMax-Text-01замінено наMiniMax-M3.1-Flashнаapi.minimax.ioіMiniMax-M3наapi.minimax.cn; базовий URL запінений до вендора (невендорський хост відхиляється в рантаймі, і ключ ніколи туди не дістає); підказка налаштувань ×19 називає нові дефолти. - Репости вимагають того самого джерела (CAR-186): роль, виставлена на LinkedIn і на Greenhouse роботодавця, більше не звітується як репост — кластери тепер діляться за
sourceHost(), який згортає регіональні субдомени лише на відомих сайтах (de.linkedin.com≡www.linkedin.com, алеca.example.comлишається окремо), згортає аліаси хостів Greenhouse в одне джерело і зрізає кінцеву крапку. - Мовний дрейф тамільських оцінювань (CAR-183): посилена тамільська директива тепер сидить у волатильному хвості промпта поруч із бюджетом об’єму (слот CAR-174) — remote-QA-звіти знову потрапляють тамільською після інтермітентного дрейфу, поміченого у v1.261.2.
Примітки
- Свідомо не робимо: релей posting-guard виходить fail-soft — форк не несе
posting-guard.mjsбатька досі (upstream-PR #6146 досі відкритий), тож ендпоінт відповідає{available:false, reason:"script-not-found"}, доки синк батька його не доставить. - Свідомо не робимо:
yearsMinне виступає UI-чіпом#/scanу цьому релізі — поле їде в снапшоті, а фільтр лише через конфіг portals. - Свідомо не робимо: upstream-зміни Machine Summary у modes/reports (#6161/#6160/#6157) не портовано — вони прийдуть із задеплоєним батьком, а не як web-ui-дзеркала.
- Тести 6734 → 6841 на релізній гілці; ключ дедупу сканера недоторканий (поведінку нормалізації URL не змінено).
[1.261.3] — 2026-10-28
Патч завершення дизайну: фаза 3 дизайну Lovable (щільність + повне виведення коралового), host-pool скану (32 компанії, 10 на хост), шість виправлень локацій в upstream-провайдерах, джерело Trakstar Hire (реєстр 122), undo + unsaved-guard у CV Studio — плюс виправлення P3 фінальної регресії та паритет документації.
Виправлено
- Підпис «related» кластерів мапи 3.16:1 → 4.68:1 (обидві теми): світлий
--map-score-midзатемнено до#567b75(запропонований#5f7f7aдав лише 4.37 — потрібен був крок темніший); темний бік вже 4.98:1, недоторканий. Закріплення red-first уmap-cluster-visual. - Фокус-кільце skip-посилання: останнє літеральне обведення
#ff385c→var(--accent-c)— усі 78 фокус-кілець тепер слідують акцентному токену. - Акцентна топологія
.tab-btn.is-active(#584, фаза 3): контроль змішував три колірні сімейства (коралова підмальовка + danger-текст + синя рамка); тепер — єдина пара accent-soft. - Виправлення локацій в upstream-провайдерах ×5 (#580, CAR-176): workday прапорець
Fully Remote(не місце), workable розгортання telecommuting + штат лише за відсутності міста, successfactors висяча кома, lever кордон слова для remote, icims мітки з префіксомLocation. - http-json: без повторних спроб для вичерпаного таймауту (#580) — аборт за дедлайном нетранзитивний в обох retry-помічниках (upstream виміряв ~94 с на одній дошці Ashby при 30 с × повтори).
- Паритет документації (#587): останні лічильники ери 17/18 у HANDOFF/CONVENTIONS/README (uk отримала бракуючу секцію Локалізація; §-історію продовжено до §32/§33 — арифметика 33 H2 перевірена).
Додано
- Фаза 3 дизайну Lovable (#584): токени щільності живуть — рядки таблиць ≈32px, контроли 30px, чипи 24px (виняток відступів WCAG 2.5.8); кожен кораловий rgba-залишок перенесено на cv3-пари (
.dash-hero,.badge-bad, гало,.tab-btn.is-active); оголошено--ease-out, TOKEN_HANDOFF порожній; контракт.callout--ok/--danger. - Джерело Trakstar Hire (#583, CAR-177): реєстр 122 джерела = 117 EN + 5 RU · 117 адаптерів — пін до хоста jsapi.recruiterbox.com, обхід за offset зі зупинками на коротких/порожніх/повторних сторінках, часткові результати зберігаються, мічення
trakstarTruncated; гейти лічильників ×5 + help §17 ×19. - Host-pool скану (#579, CAR-178): до 32 компаній одночасно, ліміт 10 на хост (upstream-патерн keyed-limiter) — снапшот байт-у-байт ідентичний послідовному прогону (red-first гейт на timing-моках).
- Undo + unsaved-guard у CV Studio (#582, CAR-179): обмежений стек undo (30) над результатами AI-прогонів із
Cmd/Ctrl+Z, що працює на розкладках, відмінних від QWERTY, гард незбережених змін при полишенні маршруту (live-перевірка брудності, confirm/rewind/beforeunload), i18n ×19. - Виправлення upstream-провайдерів протестовано ×17 (частина CAR-176); підсумок remote-QA називає джерела скану, що впали, +
tok ~Nна кожне оцінювання (#567, вже у v1.261.2 — спостережуваність використано для триажу цього релізу).
Примітки
- Акцент тепер синій (концепція Lovable): первинні дії / активні стани / фокус-кільця слідують
--cv3-accent; червоний зарезервовано для danger (.btn-danger,toast.error, danger-текст). Корал лишився лише в PNG-активі логотипа (рішення власника). - Щільність: рядки таблиць ≈32px / контроли 30px / чипи 24px — виняток відступів WCAG 2.5.8 задокументовано; канарку 44px з v1.26.1 переписано на контракт токенів.
- CodeQL: усі 10 відкритих алертів закрито (4 відхилено як хибні спрацювання з обґрунтуванням: задокументовані класи llmRateLimit/atomicWrite; 6 виправлено в джерелі).
- Тести 6655 → 6734 на релізній гілці; поведінку джерел сканера, окрім нового джерела Trakstar, не змінено.
[1.261.2] — 2026-10-28
Патч стабільності та шліфування: кеш геокодування переживає крах посеред запису (CAR-173), UI.md рендерить продовження маркованих рядків і вкладені списки, витоки i18n-ключів у відрендереній довідці закрито ×17, оцінювання отримує ліміт токенів з урахуванням локалі для ta (CAR-174), хром RTL дзеркалиться логічними властивостями (85 знахідок), скріншоти мапи перекадровано ×19, а бульбашки кластерів мапи проходять контраст у темній темі — плюс перша фаза інтеграції дизайну Lovable (пасивна таблиця стилів tokens-v3.css), бекфіл документації (реліз-рев’ю + спеки) і винятки покриття для варіативності CI-нод.
Виправлено
- Втрати кешу геокодування від розірваного запису (CAR-173, #562): обидва шляхи скидання на диск (дебаунсний insert-flush, exit-flush) писали
.cache/geocode.jsonголимwriteFileSync— truncate-then-write — тож крах, OOM чи SIGKILL посеред запису лишали розірваний файл, аJSON.parseнаступного старту падав, мовчки викидаючи весь персистентний кеш (тижні бюджету Nominatim), а не лише денний. Обидва місця запису тепер ідуть черезfile-lock.mjs::atomicWrite(temp у тому самому каталозі → rename → unlink при невдачі) — той самий контракт, який FULL-R1/FULL-M1 застосували доpipeline.md,cv.md,profile.yml,career-plan, вхідних даних batch і memory. Саму механіку скидання CAR-142 (дебаунсний insert-flush, exit-flush, обмежене в часі завершення роботи) перевірено неушкодженою red-first: тести (a)/(b) закріплюють, що вставки досягають файлу у вікніGEOCODE_SAVE_MSі він лишається валідним JSON; (c) — механізмний гейт плюс поведінкові прогони обох шляхів скидання, які стверджують відсутність temp-сміття і парсабельний файл. - Продовження рядків списків у UI.md (#563): обробник списків споживав лише підряд ідучі рядки
-/*, тож відступний рядок-продовження після маркера випадав з емітованого<ul>, а прохід абзаців пропускав фрагмент — продовження витікало сирою прозою з літеральними маркерами**/бектиків (~95 висячих прогонів на локаль по файлах довідки; en.md 95, ru.md 104). Рядок-продовження — провідні пробіли, не новий блок (заголовок, маркер, обгорнутий NUL-ами плейсхолдер fence, порожній рядок) — тепер приєднується до попереднього<li>як текст, склеєний<br/>, з прогономinline()на кожен рядок, тож маркери наголосу ніколи не паруються через склейку; гард плейсхолдера fence — NUL як перший символ, а не голе збігання за токеном. Вкладені маркери-, нумеровані списки, fence всередині списків і заголовки всередині списків лишаються окремою задокументованою прогалиною (наступна хвиля документів). - Витоки i18n-ключів у відрендереній довідці ×17 (#561): remote-QA знайшла 85 сирих i18n-ключів, видимих у відрендереній довідці (кореневу причину доведено відтворенням: те саме споживання однорядкових маркерів у UI.md — загорнутий рядок-продовження, емітований сирим текстом, тримав бектики літеральними; секції довідки v1.260/261 писали багаторядкові маркери). 163 маркери розгорнуто в один рядок у 17 локалях — лише пробіли, жодного перекладеного слова не торкнуто (звірено старе проти нового). Новий гейт
tests/help-rendered-keys.test.mjsпроганяє кожен бандл через справжній ланцюг (stripGithubOnlyBlocks+ vm-завантаженийUI.md) і падає, якщо будь-який із 1,711 ключів з’являється в тексті; red-first 17 локалей → зелено. Інструментарій відтворення їде в репозиторії:scripts/remote-qa/help-render-scan.mjs(vm) +help-render-crosscheck.mjs(справжній DOM). - Remote-QA стверджує контракт палітри CAR-122 (#561):
prod-regression.mjsтепер очікує палітру команд після Ctrl+K (.nav-v2-palette[role=dialog]+ фокус уnav-v2-input) АБО застарілий фокус пошуку як fallback, замість того щоб мовчки приймати що завгодно; юнітtests/remote-qa-ctrl-k.test.mjsзакріплює 6 сценаріїв на фейковому DOM, red-first 0/6 → 6/6. - Кадрування скріншотів мапи ×19 (#560): звіт власника щодо
images/job-map-en.png— половина пошуку топбара + обрізаний h1 зверху, сіра смужка бічного списку біля правого краю, кластер маркерів, розрізаний рамкою біля Лондона, і біла горизонтальна лінія через мапу на ~2/3 висоти.capture-map-screenshots.mjsтепер чекає на спорожнення черги геокодування (located + noLoc === total, а не лічильник N/N — рядки Remote структурно не мають локації, тож старе очікування ніколи не завершувалося б), ховаєheader.topbarі#notif-drawerна час знімка, обрізає точно прямокутник.job-map-page(жодна панель не розрізається краєм кадру), віддаляє мапу на два кроки, тож жоден кластер не сидить наполовину розрізаним, фіксує висоту мапи, робить звичайний скріншот в’юпорту (captureBeyondViewportресемплить композитний тайловий шар і малює білі смуги на межах рядів тайлів) і вирівнює контейнер мапи за цілими пікселями. Усі 19 скріншотів job-map перезнято. - Бульбашки кластерів мапи — контраст у темній темі + вміщення підпису (#572): owner-sweep P2/P3 — у темній темі кільця кластерів сиділи на фіксованій білій заливці (1.83:1); кільця тепер беруть
var(--paper)із цифрами score-палітри (виміряно 4.98:1+, із заливкою 9.53:1; найнижчий діапазон математично неможливо зафарбувати на темному — він тримає 6.1:1 через чесний шляхdata-dim), а підписrelatedвміщується у свою бульбашку (підбір шрифту через rAF, числа ніколи не зменшуються). - #/scan — перемикання мови, повторне заряджання скелетонів (довитік #534, агент у польоті, прибуває перед тегом): довитік за виправленням власника у v1.261.0 (#534) — повторно заряджені скелетони при перемальовуванні після перемикання мови більше не переживають завершене повторне отримання даних.
- Дрейф документації (CAR-139, агент у польоті, прибуває перед тегом): лічильники README ×19, HANDOFF ×19, факти CONVENTIONS пересинхронізовано з живим реєстром/станом тестів.
Додано
- Реліз-рев’ю + бекфіл спек (док, #559/#558):
docs/reviews/REVIEW-2026-10-28-v1.261.1.md(хвиля v1.261.0/v1.261.1 прорецензована проти своїх спек) і бекфіл SDD-спек тієї ж хвилі — хвіст BACKLOG тепер називає прогалину, що лишилася (окремі тести дляsources/habr.mjs). - Глибокий RTL-обхід (#571): AR-гернес проходить 39 маршрутів × LTR-проти-RTL (дзеркалання X, переповнення, обрізання, сигнатури text-align) — 85 знахідок виправлено: фіксований хром (toast, сайдбар, дровер + його × закриття, docs-FAB, usage-HUD, skip-посилання) переведено на
inset-inline-*/border-inline-*, таблиці — наtext-align: start/endу 17 файлах в’ю + 2 бібліотеках, глобальне правило[dir=rtl] text-align:rightвилучено (воно суперечило центруванню колонки зірочок скану), ізоляцію score-колонки CAR-58 перенесено на spanltrNum. - Гернес клавіатурної a11y (#570):
tests/playwright-a11y-keyboard.mjsпроходить 11 маршрутів (порядок і пастки Tab, індикатори фокусу, Escape-оверлеї, Enter/Space) — 0 продових дефектів; регресійне покриття нове. - Обхід неперекладеного i18n (#565): 36 значень, ідентичних англійським, перекладено в 9 локалях (
*.rolePh, ключі config, ta-маніфест) + гард обхіду із закритим allowlist; аудит i18n сайту (cvstart.org) (#568): 20 ключів × 18 локалях перекладено (весь блокdemo.*/stats.t.*/showcase.tab*був англійським всюди), 1 мертвий ключ вилучено ×19. - Спостережуваність remote-QA (CAR-175, #567): підсумок називає джерела скану, що впали (
stderr samples, редагування перед зістриганням), і друкуєtok ~Nз використання провайдера; логіка живе в чистомуsummary-parts.mjsз дріфт-гейтом. - Вкладені марковані списки в UI.md (один рівень, #564): маркери
-рендеряться як вкладений<ul>(безпечно щодо плейсхолдерів fence) — останні 2 висячі фрагменти прози на локаль зникли. - Інтеграція дизайну Lovable — фаза 1 (#566) / фаза 2 (на гілці, прибуває перед тегом):
public/css/tokens-v3.cssпортує концепцію Lovable дослівно (oklch-поверхні 0–3, AA-анотовані пари чорнила, сімейство акцентів, ok/warn/danger/info/neutral soft+ink, score-діапазони A–G ×2 теми, щільність + фокус + тіні); фаза 2 активує її на наявних іменах токенів (акцент розділено на blue/danger, score-діапазони + callout на cv3-токенах) з оновленими гейтами контрасту/ролі/темного. - Ліміт токенів оцінювання з урахуванням локалі (CAR-174, #569):
evalMaxTokens(lang)— ta/hi запитують 32768 (три SDK-клієнти перетискали назад до 16384; виправлено обидва шари), плюс бюджет обсягу тамільської в промпті (≤5 речень на блок, ≤900 слів) — ta-звіт remote-QA у v1.261.1 було обрізано на ліміті вихідних токенів (без Block B–G + SCORE_SUMMARY). - Продовження справжніх виправлень CodeQL: новий інструментарій рендерингу QA замінив ручну обробку сутностей структурними розрізними проходами +
htmlToText({cap: Infinity}); твердження редагування закріплюють точний замаскований вивід; невикористані константи RTL-гернеса вилучено. - Політика винятків покриття для варіативності CI-нод (habr #558, states #561):
sources/habr.mjs(гілкове покриття CI node-matrix 87.23 проти локальних ≥89) іstates.mjs(CI 86.49 проти локальних ≥89) приєднуються до винятків уscripts/coverage-baseline.json— файли, чиє виміряне на CI гілкове покриття структурно нижче локального бар’єру через варіативність версій node, отримують виняток замість опускання всього бар’єра.
Примітки
- Контракт Ctrl+K (CAR-122): комбінація відкриває палітру команд — вона НЕ фокусує поле пошуку напряму; пошук фокусується з палітри. Прод-регресія remote-QA тепер стверджує саме це (#561).
- Відхилені хибні спрацювання CodeQL ×5 (FP-dismiss): алерти
js/missing-rate-limitingна main чекають токенsecurity_events— дія оператора, а не зміна коду. - Прибуло після зрізання гілки: #561/#562/#563/#564/#565/#566/#567/#569/#570/#571/#572 — усі злиті в main; агенти #534-скелетонів і CAR-139 зливаються перед тегом. Вкладені марковані списки глибше одного рівня і щільна інлайн-геометрія в’ю лишаються задокументованими прогалинами.
- Тести 6609 на релізній гілці (+15 від v1.261.1; ще ~13 від #561 при злитті — 2 нові гейт-файли); єдині червоні тести до fan-out — гейти паритету CHANGELOG ×19 (locale fan-out — наступний крок оркестратора); джерел сканера не торкнуто (реєстр 121 = 116 EN + 5 RU · 116 адаптерів).
[1.261.1] — 2026-10-28
Клієнтський патч прод-QA: виправлення P1-краху на #/two-pager, Ctrl+K на розкладках, відмінних від QWERTY, мобільна хвиля 390px (переповнення docs-assistant, перекриття чипів мапи, гернес на 39 маршрутів), завершення CAR-137 (конкретні помилки, v2empty ×5, підказки ×13 + сторінки режимів) і CAR-140, що розставляє справжні v2-скріншоти всюди.
Виправлено
- TDZ у two-pager (CAR-170, P1, #554):
Cannot access 'previewTimer' before initialization— редактор збережених тегів викликавonChangeпід час конструювання для кожного відновленого чипа, тоді як цикл конструювання лексично розташовувався вище заlet previewTimer, тож увесь рендер#/two-pagerпадав (без h1, ×19 локалей). Планувальник і прив’язка тепер живуть вище за цикл; поведінковий тест виконує справжній модуль в’ю — до виправлення та сама прод-помилка, після — відрендерена сторінка — плюс дріфт-гейт лексичного порядку. - Ctrl+K на розкладках, відмінних від QWERTY (CAR-172, #554): обидва обробники комбінації зіставляли лише
e.key— на російській розкладці комбінація приходить якkey='л' code='KeyK'і нічого не робила. Тепер зіставлення заe.code === 'KeyK'у nav-v2.js та app.js; відтворено в реальному браузері синтетичною комбінацією «л»/KeyK. Контракт CAR-122 переформульовано (див. Примітки). - Переповнення docs-assistant на 390px (CAR-172, #554): стартові чипи CAR-136 були
.btn(nowrap,flex-shrink: 0) — один нерозривний елемент 418px виштовхував#/docs-assistantна 56px за межі в’юпорту 390px; патерн стиснення CAR-146 (у v2-llm.css, гейти CSS на 800-LOC недоторкані) зводить його до 0. - Перекриття чипів мапи @390 (#552): на в’юпорті 390px мета-чипи мапи перекривали рядок CTA (map.css).
- CAR-137 — конкретні помилки + v2empty ×5 (#550): п’ять генеричних fallback-ів помилок (‘Error’ / ‘failed’) стали конкретними дієвими ключами — збій evaluate POST, потік auto-pipeline без термінальної події, додавання scan→pipeline, завантаження кластерів репостів, undo у pipeline; останні п’ять плоских empty-станів (
#/reports,#/usage,#/portals,#/activity,#/dashboard) приєднуються до патерну v2empty з CAR-124 (пояснення + позначений aria-hidden приклад + наперед підключена дія; 15 нових ключівv2empty.*, чотири мертві однорядкові ключі вилучено) — усе ×19 словників, снапшот перегенеровано. - CAR-137 — HelpHint ×13 + набір для сторінок режимів (#550): контекстна підказка «?» тепер сидить на H1 кожної з решти сторінок (pipeline, reports, dashboard, cv, portals, usage, activity, assessments, map, health, docs-assistant, settings) і на 8 сторінках режимів через набір (кожен cfg MODES несе
hintKey, відрендерений черезHelpHint.title).
Додано
- Гернес Mobile-390 (#552): прогін Playwright по кожному маршруту SPA (31 канонічний + 8 сторінок режимів = 39 хешів) на 390×844 із сідованим корпусом — перевірка горизонтального переповнення на кожному маршруті (scrollWidth проти clientWidth, допуск 1px, називає найширшого порушника) плюс перевірка досяжності CTA (жодна sticky-панель/дровер/FAB не перекриває основну CTA); opt-in, коректно пропускається без бінарника браузера, працює без мережі.
- Свіжі v2-скріншоти ×48 (CAR-140 фаза A, #551): справжні скріншоти UI v2 із сідованої CI-ізольованої фікстури — dashboard ×19 + job-map ×19 (job-map-ta — перша поява, job-map-vi — новий) + scan/pipeline/tracker/reports × en/ru — живлять README, довідку та лендінг.
- Справжні скріншоти на cvstart.org (CAR-140 фаза B, #553): слоти-плейсхолдери CAR-140 зникли — лендінг показує фактичні v2-скріншоти (вітрина job-map + ряди скріншотів, ×19 локалей лендінгу).
- Підказки CAR-137 (#550): 21 новий перекладений ключ ×19 (
help.hint.*×20 +hintKeyсторінок режимів) — кожна сторінка тепер пояснює себе на місці.
Примітки
- Контракт Ctrl+K (CAR-122): комбінація відкриває палітру команд — вона НЕ фокусує поле пошуку напряму; пошук фокусується з палітри. Рядок чекліста QA v1.261.0 «Ctrl+K фокусит поиск» більше не чинний; оновлену перевірку несе QA-промпт v1.261.1.
- Справжні виправлення CodeQL (#547): 4 алерти закрито (ітерацію за literal-ключами в content.mjs зроблено структурною, lint-прохід) — приїхали пост-релізом на main v1.261.0 до цього патча; перелічені тут для повноти.
- Тести 6582 (+51); браузерний набір — 171–172 пройшли / 0 провалено / 1 опційний пропуск; гернес mobile-390 запускається самостійно (
node tests/playwright-mobile-390.mjs). Джерел сканера не торкнуто (реєстр 121 = 116 EN + 5 RU · 116 адаптерів).
[1.261.0] — 2026-10-28
Потяг повної ревізії 1.261.0 завершено (усі HIGH- і MEDIUM-знахідки приїхали), виправлення перемикача мови за повідомленням власника, виправлення якості оцінювання, розкладання довідки ×18, лендінг cvstart.org v2 — плюс 4 спеки SDD/OpenSpec, що документують потяг. Кожне виправлення приїхало через fix-forward red→green і рев’ю GLM-5.3-Flash зі свіжого контексту.
Безпека
- FULL-R1 — CSRF-гард acting-GET + заблоковані атомарні записи pipeline (#519): гард
Sec-Fetch-Site/Origin/Referer на/api/stream/*,/api/run/*,/api/geocode(експортованийACTING_GET, статичний гейт покриття); кожне читання-модифікація-запис уdata/pipeline.md/data/last-scan.json— підwithFileLock+ новимatomicWrite(temp у тому самому каталозі → rename → unlink при невдачі). - FULL-R2 — SSRF-кластер у джерелах (#514): rss.mjs — https + відмова приватних адрес; ~25 джерел із голим
fetchImpl→ захищені транспорти; закріплені дзеркала; валідація редіректів для habr/hh/geekjob/getmatch; спільнийdelay(ms, signal). - FULL-R4 — псування прози в stripDangerousMarkdown (#513/#518): видалення схем лише в контекстах посилань/атрибутів; сутності декодуються в окрему копію лише для виявлення («JavaScript: React, Node» і «R&D» виживають).
- FULL-M2 — зміцнення транспортів (#520):
safeGetвраховуєtimeoutMsразом із сигналом викликача (bridge controller);guardResolvedHostвідмовляє ЗАКРИТО при помилці DNS. #525 додає зістригання credential-заголовків при крос-доменних редіректах, відв’язування transport-лістенера на кожному шляху завершення та виправлення краху від заздалегідь перерваного сигналу. - FULL-M4 — логотипи/batch (#523): логотип перевіряє magic bytes на КОЖНОМУ шляху (брехлива
image/pngне може нести SVG), обрізає content-type доimage/*при збереженні, приватний кеш + nosniff; batch:--parallelобмежено 1–8,--min-scoreвалідовано, BATCH_BUSY single-flight,llmRateLimitна мутаціях config/batch.
Додано
- CAR-147 — ліміт оцінювання з урахуванням локалі (#524):
evalTimeoutMs(lang)— ta/hi отримують 480 с замість 300 с (індійські писемності токенізують кілька токенів на склад; стара стіна давала 502 + порожній звіт); клієнт remote-QA чекає 540 с. - CAR-133 — лендінг cvstart.org v2 (#532): паралакс-герой із гейтом reduced-motion, явно підписана СИМУЛЬОВАНА демонстрація потоку scan→evaluate→tracker, секції scroll-reveal із перевіреними наживо лічильниками, слоти-плейсхолдери скріншотів CAR-140.
- CAR-135 — довідку переписано під UI v2, розкладання ×18 завершене (#533, #540, #541, #542): усі 19 файлів довідки описують оболонку v2 (складана навігація, палітра Cmd/Ctrl+K, скелетони, empty states із прикладом і дією, chrome скану v2, pipeline з групуванням + undo, reports v2, канбан); підписи інтерфейсу цитують свої i18n-ключі; речення про 121 джерело живе з реєстру.
- Спеки (SDD/OpenSpec) (#535): бекфіл wave-two за 27 жовтня + три перспективні спеки, що документують потяг 1.261.0 (розкладання довідки, write hand-offs, інваріанти перемикання мови).
Виправлено
- Перемикання мови більше не знеплюює #/scan (#534) — за повідомленням власника: стан скану, локальний для в’ю (результати, чіпи фацетів), тепер живе на рівні модуля; поведінковий Playwright-гернес із відкладеним на 4 с повторним фетчем (червоний на старій формі).
- CAR-148/149 — ru-звіти «missing Block G» (#530): промпт навчав трьом суперечливим мапам A–G; скелет + список + приклад тепер узгоджені з oferta.md (G = Posting Legitimacy); поведінковий дріфт-гейт.
- CAR-150 — undo у pipeline зберігає метадані комп (#537): DELETE віддає
comp, POST приймає опційнийcompза гардом, в’ю несе{token, comp}крізь усі fallback-гілки. - FULL-M3 — мертві джерела звітують чесні борди (#522): getro/mycareersfuture/meituan/mokahr/glints кидають помилку на зламаній page-1 (мертвий API більше не читається як «healthy, 0 jobs»);
pinnedOverrideзакріплює overridescompany.apiдля 18 адаптерів. - FULL-M5 — один каскад провайдерів (#521): routes/llm.mjs через
runActiveProvider(+cachePrefix, сигнал); перехоплення сабпроцесу Gemini; контракти відповідей ідентичні байт-у-байт. - FULL-M1 — записи в спільні файли під замком (#526, #539):
runTrackedScript+ ростерTRACKER_MUTATING; спавни outcome/followup тримають замок; tracker.mjs пише черезatomicWrite; PUT зберігає locked+atomic. - FULL-M6 — консолідація сервер + клієнт (#531, #536, #525): dotenv з null-прототипом + відмова
__proto__; isPrivateOrLoopback покриває NAT64/6to4/benchmark/reserved; mtime-кеш env-config; справжній slot GC у file-lock; safeGet timeout+signal; DNS-гард fail-closed; SSE ×4 →API.postStream; бібліотеки фокусу/підписів/статусів; clipboard з await; тінт Hired на дашборді. - R3 — дріфт CSS-токенів (#527):
--accentрозв’язано (фокус-кільця мапи мовчки не працювали); 11 позатокенових тривалостей згорнуто; дріфт-гейт. - R4 — відсталі завантаження (#538): assessments/mode-page/прев’ю pipeline рендерять скелетони CAR-112.
- Пакет CodeQL + CI (#517, фікс #539): невикористані імпорти, гард ACTING_GET імпортує справжній regex, lifecycle-слайс переякірено.
Примітки
- Навмисно не зроблено: DNS-пінінг на конекті (потребує undici — поза залежностями, задокументований залишок); скріншоти CAR-140 (потребують розгорнутого UI v2); CAR-137/138/141 (наступний прохід по доках); клас CodeQL
js/missing-rate-limiting— задокументоване хибне спрацювання (кастомний лімітер не зараховано). - Розкладання довідки ×18: es/de у wave-1 пропустили нотатку вступу та дельту картки Active Companies (de доїхав із власним вступом; es отримає однорядковий follow-up).
career-ops-ui v1.267.1 · page 1/12 · mirror of the project changelog for cvstart.org