diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 52016f8894..796f718a73 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -252,3 +252,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 #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 #1480 | 6c1e76f53aee87be8408cebc295744fbdce05367 | PR #1480 bounded outstanding reliability fixes | Fixed both review findings: documented the dark accent role and added partial favourites retry without hiding valid counts; no other actionable defects found. | focused Vitest 119 passed; docs index; issue and ledger guards; Actions and Codex workflow guards; Prettier; diff check; typecheck coordinator-blocked | +| 2026-07-30 | codex/close-pr1480-issues | bf8ac88b024642eb45d1fead86f4ee30fce3f98d | archive PR 1480 issue resolutions | approved: five resolved rows moved intact to archive | check:outstanding-issues; prettier check; diff check | diff --git a/docs/outstanding-issues.md b/docs/outstanding-issues.md index ec74bf1c5d..1d89933df3 100644 --- a/docs/outstanding-issues.md +++ b/docs/outstanding-issues.md @@ -129,7 +129,6 @@ removed after current-main verification; it is not missing recommended work. | #079 | P3 | task | Disposition retained worktrees in bounded cleanup batches | **Outcome:** the retained reconciliation tail is gradually classified without another disruptive all-worktree sweep. **Next:** after the primary checkout is clean and `npm run check:primary-checkout-lease` allows writes, revalidate and remove the twenty clean redundant candidates recorded on 2026-07-30 with `branch-cleanup-deletion-pending`; then process no more than ten further worktrees per explicitly scheduled pass using current owner/process metadata, open-PR state, exact review-ledger coverage, ancestry, and cherry-pick-aware content proof. **Success:** remove only clean, inactive, bundled worktrees whose content is merged or explicitly rejected; record every disposition and retain recovery evidence. **Stop:** preserve dirty, active, secret-bearing, post-freeze, paused, or ambiguous work and never use reset, force deletion, broad clean, or process killing. | final reconciliation inventory retained 104 independent worktrees; session 2026-07-24; 2026-07-30 bounded review found 20 redundant candidates across two bounded batches but the primary-dirty write lease blocked removal | 2026-07-30 | | #086 | P3 | task | Repository maturity backlog — remaining structural work | **Outcome:** the deferred repository-maturity backlog ships as verified draft PRs, one per structural change. **Canonical runbook:** [`docs/maturity-backlog-workorders.md`](maturity-backlog-workorders.md). **Remaining:** X3 `rag.ts` decomposition (in progress); X7 finish the `src/lib` domain reorg; X6 clinical/retrieval/answer coverage floors; X5 ACL-migration consolidation (provider-gated); L1 archive the retired `backfill:*` one-shots + the dead `ci-change-scope` token; M1 repo-host hardening (maintainer, audit §8). **Shipped:** L4 ledger rotation (#1418 — `ledger:rotate`, live/archive corpus, `merge=ledger`). **X3 progress:** the evidence coverage gate shipped as `src/lib/rag/rag-coverage-gate.ts` (PR #1454, squashed `102bb1f`) — `evaluateEvidenceCoverageGate` + `applyCoverageGateTelemetry` moved byte-identically, `rag.ts` 5,030 → 4,780, budget ratcheted to 4,780, no back-edge, `evaluateEvidenceCoverageGate` still re-exported from `@/lib/rag/rag`. **Next X3 unit — `rag-hydration.ts`:** the hydration cluster is `createDocumentRankingMetadataCache` / `attachDocumentRankingMetadata` / `withCachedIndexQuality` / `attachIndexQualityMetadata` / `attachPageVisualEvidence` plus the `DocumentRankingMetadataCache` type (`rag.ts:1487-1718` as of `102bb1f`). **It does NOT on its own unblock `prepareCoverageGateResults`** (corrected 2026-07-30 after a Codex finding on PR #1461 — the earlier claim that it re-homed all five dependencies was wrong). That function needs five `rag.ts`-only runtime symbols, and hydration accounts for only two of them (`attachDocumentRankingMetadata`, `attachPageVisualEvidence`). The other three sit outside the cluster and are a separate seam: `selectRankedRetrievalResults` (`rag.ts:1825`, retrieval selection), `applySecondStageRerankIfNeeded` (`rag.ts:679`, second-stage ranking), and `measureSearchPhase` (`rag.ts:1975`, the shared pipeline timing wrapper — 21 references across the file, of which only `metadata_hydration` and `visual_hydration` are hydration phases, so it belongs with the search orchestrator/telemetry, not with hydration). So `prepareCoverageGateResults` can only move after BOTH hydration and that ranking/timing seam are re-homed; it stayed in `rag.ts` for #086 because a back-edge and a signature change were both refused. Verify with a symbol-location plus call-site search before planning the boundary — do not treat hydration alone as sufficient. **Next:** remaining X3 units on user go-ahead. **Stop:** RAG/retrieval items need the flag + go-ahead; X5 is live-DB provider-gated. | `docs/maturity-backlog-workorders.md`; audit §8/§10; session 2026-07-28 | 2026-07-28 | | #090 | P2 | task | Upgrade the eslint ecosystem to clear remaining dev-scoped high advisories | **Outcome:** full `npm audit` reports zero high advisories from the eslint toolchain. **Blocked 2026-07-30:** the stable ecosystem still has no compatible ESLint 10 set. `eslint-config-next@16.2.12` permits ESLint 10 but bundles `eslint-plugin-react@7.37.5`, `eslint-plugin-import@2.32.0`, and `eslint-plugin-jsx-a11y@6.10.2`; each plugin's published peer range still ends at ESLint 9, and the React plugin retains the previously reproduced removed-context-API crash. Keep the Dependabot major hold and ESLint `9.39.5`; do not force an invalid peer graph merely to make the audit report green. **Next:** recheck after those three plugins publish stable ESLint 10 support, then upgrade eslint and the complete plugin/config set together. Residual highs (`@eslint/config-array`, `@eslint/eslintrc`, `eslint`, `eslint-config-next`, `eslint-plugin-import`, `eslint-plugin-jsx-a11y`, plus the advisory's numeric `<=5.0.7` hit on the unused `brace-expansion@1.1.16` / `2.1.2` maintenance lines that still ship an unpatched `main`) cascade from this dev-only toolchain. **Success:** peer-valid install, `npm run lint` clean, `verify:cheap` green, full-audit highs cleared, no rule-config regressions. **Stop:** do not use `npm audit fix --force` or override plugin peer ranges. Production `npm audit --omit=dev` is already clean after the exceljs `archiver@8` / `unzipper@0.12.5` overrides on PR #1314. | stable npm metadata recheck 2026-07-30; session 2026-07-28 brace-expansion triage (PR #1314) | 2026-07-30 | -| #091 | P2 | issue | Results band cannot express a partial-source failure | **Outcome:** a favourites/results count is never asserted while some of its sources failed to load. **Detail:** `SearchResultsBandStatus` is a single flat status, so a page fed by several registries has no way to say "3 loaded, 1 failed". `saved-registry-favourites-status.ts:29` works around this with `itemCount > 0 && folded !== "ready" ? "ready" : folded`, and `favourites-command-library-page.tsx` applies the same mask a second time — so a partial failure renders a confident count with no fault indication, which is the exact defect class PR #1316 removed everywhere else. Neither favourites page consumes the true `registryStatus` the fold already returns. **Next:** decide between (a) a `partial` arm on the status union carrying a "some sources unavailable" note beside an honest count, or (b) surfacing `registryStatus` as a separate inline notice and dropping the mask. **Stop:** do not widen the mask to more surfaces before the shape is decided. | PR #1316 review thread PRRT_kwDOSh5Fis6UUf-k; session 2026-07-28 | 2026-07-28 | | #092 | P3 | task | Refetch pulse deferred on auth-backed registries (privacy invariant) | **Outcome:** a background refresh keeps the prior count visible instead of a skeleton, without weakening identity clearing. **Detail:** the `refetching` status is built in the band and adopted only on `formulation-home-page.tsx`, where the lag is `useDeferredValue` over static data. It is deliberately NOT adopted on `use-registry-records.ts:85`, `use-medication-catalog.ts:76` or `use-differential-catalog.ts:133`, which all clear data on entering loading. `use-differential-catalog.ts:122` states why: "Auth must clear prior identity's matches immediately", and `:164` that "a later retype of any prior query cannot resurrect authorized matches." **Next:** if adopted, guard preservation on identity AND query equality, and pin with a test that an identity change still clears immediately. **Stop:** never hold records across an auth transition. | PR #1316 plan phase 6; session 2026-07-28 | 2026-07-28 | | #093 | P2 | issue | Next streaming `S:` clone causes Playwright strict-mode violations under CI load | **Outcome:** duplicate-element strict-mode failures stop appearing on loaded CI runs. **Detail:** under full-suite CI load Next.js leaves a hidden duplicate page root in the stream, so a `getByTestId` that is unique locally resolves to 2 elements in CI (seen as `differentials-search-results` on PR #1316, and previously noted on PR #1294 against main). It does not reproduce in isolation, on a single spec, or locally. The documented workaround is to scope the locator to the visible root. **Reproduced locally 2026-07-28** (isolated _production_ build via `run-playwright.mjs`, full `verify:ui`): `ui-tools.spec.ts:563` duplicated `forms-home` and `ui-smoke.spec.ts:3001` duplicated `favourite-row-lithium-monitoring-guideline`; in both, copy 1 is nested under `mobile-composer-reserve-pad`. Both pass when run alone, so it is load/order-dependent, not build-mode dependent — this also corrects an earlier note that CI uses `next dev`; it does not. **Strongest evidence (CI run `30345484316`, 2026-07-28): `ui-overlap.spec.ts:199` on `/` asserted `toHaveCount(1)` successfully and then the same `header#search` locator resolved to 2 a statement later, one of them hidden.** A duplicate that appears _after_ a passing count assertion is a stream/hydration artifact by construction, not a static double mount and not something a CSS or component change can cause. That makes four distinct testids across four specs with the identical shape. **Mitigated, not fixed, on `main` (2026-07-28):** `3a8edb93` rewrapped `gotoHome` in `tests/ui-overlap.spec.ts` to retry count-and-visibility together via `toPass`, so a transient second header no longer trips strict mode there — its own note says "checking count then immediately calling waitFor races that flicker into a strict-mode violation". That hardens one helper; the duplicate root itself is unchanged and other specs remain exposed. **Confirmed pre-existing:** at `631d90d2`, the commit before PR #1316's first commit, that spec already documented "two `header#search` nodes" and "a second transient `header#search` can exist briefly" — so this predates that branch. **Next:** with a full-suite repro now available, bisect the preceding specs to find the state that triggers the second mount, then either scope the shared helpers to the visible root once or fix the mount. **Stop:** do not paper over new occurrences with `.first()` before the duplicate itself is explained. | PR #1316 CI runs; PR #1294 note on main; session 2026-07-28 | 2026-07-28 | | #094 | P2 | rec | Design-system gates assert structure, not rendered effect | **Outcome:** a style contract cannot pass while the style is inert. **Detail:** PR #1316's accent rail shipped inert because `.search-band` sat in `@layer components`, which loses to Tailwind's utilities layer regardless of specificity — and the test asserted `toHaveClass("search-band")`, i.e. class presence, not effect. Computed style showed `1px rgb(229,231,235)` where `2px rgb(11,111,134)` was intended. The same shape of gap let a rail-colour assertion compare a colour against a width and pass unconditionally. **Next:** for contracts where the visual IS the requirement (rails, forced-colors thickness, tap targets), assert `getComputedStyle` in a Playwright case rather than class names in a DOM test, and add the unlayered-component convention to the design-system contract check. **Stop:** do not convert existing passing DOM tests wholesale; add computed-style proof only where the effect carries the meaning. | PR #1316 Codex P2 finding; session 2026-07-28 | 2026-07-28 | @@ -148,20 +147,16 @@ removed after current-main verification; it is not missing recommended work. | #117 | P2 | rec | Therapy Compass catalogue payload is the mobile LCP outlier | **Outcome:** `/therapy-compass` mobile LCP lands near the other mobile routes instead of double them. **Measured 2026-07-30** by the new pre-merge Lighthouse budget: mobile LCP 5229 ms, TBT 612 ms, CLS 0.142, against 2123-2460 ms on every other mobile route and 826 ms on desktop — so it is client-side work under mobile CPU/network throttling, not server latency. **Cause:** `useTherapyData` fetches `public/therapy-compass-data/therapies-index.json` (690 KB raw, 139 KB gzipped, 205 records x 16 fields) for the home/search/pathways screens, so the download plus JSON parse sits on the critical path before content paints. 90% of that weight is long-form clinical prose — indications 159 KB (26%), contraindicationsOrCautions 139 KB (23%), bestUsedFor 73 KB (12%), clinicalSummary 67 KB (11%), patientPopulation 59 KB (10%), targetSymptoms 48 KB (8%) — while name, slug, category, tags and setting together are 54 KB (7%). **Blocked on one decision per field group: rendered on the card, matched by search, or neither.** `therapy-card.tsx` references five of those prose fields and the same index feeds the search screen, so stripping fields could silently change clinical display or search recall. **Next:** settle that per-field question, then either pre-truncate prose that only feeds card display, or move search matching server-side / load prose on first keystroke. **Gate:** `check:therapy-data-index` plus the therapy Playwright journeys; re-measure with `npm run verify:lighthouse`. **Stop:** do not drop a field from the catalogue payload without confirming no card renders it and no search path matches on it. Same class as #013 (route-chunk / catalogue JSON weight), different route and now measured. | session 2026-07-30 Lighthouse budget first run; PR #1404 | 2026-07-30 | | #118 | P2 | task | Adopt the visual and Lighthouse baselines so the two new gates actually gate | **Outcome:** `visual-baseline` and `lighthouse-budget` stop reporting and start blocking. **Detail:** PR #1404 added both as `continue-on-error` jobs outside `pr-required`, deliberately. `tests/ui-visual-baseline.spec.ts` has no committed baselines, so all six targets fail with a missing-snapshot error by design; the job uploads them on every run (run 30513537912, artifact 8748062487, 31 files). `lighthouse-budget.json` ships `enforce: false` with `baseline: null`, so the grader warns rather than grades. **Next:** (1) download that artifact, review the six PNGs and commit them under the platform-scoped screenshots directory that `playwright.visual.config.ts` names in its `snapshotPathTemplate` — from CI, never a developer machine, because font hinting differs between them; (2) run `npm run check:lighthouse-budget -- --update` against a known-good CI build and flip `enforce`, but not before #117 or the baseline pins a known-slow route; (3) then add each job to `pr-required` and drop `continue-on-error` in the same edit. **Also:** PR #1404 added the first rendered-effect contract for #094, but 37 of the 38 unlayered visual classes still carry exemptions in `tests/helpers/style-contracts.ts` rather than contracts; and `scripts/run-lighthouse-budget.mjs` duplicates about 50 lines of the isolated-server boot in `scripts/run-playwright.mjs`, deferred to avoid destabilising the required UI gate in the same change. **Stop:** do not make a missing baseline skip instead of fail — that is the soft-skip-green pattern `AGENTS.md` forbids. | session 2026-07-30; PR #1404 | 2026-07-30 | | #127 | P2 | issue | Document-route phone header did not hide; likely a short drag, fixed by #1427 | **Outcome:** the document-route phone header hides when asked, and a failure names its own cause. **Observed:** `Production UI` failed on `30521269873` (1 variant) and `30524711173` (both variants, `2 failed, 339 passed`), always at the reduced-motion hide after the section-sheet round-trip, then **passed** on `30530393684` — intermittent, 2 of 3 completed runs. **Cause most likely already fixed on main by PR #1427 (2026-07-30), not by anything in this row's earlier diagnoses.** #1427 found that `addPhoneScrollRunway` slept 50 ms and merely hoped the appended 1600 px runway had reached layout, and that `dragScrollBy` clamped silently at the end of the range while reporting nothing — so under CI load the drag delivered less than it asked for and the chrome was right to stay visible. It now polls for the runway, returns the distance actually travelled, and `dragScrollUntilHidden` refuses to expect a hide until both the remaining runway and the delivered travel clear 160 px. **Two earlier diagnoses in this row were wrong and are withdrawn:** (1) `sharedChromePinned` is stuck — rested on reading DocumentViewer's composer as a proxy for the header's `scrollHidden`, which it is not, since the header runs the shell's `chromeScrollHide` and DocumentViewer runs its own reporters; (2) a short/clamped drag was ruled out early using a `maxOffset` of 2753 measured at a different moment in the trace than the failing drag — the pre-runway reading in the same trace was 1153, and a runway that had not fully landed puts the offset inside the near-bottom band where `computeScrollHideUpdate` legitimately refuses. That is the mechanism #1427 fixes. **Still open here:** #1427's own comment notes the remaining gap — `data-scroll-hidden` is the composite `scrollHidden && !sharedChromePinned`, so a reporter that never fired and a latched pin are indistinguishable. `data-scroll-signal` now publishes the raw signal and `expectChromeHidden` reports it, so if this recurs after #1427 the failure says which. **Next:** watch for a recurrence on a post-#1427 head; if none appears, close this as fixed by #1427. **Stop:** do not infer the header's scroll state from any page-owned composer, and do not read a `maxOffset` from one trace moment as the value at another. **No recurrence across four post-#1427 runs** (`30530618838`, `30532582652`, `30534158395`, `30535651470`): this test passed in every one, including the run whose shard 1 failed on a different test (#141). Per this row's own next step, that is the evidence for closing it as fixed by #1427 — held open only for a wider sample on main. | `tests/ui-phone-scroll.spec.ts`; PR #1427; `master-search-header.tsx`; `global-search-shell.tsx:332,345,876` | 2026-07-30 | -| #128 | P2 | issue | Draft-to-ready alone does not retrigger required CI | **Outcome:** un-drafting a PR is not by itself enough to get its full required-check suite running. **Detail:** on 2026-07-30 PR #1406 sat with only 3-4 minimal checks (`PR policy`, `GitGuardian`, `Supabase Preview`) for 30+ minutes after being marked ready for review, with `mergeable_state` clean (not conflicted — distinct from #116). `.github/workflows/ci.yml`'s `on: pull_request` block has no explicit `types:`, which defaults to GitHub's `[opened, synchronize, reopened]`; `ready_for_review` is not in that list, so converting a draft to ready fires no workflow run on its own. The full suite only started once an actual new commit landed (a `synchronize` event), e.g. syncing the branch with `main`. **Next:** if drafts routinely go ready without an accompanying commit, add `ready_for_review` to `ci.yml`'s `pull_request.types` list ALONGSIDE the current implicit defaults — `types: [opened, synchronize, reopened, ready_for_review]`, not a bare `[ready_for_review]`, which would replace the defaults and stop CI firing on ordinary `opened`/`synchronize`/`reopened` events. Otherwise document that un-drafting alone is insufficient and a session should push a no-op/sync commit to actually kick off CI. **Stop:** do not conclude a draft's CI is "just slow" from elapsed time alone — check whether it actually has the full check set (16-19 checks, not 3-4) before waiting further. | PR #1406; session 2026-07-30 PR babysit | 2026-07-30 | | #129 | P2 | issue | GitHub's `update-branch` API doesn't honor this repo's `merge=ledger` driver | **Outcome:** `update-branch` can report a 422 "merge conflict between base and head" on a branch that a local `git merge origin/main` resolves cleanly. **Detail:** on 2026-07-30 PR #1406's branch was several commits behind `main` and touched `docs/branch-review-ledger.md`, which carries `merge=ledger` in `.gitattributes` specifically so parallel ledger appends resolve without conflict (see #088/#112). GitHub's own server-side merge/update-branch implementation does not read `.gitattributes` custom merge drivers, so it computed a real textual conflict at the same hunk the local `ledger` driver resolves. `git merge-tree --write-tree origin/main ` confirmed clean; the API call still 422'd. **Next:** when `update-branch` fails on a branch touching `docs/branch-review-ledger.md` (or any other `merge=ledger` path) and `git merge-tree` shows no real conflict, treat it as staleness rather than a genuine conflict needing manual resolution and fall back to a local `git merge origin/main` + push (per the existing "Open PR branch sync" guidance) — same as any other push, this still needs the explicit user confirmation AGENTS.md's "API and provider confirmation boundary" requires outside an authorized sweep (`Run PR`/`upload`), not a standing exemption for `merge=ledger` paths. **Stop:** do not conclude a real content conflict from `update-branch`'s response alone on a custom-merge-driven file; verify with `git merge-tree` first — same discipline as the existing GitHub `dirty`/`CONFLICTING` staleness guidance. | PR #1406; session 2026-07-30 PR babysit | 2026-07-30 | | #130 | P2 | issue | PR #1396 merged shared phone-chrome behaviour without its own declared physical-device gate | **Outcome:** a shared-chrome PR does not merge with a self-declared merge prerequisite left undone, or the ledger records that it did. **Detail:** PR #1396 ("overlay the phone header so hiding it never moves content") repeatedly stated in its own body and PR comments that `docs/phone-chrome-physical-acceptance.md` "genuinely applies before merge" because local Chromium cannot certify Safari chrome-minimisation or cold-launch PWA paint (invariant 23) — restated at least three times across the review thread, including after the final `a638b66e`/`f7347144` fix. It merged at 06:49:55 anyway. Checked 2026-07-30: `docs/phone-chrome-physical-acceptance.md` on `main` is still the blank checklist template — every "Result / evidence" cell is empty, no PR comment attaches a filled-in copy or device evidence, and no existing ledger row (`#120`, `#122`) covers this gap. Related but distinct: one Codex thread on this PR also names a still-missing guard — a pre-paint/cold-load hydration test comparing content position before and after hydration, which the author explicitly said they would "rather file it than ship a test that looks like it covers the window and does not" — and that filing never happened either. **Next:** run the physical-device matrix in `docs/phone-chrome-physical-acceptance.md` against `main`'s current tip on a real iPhone (Safari tab + cold-launch PWA, light/dark, portrait/landscape) and commit the filled-in evidence; separately, add the pre-paint/cold-load Playwright pattern this PR's own review identified as missing. **Stop:** do not treat this PR's extensive Codex/CI remediation (13 findings fixed, 9 threads resolved) as a substitute for the physical-device proof — headless Chromium was explicitly stated as unable to certify the two things this checklist exists for. **Design constraints recorded 2026-07-30, so the guard is not re-derived from scratch:** the value under test is the pre-paint reserve seed `calc(max(0.5rem, var(--safe-area-top)) + var(--shell-header-h))` in `globals.css`, refined by `useLayoutEffect` in `use-phone-overlay-chrome-reserve.ts`. The window that needs covering is _before_ hydration, so the test must sample content top on the cold load and again after hydration and compare them; a single post-hydration read passes on the broken shape and is the "looks like coverage" outcome this item exists to avoid. The `max()` is the part that actually breaks: seeding the bare inset under-reserves by `max(0, 0.5rem − inset)`, which is **zero on a notched iPhone and 8px on any phone reporting no top inset** — Android, and Playwright's default emulation — so the assertion must run on a zero-inset profile or it cannot fail. Prove it against the broken shape before trusting it (re-seed with the bare inset and confirm the test goes red), per the lesson recorded on `#120`. **Environment blocker:** this cannot be verified in a remote container. The Chromium build mismatch in `#121` means browser tests never launch, and the documented symlink bridge writes under `/opt/pw-browsers`, which the session sandbox refuses — so this needs a local session, or an operator-granted exception, before any claim that the guard works. | PR #1396 (merged 2026-07-30); session 2026-07-30 PR babysit | 2026-07-30 | | #132 | P3 | issue | Both client-side push guards are inert for agent pushes | **Outcome:** the format and auto-merge guards protect every push, or their blind spot is explicit. **Detail:** `scripts/guard-push.mjs` printed `auto-merge: gh not available — auto-merge check skipped (fail-open)` for pushes from a remote agent environment, so the auto-merge race sentinel never evaluated; and `core.hooksPath` is set only by a local `npm install`, so an agent pushing from its own checkout bypasses `.githooks/pre-push` entirely. Both guards therefore protect exactly the environment least likely to break the rule, which is why the AGENTS.md format-before-push instruction is still load-bearing even though the tooling now exists. Observed directly on PR #1400: a push landed while auto-merge was armed with nothing to stop it. **Next:** provide `gh` (or a token-based equivalent) in agent environments so the sentinel can evaluate. **Do not move the format check into `pull_request_target`** — that context carries secrets and a write token, and a format check must execute PR-head code including this repo's now-loadable dynamic `prettier.config.*`, which is the classic privileged-context vector; `.github/workflows/pr-policy.yml` deliberately checks out only `github.workflow_sha` for exactly this reason. Formatting is already enforced server-side by `Static PR checks` running `format:check` on ordinary `pull_request` CI, so the guard's only unique value is failing fast before the push — nothing to duplicate. The auto-merge sentinel reads PR metadata only and could safely live in `pull_request_target` if it is ever worth moving. | PR #1400; session 2026-07-30 | 2026-07-30 | | #133 | P3 | rec | Ledger conflicts on nearly every `main` advance (union driver removed) | **Outcome:** two agents editing different rows of this ledger do not silently corrupt it. **Driver half RESOLVED 2026-07-30:** `merge=union` is removed from `.gitattributes` and `check:outstanding-issues` now requires that **no** driver is set, so a reappearance is a red gate (proved by reintroducing it: `must have NO merge driver (found merge=union)`). This row already recorded union as worse — "two sides each bumping the marker produce two `next-id` lines, corrupting the file silently where a conflict would fail loudly" — but the attribute stayed in place and the gate _mandated_ it, so the repo's own tested conclusion was contradicted by its own config. PR #1430 confirmed the cost at scale: four merges in one session, each reporting success while duplicating the **entire** open-items table (`#059 appears 2 times (lines 101, 166)` and so on), every one needing a manual rebuild from `origin/main`. Union also makes `git merge-tree` report a clean tree, so the pre-merge conflict check cannot warn. Unlike `docs/branch-review-ledger.md`, this file allocates IDs by read-modify-write, so concurrent appends need manual renumbering whatever the driver does — union bought nothing. `AGENTS.md`, `docs/process-hardening.md`, `.claude/skills/issues/SKILL.md` and `docs/scripts-index.md` are updated to match. **Still open — the conflict frequency itself:** the table is padded to fixed column widths, so one row's edit re-pads every open row and git sees the whole table as one changed hunk; on 2026-07-30 it conflicted twice within an hour on PR #1400, and each conflict silently stopped **all** CI on that PR (`#116`). **Next:** stop padding this table (Prettier still renders it readably, and one-row edits become one-line diffs), or split the open items into per-row files. **Re-confirmed 2026-07-30 (PR #1451):** removal did not reduce the pain — that PR conflicted on this file, its session resolved it (renumbering a colliding row), and `git merge-tree` showed it conflicting again within minutes, because four further `main` commits (#1455, #1446, #1445, and the X3 coverage record) each touched the table. Both sides had also allocated `#141` concurrently, so a duplicate id reached a pushed tip and failed `check:outstanding-issues` there (`#141 appears 2 times`) independently of the conflict — the read-modify-write allocation race this row already predicts. **Prerequisite for the un-pad fix:** Prettier enforces padded markdown tables under `docs/`, verified by checking identical ragged content in an ignored path (passes) and under `docs/` (fails) — so un-padding also needs `docs/outstanding-issues.md` added to `.prettierignore`, precisely as its sibling `docs/branch-review-ledger.md` already is at `.prettierignore:23`. Do that in one commit while the ledger queue is quiet: the un-pad rewrites every open row and will conflict with any in-flight ledger edit. **Stop:** do not reintroduce a merge driver here; if concurrent-append pain returns, write a dedupe driver like `merge=ledger`'s rather than stock union. | `.gitattributes`; `scripts/check-outstanding-issues.mjs`; PR #1400; PR #1430 | 2026-07-30 | -| #134 | P2 | issue | Ledger union-merge driver is absent wherever `npm install` was skipped | **Outcome:** the ledger's union-merge protection is present wherever a merge happens, or its absence is loud. **Detail:** `.gitattributes` declares `docs/branch-review-ledger.md merge=ledger`, but the driver itself lives in git _config_, installed by `postinstall` -> `scripts/install-git-hooks.mjs`. A container that skips `npm install` (this repo's remote agent sessions do — the session hook reports "node_modules matches the lockfile, skipping install") therefore has the attribute without the driver, and git silently falls back to an ordinary merge. On 2026-07-30 a `git merge origin/main` on PR #1424 produced **conflict markers inside the append-only ledger** at three lines; `git merge` itself did not name the file, so only `npm run check:branch-review-ledger` caught it. Committing that would have corrupted the file the guard exists to protect. **Next:** make the absence loud — have `check:branch-review-ledger` (already in `verify:cheap` and `static-pr`) fail when `.gitattributes` declares `merge=ledger` but `git config merge.ledger.driver` is unset, so the environment is caught before a merge rather than after. `npm run hooks:install` is the one-line fix once detected. **Stop:** never trust a `merge=union`-style attribute to be active just because `.gitattributes` declares it; the driver is per-checkout config. | PR #1424; session 2026-07-30 | 2026-07-30 | -| #144 | P2 | issue | `check:docs-index` has no repo-root directory coverage | **Outcome:** a new top-level directory cannot stay absent from the orientation maps. **Detail:** `scripts/check-codebase-index-coverage.mjs` exists precisely to catch "a module that never gets added to the map, silently staling it", but its granularity is route groups, `src/lib` module dirs and schema tables — it never looks at repo-root directories. That blind spot is how root `data/` stayed undocumented in both `CLAUDE.md` and `docs/codebase-index.md` while seven `src/lib` modules loaded it at runtime (differentials, forms, medications, services, specifiers), and how it stayed easy to confuse with the unrelated `src/data/`. PR #1436 documented `data/`, `mockups/`, `plugins/`, `eslint-rules/`, `.agents/`, `.claude/` and `.githooks/` by hand, so the maps are correct today with nothing stopping the next one from drifting. **Next:** extend the checker with a repo-root pass plus an allowlist for directories that are deliberately unmapped (build output, `.git`, vendored dirs). Kept out of #1436 deliberately — widening a blocking gate that runs in `verify:cheap` and `static-pr` deserves its own PR and its own red-then-green proof. | PR #1436; `scripts/check-codebase-index-coverage.mjs:1-17`; session 2026-07-30 | 2026-07-30 | | #142 | P3 | task | Four loose dated docs need source and migration edits before they can be filed | **Outcome:** every dated point-in-time doc lives in `docs/audit/` or `docs/archive/` as `docs/README.md` requires, not loose at the `docs/` top level. **Detail:** PR #1436 filed the five that were docs-only moves. These four are referenced from outside `docs/`, so relocating them means editing source, tests and migration SQL comments — a different risk class than a docs tidy, and not worth bundling into one: `capacity-review.md` (`scripts/soak-test.ts`), `tenancy-defense-in-depth-review.md` (`src/lib/owner-scope.ts`, `tests/owner-scope-guard.test.ts`, two migrations, `SECURITY.md`, `.claude/agents/clinical-governance-reviewer.md`), `operator-apply-july8-batch.md` (three migrations plus `supabase/schema.sql`), `scale-readiness-review.md` (one migration). Also note `forward-codify-retrieval-rpcs-workorder.md` is indexed as a completed workorder but live is still ahead of the repo on those RPCs, so archiving it would misrepresent open operator work. **Next:** treat as low priority — the docs are correctly indexed and reachable where they are; only file them if a pass is already editing those migrations. Editing applied migration SQL is subject to `npm run check:migration-role`'s immutability pin. | PR #1436; session 2026-07-30 | 2026-07-30 | | #143 | P2 | issue | An uncommitted hook in the main checkout aborts commits in every worktree | **Outcome:** a worktree commit is not blocked by unfinished work sitting in a different checkout. **Detail:** `core.hooksPath` is an absolute path to the primary checkout's `.githooks` (`C:\Dev\Apps\Database\.githooks`), and it is shared by every linked worktree because `git config` writes to the common config file. That primary checkout currently has an **uncommitted** `.githooks/pre-commit` plus an `update-docs-inventory.mjs` under `scripts/`; neither is on `origin/main` (so that path deliberately appears un-linked here — it does not resolve in this repo). The hook therefore fires in worktrees whose branch does not contain the script and dies: `Error: Cannot find module '/scripts/update-docs-inventory.mjs'` — `[pre-commit] Synchronizing generated documentation...` then `MODULE_NOT_FOUND`, commit aborted. Hit directly on PR #1436, worked around per-command with `git -c core.hooksPath=/.githooks commit` (not `--no-verify`, and without touching the shared checkout). **Mitigated 2026-07-30, NOT closed:** the hook now fails open — if the inventory task is selected but its script is absent from the committing worktree it prints `skipping inventory sync`, drops that task, and re-checks the all-tasks-empty early exit (without that re-check an empty `docs_to_check` makes the trailing `git diff --name-only --` match every modified file in the tree). Proven by committing from a linked worktree with the shared `core.hooksPath` untouched: exit 0. **Why this is not a fix:** that hook file is _untracked in the primary checkout and committed on no branch_, so the mitigation is not version-controlled — it disappears on overwrite and is absent from any fresh clone or other machine. The script itself IS committed, on `codex/docs-sync-automation-pr` (3 commits ahead of `main`), so the durable fix is to commit `.githooks/pre-commit` alongside it on that branch and let `scripts/install-git-hooks.mjs` distribute it. Inverse blind spot to #132: there the hook never runs, here it runs from the wrong tree. | PR #1436 commit attempt; session 2026-07-30 | 2026-07-30 | | #135 | P2 | rec | UI scope overfires: `ui_changed` matches every `src/app` path | **Outcome:** a change that cannot alter a rendered journey stops paying the longest job in CI. **Detail:** `uiPatterns` in `scripts/ci-change-scope.mjs` matches all of `src/app`, so an edit confined to `src/app/api/**` sets `ui_changed` and runs the full Chromium gate — measured 2026-07-30 at 15m26 of an 18m36 run, the entire critical path. **Next:** decide whether API-only diffs can be excluded. **Not done blind, deliberately:** the journeys exercise a production build that serves those routes, so a naive exclusion can hide a real regression; this needs a decision plus a compensating check, not a quieter filter. Sharding `ui-critical` (PR #1427) cut the cost of over-firing but did not remove it. **VALIDATED 2026-07-30, and it is already implemented:** `isUiChangedPath` in `scripts/ci-change-scope.mjs` already excludes `src/app/api/**`. A naive grep suggested a coverage hole — 16 of 17 API route groups appear in Playwright specs — but 92 of those references are `page.route(` / `context.route(` **stubs**, not real calls: the journeys mock the API layer, so an API handler change genuinely cannot move them. The exclusion is correct and the measurement now says so. **Stop:** do not re-open on a bare grep for `/api/` in specs; check whether the reference is an interception before concluding anything. | `scripts/ci-change-scope.mjs`; CI runs 30520443076 / 30519912667; session 2026-07-30 | 2026-07-30 | | #137 | P3 | rec | Advisory UI spends ~3 min per UI PR on five mockup tests | **Outcome:** the advisory lane costs what its signal is worth. **Detail:** `ui-advisory` runs on every UI PR (3m14 measured 2026-07-30) to cover `@quarantine` plus `@mockup` journeys — but `tests/flake-ledger.json` is empty and there are **zero** `@quarantine` tests in the suite, so it executes 5 `@mockup` tests. It is `continue-on-error` and outside `pr-required`, so it can also rot unnoticed. **Next:** gate it on mockup-file scope, or accept the cost as the price of keeping the lane warm for future quarantines. Either is defensible; the current state is just unmeasured. **DONE 2026-07-30:** `advisory_ui_changed` gates the lane on a mockup surface having changed or the flake ledger being non-empty. Reading the ledger rather than hard-coding "no quarantines exist" makes it self-correcting — quarantining a test brings the lane straight back. Fails OPEN on an unreadable ledger. Five self-test cases cover both directions plus the fail-open path. | `.github/workflows/ci.yml` `ui-advisory`; `tests/flake-ledger.json`; session 2026-07-30 | 2026-07-30 | | #138 | P3 | task | CI Triage ships inert pending a repo variable | **Outcome:** a PR red that is really a main-side regression is labelled as such instead of costing an author a debugging session. **Detail:** `.github/workflows/ci-triage.yml` is complete and self-tested (`check:ci-triage` runs in `verify:cheap` and `static-pr`) but every run short-circuits on `vars.CI_TRIAGE_ENABLED == 'true'`, which is unset. Its purpose is the failure mode this doc records repeatedly: CI merges the PR branch with current `main`, so a main regression surfaces on every open PR. **Next:** operator sets the repository variable, then confirm one triage comment posts. Reads job metadata from a trusted default-branch checkout only; never runs PR code. | `.github/workflows/ci-triage.yml`; session 2026-07-30 | 2026-07-30 | | #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 | | #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 | @@ -171,6 +166,14 @@ 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 | +| ---- | ----- | ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | +| #091 | issue | Results band could not express a partial-source failure | Resolved by PR #1480. Favourites and shared result bands now retain only the honest loaded count, visibly report unavailable sources, and expose a tested Retry recovery path without masking partial failures as ready. | 2026-07-30 | +| #128 | issue | Draft-to-ready alone did not retrigger required CI | Resolved by PR #1480. The CI pull-request trigger now includes ready_for_review alongside opened, synchronize, and reopened, with the workflow contract and exact-head Actions runs passing. | 2026-07-30 | +| #134 | issue | Ledger merge driver could be absent when install was skipped | Resolved by PR #1480. The branch-review-ledger guard now fails loudly when merge=ledger is declared but merge.ledger.driver is not configured, and its self-test covers the missing-driver case. | 2026-07-30 | +| #141 | issue | Production --clinical-accent-strong token was undefined | Resolved by PR #1480. The token is defined for light, dark, and forced-colors themes, documented in the permanent colour direction, and protected by focused design-token tests. | 2026-07-30 | +| #144 | issue | check:docs-index lacked repository-root directory coverage | Resolved by PR #1480. The checker now discovers tracked top-level directories, applies an explicit infrastructure/output allowlist, verifies both orientation maps, and reports all 49 covered roots/modules/routes plus schema tables. | 2026-07-30 | + | ID | Type | Summary | Outcome | Resolved | | ---- | ----- | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | | #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 |