From c71b7f12773661e125ef8f6aab067551c99d281b Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Jul 2026 15:52:33 +0000 Subject: [PATCH 1/6] issues: capture the production live-region defect; record the landed #1457 review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds #147 — `search-results-header-band.tsx` sets aria-live="off" when faulted, so a clinical search that fails while focus is elsewhere is announced to nobody and the user is never told Retry appeared. Affects all twelve call sites that render the band. Raised by Codex against the copied line on PR #1457, fixed there, and confirmed to exist unchanged in production. The merged mockup carries the proven pattern and a non-vacuous assertion to port. Appends two further data points to #146 from PR #1457: the Services viewport anchor failed once more on head c739340 (expected -138, received -7) and then passed on two later heads with the diff byte-identical — six data points, two failures, shard 1 only. Both failures landed on a PR touching only mockups, which strengthens the unchanged-code reading. Notes that the 131px delta is roughly 2x the 64px shrink rather than sub-pixel drift. Records the prlanded verification for PR #1457 in the branch review ledger: squash e79e499, content diff against the branch tip empty, and the late aria-live/role="alert" commit confirmed present on main rather than orphaned by the squash. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01JdPa3mHCX5ZQZZvU5GHU3r --- docs/branch-review-ledger.md | 1 + docs/outstanding-issues.md | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 2564a29b51..440bd57e88 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -188,3 +188,4 @@ Records before 2026-07-28 were written by hand and had drifted: 146 lines carrie | 2026-07-30 | claude/x3-rag-coverage-gate-qx9j7d (PR #1454, squashed as 102bb1f) | 102bb1f5edf09e666d1be5934ff5dfb2aa5abcf0 | X3/#086 evidence coverage gate extraction from rag.ts into rag-coverage-gate.ts | clean and landed — byte-identical move verified against pre-merge main, rag.ts 5030->4780, budget ratcheted to 4780, no back-edge, public re-export preserved; squash captured 100% of branch content, nothing orphaned | workflow:rag-lab, focused vitest 81/81, check:maintainability-budgets 4780/4780, check:rag:fixtures 36 golden, eval:rag:offline 567/567, typecheck, lint, check:knip, format:check, verify:cheap 4569 passed, npm test 4569 passed, verify:pr-local build+bundle-scan, post-merge npm test on main 4574 passed | | 2026-07-30 | PR #1432 | 74adc5aa3f8a4dad659c7a40490288ef8efcb82e | Playwright browser preflight and phone-sheet focus repair | APPROVE after current-main sync: browser-project resolution fails closed, phone-sheet focus is stable, and no stale issue-ledger state remains. | 3 focused files 45 passed; phone-chrome dry-run; installed-lock parity; docs and ledger guards; formatting | | 2026-07-30 | PR-1470 | 1932e81ece9361c08607d2ef01ad653a7df0ac8d | PR #1470 full diff vs origin/main | PASS after repair: #013 remains open and measurement-gated | check:outstanding-issues passed; docs:check-links 1408 passed; Prettier passed; git diff --check | +| 2026-07-30 | claude/global-search-mockups-mrgmzl | e79e499839e56bff66aecc37d1c915ac3127e995 | prlanded | merged and verified by content | verify:cheap exit 0 (437 files, 4576 passed); verify:pr-local exit 0; CI pr-required green on a6f2281; squash e79e499 content diff vs branch tip empty; late aria-live/role=alert commit confirmed present on main | diff --git a/docs/outstanding-issues.md b/docs/outstanding-issues.md index 48b9584716..68739e81cf 100644 --- a/docs/outstanding-issues.md +++ b/docs/outstanding-issues.md @@ -82,7 +82,7 @@ removed after current-main verification; it is not missing recommended work. | 34 | `#105` | Optional | High — browser/UI verification | When the heavy-run lock is free | 20–40 minutes | Run `verify:ui` over the ten `LoadingPanel` fallbacks and confirm the Supabase `preconnect` reaches `` on a live page. Implementation already shipped; this row is the outstanding verification only. | | 35 | `#126` | Optional | Standard — repository hygiene | Once per UTC calendar quarter, or when the live ledger grows large | 5–15 minutes | Run `npm run ledger:rotate -- --dry-run`, then `npm run ledger:rotate` if the preview looks right; commit the live+archive diff. Stop if dry-run shows unexpected mass moves or archive path collisions. | - + ## Open items @@ -167,7 +167,8 @@ removed after current-main verification; it is not missing recommended work. | #139 | P3 | rec | `changes` job computes outputs nothing consumes, and over-triggers coverage | **Outcome:** the change-scope contract says what it means. **Detail:** `changes` exports `source_changed`, `workflow_changed`, `changed_files` and `rag_eval_changed` and **no job reads any of them** — only `rag_eval_changed` is documented as intentionally advisory (`ci-change-scope.mjs`); the other three read as live wiring. Separately `coverage_changed` is derived as _any non-doc file_, so a workflow-only edit runs the ~4 min coverage job. **Next:** delete or document the dead outputs; decide whether coverage should narrow. Low value alone — bundle with the next `ci.yml` change rather than minting a PR. **DONE 2026-07-30:** `source_changed`, `workflow_changed` and `changed_files` are no longer job outputs. The script still computes all three — `docs_only` derives from the first two — they were simply exported and read by nothing. The coverage-breadth half is untouched and stays open. | `.github/workflows/ci.yml`; `scripts/ci-change-scope.mjs`; session 2026-07-30 | 2026-07-30 | | #141 | P2 | issue | Production `--clinical-accent-strong` and mockup hover tokens are undefined | **Outcome:** every static `var(--token)` a component reads either resolves from `@theme`/globals or is documented as runtime-set. **Detail:** the 2026-07-30 `/design-sync` re-sync surfaced `[TOKENS_MISSING]` from `package-validate.mjs`. Triage: `--med-accent` / `--med-accent-border` are **not** defects — `medicationAccentStyle()` in `src/components/clinical-dashboard/medication-record-page.tsx:88-94` sets both on the ancestor at `:393`, so every referenced class resolves at runtime. The genuine production gap is `--clinical-accent-strong` at `src/components/clinical-dashboard/answer-status.tsx:252` (no stylesheet or React `style` definition, no fallback — declaration dropped at parse time). Two more sit in gate-exempt scratch (`--primary-hover`, `--success-hover` in `src/components/favourites-page-mockups/favourites-library-redesign-page.tsx`). Same defect family as the dead `--text-4xs` classes repaired in `39f2bcea7`; invisible to every static gate that only inspects stylesheets (`#094`). **Next:** map `--clinical-accent-strong` onto an existing token (likely `--clinical-accent` / a stronger mix; confirm against `docs/redesign/permanent-colour-direction.md`) or add it to `@theme` — never a hex literal (`eslint-rules/no-hardcoded-hex.mjs`). Optionally define or drop the mockup hover tokens. **Verify:** `npm run verify:cheap`, then `npm run ensure` + `npm run verify:ui`, and confirm answer-status accent text renders coloured. **Stop:** do not "fix" `--med-accent` / `--med-accent-border` (runtime-set), `--mobile-composer-reserve` (runtime-set via `var(…, 0rem)`), or `--x` (Tailwind scan of prose in `docs/redesign/03-decision-log.md`); all are expected in `.design-sync/NOTES.md`. | session 2026-07-30 design-sync re-sync (PR #1443); `package-validate.mjs` `[TOKENS_MISSING]` | 2026-07-30 | | #145 | P3 | issue | A branch sync during in-flight CI cancels a near-complete run, and nothing warns | **Outcome:** a behind-but-passing PR is not re-synced into a CI restart loop. **Observed 2026-07-30 on PR #1454:** three `Update-branch` merges in ~20 minutes (`d49ac0f`, `6de2bf5`, `d9598b7`) each landed while that head's CI was mid-flight, so `cancel-in-progress` cancelled `Build` + `Unit coverage` three times and restarted a full cycle each time; no run finished, so armed auto-merge never fired until the queue quietened. Every check that got time to complete passed, twice over — the PR was never broken. **Gap:** AGENTS.md's anti-churn rule ("assemble every commit before the first push, or wait for the current run to settle") addresses _your own_ pushes only. It says nothing about an operator/`update-branch` sync landing on a head whose CI is already running, which costs the same cancelled cycle and is the case that actually recurs — `#1406`'s ~40%-cancellation measurement is the same phenomenon from the push side. Distinct from `#095` (which is the _reporting_ of cancelled jobs, and worked correctly here: `PR required` said "CANCELLED with no failing job … look for a newer run on the PR's current head") and from `#129`/`#116` (genuine-vs-apparent conflict). **Next:** extend the anti-churn guidance to cover syncs — before an `update-branch`/`git merge origin/main` on an open PR, check whether that head has a run in flight and let it settle first unless the branch is genuinely blocking-behind; prefer syncing once, late, over repeatedly. **Stop:** do not "fix" this by disabling `cancel-in-progress` — superseded runs should still be cancelled; the cost is in _when_ the sync lands, not in the cancellation policy. | session 2026-07-30; PR #1454 runs `30546286820`, `30546667904`, `30547100273`; AGENTS.md § Anti-conflict and CI-speed operating procedure | 2026-07-30 | -| #146 | P2 | issue | `ui-phone-scroll` Services result anchor jumps on viewport shrink under CI load | **Outcome:** the required `Production UI` job stops failing on a test the PR did not touch. **Evidence 2026-07-30 (PR #1427 run `30534158395`, job `90844138237`, shard 1 of 3):** `tests/ui-phone-scroll.spec.ts:973` "Services results keep a continuous browser viewport after shared chrome releases" failed at `:1133` — `afterViewportResize.anchorTop` did not stay close to `hidden.anchorTop` across the viewport shrink. `120 passed (6.5m)`, classified "needs investigation". **Intermittent on unchanged code:** the spec is byte-identical across heads `79dd28f`, `76f80f3` and `17c8cb9`; shard 1 passed, passed, failed, then passed again on re-run — four data points, one failure. **Not #127 and not fixed by #1427:** this head already carried #1427's runway poll and travel assertion, and the failing assertion is result-content anchor stability across a resize, not the chrome hide. #127 is the reduced-motion hide on the document route; this is the Services result canvas. **A narrowing claimed here was WITHDRAWN 2026-07-30 (Codex, PR #1446):** an earlier version of this row argued that the sibling `documentScrollTop` assertion did not fail, so the scroll position must have held. That inference is invalid — Playwright aborts a test at the first failing `expect`, so once the `anchorTop` assertion threw, the `documentScrollTop` assertion never executed. Its absence from the output shows nothing, and scroll-restoration causes are NOT ruled out. **Next:** capture `anchorTop` and `documentScrollTop` either side of the resize from a CI-load run, and check whether the chrome release and the viewport change can interleave. **Stop:** do not add to `tests/flake-ledger.json` — that ledger takes reproduced `@quarantine` entries only, and this reproduces once in four. Do not loosen the `toBeCloseTo` tolerance; the anchor stability is the assertion's whole point. | PR #1427 run `30534158395`; session 2026-07-30 | 2026-07-30 | +| #146 | P2 | issue | `ui-phone-scroll` Services result anchor jumps on viewport shrink under CI load | **Outcome:** the required `Production UI` job stops failing on a test the PR did not touch. **Evidence 2026-07-30 (PR #1427 run `30534158395`, job `90844138237`, shard 1 of 3):** `tests/ui-phone-scroll.spec.ts:973` "Services results keep a continuous browser viewport after shared chrome releases" failed at `:1133` — `afterViewportResize.anchorTop` did not stay close to `hidden.anchorTop` across the viewport shrink. `120 passed (6.5m)`, classified "needs investigation". **Intermittent on unchanged code:** the spec is byte-identical across heads `79dd28f`, `76f80f3` and `17c8cb9`; shard 1 passed, passed, failed, then passed again on re-run — four data points, one failure. **Not #127 and not fixed by #1427:** this head already carried #1427's runway poll and travel assertion, and the failing assertion is result-content anchor stability across a resize, not the chrome hide. #127 is the reduced-motion hide on the document route; this is the Services result canvas. **A narrowing claimed here was WITHDRAWN 2026-07-30 (Codex, PR #1446):** an earlier version of this row argued that the sibling `documentScrollTop` assertion did not fail, so the scroll position must have held. That inference is invalid — Playwright aborts a test at the first failing `expect`, so once the `anchorTop` assertion threw, the `documentScrollTop` assertion never executed. Its absence from the output shows nothing, and scroll-restoration causes are NOT ruled out. **Next:** capture `anchorTop` and `documentScrollTop` either side of the resize from a CI-load run, and check whether the chrome release and the viewport change can interleave. **Stop:** do not add to `tests/flake-ledger.json` — that ledger takes reproduced `@quarantine` entries only, and this reproduces once in four. Do not loosen the `toBeCloseTo` tolerance; the anchor stability is the assertion's whole point. **Two further data points 2026-07-30 (PR #1457):** failed once more on head `c739340` (run `30551018893`, shard 1) with `anchorTop` expected `-138`, received `-7`, difference `131`, `120 passed`; then passed on heads `9da02d9` and `a6f2281` across all three shards with the diff byte-identical. That is six data points, two failures, still shard 1 only — and both failures landed on a PR touching nothing but `src/app/mockups/**` plus one mockup test, which strengthens the unchanged-code reading. The magnitude is worth noting for the capture suggested above: `131` is roughly 2x the 64px viewport shrink, not a sub-pixel drift. | PR #1427 run `30534158395`; session 2026-07-30 | 2026-07-30 | +| #147 | P2 | issue | Failed clinical searches are announced to nobody — the results band mutes its live region | **Outcome:** a search that fails while focus is elsewhere still reaches a screen-reader user. **Defect:** `src/components/clinical-dashboard/search-results-header-band.tsx` sets `aria-live={faulted ? "off" : "polite"}` on the count/status span, so the `searching` to `error` transition switches the live region OFF. A user whose focus is not in the band is never told the search failed, never told Retry appeared, and is left waiting on a result that is not coming. This affects all twelve call sites that render the band (documents, services, forms, DSM, formulation, prescribing, specifiers, factsheets, differentials, favourites, therapy-compass, applications launcher). **Provenance:** raised by Codex as a P2 on the mockup PR #1457 against the copied line, fixed there, and confirmed on 2026-07-30 to exist unchanged in production. **Proven pattern to copy:** the merged mockup `src/components/search-refine-adaptive-mockups.tsx` escalates instead of muting — `role="alert"` plus `aria-live="assertive"` on failure, `role="status"` plus `aria-live="polite"` otherwise — with a non-vacuous DOM assertion in `tests/search-refine-adaptive-mockups.dom.test.tsx`; restoring `aria-live="off"` fails it with "Unable to find an accessible element with the role alert". **Next:** apply the same escalation to the production band and port that assertion against the real component. Small and well understood; the fix is two attributes and one test. **Stop:** do not widen the live region back over the whole status span — wrapping the controls too lets a refetch re-announce chrome, which is why the region is scoped to the count alone. | Codex review on PR #1457 (`#discussion_r3683379789`); merged squash `e79e499`; session 2026-07-30 | 2026-07-30 | ## Resolved / archive From bef8c489deaa9e96a0f1917a514fe90267729773 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Jul 2026 17:29:43 +0000 Subject: [PATCH 2/6] =?UTF-8?q?issues:=20withdraw=20#149=20=E2=80=94=20the?= =?UTF-8?q?=20live-region=20defect=20was=20false,=20and=20the=20fix=20was?= =?UTF-8?q?=20worse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex caught this on PR #1481 and it is correct. I filed #149 claiming a failed clinical search is announced to nobody because the count span sets aria-live="off" when faulted. I never checked whether another node makes the announcement. It does. search-results-header-band.tsx mounts a fault panel with role="alert" carrying the failure title, body and Retry (lines 407-414), and the mute is deliberate, documented in place: "While faulted the live region is silenced (aria-live='off') and the freshly-mounted fault role='alert' below makes the single announcement, rather than both speaking." tests/search-results-header-band.dom.test.tsx already pins exactly that with singular role queries that throw on duplicates. Escalating the count span in production, as #149 recommended, would have added a second alert beside the fault panel — a duplicate announcement and a broken test. The row is withdrawn to the archive rather than deleted, with the reasoning recorded so nobody re-files it. The mockup is unaffected: search-refine-adaptive-mockups.tsx has no fault panel, so there the count span is the only announcement channel and its escalation is correct. It simply does not port to production. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01JdPa3mHCX5ZQZZvU5GHU3r --- docs/outstanding-issues.md | 162 ++++++++++++++++++------------------- 1 file changed, 81 insertions(+), 81 deletions(-) diff --git a/docs/outstanding-issues.md b/docs/outstanding-issues.md index 7c2ad4ccc7..63629efc3e 100644 --- a/docs/outstanding-issues.md +++ b/docs/outstanding-issues.md @@ -167,89 +167,89 @@ removed after current-main verification; it is not missing recommended work. | #146 | P2 | issue | `ui-phone-scroll` Services result anchor jumps on viewport shrink under CI load | **Outcome:** the required `Production UI` job stops failing on a test the PR did not touch. **Evidence 2026-07-30 (PR #1427 run `30534158395`, job `90844138237`, shard 1 of 3):** `tests/ui-phone-scroll.spec.ts:973` "Services results keep a continuous browser viewport after shared chrome releases" failed at `:1133` — `afterViewportResize.anchorTop` did not stay close to `hidden.anchorTop` across the viewport shrink. `120 passed (6.5m)`, classified "needs investigation". **Intermittent on unchanged code:** the spec is byte-identical across heads `79dd28f`, `76f80f3` and `17c8cb9`; shard 1 passed, passed, failed, then passed again on re-run — four data points, one failure. **Not #127 and not fixed by #1427:** this head already carried #1427's runway poll and travel assertion, and the failing assertion is result-content anchor stability across a resize, not the chrome hide. #127 is the reduced-motion hide on the document route; this is the Services result canvas. **A narrowing claimed here was WITHDRAWN 2026-07-30 (Codex, PR #1446):** an earlier version of this row argued that the sibling `documentScrollTop` assertion did not fail, so the scroll position must have held. That inference is invalid — Playwright aborts a test at the first failing `expect`, so once the `anchorTop` assertion threw, the `documentScrollTop` assertion never executed. Its absence from the output shows nothing, and scroll-restoration causes are NOT ruled out. **Next:** capture `anchorTop` and `documentScrollTop` either side of the resize from a CI-load run, and check whether the chrome release and the viewport change can interleave. **Stop:** do not add to `tests/flake-ledger.json` — that ledger takes reproduced `@quarantine` entries only, and this reproduces once in four. Do not loosen the `toBeCloseTo` tolerance; the anchor stability is the assertion's whole point. **Two further data points 2026-07-30 (PR #1457):** failed once more on head `c739340` (run `30551018893`, shard 1) with `anchorTop` expected `-138`, received `-7`, difference `131`, `120 passed`; then passed on heads `9da02d9` and `a6f2281` across all three shards with the diff byte-identical. That is six data points, two failures, still shard 1 only — and both failures landed on a PR touching nothing but `src/app/mockups/**` plus one mockup test, which strengthens the unchanged-code reading. The magnitude is worth noting for the capture suggested above: `131` is roughly 2x the 64px viewport shrink, not a sub-pixel drift. | PR #1427 run `30534158395`; session 2026-07-30 | 2026-07-30 | | #147 | P2 | rec | Mobile CLS breaches four routes, and it reproduces exactly offline | **Outcome:** mobile CLS is under 0.1 on every measured route, or each remaining breach has a recorded reason. **Measured 2026-07-30 with the LOCAL OFFLINE production harness** (`CHROME_PATH=/opt/pw-browsers/chromium npm run verify:lighthouse -- --keep`; Lighthouse 12.8.2, Chromium 141.0.7390.37, demo corpus, inert loopback Supabase). Mobile CLS: `/dsm` 0.363, `/documents/search` 0.220, `/therapy-compass` 0.142, `/` 0.023 — `/forms` did not measure locally (Lighthouse `NO_NAVSTART`, its own "run again" transient), but the live dispatch put it at 0.212. Desktop passes everywhere: 0.016–0.097. **The finding that matters is that these reproduce the live production dispatch EXACTLY.** Against run `30548662649` on `psychiatry.tools`, all four locally measurable mobile routes match to three decimals — 0.023 / 0.142 / 0.220 / 0.363 on both — and the desktop range matches at both endpoints, 0.016–0.097. So CLS on these routes is deterministic layout structure, not network, latency, or production data. Three consequences: (a) the CLS half of `#017` is debuggable and gate-able locally at zero provider cost; (b) `#017`'s recorded blocker "prod server hard-requires Supabase secrets" is stale — `scripts/run-lighthouse-budget.mjs` builds and serves an offline production app precisely to sidestep that, and its earlier note that dev-mode CLS looked "excellent at 0.00–0.04" was measuring DEV, which does not reproduce production; (c) re-dispatching the live workflow to re-measure CLS buys nothing. **LCP does NOT reproduce and must not be read from local runs:** local mobile LCP is 2014–2051 ms against 3639–4716 ms live, because the loopback server has no network latency. The LCP _ranking_ does survive — `/therapy-compass` is the outlier both locally (4978 ms, ~2.4x every other route) and live — which corroborates `#117` rather than replacing it. Mobile TBT, local: `/documents/search` 581 ms, `/dsm` 448, `/therapy-compass` 379, `/` 338. **Ranking by measured contribution, which is what `#017` asked for:** 1. `/dsm` CLS 0.363 (3.6x the threshold, the worst single number anywhere); 2. `/documents/search` 0.220; 3. `/forms` 0.212 (live only); 4. `/therapy-compass` 0.142, and separately the sole LCP outlier via `#117`; 5. `/` 0.023, which passes and needs nothing. **Next:** attribute the shifts to elements. Lighthouse's `layout-shift-elements` audit returned **zero items on every route** — the `cumulative-layout-shift` audit carries only `debugdata` — so attribution needs a driven Chromium session with a `PerformanceObserver` on `layout-shift`, reading `entry.sources[].node` and `entry.value`, against the same offline production build. Start with `/dsm`. **Gate — `#118` owns it, and this row supplies a constraint it was missing.** `#118` already tracks flipping `lighthouse-budget.json` to `enforce: true` and says not to do it "before `#117` or the baseline pins a known-slow route". These numbers make that concrete: pinning a baseline today would bake CLS 0.363 on `/dsm` in as the accepted value, so the CLS fixes must land first or the gate ratifies the breach. Separately, no baseline could have been committed from this run anyway — the grader correctly refused it as incomplete evidence because `/forms` produced no report, and that refusal was not overridden. **Reproduction trap:** without `CHROME_PATH`, `chrome-launcher` cannot find a browser in this container and every route fails; the harness reads `CHROME_PATH`/`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH` (`run-lighthouse-budget.mjs:291`) but nothing sets either, even though `PLAYWRIGHT_BROWSERS_PATH` is set. Related to `#121`. Also seen: a truncated `progress` package in the npx cache broke Lighthouse startup entirely; clearing the npx cache entry fixed it. **Stop:** do not re-dispatch the live Web-Vitals workflow to re-measure CLS — it is reproducible offline for free. Do not commit a budget baseline from a run with a failed route. Do not treat local LCP numbers as production LCP. | `#017` live run `30548662649`; local `verify:lighthouse` 2026-07-30; `lighthouse-budget.json` | 2026-07-30 | | #148 | P2 | issue | Rows can be deleted from the issues ledger and no gate notices | **Outcome:** a row cannot leave `docs/outstanding-issues.md` without being archived, and a gate catches it if one does. **Observed 2026-07-30 on `main`:** commit `d8b034c8` (PR #1441, "guard upload limits in production builds") deleted **three** rows from the Open items table and added **zero** rows to the archive — `#085` (genuinely resolved by that same PR, which added `scripts/check-upload-limit-parity.mjs`), `#119` (a duplicate of `#122`), and `#105` (**open and unresolved** — its `LoadingPanel` half was and is unverified). Two of the three were dispositionable; none was dispositioned, and the third should not have been touched at all. The file's own Conventions section says of the archive: _"Move resolved rows here with the resolution date and a one-line outcome. Keep them — do not delete."_ There is precedent for doing it properly even for duplicates — `#140` was archived as "CLOSED as a duplicate, superseded by `#133`" rather than removed. **Why nothing caught it:** `npm run check:outstanding-issues` validates duplicate ids, an id present in both tables, a marker at or below the highest id, malformed rows, and missing headings — every shape the 2026-07-29 triple collision took (`#112`). It does **not** compare against the previous revision, so a row that simply disappears is invisible to it, in `verify:cheap` and in `static-pr` alike. **Likely mechanism, and why this file is the one it happens to:** `#133` deliberately left this file without a `merge=union` driver so overlapping edits conflict loudly instead of being silently concatenated. That is the right design, but it means every sync is a hand-resolution, and taking one side wholesale silently drops the other side's rows — exactly what AGENTS.md forbids for this file. This branch hit five consecutive genuine conflicts on it, and `#105` only survived because each resolution was done row by row and diffed with column padding normalised away afterwards. **Next:** add a deletion check — compare the id set against the merge base (or `origin/main`) and fail when an id present there is absent from both tables, unless the same commit archives it. That is a few lines in `scripts/check-outstanding-issues.mjs` and closes the one shape `#112`'s guard left open. **Stop:** do not fix this by adding a union merge driver — `#133` removed that deliberately and union merge would reintroduce the duplicate-row damage it was removed to stop. The fix is detection, not merge policy. | `main` commit `d8b034c8` (PR #1441); `scripts/check-outstanding-issues.mjs`; `#112`, `#133` | 2026-07-30 | -| #149 | P2 | issue | Failed clinical searches are announced to nobody — the results band mutes its live region | **Outcome:** a search that fails while focus is elsewhere still reaches a screen-reader user. **Defect:** `src/components/clinical-dashboard/search-results-header-band.tsx` sets `aria-live={faulted ? "off" : "polite"}` on the count/status span, so the `searching` to `error` transition switches the live region OFF. A user whose focus is not in the band is never told the search failed, never told Retry appeared, and is left waiting on a result that is not coming. This affects all twelve call sites that render the band (documents, services, forms, DSM, formulation, prescribing, specifiers, factsheets, differentials, favourites, therapy-compass, applications launcher). **Provenance:** raised by Codex as a P2 on the mockup PR #1457 against the copied line, fixed there, and confirmed on 2026-07-30 to exist unchanged in production. **Proven pattern to copy:** the merged mockup `src/components/search-refine-adaptive-mockups.tsx` escalates instead of muting — `role="alert"` plus `aria-live="assertive"` on failure, `role="status"` plus `aria-live="polite"` otherwise — with a non-vacuous DOM assertion in `tests/search-refine-adaptive-mockups.dom.test.tsx`; restoring `aria-live="off"` fails it with "Unable to find an accessible element with the role alert". **Next:** apply the same escalation to the production band and port that assertion against the real component. Small and well understood; the fix is two attributes and one test. **Stop:** do not widen the live region back over the whole status span — wrapping the controls too lets a refetch re-announce chrome, which is why the region is scoped to the count alone. | Codex review on PR #1457 (`#discussion_r3683379789`); merged squash `e79e499`; session 2026-07-30 | 2026-07-30 | ## Resolved / archive Move resolved rows here with the resolution date and a one-line outcome. Keep them — do not delete. -| ID | Type | Summary | Outcome | Resolved | -| ---- | ----- | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | -| #136 | rec | Reuse Next build cache across isolated Playwright production builds | CLOSED 2026-07-30 after an end-to-end implementation benchmark rejected the proposed cache as a net CI loss. Keeping the reusable cache outside each disposable run root preserved the runner cleanup contract, but a warm build saved only 34 seconds (97s cold to 63s warm) while producing an 804 MB cache. Persisting that entry per commit would consume the repository cache budget and evict the substantially more valuable Playwright browser cache. No cache wiring ships; reconsider only if Next materially reduces the cache size or a later measurement changes the storage/time trade-off. Source: `scripts/run-playwright.mjs`; `docs/testing.md`; session 2026-07-30 | 2026-07-30 | -| #085 | rec | Upload-limit client/server sync is unguarded | Resolved 2026-07-30. A provider-free parity checker now validates both configured limits, their shared 150 MB default, invalid and over-ceiling values, and mismatches. It runs in `verify:cheap`, the static PR job, and before every production build so a client-side value cannot silently diverge from the server runtime limit. | 2026-07-30 | -| #119 | issue | `ci/circleci: verify` is failing repo-wide | Retired 2026-07-30. CircleCI was removed from the repository in commit `977982857`, and the user confirmed the integration is deleted. The prior request to inspect build 672 is obsolete; GitHub Actions remains the repository CI path. | 2026-07-30 | -| #125 | issue | `ui-therapy-nav-scroll.spec.ts` cites a spec file that does not exist | Resolved 2026-07-30. The Therapy navigation comment now points to the live `ui-mode-nav-density.spec.ts` coverage instead of the deleted `mode-nav-bar-anchoring.spec.ts` file. | 2026-07-30 | -| #095 | issue | `PR required` reports failure for concurrency-cancelled jobs | RESOLVED 2026-07-30. The `pr-required` aggregate now distinguishes a cancelled job from a failed one. `require_success` / `require_skipped_or_success` are thin wrappers over one `record()` collector that reads **each job's own `result`** and appends to a `failures` or `cancellations` array; both arrays are filled before anything is reported. **Genuine failures win:** every failure is emitted as its own `::error::` and a concurrent cancellation is demoted to a `::warning::`, so a run that is cancelled AND broken cannot read as an excuse (refinement reported by Codex on PR #1409). Cancelled with nothing failing stays **RED**, and the message states the two possibilities rather than asserting supersession: it points the reader at a newer `PR required` run on the PR's current head SHA, and says that if there is none the run was cancelled by hand and must be re-run rather than merged past. It deliberately does NOT read the workflow-level cancelled status function — an earlier revision passed that through an `env:` value, which is invalid because GitHub allows those functions only in `if:` conditions, so the whole file failed to parse, ran as `.github/workflows/ci.yml` instead of `CI`, and created zero jobs; valid YAML but invalid Actions schema, so prettier and every local gate passed it. **The tempting fix was rejected as unsafe:** treating `cancelled` as neutral, or skipping the aggregate via a not-cancelled condition, would make the red disappear, but GitHub counts a SKIPPED required check as PASSING, so a hand-cancelled run on the current head would become mergeable with nothing verified — `if: always()` is therefore deliberate. Guarded by ten cases in `tests/ci-cache-safety.test.ts` that EXECUTE the extracted aggregate script under synthetic job results rather than grepping the YAML. **Corrected in review (Codex, PR #1428):** the first version of this record described a shared `cancelled_error` helper that never existed and claimed the error names the newest run; both were carried over from an obsolete sentence in the open row and neither matched the shipped implementation. Source: PR #1316 runs 30340972329 / 30341225585; PR #1409 | 2026-07-30 | -| #096 | task | PR #1316 review follow-ups — adoption-gate coverage closed | RESOLVED 2026-07-30. Every sub-item is dispositioned. The band adoption gate's root-path gap closed on PR #1394 — root and href-less modes now resolve to `src/app/(search-app)/page.tsx` — and closing it surfaced two further defects in the same gate the original finding did not name: the hand-rolled walk was capped at two import hops where the root route's real chain is four, and it followed neither `layout.tsx` (where that route's band actually comes from) nor `dynamic(() => import(...))` (how the dashboard code-splits its mode workspaces). All three were fixed together with a bounded BFS, each verified load-bearing by reverting it and watching the gate fail. Four findings — favourites hub counts, the document-search status derivation, the 401 session-expiry path, the record-path duplicate notice — were already fixed independently. The Therapy Compass retry-waiter finding was corrected to NOT a live defect: `retryWaitersRef` is genuinely unscoped but no caller observes it. The seven Codex follow-up SHAs remain unreachable and were never pushed; the PR #1316 review threads are the durable source. Archived by the 2026-07-30 triage pass. Source: PR #1316 review sweep | 2026-07-30 | -| #104 | rec | Worker's triple image read is deliberate, not debt | NOT DEBT — archived so a fourth audit does not re-file it. The 2026-07-28 latency audit listed L4-2 (`worker/main.ts` reads each extracted image up to 3x per document — hash, caption on cache miss, upload) as "CONFIRMED with no fix evidence", carried forward from the 2026-07-01 audit's L11. **That was wrong.** The 2026-07-01 disposition table already recorded it as a deliberate peak-memory trade-off, and the rationale is documented in place at `worker/main.ts:866-869`: holding every extracted image Buffer for a document with hundreds of multi-MB page images would multiply the worker's peak memory, and disk I/O is the cheaper resource for a background pipeline. The three reads (`:872`, `:1034`, `:1129`) are real but accepted. This row carried no next action, so it is archived rather than left open — revisit only if ingestion throughput becomes a measured complaint AND a bounded-buffer design is proposed. **Stop:** do not re-file this as debt. Source: `docs/audit/repo-audit-2026-07-01.md` L11 + disposition table | 2026-07-30 | -| #109 | issue | Remote sessions clone shallow, invalidating branch analysis | RESOLVED 2026-07-29, hardened in review. `scripts/sweep-branch-ledger.mjs` refuses outright on a shallow clone via the exported `shallowCloneRefusal`, printing no inventory and exiting 1 in both text and `--json` mode, before the fetch and before any branch maths; `docs/branch-cleanup-guide.md` §Safety Rules gains the `is-shallow-repository` precondition ahead of its numbered steps. An indeterminate result (empty output from a swallowed `git` failure) is refused as its own failure, and the same guard had to move OUT of the reconciliation preflight CLI and INTO the exported `collectReconciliationState`, because `buildReconciliationEvidencePack` calls the collector directly and stamped `status: complete` — in a `--depth 1` clone the guarded CLI exited 1 while the evidence-pack command exited 0 and persisted shallow ahead/behind as completed evidence. The collector now throws `UnverifiedHistoryError` so every current and future caller fails closed by default. Review found two further routes to a false-empty inventory from half-checked refspecs; coverage requires exactly `refs/heads/*` to `refs/remotes/origin/*`. Proven against real `--depth 1` clones in `tests/repo-hygiene.test.ts`, `tests/reconciliation-preflight.test.ts` and `tests/reconciliation-evidence-pack.test.ts`. Source: session 2026-07-29 | 2026-07-29 | -| #115 | rec | Band adoption gate treats a discovered import as rendered | RESOLVED 2026-07-30 by redesign rather than another patch. `tests/search-results-band-adoption.test.ts` no longer asks whether a file mentions the band but whether anything the route actually mounts reaches it: it parses each module with `@babel/parser` into a small graph — exported name to local declaration, local to the identifiers its body references, and which locals render the band — then walks from the route's default export, carrying at each hop the set of exports the importer mounts. A static `import { X }` is followed only when `X` is reachable from a mounted declaration; `dynamic(() => import(...).then((m) => m.Named))` follows only that binding; a bare side-effect import is not followed at all; `export *` never supplies a default. **Why redesign:** six false greens were reported in one day, all one defect — presence is not reach — and two of the six had been introduced by an earlier patch to the same walker. Verified: all five production search routes still reach the band, gutting `services/page.tsx` and `tools/page.tsx` to `
` reports an orphan each, and fourteen temp-dir fixtures cover both directions with two mutation-confirmed. Accepted residual: reachability is per module and control flow inside a mounted declaration is not modelled. Source: PR #1400 | 2026-07-30 | -| #108 | task | Five verified-landed remote branches await deletion (blocked in-session) | DONE 2026-07-30. Deleted the three remaining empty remote refs after re-verify (tree(tip)==tree(merge-base), 0 files vs main, no open PR): `codex/document-reader-condensed-view` @ `b5cdbf301d517239ffe9ed941b9ebe809aea0bfd`, `cursor/page-anchored-search-composer-30ee` @ `7ff134ca7f614db527b8d142676640305533669d`, `cursor/pr-1379-babysit-ledger-9365` @ `be2de03f855cb7fdfccea4bb74d05eb4c9bf6c61`. The other two candidates were already gone. Completed `branch-cleanup` ledger rows were already present; deletion succeeded from this session (prior 403 cleared). | 2026-07-30 | -| #088 | task | Union-driver ledger duplication watch after repair | CLOSED 2026-07-30. Post-repair merges take main's repaired lines; residual exact-dupe babysit twins from stock `merge=union` are addressed by the custom `merge=ledger` driver (union + exact-row dedupe), `npm run ledger:dedupe`, and the Run PR anti-churn ledger policy. Success criterion (three consecutive post-repair guard passes) met on ordinary main syncs; ongoing exact-dupe class is now gated rather than watched. | 2026-07-30 | -| #087 | issue | `npm run check:knip` reported pre-existing dependency findings | NOT A DEFECT — false positive. The findings (unused `rimraf`/`tsx`/`@testing-library/dom`, unlisted `playwright-core`) only appeared because the worktree had no `node_modules` of its own and tooling resolved from the parent checkout. After `npm ci` in the same worktree, `npm run check:knip` exits 0 with no ignore-list change. Durable lesson: never act on a knip finding from a worktree that has not been installed. | 2026-07-28 | -| #089 | issue | Branch-review-ledger hygiene deferred from PR #1275 | Closed by the 2026-07-28 hygiene pass. The MD056 cell-count and duplicate-row findings dispositioned as "belongs in a dedicated main ledger hygiene pass" on PR #1275 are fixed: 140 mojibake lines restored byte-exact from git history, 6 residual separators repaired, 46 exact duplicates removed, 21 wrong-width rows normalised, and 4 heading+bullet records converted to table rows — 1067 records, all six cells. Root cause closed too: `npm run ledger:lookup` / `ledger:append` (`scripts/branch-review-ledger.mjs`) replace hand-written rows, and `check:branch-review-ledger` now fails on mojibake, cell width, heading records, impossible dates, table gaps, and (from 2026-07-29) unresolvable HEADs and near-duplicates. | 2026-07-28 | -| #097 | issue | Gitleaks reports a false red when the PR head moves mid-run | RESOLVED 2026-07-30: Secret Scan checks out the event head SHA and runs `scripts/run-gitleaks-pinned.mjs` against the immutable event base..head range (no mid-run PR commits API re-query). | 2026-07-30 | -| #111 | issue | `ui-overlap` phone-inset case still flakes under load | RESOLVED 2026-07-29 (PR #1391). The inset measurement now retries inside `expect(async () => …).toPass({ timeout: 15_000 })` (`ui-overlap.spec.ts:159`) — the same retry-the-measurement shape PR #1375 applied to the eight overlap cases. The 2px symmetry tolerance and the assertions themselves are byte-for-byte unchanged, so a genuinely asymmetric header still fails once the retry budget is spent; only a transient mid-remount sample settles. Full spec green across 4 consecutive runs (14 passed each). Do not relax the tolerance if this recurs — the assertion is the point; make the measurement robust instead. Source: PR #1375 flake triage; session 2026-07-29 | 2026-07-29 | -| #112 | issue | `issues:next-id` has no concurrency protection | RESOLVED 2026-07-30. `npm run check:outstanding-issues` now gates this file, in `verify:cheap` and in the `static-pr` CI job (the gate-manifest check refuses a local gate that CI does not run). It fails on a duplicate id, an id present in both tables, a marker at or below the highest id, a malformed row, and a missing heading or marker — so every shape the 2026-07-29 triple collision took is now a red gate rather than a silent row loss. Verified by replaying that collision against the real file: two rows claiming `#110` produced "#110 appears 2 times (lines 151, 164)", and a lost marker bump produced "issues:next-id=113 is not above the highest id #114". The checker honours `\|` escapes — its first run against the live file flagged row #042, which is correctly escaped, and a gate with false positives is a gate people switch off. NOT fixed: the underlying race. Ids are still allocated by read-modify-write with no lock, and this file still has no `merge=union` driver; what changed is that a collision can no longer land silently. Source: session 2026-07-29 PR sweep; PR #1391 conflict resolution; `.gitattributes` | 2026-07-30 | -| #113 | issue | `ModeNav` clips its labels at every phone width on `main` | RESOLVED 2026-07-30 (PR #1405, landed on `main` as `020c1260`). The two lower density bands used `grid-auto-columns: 1fr`, so the widest slot set every slot's width and the label's `truncate` hid the shortfall — nothing overflowed and nothing failed, the word was simply gone. `display: flex` at every band removes the mode rather than retuning it; thresholds went `16/26/34rem` -> `22/33/42rem` against remeasured intrinsic widths (471px for four labels, not the 394px originally budgeted). `tests/ui-mode-nav-density.spec.ts` asserts at both band boundaries and one pixel either side, registered in BOTH Playwright allowlists. **Carry-forward:** the ~8% threshold headroom is not padding — a measured-plus-one-pixel attempt (21/31rem) passed locally and failed CI by exactly 1px, because the variable face rasterises wider on `ubuntu-24.04`. A threshold calibrated to one machine's font metrics is calibrated to nothing. | 2026-07-30 | -| #114 | issue | The live Web-Vitals instrument cannot measure its own noise | RESOLVED 2026-07-30 (PR #1407, landed as `a73db460`). Each route/strategy cell is measured `SAMPLES` times (default 3), the MEDIAN is graded, and a cell whose samples straddle a threshold is reported as incomplete evidence rather than resolved either way — so #017's "evidence is too noisy" stop rule can finally fire. Mid-run Chrome drift disqualifies the verdict. **Second defect found in review and fixed in the same PR:** `samples` is a free-text `workflow_dispatch` input, so `samples=1` reached the grader as a valid integer and reproduced the exact single-run matrix this work removes — verified against the pre-fix logic, it reported 0 breaches, 0 incomplete and printed the bold "Every mobile route is within" verdict. Now refused in three places (`main()` before reading any report, `renderTable`'s disqualifier list, and the workflow before spending a live measurement pass). | 2026-07-30 | -| #120 | issue | `verify:phone-chrome` exits 0 while reporting failed browser tests | RESOLVED 2026-07-30 — **closed as not reproducible, not as fixed.** The premise is contradicted by a reproduction of its own failure condition. **Evidence, `main` at `fcd9041`:** the container ships Chromium build `1194` while the repo wants `1234`, so `npm run verify:phone-chrome -- --files src/components/clinical-dashboard/mobile-composer-reserve.ts` reproduced the identical shape — the same `13 failed`, every one at `browserType.launch: Executable doesn't exist at /opt/pw-browsers/chromium_headless_shell-1234/…` — and the captured status was **`OVERALL_EXIT=1`**, not 0. The gate stopped at the `focused-browser` stage and never reached `full-ui`, which is correct; the three stages ahead of it were genuinely green (`Test Files 9 passed (9)`, `Tests 105 passed (105)`). **The code path agrees:** status propagates at `scripts/verify-phone-chrome.mjs:72-73` and `scripts/run-playwright.mjs:271-273`, and `childProcessExitCode` returns `1` when `status` is not an integer, so a launch error cannot read as 0. Neither file, nor `scripts/child-process-result.mjs`, has changed since `7581cfc`, which predates this row — so "fixed in the meantime" is ruled out and the original exit-0 reading is what is in doubt. **Leading hypothesis, explicitly unverified:** the 0 was read through a pipe, since `npm run verify:phone-chrome` piped to `tail` reports `tail`'s status rather than the gate's. The original command line was not recorded, so this cannot be confirmed. **Not covered by this reproduction:** the zero-selected-test case (a `--grep` matching nothing) was never exercised — only the launch-failure case this row describes. **If it recurs:** capture the exact command line and `echo $?` with no pipe before re-filing. | 2026-07-30 | -| #122 | issue | `ci/circleci: verify` fails on every branch and its log needs operator access | RESOLVED 2026-07-30 — moot: the status cannot mislead because the provider is gone from the repository. The row's own outcome allowed either branch ("trustworthy signal again, **or** it stops reporting"); the second is what happened, so no operator log read is needed and the **Next** step is withdrawn. **Evidence:** `.circleci/config.yml` was deleted by `9779828` ("chore: remove CircleCI from repo", PR #1412), and `ls -d .circleci` on `main` at `c5c1a86` returns `No such file or directory`. Confirmed live rather than only from the tree: PR #1452's head reported **21 check runs and not one named `ci/circleci: verify`**, so the status no longer reports on new PRs at all. The unverified quota hypothesis is retired unproven — it was never confirmable without a CircleCI token, and there is now nothing left to confirm it against. One residual was found and fixed in the same change: `AGENTS.md` still cited `ci/circleci: verify` as a check that fails on unformatted files, which would have sent a future session chasing a check that can never report again. | 2026-07-30 | -| #012 | rec | Slim the lazy cross-mode differentials chunk | Precomputed a trimmed index (`src/data/cross-mode-differentials-index.json` via `scripts/build-cross-mode-differentials-index.mjs`) so the lazily-loaded cross-mode chunk imports a ~53 KB catalog instead of statically pulling the ~1.2 MB differentials snapshot (only that dynamic path reached it). A drift test plus `check:cross-mode-index` (in verify:cheap) lock the index to the live projection. | 2026-07-27 | -| #029 | issue | Residual answer-quality fallback stubs | Closed after fixing each causal cluster independently. Active-community ED, community-home-visit, clozapine blood-threshold/typo, discharge source-gap recovery, and Best Practice Prescription now use narrowly validated, source-bound answers or auditable recovery; cited provider refusal prose can no longer masquerade as grounded, and terminal gaps retain no claim citations. The final 44-case gate reported 30/30 substantive grounded supported answers, 14/14 unsupported correct, zero review fallbacks, zero citation/numeric failures, and zero route-ceiling failures. Measurement still reports review fallback separately and denies targeting credit for echoed boilerplate. | 2026-07-27 | -| #019 | issue | Preserve admission/discharge sources through comparison fallback | The actual fallback path now selects source-bound facts, preserves one admission and one discharge citation from distinct documents, and terminates at an evidence gap for qualified, negated, unrelated, title-only, single-sided, or same-document traps. Both exact live cases complete in about one second with zero provider calls; the final 44-case canary passed them with two citations each, while the 36-case retrieval canary held recall 1.0 and zero RR regressions. Retrieval scores, aliases, clamps, and comparator ordering were unchanged. | 2026-07-27 | -| #084 | task | Persist per-result irrelevant-at-10 grading evidence | `eval-retrieval` now persists each top result's `relevanceGrade` and `matchedDeclaredSignals`; focused fixtures cover ideal and zero-grade rows. The final golden artifact contains 338 graded top rows, including 33 grade-zero rows. This closes the reproducibility gap only: fixture labels, ranking, thresholds, and provider behavior were not changed, and human disposition remains #023. | 2026-07-27 | -| #080 | rec | Re-test the removed admission-to-discharge alias widening | Restored the two approved NMHS Admission-to-Discharge titles only on the eval-expectation surface. Canonical document-identity dedupe plus maximum bipartite matching prevents one dual-listed physical document from satisfying both comparison slots. Focused matching tests, both targeted admission cases, the final 36-case golden retrieval run, and the 44-case answer run passed; runtime retrieval/ranking behavior was not changed. | 2026-07-27 | -| #083 | issue | Documents-only universal search timed out on staging tenancy | A current staging nightly reproducer showed the documents-only search losing its synthetic fixture after the federated typeahead timeout was reduced to 750 ms. Current main retains 750 ms for multi-domain requests and uses the established 6,000 ms budget only when documents are the sole requested domain; fake-timer coverage proves both paths. RAG impact: no retrieval, ranking, ordering, alias, score, or result-selection change—only availability of the explicitly focused request. | 2026-07-27 | -| #082 | issue | Bot branch-sync heads leave required checks unapproved | Retired the automatic `GITHUB_TOKEN` PR branch-update workflow instead of weakening required-check approvals or introducing a privileged automation token. The existing helper remains dry-run by default, verifies its apply identity, and refuses missing or bot identities. The fast GitHub Actions policy check rejects both direct workflow `update-branch` calls and indirect apply-helper invocation. | 2026-07-27 | -| #058 | task | Verify production content before any seed write | Read-only production counts on project `sjrfecxgysukkwxsowpy` found 276 clinical registry, 328 medication, and 232 differential records. The required tables are non-empty, so no seed or production write was needed. | 2026-07-27 | -| #069 | task | Validate hosted table-facts RPC latency | Read-only profiling on the correct hosted project separated sample 1 (`first_unprimed`) from five `warm_repeat` samples; managed Supabase buffers were not flushed, so no true-cold claim is made. First-unprimed client/DB execution was 662.916/141.537 ms (clozapine), 277.661/96.229 ms (lithium), and 322.598/148.378 ms (metabolic). Warm client median/p90 was 187.029/198.907, 167.803/174.391, and 189.065/243.324 ms; warm DB execution median/p90 was 88.292/89.355, 64.342/65.566, and 107.448/148.417 ms. Earlier exact clinical probes were lower again. Plans are not the multi-second tail; no hosted migration, ranking, or provider configuration changed. | 2026-07-27 | -| #051 | task | Stabilise the live answer-quality canary before more RAG tuning | Closed after the scheduled structured report supplied a comparable second 36-retrieval/44-answer datapoint. Content gates stayed stable, the prior citation failure cleared, and #019 repeated with an identical diagnostic signature. Retrieval latency was investigated separately: #069 subsequently found acceptable table-facts database plans, so the broad scheduled tail was not treated as ranking debt. The report/trend tooling is now sufficient to compare future approved runs; no scheduled rerun or tuning was dispatched. | 2026-07-27 | -| #054 | task | Reconcile local and hosted secrets/config | Completed production names-only reconciliation on 2026-07-27. The correctly identified primary checkout received distinct gitignored local safety/query-hash/deep-probe values. A hardened checker now pins GitHub to `BigSimmo/Database` and Railway to the live production project/environment plus `Database`/`worker`, catches multiline schema and `.env.example` drift, and verifies GitHub secrets/variables and per-service Railway contracts without emitting provider values. All required names passed; the Ops Digest workflow is active with a successful scheduled run; both Railway services have later successful deployments; Supabase names-only proof found the expected cron/Vault configuration. Value equality remains deliberately unobservable, staging stays #056, webhook activation stays #025, and legal/ZDR work stays #053. | 2026-07-27 | -| #064 | task | Reconcile the preserved browser and contrast patch | Landed via PR #1250 squash `b91b4600171be08198e92bcf19b7d67e8207cb2f`. Opacity-free disabled Previous/Continue styling plus native-disabled/focus/axe Playwright coverage is on `main`. Historical `agent/formulation-disabled-contrast` remained unrecovered; conflicted PRs #1219/#1223/#1226/#1231/#1249 were closed without merge. | 2026-07-26 | -| #081 | issue | Open PR #1196 would undo the #030 alias tightening | Closed as no longer live: PR #1196 was closed 2026-07-25 as superseded by #913/current `main` (~680 commits behind, conflicting), and its successor #1198 does not touch `src/lib/eval-document-matching.ts`. The generalized alias-disjointness and single-document contracts landed in PR #1215 fail closed if any later branch re-adds the dual-listed admission aliases, so the regression route is guarded rather than watched. | 2026-07-25 | -| #077 | issue | Concurrent tasks can re-dirty the canonical primary checkout | Added cooperative primary-checkout write lease with dirty/operation fail-closed checks, stale-owner recovery, and lifecycle start/cleanup wiring; focused concurrency tests refuse a second primary writer while read-only/feature worktrees stay unblocked. | 2026-07-25 | -| #078 | task | Generate a deterministic reconciliation evidence pack | Added report-only atomic evidence pack with dispositions, markers, archive refs, bundle verify/hash, worktree counts, and local/base equality; fixture tests prove determinism/redaction and no false completion record on interrupt. | 2026-07-25 | -| #066 | task | Land and prove the streamlined six-item sidebar | Proven on `origin/main` via PR #1174 (`4dc76306 Land streamlined six-item sidebar`). Six-item rail shipped; open ledger row was stale post-merge. | 2026-07-25 | -| #067 | issue | Reconciliation preflight test times out under full-suite load | Fixed in PR #1191 (`e2488dbb`) by calling `collectReconciliationState()` in-process; PR #1203 further injects a fixture `repositoryRoot` so the contract no longer scales with the live worktree farm. No global timeout raise or heavy-test lock bypass. | 2026-07-25 | -| #007 | rec | `/tools` vs `/?mode=tools` parallel Tools entry points | Resolved as `/tools` canonical (PT-11 already documented on `/applications` redirect). Sidebar, appModeHomeHref, universal-search, prefetch, sitemap, and reachability now use `/tools`; `/?mode=tools` remains a dashboard-mode alias. Reachability allowlist entry removed. | 2026-07-24 | -| #030 | issue | Wide-tier alias lets one doc satisfy both comparison slots | Fixed on `cursor/search-correctness-030-075-6273`: removed dual-listed Admission-to-Discharge titles from AdmissionCommunityPts so one retrieved source cannot make allHit true for both comparison slots; fail-closed contracts in `tests/eval-document-matching.test.ts`. RAG impact: no retrieval behaviour change — eval matching only. Hardened after merge: coverage dedupes by document identity and assigns by maximum matching (#080). | 2026-07-24 | -| #075 | issue | Search-scope label enumeration can truncate after 1,000 rows | Fixed on `cursor/search-correctness-030-075-6273`: `loadScopeLabels` pages document_labels with deterministic order/batching past the Supabase 1k cap; multi-page >1000 contracts in `tests/search-scope.test.ts`. Isolated from mixed PR #1132. RAG impact: no retrieval behaviour change — label pagination only. | 2026-07-24 | -| #009 | rec | Confirm `/api/jobs` is intentionally server/ops-only | Kept as deliberate administrator/ops listing: no client `fetch("/api/jobs")` (UI uses `/api/ingestion/jobs`); documented in `docs/api-jobs-ops-surface.md` plus wiring/codebase-index/site-map notes. Not abandoned — do not remove without updating API contract tests. | 2026-07-24 | -| #010 | task | Un-built "Coming soon" controls across forms/favourites | Audited forms/favourites/presentation placeholders: all use honest `disabled` or `aria-disabled` + coming-soon copy (or presentational `ToggleSwitch` without `onToggle`). No fake-interactive controls; leave unwired until features land. Recorded in `docs/wiring-conventions.md`. | 2026-07-24 | -| #032 | rec | Governance ranking weighting: REFUTED, not debt | Reinforced as guardrail only in `docs/rag-behaviour/refuted-approaches.md` (Refutation 3), README, and safeguards — do **not** implement `review_due`/unknownCurrentness ranking penalties or boosts. No retrieval/ranking code changed. RC8 filter path remains the only revisit route behind canary gates. | 2026-07-24 | -| #041 | rec | Extend the existing Factsheets reading model | Brief recorded in `docs/factsheets-reading-model-brief.md`: extend Easy Read/Standard on existing Factsheets routes; reject a second patient-facing Factsheets mode unless concrete need + source-governance plan exist. | 2026-07-24 | -| #063 | rec | Define “Current Clinical Work” before implementation | Product/privacy/persistence brief recorded in `docs/current-clinical-work-brief.md`. Default v0 = no new storage (tab/URL resume); Class C free text needs privacy clearance. Stop without demand evidence. No UI/schema implemented. | 2026-07-24 | -| #076 | task | Reproduce malformed fallback PDF image/table crops | Reproduced truncated page-edge `table_crop`s on current-main with `worker/python/fixtures/malformed-table-crop-page-edge.pdf`. Root cause: `pymupdf_find_tables` stops at the last fully detected row; fix extends the candidate from contiguous cell drawings, recovers the on-page score-5 remnant, and emits `table_crop_edge_incomplete` / `crop_completeness=0.9` when content continues past the page. PR #1176. Broad PR #1129 retention/padding changes not merged. | 2026-07-24 | -| #070 | issue | Presentation mobile tabs misroute Overview/Map/Related | Fixed in PR #1135: Overview/Map/Related deep-link to diagnosis `?tab=` sections; Compare stays on the presentation page. Regression in `tests/mobile-interaction-regressions.test.ts`. (Provisional PR-branch IDs `#068`–`#072` were renumbered after `main` claimed `#068`/`#069`.) | 2026-07-24 | -| #071 | issue | Evidence/Clinical Notes Add fakes success without persistence | Fixed in PR #1135: sticky Add controls use the focusable coming-soon placeholder pattern instead of optimistic `setAdded(true)`. | 2026-07-24 | -| #072 | issue | Tools hub exposes false Sort/More affordances | Fixed in PR #1135: Sort is a status label, More filter targets coordination/saved without a fake menu chevron, and the favourites shortcut is labelled Saved/Favourites. | 2026-07-24 | -| #073 | issue | Presentation compare dock CTA is a self-link no-op | Fixed in PR #1135: dock shows non-link "Comparing (N)" status while already comparing. | 2026-07-24 | -| #074 | issue | Mode-action popup hard-reloads internal clinical routes | Fixed in PR #1135: `master-search-header` uses `router.push` for DSM/Specifiers/Formulation actions and mode href fallback. | 2026-07-24 | -| #068 | task | Regenerate full drift-manifest snapshot after schema hygiene | Full Docker `npm run drift:manifest` replay succeeded on a Docker-capable host; `supabase/drift-manifest.json` now carries live `def_hash` values for the plpgsql table-facts body (offline generator_note removed). | 2026-07-24 | -| #052 | issue | Reindex can overlap a fresh agent-enrichment pass | PR #1143 retained the friendly full/retry preflight and closed its check-then-enqueue race with an owner-scoped transactional RPC. Reindex enqueue and the agent claim path serialize on the document row; disposable PostgreSQL proved both interleavings, and exact-head migration replay/unit/build/Chromium/policy/security checks passed. | 2026-07-24 | -| #062 | issue | Upload crash can strand a queued document without a job | Aged owner-scoped `queued`-without-open-job rows are detected by `reindex:health`; the six-hour autopilot raises a durable alert, and guarded recovery uses PR #1143's transactional RPC so enqueue is owner-scoped, idempotent and atomic. `recover:ingestion --include-stranded-queued` remains dry-run/confirmation-first; scheduled production mutation is not enabled. | 2026-07-24 | -| #060 | issue | Safety Plan Generator contradicted the privacy contract | PR #1119 removed patient identifier entry, leaves the post-export name line blank, and aligned tool, privacy and PIA copy. DOM/privacy tests and Chromium copy/print/network coverage prove working content remains in React memory with no fetch/XHR; hosted Production UI, build, unit, policy, safety, static-analysis and secret checks passed. Support-contact details remain classified as sensitive local-only working content. | 2026-07-24 | -| #061 | issue | Missing answer relevance metadata was treated as source-backed | PR #1125 now requires explicit source-backed relevance for trusted/grounded presentation and prevents visual tables, clinical-note sections and quotes, and comparison metadata from bypassing the render model. Three actionable P2 review paths were fixed; focused policy/DOM tests, offline RAG, production-readiness, build, unit, static, security, and Production UI gates passed. No retrieval, ranking, generation, provider, or data behavior changed. | 2026-07-24 | -| #034 | issue | Answer cache can serve stale governance metadata | Current-source verification found direct route coverage already asserts RAG-cache invalidation on document PATCH, source review, label, bulk, and reindex mutation paths. The residual test recommendation is already met; changing the protected cache key is unnecessary. | 2026-07-24 | -| #014 | rec | Realize the `next/image` win on signed previews | Superseded: `SignedImage` uses `next/image` for layout and sizing but deliberately sets `unoptimized`, preventing bearer signed URLs from entering the unauthenticated optimizer cache where cached content could outlive the token. No optimization task remains unless private-image delivery changes. | 2026-07-24 | -| #026 | task | Wire the Supabase document-change trigger | PR #1100 merged after disposable PostgreSQL replay and hosted migration replay. Production migration history and read-only catalog proof confirm the enabled metadata trigger, security-definer function, pinned search path and denied anonymous/authenticated execution; `npm run check:drift` reports no unexpected live drift. Delivery remains intentionally inert until the operator inputs tracked in #025 are configured. | 2026-07-24 | -| #031 | issue | Populate canary Source Governance table | The answer-quality step now consumes the preceding `golden-retrieval.json` only for source-governance reporting. Offline replay of run `30018289898` populated 338 top results, including 202 review-required entries, while retaining zero retrieval cases and no additional threshold failures. Retrieval and ranking behavior are unchanged. | 2026-07-24 | -| #020 | task | Validate eval:quality cost readout post-fix | Confirmed on merged-main canary run `30018289898`: Answer Metrics reported 9 nonzero-cost cases and an estimated answer cost of `$0.234736`; the structured report retained the same value. The PR #1050 estimator fix is operationally proven. | 2026-07-23 | -| #003 | task | Staging tenancy release evidence outstanding | Ran GitHub Action and validated isolation | 2026-07-21 | -| #002 | task | Process-ownership fix not yet isolated on `main` | Fixed process isolation using child.pid termination | 2026-07-21 | -| #008 | rec | Dead href builders in `document-flow-routes.ts` | Not dead code (false positive): `documentReaderHref`/`documentEvidenceHref` are live via the mock wrappers in `src/components/document-search-mockups.tsx` + `src/components/master-document-flow-mockups.tsx` (rendered under `src/app/mockups/document-search/`) and covered by `tests/document-flow-routes.test.ts`; removing breaks the build. Only the production non-mock hrefs are unlinked from prod UI — a wiring gap, not dead code. | 2026-07-22 | -| #015 | task | Content-first fallback regression tests | Added `tests/registry-record-loader.dom.test.tsx` (8) + `tests/medication-record-page.dom.test.tsx` (6) covering content-first fallback paint, live swap-in, spinner/skeleton, error + not-found/unauthorized states, and the invariant that no authoritative verification badge shows before live governance reconciles (registry fixture-flag neutralization + medication governance-drop-on-error). | 2026-07-22 | -| #004 | rec | Rescope provider-gated RAG safety ideas | Closed obsolete — rescue source (754-line RAG-safety worktree) unrecoverable/pruned across all refs; answer-quality thresholds + deep-health already shipped on `main` (#585/#587); only cost-cap preflight was genuinely missing and, per session decision, dropped rather than re-filed. | 2026-07-22 | -| #006 | issue | Globe "Language & region" button had no handler | Resolved on main with the repository's disabled "Coming soon" placeholder convention and button-wiring coverage. Future language/region work remains a feature request, not an inert-control defect. | 2026-07-22 | -| #042 | issue | Invalid optional credentials fell into anonymous access | PRs #1078/#1079 introduced `absent \| valid \| invalid`, return 401 for presented invalid credentials, preserve authoritative header precedence and prefer the current-project session cookie. The archived anonymous-upload metadata patch was rejected as stale because uploads are already administrator-only before duplicate lookup. | 2026-07-22 | -| #043 | issue | Readiness could report healthy or throw on Supabase errors | PR #1080 now fails readiness closed for returned and thrown dependency failures, preserves recognized actionable messages, and prevents raw dependency-error disclosure. | 2026-07-22 | -| #044 | issue | Publication approval was not bound to immutable reviewed state | PR #1081 added a canonical reviewed-state digest, row locks, active-job rejection and a new forward migration with replay/schema/type/drift evidence. | 2026-07-22 | -| #045 | issue | Bulk reindex discarded partial-success results | PR #1084 reserves preflight conflicts for non-2xx responses; completed mixed batches return per-item success/failure/missing results, and the UI refreshes successful work. | 2026-07-22 | -| #046 | issue | DOCX extraction lacked explicit resource budgets | PR #1085 added pre-inflate declared-size checks and post-read fail-safes for artifact count, per-artifact bytes, aggregate media, Word XML and extracted UTF-8 text. | 2026-07-22 | -| #047 | issue | XLSX extraction could construct unbounded results | PR #1086 bounds worksheets, non-empty rows, rendered cells and UTF-8 output while preserving sparse-column rendering. | 2026-07-22 | -| #048 | issue | Account copy overstated sync/privacy and enabled unavailable SSO | PR #1087 now maps copy to actual favourites/preferences persistence, identifies browser-session recents, removes the contradictory "never shared" claim and clearly disables unavailable providers using the accessible placeholder contract. | 2026-07-22 | -| #049 | issue | Process diagnostic exposed a Cursor worker API key | The exact worker was stopped, the key was revoked server-side, both local encrypted worker-secret records were removed, and authorized repository/backup scans found no plaintext copy. Follow-up guardrails now prevent repository process inventory from serializing command lines and redact heavyweight-lock command text before persistence or errors. | 2026-07-23 | -| #050 | issue | Next.js 16.2.10 remained in a high-severity security range | Upgraded `next` and `@next/env` to 16.2.11, regenerated the npm lockfile, confirmed the production dependency audit is clean, and passed focused framework checks, `verify:cheap`, and the full Chromium UI gate. | 2026-07-23 | -| #116 | issue | Missing CI signal on conflicted PRs | RESOLVED 2026-07-30: `.github/workflows/pr-mergeability.yml` checks trusted `pull_request_target` events and refreshes unchanged heads after protected-base pushes. A conflicted PR gets a red `PR mergeability` check naming the conflict instead of an empty CI list. The base-push job alone has scoped `checks: write`; neither path checks out PR code or updates branches. Classifier: `scripts/pr-mergeability.mjs`; contract: `npm run check:pr-mergeability`. Behind-but-clean remains `sync:pr-branches`. | 2026-07-30 | -| #140 | issue | Union-driver table duplication — duplicate of #133 | CLOSED 2026-07-30 as a duplicate, superseded by `#133` and resolved by PR #1444. This row was opened mid-session for the union-driver damage before its author noticed `#133` had already recorded the same finding — and recorded it _earlier_, with the same conclusion ("**Do not** apply a `merge=union` driver — tested 2026-07-30 and it is worse"). Keeping both would have left two open rows describing one condition, and that condition no longer exists: PR #1444 removed `merge=union` from `.gitattributes`, inverted `check:outstanding-issues` to require an unspecified `merge` attribute, and added regression tests covering `union`, `-merge` and an unparsed reading. The surviving evidence — four merges on PR #1430 each reporting success while duplicating the entire open-items table — lives in `#133`, whose still-open half is the real conflict-frequency cause: fixed-width column padding makes any one-row edit re-pad every row, so git sees the whole table as one hunk. Read `#133`, not this. | 2026-07-30 | +| ID | Type | Summary | Outcome | Resolved | +| ---- | ----- | ----------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | +| #149 | issue | Failed clinical searches are announced to nobody — the results band mutes its live region | WITHDRAWN 2026-07-30, same day it was filed: the claim was FALSE and the proposed fix would have caused a regression. Codex caught it on PR #1481. **What I got wrong:** I read `aria-live={faulted ? "off" : "polite"}` on the count span in `search-results-header-band.tsx` and concluded a failed search is announced to nobody. I never checked whether another node announces it. It does — the band mounts a fault panel with `role="alert"` carrying the failure title, body and Retry (`search-results-header-band.tsx:407-414`), and the mute is deliberate and documented in place: "While faulted the live region is silenced (`aria-live="off"`) and the freshly-mounted fault `role="alert"` below makes the single announcement, rather than both speaking." `tests/search-results-header-band.dom.test.tsx` ("keeps exactly one status region and one alert while faulted") already pins exactly this, using singular role queries that throw on duplicates. **Why the proposed fix was worse than nothing:** escalating the count span to `role="alert"`/`aria-live="assertive"` in production would add a second alert alongside the fault panel, producing a duplicate announcement and breaking that test. **Not a defect in the mockup:** `search-refine-adaptive-mockups.tsx` has no fault panel, so there the count span is the only announcement channel and its escalation is correct — it simply does not port. **Stop:** do not re-file this. The production behaviour is correct as written; verify which node owns an announcement before calling a live region a defect. | 2026-07-30 | +| #136 | rec | Reuse Next build cache across isolated Playwright production builds | CLOSED 2026-07-30 after an end-to-end implementation benchmark rejected the proposed cache as a net CI loss. Keeping the reusable cache outside each disposable run root preserved the runner cleanup contract, but a warm build saved only 34 seconds (97s cold to 63s warm) while producing an 804 MB cache. Persisting that entry per commit would consume the repository cache budget and evict the substantially more valuable Playwright browser cache. No cache wiring ships; reconsider only if Next materially reduces the cache size or a later measurement changes the storage/time trade-off. Source: `scripts/run-playwright.mjs`; `docs/testing.md`; session 2026-07-30 | 2026-07-30 | +| #085 | rec | Upload-limit client/server sync is unguarded | Resolved 2026-07-30. A provider-free parity checker now validates both configured limits, their shared 150 MB default, invalid and over-ceiling values, and mismatches. It runs in `verify:cheap`, the static PR job, and before every production build so a client-side value cannot silently diverge from the server runtime limit. | 2026-07-30 | +| #119 | issue | `ci/circleci: verify` is failing repo-wide | Retired 2026-07-30. CircleCI was removed from the repository in commit `977982857`, and the user confirmed the integration is deleted. The prior request to inspect build 672 is obsolete; GitHub Actions remains the repository CI path. | 2026-07-30 | +| #125 | issue | `ui-therapy-nav-scroll.spec.ts` cites a spec file that does not exist | Resolved 2026-07-30. The Therapy navigation comment now points to the live `ui-mode-nav-density.spec.ts` coverage instead of the deleted `mode-nav-bar-anchoring.spec.ts` file. | 2026-07-30 | +| #095 | issue | `PR required` reports failure for concurrency-cancelled jobs | RESOLVED 2026-07-30. The `pr-required` aggregate now distinguishes a cancelled job from a failed one. `require_success` / `require_skipped_or_success` are thin wrappers over one `record()` collector that reads **each job's own `result`** and appends to a `failures` or `cancellations` array; both arrays are filled before anything is reported. **Genuine failures win:** every failure is emitted as its own `::error::` and a concurrent cancellation is demoted to a `::warning::`, so a run that is cancelled AND broken cannot read as an excuse (refinement reported by Codex on PR #1409). Cancelled with nothing failing stays **RED**, and the message states the two possibilities rather than asserting supersession: it points the reader at a newer `PR required` run on the PR's current head SHA, and says that if there is none the run was cancelled by hand and must be re-run rather than merged past. It deliberately does NOT read the workflow-level cancelled status function — an earlier revision passed that through an `env:` value, which is invalid because GitHub allows those functions only in `if:` conditions, so the whole file failed to parse, ran as `.github/workflows/ci.yml` instead of `CI`, and created zero jobs; valid YAML but invalid Actions schema, so prettier and every local gate passed it. **The tempting fix was rejected as unsafe:** treating `cancelled` as neutral, or skipping the aggregate via a not-cancelled condition, would make the red disappear, but GitHub counts a SKIPPED required check as PASSING, so a hand-cancelled run on the current head would become mergeable with nothing verified — `if: always()` is therefore deliberate. Guarded by ten cases in `tests/ci-cache-safety.test.ts` that EXECUTE the extracted aggregate script under synthetic job results rather than grepping the YAML. **Corrected in review (Codex, PR #1428):** the first version of this record described a shared `cancelled_error` helper that never existed and claimed the error names the newest run; both were carried over from an obsolete sentence in the open row and neither matched the shipped implementation. Source: PR #1316 runs 30340972329 / 30341225585; PR #1409 | 2026-07-30 | +| #096 | task | PR #1316 review follow-ups — adoption-gate coverage closed | RESOLVED 2026-07-30. Every sub-item is dispositioned. The band adoption gate's root-path gap closed on PR #1394 — root and href-less modes now resolve to `src/app/(search-app)/page.tsx` — and closing it surfaced two further defects in the same gate the original finding did not name: the hand-rolled walk was capped at two import hops where the root route's real chain is four, and it followed neither `layout.tsx` (where that route's band actually comes from) nor `dynamic(() => import(...))` (how the dashboard code-splits its mode workspaces). All three were fixed together with a bounded BFS, each verified load-bearing by reverting it and watching the gate fail. Four findings — favourites hub counts, the document-search status derivation, the 401 session-expiry path, the record-path duplicate notice — were already fixed independently. The Therapy Compass retry-waiter finding was corrected to NOT a live defect: `retryWaitersRef` is genuinely unscoped but no caller observes it. The seven Codex follow-up SHAs remain unreachable and were never pushed; the PR #1316 review threads are the durable source. Archived by the 2026-07-30 triage pass. Source: PR #1316 review sweep | 2026-07-30 | +| #104 | rec | Worker's triple image read is deliberate, not debt | NOT DEBT — archived so a fourth audit does not re-file it. The 2026-07-28 latency audit listed L4-2 (`worker/main.ts` reads each extracted image up to 3x per document — hash, caption on cache miss, upload) as "CONFIRMED with no fix evidence", carried forward from the 2026-07-01 audit's L11. **That was wrong.** The 2026-07-01 disposition table already recorded it as a deliberate peak-memory trade-off, and the rationale is documented in place at `worker/main.ts:866-869`: holding every extracted image Buffer for a document with hundreds of multi-MB page images would multiply the worker's peak memory, and disk I/O is the cheaper resource for a background pipeline. The three reads (`:872`, `:1034`, `:1129`) are real but accepted. This row carried no next action, so it is archived rather than left open — revisit only if ingestion throughput becomes a measured complaint AND a bounded-buffer design is proposed. **Stop:** do not re-file this as debt. Source: `docs/audit/repo-audit-2026-07-01.md` L11 + disposition table | 2026-07-30 | +| #109 | issue | Remote sessions clone shallow, invalidating branch analysis | RESOLVED 2026-07-29, hardened in review. `scripts/sweep-branch-ledger.mjs` refuses outright on a shallow clone via the exported `shallowCloneRefusal`, printing no inventory and exiting 1 in both text and `--json` mode, before the fetch and before any branch maths; `docs/branch-cleanup-guide.md` §Safety Rules gains the `is-shallow-repository` precondition ahead of its numbered steps. An indeterminate result (empty output from a swallowed `git` failure) is refused as its own failure, and the same guard had to move OUT of the reconciliation preflight CLI and INTO the exported `collectReconciliationState`, because `buildReconciliationEvidencePack` calls the collector directly and stamped `status: complete` — in a `--depth 1` clone the guarded CLI exited 1 while the evidence-pack command exited 0 and persisted shallow ahead/behind as completed evidence. The collector now throws `UnverifiedHistoryError` so every current and future caller fails closed by default. Review found two further routes to a false-empty inventory from half-checked refspecs; coverage requires exactly `refs/heads/*` to `refs/remotes/origin/*`. Proven against real `--depth 1` clones in `tests/repo-hygiene.test.ts`, `tests/reconciliation-preflight.test.ts` and `tests/reconciliation-evidence-pack.test.ts`. Source: session 2026-07-29 | 2026-07-29 | +| #115 | rec | Band adoption gate treats a discovered import as rendered | RESOLVED 2026-07-30 by redesign rather than another patch. `tests/search-results-band-adoption.test.ts` no longer asks whether a file mentions the band but whether anything the route actually mounts reaches it: it parses each module with `@babel/parser` into a small graph — exported name to local declaration, local to the identifiers its body references, and which locals render the band — then walks from the route's default export, carrying at each hop the set of exports the importer mounts. A static `import { X }` is followed only when `X` is reachable from a mounted declaration; `dynamic(() => import(...).then((m) => m.Named))` follows only that binding; a bare side-effect import is not followed at all; `export *` never supplies a default. **Why redesign:** six false greens were reported in one day, all one defect — presence is not reach — and two of the six had been introduced by an earlier patch to the same walker. Verified: all five production search routes still reach the band, gutting `services/page.tsx` and `tools/page.tsx` to `
` reports an orphan each, and fourteen temp-dir fixtures cover both directions with two mutation-confirmed. Accepted residual: reachability is per module and control flow inside a mounted declaration is not modelled. Source: PR #1400 | 2026-07-30 | +| #108 | task | Five verified-landed remote branches await deletion (blocked in-session) | DONE 2026-07-30. Deleted the three remaining empty remote refs after re-verify (tree(tip)==tree(merge-base), 0 files vs main, no open PR): `codex/document-reader-condensed-view` @ `b5cdbf301d517239ffe9ed941b9ebe809aea0bfd`, `cursor/page-anchored-search-composer-30ee` @ `7ff134ca7f614db527b8d142676640305533669d`, `cursor/pr-1379-babysit-ledger-9365` @ `be2de03f855cb7fdfccea4bb74d05eb4c9bf6c61`. The other two candidates were already gone. Completed `branch-cleanup` ledger rows were already present; deletion succeeded from this session (prior 403 cleared). | 2026-07-30 | +| #088 | task | Union-driver ledger duplication watch after repair | CLOSED 2026-07-30. Post-repair merges take main's repaired lines; residual exact-dupe babysit twins from stock `merge=union` are addressed by the custom `merge=ledger` driver (union + exact-row dedupe), `npm run ledger:dedupe`, and the Run PR anti-churn ledger policy. Success criterion (three consecutive post-repair guard passes) met on ordinary main syncs; ongoing exact-dupe class is now gated rather than watched. | 2026-07-30 | +| #087 | issue | `npm run check:knip` reported pre-existing dependency findings | NOT A DEFECT — false positive. The findings (unused `rimraf`/`tsx`/`@testing-library/dom`, unlisted `playwright-core`) only appeared because the worktree had no `node_modules` of its own and tooling resolved from the parent checkout. After `npm ci` in the same worktree, `npm run check:knip` exits 0 with no ignore-list change. Durable lesson: never act on a knip finding from a worktree that has not been installed. | 2026-07-28 | +| #089 | issue | Branch-review-ledger hygiene deferred from PR #1275 | Closed by the 2026-07-28 hygiene pass. The MD056 cell-count and duplicate-row findings dispositioned as "belongs in a dedicated main ledger hygiene pass" on PR #1275 are fixed: 140 mojibake lines restored byte-exact from git history, 6 residual separators repaired, 46 exact duplicates removed, 21 wrong-width rows normalised, and 4 heading+bullet records converted to table rows — 1067 records, all six cells. Root cause closed too: `npm run ledger:lookup` / `ledger:append` (`scripts/branch-review-ledger.mjs`) replace hand-written rows, and `check:branch-review-ledger` now fails on mojibake, cell width, heading records, impossible dates, table gaps, and (from 2026-07-29) unresolvable HEADs and near-duplicates. | 2026-07-28 | +| #097 | issue | Gitleaks reports a false red when the PR head moves mid-run | RESOLVED 2026-07-30: Secret Scan checks out the event head SHA and runs `scripts/run-gitleaks-pinned.mjs` against the immutable event base..head range (no mid-run PR commits API re-query). | 2026-07-30 | +| #111 | issue | `ui-overlap` phone-inset case still flakes under load | RESOLVED 2026-07-29 (PR #1391). The inset measurement now retries inside `expect(async () => …).toPass({ timeout: 15_000 })` (`ui-overlap.spec.ts:159`) — the same retry-the-measurement shape PR #1375 applied to the eight overlap cases. The 2px symmetry tolerance and the assertions themselves are byte-for-byte unchanged, so a genuinely asymmetric header still fails once the retry budget is spent; only a transient mid-remount sample settles. Full spec green across 4 consecutive runs (14 passed each). Do not relax the tolerance if this recurs — the assertion is the point; make the measurement robust instead. Source: PR #1375 flake triage; session 2026-07-29 | 2026-07-29 | +| #112 | issue | `issues:next-id` has no concurrency protection | RESOLVED 2026-07-30. `npm run check:outstanding-issues` now gates this file, in `verify:cheap` and in the `static-pr` CI job (the gate-manifest check refuses a local gate that CI does not run). It fails on a duplicate id, an id present in both tables, a marker at or below the highest id, a malformed row, and a missing heading or marker — so every shape the 2026-07-29 triple collision took is now a red gate rather than a silent row loss. Verified by replaying that collision against the real file: two rows claiming `#110` produced "#110 appears 2 times (lines 151, 164)", and a lost marker bump produced "issues:next-id=113 is not above the highest id #114". The checker honours `\|` escapes — its first run against the live file flagged row #042, which is correctly escaped, and a gate with false positives is a gate people switch off. NOT fixed: the underlying race. Ids are still allocated by read-modify-write with no lock, and this file still has no `merge=union` driver; what changed is that a collision can no longer land silently. Source: session 2026-07-29 PR sweep; PR #1391 conflict resolution; `.gitattributes` | 2026-07-30 | +| #113 | issue | `ModeNav` clips its labels at every phone width on `main` | RESOLVED 2026-07-30 (PR #1405, landed on `main` as `020c1260`). The two lower density bands used `grid-auto-columns: 1fr`, so the widest slot set every slot's width and the label's `truncate` hid the shortfall — nothing overflowed and nothing failed, the word was simply gone. `display: flex` at every band removes the mode rather than retuning it; thresholds went `16/26/34rem` -> `22/33/42rem` against remeasured intrinsic widths (471px for four labels, not the 394px originally budgeted). `tests/ui-mode-nav-density.spec.ts` asserts at both band boundaries and one pixel either side, registered in BOTH Playwright allowlists. **Carry-forward:** the ~8% threshold headroom is not padding — a measured-plus-one-pixel attempt (21/31rem) passed locally and failed CI by exactly 1px, because the variable face rasterises wider on `ubuntu-24.04`. A threshold calibrated to one machine's font metrics is calibrated to nothing. | 2026-07-30 | +| #114 | issue | The live Web-Vitals instrument cannot measure its own noise | RESOLVED 2026-07-30 (PR #1407, landed as `a73db460`). Each route/strategy cell is measured `SAMPLES` times (default 3), the MEDIAN is graded, and a cell whose samples straddle a threshold is reported as incomplete evidence rather than resolved either way — so #017's "evidence is too noisy" stop rule can finally fire. Mid-run Chrome drift disqualifies the verdict. **Second defect found in review and fixed in the same PR:** `samples` is a free-text `workflow_dispatch` input, so `samples=1` reached the grader as a valid integer and reproduced the exact single-run matrix this work removes — verified against the pre-fix logic, it reported 0 breaches, 0 incomplete and printed the bold "Every mobile route is within" verdict. Now refused in three places (`main()` before reading any report, `renderTable`'s disqualifier list, and the workflow before spending a live measurement pass). | 2026-07-30 | +| #120 | issue | `verify:phone-chrome` exits 0 while reporting failed browser tests | RESOLVED 2026-07-30 — **closed as not reproducible, not as fixed.** The premise is contradicted by a reproduction of its own failure condition. **Evidence, `main` at `fcd9041`:** the container ships Chromium build `1194` while the repo wants `1234`, so `npm run verify:phone-chrome -- --files src/components/clinical-dashboard/mobile-composer-reserve.ts` reproduced the identical shape — the same `13 failed`, every one at `browserType.launch: Executable doesn't exist at /opt/pw-browsers/chromium_headless_shell-1234/…` — and the captured status was **`OVERALL_EXIT=1`**, not 0. The gate stopped at the `focused-browser` stage and never reached `full-ui`, which is correct; the three stages ahead of it were genuinely green (`Test Files 9 passed (9)`, `Tests 105 passed (105)`). **The code path agrees:** status propagates at `scripts/verify-phone-chrome.mjs:72-73` and `scripts/run-playwright.mjs:271-273`, and `childProcessExitCode` returns `1` when `status` is not an integer, so a launch error cannot read as 0. Neither file, nor `scripts/child-process-result.mjs`, has changed since `7581cfc`, which predates this row — so "fixed in the meantime" is ruled out and the original exit-0 reading is what is in doubt. **Leading hypothesis, explicitly unverified:** the 0 was read through a pipe, since `npm run verify:phone-chrome` piped to `tail` reports `tail`'s status rather than the gate's. The original command line was not recorded, so this cannot be confirmed. **Not covered by this reproduction:** the zero-selected-test case (a `--grep` matching nothing) was never exercised — only the launch-failure case this row describes. **If it recurs:** capture the exact command line and `echo $?` with no pipe before re-filing. | 2026-07-30 | +| #122 | issue | `ci/circleci: verify` fails on every branch and its log needs operator access | RESOLVED 2026-07-30 — moot: the status cannot mislead because the provider is gone from the repository. The row's own outcome allowed either branch ("trustworthy signal again, **or** it stops reporting"); the second is what happened, so no operator log read is needed and the **Next** step is withdrawn. **Evidence:** `.circleci/config.yml` was deleted by `9779828` ("chore: remove CircleCI from repo", PR #1412), and `ls -d .circleci` on `main` at `c5c1a86` returns `No such file or directory`. Confirmed live rather than only from the tree: PR #1452's head reported **21 check runs and not one named `ci/circleci: verify`**, so the status no longer reports on new PRs at all. The unverified quota hypothesis is retired unproven — it was never confirmable without a CircleCI token, and there is now nothing left to confirm it against. One residual was found and fixed in the same change: `AGENTS.md` still cited `ci/circleci: verify` as a check that fails on unformatted files, which would have sent a future session chasing a check that can never report again. | 2026-07-30 | +| #012 | rec | Slim the lazy cross-mode differentials chunk | Precomputed a trimmed index (`src/data/cross-mode-differentials-index.json` via `scripts/build-cross-mode-differentials-index.mjs`) so the lazily-loaded cross-mode chunk imports a ~53 KB catalog instead of statically pulling the ~1.2 MB differentials snapshot (only that dynamic path reached it). A drift test plus `check:cross-mode-index` (in verify:cheap) lock the index to the live projection. | 2026-07-27 | +| #029 | issue | Residual answer-quality fallback stubs | Closed after fixing each causal cluster independently. Active-community ED, community-home-visit, clozapine blood-threshold/typo, discharge source-gap recovery, and Best Practice Prescription now use narrowly validated, source-bound answers or auditable recovery; cited provider refusal prose can no longer masquerade as grounded, and terminal gaps retain no claim citations. The final 44-case gate reported 30/30 substantive grounded supported answers, 14/14 unsupported correct, zero review fallbacks, zero citation/numeric failures, and zero route-ceiling failures. Measurement still reports review fallback separately and denies targeting credit for echoed boilerplate. | 2026-07-27 | +| #019 | issue | Preserve admission/discharge sources through comparison fallback | The actual fallback path now selects source-bound facts, preserves one admission and one discharge citation from distinct documents, and terminates at an evidence gap for qualified, negated, unrelated, title-only, single-sided, or same-document traps. Both exact live cases complete in about one second with zero provider calls; the final 44-case canary passed them with two citations each, while the 36-case retrieval canary held recall 1.0 and zero RR regressions. Retrieval scores, aliases, clamps, and comparator ordering were unchanged. | 2026-07-27 | +| #084 | task | Persist per-result irrelevant-at-10 grading evidence | `eval-retrieval` now persists each top result's `relevanceGrade` and `matchedDeclaredSignals`; focused fixtures cover ideal and zero-grade rows. The final golden artifact contains 338 graded top rows, including 33 grade-zero rows. This closes the reproducibility gap only: fixture labels, ranking, thresholds, and provider behavior were not changed, and human disposition remains #023. | 2026-07-27 | +| #080 | rec | Re-test the removed admission-to-discharge alias widening | Restored the two approved NMHS Admission-to-Discharge titles only on the eval-expectation surface. Canonical document-identity dedupe plus maximum bipartite matching prevents one dual-listed physical document from satisfying both comparison slots. Focused matching tests, both targeted admission cases, the final 36-case golden retrieval run, and the 44-case answer run passed; runtime retrieval/ranking behavior was not changed. | 2026-07-27 | +| #083 | issue | Documents-only universal search timed out on staging tenancy | A current staging nightly reproducer showed the documents-only search losing its synthetic fixture after the federated typeahead timeout was reduced to 750 ms. Current main retains 750 ms for multi-domain requests and uses the established 6,000 ms budget only when documents are the sole requested domain; fake-timer coverage proves both paths. RAG impact: no retrieval, ranking, ordering, alias, score, or result-selection change—only availability of the explicitly focused request. | 2026-07-27 | +| #082 | issue | Bot branch-sync heads leave required checks unapproved | Retired the automatic `GITHUB_TOKEN` PR branch-update workflow instead of weakening required-check approvals or introducing a privileged automation token. The existing helper remains dry-run by default, verifies its apply identity, and refuses missing or bot identities. The fast GitHub Actions policy check rejects both direct workflow `update-branch` calls and indirect apply-helper invocation. | 2026-07-27 | +| #058 | task | Verify production content before any seed write | Read-only production counts on project `sjrfecxgysukkwxsowpy` found 276 clinical registry, 328 medication, and 232 differential records. The required tables are non-empty, so no seed or production write was needed. | 2026-07-27 | +| #069 | task | Validate hosted table-facts RPC latency | Read-only profiling on the correct hosted project separated sample 1 (`first_unprimed`) from five `warm_repeat` samples; managed Supabase buffers were not flushed, so no true-cold claim is made. First-unprimed client/DB execution was 662.916/141.537 ms (clozapine), 277.661/96.229 ms (lithium), and 322.598/148.378 ms (metabolic). Warm client median/p90 was 187.029/198.907, 167.803/174.391, and 189.065/243.324 ms; warm DB execution median/p90 was 88.292/89.355, 64.342/65.566, and 107.448/148.417 ms. Earlier exact clinical probes were lower again. Plans are not the multi-second tail; no hosted migration, ranking, or provider configuration changed. | 2026-07-27 | +| #051 | task | Stabilise the live answer-quality canary before more RAG tuning | Closed after the scheduled structured report supplied a comparable second 36-retrieval/44-answer datapoint. Content gates stayed stable, the prior citation failure cleared, and #019 repeated with an identical diagnostic signature. Retrieval latency was investigated separately: #069 subsequently found acceptable table-facts database plans, so the broad scheduled tail was not treated as ranking debt. The report/trend tooling is now sufficient to compare future approved runs; no scheduled rerun or tuning was dispatched. | 2026-07-27 | +| #054 | task | Reconcile local and hosted secrets/config | Completed production names-only reconciliation on 2026-07-27. The correctly identified primary checkout received distinct gitignored local safety/query-hash/deep-probe values. A hardened checker now pins GitHub to `BigSimmo/Database` and Railway to the live production project/environment plus `Database`/`worker`, catches multiline schema and `.env.example` drift, and verifies GitHub secrets/variables and per-service Railway contracts without emitting provider values. All required names passed; the Ops Digest workflow is active with a successful scheduled run; both Railway services have later successful deployments; Supabase names-only proof found the expected cron/Vault configuration. Value equality remains deliberately unobservable, staging stays #056, webhook activation stays #025, and legal/ZDR work stays #053. | 2026-07-27 | +| #064 | task | Reconcile the preserved browser and contrast patch | Landed via PR #1250 squash `b91b4600171be08198e92bcf19b7d67e8207cb2f`. Opacity-free disabled Previous/Continue styling plus native-disabled/focus/axe Playwright coverage is on `main`. Historical `agent/formulation-disabled-contrast` remained unrecovered; conflicted PRs #1219/#1223/#1226/#1231/#1249 were closed without merge. | 2026-07-26 | +| #081 | issue | Open PR #1196 would undo the #030 alias tightening | Closed as no longer live: PR #1196 was closed 2026-07-25 as superseded by #913/current `main` (~680 commits behind, conflicting), and its successor #1198 does not touch `src/lib/eval-document-matching.ts`. The generalized alias-disjointness and single-document contracts landed in PR #1215 fail closed if any later branch re-adds the dual-listed admission aliases, so the regression route is guarded rather than watched. | 2026-07-25 | +| #077 | issue | Concurrent tasks can re-dirty the canonical primary checkout | Added cooperative primary-checkout write lease with dirty/operation fail-closed checks, stale-owner recovery, and lifecycle start/cleanup wiring; focused concurrency tests refuse a second primary writer while read-only/feature worktrees stay unblocked. | 2026-07-25 | +| #078 | task | Generate a deterministic reconciliation evidence pack | Added report-only atomic evidence pack with dispositions, markers, archive refs, bundle verify/hash, worktree counts, and local/base equality; fixture tests prove determinism/redaction and no false completion record on interrupt. | 2026-07-25 | +| #066 | task | Land and prove the streamlined six-item sidebar | Proven on `origin/main` via PR #1174 (`4dc76306 Land streamlined six-item sidebar`). Six-item rail shipped; open ledger row was stale post-merge. | 2026-07-25 | +| #067 | issue | Reconciliation preflight test times out under full-suite load | Fixed in PR #1191 (`e2488dbb`) by calling `collectReconciliationState()` in-process; PR #1203 further injects a fixture `repositoryRoot` so the contract no longer scales with the live worktree farm. No global timeout raise or heavy-test lock bypass. | 2026-07-25 | +| #007 | rec | `/tools` vs `/?mode=tools` parallel Tools entry points | Resolved as `/tools` canonical (PT-11 already documented on `/applications` redirect). Sidebar, appModeHomeHref, universal-search, prefetch, sitemap, and reachability now use `/tools`; `/?mode=tools` remains a dashboard-mode alias. Reachability allowlist entry removed. | 2026-07-24 | +| #030 | issue | Wide-tier alias lets one doc satisfy both comparison slots | Fixed on `cursor/search-correctness-030-075-6273`: removed dual-listed Admission-to-Discharge titles from AdmissionCommunityPts so one retrieved source cannot make allHit true for both comparison slots; fail-closed contracts in `tests/eval-document-matching.test.ts`. RAG impact: no retrieval behaviour change — eval matching only. Hardened after merge: coverage dedupes by document identity and assigns by maximum matching (#080). | 2026-07-24 | +| #075 | issue | Search-scope label enumeration can truncate after 1,000 rows | Fixed on `cursor/search-correctness-030-075-6273`: `loadScopeLabels` pages document_labels with deterministic order/batching past the Supabase 1k cap; multi-page >1000 contracts in `tests/search-scope.test.ts`. Isolated from mixed PR #1132. RAG impact: no retrieval behaviour change — label pagination only. | 2026-07-24 | +| #009 | rec | Confirm `/api/jobs` is intentionally server/ops-only | Kept as deliberate administrator/ops listing: no client `fetch("/api/jobs")` (UI uses `/api/ingestion/jobs`); documented in `docs/api-jobs-ops-surface.md` plus wiring/codebase-index/site-map notes. Not abandoned — do not remove without updating API contract tests. | 2026-07-24 | +| #010 | task | Un-built "Coming soon" controls across forms/favourites | Audited forms/favourites/presentation placeholders: all use honest `disabled` or `aria-disabled` + coming-soon copy (or presentational `ToggleSwitch` without `onToggle`). No fake-interactive controls; leave unwired until features land. Recorded in `docs/wiring-conventions.md`. | 2026-07-24 | +| #032 | rec | Governance ranking weighting: REFUTED, not debt | Reinforced as guardrail only in `docs/rag-behaviour/refuted-approaches.md` (Refutation 3), README, and safeguards — do **not** implement `review_due`/unknownCurrentness ranking penalties or boosts. No retrieval/ranking code changed. RC8 filter path remains the only revisit route behind canary gates. | 2026-07-24 | +| #041 | rec | Extend the existing Factsheets reading model | Brief recorded in `docs/factsheets-reading-model-brief.md`: extend Easy Read/Standard on existing Factsheets routes; reject a second patient-facing Factsheets mode unless concrete need + source-governance plan exist. | 2026-07-24 | +| #063 | rec | Define “Current Clinical Work” before implementation | Product/privacy/persistence brief recorded in `docs/current-clinical-work-brief.md`. Default v0 = no new storage (tab/URL resume); Class C free text needs privacy clearance. Stop without demand evidence. No UI/schema implemented. | 2026-07-24 | +| #076 | task | Reproduce malformed fallback PDF image/table crops | Reproduced truncated page-edge `table_crop`s on current-main with `worker/python/fixtures/malformed-table-crop-page-edge.pdf`. Root cause: `pymupdf_find_tables` stops at the last fully detected row; fix extends the candidate from contiguous cell drawings, recovers the on-page score-5 remnant, and emits `table_crop_edge_incomplete` / `crop_completeness=0.9` when content continues past the page. PR #1176. Broad PR #1129 retention/padding changes not merged. | 2026-07-24 | +| #070 | issue | Presentation mobile tabs misroute Overview/Map/Related | Fixed in PR #1135: Overview/Map/Related deep-link to diagnosis `?tab=` sections; Compare stays on the presentation page. Regression in `tests/mobile-interaction-regressions.test.ts`. (Provisional PR-branch IDs `#068`–`#072` were renumbered after `main` claimed `#068`/`#069`.) | 2026-07-24 | +| #071 | issue | Evidence/Clinical Notes Add fakes success without persistence | Fixed in PR #1135: sticky Add controls use the focusable coming-soon placeholder pattern instead of optimistic `setAdded(true)`. | 2026-07-24 | +| #072 | issue | Tools hub exposes false Sort/More affordances | Fixed in PR #1135: Sort is a status label, More filter targets coordination/saved without a fake menu chevron, and the favourites shortcut is labelled Saved/Favourites. | 2026-07-24 | +| #073 | issue | Presentation compare dock CTA is a self-link no-op | Fixed in PR #1135: dock shows non-link "Comparing (N)" status while already comparing. | 2026-07-24 | +| #074 | issue | Mode-action popup hard-reloads internal clinical routes | Fixed in PR #1135: `master-search-header` uses `router.push` for DSM/Specifiers/Formulation actions and mode href fallback. | 2026-07-24 | +| #068 | task | Regenerate full drift-manifest snapshot after schema hygiene | Full Docker `npm run drift:manifest` replay succeeded on a Docker-capable host; `supabase/drift-manifest.json` now carries live `def_hash` values for the plpgsql table-facts body (offline generator_note removed). | 2026-07-24 | +| #052 | issue | Reindex can overlap a fresh agent-enrichment pass | PR #1143 retained the friendly full/retry preflight and closed its check-then-enqueue race with an owner-scoped transactional RPC. Reindex enqueue and the agent claim path serialize on the document row; disposable PostgreSQL proved both interleavings, and exact-head migration replay/unit/build/Chromium/policy/security checks passed. | 2026-07-24 | +| #062 | issue | Upload crash can strand a queued document without a job | Aged owner-scoped `queued`-without-open-job rows are detected by `reindex:health`; the six-hour autopilot raises a durable alert, and guarded recovery uses PR #1143's transactional RPC so enqueue is owner-scoped, idempotent and atomic. `recover:ingestion --include-stranded-queued` remains dry-run/confirmation-first; scheduled production mutation is not enabled. | 2026-07-24 | +| #060 | issue | Safety Plan Generator contradicted the privacy contract | PR #1119 removed patient identifier entry, leaves the post-export name line blank, and aligned tool, privacy and PIA copy. DOM/privacy tests and Chromium copy/print/network coverage prove working content remains in React memory with no fetch/XHR; hosted Production UI, build, unit, policy, safety, static-analysis and secret checks passed. Support-contact details remain classified as sensitive local-only working content. | 2026-07-24 | +| #061 | issue | Missing answer relevance metadata was treated as source-backed | PR #1125 now requires explicit source-backed relevance for trusted/grounded presentation and prevents visual tables, clinical-note sections and quotes, and comparison metadata from bypassing the render model. Three actionable P2 review paths were fixed; focused policy/DOM tests, offline RAG, production-readiness, build, unit, static, security, and Production UI gates passed. No retrieval, ranking, generation, provider, or data behavior changed. | 2026-07-24 | +| #034 | issue | Answer cache can serve stale governance metadata | Current-source verification found direct route coverage already asserts RAG-cache invalidation on document PATCH, source review, label, bulk, and reindex mutation paths. The residual test recommendation is already met; changing the protected cache key is unnecessary. | 2026-07-24 | +| #014 | rec | Realize the `next/image` win on signed previews | Superseded: `SignedImage` uses `next/image` for layout and sizing but deliberately sets `unoptimized`, preventing bearer signed URLs from entering the unauthenticated optimizer cache where cached content could outlive the token. No optimization task remains unless private-image delivery changes. | 2026-07-24 | +| #026 | task | Wire the Supabase document-change trigger | PR #1100 merged after disposable PostgreSQL replay and hosted migration replay. Production migration history and read-only catalog proof confirm the enabled metadata trigger, security-definer function, pinned search path and denied anonymous/authenticated execution; `npm run check:drift` reports no unexpected live drift. Delivery remains intentionally inert until the operator inputs tracked in #025 are configured. | 2026-07-24 | +| #031 | issue | Populate canary Source Governance table | The answer-quality step now consumes the preceding `golden-retrieval.json` only for source-governance reporting. Offline replay of run `30018289898` populated 338 top results, including 202 review-required entries, while retaining zero retrieval cases and no additional threshold failures. Retrieval and ranking behavior are unchanged. | 2026-07-24 | +| #020 | task | Validate eval:quality cost readout post-fix | Confirmed on merged-main canary run `30018289898`: Answer Metrics reported 9 nonzero-cost cases and an estimated answer cost of `$0.234736`; the structured report retained the same value. The PR #1050 estimator fix is operationally proven. | 2026-07-23 | +| #003 | task | Staging tenancy release evidence outstanding | Ran GitHub Action and validated isolation | 2026-07-21 | +| #002 | task | Process-ownership fix not yet isolated on `main` | Fixed process isolation using child.pid termination | 2026-07-21 | +| #008 | rec | Dead href builders in `document-flow-routes.ts` | Not dead code (false positive): `documentReaderHref`/`documentEvidenceHref` are live via the mock wrappers in `src/components/document-search-mockups.tsx` + `src/components/master-document-flow-mockups.tsx` (rendered under `src/app/mockups/document-search/`) and covered by `tests/document-flow-routes.test.ts`; removing breaks the build. Only the production non-mock hrefs are unlinked from prod UI — a wiring gap, not dead code. | 2026-07-22 | +| #015 | task | Content-first fallback regression tests | Added `tests/registry-record-loader.dom.test.tsx` (8) + `tests/medication-record-page.dom.test.tsx` (6) covering content-first fallback paint, live swap-in, spinner/skeleton, error + not-found/unauthorized states, and the invariant that no authoritative verification badge shows before live governance reconciles (registry fixture-flag neutralization + medication governance-drop-on-error). | 2026-07-22 | +| #004 | rec | Rescope provider-gated RAG safety ideas | Closed obsolete — rescue source (754-line RAG-safety worktree) unrecoverable/pruned across all refs; answer-quality thresholds + deep-health already shipped on `main` (#585/#587); only cost-cap preflight was genuinely missing and, per session decision, dropped rather than re-filed. | 2026-07-22 | +| #006 | issue | Globe "Language & region" button had no handler | Resolved on main with the repository's disabled "Coming soon" placeholder convention and button-wiring coverage. Future language/region work remains a feature request, not an inert-control defect. | 2026-07-22 | +| #042 | issue | Invalid optional credentials fell into anonymous access | PRs #1078/#1079 introduced `absent \| valid \| invalid`, return 401 for presented invalid credentials, preserve authoritative header precedence and prefer the current-project session cookie. The archived anonymous-upload metadata patch was rejected as stale because uploads are already administrator-only before duplicate lookup. | 2026-07-22 | +| #043 | issue | Readiness could report healthy or throw on Supabase errors | PR #1080 now fails readiness closed for returned and thrown dependency failures, preserves recognized actionable messages, and prevents raw dependency-error disclosure. | 2026-07-22 | +| #044 | issue | Publication approval was not bound to immutable reviewed state | PR #1081 added a canonical reviewed-state digest, row locks, active-job rejection and a new forward migration with replay/schema/type/drift evidence. | 2026-07-22 | +| #045 | issue | Bulk reindex discarded partial-success results | PR #1084 reserves preflight conflicts for non-2xx responses; completed mixed batches return per-item success/failure/missing results, and the UI refreshes successful work. | 2026-07-22 | +| #046 | issue | DOCX extraction lacked explicit resource budgets | PR #1085 added pre-inflate declared-size checks and post-read fail-safes for artifact count, per-artifact bytes, aggregate media, Word XML and extracted UTF-8 text. | 2026-07-22 | +| #047 | issue | XLSX extraction could construct unbounded results | PR #1086 bounds worksheets, non-empty rows, rendered cells and UTF-8 output while preserving sparse-column rendering. | 2026-07-22 | +| #048 | issue | Account copy overstated sync/privacy and enabled unavailable SSO | PR #1087 now maps copy to actual favourites/preferences persistence, identifies browser-session recents, removes the contradictory "never shared" claim and clearly disables unavailable providers using the accessible placeholder contract. | 2026-07-22 | +| #049 | issue | Process diagnostic exposed a Cursor worker API key | The exact worker was stopped, the key was revoked server-side, both local encrypted worker-secret records were removed, and authorized repository/backup scans found no plaintext copy. Follow-up guardrails now prevent repository process inventory from serializing command lines and redact heavyweight-lock command text before persistence or errors. | 2026-07-23 | +| #050 | issue | Next.js 16.2.10 remained in a high-severity security range | Upgraded `next` and `@next/env` to 16.2.11, regenerated the npm lockfile, confirmed the production dependency audit is clean, and passed focused framework checks, `verify:cheap`, and the full Chromium UI gate. | 2026-07-23 | +| #116 | issue | Missing CI signal on conflicted PRs | RESOLVED 2026-07-30: `.github/workflows/pr-mergeability.yml` checks trusted `pull_request_target` events and refreshes unchanged heads after protected-base pushes. A conflicted PR gets a red `PR mergeability` check naming the conflict instead of an empty CI list. The base-push job alone has scoped `checks: write`; neither path checks out PR code or updates branches. Classifier: `scripts/pr-mergeability.mjs`; contract: `npm run check:pr-mergeability`. Behind-but-clean remains `sync:pr-branches`. | 2026-07-30 | +| #140 | issue | Union-driver table duplication — duplicate of #133 | CLOSED 2026-07-30 as a duplicate, superseded by `#133` and resolved by PR #1444. This row was opened mid-session for the union-driver damage before its author noticed `#133` had already recorded the same finding — and recorded it _earlier_, with the same conclusion ("**Do not** apply a `merge=union` driver — tested 2026-07-30 and it is worse"). Keeping both would have left two open rows describing one condition, and that condition no longer exists: PR #1444 removed `merge=union` from `.gitattributes`, inverted `check:outstanding-issues` to require an unspecified `merge` attribute, and added regression tests covering `union`, `-merge` and an unparsed reading. The surviving evidence — four merges on PR #1430 each reporting success while duplicating the entire open-items table — lives in `#133`, whose still-open half is the real conflict-frequency cause: fixed-width column padding makes any one-row edit re-pad every row, so git sees the whole table as one hunk. Read `#133`, not this. | 2026-07-30 | From b6249c61303753d13d368d556575c6356024eabb Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Fri, 31 Jul 2026 01:47:07 +0800 Subject: [PATCH 3/6] docs: record PR 1481 review --- docs/branch-review-ledger.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index c2816a0664..8dcfd308ac 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -247,3 +247,4 @@ Records before 2026-07-28 were written by hand and had drifted: 146 lines carrie | 2026-07-30 | PR #1477 | 26d713922006c1af8187994edfa76669dc14cd46 | PR #1477 fork-safe Codex autofix routing | Fixed fork routing to the PR head repository, added fail-closed metadata handling, reconciled current main, and found no remaining actionable defects. | check:codex-autofix-workflow; check:github-actions; check:pr-policy; check:outstanding-issues; check:branch-review-ledger; docs:check-inventory; docs:check-links; docs:check-scripts; typecheck; focused Vitest 53 passed; Prettier | | 2026-07-30 | pr/1465 | 4e34d97bb9eb5122b9d8f8e54c42793c727f5085 | issues: record fresh #133 evidence | approved; duplicate-ID race and Prettier prerequisite accurately recorded | issue/ledger; docs inventory/links; Prettier; diff-check | | 2026-07-30 | PR #1477 | 20f795da2d9d0adafa6cb3117429ab3665129c0d | PR #1477 fork-safe Codex autofix routing | Refreshed onto current main after #1465; issue and ledger reconciliation remained clean and no new actionable defects were introduced. | check:outstanding-issues; check:branch-review-ledger; check:codex-autofix-workflow; focused Vitest 53 passed | +| 2026-07-30 | PR #1481 | 78193371d0cc67c3231c72f1d9ef2a1c5dbdc499 | PR #1481 live-region finding correction | Confirmed the proposed production live-region change was incorrect, preserved the false finding as a withdrawn archive record, and found no actionable defects. | check:outstanding-issues; check:branch-review-ledger; docs:check-links; diff check | From 04c477d3f6720a18a3846ee01396cc978caa7b88 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Fri, 31 Jul 2026 01:47:52 +0800 Subject: [PATCH 4/6] docs: preserve issue 149 allocation --- docs/outstanding-issues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/outstanding-issues.md b/docs/outstanding-issues.md index 7e3403fe6f..a8aa0b1791 100644 --- a/docs/outstanding-issues.md +++ b/docs/outstanding-issues.md @@ -83,7 +83,7 @@ removed after current-main verification; it is not missing recommended work. | 34 | `#105` | Optional | High — browser/UI verification | When a driven-browser session is available | 30–60 minutes | Decide how the ten `LoadingPanel` fallbacks can be observed at all, then verify or re-scope. A cold-load grep cannot see them — every lazy surface mounts behind interaction state, so nothing renders them on first paint — and a claimed cold-load verification was withdrawn 2026-07-30 after it turned out to be matching `ModeHomePageSkeleton`. Either drive the interaction in a browser and assert the surface's specific label, or record that they are unobservable on cold load. The preconnect half is already verified; do not redo it. **Stop:** do not close this on a cold-load grep. | | 35 | `#126` | Optional | Standard — repository hygiene | Once per UTC calendar quarter, or when the live ledger grows large | 5–15 minutes | Run `npm run ledger:rotate -- --dry-run`, then `npm run ledger:rotate` if the preview looks right; commit the live+archive diff. Stop if dry-run shows unexpected mass moves or archive path collisions. | - + ## Open items From f408bba957acf8903b29b6575971c9d77e0441b5 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Fri, 31 Jul 2026 01:48:23 +0800 Subject: [PATCH 5/6] docs: format withdrawn issue record --- docs/outstanding-issues.md | 164 ++++++++++++++++++------------------- 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/docs/outstanding-issues.md b/docs/outstanding-issues.md index a8aa0b1791..95d2c89e03 100644 --- a/docs/outstanding-issues.md +++ b/docs/outstanding-issues.md @@ -171,85 +171,85 @@ removed after current-main verification; it is not missing recommended work. Move resolved rows here with the resolution date and a one-line outcome. Keep them — do not delete. -| ID | Type | Summary | Outcome | Resolved | -| ---- | ----- | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | -| #149 | issue | Failed clinical searches are announced to nobody — the results band mutes its live region | WITHDRAWN 2026-07-30, same day it was filed: the claim was false and the proposed fix would have caused a regression. Codex caught it on PR #1481. **What was missed:** `search-results-header-band.tsx` deliberately silences the count span while its separately mounted `role="alert"` fault panel announces the failure title, body, and Retry action. `tests/search-results-header-band.dom.test.tsx` already pins exactly one status region and one alert. Escalating the count span too would create a duplicate announcement and break that contract. **Not a defect in the mockup:** `search-refine-adaptive-mockups.tsx` has no separate fault panel, so its count span correctly owns the announcement there. **Stop:** do not re-file this; verify which node owns an announcement before treating a muted sibling live region as a defect. | 2026-07-30 | -| #131 | issue | `@codex fix` produced commits that never reached the repository | RESOLVED 2026-07-30. Automatic repair requests now bind Codex to the exact repository, pull-request head branch, and starting SHA; explicitly forbid detached/synthetic `work` branches and stacked pull requests; and require the authenticated GitHub connector to publish and verify the fix before success is reported. Thread closure is fail-closed: a fixed reply must name its 40-character pushed commit SHA, and the workflow resolves the thread only when that SHA is the pull-request head. No-code dispositions carry a distinct marker, while missing, conflicting, or unlanded result claims leave the thread open with a visible workflow failure. The guard and executable workflow tests pin the destination prompt, both valid outcomes, malformed results, and mismatched-head refusal. Source: issue #131; session 2026-07-30 | 2026-07-30 | -| #136 | rec | Reuse Next build cache across isolated Playwright production builds | CLOSED 2026-07-30 after an end-to-end implementation benchmark rejected the proposed cache as a net CI loss. Keeping the reusable cache outside each disposable run root preserved the runner cleanup contract, but a warm build saved only 34 seconds (97s cold to 63s warm) while producing an 804 MB cache. Persisting that entry per commit would consume the repository cache budget and evict the substantially more valuable Playwright browser cache. No cache wiring ships; reconsider only if Next materially reduces the cache size or a later measurement changes the storage/time trade-off. Source: `scripts/run-playwright.mjs`; `docs/testing.md`; session 2026-07-30 | 2026-07-30 | -| #085 | rec | Upload-limit client/server sync is unguarded | Resolved 2026-07-30. A provider-free parity checker now validates both configured limits, their shared 150 MB default, invalid and over-ceiling values, and mismatches. It runs in `verify:cheap`, the static PR job, and before every production build so a client-side value cannot silently diverge from the server runtime limit. | 2026-07-30 | -| #119 | issue | `ci/circleci: verify` is failing repo-wide | Retired 2026-07-30. CircleCI was removed from the repository in commit `977982857`, and the user confirmed the integration is deleted. The prior request to inspect build 672 is obsolete; GitHub Actions remains the repository CI path. | 2026-07-30 | -| #125 | issue | `ui-therapy-nav-scroll.spec.ts` cites a spec file that does not exist | Resolved 2026-07-30. The Therapy navigation comment now points to the live `ui-mode-nav-density.spec.ts` coverage instead of the deleted `mode-nav-bar-anchoring.spec.ts` file. | 2026-07-30 | -| #095 | issue | `PR required` reports failure for concurrency-cancelled jobs | RESOLVED 2026-07-30. The `pr-required` aggregate now distinguishes a cancelled job from a failed one. `require_success` / `require_skipped_or_success` are thin wrappers over one `record()` collector that reads **each job's own `result`** and appends to a `failures` or `cancellations` array; both arrays are filled before anything is reported. **Genuine failures win:** every failure is emitted as its own `::error::` and a concurrent cancellation is demoted to a `::warning::`, so a run that is cancelled AND broken cannot read as an excuse (refinement reported by Codex on PR #1409). Cancelled with nothing failing stays **RED**, and the message states the two possibilities rather than asserting supersession: it points the reader at a newer `PR required` run on the PR's current head SHA, and says that if there is none the run was cancelled by hand and must be re-run rather than merged past. It deliberately does NOT read the workflow-level cancelled status function — an earlier revision passed that through an `env:` value, which is invalid because GitHub allows those functions only in `if:` conditions, so the whole file failed to parse, ran as `.github/workflows/ci.yml` instead of `CI`, and created zero jobs; valid YAML but invalid Actions schema, so prettier and every local gate passed it. **The tempting fix was rejected as unsafe:** treating `cancelled` as neutral, or skipping the aggregate via a not-cancelled condition, would make the red disappear, but GitHub counts a SKIPPED required check as PASSING, so a hand-cancelled run on the current head would become mergeable with nothing verified — `if: always()` is therefore deliberate. Guarded by ten cases in `tests/ci-cache-safety.test.ts` that EXECUTE the extracted aggregate script under synthetic job results rather than grepping the YAML. **Corrected in review (Codex, PR #1428):** the first version of this record described a shared `cancelled_error` helper that never existed and claimed the error names the newest run; both were carried over from an obsolete sentence in the open row and neither matched the shipped implementation. Source: PR #1316 runs 30340972329 / 30341225585; PR #1409 | 2026-07-30 | -| #096 | task | PR #1316 review follow-ups — adoption-gate coverage closed | RESOLVED 2026-07-30. Every sub-item is dispositioned. The band adoption gate's root-path gap closed on PR #1394 — root and href-less modes now resolve to `src/app/(search-app)/page.tsx` — and closing it surfaced two further defects in the same gate the original finding did not name: the hand-rolled walk was capped at two import hops where the root route's real chain is four, and it followed neither `layout.tsx` (where that route's band actually comes from) nor `dynamic(() => import(...))` (how the dashboard code-splits its mode workspaces). All three were fixed together with a bounded BFS, each verified load-bearing by reverting it and watching the gate fail. Four findings — favourites hub counts, the document-search status derivation, the 401 session-expiry path, the record-path duplicate notice — were already fixed independently. The Therapy Compass retry-waiter finding was corrected to NOT a live defect: `retryWaitersRef` is genuinely unscoped but no caller observes it. The seven Codex follow-up SHAs remain unreachable and were never pushed; the PR #1316 review threads are the durable source. Archived by the 2026-07-30 triage pass. Source: PR #1316 review sweep | 2026-07-30 | -| #104 | rec | Worker's triple image read is deliberate, not debt | NOT DEBT — archived so a fourth audit does not re-file it. The 2026-07-28 latency audit listed L4-2 (`worker/main.ts` reads each extracted image up to 3x per document — hash, caption on cache miss, upload) as "CONFIRMED with no fix evidence", carried forward from the 2026-07-01 audit's L11. **That was wrong.** The 2026-07-01 disposition table already recorded it as a deliberate peak-memory trade-off, and the rationale is documented in place at `worker/main.ts:866-869`: holding every extracted image Buffer for a document with hundreds of multi-MB page images would multiply the worker's peak memory, and disk I/O is the cheaper resource for a background pipeline. The three reads (`:872`, `:1034`, `:1129`) are real but accepted. This row carried no next action, so it is archived rather than left open — revisit only if ingestion throughput becomes a measured complaint AND a bounded-buffer design is proposed. **Stop:** do not re-file this as debt. Source: `docs/audit/repo-audit-2026-07-01.md` L11 + disposition table | 2026-07-30 | -| #109 | issue | Remote sessions clone shallow, invalidating branch analysis | RESOLVED 2026-07-29, hardened in review. `scripts/sweep-branch-ledger.mjs` refuses outright on a shallow clone via the exported `shallowCloneRefusal`, printing no inventory and exiting 1 in both text and `--json` mode, before the fetch and before any branch maths; `docs/branch-cleanup-guide.md` §Safety Rules gains the `is-shallow-repository` precondition ahead of its numbered steps. An indeterminate result (empty output from a swallowed `git` failure) is refused as its own failure, and the same guard had to move OUT of the reconciliation preflight CLI and INTO the exported `collectReconciliationState`, because `buildReconciliationEvidencePack` calls the collector directly and stamped `status: complete` — in a `--depth 1` clone the guarded CLI exited 1 while the evidence-pack command exited 0 and persisted shallow ahead/behind as completed evidence. The collector now throws `UnverifiedHistoryError` so every current and future caller fails closed by default. Review found two further routes to a false-empty inventory from half-checked refspecs; coverage requires exactly `refs/heads/*` to `refs/remotes/origin/*`. Proven against real `--depth 1` clones in `tests/repo-hygiene.test.ts`, `tests/reconciliation-preflight.test.ts` and `tests/reconciliation-evidence-pack.test.ts`. Source: session 2026-07-29 | 2026-07-29 | -| #115 | rec | Band adoption gate treats a discovered import as rendered | RESOLVED 2026-07-30 by redesign rather than another patch. `tests/search-results-band-adoption.test.ts` no longer asks whether a file mentions the band but whether anything the route actually mounts reaches it: it parses each module with `@babel/parser` into a small graph — exported name to local declaration, local to the identifiers its body references, and which locals render the band — then walks from the route's default export, carrying at each hop the set of exports the importer mounts. A static `import { X }` is followed only when `X` is reachable from a mounted declaration; `dynamic(() => import(...).then((m) => m.Named))` follows only that binding; a bare side-effect import is not followed at all; `export *` never supplies a default. **Why redesign:** six false greens were reported in one day, all one defect — presence is not reach — and two of the six had been introduced by an earlier patch to the same walker. Verified: all five production search routes still reach the band, gutting `services/page.tsx` and `tools/page.tsx` to `
` reports an orphan each, and fourteen temp-dir fixtures cover both directions with two mutation-confirmed. Accepted residual: reachability is per module and control flow inside a mounted declaration is not modelled. Source: PR #1400 | 2026-07-30 | -| #108 | task | Five verified-landed remote branches await deletion (blocked in-session) | DONE 2026-07-30. Deleted the three remaining empty remote refs after re-verify (tree(tip)==tree(merge-base), 0 files vs main, no open PR): `codex/document-reader-condensed-view` @ `b5cdbf301d517239ffe9ed941b9ebe809aea0bfd`, `cursor/page-anchored-search-composer-30ee` @ `7ff134ca7f614db527b8d142676640305533669d`, `cursor/pr-1379-babysit-ledger-9365` @ `be2de03f855cb7fdfccea4bb74d05eb4c9bf6c61`. The other two candidates were already gone. Completed `branch-cleanup` ledger rows were already present; deletion succeeded from this session (prior 403 cleared). | 2026-07-30 | -| #088 | task | Union-driver ledger duplication watch after repair | CLOSED 2026-07-30. Post-repair merges take main's repaired lines; residual exact-dupe babysit twins from stock `merge=union` are addressed by the custom `merge=ledger` driver (union + exact-row dedupe), `npm run ledger:dedupe`, and the Run PR anti-churn ledger policy. Success criterion (three consecutive post-repair guard passes) met on ordinary main syncs; ongoing exact-dupe class is now gated rather than watched. | 2026-07-30 | -| #087 | issue | `npm run check:knip` reported pre-existing dependency findings | NOT A DEFECT — false positive. The findings (unused `rimraf`/`tsx`/`@testing-library/dom`, unlisted `playwright-core`) only appeared because the worktree had no `node_modules` of its own and tooling resolved from the parent checkout. After `npm ci` in the same worktree, `npm run check:knip` exits 0 with no ignore-list change. Durable lesson: never act on a knip finding from a worktree that has not been installed. | 2026-07-28 | -| #089 | issue | Branch-review-ledger hygiene deferred from PR #1275 | Closed by the 2026-07-28 hygiene pass. The MD056 cell-count and duplicate-row findings dispositioned as "belongs in a dedicated main ledger hygiene pass" on PR #1275 are fixed: 140 mojibake lines restored byte-exact from git history, 6 residual separators repaired, 46 exact duplicates removed, 21 wrong-width rows normalised, and 4 heading+bullet records converted to table rows — 1067 records, all six cells. Root cause closed too: `npm run ledger:lookup` / `ledger:append` (`scripts/branch-review-ledger.mjs`) replace hand-written rows, and `check:branch-review-ledger` now fails on mojibake, cell width, heading records, impossible dates, table gaps, and (from 2026-07-29) unresolvable HEADs and near-duplicates. | 2026-07-28 | -| #097 | issue | Gitleaks reports a false red when the PR head moves mid-run | RESOLVED 2026-07-30: Secret Scan checks out the event head SHA and runs `scripts/run-gitleaks-pinned.mjs` against the immutable event base..head range (no mid-run PR commits API re-query). | 2026-07-30 | -| #111 | issue | `ui-overlap` phone-inset case still flakes under load | RESOLVED 2026-07-29 (PR #1391). The inset measurement now retries inside `expect(async () => …).toPass({ timeout: 15_000 })` (`ui-overlap.spec.ts:159`) — the same retry-the-measurement shape PR #1375 applied to the eight overlap cases. The 2px symmetry tolerance and the assertions themselves are byte-for-byte unchanged, so a genuinely asymmetric header still fails once the retry budget is spent; only a transient mid-remount sample settles. Full spec green across 4 consecutive runs (14 passed each). Do not relax the tolerance if this recurs — the assertion is the point; make the measurement robust instead. Source: PR #1375 flake triage; session 2026-07-29 | 2026-07-29 | -| #112 | issue | `issues:next-id` has no concurrency protection | RESOLVED 2026-07-30. `npm run check:outstanding-issues` now gates this file, in `verify:cheap` and in the `static-pr` CI job (the gate-manifest check refuses a local gate that CI does not run). It fails on a duplicate id, an id present in both tables, a marker at or below the highest id, a malformed row, and a missing heading or marker — so every shape the 2026-07-29 triple collision took is now a red gate rather than a silent row loss. Verified by replaying that collision against the real file: two rows claiming `#110` produced "#110 appears 2 times (lines 151, 164)", and a lost marker bump produced "issues:next-id=113 is not above the highest id #114". The checker honours `\|` escapes — its first run against the live file flagged row #042, which is correctly escaped, and a gate with false positives is a gate people switch off. NOT fixed: the underlying race. Ids are still allocated by read-modify-write with no lock, and this file still has no `merge=union` driver; what changed is that a collision can no longer land silently. Source: session 2026-07-29 PR sweep; PR #1391 conflict resolution; `.gitattributes` | 2026-07-30 | -| #113 | issue | `ModeNav` clips its labels at every phone width on `main` | RESOLVED 2026-07-30 (PR #1405, landed on `main` as `020c1260`). The two lower density bands used `grid-auto-columns: 1fr`, so the widest slot set every slot's width and the label's `truncate` hid the shortfall — nothing overflowed and nothing failed, the word was simply gone. `display: flex` at every band removes the mode rather than retuning it; thresholds went `16/26/34rem` -> `22/33/42rem` against remeasured intrinsic widths (471px for four labels, not the 394px originally budgeted). `tests/ui-mode-nav-density.spec.ts` asserts at both band boundaries and one pixel either side, registered in BOTH Playwright allowlists. **Carry-forward:** the ~8% threshold headroom is not padding — a measured-plus-one-pixel attempt (21/31rem) passed locally and failed CI by exactly 1px, because the variable face rasterises wider on `ubuntu-24.04`. A threshold calibrated to one machine's font metrics is calibrated to nothing. | 2026-07-30 | -| #114 | issue | The live Web-Vitals instrument cannot measure its own noise | RESOLVED 2026-07-30 (PR #1407, landed as `a73db460`). Each route/strategy cell is measured `SAMPLES` times (default 3), the MEDIAN is graded, and a cell whose samples straddle a threshold is reported as incomplete evidence rather than resolved either way — so #017's "evidence is too noisy" stop rule can finally fire. Mid-run Chrome drift disqualifies the verdict. **Second defect found in review and fixed in the same PR:** `samples` is a free-text `workflow_dispatch` input, so `samples=1` reached the grader as a valid integer and reproduced the exact single-run matrix this work removes — verified against the pre-fix logic, it reported 0 breaches, 0 incomplete and printed the bold "Every mobile route is within" verdict. Now refused in three places (`main()` before reading any report, `renderTable`'s disqualifier list, and the workflow before spending a live measurement pass). | 2026-07-30 | -| #120 | issue | `verify:phone-chrome` exits 0 while reporting failed browser tests | RESOLVED 2026-07-30 — **closed as not reproducible, not as fixed.** The premise is contradicted by a reproduction of its own failure condition. **Evidence, `main` at `fcd9041`:** the container ships Chromium build `1194` while the repo wants `1234`, so `npm run verify:phone-chrome -- --files src/components/clinical-dashboard/mobile-composer-reserve.ts` reproduced the identical shape — the same `13 failed`, every one at `browserType.launch: Executable doesn't exist at /opt/pw-browsers/chromium_headless_shell-1234/…` — and the captured status was **`OVERALL_EXIT=1`**, not 0. The gate stopped at the `focused-browser` stage and never reached `full-ui`, which is correct; the three stages ahead of it were genuinely green (`Test Files 9 passed (9)`, `Tests 105 passed (105)`). **The code path agrees:** status propagates at `scripts/verify-phone-chrome.mjs:72-73` and `scripts/run-playwright.mjs:271-273`, and `childProcessExitCode` returns `1` when `status` is not an integer, so a launch error cannot read as 0. Neither file, nor `scripts/child-process-result.mjs`, has changed since `7581cfc`, which predates this row — so "fixed in the meantime" is ruled out and the original exit-0 reading is what is in doubt. **Leading hypothesis, explicitly unverified:** the 0 was read through a pipe, since `npm run verify:phone-chrome` piped to `tail` reports `tail`'s status rather than the gate's. The original command line was not recorded, so this cannot be confirmed. **Not covered by this reproduction:** the zero-selected-test case (a `--grep` matching nothing) was never exercised — only the launch-failure case this row describes. **If it recurs:** capture the exact command line and `echo $?` with no pipe before re-filing. | 2026-07-30 | -| #122 | issue | `ci/circleci: verify` fails on every branch and its log needs operator access | RESOLVED 2026-07-30 — moot: the status cannot mislead because the provider is gone from the repository. The row's own outcome allowed either branch ("trustworthy signal again, **or** it stops reporting"); the second is what happened, so no operator log read is needed and the **Next** step is withdrawn. **Evidence:** `.circleci/config.yml` was deleted by `9779828` ("chore: remove CircleCI from repo", PR #1412), and `ls -d .circleci` on `main` at `c5c1a86` returns `No such file or directory`. Confirmed live rather than only from the tree: PR #1452's head reported **21 check runs and not one named `ci/circleci: verify`**, so the status no longer reports on new PRs at all. The unverified quota hypothesis is retired unproven — it was never confirmable without a CircleCI token, and there is now nothing left to confirm it against. One residual was found and fixed in the same change: `AGENTS.md` still cited `ci/circleci: verify` as a check that fails on unformatted files, which would have sent a future session chasing a check that can never report again. | 2026-07-30 | -| #012 | rec | Slim the lazy cross-mode differentials chunk | Precomputed a trimmed index (`src/data/cross-mode-differentials-index.json` via `scripts/build-cross-mode-differentials-index.mjs`) so the lazily-loaded cross-mode chunk imports a ~53 KB catalog instead of statically pulling the ~1.2 MB differentials snapshot (only that dynamic path reached it). A drift test plus `check:cross-mode-index` (in verify:cheap) lock the index to the live projection. | 2026-07-27 | -| #029 | issue | Residual answer-quality fallback stubs | Closed after fixing each causal cluster independently. Active-community ED, community-home-visit, clozapine blood-threshold/typo, discharge source-gap recovery, and Best Practice Prescription now use narrowly validated, source-bound answers or auditable recovery; cited provider refusal prose can no longer masquerade as grounded, and terminal gaps retain no claim citations. The final 44-case gate reported 30/30 substantive grounded supported answers, 14/14 unsupported correct, zero review fallbacks, zero citation/numeric failures, and zero route-ceiling failures. Measurement still reports review fallback separately and denies targeting credit for echoed boilerplate. | 2026-07-27 | -| #019 | issue | Preserve admission/discharge sources through comparison fallback | The actual fallback path now selects source-bound facts, preserves one admission and one discharge citation from distinct documents, and terminates at an evidence gap for qualified, negated, unrelated, title-only, single-sided, or same-document traps. Both exact live cases complete in about one second with zero provider calls; the final 44-case canary passed them with two citations each, while the 36-case retrieval canary held recall 1.0 and zero RR regressions. Retrieval scores, aliases, clamps, and comparator ordering were unchanged. | 2026-07-27 | -| #084 | task | Persist per-result irrelevant-at-10 grading evidence | `eval-retrieval` now persists each top result's `relevanceGrade` and `matchedDeclaredSignals`; focused fixtures cover ideal and zero-grade rows. The final golden artifact contains 338 graded top rows, including 33 grade-zero rows. This closes the reproducibility gap only: fixture labels, ranking, thresholds, and provider behavior were not changed, and human disposition remains #023. | 2026-07-27 | -| #080 | rec | Re-test the removed admission-to-discharge alias widening | Restored the two approved NMHS Admission-to-Discharge titles only on the eval-expectation surface. Canonical document-identity dedupe plus maximum bipartite matching prevents one dual-listed physical document from satisfying both comparison slots. Focused matching tests, both targeted admission cases, the final 36-case golden retrieval run, and the 44-case answer run passed; runtime retrieval/ranking behavior was not changed. | 2026-07-27 | -| #083 | issue | Documents-only universal search timed out on staging tenancy | A current staging nightly reproducer showed the documents-only search losing its synthetic fixture after the federated typeahead timeout was reduced to 750 ms. Current main retains 750 ms for multi-domain requests and uses the established 6,000 ms budget only when documents are the sole requested domain; fake-timer coverage proves both paths. RAG impact: no retrieval, ranking, ordering, alias, score, or result-selection change—only availability of the explicitly focused request. | 2026-07-27 | -| #082 | issue | Bot branch-sync heads leave required checks unapproved | Retired the automatic `GITHUB_TOKEN` PR branch-update workflow instead of weakening required-check approvals or introducing a privileged automation token. The existing helper remains dry-run by default, verifies its apply identity, and refuses missing or bot identities. The fast GitHub Actions policy check rejects both direct workflow `update-branch` calls and indirect apply-helper invocation. | 2026-07-27 | -| #058 | task | Verify production content before any seed write | Read-only production counts on project `sjrfecxgysukkwxsowpy` found 276 clinical registry, 328 medication, and 232 differential records. The required tables are non-empty, so no seed or production write was needed. | 2026-07-27 | -| #069 | task | Validate hosted table-facts RPC latency | Read-only profiling on the correct hosted project separated sample 1 (`first_unprimed`) from five `warm_repeat` samples; managed Supabase buffers were not flushed, so no true-cold claim is made. First-unprimed client/DB execution was 662.916/141.537 ms (clozapine), 277.661/96.229 ms (lithium), and 322.598/148.378 ms (metabolic). Warm client median/p90 was 187.029/198.907, 167.803/174.391, and 189.065/243.324 ms; warm DB execution median/p90 was 88.292/89.355, 64.342/65.566, and 107.448/148.417 ms. Earlier exact clinical probes were lower again. Plans are not the multi-second tail; no hosted migration, ranking, or provider configuration changed. | 2026-07-27 | -| #051 | task | Stabilise the live answer-quality canary before more RAG tuning | Closed after the scheduled structured report supplied a comparable second 36-retrieval/44-answer datapoint. Content gates stayed stable, the prior citation failure cleared, and #019 repeated with an identical diagnostic signature. Retrieval latency was investigated separately: #069 subsequently found acceptable table-facts database plans, so the broad scheduled tail was not treated as ranking debt. The report/trend tooling is now sufficient to compare future approved runs; no scheduled rerun or tuning was dispatched. | 2026-07-27 | -| #054 | task | Reconcile local and hosted secrets/config | Completed production names-only reconciliation on 2026-07-27. The correctly identified primary checkout received distinct gitignored local safety/query-hash/deep-probe values. A hardened checker now pins GitHub to `BigSimmo/Database` and Railway to the live production project/environment plus `Database`/`worker`, catches multiline schema and `.env.example` drift, and verifies GitHub secrets/variables and per-service Railway contracts without emitting provider values. All required names passed; the Ops Digest workflow is active with a successful scheduled run; both Railway services have later successful deployments; Supabase names-only proof found the expected cron/Vault configuration. Value equality remains deliberately unobservable, staging stays #056, webhook activation stays #025, and legal/ZDR work stays #053. | 2026-07-27 | -| #064 | task | Reconcile the preserved browser and contrast patch | Landed via PR #1250 squash `b91b4600171be08198e92bcf19b7d67e8207cb2f`. Opacity-free disabled Previous/Continue styling plus native-disabled/focus/axe Playwright coverage is on `main`. Historical `agent/formulation-disabled-contrast` remained unrecovered; conflicted PRs #1219/#1223/#1226/#1231/#1249 were closed without merge. | 2026-07-26 | -| #081 | issue | Open PR #1196 would undo the #030 alias tightening | Closed as no longer live: PR #1196 was closed 2026-07-25 as superseded by #913/current `main` (~680 commits behind, conflicting), and its successor #1198 does not touch `src/lib/eval-document-matching.ts`. The generalized alias-disjointness and single-document contracts landed in PR #1215 fail closed if any later branch re-adds the dual-listed admission aliases, so the regression route is guarded rather than watched. | 2026-07-25 | -| #077 | issue | Concurrent tasks can re-dirty the canonical primary checkout | Added cooperative primary-checkout write lease with dirty/operation fail-closed checks, stale-owner recovery, and lifecycle start/cleanup wiring; focused concurrency tests refuse a second primary writer while read-only/feature worktrees stay unblocked. | 2026-07-25 | -| #078 | task | Generate a deterministic reconciliation evidence pack | Added report-only atomic evidence pack with dispositions, markers, archive refs, bundle verify/hash, worktree counts, and local/base equality; fixture tests prove determinism/redaction and no false completion record on interrupt. | 2026-07-25 | -| #066 | task | Land and prove the streamlined six-item sidebar | Proven on `origin/main` via PR #1174 (`4dc76306 Land streamlined six-item sidebar`). Six-item rail shipped; open ledger row was stale post-merge. | 2026-07-25 | -| #067 | issue | Reconciliation preflight test times out under full-suite load | Fixed in PR #1191 (`e2488dbb`) by calling `collectReconciliationState()` in-process; PR #1203 further injects a fixture `repositoryRoot` so the contract no longer scales with the live worktree farm. No global timeout raise or heavy-test lock bypass. | 2026-07-25 | -| #007 | rec | `/tools` vs `/?mode=tools` parallel Tools entry points | Resolved as `/tools` canonical (PT-11 already documented on `/applications` redirect). Sidebar, appModeHomeHref, universal-search, prefetch, sitemap, and reachability now use `/tools`; `/?mode=tools` remains a dashboard-mode alias. Reachability allowlist entry removed. | 2026-07-24 | -| #030 | issue | Wide-tier alias lets one doc satisfy both comparison slots | Fixed on `cursor/search-correctness-030-075-6273`: removed dual-listed Admission-to-Discharge titles from AdmissionCommunityPts so one retrieved source cannot make allHit true for both comparison slots; fail-closed contracts in `tests/eval-document-matching.test.ts`. RAG impact: no retrieval behaviour change — eval matching only. Hardened after merge: coverage dedupes by document identity and assigns by maximum matching (#080). | 2026-07-24 | -| #075 | issue | Search-scope label enumeration can truncate after 1,000 rows | Fixed on `cursor/search-correctness-030-075-6273`: `loadScopeLabels` pages document_labels with deterministic order/batching past the Supabase 1k cap; multi-page >1000 contracts in `tests/search-scope.test.ts`. Isolated from mixed PR #1132. RAG impact: no retrieval behaviour change — label pagination only. | 2026-07-24 | -| #009 | rec | Confirm `/api/jobs` is intentionally server/ops-only | Kept as deliberate administrator/ops listing: no client `fetch("/api/jobs")` (UI uses `/api/ingestion/jobs`); documented in `docs/api-jobs-ops-surface.md` plus wiring/codebase-index/site-map notes. Not abandoned — do not remove without updating API contract tests. | 2026-07-24 | -| #010 | task | Un-built "Coming soon" controls across forms/favourites | Audited forms/favourites/presentation placeholders: all use honest `disabled` or `aria-disabled` + coming-soon copy (or presentational `ToggleSwitch` without `onToggle`). No fake-interactive controls; leave unwired until features land. Recorded in `docs/wiring-conventions.md`. | 2026-07-24 | -| #032 | rec | Governance ranking weighting: REFUTED, not debt | Reinforced as guardrail only in `docs/rag-behaviour/refuted-approaches.md` (Refutation 3), README, and safeguards — do **not** implement `review_due`/unknownCurrentness ranking penalties or boosts. No retrieval/ranking code changed. RC8 filter path remains the only revisit route behind canary gates. | 2026-07-24 | -| #041 | rec | Extend the existing Factsheets reading model | Brief recorded in `docs/factsheets-reading-model-brief.md`: extend Easy Read/Standard on existing Factsheets routes; reject a second patient-facing Factsheets mode unless concrete need + source-governance plan exist. | 2026-07-24 | -| #063 | rec | Define “Current Clinical Work” before implementation | Product/privacy/persistence brief recorded in `docs/current-clinical-work-brief.md`. Default v0 = no new storage (tab/URL resume); Class C free text needs privacy clearance. Stop without demand evidence. No UI/schema implemented. | 2026-07-24 | -| #076 | task | Reproduce malformed fallback PDF image/table crops | Reproduced truncated page-edge `table_crop`s on current-main with `worker/python/fixtures/malformed-table-crop-page-edge.pdf`. Root cause: `pymupdf_find_tables` stops at the last fully detected row; fix extends the candidate from contiguous cell drawings, recovers the on-page score-5 remnant, and emits `table_crop_edge_incomplete` / `crop_completeness=0.9` when content continues past the page. PR #1176. Broad PR #1129 retention/padding changes not merged. | 2026-07-24 | -| #070 | issue | Presentation mobile tabs misroute Overview/Map/Related | Fixed in PR #1135: Overview/Map/Related deep-link to diagnosis `?tab=` sections; Compare stays on the presentation page. Regression in `tests/mobile-interaction-regressions.test.ts`. (Provisional PR-branch IDs `#068`–`#072` were renumbered after `main` claimed `#068`/`#069`.) | 2026-07-24 | -| #071 | issue | Evidence/Clinical Notes Add fakes success without persistence | Fixed in PR #1135: sticky Add controls use the focusable coming-soon placeholder pattern instead of optimistic `setAdded(true)`. | 2026-07-24 | -| #072 | issue | Tools hub exposes false Sort/More affordances | Fixed in PR #1135: Sort is a status label, More filter targets coordination/saved without a fake menu chevron, and the favourites shortcut is labelled Saved/Favourites. | 2026-07-24 | -| #073 | issue | Presentation compare dock CTA is a self-link no-op | Fixed in PR #1135: dock shows non-link "Comparing (N)" status while already comparing. | 2026-07-24 | -| #074 | issue | Mode-action popup hard-reloads internal clinical routes | Fixed in PR #1135: `master-search-header` uses `router.push` for DSM/Specifiers/Formulation actions and mode href fallback. | 2026-07-24 | -| #068 | task | Regenerate full drift-manifest snapshot after schema hygiene | Full Docker `npm run drift:manifest` replay succeeded on a Docker-capable host; `supabase/drift-manifest.json` now carries live `def_hash` values for the plpgsql table-facts body (offline generator_note removed). | 2026-07-24 | -| #052 | issue | Reindex can overlap a fresh agent-enrichment pass | PR #1143 retained the friendly full/retry preflight and closed its check-then-enqueue race with an owner-scoped transactional RPC. Reindex enqueue and the agent claim path serialize on the document row; disposable PostgreSQL proved both interleavings, and exact-head migration replay/unit/build/Chromium/policy/security checks passed. | 2026-07-24 | -| #062 | issue | Upload crash can strand a queued document without a job | Aged owner-scoped `queued`-without-open-job rows are detected by `reindex:health`; the six-hour autopilot raises a durable alert, and guarded recovery uses PR #1143's transactional RPC so enqueue is owner-scoped, idempotent and atomic. `recover:ingestion --include-stranded-queued` remains dry-run/confirmation-first; scheduled production mutation is not enabled. | 2026-07-24 | -| #060 | issue | Safety Plan Generator contradicted the privacy contract | PR #1119 removed patient identifier entry, leaves the post-export name line blank, and aligned tool, privacy and PIA copy. DOM/privacy tests and Chromium copy/print/network coverage prove working content remains in React memory with no fetch/XHR; hosted Production UI, build, unit, policy, safety, static-analysis and secret checks passed. Support-contact details remain classified as sensitive local-only working content. | 2026-07-24 | -| #061 | issue | Missing answer relevance metadata was treated as source-backed | PR #1125 now requires explicit source-backed relevance for trusted/grounded presentation and prevents visual tables, clinical-note sections and quotes, and comparison metadata from bypassing the render model. Three actionable P2 review paths were fixed; focused policy/DOM tests, offline RAG, production-readiness, build, unit, static, security, and Production UI gates passed. No retrieval, ranking, generation, provider, or data behavior changed. | 2026-07-24 | -| #034 | issue | Answer cache can serve stale governance metadata | Current-source verification found direct route coverage already asserts RAG-cache invalidation on document PATCH, source review, label, bulk, and reindex mutation paths. The residual test recommendation is already met; changing the protected cache key is unnecessary. | 2026-07-24 | -| #014 | rec | Realize the `next/image` win on signed previews | Superseded: `SignedImage` uses `next/image` for layout and sizing but deliberately sets `unoptimized`, preventing bearer signed URLs from entering the unauthenticated optimizer cache where cached content could outlive the token. No optimization task remains unless private-image delivery changes. | 2026-07-24 | -| #026 | task | Wire the Supabase document-change trigger | PR #1100 merged after disposable PostgreSQL replay and hosted migration replay. Production migration history and read-only catalog proof confirm the enabled metadata trigger, security-definer function, pinned search path and denied anonymous/authenticated execution; `npm run check:drift` reports no unexpected live drift. Delivery remains intentionally inert until the operator inputs tracked in #025 are configured. | 2026-07-24 | -| #031 | issue | Populate canary Source Governance table | The answer-quality step now consumes the preceding `golden-retrieval.json` only for source-governance reporting. Offline replay of run `30018289898` populated 338 top results, including 202 review-required entries, while retaining zero retrieval cases and no additional threshold failures. Retrieval and ranking behavior are unchanged. | 2026-07-24 | -| #020 | task | Validate eval:quality cost readout post-fix | Confirmed on merged-main canary run `30018289898`: Answer Metrics reported 9 nonzero-cost cases and an estimated answer cost of `$0.234736`; the structured report retained the same value. The PR #1050 estimator fix is operationally proven. | 2026-07-23 | -| #003 | task | Staging tenancy release evidence outstanding | Ran GitHub Action and validated isolation | 2026-07-21 | -| #002 | task | Process-ownership fix not yet isolated on `main` | Fixed process isolation using child.pid termination | 2026-07-21 | -| #008 | rec | Dead href builders in `document-flow-routes.ts` | Not dead code (false positive): `documentReaderHref`/`documentEvidenceHref` are live via the mock wrappers in `src/components/document-search-mockups.tsx` + `src/components/master-document-flow-mockups.tsx` (rendered under `src/app/mockups/document-search/`) and covered by `tests/document-flow-routes.test.ts`; removing breaks the build. Only the production non-mock hrefs are unlinked from prod UI — a wiring gap, not dead code. | 2026-07-22 | -| #015 | task | Content-first fallback regression tests | Added `tests/registry-record-loader.dom.test.tsx` (8) + `tests/medication-record-page.dom.test.tsx` (6) covering content-first fallback paint, live swap-in, spinner/skeleton, error + not-found/unauthorized states, and the invariant that no authoritative verification badge shows before live governance reconciles (registry fixture-flag neutralization + medication governance-drop-on-error). | 2026-07-22 | -| #004 | rec | Rescope provider-gated RAG safety ideas | Closed obsolete — rescue source (754-line RAG-safety worktree) unrecoverable/pruned across all refs; answer-quality thresholds + deep-health already shipped on `main` (#585/#587); only cost-cap preflight was genuinely missing and, per session decision, dropped rather than re-filed. | 2026-07-22 | -| #006 | issue | Globe "Language & region" button had no handler | Resolved on main with the repository's disabled "Coming soon" placeholder convention and button-wiring coverage. Future language/region work remains a feature request, not an inert-control defect. | 2026-07-22 | -| #042 | issue | Invalid optional credentials fell into anonymous access | PRs #1078/#1079 introduced `absent \| valid \| invalid`, return 401 for presented invalid credentials, preserve authoritative header precedence and prefer the current-project session cookie. The archived anonymous-upload metadata patch was rejected as stale because uploads are already administrator-only before duplicate lookup. | 2026-07-22 | -| #043 | issue | Readiness could report healthy or throw on Supabase errors | PR #1080 now fails readiness closed for returned and thrown dependency failures, preserves recognized actionable messages, and prevents raw dependency-error disclosure. | 2026-07-22 | -| #044 | issue | Publication approval was not bound to immutable reviewed state | PR #1081 added a canonical reviewed-state digest, row locks, active-job rejection and a new forward migration with replay/schema/type/drift evidence. | 2026-07-22 | -| #045 | issue | Bulk reindex discarded partial-success results | PR #1084 reserves preflight conflicts for non-2xx responses; completed mixed batches return per-item success/failure/missing results, and the UI refreshes successful work. | 2026-07-22 | -| #046 | issue | DOCX extraction lacked explicit resource budgets | PR #1085 added pre-inflate declared-size checks and post-read fail-safes for artifact count, per-artifact bytes, aggregate media, Word XML and extracted UTF-8 text. | 2026-07-22 | -| #047 | issue | XLSX extraction could construct unbounded results | PR #1086 bounds worksheets, non-empty rows, rendered cells and UTF-8 output while preserving sparse-column rendering. | 2026-07-22 | -| #048 | issue | Account copy overstated sync/privacy and enabled unavailable SSO | PR #1087 now maps copy to actual favourites/preferences persistence, identifies browser-session recents, removes the contradictory "never shared" claim and clearly disables unavailable providers using the accessible placeholder contract. | 2026-07-22 | -| #049 | issue | Process diagnostic exposed a Cursor worker API key | The exact worker was stopped, the key was revoked server-side, both local encrypted worker-secret records were removed, and authorized repository/backup scans found no plaintext copy. Follow-up guardrails now prevent repository process inventory from serializing command lines and redact heavyweight-lock command text before persistence or errors. | 2026-07-23 | -| #050 | issue | Next.js 16.2.10 remained in a high-severity security range | Upgraded `next` and `@next/env` to 16.2.11, regenerated the npm lockfile, confirmed the production dependency audit is clean, and passed focused framework checks, `verify:cheap`, and the full Chromium UI gate. | 2026-07-23 | -| #116 | issue | Missing CI signal on conflicted PRs | RESOLVED 2026-07-30: `.github/workflows/pr-mergeability.yml` checks trusted `pull_request_target` events and refreshes unchanged heads after protected-base pushes. A conflicted PR gets a red `PR mergeability` check naming the conflict instead of an empty CI list. The base-push job alone has scoped `checks: write`; neither path checks out PR code or updates branches. Classifier: `scripts/pr-mergeability.mjs`; contract: `npm run check:pr-mergeability`. Behind-but-clean remains `sync:pr-branches`. | 2026-07-30 | -| #140 | issue | Union-driver table duplication — duplicate of #133 | CLOSED 2026-07-30 as a duplicate, superseded by `#133` and resolved by PR #1444. This row was opened mid-session for the union-driver damage before its author noticed `#133` had already recorded the same finding — and recorded it _earlier_, with the same conclusion ("**Do not** apply a `merge=union` driver — tested 2026-07-30 and it is worse"). Keeping both would have left two open rows describing one condition, and that condition no longer exists: PR #1444 removed `merge=union` from `.gitattributes`, inverted `check:outstanding-issues` to require an unspecified `merge` attribute, and added regression tests covering `union`, `-merge` and an unparsed reading. The surviving evidence — four merges on PR #1430 each reporting success while duplicating the entire open-items table — lives in `#133`, whose still-open half is the real conflict-frequency cause: fixed-width column padding makes any one-row edit re-pad every row, so git sees the whole table as one hunk. Read `#133`, not this. | 2026-07-30 | +| ID | Type | Summary | Outcome | Resolved | +| ---- | ----- | ----------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | +| #149 | issue | Failed clinical searches are announced to nobody — the results band mutes its live region | WITHDRAWN 2026-07-30, same day it was filed: the claim was false and the proposed fix would have caused a regression. Codex caught it on PR #1481. **What was missed:** `search-results-header-band.tsx` deliberately silences the count span while its separately mounted `role="alert"` fault panel announces the failure title, body, and Retry action. `tests/search-results-header-band.dom.test.tsx` already pins exactly one status region and one alert. Escalating the count span too would create a duplicate announcement and break that contract. **Not a defect in the mockup:** `search-refine-adaptive-mockups.tsx` has no separate fault panel, so its count span correctly owns the announcement there. **Stop:** do not re-file this; verify which node owns an announcement before treating a muted sibling live region as a defect. | 2026-07-30 | +| #131 | issue | `@codex fix` produced commits that never reached the repository | RESOLVED 2026-07-30. Automatic repair requests now bind Codex to the exact repository, pull-request head branch, and starting SHA; explicitly forbid detached/synthetic `work` branches and stacked pull requests; and require the authenticated GitHub connector to publish and verify the fix before success is reported. Thread closure is fail-closed: a fixed reply must name its 40-character pushed commit SHA, and the workflow resolves the thread only when that SHA is the pull-request head. No-code dispositions carry a distinct marker, while missing, conflicting, or unlanded result claims leave the thread open with a visible workflow failure. The guard and executable workflow tests pin the destination prompt, both valid outcomes, malformed results, and mismatched-head refusal. Source: issue #131; session 2026-07-30 | 2026-07-30 | +| #136 | rec | Reuse Next build cache across isolated Playwright production builds | CLOSED 2026-07-30 after an end-to-end implementation benchmark rejected the proposed cache as a net CI loss. Keeping the reusable cache outside each disposable run root preserved the runner cleanup contract, but a warm build saved only 34 seconds (97s cold to 63s warm) while producing an 804 MB cache. Persisting that entry per commit would consume the repository cache budget and evict the substantially more valuable Playwright browser cache. No cache wiring ships; reconsider only if Next materially reduces the cache size or a later measurement changes the storage/time trade-off. Source: `scripts/run-playwright.mjs`; `docs/testing.md`; session 2026-07-30 | 2026-07-30 | +| #085 | rec | Upload-limit client/server sync is unguarded | Resolved 2026-07-30. A provider-free parity checker now validates both configured limits, their shared 150 MB default, invalid and over-ceiling values, and mismatches. It runs in `verify:cheap`, the static PR job, and before every production build so a client-side value cannot silently diverge from the server runtime limit. | 2026-07-30 | +| #119 | issue | `ci/circleci: verify` is failing repo-wide | Retired 2026-07-30. CircleCI was removed from the repository in commit `977982857`, and the user confirmed the integration is deleted. The prior request to inspect build 672 is obsolete; GitHub Actions remains the repository CI path. | 2026-07-30 | +| #125 | issue | `ui-therapy-nav-scroll.spec.ts` cites a spec file that does not exist | Resolved 2026-07-30. The Therapy navigation comment now points to the live `ui-mode-nav-density.spec.ts` coverage instead of the deleted `mode-nav-bar-anchoring.spec.ts` file. | 2026-07-30 | +| #095 | issue | `PR required` reports failure for concurrency-cancelled jobs | RESOLVED 2026-07-30. The `pr-required` aggregate now distinguishes a cancelled job from a failed one. `require_success` / `require_skipped_or_success` are thin wrappers over one `record()` collector that reads **each job's own `result`** and appends to a `failures` or `cancellations` array; both arrays are filled before anything is reported. **Genuine failures win:** every failure is emitted as its own `::error::` and a concurrent cancellation is demoted to a `::warning::`, so a run that is cancelled AND broken cannot read as an excuse (refinement reported by Codex on PR #1409). Cancelled with nothing failing stays **RED**, and the message states the two possibilities rather than asserting supersession: it points the reader at a newer `PR required` run on the PR's current head SHA, and says that if there is none the run was cancelled by hand and must be re-run rather than merged past. It deliberately does NOT read the workflow-level cancelled status function — an earlier revision passed that through an `env:` value, which is invalid because GitHub allows those functions only in `if:` conditions, so the whole file failed to parse, ran as `.github/workflows/ci.yml` instead of `CI`, and created zero jobs; valid YAML but invalid Actions schema, so prettier and every local gate passed it. **The tempting fix was rejected as unsafe:** treating `cancelled` as neutral, or skipping the aggregate via a not-cancelled condition, would make the red disappear, but GitHub counts a SKIPPED required check as PASSING, so a hand-cancelled run on the current head would become mergeable with nothing verified — `if: always()` is therefore deliberate. Guarded by ten cases in `tests/ci-cache-safety.test.ts` that EXECUTE the extracted aggregate script under synthetic job results rather than grepping the YAML. **Corrected in review (Codex, PR #1428):** the first version of this record described a shared `cancelled_error` helper that never existed and claimed the error names the newest run; both were carried over from an obsolete sentence in the open row and neither matched the shipped implementation. Source: PR #1316 runs 30340972329 / 30341225585; PR #1409 | 2026-07-30 | +| #096 | task | PR #1316 review follow-ups — adoption-gate coverage closed | RESOLVED 2026-07-30. Every sub-item is dispositioned. The band adoption gate's root-path gap closed on PR #1394 — root and href-less modes now resolve to `src/app/(search-app)/page.tsx` — and closing it surfaced two further defects in the same gate the original finding did not name: the hand-rolled walk was capped at two import hops where the root route's real chain is four, and it followed neither `layout.tsx` (where that route's band actually comes from) nor `dynamic(() => import(...))` (how the dashboard code-splits its mode workspaces). All three were fixed together with a bounded BFS, each verified load-bearing by reverting it and watching the gate fail. Four findings — favourites hub counts, the document-search status derivation, the 401 session-expiry path, the record-path duplicate notice — were already fixed independently. The Therapy Compass retry-waiter finding was corrected to NOT a live defect: `retryWaitersRef` is genuinely unscoped but no caller observes it. The seven Codex follow-up SHAs remain unreachable and were never pushed; the PR #1316 review threads are the durable source. Archived by the 2026-07-30 triage pass. Source: PR #1316 review sweep | 2026-07-30 | +| #104 | rec | Worker's triple image read is deliberate, not debt | NOT DEBT — archived so a fourth audit does not re-file it. The 2026-07-28 latency audit listed L4-2 (`worker/main.ts` reads each extracted image up to 3x per document — hash, caption on cache miss, upload) as "CONFIRMED with no fix evidence", carried forward from the 2026-07-01 audit's L11. **That was wrong.** The 2026-07-01 disposition table already recorded it as a deliberate peak-memory trade-off, and the rationale is documented in place at `worker/main.ts:866-869`: holding every extracted image Buffer for a document with hundreds of multi-MB page images would multiply the worker's peak memory, and disk I/O is the cheaper resource for a background pipeline. The three reads (`:872`, `:1034`, `:1129`) are real but accepted. This row carried no next action, so it is archived rather than left open — revisit only if ingestion throughput becomes a measured complaint AND a bounded-buffer design is proposed. **Stop:** do not re-file this as debt. Source: `docs/audit/repo-audit-2026-07-01.md` L11 + disposition table | 2026-07-30 | +| #109 | issue | Remote sessions clone shallow, invalidating branch analysis | RESOLVED 2026-07-29, hardened in review. `scripts/sweep-branch-ledger.mjs` refuses outright on a shallow clone via the exported `shallowCloneRefusal`, printing no inventory and exiting 1 in both text and `--json` mode, before the fetch and before any branch maths; `docs/branch-cleanup-guide.md` §Safety Rules gains the `is-shallow-repository` precondition ahead of its numbered steps. An indeterminate result (empty output from a swallowed `git` failure) is refused as its own failure, and the same guard had to move OUT of the reconciliation preflight CLI and INTO the exported `collectReconciliationState`, because `buildReconciliationEvidencePack` calls the collector directly and stamped `status: complete` — in a `--depth 1` clone the guarded CLI exited 1 while the evidence-pack command exited 0 and persisted shallow ahead/behind as completed evidence. The collector now throws `UnverifiedHistoryError` so every current and future caller fails closed by default. Review found two further routes to a false-empty inventory from half-checked refspecs; coverage requires exactly `refs/heads/*` to `refs/remotes/origin/*`. Proven against real `--depth 1` clones in `tests/repo-hygiene.test.ts`, `tests/reconciliation-preflight.test.ts` and `tests/reconciliation-evidence-pack.test.ts`. Source: session 2026-07-29 | 2026-07-29 | +| #115 | rec | Band adoption gate treats a discovered import as rendered | RESOLVED 2026-07-30 by redesign rather than another patch. `tests/search-results-band-adoption.test.ts` no longer asks whether a file mentions the band but whether anything the route actually mounts reaches it: it parses each module with `@babel/parser` into a small graph — exported name to local declaration, local to the identifiers its body references, and which locals render the band — then walks from the route's default export, carrying at each hop the set of exports the importer mounts. A static `import { X }` is followed only when `X` is reachable from a mounted declaration; `dynamic(() => import(...).then((m) => m.Named))` follows only that binding; a bare side-effect import is not followed at all; `export *` never supplies a default. **Why redesign:** six false greens were reported in one day, all one defect — presence is not reach — and two of the six had been introduced by an earlier patch to the same walker. Verified: all five production search routes still reach the band, gutting `services/page.tsx` and `tools/page.tsx` to `
` reports an orphan each, and fourteen temp-dir fixtures cover both directions with two mutation-confirmed. Accepted residual: reachability is per module and control flow inside a mounted declaration is not modelled. Source: PR #1400 | 2026-07-30 | +| #108 | task | Five verified-landed remote branches await deletion (blocked in-session) | DONE 2026-07-30. Deleted the three remaining empty remote refs after re-verify (tree(tip)==tree(merge-base), 0 files vs main, no open PR): `codex/document-reader-condensed-view` @ `b5cdbf301d517239ffe9ed941b9ebe809aea0bfd`, `cursor/page-anchored-search-composer-30ee` @ `7ff134ca7f614db527b8d142676640305533669d`, `cursor/pr-1379-babysit-ledger-9365` @ `be2de03f855cb7fdfccea4bb74d05eb4c9bf6c61`. The other two candidates were already gone. Completed `branch-cleanup` ledger rows were already present; deletion succeeded from this session (prior 403 cleared). | 2026-07-30 | +| #088 | task | Union-driver ledger duplication watch after repair | CLOSED 2026-07-30. Post-repair merges take main's repaired lines; residual exact-dupe babysit twins from stock `merge=union` are addressed by the custom `merge=ledger` driver (union + exact-row dedupe), `npm run ledger:dedupe`, and the Run PR anti-churn ledger policy. Success criterion (three consecutive post-repair guard passes) met on ordinary main syncs; ongoing exact-dupe class is now gated rather than watched. | 2026-07-30 | +| #087 | issue | `npm run check:knip` reported pre-existing dependency findings | NOT A DEFECT — false positive. The findings (unused `rimraf`/`tsx`/`@testing-library/dom`, unlisted `playwright-core`) only appeared because the worktree had no `node_modules` of its own and tooling resolved from the parent checkout. After `npm ci` in the same worktree, `npm run check:knip` exits 0 with no ignore-list change. Durable lesson: never act on a knip finding from a worktree that has not been installed. | 2026-07-28 | +| #089 | issue | Branch-review-ledger hygiene deferred from PR #1275 | Closed by the 2026-07-28 hygiene pass. The MD056 cell-count and duplicate-row findings dispositioned as "belongs in a dedicated main ledger hygiene pass" on PR #1275 are fixed: 140 mojibake lines restored byte-exact from git history, 6 residual separators repaired, 46 exact duplicates removed, 21 wrong-width rows normalised, and 4 heading+bullet records converted to table rows — 1067 records, all six cells. Root cause closed too: `npm run ledger:lookup` / `ledger:append` (`scripts/branch-review-ledger.mjs`) replace hand-written rows, and `check:branch-review-ledger` now fails on mojibake, cell width, heading records, impossible dates, table gaps, and (from 2026-07-29) unresolvable HEADs and near-duplicates. | 2026-07-28 | +| #097 | issue | Gitleaks reports a false red when the PR head moves mid-run | RESOLVED 2026-07-30: Secret Scan checks out the event head SHA and runs `scripts/run-gitleaks-pinned.mjs` against the immutable event base..head range (no mid-run PR commits API re-query). | 2026-07-30 | +| #111 | issue | `ui-overlap` phone-inset case still flakes under load | RESOLVED 2026-07-29 (PR #1391). The inset measurement now retries inside `expect(async () => …).toPass({ timeout: 15_000 })` (`ui-overlap.spec.ts:159`) — the same retry-the-measurement shape PR #1375 applied to the eight overlap cases. The 2px symmetry tolerance and the assertions themselves are byte-for-byte unchanged, so a genuinely asymmetric header still fails once the retry budget is spent; only a transient mid-remount sample settles. Full spec green across 4 consecutive runs (14 passed each). Do not relax the tolerance if this recurs — the assertion is the point; make the measurement robust instead. Source: PR #1375 flake triage; session 2026-07-29 | 2026-07-29 | +| #112 | issue | `issues:next-id` has no concurrency protection | RESOLVED 2026-07-30. `npm run check:outstanding-issues` now gates this file, in `verify:cheap` and in the `static-pr` CI job (the gate-manifest check refuses a local gate that CI does not run). It fails on a duplicate id, an id present in both tables, a marker at or below the highest id, a malformed row, and a missing heading or marker — so every shape the 2026-07-29 triple collision took is now a red gate rather than a silent row loss. Verified by replaying that collision against the real file: two rows claiming `#110` produced "#110 appears 2 times (lines 151, 164)", and a lost marker bump produced "issues:next-id=113 is not above the highest id #114". The checker honours `\|` escapes — its first run against the live file flagged row #042, which is correctly escaped, and a gate with false positives is a gate people switch off. NOT fixed: the underlying race. Ids are still allocated by read-modify-write with no lock, and this file still has no `merge=union` driver; what changed is that a collision can no longer land silently. Source: session 2026-07-29 PR sweep; PR #1391 conflict resolution; `.gitattributes` | 2026-07-30 | +| #113 | issue | `ModeNav` clips its labels at every phone width on `main` | RESOLVED 2026-07-30 (PR #1405, landed on `main` as `020c1260`). The two lower density bands used `grid-auto-columns: 1fr`, so the widest slot set every slot's width and the label's `truncate` hid the shortfall — nothing overflowed and nothing failed, the word was simply gone. `display: flex` at every band removes the mode rather than retuning it; thresholds went `16/26/34rem` -> `22/33/42rem` against remeasured intrinsic widths (471px for four labels, not the 394px originally budgeted). `tests/ui-mode-nav-density.spec.ts` asserts at both band boundaries and one pixel either side, registered in BOTH Playwright allowlists. **Carry-forward:** the ~8% threshold headroom is not padding — a measured-plus-one-pixel attempt (21/31rem) passed locally and failed CI by exactly 1px, because the variable face rasterises wider on `ubuntu-24.04`. A threshold calibrated to one machine's font metrics is calibrated to nothing. | 2026-07-30 | +| #114 | issue | The live Web-Vitals instrument cannot measure its own noise | RESOLVED 2026-07-30 (PR #1407, landed as `a73db460`). Each route/strategy cell is measured `SAMPLES` times (default 3), the MEDIAN is graded, and a cell whose samples straddle a threshold is reported as incomplete evidence rather than resolved either way — so #017's "evidence is too noisy" stop rule can finally fire. Mid-run Chrome drift disqualifies the verdict. **Second defect found in review and fixed in the same PR:** `samples` is a free-text `workflow_dispatch` input, so `samples=1` reached the grader as a valid integer and reproduced the exact single-run matrix this work removes — verified against the pre-fix logic, it reported 0 breaches, 0 incomplete and printed the bold "Every mobile route is within" verdict. Now refused in three places (`main()` before reading any report, `renderTable`'s disqualifier list, and the workflow before spending a live measurement pass). | 2026-07-30 | +| #120 | issue | `verify:phone-chrome` exits 0 while reporting failed browser tests | RESOLVED 2026-07-30 — **closed as not reproducible, not as fixed.** The premise is contradicted by a reproduction of its own failure condition. **Evidence, `main` at `fcd9041`:** the container ships Chromium build `1194` while the repo wants `1234`, so `npm run verify:phone-chrome -- --files src/components/clinical-dashboard/mobile-composer-reserve.ts` reproduced the identical shape — the same `13 failed`, every one at `browserType.launch: Executable doesn't exist at /opt/pw-browsers/chromium_headless_shell-1234/…` — and the captured status was **`OVERALL_EXIT=1`**, not 0. The gate stopped at the `focused-browser` stage and never reached `full-ui`, which is correct; the three stages ahead of it were genuinely green (`Test Files 9 passed (9)`, `Tests 105 passed (105)`). **The code path agrees:** status propagates at `scripts/verify-phone-chrome.mjs:72-73` and `scripts/run-playwright.mjs:271-273`, and `childProcessExitCode` returns `1` when `status` is not an integer, so a launch error cannot read as 0. Neither file, nor `scripts/child-process-result.mjs`, has changed since `7581cfc`, which predates this row — so "fixed in the meantime" is ruled out and the original exit-0 reading is what is in doubt. **Leading hypothesis, explicitly unverified:** the 0 was read through a pipe, since `npm run verify:phone-chrome` piped to `tail` reports `tail`'s status rather than the gate's. The original command line was not recorded, so this cannot be confirmed. **Not covered by this reproduction:** the zero-selected-test case (a `--grep` matching nothing) was never exercised — only the launch-failure case this row describes. **If it recurs:** capture the exact command line and `echo $?` with no pipe before re-filing. | 2026-07-30 | +| #122 | issue | `ci/circleci: verify` fails on every branch and its log needs operator access | RESOLVED 2026-07-30 — moot: the status cannot mislead because the provider is gone from the repository. The row's own outcome allowed either branch ("trustworthy signal again, **or** it stops reporting"); the second is what happened, so no operator log read is needed and the **Next** step is withdrawn. **Evidence:** `.circleci/config.yml` was deleted by `9779828` ("chore: remove CircleCI from repo", PR #1412), and `ls -d .circleci` on `main` at `c5c1a86` returns `No such file or directory`. Confirmed live rather than only from the tree: PR #1452's head reported **21 check runs and not one named `ci/circleci: verify`**, so the status no longer reports on new PRs at all. The unverified quota hypothesis is retired unproven — it was never confirmable without a CircleCI token, and there is now nothing left to confirm it against. One residual was found and fixed in the same change: `AGENTS.md` still cited `ci/circleci: verify` as a check that fails on unformatted files, which would have sent a future session chasing a check that can never report again. | 2026-07-30 | +| #012 | rec | Slim the lazy cross-mode differentials chunk | Precomputed a trimmed index (`src/data/cross-mode-differentials-index.json` via `scripts/build-cross-mode-differentials-index.mjs`) so the lazily-loaded cross-mode chunk imports a ~53 KB catalog instead of statically pulling the ~1.2 MB differentials snapshot (only that dynamic path reached it). A drift test plus `check:cross-mode-index` (in verify:cheap) lock the index to the live projection. | 2026-07-27 | +| #029 | issue | Residual answer-quality fallback stubs | Closed after fixing each causal cluster independently. Active-community ED, community-home-visit, clozapine blood-threshold/typo, discharge source-gap recovery, and Best Practice Prescription now use narrowly validated, source-bound answers or auditable recovery; cited provider refusal prose can no longer masquerade as grounded, and terminal gaps retain no claim citations. The final 44-case gate reported 30/30 substantive grounded supported answers, 14/14 unsupported correct, zero review fallbacks, zero citation/numeric failures, and zero route-ceiling failures. Measurement still reports review fallback separately and denies targeting credit for echoed boilerplate. | 2026-07-27 | +| #019 | issue | Preserve admission/discharge sources through comparison fallback | The actual fallback path now selects source-bound facts, preserves one admission and one discharge citation from distinct documents, and terminates at an evidence gap for qualified, negated, unrelated, title-only, single-sided, or same-document traps. Both exact live cases complete in about one second with zero provider calls; the final 44-case canary passed them with two citations each, while the 36-case retrieval canary held recall 1.0 and zero RR regressions. Retrieval scores, aliases, clamps, and comparator ordering were unchanged. | 2026-07-27 | +| #084 | task | Persist per-result irrelevant-at-10 grading evidence | `eval-retrieval` now persists each top result's `relevanceGrade` and `matchedDeclaredSignals`; focused fixtures cover ideal and zero-grade rows. The final golden artifact contains 338 graded top rows, including 33 grade-zero rows. This closes the reproducibility gap only: fixture labels, ranking, thresholds, and provider behavior were not changed, and human disposition remains #023. | 2026-07-27 | +| #080 | rec | Re-test the removed admission-to-discharge alias widening | Restored the two approved NMHS Admission-to-Discharge titles only on the eval-expectation surface. Canonical document-identity dedupe plus maximum bipartite matching prevents one dual-listed physical document from satisfying both comparison slots. Focused matching tests, both targeted admission cases, the final 36-case golden retrieval run, and the 44-case answer run passed; runtime retrieval/ranking behavior was not changed. | 2026-07-27 | +| #083 | issue | Documents-only universal search timed out on staging tenancy | A current staging nightly reproducer showed the documents-only search losing its synthetic fixture after the federated typeahead timeout was reduced to 750 ms. Current main retains 750 ms for multi-domain requests and uses the established 6,000 ms budget only when documents are the sole requested domain; fake-timer coverage proves both paths. RAG impact: no retrieval, ranking, ordering, alias, score, or result-selection change—only availability of the explicitly focused request. | 2026-07-27 | +| #082 | issue | Bot branch-sync heads leave required checks unapproved | Retired the automatic `GITHUB_TOKEN` PR branch-update workflow instead of weakening required-check approvals or introducing a privileged automation token. The existing helper remains dry-run by default, verifies its apply identity, and refuses missing or bot identities. The fast GitHub Actions policy check rejects both direct workflow `update-branch` calls and indirect apply-helper invocation. | 2026-07-27 | +| #058 | task | Verify production content before any seed write | Read-only production counts on project `sjrfecxgysukkwxsowpy` found 276 clinical registry, 328 medication, and 232 differential records. The required tables are non-empty, so no seed or production write was needed. | 2026-07-27 | +| #069 | task | Validate hosted table-facts RPC latency | Read-only profiling on the correct hosted project separated sample 1 (`first_unprimed`) from five `warm_repeat` samples; managed Supabase buffers were not flushed, so no true-cold claim is made. First-unprimed client/DB execution was 662.916/141.537 ms (clozapine), 277.661/96.229 ms (lithium), and 322.598/148.378 ms (metabolic). Warm client median/p90 was 187.029/198.907, 167.803/174.391, and 189.065/243.324 ms; warm DB execution median/p90 was 88.292/89.355, 64.342/65.566, and 107.448/148.417 ms. Earlier exact clinical probes were lower again. Plans are not the multi-second tail; no hosted migration, ranking, or provider configuration changed. | 2026-07-27 | +| #051 | task | Stabilise the live answer-quality canary before more RAG tuning | Closed after the scheduled structured report supplied a comparable second 36-retrieval/44-answer datapoint. Content gates stayed stable, the prior citation failure cleared, and #019 repeated with an identical diagnostic signature. Retrieval latency was investigated separately: #069 subsequently found acceptable table-facts database plans, so the broad scheduled tail was not treated as ranking debt. The report/trend tooling is now sufficient to compare future approved runs; no scheduled rerun or tuning was dispatched. | 2026-07-27 | +| #054 | task | Reconcile local and hosted secrets/config | Completed production names-only reconciliation on 2026-07-27. The correctly identified primary checkout received distinct gitignored local safety/query-hash/deep-probe values. A hardened checker now pins GitHub to `BigSimmo/Database` and Railway to the live production project/environment plus `Database`/`worker`, catches multiline schema and `.env.example` drift, and verifies GitHub secrets/variables and per-service Railway contracts without emitting provider values. All required names passed; the Ops Digest workflow is active with a successful scheduled run; both Railway services have later successful deployments; Supabase names-only proof found the expected cron/Vault configuration. Value equality remains deliberately unobservable, staging stays #056, webhook activation stays #025, and legal/ZDR work stays #053. | 2026-07-27 | +| #064 | task | Reconcile the preserved browser and contrast patch | Landed via PR #1250 squash `b91b4600171be08198e92bcf19b7d67e8207cb2f`. Opacity-free disabled Previous/Continue styling plus native-disabled/focus/axe Playwright coverage is on `main`. Historical `agent/formulation-disabled-contrast` remained unrecovered; conflicted PRs #1219/#1223/#1226/#1231/#1249 were closed without merge. | 2026-07-26 | +| #081 | issue | Open PR #1196 would undo the #030 alias tightening | Closed as no longer live: PR #1196 was closed 2026-07-25 as superseded by #913/current `main` (~680 commits behind, conflicting), and its successor #1198 does not touch `src/lib/eval-document-matching.ts`. The generalized alias-disjointness and single-document contracts landed in PR #1215 fail closed if any later branch re-adds the dual-listed admission aliases, so the regression route is guarded rather than watched. | 2026-07-25 | +| #077 | issue | Concurrent tasks can re-dirty the canonical primary checkout | Added cooperative primary-checkout write lease with dirty/operation fail-closed checks, stale-owner recovery, and lifecycle start/cleanup wiring; focused concurrency tests refuse a second primary writer while read-only/feature worktrees stay unblocked. | 2026-07-25 | +| #078 | task | Generate a deterministic reconciliation evidence pack | Added report-only atomic evidence pack with dispositions, markers, archive refs, bundle verify/hash, worktree counts, and local/base equality; fixture tests prove determinism/redaction and no false completion record on interrupt. | 2026-07-25 | +| #066 | task | Land and prove the streamlined six-item sidebar | Proven on `origin/main` via PR #1174 (`4dc76306 Land streamlined six-item sidebar`). Six-item rail shipped; open ledger row was stale post-merge. | 2026-07-25 | +| #067 | issue | Reconciliation preflight test times out under full-suite load | Fixed in PR #1191 (`e2488dbb`) by calling `collectReconciliationState()` in-process; PR #1203 further injects a fixture `repositoryRoot` so the contract no longer scales with the live worktree farm. No global timeout raise or heavy-test lock bypass. | 2026-07-25 | +| #007 | rec | `/tools` vs `/?mode=tools` parallel Tools entry points | Resolved as `/tools` canonical (PT-11 already documented on `/applications` redirect). Sidebar, appModeHomeHref, universal-search, prefetch, sitemap, and reachability now use `/tools`; `/?mode=tools` remains a dashboard-mode alias. Reachability allowlist entry removed. | 2026-07-24 | +| #030 | issue | Wide-tier alias lets one doc satisfy both comparison slots | Fixed on `cursor/search-correctness-030-075-6273`: removed dual-listed Admission-to-Discharge titles from AdmissionCommunityPts so one retrieved source cannot make allHit true for both comparison slots; fail-closed contracts in `tests/eval-document-matching.test.ts`. RAG impact: no retrieval behaviour change — eval matching only. Hardened after merge: coverage dedupes by document identity and assigns by maximum matching (#080). | 2026-07-24 | +| #075 | issue | Search-scope label enumeration can truncate after 1,000 rows | Fixed on `cursor/search-correctness-030-075-6273`: `loadScopeLabels` pages document_labels with deterministic order/batching past the Supabase 1k cap; multi-page >1000 contracts in `tests/search-scope.test.ts`. Isolated from mixed PR #1132. RAG impact: no retrieval behaviour change — label pagination only. | 2026-07-24 | +| #009 | rec | Confirm `/api/jobs` is intentionally server/ops-only | Kept as deliberate administrator/ops listing: no client `fetch("/api/jobs")` (UI uses `/api/ingestion/jobs`); documented in `docs/api-jobs-ops-surface.md` plus wiring/codebase-index/site-map notes. Not abandoned — do not remove without updating API contract tests. | 2026-07-24 | +| #010 | task | Un-built "Coming soon" controls across forms/favourites | Audited forms/favourites/presentation placeholders: all use honest `disabled` or `aria-disabled` + coming-soon copy (or presentational `ToggleSwitch` without `onToggle`). No fake-interactive controls; leave unwired until features land. Recorded in `docs/wiring-conventions.md`. | 2026-07-24 | +| #032 | rec | Governance ranking weighting: REFUTED, not debt | Reinforced as guardrail only in `docs/rag-behaviour/refuted-approaches.md` (Refutation 3), README, and safeguards — do **not** implement `review_due`/unknownCurrentness ranking penalties or boosts. No retrieval/ranking code changed. RC8 filter path remains the only revisit route behind canary gates. | 2026-07-24 | +| #041 | rec | Extend the existing Factsheets reading model | Brief recorded in `docs/factsheets-reading-model-brief.md`: extend Easy Read/Standard on existing Factsheets routes; reject a second patient-facing Factsheets mode unless concrete need + source-governance plan exist. | 2026-07-24 | +| #063 | rec | Define “Current Clinical Work” before implementation | Product/privacy/persistence brief recorded in `docs/current-clinical-work-brief.md`. Default v0 = no new storage (tab/URL resume); Class C free text needs privacy clearance. Stop without demand evidence. No UI/schema implemented. | 2026-07-24 | +| #076 | task | Reproduce malformed fallback PDF image/table crops | Reproduced truncated page-edge `table_crop`s on current-main with `worker/python/fixtures/malformed-table-crop-page-edge.pdf`. Root cause: `pymupdf_find_tables` stops at the last fully detected row; fix extends the candidate from contiguous cell drawings, recovers the on-page score-5 remnant, and emits `table_crop_edge_incomplete` / `crop_completeness=0.9` when content continues past the page. PR #1176. Broad PR #1129 retention/padding changes not merged. | 2026-07-24 | +| #070 | issue | Presentation mobile tabs misroute Overview/Map/Related | Fixed in PR #1135: Overview/Map/Related deep-link to diagnosis `?tab=` sections; Compare stays on the presentation page. Regression in `tests/mobile-interaction-regressions.test.ts`. (Provisional PR-branch IDs `#068`–`#072` were renumbered after `main` claimed `#068`/`#069`.) | 2026-07-24 | +| #071 | issue | Evidence/Clinical Notes Add fakes success without persistence | Fixed in PR #1135: sticky Add controls use the focusable coming-soon placeholder pattern instead of optimistic `setAdded(true)`. | 2026-07-24 | +| #072 | issue | Tools hub exposes false Sort/More affordances | Fixed in PR #1135: Sort is a status label, More filter targets coordination/saved without a fake menu chevron, and the favourites shortcut is labelled Saved/Favourites. | 2026-07-24 | +| #073 | issue | Presentation compare dock CTA is a self-link no-op | Fixed in PR #1135: dock shows non-link "Comparing (N)" status while already comparing. | 2026-07-24 | +| #074 | issue | Mode-action popup hard-reloads internal clinical routes | Fixed in PR #1135: `master-search-header` uses `router.push` for DSM/Specifiers/Formulation actions and mode href fallback. | 2026-07-24 | +| #068 | task | Regenerate full drift-manifest snapshot after schema hygiene | Full Docker `npm run drift:manifest` replay succeeded on a Docker-capable host; `supabase/drift-manifest.json` now carries live `def_hash` values for the plpgsql table-facts body (offline generator_note removed). | 2026-07-24 | +| #052 | issue | Reindex can overlap a fresh agent-enrichment pass | PR #1143 retained the friendly full/retry preflight and closed its check-then-enqueue race with an owner-scoped transactional RPC. Reindex enqueue and the agent claim path serialize on the document row; disposable PostgreSQL proved both interleavings, and exact-head migration replay/unit/build/Chromium/policy/security checks passed. | 2026-07-24 | +| #062 | issue | Upload crash can strand a queued document without a job | Aged owner-scoped `queued`-without-open-job rows are detected by `reindex:health`; the six-hour autopilot raises a durable alert, and guarded recovery uses PR #1143's transactional RPC so enqueue is owner-scoped, idempotent and atomic. `recover:ingestion --include-stranded-queued` remains dry-run/confirmation-first; scheduled production mutation is not enabled. | 2026-07-24 | +| #060 | issue | Safety Plan Generator contradicted the privacy contract | PR #1119 removed patient identifier entry, leaves the post-export name line blank, and aligned tool, privacy and PIA copy. DOM/privacy tests and Chromium copy/print/network coverage prove working content remains in React memory with no fetch/XHR; hosted Production UI, build, unit, policy, safety, static-analysis and secret checks passed. Support-contact details remain classified as sensitive local-only working content. | 2026-07-24 | +| #061 | issue | Missing answer relevance metadata was treated as source-backed | PR #1125 now requires explicit source-backed relevance for trusted/grounded presentation and prevents visual tables, clinical-note sections and quotes, and comparison metadata from bypassing the render model. Three actionable P2 review paths were fixed; focused policy/DOM tests, offline RAG, production-readiness, build, unit, static, security, and Production UI gates passed. No retrieval, ranking, generation, provider, or data behavior changed. | 2026-07-24 | +| #034 | issue | Answer cache can serve stale governance metadata | Current-source verification found direct route coverage already asserts RAG-cache invalidation on document PATCH, source review, label, bulk, and reindex mutation paths. The residual test recommendation is already met; changing the protected cache key is unnecessary. | 2026-07-24 | +| #014 | rec | Realize the `next/image` win on signed previews | Superseded: `SignedImage` uses `next/image` for layout and sizing but deliberately sets `unoptimized`, preventing bearer signed URLs from entering the unauthenticated optimizer cache where cached content could outlive the token. No optimization task remains unless private-image delivery changes. | 2026-07-24 | +| #026 | task | Wire the Supabase document-change trigger | PR #1100 merged after disposable PostgreSQL replay and hosted migration replay. Production migration history and read-only catalog proof confirm the enabled metadata trigger, security-definer function, pinned search path and denied anonymous/authenticated execution; `npm run check:drift` reports no unexpected live drift. Delivery remains intentionally inert until the operator inputs tracked in #025 are configured. | 2026-07-24 | +| #031 | issue | Populate canary Source Governance table | The answer-quality step now consumes the preceding `golden-retrieval.json` only for source-governance reporting. Offline replay of run `30018289898` populated 338 top results, including 202 review-required entries, while retaining zero retrieval cases and no additional threshold failures. Retrieval and ranking behavior are unchanged. | 2026-07-24 | +| #020 | task | Validate eval:quality cost readout post-fix | Confirmed on merged-main canary run `30018289898`: Answer Metrics reported 9 nonzero-cost cases and an estimated answer cost of `$0.234736`; the structured report retained the same value. The PR #1050 estimator fix is operationally proven. | 2026-07-23 | +| #003 | task | Staging tenancy release evidence outstanding | Ran GitHub Action and validated isolation | 2026-07-21 | +| #002 | task | Process-ownership fix not yet isolated on `main` | Fixed process isolation using child.pid termination | 2026-07-21 | +| #008 | rec | Dead href builders in `document-flow-routes.ts` | Not dead code (false positive): `documentReaderHref`/`documentEvidenceHref` are live via the mock wrappers in `src/components/document-search-mockups.tsx` + `src/components/master-document-flow-mockups.tsx` (rendered under `src/app/mockups/document-search/`) and covered by `tests/document-flow-routes.test.ts`; removing breaks the build. Only the production non-mock hrefs are unlinked from prod UI — a wiring gap, not dead code. | 2026-07-22 | +| #015 | task | Content-first fallback regression tests | Added `tests/registry-record-loader.dom.test.tsx` (8) + `tests/medication-record-page.dom.test.tsx` (6) covering content-first fallback paint, live swap-in, spinner/skeleton, error + not-found/unauthorized states, and the invariant that no authoritative verification badge shows before live governance reconciles (registry fixture-flag neutralization + medication governance-drop-on-error). | 2026-07-22 | +| #004 | rec | Rescope provider-gated RAG safety ideas | Closed obsolete — rescue source (754-line RAG-safety worktree) unrecoverable/pruned across all refs; answer-quality thresholds + deep-health already shipped on `main` (#585/#587); only cost-cap preflight was genuinely missing and, per session decision, dropped rather than re-filed. | 2026-07-22 | +| #006 | issue | Globe "Language & region" button had no handler | Resolved on main with the repository's disabled "Coming soon" placeholder convention and button-wiring coverage. Future language/region work remains a feature request, not an inert-control defect. | 2026-07-22 | +| #042 | issue | Invalid optional credentials fell into anonymous access | PRs #1078/#1079 introduced `absent \| valid \| invalid`, return 401 for presented invalid credentials, preserve authoritative header precedence and prefer the current-project session cookie. The archived anonymous-upload metadata patch was rejected as stale because uploads are already administrator-only before duplicate lookup. | 2026-07-22 | +| #043 | issue | Readiness could report healthy or throw on Supabase errors | PR #1080 now fails readiness closed for returned and thrown dependency failures, preserves recognized actionable messages, and prevents raw dependency-error disclosure. | 2026-07-22 | +| #044 | issue | Publication approval was not bound to immutable reviewed state | PR #1081 added a canonical reviewed-state digest, row locks, active-job rejection and a new forward migration with replay/schema/type/drift evidence. | 2026-07-22 | +| #045 | issue | Bulk reindex discarded partial-success results | PR #1084 reserves preflight conflicts for non-2xx responses; completed mixed batches return per-item success/failure/missing results, and the UI refreshes successful work. | 2026-07-22 | +| #046 | issue | DOCX extraction lacked explicit resource budgets | PR #1085 added pre-inflate declared-size checks and post-read fail-safes for artifact count, per-artifact bytes, aggregate media, Word XML and extracted UTF-8 text. | 2026-07-22 | +| #047 | issue | XLSX extraction could construct unbounded results | PR #1086 bounds worksheets, non-empty rows, rendered cells and UTF-8 output while preserving sparse-column rendering. | 2026-07-22 | +| #048 | issue | Account copy overstated sync/privacy and enabled unavailable SSO | PR #1087 now maps copy to actual favourites/preferences persistence, identifies browser-session recents, removes the contradictory "never shared" claim and clearly disables unavailable providers using the accessible placeholder contract. | 2026-07-22 | +| #049 | issue | Process diagnostic exposed a Cursor worker API key | The exact worker was stopped, the key was revoked server-side, both local encrypted worker-secret records were removed, and authorized repository/backup scans found no plaintext copy. Follow-up guardrails now prevent repository process inventory from serializing command lines and redact heavyweight-lock command text before persistence or errors. | 2026-07-23 | +| #050 | issue | Next.js 16.2.10 remained in a high-severity security range | Upgraded `next` and `@next/env` to 16.2.11, regenerated the npm lockfile, confirmed the production dependency audit is clean, and passed focused framework checks, `verify:cheap`, and the full Chromium UI gate. | 2026-07-23 | +| #116 | issue | Missing CI signal on conflicted PRs | RESOLVED 2026-07-30: `.github/workflows/pr-mergeability.yml` checks trusted `pull_request_target` events and refreshes unchanged heads after protected-base pushes. A conflicted PR gets a red `PR mergeability` check naming the conflict instead of an empty CI list. The base-push job alone has scoped `checks: write`; neither path checks out PR code or updates branches. Classifier: `scripts/pr-mergeability.mjs`; contract: `npm run check:pr-mergeability`. Behind-but-clean remains `sync:pr-branches`. | 2026-07-30 | +| #140 | issue | Union-driver table duplication — duplicate of #133 | CLOSED 2026-07-30 as a duplicate, superseded by `#133` and resolved by PR #1444. This row was opened mid-session for the union-driver damage before its author noticed `#133` had already recorded the same finding — and recorded it _earlier_, with the same conclusion ("**Do not** apply a `merge=union` driver — tested 2026-07-30 and it is worse"). Keeping both would have left two open rows describing one condition, and that condition no longer exists: PR #1444 removed `merge=union` from `.gitattributes`, inverted `check:outstanding-issues` to require an unspecified `merge` attribute, and added regression tests covering `union`, `-merge` and an unparsed reading. The surviving evidence — four merges on PR #1430 each reporting success while duplicating the entire open-items table — lives in `#133`, whose still-open half is the real conflict-frequency cause: fixed-width column padding makes any one-row edit re-pad every row, so git sees the whole table as one hunk. Read `#133`, not this. | 2026-07-30 | From 16eb35368d4cf3319fb60502c096ac1702b96537 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Jul 2026 17:50:53 +0000 Subject: [PATCH 6/6] issues: restore the #146 data points lost while preserving the #149 record MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The two Services viewport-anchor data points from PR #1457 were dropped when the withdrawn #149 row and its id allocation were restored. They are unrelated to that decision, so this puts them back and changes nothing else. #149 stays exactly as set: archived as a withdrawn record, with issues:next-id preserved at 150 so the id is retired rather than reused. Restored to #146: the test failed once more on head c739340 (anchorTop expected -138, received -7) then passed on 9da02d9 and a6f2281 across all three shards with the spec byte-identical — six data points, two failures, shard 1 only, both failures on a mockups-only PR. Notes that the 131px delta is roughly 2x the 64px viewport shrink rather than sub-pixel drift, which constrains the element attribution that row already asks for. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01JdPa3mHCX5ZQZZvU5GHU3r --- docs/outstanding-issues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/outstanding-issues.md b/docs/outstanding-issues.md index 95d2c89e03..ce899069e0 100644 --- a/docs/outstanding-issues.md +++ b/docs/outstanding-issues.md @@ -163,7 +163,7 @@ removed after current-main verification; it is not missing recommended work. | #139 | P3 | rec | `changes` job computes outputs nothing consumes, and over-triggers coverage | **Outcome:** the change-scope contract says what it means. **Detail:** `changes` exports `source_changed`, `workflow_changed`, `changed_files` and `rag_eval_changed` and **no job reads any of them** — only `rag_eval_changed` is documented as intentionally advisory (`ci-change-scope.mjs`); the other three read as live wiring. Separately `coverage_changed` is derived as _any non-doc file_, so a workflow-only edit runs the ~4 min coverage job. **Next:** delete or document the dead outputs; decide whether coverage should narrow. Low value alone — bundle with the next `ci.yml` change rather than minting a PR. **DONE 2026-07-30:** `source_changed`, `workflow_changed` and `changed_files` are no longer job outputs. The script still computes all three — `docs_only` derives from the first two — they were simply exported and read by nothing. The coverage-breadth half is untouched and stays open. | `.github/workflows/ci.yml`; `scripts/ci-change-scope.mjs`; session 2026-07-30 | 2026-07-30 | | #141 | P2 | issue | Production `--clinical-accent-strong` and mockup hover tokens are undefined | **Outcome:** every static `var(--token)` a component reads either resolves from `@theme`/globals or is documented as runtime-set. **Detail:** the 2026-07-30 `/design-sync` re-sync surfaced `[TOKENS_MISSING]` from `package-validate.mjs`. Triage: `--med-accent` / `--med-accent-border` are **not** defects — `medicationAccentStyle()` in `src/components/clinical-dashboard/medication-record-page.tsx:88-94` sets both on the ancestor at `:393`, so every referenced class resolves at runtime. The genuine production gap is `--clinical-accent-strong` at `src/components/clinical-dashboard/answer-status.tsx:252` (no stylesheet or React `style` definition, no fallback — declaration dropped at parse time). Two more sit in gate-exempt scratch (`--primary-hover`, `--success-hover` in `src/components/favourites-page-mockups/favourites-library-redesign-page.tsx`). Same defect family as the dead `--text-4xs` classes repaired in `39f2bcea7`; invisible to every static gate that only inspects stylesheets (`#094`). **Next:** map `--clinical-accent-strong` onto an existing token (likely `--clinical-accent` / a stronger mix; confirm against `docs/redesign/permanent-colour-direction.md`) or add it to `@theme` — never a hex literal (`eslint-rules/no-hardcoded-hex.mjs`). Optionally define or drop the mockup hover tokens. **Verify:** `npm run verify:cheap`, then `npm run ensure` + `npm run verify:ui`, and confirm answer-status accent text renders coloured. **Stop:** do not "fix" `--med-accent` / `--med-accent-border` (runtime-set), `--mobile-composer-reserve` (runtime-set via `var(…, 0rem)`), or `--x` (Tailwind scan of prose in `docs/redesign/03-decision-log.md`); all are expected in `.design-sync/NOTES.md`. | session 2026-07-30 design-sync re-sync (PR #1443); `package-validate.mjs` `[TOKENS_MISSING]` | 2026-07-30 | | #145 | P3 | issue | A branch sync during in-flight CI cancels a near-complete run, and nothing warns | **Outcome:** a behind-but-passing PR is not re-synced into a CI restart loop. **Observed 2026-07-30 on PR #1454:** three `Update-branch` merges in ~20 minutes (`d49ac0f`, `6de2bf5`, `d9598b7`) each landed while that head's CI was mid-flight, so `cancel-in-progress` cancelled `Build` + `Unit coverage` three times and restarted a full cycle each time; no run finished, so armed auto-merge never fired until the queue quietened. Every check that got time to complete passed, twice over — the PR was never broken. **Gap:** AGENTS.md's anti-churn rule ("assemble every commit before the first push, or wait for the current run to settle") addresses _your own_ pushes only. It says nothing about an operator/`update-branch` sync landing on a head whose CI is already running, which costs the same cancelled cycle and is the case that actually recurs — `#1406`'s ~40%-cancellation measurement is the same phenomenon from the push side. Distinct from `#095` (which is the _reporting_ of cancelled jobs, and worked correctly here: `PR required` said "CANCELLED with no failing job … look for a newer run on the PR's current head") and from `#129`/`#116` (genuine-vs-apparent conflict). **Next:** extend the anti-churn guidance to cover syncs — before an `update-branch`/`git merge origin/main` on an open PR, check whether that head has a run in flight and let it settle first unless the branch is genuinely blocking-behind; prefer syncing once, late, over repeatedly. **Stop:** do not "fix" this by disabling `cancel-in-progress` — superseded runs should still be cancelled; the cost is in _when_ the sync lands, not in the cancellation policy. | session 2026-07-30; PR #1454 runs `30546286820`, `30546667904`, `30547100273`; AGENTS.md § Anti-conflict and CI-speed operating procedure | 2026-07-30 | -| #146 | P2 | issue | `ui-phone-scroll` Services result anchor jumps on viewport shrink under CI load | **Outcome:** the required `Production UI` job stops failing on a test the PR did not touch. **Evidence 2026-07-30 (PR #1427 run `30534158395`, job `90844138237`, shard 1 of 3):** `tests/ui-phone-scroll.spec.ts:973` "Services results keep a continuous browser viewport after shared chrome releases" failed at `:1133` — `afterViewportResize.anchorTop` did not stay close to `hidden.anchorTop` across the viewport shrink. `120 passed (6.5m)`, classified "needs investigation". **Intermittent on unchanged code:** the spec is byte-identical across heads `79dd28f`, `76f80f3` and `17c8cb9`; shard 1 passed, passed, failed, then passed again on re-run — four data points, one failure. **Not #127 and not fixed by #1427:** this head already carried #1427's runway poll and travel assertion, and the failing assertion is result-content anchor stability across a resize, not the chrome hide. #127 is the reduced-motion hide on the document route; this is the Services result canvas. **A narrowing claimed here was WITHDRAWN 2026-07-30 (Codex, PR #1446):** an earlier version of this row argued that the sibling `documentScrollTop` assertion did not fail, so the scroll position must have held. That inference is invalid — Playwright aborts a test at the first failing `expect`, so once the `anchorTop` assertion threw, the `documentScrollTop` assertion never executed. Its absence from the output shows nothing, and scroll-restoration causes are NOT ruled out. **Next:** capture `anchorTop` and `documentScrollTop` either side of the resize from a CI-load run, and check whether the chrome release and the viewport change can interleave. **Stop:** do not add to `tests/flake-ledger.json` — that ledger takes reproduced `@quarantine` entries only, and this reproduces once in four. Do not loosen the `toBeCloseTo` tolerance; the anchor stability is the assertion's whole point. | PR #1427 run `30534158395`; session 2026-07-30 | 2026-07-30 | +| #146 | P2 | issue | `ui-phone-scroll` Services result anchor jumps on viewport shrink under CI load | **Outcome:** the required `Production UI` job stops failing on a test the PR did not touch. **Evidence 2026-07-30 (PR #1427 run `30534158395`, job `90844138237`, shard 1 of 3):** `tests/ui-phone-scroll.spec.ts:973` "Services results keep a continuous browser viewport after shared chrome releases" failed at `:1133` — `afterViewportResize.anchorTop` did not stay close to `hidden.anchorTop` across the viewport shrink. `120 passed (6.5m)`, classified "needs investigation". **Intermittent on unchanged code:** the spec is byte-identical across heads `79dd28f`, `76f80f3` and `17c8cb9`; shard 1 passed, passed, failed, then passed again on re-run — four data points, one failure. **Not #127 and not fixed by #1427:** this head already carried #1427's runway poll and travel assertion, and the failing assertion is result-content anchor stability across a resize, not the chrome hide. #127 is the reduced-motion hide on the document route; this is the Services result canvas. **A narrowing claimed here was WITHDRAWN 2026-07-30 (Codex, PR #1446):** an earlier version of this row argued that the sibling `documentScrollTop` assertion did not fail, so the scroll position must have held. That inference is invalid — Playwright aborts a test at the first failing `expect`, so once the `anchorTop` assertion threw, the `documentScrollTop` assertion never executed. Its absence from the output shows nothing, and scroll-restoration causes are NOT ruled out. **Next:** capture `anchorTop` and `documentScrollTop` either side of the resize from a CI-load run, and check whether the chrome release and the viewport change can interleave. **Stop:** do not add to `tests/flake-ledger.json` — that ledger takes reproduced `@quarantine` entries only, and this reproduces once in four. Do not loosen the `toBeCloseTo` tolerance; the anchor stability is the assertion's whole point. **Two further data points 2026-07-30 (PR #1457):** failed once more on head `c739340` (run `30551018893`, shard 1) with `anchorTop` expected `-138`, received `-7`, difference `131`, `120 passed`; then passed on heads `9da02d9` and `a6f2281` across all three shards with the diff byte-identical. That is six data points, two failures, still shard 1 only — and both failures landed on a PR touching nothing but `src/app/mockups/**` plus one mockup test, which strengthens the unchanged-code reading. The magnitude is worth noting for the capture suggested above: `131` is roughly 2x the 64px viewport shrink, not a sub-pixel drift. | PR #1427 run `30534158395`; session 2026-07-30 | 2026-07-30 | | #147 | P2 | rec | Mobile CLS breaches four routes, and it reproduces exactly offline | **Outcome:** mobile CLS is under 0.1 on every measured route, or each remaining breach has a recorded reason. **Measured 2026-07-30 with the LOCAL OFFLINE production harness** (`CHROME_PATH=/opt/pw-browsers/chromium npm run verify:lighthouse -- --keep`; Lighthouse 12.8.2, Chromium 141.0.7390.37, demo corpus, inert loopback Supabase). Mobile CLS: `/dsm` 0.363, `/documents/search` 0.220, `/therapy-compass` 0.142, `/` 0.023 — `/forms` did not measure locally (Lighthouse `NO_NAVSTART`, its own "run again" transient), but the live dispatch put it at 0.212. Desktop passes everywhere: 0.016–0.097. **The finding that matters is that these reproduce the live production dispatch EXACTLY.** Against run `30548662649` on `psychiatry.tools`, all four locally measurable mobile routes match to three decimals — 0.023 / 0.142 / 0.220 / 0.363 on both — and the desktop range matches at both endpoints, 0.016–0.097. So CLS on these routes is deterministic layout structure, not network, latency, or production data. Three consequences: (a) the CLS half of `#017` is debuggable and gate-able locally at zero provider cost; (b) `#017`'s recorded blocker "prod server hard-requires Supabase secrets" is stale — `scripts/run-lighthouse-budget.mjs` builds and serves an offline production app precisely to sidestep that, and its earlier note that dev-mode CLS looked "excellent at 0.00–0.04" was measuring DEV, which does not reproduce production; (c) re-dispatching the live workflow to re-measure CLS buys nothing. **LCP does NOT reproduce and must not be read from local runs:** local mobile LCP is 2014–2051 ms against 3639–4716 ms live, because the loopback server has no network latency. The LCP _ranking_ does survive — `/therapy-compass` is the outlier both locally (4978 ms, ~2.4x every other route) and live — which corroborates `#117` rather than replacing it. Mobile TBT, local: `/documents/search` 581 ms, `/dsm` 448, `/therapy-compass` 379, `/` 338. **Ranking by measured contribution, which is what `#017` asked for:** 1. `/dsm` CLS 0.363 (3.6x the threshold, the worst single number anywhere); 2. `/documents/search` 0.220; 3. `/forms` 0.212 (live only); 4. `/therapy-compass` 0.142, and separately the sole LCP outlier via `#117`; 5. `/` 0.023, which passes and needs nothing. **Next:** attribute the shifts to elements. Lighthouse's `layout-shift-elements` audit returned **zero items on every route** — the `cumulative-layout-shift` audit carries only `debugdata` — so attribution needs a driven Chromium session with a `PerformanceObserver` on `layout-shift`, reading `entry.sources[].node` and `entry.value`, against the same offline production build. Start with `/dsm`. **Gate — `#118` owns it, and this row supplies a constraint it was missing.** `#118` already tracks flipping `lighthouse-budget.json` to `enforce: true` and says not to do it "before `#117` or the baseline pins a known-slow route". These numbers make that concrete: pinning a baseline today would bake CLS 0.363 on `/dsm` in as the accepted value, so the CLS fixes must land first or the gate ratifies the breach. Separately, no baseline could have been committed from this run anyway — the grader correctly refused it as incomplete evidence because `/forms` produced no report, and that refusal was not overridden. **Reproduction trap:** without `CHROME_PATH`, `chrome-launcher` cannot find a browser in this container and every route fails; the harness reads `CHROME_PATH`/`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH` (`run-lighthouse-budget.mjs:291`) but nothing sets either, even though `PLAYWRIGHT_BROWSERS_PATH` is set. Related to `#121`. Also seen: a truncated `progress` package in the npx cache broke Lighthouse startup entirely; clearing the npx cache entry fixed it. **Stop:** do not re-dispatch the live Web-Vitals workflow to re-measure CLS — it is reproducible offline for free. Do not commit a budget baseline from a run with a failed route. Do not treat local LCP numbers as production LCP. | `#017` live run `30548662649`; local `verify:lighthouse` 2026-07-30; `lighthouse-budget.json` | 2026-07-30 | | #148 | P2 | issue | Rows can be deleted from the issues ledger and no gate notices | **Outcome:** a row cannot leave `docs/outstanding-issues.md` without being archived, and a gate catches it if one does. **Observed 2026-07-30 on `main`:** commit `d8b034c8` (PR #1441, "guard upload limits in production builds") deleted **three** rows from the Open items table and added **zero** rows to the archive — `#085` (genuinely resolved by that same PR, which added `scripts/check-upload-limit-parity.mjs`), `#119` (a duplicate of `#122`), and `#105` (**open and unresolved** — its `LoadingPanel` half was and is unverified). Two of the three were dispositionable; none was dispositioned, and the third should not have been touched at all. The file's own Conventions section says of the archive: _"Move resolved rows here with the resolution date and a one-line outcome. Keep them — do not delete."_ There is precedent for doing it properly even for duplicates — `#140` was archived as "CLOSED as a duplicate, superseded by `#133`" rather than removed. **Why nothing caught it:** `npm run check:outstanding-issues` validates duplicate ids, an id present in both tables, a marker at or below the highest id, malformed rows, and missing headings — every shape the 2026-07-29 triple collision took (`#112`). It does **not** compare against the previous revision, so a row that simply disappears is invisible to it, in `verify:cheap` and in `static-pr` alike. **Likely mechanism, and why this file is the one it happens to:** `#133` deliberately left this file without a `merge=union` driver so overlapping edits conflict loudly instead of being silently concatenated. That is the right design, but it means every sync is a hand-resolution, and taking one side wholesale silently drops the other side's rows — exactly what AGENTS.md forbids for this file. This branch hit five consecutive genuine conflicts on it, and `#105` only survived because each resolution was done row by row and diffed with column padding normalised away afterwards. **Next:** add a deletion check — compare the id set against the merge base (or `origin/main`) and fail when an id present there is absent from both tables, unless the same commit archives it. That is a few lines in `scripts/check-outstanding-issues.mjs` and closes the one shape `#112`'s guard left open. **Stop:** do not fix this by adding a union merge driver — `#133` removed that deliberately and union merge would reintroduce the duplicate-row damage it was removed to stop. The fix is detection, not merge policy. | `main` commit `d8b034c8` (PR #1441); `scripts/check-outstanding-issues.mjs`; `#112`, `#133` | 2026-07-30 |