github Fighter90/career-ops-ui v1.261.2

3 hours ago

[1.261.2] — 2026-10-28

The stability + polish patch: the geocode cache survives a mid-write crash (CAR-173), UI.md renders bullet continuations and nested lists, the help bundles' rendered-text key leaks are closed ×17, evaluate gets a per-locale token ceiling for ta (CAR-174), the RTL chrome mirrors via logical properties (85 findings), the map captures are re-framed ×19 and the map clusters pass contrast in dark — plus the first phase of the Lovable design integration (passive tokens-v3.css sheet), docs backfill (release review + specs), and CI-node coverage exemptions.

Fixed

  • Geocode cache torn-write loss (CAR-173, #562): both flush paths (debounced insert-flush, exit-flush) wrote .cache/geocode.json with a bare writeFileSync — truncate-then-write — so a crash, OOM or SIGKILL mid-write left a torn file and the next boot's JSON.parse threw, silently discarding the whole persistent cache (weeks of Nominatim budget), not just the day's. Both write sites now go through file-lock.mjs::atomicWrite (same-directory temp → rename → unlink-on-failure) — the same contract FULL-R1/FULL-M1 applied to pipeline.md, cv.md, profile.yml, career-plan, batch input and memory. The CAR-142 flush machinery itself (debounced insert-flush, exit-flush, bounded shutdown) was verified intact red-first: tests (a)/(b) lock that inserts reach the file within the GEOCODE_SAVE_MS window and it stays valid JSON; (c) is a mechanism gate plus behavioral runs of both flush paths asserting no temp litter and a parseable file.
  • UI.md list continuation (#563): the list handler only consumed consecutive - /* lines, so an indented continuation line after a bullet fell out of the emitted <ul> and the paragraph pass skipped the chunk — the continuation leaked as raw prose with literal **/backtick markers (~95 dangling runs per locale across the help bundles; en.md 95, ru.md 104). A continuation line — leading whitespace, not a new block (heading, bullet, NUL-wrapped fence placeholder, blank) — now joins the previous <li> as <br/>-joined text, with inline() run per line so emphasis markers never pair across the join; the fence-placeholder guard is NUL-first-char, not a bare token match. Nested - bullets, numbered lists, fences inside lists and headings inside lists remain a documented separate gap (next docs wave).
  • Help rendered-text key leaks ×17 (#561): remote-QA found 85 raw i18n keys visible in the rendered help (root cause repro-proven: the same UI.md single-line-bullet consumption — a wrapped continuation emitted as raw text kept its backticks literal; the v1.260/261 help sections wrote multi-line bullets). 163 bullets unfolded to one line across 17 locales — whitespace-only, not a single translated word touched (old-vs-new verified). New gate tests/help-rendered-keys.test.mjs renders every bundle through the real chain (stripGithubOnlyBlocks + vm-loaded UI.md) and fails on any of 1,711 keys appearing in the text; red-first 17 locales → green. Repro tooling ships in-repo: scripts/remote-qa/help-render-scan.mjs (vm) + help-render-crosscheck.mjs (real DOM).
  • Remote-QA Ctrl+K asserts the CAR-122 palette contract (#561): prod-regression.mjs now expects the command palette after Ctrl+K (.nav-v2-palette[role=dialog] + focus in nav-v2-input) OR the legacy search-focus as fallback, instead of silently accepting either; unit tests/remote-qa-ctrl-k.test.mjs locks 6 scenarios on a mock DOM, red-first 0/6 → 6/6.
  • Map capture framing ×19 (#560): owner report on images/job-map-en.png — half the topbar search + a cut h1 at the top, a grey sliver of the side list at the right edge, a marker cluster cut by the frame near London, and a white horizontal line across the map at ~2/3 height. capture-map-screenshots.mjs now waits on the geocode queue drain (located + noLoc === total, not the N/N counter — Remote rows are structurally without a location, so the old wait could never complete), hides header.topbar and #notif-drawer for the shot, clips the exact .job-map-page box (no panel cut by the frame edge), zooms out two steps so no cluster sits half-cut, pins the map height and takes a plain viewport screenshot (captureBeyondViewport resamples the composited tile layer and paints white strips at tile-row boundaries), and integer-aligns the map container. All 19 job-map captures re-shot.
  • Map cluster bubbles — dark-theme contrast + label fit (#572): owner-sweep P2/P3 — the dark-theme cluster rings sat on a fixed white fill (1.83:1); rings now take var(--paper) with score-palette digits (measured 4.98:1+, filled 9.53:1; the lowest band is mathematically un-colorable on dark and keeps 6.1:1 via an honest data-dim path), and the related label fits its bubble (rAF font-fit, numbers never shrink).
  • #/scan language switch — skeleton re-arm (#534 follow-up): follow-up on the v1.261.0 owner fix (#534) — the re-armed skeletons on a language-switch repaint no longer survive a completed refetch.
  • Docs drift (CAR-139, agent in flight, lands before tag): README ×19 counters, HANDOFF ×19, CONVENTIONS facts resynced with the live registry/test state.

Added

  • Release review + spec backfill (docs, #559/#558): docs/reviews/REVIEW-2026-10-28-v1.261.1.md (the v1.261.0/v1.261.1 wave reviewed against its specs) and the SDD spec backfill for the same wave — the BACKLOG tail now names the remaining gap (dedicated tests for sources/habr.mjs).
  • RTL deep sweep (#571): AR harness walks 39 routes × LTR-vs-RTL (X-mirror, overflow, clipping, text-align signatures) — 85 findings fixed: fixed chrome (toast, sidebar, drawer + its dismiss ×, docs-FAB, usage-HUD, skip-link) moved to inset-inline-*/border-inline-*, tables to text-align: start/end across 17 view files + 2 libs, the blanket [dir=rtl] text-align:right removed (it fought the scan star-column centering), the CAR-58 score-column isolation moved onto an ltrNum span.
  • Keyboard a11y harness (#570): tests/playwright-a11y-keyboard.mjs walks 11 routes (Tab order/traps, focus indicators, Escape overlays, Enter/Space) — 0 production defects; regression coverage is new.
  • i18n untranslated sweep (#565): 36 en-identical values translated ×9 locales (*.rolePh, config keys, ta manifesto) + a closed-allowlist sweep guard; site (cvstart.org) i18n audit (#568): 20 keys × 18 locales translated (the whole demo.*/stats.t.*/showcase.tab* block was English everywhere), 1 dead key removed ×19.
  • Remote-QA observability (CAR-175, #567): the summary names failing scan sources (stderr samples, redact-before-truncate) and prints tok ~N from provider usage; the logic lives in a pure summary-parts.mjs with a drift gate.
  • UI.md nested marker lists (one level, #564): - bullets render as a nested <ul> (fence-placeholder-safe) — the last 2 dangling prose runs per locale are gone.
  • Lovable design integration — phases 1+2 (#566 + feat/design-v3-active): public/css/tokens-v3.css ports the Lovable concept verbatim (oklch surfaces 0–3, AA-annotated ink pairs, accent family, ok/warn/danger/info/neutral soft+ink, score bands A–G ×2 themes, density + focus + shadows); phase 2 activates it on the existing token names (accent split blue/danger, score bands + callouts on cv3 tokens) with the contrast/role/dark gates updated.
  • Evaluate per-locale token ceiling (CAR-174, #569): evalMaxTokens(lang) — ta/hi ask 32768 (the three SDK clients re-clamped at 16384; both layers fixed), plus a Tamil volume budget in the prompt (≤5 sentences per block, ≤900 words) — the v1.261.1 remote-QA ta report was cut off at the output-token limit (missing Block B–G + SCORE_SUMMARY).
  • CodeQL real-fix follow-through: the new QA render tooling replaced hand-rolled entity handling with structural split passes + htmlToText({cap: Infinity}); redact assertions pin exact masked output; RTL harness unused constants dropped.
  • Coverage exempt-политика for CI-node variance (habr #558, states #561): sources/habr.mjs (CI node-matrix branch 87.23 vs local ≥89) and states.mjs (CI 86.49 vs local ≥89) join the exemptions in scripts/coverage-baseline.json — files whose CI measured branch coverage is structurally below the local floor due to node-version variance are exempted instead of ratcheting the whole gate down.

Notes

  • Ctrl+K contract (CAR-122): the chord opens the command palette — it does NOT focus the search box directly; search is focused from the palette. The remote-QA prod regression now asserts exactly this (#561).
  • CodeQL FP-dismiss ×5: the js/missing-rate-limiting alerts on main await the security_events token — operator action, not a code change.
  • Landed after branch cut: #561/#562/#563/#564/#565/#566/#567/#569/#570/#571/#572 are all merged to main; the #534 follow-up landed (skeleton re-arm). Nested marker lists beyond one level and inline-heavy view geometry remain documented gaps.
  • Tests 6655 on the release branch (+46 since v1.261.1); full suite 6649 pass / 0 fail / 6 env skips; no scanner sources touched (registry 121 = 116 EN + 5 RU · 116 adapters).

Don't miss a new career-ops-ui release

NewReleases is sending notifications on new releases.