diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b872dd0b2c..a7deb70cbe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,7 +75,15 @@ jobs: env: BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha || github.event.before }} HEAD_SHA: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha || github.sha }} - run: node scripts/ci-change-scope.mjs + run: | + if [ "${{ github.event.inputs.refresh_lighthouse_baseline }}" = "true" ]; then + # A baseline refresh is a focused measurement operation, not a + # synthetic full repository change. Keep the trusted workflow/perf + # contracts without starting unrelated build, UI, DB or container jobs. + node scripts/ci-change-scope.mjs --files .github/actions/setup-lighthouse-chromium/action.yml + else + node scripts/ci-change-scope.mjs + fi sync-pr-policy-body: name: Sync PR policy body @@ -209,7 +217,8 @@ jobs: run: npm run check:pr-mergeability - name: Focused CI workflow contracts - if: needs.changes.outputs.workflow_changed == 'true' + # Full coverage already contains these workflow-reading Vitest files. + if: needs.changes.outputs.workflow_changed == 'true' && needs.changes.outputs.coverage_changed != 'true' run: npm run test:ci-workflows - name: Codex auto-resolve workflow guard @@ -247,7 +256,7 @@ jobs: run: npm run format:changed - name: Scheduled full-tree format drift - if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' + if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.refresh_lighthouse_baseline != 'true') run: npm run format:check # Design-system guards that were previously only in the local verify:cheap @@ -481,36 +490,8 @@ jobs: uses: ./.github/actions/setup-ui-e2e - name: Chromium @critical journeys - env: - PLAYWRIGHT_BUILD_ROOT_ID: ci-production - PLAYWRIGHT_KEEP_BUILD_ROOT: "true" run: npm run test:e2e:critical - # Run-scoped only: publish the webpack filesystem cache for the dependent - # production shards in this workflow run. Do not use actions/cache — the - # ~804 MB warm cache was measured as a net CI loss against the shared - # 10 GB budget (would evict the Playwright browser cache). Artifacts expire - # after one day and never compete with that budget. - # continue-on-error: publish is an optimization. A miss must not fail the - # critical job after green @critical journeys — shards cold-build instead. - - name: Publish isolated Next.js build cache - if: success() - continue-on-error: true - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 - with: - name: playwright-next-build-cache-${{ github.run_id }} - # Path lives under `.next-playwright/` (dot-directory). upload-artifact - # v4.4+ excludes hidden paths unless this is set — without it the - # publish is a silent no-op and every shard cold-builds. - path: .next-playwright/ci-production/dist/cache - include-hidden-files: true - # Next's production webpack cache is already uncompressed; leave - # compression to the outer transport at level 0 so the critical path - # does not pay zlib-6 CPU on ~800 MB. - compression-level: 0 - retention-days: 1 - if-no-files-found: error - - name: Classify exact failed test identities if: failure() run: node scripts/classify-playwright-failures.mjs @@ -556,12 +537,11 @@ jobs: fail-fast: false matrix: # THREE explicit duration-aware file groups (scripts/playwright-pr-shards.mjs), - # not Playwright `--shard=i/N`. Count-balanced `--shard` packed the slow - # phone-scroll family into one runner (measured 9m36 vs 6m54/6m20 on - # CI 30530618838). Explicit groups mix slow-per-test specs with faster - # mega-specs. `tests/playwright-pr-shards.test.ts` fails closed if any - # production e2e:pr file is missing, duplicated, or leaves a shard empty. - # Re-measure wall time after suite growth before reshuffling membership: + # not Playwright `--shard=i/N`. The first explicit split still measured + # 5.5m / 3.7m / 2.4m of tests on CI 31285952061; the current profiles use + # those per-file durations and keep both full and post-critical totals + # within 30 seconds. Contract tests fail closed on membership or balance + # drift. Re-measure after suite growth before reshuffling membership: # node scripts/playwright-pr-shards.mjs --list # node scripts/playwright-pr-shards.mjs --validate shard: [1, 2, 3] @@ -574,24 +554,15 @@ jobs: - name: Setup UI e2e environment uses: ./.github/actions/setup-ui-e2e - # ui-critical-fast publishes a run-scoped artifact (not actions/cache) so - # all three shards can reuse webpack state without competing for the - # shared 10 GB cache budget. Server processes and reports stay isolated. - # continue-on-error: a missing artifact must not fail the shard — cold build - # remains the correct fallback when critical skipped or published nothing. - - name: Restore isolated Next.js build cache - if: needs.ui-critical-fast.result == 'success' - continue-on-error: true - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: playwright-next-build-cache-${{ github.run_id }} - path: .next-playwright/ci-production/dist/cache - - name: Chromium production journeys - env: - PLAYWRIGHT_BUILD_ROOT_ID: ci-production - PLAYWRIGHT_KEEP_BUILD_ROOT: "true" - run: npm run test:e2e:pr:shard -- --shard ${{ matrix.shard }} + run: | + if [ "${{ github.event_name }}" = "pull_request" ] || [ "${{ github.event_name }}" = "merge_group" ]; then + npm run test:e2e:pr:shard -- --shard ${{ matrix.shard }} --exclude-critical + else + # The fast subset is skipped on main/schedule/manual runs, so these + # events retain the complete production set. + npm run test:e2e:pr:shard -- --shard ${{ matrix.shard }} + fi - name: Classify exact failed test identities if: failure() @@ -1124,7 +1095,19 @@ jobs: release-browser-matrix: if: > always() && - (github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' || github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && + ( + (github.event_name == 'workflow_dispatch' && github.event.inputs.refresh_lighthouse_baseline != 'true') || + github.event_name == 'schedule' || + startsWith(github.ref, 'refs/heads/release/') || + ( + github.ref == 'refs/heads/main' && + ( + needs.changes.outputs.ui_changed == 'true' || + needs.changes.outputs.perf_changed == 'true' || + needs.changes.outputs.lockfile_changed == 'true' + ) + ) + ) && needs.changes.result == 'success' && needs.static-pr.result == 'success' && (needs.build.result == 'success' || needs.build.result == 'skipped') && @@ -1142,19 +1125,6 @@ jobs: - name: Setup Node and dependencies uses: ./.github/actions/setup-node-cached - - name: Restore Next.js build cache - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6 - with: - path: .next/cache - key: nextjs-${{ runner.os }}-${{ hashFiles('.nvmrc', 'package-lock.json') }}-${{ hashFiles('src/**', 'data/**', 'public/**', 'next.config.ts', 'tsconfig.json', 'postcss.config.mjs') }} - restore-keys: | - nextjs-${{ runner.os }}-${{ hashFiles('.nvmrc', 'package-lock.json') }}- - - - name: Build - env: - NEXT_BUILD_CPUS: "4" - run: npm run build - - name: Restore browser cache id: pw-cache uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6 @@ -1177,7 +1147,15 @@ jobs: - name: Full browser UI matrix id: e2e-matrix - run: npm run test:e2e + run: | + if [ "${{ needs.changes.outputs.ui_changed }}" = "true" ] && [ "${{ needs.ui-critical.result }}" = "success" ]; then + # Production Chromium already passed in this run. Keep mockup + # Chromium plus the cross-browser backstop without repeating it. + npm run test:e2e -- --project=chromium-mockups --project=firefox --project=webkit + else + # Fail-safe path for perf/lockfile-only or otherwise skipped UI proof. + npm run test:e2e + fi - name: Upload UI diagnostics if: failure() diff --git a/AGENTS.md b/AGENTS.md index 199173e26b..88ec727f80 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -242,6 +242,7 @@ still free to change, and it stops the blanket default from being applied silent - Do not run a broad baseline routinely before localized work, and do not select `verify:cheap` merely because a change is described as “non-trivial.” Use `npm run verify:cheap` once when cross-module risk warrants a broad offline gate. Use `npm run verify:pr-local` when a change is ready for PR handoff: it now classifies the changed paths, runs focused documentation/workflow contracts for recognised low-risk scopes, and fails closed to lint, typecheck, the full unit suite, RAG fixture validation, and relevant build/domain gates for executable or unknown scope. If the diff has not changed, do not run `verify:cheap` first merely to repeat the same coverage. - Do not stack focused tests, full tests, typecheck, lint, build, and browser checks unless each catches a distinct plausible regression. Do not rerun an unchanged successful gate. A deliberately skipped low-yield broad gate is not automatically verification debt; report the skipped check and its risk-based reason concisely. +- A fast-fail subset may precede a broader required gate only when the later gate excludes that subset for the same event; retain a fail-safe full path whenever the subset is skipped. Likewise, do not pre-run a build, install, or server setup that the selected wrapper performs itself. Guard these disjoint/fallback rules with workflow contract tests so a later edit cannot silently restore duplicate work or create a coverage hole. - Use dry-run selectors before expensive gates when scope is uncertain. `npm run verify:pr-local -- --dry-run --files ` inspects PR-local selection without running commands. The broader `--extended` plan is dry-run only unless explicit approval is reflected by `ALLOW_EXTENDED_PR_LOCAL=true`. - CI uses the same fail-closed scope model: recognised docs and workflow/policy-only changes run focused contracts; executable product/test/config, dependency, database, container, RAG, security-sensitive, mixed, or unknown paths retain the applicable heavy jobs. Do not broaden a path trigger or restore an always-on heavy job without evidence that the focused route misses a realistic failure class. Scheduled drift/release checks and the always-reporting `PR required` aggregate remain safety backstops. - Let the repository run coordinator control cross-worktree verification. It permits at most two focused Vitest/read-only typecheck leases from different worktrees; full Vitest, coverage, lint, build, Playwright, and live-provider tests remain exclusive. Do not install while a repository test, build, lint, typecheck, or server command is active. Avoid aggressive short-interval polling, and do not repeat an unchanged full gate after it passes. diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index a472047ca5..fe5bf79f39 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -837,23 +837,26 @@ Records before 2026-07-28 were written by hand and had drifted: 146 lines carrie | 2026-08-09 | cursor/differentials-diagnosis-links-9f18 | 0e77e7bc0842bef4ffc045c6dbea4626152490d1 | differentials diagnosis term links | implemented exact+alias termLinks chips on diagnosis+presentation pages; vitest 58/58; verify:pr-local green | vitest differential-diagnosis-links+detail+section-nav+route; verify:pr-local; ensure spot-check | | 2026-08-09 | cursor/differentials-diagnosis-links-9f18 | 0daa9e2f9fc84e879fd661da94203568309234a6 | PR #1768 Autopilot+Bugbot review-and-fix | Merged origin/main (DIRTY was ledger+detail-page staleness; merge-tree clean). Fixed SEGMENT_SPLIT to spaced-slash only so Delirium / medical psychosis links while alcohol/benzo, DVT/PE, food/fluid stay intact. Dispositioned: Copilot termLinks ??{} + Fragment key already fixed; CodeRabbit clean-keys moot (visibleSectionItems already cleans); CodeRabbit bare-slash split rejected (clinical harm). No Bugbot findings. Threads cleared on push. Merge left to user. | vitest differential-diagnosis-links+detail+route 49/49; verify:cheap exit 0 (543 files, 5828 passed/4 skipped); verify:pr-local exit 0 (lint/typecheck/test/build/rag-fixtures); merge-tree clean vs origin/main; no provider gates | | 2026-08-09 | cursor/differentials-diagnosis-links-9f18 | f784e81bcc0b53ef76b3da07a8e81f96d9bf0c71 | pr-1768 unblock | merged origin/main onto ba590f9; merge-tree clean; DIRTY mergeability cleared; push tip follows amend with this ledger | merge-tree clean; threads resolved; auto-merge was armed | -| 2026-08-09 | claude/document-viewer-phase-3-bj5k5v | 156db63f1b60f09791e426b043ea90d427b789ab | post-#1772 test simplification: replace the viewer perf source-text grep with behavioural coverage; de-literalise rail window and keyboard label assertions | PR #1777 opened. Self-review of #1772's own tests against an excessive-strictness challenge. Finding: the client-performance-boundaries grep for resolveLiveCanvasWindow / resolveRenderAheadPages / liveCanvasLimit / requestIdleCallback was not merely brittle, it was INEFFECTIVE - replacing the budget call with a hardcoded 3 leaves every identifier in the file, so it stayed green while the viewer retained three full-zoom canvases (measured both ways). Replaced by a DOM case that binds the budget (VIEWER_MAX_ZOOM at dpr 3 gives ~16.8M backing px against the 24M budget, window collapses to 1) and fails on exactly that substitution. Also exported RAIL_IMAGE_WINDOW so the rail test derives its counts (verified by tuning 6->8: all 7 still pass), and relaxed the keyboard aria-label assertions from exact prose to the key names. Pre-existing greps for disableAutoFetch / canvas.width = 0 / pageToCleanup left alone deliberately - two are now redundant but they are another author's guard. | verify:pr-local (1 pre-existing root-only failure: pr-handoff-stop #291; 5872 passed), build OK 80s + client bundle secret check, eval:rag:offline 36 golden cases / 574 tests, lint + typecheck clean. Sabotage-verified in both directions. Browser gates unrunnable here (#279) - unchanged by this diff. | +| 2026-08-09 | claude/documentviewer-nav-convergence-oddhjx | 1395d533cb13eadc705e47f76aa9f39a7a11c058 | DocumentViewer / in-page-nav convergence (#288): non-adoption decision recorded in docs/search-chrome-behaviour.md; merged duplicated visible-element predicate into resolveVisibleElement; new convergence guard test | Converged what was duplicated; DocumentReviewer header adoption declined on the merits with four blocking reasons recorded. No contract test edited. | verify:pr-local (546/547 files, 5883 tests pass; sole failure tests/pr-handoff-stop.test.ts reproduced on pristine origin/main), verify:phone-chrome (contracts 123 pass; focused Chromium 7 pass), contract set 12 files/151 tests pass, lint, typecheck, format | | 2026-08-09 | claude/planning-build-intelligence-9ot0nm | 3df3cb3993f73cda4dbbc4ac7549f84b3c6ea7ed | Node 24.15 engine floor: engines.node, preinstall hook, check:runtime, session-start provisioning, codex-cloud assertion | Authored and handed off as PR #1771; closes #285; operationalRisk true, clinicalRisk/ragRanking false | test 5800 passed/1 pre-existing root-uid failure (pr-handoff-stop, confirmed on stashed clean tree); lint 0; typecheck 0; prettier --check . pass; check:runtime pass; check:codex-cloud pass; check:outstanding-issues pass; preinstall boundary proof 24.13/24.14.9 reject, 24.15/24.19 accept, 25.0.0 reject; contract test mutation-checked red | | 2026-08-09 | pull/1771 | 466ec4216272c31c5f754db213dbdc529583b167 | PR 1771 runtime floor enforcement | P2: Cloud and Desktop setup paths remain major-only; do not merge until range-aware | static review; check:runtime PASS; check:codex-cloud PASS; ledger PASS; outstanding issues PASS; focused Vitest blocked by active Playwright lease | -| 2026-08-09 | cursor/therapy-card-densify-e975 | 3db839a6bb1f5b45fc55bb732d21b30551a506b0 | therapy search ResultCard densify (gap, tags, favourite, actions, match cells) | pass — denser cards; band gap fixed; single-row prioritized tags; heart top-right; 3-col actions; summarised cells | unit 35/35; verify:pr-local pass; ensure visual phone+desktop pass | -| 2026-08-09 | claude/m3-token-debt-262-261 | c6e1fe7fc42ec6f286eb5a3d8f7ddad7dfad2724 | design-system contract: raw padding/radius/line-height ratchets + type-step selection gate (#262 parts 2/3); closed #218/#270 | Authored and self-verified; PR #1780 open, auto-merge deliberately not armed (gate change). Baseline additive: all 15 pre-existing metrics and every debtByPath entry byte-identical; 94/94 new findings verified present at their cited line. Mutation-tested both halves of part 3 and three failure modes of part 2. | check:design-system-contract, check:icon-scale, check:type-scale, check:outstanding-issues, vitest design-system-contract-utils (31 passed), format:check whole-tree, verify:cheap (exit 1 from 5 pre-existing failures, none in this diff; 3 cleared by merging main, remaining 2 byte-identical to origin/main) | -| 2026-08-09 | claude/documentviewer-nav-convergence-oddhjx | 1395d533cb13eadc705e47f76aa9f39a7a11c058 | DocumentViewer / in-page-nav convergence (#288): non-adoption decision recorded in docs/search-chrome-behaviour.md; merged duplicated visible-element predicate into resolveVisibleElement; new convergence guard test | Converged what was duplicated; DocumentReviewer header adoption declined on the merits with four blocking reasons recorded. No contract test edited. | verify:pr-local (546/547 files, 5883 tests pass; sole failure tests/pr-handoff-stop.test.ts reproduced on pristine origin/main), verify:phone-chrome (contracts 123 pass; focused Chromium 7 pass), contract set 12 files/151 tests pass, lint, typecheck, format | +| 2026-08-09 | claude/document-viewer-phase-3-bj5k5v | 156db63f1b60f09791e426b043ea90d427b789ab | post-#1772 test simplification: replace the viewer perf source-text grep with behavioural coverage; de-literalise rail window and keyboard label assertions | PR #1777 opened. Self-review of #1772's own tests against an excessive-strictness challenge. Finding: the client-performance-boundaries grep for resolveLiveCanvasWindow / resolveRenderAheadPages / liveCanvasLimit / requestIdleCallback was not merely brittle, it was INEFFECTIVE - replacing the budget call with a hardcoded 3 leaves every identifier in the file, so it stayed green while the viewer retained three full-zoom canvases (measured both ways). Replaced by a DOM case that binds the budget (VIEWER_MAX_ZOOM at dpr 3 gives ~16.8M backing px against the 24M budget, window collapses to 1) and fails on exactly that substitution. Also exported RAIL_IMAGE_WINDOW so the rail test derives its counts (verified by tuning 6->8: all 7 still pass), and relaxed the keyboard aria-label assertions from exact prose to the key names. Pre-existing greps for disableAutoFetch / canvas.width = 0 / pageToCleanup left alone deliberately - two are now redundant but they are another author's guard. | verify:pr-local (1 pre-existing root-only failure: pr-handoff-stop #291; 5872 passed), build OK 80s + client bundle secret check, eval:rag:offline 36 golden cases / 574 tests, lint + typecheck clean. Sabotage-verified in both directions. Browser gates unrunnable here (#279) - unchanged by this diff. | | 2026-08-09 | claude/disabled-button-accessibility-piclvr | 722abdb780c715c0a89df268ed48f6c741ffd569 | disabled-placeholder buttons -> aria-disabled + inert handler (25 sites, 13 components); controlDisabled/therapy recipe aria-disabled styling; require-button-wiring redundantDisabledPair gate; wiring-conventions contract rewrite (settles #291) | authored — PR #1778 opened | lint (uncached, exit 0); typecheck; test 5878 passed/1 pre-existing root-env failure in pr-handoff-stop; build; check:rag:fixtures 36 golden cases; prettier --check clean; verify:ui not run (no browser in container) | +| 2026-08-09 | claude/in-page-nav-pr-3-i6gi8n | 6651feef4fab63f1181fba57908cb22e2932df3c | in-page-nav PR 3: convert /medications/[slug] (panel-swap) and /factsheets/[slug] (anchors) onto InPageNavHeader; record the differentials-presentations exception; delete orphaned SecondaryNavigation (#271) | converted 2 of 3 routes, 3rd recorded as a reasoned lasting exception; tocFor and SecondaryNavigation deleted; route-sections contract 7 -> 12 routes plus a panel-swap suite | verify:pr-local (1 pre-existing root-permission failure in pr-handoff-stop.test.ts, all else green); test 5932 passed; in-page-nav-route-sections 29 passed; verify:phone-chrome 3/4 stages (focused-browser blocked by #255 Chromium 1194 vs 1234); build + bundle-budget + rag:fixtures green; verify:ui not run (#255, delegated to CI) | +| 2026-08-09 | claude/m3-token-debt-262-261 | c6e1fe7fc42ec6f286eb5a3d8f7ddad7dfad2724 | design-system contract: raw padding/radius/line-height ratchets + type-step selection gate (#262 parts 2/3); closed #218/#270 | Authored and self-verified; PR #1780 open, auto-merge deliberately not armed (gate change). Baseline additive: all 15 pre-existing metrics and every debtByPath entry byte-identical; 94/94 new findings verified present at their cited line. Mutation-tested both halves of part 3 and three failure modes of part 2. | check:design-system-contract, check:icon-scale, check:type-scale, check:outstanding-issues, vitest design-system-contract-utils (31 passed), format:check whole-tree, verify:cheap (exit 1 from 5 pre-existing failures, none in this diff; 3 cleared by merging main, remaining 2 byte-identical to origin/main) | | 2026-08-09 | cursor/differentials-four-page-nav-5ebf | 93ea437610c1f1b681c3a5cbdc72fe8b9b178710 | differentials four-page nav | implemented Search/Diagnoses/Presentations/Compare equal pages; compare queue; kind labels; Search q+run restore | vitest nav+differentials-navigation; typecheck; lint; full unit 5814 passed | | 2026-08-09 | cursor/differentials-four-page-nav-5ebf | 384a1bedd8dd1064fb2fcf26ac845224e2cafdc4 | PR #1774 differentials four-page nav heavy review-and-fix | fixed P1 ids+Playwright; ModeNav route gate; RSC queue clears bundle+shadow; Copilot ModeNav-on-detail dispositioned (info page); ledger reorder dispositioned (merge=ledger) | vitest nav 47p; design-system-contract; typecheck; lint; test 5897p; build+bundle-budget 1543.7 within tol; focused pw compare queue 1p | | 2026-08-09 | claude/m3-token-debt-262-261 | 95221ef4235abd9544158b07b8b8569f00c9ec78 | PR #1780 review-and-fix | fixed P2 ratchet bypasses (arbitrary-property classes, CSS-consumer exemption anti-rot, modern CSS zero units); Bugbot clean; merge-tree clean; required CI was green on prior tip | vitest design-system-contract-utils 32/32; check:design-system-contract; mutation CSS-exemption fail→restore; verify:cheap PASS (549 files / 5933 tests); verify:pr-local stages PASS (test flake in design-system-adoption timed out once then 51/51 + full test 549/549 + check:rag:fixtures PASS); no provider gates | | 2026-08-09 | claude/m3-token-debt-262-261 | 7bac3bd762b381cb25c9b2a15ef3bb7223d15b16 | PR #1780 review-and-fix | fixed P2 ratchet bypasses (arbitrary-property classes, CSS-consumer exemption anti-rot, modern CSS zero units); Bugbot clean; merge-tree clean; required CI was green on prior tip | vitest design-system-contract-utils 32/32; check:design-system-contract; mutation CSS-exemption fail→restore; verify:cheap PASS (549 files / 5933 tests); verify:pr-local stages PASS (test flake in design-system-adoption timed out once then 51/51 + full test 549/549 + check:rag:fixtures PASS); no provider gates | | 2026-08-09 | cursor/dsm-search-header-fix-15d6 | df088c766f1761496189ec09146aa54c23b1c012 | dsm-search-header | pass: removed catalogue page strip; ribbon + category filter match target | vitest dsm-search-empty-state; npm test 5857 passed; lint; typecheck; ensure phone /dsm/search?q=Delirium | | 2026-08-09 | claude/m3-token-debt-262-261 | fe75e6acade008e68f953e235cc035f2e5d9d216 | PR #1780 review-and-fix | fixed P2 ratchet bypasses; synced origin/main (#1775); Bugbot clean; merge-tree clean | vitest design-system-contract-utils 32/32; check:design-system-contract; mutation CSS-exemption; verify:cheap PASS 549/5933; verify:pr-local stages PASS after adoption flake retest; check:rag:fixtures PASS; no provider gates | +| 2026-08-09 | origin/pr/1686 | a5cce760d73bd174dba200b53852568dcdb9be0d | PR #1686 CI testing perfection and merged rollout reconciliation | Merged required CI was green, but hosted evidence confirmed P2 shard imbalance, duplicated critical coverage, net-negative 1.09 GB cache transport, inactive container revision enforcement, duplicated workflow/build/browser work, and missing local npm-ci selection. Fixed locally on current main; no P0/P1. | Hosted run 31285952061 inspected; focused Vitest 55 passed plus browser-preflight 12 passed; CI workflow suite 256 passed; typecheck passed; CI scope, verification plan, shard parity, gate manifest, action pins, npm-ci dry-run, docs and outstanding-issues guards passed; no Playwright/browser run or provider mutation. | +| 2026-08-09 | cursor/therapy-card-densify-e975 | 3db839a6bb1f5b45fc55bb732d21b30551a506b0 | therapy search ResultCard densify (gap, tags, favourite, actions, match cells) | pass — denser cards; band gap fixed; single-row prioritized tags; heart top-right; 3-col actions; summarised cells | unit 35/35; verify:pr-local pass; ensure visual phone+desktop pass | | 2026-08-09 | cursor/therapy-card-densify-e975 | 52f07d49f89e6c786c624ccbd38ae552818a2071 | PR 1783 babysit | fixed review threads: TagRow +N clip, title/alias preview exclusion, preview field fallbacks; Copilot md grid kept; CI re-triggered after Copilot tip | npm test: 5958 passed / 4 skipped | -| 2026-08-09 | claude/in-page-nav-pr-3-i6gi8n | 6651feef4fab63f1181fba57908cb22e2932df3c | in-page-nav PR 3: convert /medications/[slug] (panel-swap) and /factsheets/[slug] (anchors) onto InPageNavHeader; record the differentials-presentations exception; delete orphaned SecondaryNavigation (#271) | converted 2 of 3 routes, 3rd recorded as a reasoned lasting exception; tocFor and SecondaryNavigation deleted; route-sections contract 7 -> 12 routes plus a panel-swap suite | verify:pr-local (1 pre-existing root-permission failure in pr-handoff-stop.test.ts, all else green); test 5932 passed; in-page-nav-route-sections 29 passed; verify:phone-chrome 3/4 stages (focused-browser blocked by #255 Chromium 1194 vs 1234); build + bundle-budget + rag:fixtures green; verify:ui not run (#255, delegated to CI) | | 2026-08-09 | PR #1782 / cursor/fix-document-open-scroll-e5bf | 5709f2cc7a954197e02107c96d7896d8d13445c3 | document-viewer open-at-top | ship: remove chunk mount scrollIntoView so document opens stay at overview top | document-viewer-shell.dom 7 pass; document-section-summary.dom 8 pass; verify:pr-local dry-run | | 2026-08-09 | cursor/fix-document-open-scroll-e5bf (PR #1782) | 98029875db7d640d3e699829249bb33892296bff | PR #1782 unblock | before: static-pr+coverage failed on stale adoption-manifest (document-viewer-shell testFiles drift), merge-tree clean 0 behind, auto-merge armed, 1 advisory CodeRabbit waitFor thread; after: regenerated adoption-manifest, hardened scroll negative assertion, pre-commit+handoff adoption sync to prevent recurrence; CodeRabbit dispositioned as fixed by sync assert | check:design-system-adoption PASS; vitest design-system-adoption+document-viewer-shell+docs-inventory 63/63 PASS; format; no provider-backed checks | | 2026-08-09 | cursor/fix-document-open-scroll-e5bf (PR #1782) | 86698228533ebe10452c10c1bd7a3e1610d891ae | PR #1782 unblock | merged origin/main (behind-but-clean); fixed static-pr TS2322 on document-viewer-shell chunk fixture; fixed Production UI DSM compare remove stall via location.assign + DOM proof; prior adoption-manifest drift already fixed | tsc clean for changed files; vitest document-viewer-shell+dsm-compare-remove+design-system-adoption 59/59 PASS; check:design-system-adoption PASS; format; no provider-backed checks | -| 2026-08-10 | codex/visual-baseline-advisory-pr | 6bc57714c36bc6d027561bb8f5f8b00bb92524b2 | PR #1791 babysit unblock | fixed Production UI formulation Clear→Draft flake settle; classified visual drift vs non-drift failures | test:ci-workflows 263; classify-visual-baseline-outcome+ci-cache-safety 40 | +| 2026-08-10 | codex/ci-perfected-rollout-20260809 (PR #1789) | accbc7c6324b839112ff8df8f9b66d3557f2b98e | PR babysit | unblocked; merged origin/main (false-DIRTY behind-but-clean); fixed Codex P2 ui_changed for Playwright runner helpers; thread replied+resolved | ci-change-scope --self-test pass; merge-tree clean; no provider gates | +| 2026-08-10 | codex/ci-perfected-rollout-20260809 (PR #1789) | bf437370441c43a35ec63353642b0180ba5beba6 | PR babysit | late sync: merged origin/main (#1793/#1794); behind-but-clean; prior tip CI green; no code fixes | merge-tree clean; format clean; prior tip PR required pass; no provider gates | | 2026-08-10 | PR #1797 / claude/codex-m4a-retire-dead-type-8wq9ta | 6bf3c7b2a0600021290e165302fd07d721af6592 | retire the dead --text-2xl-compact type step (ledger #297): globals.css @theme, twMerge config, two test lists, the design-system-contract exemption, TOKENS.md/GATES.md | Executed the recorded next action on outstanding-issues #297. The step had zero class-utility and zero var(--text-*) consumers, so the deletion renders identically; UNUSED_TYPE_STEP_EXEMPTIONS is now empty and the declared-but-unconsumed gate holds the line with no carve-out. One test fixture using the token as a synthetic var() consumer was repointed at --text-2xl-minus. GATES.md corrected to eight non-standard steps; the 705-consumer total is unchanged because this step contributed 0. No clinical, RAG-ranking or operational risk paths touched (classifyPullRequestFiles: all false). | check:design-system-contract PASS (705 production files); check:type-scale --strict PASS; lint exit 0; typecheck exit 0; npm run build after rm -rf .next exit 0 (Compiled successfully in 63s); check:outstanding-issues PASS; verify:pr-local completed through typecheck then failed at test on a PRE-EXISTING root-permission failure in tests/pr-handoff-stop.test.ts that reproduces on clean d812c76 (5993 passed, 1 failed); build and check:rag:fixtures run/assessed separately. No UI gate: no rendered output can change. No provider-backed check run. | +| 2026-08-10 | codex/visual-baseline-advisory-pr | 6bc57714c36bc6d027561bb8f5f8b00bb92524b2 | PR #1791 babysit unblock | fixed Production UI formulation Clear→Draft flake settle; classified visual drift vs non-drift failures | test:ci-workflows 263; classify-visual-baseline-outcome+ci-cache-safety 40 | diff --git a/docs/outstanding-issues.md b/docs/outstanding-issues.md index cd0c013ea9..50b9284bbf 100644 --- a/docs/outstanding-issues.md +++ b/docs/outstanding-issues.md @@ -96,77 +96,74 @@ removed after current-main verification; it is not missing recommended work. | 41 | `#159` | A3 | High — test hygiene | Next test-infra pass | 1–2 hours | Lists naming test files are duplicated, and the stale copy fails by running nothing — no gate, plan or config names a set of test files in a second place without being derived from the filesystem or asserted against it. | | 42 | `#165` | A2 | High — clinical UI | Next answer-home UX pass | 0.5–1 day | Adopt a consolidated answer-home notice block — the studies exist, nothing adopts them — the answer hero states its safety obligation, its scope, and its verification requirement as one block in one voice. | | 43 | `#166` | A2 | High — clinical safety UI | With #165 or next clinical chrome pass | 2–4 hours | Answer mode ships no verify-before-use caveat; every other clinical mode does — the surface that actually generates prose from retrieved sources says so, and says it must be checked. | -| 44 | `#167` | A2 | High — verify gates | Next verify:pr-local change | 1–3 hours | `verify:pr-local` exits 0 when its own build step refuses to run — the PR-local gate cannot report success for a step that never executed. | -| 45 | `#168` | A3 | High — ledger architecture | With #156 / id-scheme redesign | design first | Sequential issue ids force every concurrent append to conflict — two sessions can append to this ledger at the same time without conflicting. | -| 46 | `#169` | A3 | High — git hygiene | Next branch cleanup batch | 1–2 hours | Local branches carry work that exists on no remote — committed work is not lost when a machine or worktree is reclaimed. | -| 47 | `#170` | A2 | High — phone UI | Next documents/filter phone pass | 0.5–1 day | Documents and therapy already have page-owned phone filter sheets; remaining modes still use inline controls — shared-band Filter+Sheet adoption without regressing those two or sheetless Sort. | -| 48 | `#171` | A2 | High — documents UI | With #170 / filter consolidation | 0.5–1.5 days | Documents mode has four overlapping filtering surfaces, two of them the same job — one Filter control opening one panel, so a reader learns filtering once. | -| 49 | `#175` | A2 | Operator — clinical data + Standard | Next therapy catalogue curation window | 2–4 hours | Therapy modality is now null on all 205 records and needs curation or removal — the Therapy detail and recommend screens either show a curated modality or stop carrying the field at all. | -| 50 | `#178` | A3 | High — PR policy | Next pr-policy change | 1–2 hours | pr-policy does not flag operational risk bundled with clinical or UI risk — a PR that mixes operational-risk paths with clinical or UI risk is called out before it merges, because squash-merging that mix destroys per-it… | -| 51 | `#189` | A2 | Specialist — search/RAG budgets | After #098 route residual; before collapsing RPCs | 2–4 hours + canary if behaviour | Pin /api/search route-level round trips and disposition the x3 text RPC probes — a counting-proxy budget drives `POST` `/api/search` (auth/ratelimit/scope/enrichment/telemetry), and the retrieval-core finding that `matc… | -| 52 | `#036` | Optional | Specialist — privacy/schema | When visibility model is redesigned | design + migration | No explicit `is_public` visibility flag on documents — Public-corpus visibility is implicit: `owner_id IS NULL` on an `indexed` document (`resolveSearchScope`). The `metadata.public_corpus` marker is written by the prom… | -| 53 | `#101` | A3 | Specialist — RAG/retrieval | After #186 update + canary approval | canary-gated | Canary-gated retrieval parallelisation candidates — independent retrieval stages stop running serially, proven by a live canary pair. Candidates: metadata/memory/visual hydration triples repeated on four branches (`rag.… | -| 54 | `#142` | Optional | High — docs hygiene | Next docs filing pass | 1–2 hours | Four loose dated docs need source and migration edits before they can be filed — 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. | -| 55 | `#151` | Optional | Operator — GitHub PAT | When writing the durable note (#187) | 15–30 min | `gh pr checks` cannot read CI, but the Actions API can — nobody concludes CI is unverifiable when it is merely reached through a different endpoint. | -| 56 | `#154` | Optional | High — agent process | When writing the durable note (#187) | 15–30 min | Row ids are not stable identifiers for "did my change land" — an agent confirms work reached `main` by content, never by id, title or PR state. | -| 57 | `#156` | A3 | High — ledger architecture | With #168 id-scheme work | design first | Outstanding-issues ids are still allocated read-modify-write, and Update-branch corrupts the merge — two branches cannot silently claim the same outstanding-issues id, and no merge path can commit a file where they have. | -| 58 | `#172` | A3 | High — documents UI | With #171 filter consolidation | 1–2 hours | `Sources` sits in the results bar but is navigation, not a filter — the results bar holds only controls that act on the current results. | -| 59 | `#174` | A3 | High — search facets | When facet UX is redesigned | 0.5–1 day | Facets AND within a group, so two values from one group almost always return nothing — a decision on record, either way. | -| 60 | `#177` | A3 | High — therapy catalogue build | Next therapy-index build change | 1–2 hours | Therapy catalogue aliases duplicate 2.53 MB of bytes instead of pointing at the hashed file — the unversioned catalogue aliases stop costing a second copy of every payload in the repo and the image. | -| 61 | `#179` | A3 | High — therapy catalogue build | With #177/#180 | 1–2 hours | The full therapy catalogue silently switched from minified to pretty-printed — the full catalogue's on-disk format is a decision someone made, not a side effect. | -| 62 | `#180` | A3 | High — therapy catalogue build | With #177/#179 | 1–2 hours | build-therapies-index now overwrites its own source input — the therapy catalogue generator has a source it does not also destroy. | -| 63 | `#181` | Optional | High — documents UI clarification | When updating #171 | 15–30 min | Correction to `#171`: source-type does NOT duplicate the `Document type` facet group — `#171` states that the documents source-type control "duplicates the facet group already named `Document type`". That is wrong, and … | -| 64 | `#188` | A3 | Operator — DR/SRE | After any schema restore drill, or next DR review | checklist-owned | Document and track disaster-recovery re-creation checklist as ledger work — the five DR items that do not survive a schema restore are tracked with owners and verify steps, not only in `docs/operator-backlog.md`. | -| 65 | `#190` | A3 | Specialist — RAG structure | On explicit X3 go-ahead | 1 PR per extraction unit | X3: Finish rag.ts monolith decomposition — `src/lib/rag/rag.ts` is decomposed into focused modules per `docs/maturity-backlog-workorders.md` X3, with existing offline RAG contracts green. | -| 66 | `#191` | A3 | Operator — DB + Specialist | Approved live-DB window only | provider-gated | X5: ACL-migration consolidation (provider-gated) — ACL-related migrations are consolidated per maturity work-order X5 without weakening owner-scope/RLS. | -| 67 | `#192` | A3 | High — test coverage | Next coverage-floor pass | 0.5–1 day | X6: Raise clinical/retrieval/answer coverage floors — coverage floors for clinical, retrieval, and answer domains meet the maturity X6 targets with CI enforcing them. | -| 68 | `#193` | A3 | High — src/lib structure | After/with X3 non-protected clusters | 1 PR per cluster | X7: Complete the remaining src/lib domain-directory reorg — remaining `src/lib` clusters sit in their domain directories per X7 follow-on to X2. | -| 69 | `#194` | A3 | High — scripts/docs hygiene | Next scripts archive pass | 1–2 hours | L1: Archive retired backfill one-shots and dead ci-change-scope token — retired `backfill:*` one-shots and the dead `ci-change-scope` token are archived/removed with docs/script index updated. | -| 70 | `#195` | A3 | Operator — GitHub maintainer | Maintainer UI window | 30–60 min | M1: Repo-host hardening (branch protection and required checks) — GitHub branch-protection rulesets and required checks match audit §8 / maturity M1. | -| 71 | `#196` | A3 | Operator — DR/SRE | After schema restore drill | 1–2 hours | DR: Re-create pg_cron schedules after schema restore — ingestion/retention and related pg_cron schedules exist on the target DB after any schema restore. | -| 72 | `#197` | A3 | Operator — DR/SRE | After schema restore drill | 30–60 min | DR: Re-add Vault secrets including cron_ingestion_jwt — required Vault secrets (at least `cron_ingestion_jwt`) are present after schema restore. | -| 73 | `#198` | A3 | Operator — DR/SRE | After schema restore drill | 30–60 min | DR: Re-set custom database GUCs after schema restore — custom `app.*` GUCs required by the app/worker are set on the restored database. | -| 74 | `#199` | A3 | Operator — DR/SRE | After schema restore; Deno v2 available | 1–2 hours | DR: Redeploy Supabase edge functions (Deno v2.x) — required edge functions are deployed to the target project with Deno v2.x. | -| 75 | `#200` | A3 | Operator — DR/SRE | After schema restore drill | 1–2 hours | DR: Re-enter dashboard config after schema restore — auth providers/SSO redirect URLs, connection-pool caps, per-project keys, and `E2E_USER_*` are re-entered in the Supabase/Railway dashboards after restore. | -| 76 | `#204` | A2 | High — install/CI integrity | Next dependency or verify:pr-local change | 1–3 hours | npm 11.6.2 regenerates a lockfile its own `npm ci` rejects, reddening every CI job — add `npm ci --dry-run` (or lockfile-sync assertion) to `verify:pr-local` when package.json/lock change; do not regenerate with `npm install` to "fix". Distinct from #149. | -| 77 | `#183` | A2 | Operator — Sentry + Specialist | Next approved observability window with SENTRY_AUTH_TOKEN | 1–2 hours | Create Sentry metric alert for production DB span p95 > 500ms (`span.op:db`, environment production). **Stop:** no secret printing; blocked until token/env available. | -| 78 | `#206` | A2 | Specialist — answer UI contract | With AnswerState producer work (`#207`) | 2–4 hours | `partial_retrieval` has no app-facing producer — decide RAG contract vs UI-only mapping before AnswerCard. **Stop:** no retrieval behaviour change without RAG flag. | -| 79 | `#208` | A2 | Specialist — clinical copy | With answer clipboard / PR-13 work | 1–2 hours | `answerClipboardText` must not replace `formatAnswerRenderCopyText` — compose render-policy warnings. **Gate:** focused clipboard/copy tests. **Stop:** do not drop render-policy caveats. | -| 80 | `#216` | A2 | High — design-system answer shell | After `#207` and clinical surface decision | 0.5–1 day | Adopt AnswerCard on the answer surface (deferred from PR-J). Own PR, own `verify:ui`. **Stop:** not before `#207`; show both surface treatments before choosing. | -| 81 | `#230` | A2 | High — PR policy / CI | Next ci.yml / pr-policy change | 1–2 hours | PR-policy body sync must no-op unless `PR_POLICY_BODY.md` is new in that PR's own diff (or move body out of repo). **Gate:** `check:github-actions` / workflow self-test. **Stop:** do not re-commit scratch bodies to main. | -| 82 | `#232` | A2 | High — review ledger hygiene | Next ledger touch for PR-J | 30–60 min | Supersede the PR-J clinical-governance ledger row so it describes the merged head (`ledger:append --supersede`). **Stop:** append-only — never edit/delete the old row. | -| 83 | `#209` | A3 | High — design tokens / contrast | Next Gate 1 / warning-token pass | 1–2 hours | Add contrast pair for `--warning` used as body text (VerificationNotice / DoseLine). **Gate:** design-system contrast checks. **Stop:** do not invent a new status token without TOKENS.md. | -| 84 | `#211` | A3 | High — TypeScript strictness | Dedicated migration branch | multi-PR | Plan and start `noUncheckedIndexedAccess` migration (1266 errors); highest-risk files first. **Stop:** do not flip the flag on main without a staged plan. | -| 85 | `#212` | A3 | High — runtime validation | After highest-risk cast inventory | multi-PR | Replace `as unknown as` and unvalidated `JSON.parse` with Zod/guards at trust boundaries. **Stop:** RAG/provider boundaries need clinical/privacy care. | -| 86 | `#213` | A3 | High — error handling | Next fetch/stream hardening pass | 0.5–1 day | Stop swallowing fetch/stream errors with empty catches; check `response.ok`. **Stop:** do not change telemetry contracts silently. | -| 87 | `#215` | Optional | High — image perf | Next image/PWA pass | 2–4 hours | Image-optimization basics for lightbox, PWA lifecycle, demo PNGs. **Stop:** optional until measured need. | -| 88 | `#221` | A3 | High — design-system convergence | After `#218` cn() decision | 0.5–1 day | Converge remaining local EmptyState/LoadingState/Chip duplicates. **Stop:** not piecemeal before cn()/Chip decisions. | -| 89 | `#222` | A3 | High — headers / search chrome | During headers redesign decision | 2–4 hours | Decide whether mode-home-template / search-results-header-band are in PageHeader scope or permanently out. **Stop:** do not flatten phone composer ownership. | -| 90 | `#233` | A3 | High — design-system docs | Next COMPONENTS.md docs PR | 1–2 hours | Refresh section 0 maturity matrix and document FormField optionality-marker contract. **Gate:** docs checks. **Stop:** docs-only; no product behaviour change. | -| 91 | `#234` | A3 | High — design-system docs | With answer-surface docs | 30–60 min | Document `answer-copy-payload.ts` as the clipboard contract for three surfaces. **Stop:** do not add a second copy builder. | -| 92 | `#235` | A3 | High — design-system evidence | Next warmed local proof-shot pass | 1–2 hours | Capture missing ADOPTION.md §7 proof shots for adopted surfaces. **Stop:** not visual-baseline PNGs (`#118`); no Playwright snapshot commit. | -| 93 | `#236` | Optional | High — branch hygiene | Next cleanup batch with `#079` | 30–60 min | Dispose orphan DS V2 builder branches and leftover wave-5 dev servers. **Stop:** content-verify before delete; no force-clean. | -| 94 | `#237` | A3 | High — design-system a11y | Before freezing Linux visual baselines (#242) | 30–60 min | Eyeball low-confidence AccessibleTable densities at 320px; MissingValue phrases must remain readable. **Gate:** visual spot-check only. **Stop:** do not abbreviate MissingValue to a dash. | -| 95 | `#238` | A3 | High — overlays/UI | After Sheet portal default change (#1616) | 30–60 min | Visual pass for Sheet portal default on settings, sidebar, and answer overlays under OverlayRoot. **Stop:** do not revert portal default without evidence. | -| 96 | `#239` | Optional | High — phone chrome | When phone orientation QA is available | 15–30 min | Manual phone rotation check for ResizeObserver-only phone chrome reserve. **Gate:** `verify:phone-chrome` still owns automated coverage. **Stop:** do not widen reserve heuristics without reproduction. | -| 97 | `#240` | Optional | High — design tokens | Next design-owner review | 15–30 min | Confirm tooltip visual hard-clip asymmetry with design owner (sr-only keeps full text). **Stop:** no product change without that confirmation. | -| 98 | `#241` | A3 | High — therapy catalogue | Standing; with any therapy-home change | 15–30 min | Therapy home summary count/slugs remain build-time; keep `build-therapies-index --check` load-bearing. **Stop:** do not bypass the check. | -| 99 | `#242` | A2 | High — design-system baselines | After human review of Linux baselines | 1–2 hours | Commit approved Linux visual baselines and promote adoption not-committed → committed. **Stop:** never commit baselines from an unreviewed machine run. | -| 100 | `#244` | A3 | High — design tokens / forced-colors | With any ckb-v2 forced-colours edit | 15–30 min | Keep grouped dark selectors in the forced-colours media block so specificity matches dark rules. **Stop:** do not trim to a single `.ckb-v2.ckb-v2` selector. | -| 101 | `#245` | A3 | High — cross-mode links | Next CrossModeLinks / analytics pass | 30–60 min | responsive-compact CrossModeLinks keeps duplicate rails in the DOM; prefer one mount or accept test double-counts. **Stop:** do not break phone-only rail contract. | -| 102 | `#248` | A2 | Operator — Supabase + Specialist | After PR #1614 symptom repair; approved live/history window | 1–2 hours | Investigate why 20260705180000 search-health indexes were missing on live despite applied history; decide if drift checks should catch this class. **Stop:** no hosted mutation without approval. | -| 103 | `#249` | A3 | High — agent process | Next issues-skill / plan touch | 1–2 hours | Extend issues/plan with an agent-safe wins classifier (optional filter; no new skill unless reused thrice). **Stop:** do not outrank A1 operator work. | -| 104 | `#250` | A2 | High — multi-agent execution | After Wave 0 queue repair on main (done in this capture); run remaining Wave 0/#202 process gates next on the engineering track | multi-wave | Execute the fastest-wins multi-wave plan (Waves 0–4 + operator track) with parallel agents and per-PR gates. Waves do not outrank A1 acuity. **Stop:** provider/RAG approvals still required where flagged. | -| 105 | `#251` | Optional | High — agent process | Next handoff/gates doc touch | 15–30 min | Handoff checklist pairs gates skill with verification-router; paste decisive proof line. **Stop:** do not stack broad gates by default. | -| 106 | `#253` | A2 | High — phone results UI | Next open-PR sweep | 15–30 min | Decide #1606's fate: the `MobileResultFilterControl` it rewrites was deleted by #247, so there is nothing left to hand-merge. Verify keyboard parity of the replacement sheet on a real device, then close #1606 as superseded. **Stop:** the decision is a human's; do not close #1606 automatically. | -| 107 | `#254` | A2 | Operator — Codex Cloud | Before #1617 leaves draft | 1–2 hours | Re-run Codex Cloud acceptance at the exact current head or mark head-independent evidence. **Stop:** do not treat stale pins as coverage. | -| 108 | `#255` | A2 | High — Cloud/browser gates | Next environment image update | 2–4 hours | Align Cloud Playwright browser builds with lockfile pin; document CI delegation until then. **Stop:** do not force mismatched Chromium revisions. | -| 109 | `#256` | A2 | High — mode section nav | Next information-page / mode-nav pass | 2–4 hours | Declared information-page section sets whose target ids nothing renders — verify each set against the rendered DOM per route; render anchors or delete the set. **Stop:** do not audit by grepping for `id=` alone (sectionId props exist). | -| 110 | `#257` | Optional | High — formulation/specifiers flake | Standing until second reproduction | 15–30 min | Single unreproduced ui-formulation flake when run with ui-specifiers — record a second sighting only; do not quarantine until three on the same SHA. **Stop:** do not weaken assertions. | -| 111 | `#286` | A3 | High — in-page nav + frontend | After owner go-ahead for the information-page series | 1–2 days | Convert the six pill-rail information pages onto `InPageNavHeader`, widen Server Component–safe actions, then delete `informationPageSectionDefinitions`. **Gate:** focused DOM/contract tests + `verify:phone-chrome` for touched owners. **Stop:** do not convert DocumentViewer here; do not verify anchors by grepping `id=` alone. | -| 112 | `#287` | A3 | High — in-page nav + clinical owner | After `#286`; medications needs an owner product call | 0.5–1 day design + convert | Decide medications tab model, presentations MobileTabs vs `InPageNavHeader`, and factsheets heading→id scheme; convert or record lasting exceptions. **Stop:** do not port medications mechanically. | -| 113 | `#288` | Optional | High — document chrome | After `#286`/`#287`, or when declaring the series complete | 30–60 min | Confirm DocumentViewer non-adoption (already noted in `docs/search-chrome-behaviour.md`) as the final end state, or schedule a separate convergence PR that leaves pinned `--document-*` CSS names untouched. | -| 114 | `#289` | A2 | High — auth/identity | Next auth module touch | 1–2 hours | Export a named helper (e.g. `authorizationIdentity(headers)`) from the auth module and use it at every property-access call site; consider a lint rule or branded type so `.Authorization` stops type-checking at all. **Stop:** do not change `authorizationHeadersForAccessToken` to emit uppercase — lowercase is the correct Fetch/Headers convention and callers that pass the object wholesale to `fetch` depend on it. | +| 44 | `#168` | A3 | High — ledger architecture | With #156 / id-scheme redesign | design first | Sequential issue ids force every concurrent append to conflict — two sessions can append to this ledger at the same time without conflicting. | +| 45 | `#169` | A3 | High — git hygiene | Next branch cleanup batch | 1–2 hours | Local branches carry work that exists on no remote — committed work is not lost when a machine or worktree is reclaimed. | +| 46 | `#170` | A2 | High — phone UI | Next documents/filter phone pass | 0.5–1 day | Documents and therapy already have page-owned phone filter sheets; remaining modes still use inline controls — shared-band Filter+Sheet adoption without regressing those two or sheetless Sort. | +| 47 | `#171` | A2 | High — documents UI | With #170 / filter consolidation | 0.5–1.5 days | Documents mode has four overlapping filtering surfaces, two of them the same job — one Filter control opening one panel, so a reader learns filtering once. | +| 48 | `#175` | A2 | Operator — clinical data + Standard | Next therapy catalogue curation window | 2–4 hours | Therapy modality is now null on all 205 records and needs curation or removal — the Therapy detail and recommend screens either show a curated modality or stop carrying the field at all. | +| 49 | `#178` | A3 | High — PR policy | Next pr-policy change | 1–2 hours | pr-policy does not flag operational risk bundled with clinical or UI risk — a PR that mixes operational-risk paths with clinical or UI risk is called out before it merges, because squash-merging that mix destroys per-it… | +| 50 | `#189` | A2 | Specialist — search/RAG budgets | After #098 route residual; before collapsing RPCs | 2–4 hours + canary if behaviour | Pin /api/search route-level round trips and disposition the x3 text RPC probes — a counting-proxy budget drives `POST` `/api/search` (auth/ratelimit/scope/enrichment/telemetry), and the retrieval-core finding that `matc… | +| 51 | `#036` | Optional | Specialist — privacy/schema | When visibility model is redesigned | design + migration | No explicit `is_public` visibility flag on documents — Public-corpus visibility is implicit: `owner_id IS NULL` on an `indexed` document (`resolveSearchScope`). The `metadata.public_corpus` marker is written by the prom… | +| 52 | `#101` | A3 | Specialist — RAG/retrieval | After #186 update + canary approval | canary-gated | Canary-gated retrieval parallelisation candidates — independent retrieval stages stop running serially, proven by a live canary pair. Candidates: metadata/memory/visual hydration triples repeated on four branches (`rag.… | +| 53 | `#142` | Optional | High — docs hygiene | Next docs filing pass | 1–2 hours | Four loose dated docs need source and migration edits before they can be filed — 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. | +| 54 | `#151` | Optional | Operator — GitHub PAT | When writing the durable note (#187) | 15–30 min | `gh pr checks` cannot read CI, but the Actions API can — nobody concludes CI is unverifiable when it is merely reached through a different endpoint. | +| 55 | `#154` | Optional | High — agent process | When writing the durable note (#187) | 15–30 min | Row ids are not stable identifiers for "did my change land" — an agent confirms work reached `main` by content, never by id, title or PR state. | +| 56 | `#156` | A3 | High — ledger architecture | With #168 id-scheme work | design first | Outstanding-issues ids are still allocated read-modify-write, and Update-branch corrupts the merge — two branches cannot silently claim the same outstanding-issues id, and no merge path can commit a file where they have. | +| 57 | `#172` | A3 | High — documents UI | With #171 filter consolidation | 1–2 hours | `Sources` sits in the results bar but is navigation, not a filter — the results bar holds only controls that act on the current results. | +| 58 | `#174` | A3 | High — search facets | When facet UX is redesigned | 0.5–1 day | Facets AND within a group, so two values from one group almost always return nothing — a decision on record, either way. | +| 59 | `#177` | A3 | High — therapy catalogue build | Next therapy-index build change | 1–2 hours | Therapy catalogue aliases duplicate 2.53 MB of bytes instead of pointing at the hashed file — the unversioned catalogue aliases stop costing a second copy of every payload in the repo and the image. | +| 60 | `#179` | A3 | High — therapy catalogue build | With #177/#180 | 1–2 hours | The full therapy catalogue silently switched from minified to pretty-printed — the full catalogue's on-disk format is a decision someone made, not a side effect. | +| 61 | `#180` | A3 | High — therapy catalogue build | With #177/#179 | 1–2 hours | build-therapies-index now overwrites its own source input — the therapy catalogue generator has a source it does not also destroy. | +| 62 | `#181` | Optional | High — documents UI clarification | When updating #171 | 15–30 min | Correction to `#171`: source-type does NOT duplicate the `Document type` facet group — `#171` states that the documents source-type control "duplicates the facet group already named `Document type`". That is wrong, and … | +| 63 | `#188` | A3 | Operator — DR/SRE | After any schema restore drill, or next DR review | checklist-owned | Document and track disaster-recovery re-creation checklist as ledger work — the five DR items that do not survive a schema restore are tracked with owners and verify steps, not only in `docs/operator-backlog.md`. | +| 64 | `#190` | A3 | Specialist — RAG structure | On explicit X3 go-ahead | 1 PR per extraction unit | X3: Finish rag.ts monolith decomposition — `src/lib/rag/rag.ts` is decomposed into focused modules per `docs/maturity-backlog-workorders.md` X3, with existing offline RAG contracts green. | +| 65 | `#191` | A3 | Operator — DB + Specialist | Approved live-DB window only | provider-gated | X5: ACL-migration consolidation (provider-gated) — ACL-related migrations are consolidated per maturity work-order X5 without weakening owner-scope/RLS. | +| 66 | `#192` | A3 | High — test coverage | Next coverage-floor pass | 0.5–1 day | X6: Raise clinical/retrieval/answer coverage floors — coverage floors for clinical, retrieval, and answer domains meet the maturity X6 targets with CI enforcing them. | +| 67 | `#193` | A3 | High — src/lib structure | After/with X3 non-protected clusters | 1 PR per cluster | X7: Complete the remaining src/lib domain-directory reorg — remaining `src/lib` clusters sit in their domain directories per X7 follow-on to X2. | +| 68 | `#194` | A3 | High — scripts/docs hygiene | Next scripts archive pass | 1–2 hours | L1: Archive retired backfill one-shots and dead ci-change-scope token — retired `backfill:*` one-shots and the dead `ci-change-scope` token are archived/removed with docs/script index updated. | +| 69 | `#195` | A3 | Operator — GitHub maintainer | Maintainer UI window | 30–60 min | M1: Repo-host hardening (branch protection and required checks) — GitHub branch-protection rulesets and required checks match audit §8 / maturity M1. | +| 70 | `#196` | A3 | Operator — DR/SRE | After schema restore drill | 1–2 hours | DR: Re-create pg_cron schedules after schema restore — ingestion/retention and related pg_cron schedules exist on the target DB after any schema restore. | +| 71 | `#197` | A3 | Operator — DR/SRE | After schema restore drill | 30–60 min | DR: Re-add Vault secrets including cron_ingestion_jwt — required Vault secrets (at least `cron_ingestion_jwt`) are present after schema restore. | +| 72 | `#198` | A3 | Operator — DR/SRE | After schema restore drill | 30–60 min | DR: Re-set custom database GUCs after schema restore — custom `app.*` GUCs required by the app/worker are set on the restored database. | +| 73 | `#199` | A3 | Operator — DR/SRE | After schema restore; Deno v2 available | 1–2 hours | DR: Redeploy Supabase edge functions (Deno v2.x) — required edge functions are deployed to the target project with Deno v2.x. | +| 74 | `#200` | A3 | Operator — DR/SRE | After schema restore drill | 1–2 hours | DR: Re-enter dashboard config after schema restore — auth providers/SSO redirect URLs, connection-pool caps, per-project keys, and `E2E_USER_*` are re-entered in the Supabase/Railway dashboards after restore. | +| 75 | `#183` | A2 | Operator — Sentry + Specialist | Next approved observability window with SENTRY_AUTH_TOKEN | 1–2 hours | Create Sentry metric alert for production DB span p95 > 500ms (`span.op:db`, environment production). **Stop:** no secret printing; blocked until token/env available. | +| 76 | `#206` | A2 | Specialist — answer UI contract | With AnswerState producer work (`#207`) | 2–4 hours | `partial_retrieval` has no app-facing producer — decide RAG contract vs UI-only mapping before AnswerCard. **Stop:** no retrieval behaviour change without RAG flag. | +| 77 | `#208` | A2 | Specialist — clinical copy | With answer clipboard / PR-13 work | 1–2 hours | `answerClipboardText` must not replace `formatAnswerRenderCopyText` — compose render-policy warnings. **Gate:** focused clipboard/copy tests. **Stop:** do not drop render-policy caveats. | +| 78 | `#216` | A2 | High — design-system answer shell | After `#207` and clinical surface decision | 0.5–1 day | Adopt AnswerCard on the answer surface (deferred from PR-J). Own PR, own `verify:ui`. **Stop:** not before `#207`; show both surface treatments before choosing. | +| 79 | `#230` | A2 | High — PR policy / CI | Next ci.yml / pr-policy change | 1–2 hours | PR-policy body sync must no-op unless `PR_POLICY_BODY.md` is new in that PR's own diff (or move body out of repo). **Gate:** `check:github-actions` / workflow self-test. **Stop:** do not re-commit scratch bodies to main. | +| 80 | `#232` | A2 | High — review ledger hygiene | Next ledger touch for PR-J | 30–60 min | Supersede the PR-J clinical-governance ledger row so it describes the merged head (`ledger:append --supersede`). **Stop:** append-only — never edit/delete the old row. | +| 81 | `#209` | A3 | High — design tokens / contrast | Next Gate 1 / warning-token pass | 1–2 hours | Add contrast pair for `--warning` used as body text (VerificationNotice / DoseLine). **Gate:** design-system contrast checks. **Stop:** do not invent a new status token without TOKENS.md. | +| 82 | `#211` | A3 | High — TypeScript strictness | Dedicated migration branch | multi-PR | Plan and start `noUncheckedIndexedAccess` migration (1266 errors); highest-risk files first. **Stop:** do not flip the flag on main without a staged plan. | +| 83 | `#212` | A3 | High — runtime validation | After highest-risk cast inventory | multi-PR | Replace `as unknown as` and unvalidated `JSON.parse` with Zod/guards at trust boundaries. **Stop:** RAG/provider boundaries need clinical/privacy care. | +| 84 | `#213` | A3 | High — error handling | Next fetch/stream hardening pass | 0.5–1 day | Stop swallowing fetch/stream errors with empty catches; check `response.ok`. **Stop:** do not change telemetry contracts silently. | +| 85 | `#215` | Optional | High — image perf | Next image/PWA pass | 2–4 hours | Image-optimization basics for lightbox, PWA lifecycle, demo PNGs. **Stop:** optional until measured need. | +| 86 | `#221` | A3 | High — design-system convergence | After `#218` cn() decision | 0.5–1 day | Converge remaining local EmptyState/LoadingState/Chip duplicates. **Stop:** not piecemeal before cn()/Chip decisions. | +| 87 | `#222` | A3 | High — headers / search chrome | During headers redesign decision | 2–4 hours | Decide whether mode-home-template / search-results-header-band are in PageHeader scope or permanently out. **Stop:** do not flatten phone composer ownership. | +| 88 | `#233` | A3 | High — design-system docs | Next COMPONENTS.md docs PR | 1–2 hours | Refresh section 0 maturity matrix and document FormField optionality-marker contract. **Gate:** docs checks. **Stop:** docs-only; no product behaviour change. | +| 89 | `#234` | A3 | High — design-system docs | With answer-surface docs | 30–60 min | Document `answer-copy-payload.ts` as the clipboard contract for three surfaces. **Stop:** do not add a second copy builder. | +| 90 | `#235` | A3 | High — design-system evidence | Next warmed local proof-shot pass | 1–2 hours | Capture missing ADOPTION.md §7 proof shots for adopted surfaces. **Stop:** not visual-baseline PNGs (`#118`); no Playwright snapshot commit. | +| 91 | `#236` | Optional | High — branch hygiene | Next cleanup batch with `#079` | 30–60 min | Dispose orphan DS V2 builder branches and leftover wave-5 dev servers. **Stop:** content-verify before delete; no force-clean. | +| 92 | `#237` | A3 | High — design-system a11y | Before freezing Linux visual baselines (#242) | 30–60 min | Eyeball low-confidence AccessibleTable densities at 320px; MissingValue phrases must remain readable. **Gate:** visual spot-check only. **Stop:** do not abbreviate MissingValue to a dash. | +| 93 | `#238` | A3 | High — overlays/UI | After Sheet portal default change (#1616) | 30–60 min | Visual pass for Sheet portal default on settings, sidebar, and answer overlays under OverlayRoot. **Stop:** do not revert portal default without evidence. | +| 94 | `#239` | Optional | High — phone chrome | When phone orientation QA is available | 15–30 min | Manual phone rotation check for ResizeObserver-only phone chrome reserve. **Gate:** `verify:phone-chrome` still owns automated coverage. **Stop:** do not widen reserve heuristics without reproduction. | +| 95 | `#240` | Optional | High — design tokens | Next design-owner review | 15–30 min | Confirm tooltip visual hard-clip asymmetry with design owner (sr-only keeps full text). **Stop:** no product change without that confirmation. | +| 96 | `#241` | A3 | High — therapy catalogue | Standing; with any therapy-home change | 15–30 min | Therapy home summary count/slugs remain build-time; keep `build-therapies-index --check` load-bearing. **Stop:** do not bypass the check. | +| 97 | `#242` | A2 | High — design-system baselines | After human review of Linux baselines | 1–2 hours | Commit approved Linux visual baselines and promote adoption not-committed → committed. **Stop:** never commit baselines from an unreviewed machine run. | +| 98 | `#244` | A3 | High — design tokens / forced-colors | With any ckb-v2 forced-colours edit | 15–30 min | Keep grouped dark selectors in the forced-colours media block so specificity matches dark rules. **Stop:** do not trim to a single `.ckb-v2.ckb-v2` selector. | +| 99 | `#245` | A3 | High — cross-mode links | Next CrossModeLinks / analytics pass | 30–60 min | responsive-compact CrossModeLinks keeps duplicate rails in the DOM; prefer one mount or accept test double-counts. **Stop:** do not break phone-only rail contract. | +| 100 | `#248` | A2 | Operator — Supabase + Specialist | After PR #1614 symptom repair; approved live/history window | 1–2 hours | Investigate why 20260705180000 search-health indexes were missing on live despite applied history; decide if drift checks should catch this class. **Stop:** no hosted mutation without approval. | +| 101 | `#249` | A3 | High — agent process | Next issues-skill / plan touch | 1–2 hours | Extend issues/plan with an agent-safe wins classifier (optional filter; no new skill unless reused thrice). **Stop:** do not outrank A1 operator work. | +| 102 | `#250` | A2 | High — multi-agent execution | After Wave 0 queue repair on main (done in this capture); run remaining Wave 0/#202 process gates next on the engineering track | multi-wave | Execute the fastest-wins multi-wave plan (Waves 0–4 + operator track) with parallel agents and per-PR gates. Waves do not outrank A1 acuity. **Stop:** provider/RAG approvals still required where flagged. | +| 103 | `#251` | Optional | High — agent process | Next handoff/gates doc touch | 15–30 min | Handoff checklist pairs gates skill with verification-router; paste decisive proof line. **Stop:** do not stack broad gates by default. | +| 104 | `#253` | A2 | High — phone results UI | Next open-PR sweep | 15–30 min | Decide #1606's fate: the `MobileResultFilterControl` it rewrites was deleted by #247, so there is nothing left to hand-merge. Verify keyboard parity of the replacement sheet on a real device, then close #1606 as superseded. **Stop:** the decision is a human's; do not close #1606 automatically. | +| 105 | `#254` | A2 | Operator — Codex Cloud | Before #1617 leaves draft | 1–2 hours | Re-run Codex Cloud acceptance at the exact current head or mark head-independent evidence. **Stop:** do not treat stale pins as coverage. | +| 106 | `#256` | A2 | High — mode section nav | Next information-page / mode-nav pass | 2–4 hours | Declared information-page section sets whose target ids nothing renders — verify each set against the rendered DOM per route; render anchors or delete the set. **Stop:** do not audit by grepping for `id=` alone (sectionId props exist). | +| 107 | `#257` | Optional | High — formulation/specifiers flake | Standing until second reproduction | 15–30 min | Single unreproduced ui-formulation flake when run with ui-specifiers — record a second sighting only; do not quarantine until three on the same SHA. **Stop:** do not weaken assertions. | +| 108 | `#286` | A3 | High — in-page nav + frontend | After owner go-ahead for the information-page series | 1–2 days | Convert the six pill-rail information pages onto `InPageNavHeader`, widen Server Component–safe actions, then delete `informationPageSectionDefinitions`. **Gate:** focused DOM/contract tests + `verify:phone-chrome` for touched owners. **Stop:** do not convert DocumentViewer here; do not verify anchors by grepping `id=` alone. | +| 109 | `#287` | A3 | High — in-page nav + clinical owner | After `#286`; medications needs an owner product call | 0.5–1 day design + convert | Decide medications tab model, presentations MobileTabs vs `InPageNavHeader`, and factsheets heading→id scheme; convert or record lasting exceptions. **Stop:** do not port medications mechanically. | +| 110 | `#288` | Optional | High — document chrome | After `#286`/`#287`, or when declaring the series complete | 30–60 min | Confirm DocumentViewer non-adoption (already noted in `docs/search-chrome-behaviour.md`) as the final end state, or schedule a separate convergence PR that leaves pinned `--document-*` CSS names untouched. | +| 111 | `#289` | A2 | High — auth/identity | Next auth module touch | 1–2 hours | Export a named helper (e.g. `authorizationIdentity(headers)`) from the auth module and use it at every property-access call site; consider a lint rule or branded type so `.Authorization` stops type-checking at all. **Stop:** do not change `authorizationHeadersForAccessToken` to emit uppercase — lowercase is the correct Fetch/Headers convention and callers that pass the object wholesale to `fetch` depend on it. | @@ -231,7 +228,6 @@ removed after current-main verification; it is not missing recommended work. | #164 | P2 | task | Redesign Favourites as hybrid dashboard + search (no ModeHome) | **Outcome:** `/favourites` is one dashboard+search workspace; empty query shows Continue/recent/sets/table; typed query filters in place; no ModeHome hero. **Product pick:** Search-Led Workspace (direction B) from comps in `public/mockups/mode-page-redesign-2026-07/favourites-hybrid/`. User rejected ModeHome for Favourites. **Next:** implement B; retire command-library marketing H1 and redundant dual search. **Stop:** do not reintroduce ModeHome or a separate Favourites home route. | session 2026-07-31 mode-page design audit; user Favourites hybrid decision | 2026-07-31 | | #165 | P2 | task | Adopt a consolidated answer-home notice block — the studies exist, nothing adopts them | **Outcome:** the answer hero states its safety obligation, its scope, and its verification requirement as one block in one voice. **Detail:** `/mockups/warning-consolidation` (PR #1437) diagnoses today's three stacked notices — the APP-5 privacy warning at 11px muted, a bare `/privacy` link, and an accent-blue `ShieldCheck` capability claim at 14px semibold — and shows the hierarchy is inverted: the least important line is the loudest, and two shields with opposite meanings sit ~40px apart. Three consolidations are drawn at 1440px and 390px. Recommended: **02 Safety card** on the hero (obligation on a warning-tinted top row, everything descriptive in one grey voice below) and **01 Assurance bar** on the docked composer — the same content model at two densities, so one component with a `density` prop covers both. **This is a governance change, not just a design one:** `PrivacyInputNotice` is the single site-wide APP-5 line and renders on the answer, documents and calculators composers, so all three move together; `tests/privacy-ui.test.ts`, `tests/ui-accessibility.spec.ts` and the phone-chrome reserve coverage all assert against the current markup and must change in the same commit; and the PR will need a full `## Clinical Governance Preflight` (the mockup PR correctly did not). **Third study (before/after):** `/mockups/answer-home-proposal` draws the concrete D-direction proposal as a full hero before/after rather than an isolated notice. **Second study (words only):** `/mockups/warning-line` answers a narrower brief — no icon, border, tint or background, one line where width allows. Six variants A-F; line counts measured from the rendered DOM, not asserted. Only B (middot clauses), D (obligation + verify) and F (compressed obligation) hold one line at desktop width, and **none fit one line on a 390px phone while the pinned APP-5 sentence stays verbatim** — 46 characters of obligation plus the 27-character link exceeds the ~60 available at 11px. Recommended there: **D**, the only compliant variant that is both one line and keeps weight-only hierarchy, reached by dropping the scope claim (a capability statement already visible on the answer itself). F fits best but rewrites the pinned obligation to \|No patient-identifiable information.\| and so needs the same privacy sign-off as `#166` plus a matching `tests/privacy-ui.test.ts` update. **Status:** PR #1437 was closed unmerged on 2026-07-30 as a deliberate pause during an owner-authorized ordered merge sweep, to be reopened at its queued place; branch `claude/warning-consolidation-mockups-09jyj7` is preserved and merged onto current `main`; these follow-up rows have been renumbered on each sync because `main` kept claiming the next ids while the PR was paused; the superseded numbers are deliberately not listed, since they now belong to unrelated rows. **Next:** decide block (02 + 01) versus line (D) direction, get wording sign-off for `#166`, then implement behind one component and run `verify:phone-chrome` before `verify:ui`. | session 2026-07-30; PR #1437; `/mockups/warning-consolidation`; `/mockups/warning-line` | 2026-07-30 | | #166 | P2 | issue | Answer mode ships no verify-before-use caveat; every other clinical mode does | **Outcome:** the surface that actually generates prose from retrieved sources says so, and says it must be checked. **Detail:** differentials carry "Clinical decision support only. Review before use.", prescribing carries "Confirm against source", specifiers carry a confirm-the-manual line, and calculators carry "Scores support clinical judgement — they never replace a full assessment." The answer hero carries neither an equivalent nor anything about generation: only the APP-5 privacy line and "Searches indexed clinical sources", which reads as assurance rather than caution. `CLAUDE.md` calls this repo a clinical reference prototype and explicitly **not** validated clinical decision support, so the one mode that synthesises text is the one most needing the caveat. Proposed wording, matching the registers above rather than opening a new one: "Answers are AI-generated — verify against the cited source before clinical use." **Independent of `#165`:** even keeping today's three-notice layout, the missing sentence is the gap. **Next:** clinical-governance sign-off on the exact wording, then add it to the answer hero (bundled with `#165` if that lands first). | session 2026-07-30; PR #1437; `src/components/clinical-dashboard/answer-status.tsx` | 2026-07-30 | -| #167 | P2 | issue | `verify:pr-local` exits 0 when its own build step refuses to run | **Outcome:** the PR-local gate cannot report success for a step that never executed. **Detail:** on 2026-07-30 `npm run verify:pr-local` selected the conditional production build for a UI diff; `scripts/guard-next-build.mjs` printed `Refusing to run next build while Clinical KB dev server is running. Stop the dev server first, or set ALLOW_BUILD_WITH_DEV_SERVER=1` — and the aggregate still exited **0**, so the run reported green with the build never run. Caught only by reading the tail of the log; `npm run build` was then re-run separately with the server stopped and passed. Same family as `#120` (`verify:phone-chrome` exits 0 while reporting failed browser tests) and exactly the trap `AGENTS.md` names — "exit code 0 alone is not proof". The guard itself is correct and protects the dev cache; what is wrong is the aggregate treating a refusal as a pass. **Next:** make the refusal exit non-zero, or have `verify:pr-local` list skipped-but-selected steps in its closing summary so a green exit cannot be misread as a build. | session 2026-07-30; PR #1437; `scripts/guard-next-build.mjs` | 2026-07-30 | | #168 | P2 | rec | Sequential issue ids force every concurrent append to conflict | **Outcome:** two sessions can append to this ledger at the same time without conflicting. **Detail:** ids are allocated read-modify-write against the `issues:next-id` marker inside the file being edited, so two branches both read N and both write N. Because duplicate ids are unacceptable, a union merge driver is unsafe — .gitattributes says so explicitly — which is why this file deliberately has no driver and every overlapping append conflicts by hand. Manual resolution is where rows get dropped: PR #1490 was closed during one and took the only record of four snapshots with it (#152), and ids were renumbered under in-flight work three times in one session (#154, #155). The new writer (`scripts/outstanding-issues.mjs`) removes the mechanical errors but explicitly not this one. **Next:** replace the counter with a collision-free id (ULID, timestamp+suffix, or a content hash), keeping a short display number derived at render time if `#151` reads better than 01JQ…; then a union driver becomes safe to reinstate and concurrent appends stop conflicting at all. A larger variant is one row per file under `docs/issues` with the table generated, which the repo already does for `site-map.md`. **Stop:** do not reinstate `merge=union` while ids are sequential — that combination was tried in PR #1416 and removed for duplicating rows and the marker. Renumbered from this PR's original `#159` because `main` already used `#159` for the duplicated test-file-list finding. | session 2026-07-31; .gitattributes; #154/#155; PR #1524 sync | 2026-07-31 | | #169 | P2 | issue | Local branches carry work that exists on no remote | **Outcome:** committed work is not lost when a machine or worktree is reclaimed. **Detail 2026-07-31:** six `claude/*` branches in this checkout have commits and no `origin/` counterpart. Verified real for `claude/clinical-kb-design-system-333a69` — 57 files / +4069, tip `feat(design-system): v2 token layer, 26 components, browser-crash fix` dated 2026-07-31 17:40, whose added `.design-sync/previews/*.tsx` files are absent from main. Others unverified: `design-sync-db0a54`, `fable-implementation-fc937c`, `frosty-mayer-2c6167`, `issues-133-evidence`. **How to check, because the obvious measure lies:** `git rev-list --count origin/main..` and a three-dot diff both report landed work as unmerged, since this repo squash-merges and the original commits never become ancestors — my own merged branch reported 1 commit and +476 by that measure. Test instead whether files the branch adds exist on main (`git ls-tree origin/main `). **Next:** per branch, push it for review or confirm it is superseded and delete it; do not bulk-delete on the commit count. Sibling of #152, which covers uncommitted work in worktrees rather than unpushed commits on branches. | session 2026-07-31; local branch audit | 2026-07-31 | | #170 | P2 | task | Phone filter sheets exist for documents and therapy; shared-band adoption remains | **Outcome:** phone filtering opens the repo's own bottom sheet instead of competing for width in the utilities rail. **Current state (2026-08-04):** documents already mounts a Filter documents Sheet, and therapy-compass mounts TherapyFilterSheet; both also pass appliedFilters into SearchResultsHeaderBand. The remaining modes still keep filter controls inline (or page-owned chips/nav), and the shared band has not yet adopted a Filter trigger + Sheet that wraps mobileControls for every mode. **Detail:** SearchResultsHeaderBand still renders mobileControls inline below sm for pages that supply them that way. src/components/ui/sheet.tsx already provides the primitive — bottom sheet on mobile via sheet-up, centred dialog at sm+, safe-area aware, focus-trapped, Escape and backdrop dismiss — so shared-band adoption is the remaining work, not new UI. Design settled in the round-7 study: one Filter control at both widths, badge counting applied filters, sheet titled Filter and sort because a phone bar cannot fit a labelled Sort beside Filter at 390 px. **Next:** replace the inline mobileControls render with a Filter trigger + Sheet containing mobileControls ?? filterControls for the modes that still lack a page-owned sheet, without regressing documents/therapy sheets or removing Sort from sheetless Sort consumers (differentials, forms, services). **Stop:** at least four Playwright specs drive those inline controls directly (document-source-type-select, search-query-ribbon-mobile-control-pair); budget for updating them, and do not ship without verify:ui — PR #1523 showed Production UI critical does gate this surface properly. | Round-7 design study; PR #1523 notes; `src/components/ui/sheet.tsx` | 2026-07-31 | @@ -261,7 +257,6 @@ removed after current-main verification; it is not missing recommended work. | #198 | P3 | task | DR: Re-set custom database GUCs after schema restore | **Outcome:** custom `app.*` GUCs required by the app/worker are set on the restored database. **Next:** apply from the operator runbook; verify with a read-only show/settings check. Parent `#188`. | docs/operator-backlog.md; #188 | 2026-07-31 | | #199 | P3 | task | DR: Redeploy Supabase edge functions (Deno v2.x) | **Outcome:** required edge functions are deployed to the target project with Deno v2.x. **Next:** operator deploy after restore; confirm function list/health. Parent `#188`. **Stop:** needs Deno toolchain and explicit approval for hosted deploy. | docs/operator-backlog.md; #188 | 2026-07-31 | | #200 | P3 | task | DR: Re-enter dashboard config after schema restore | **Outcome:** auth providers/SSO redirect URLs, connection-pool caps, per-project keys, and `E2E_USER_*` are re-entered in the Supabase/Railway dashboards after restore. **Next:** operator checklist in `docs/operator-backlog.md`. Parent `#188`. **Stop:** do not commit dashboard secrets. | docs/operator-backlog.md; #188 | 2026-07-31 | -| #204 | P2 | issue | npm 11.6.2 regenerates a lockfile its own `npm ci` rejects, reddening every CI job | **Outcome:** a dependency change cannot land a lockfile that installs locally but fails `npm ci` in CI. **Evidence 2026-07-31 (PR #1544):** bumping one dependency range and running `npm install` (npm 11.6.2 / Node 24.13.0) rewrote `package-lock.json` with 310 changed lines and *pruned* the `@emnapi/core` and `@emnapi/runtime` entries (optional wasm32-wasi deps reached via `@tailwindcss/oxide` / `@napi-rs/wasm-runtime`). The same npm's `npm ci` then refuses that lockfile with `Missing: @emnapi/core@1.11.2 from lock file`, so **all 8 CI jobs failed at the dependency-install step before running a single check** — including `autofix`, `Build`, `Unit coverage`, `Static PR checks` and both container images. Nothing about the diff was wrong; the local install was green throughout, because `npm install` accepts what `npm ci` rejects. **Workaround used:** restore `package-lock.json` from `origin/main`, hand-edit only the line(s) that must change (the root `dependencies` range, plus a root entry when adding a direct dep whose package is already hoisted), then gate the push on `npm ci --dry-run` exiting 0. Proven twice on that PR — once for a range bump, once for adding `@sentry/node`. **Next:** add `npm ci --dry-run` (or an equivalent lockfile-sync assertion) to `verify:pr-local` when `package.json`/`package-lock.json` are in the changed set, so this fails locally in seconds instead of costing a full red CI round; then re-test whether a newer npm still prunes the entries. **Stop:** do not 'fix' this by regenerating the lockfile again with `npm install` — that reproduces it. Distinct from #149, which is about `check:installed-lock-parity` scope, not lockfile/CI disagreement. | session 2026-07-31; PR #1544 CI runs 30639506768 / 30640587607 | 2026-07-31 | | #206 | P2 | task | AnswerState partial_retrieval has no app-facing producer | PR-E step 0 found nothing in the client payload names which expected sources were unavailable (retrievalDiagnostics = candidate counts; conflictsOrGaps = prose). RetrievalStateBanner supports the state but PR-J adoption can only emit ready/stale_evidence/source_only. Next action: decide whether a separate RAG contract PR should add a named missing-source signal (governance preflight + RAG impact line + offline eval); until then do not synthesise the state from counts. Pinned by tests/answer-state-contract.test.ts and SPEC 13 / COMPONENTS 2. | PR-E step 0, session 2026-08-02 | 2026-08-02 | | #207 | P1 | task | DS V2 PR 13 blocker: AnswerState has no ungrounded-answer channel | answerStateFromRetrieval() maps a grounded:false / confidence:'unsupported' answer over current sources to 'ready'. The live product already gates on grounded/confidence/unverifiedNumericTokens (evidence-panels.tsx, answer-thread-turn.tsx) to show 'Review source match', so adopting AnswerCard as-is would silently retire a warning shipped today. Needs a fifth state or companion flag; wording is a clinical-owner decision. | clinical-governance-reviewer P1-2 on PR 6 (claude/ds-v2-answer-safety); recorded in docs/design-system/SPEC.md PR 6 clinical review note and COMPONENTS.md 9.13 | 2026-08-02 | | #208 | P2 | task | DS V2: answerClipboardText must not replace formatAnswerRenderCopyText in PR 13 | PR 6 strengthened answerClipboardText (unconditional attribution + verify line, enumerated sources, provenance suppressed where it would contradict the caveat). It is still narrower than formatAnswerRenderCopyText (src/lib/answer-render-policy.ts), which carries the render policy's own warnings. PR 13 must compose the two or extend answerClipboardText with clinical-owner review, never swap it in. | clinical-governance-reviewer P1-1 on PR 6; recorded in docs/design-system/SPEC.md and COMPONENTS.md 9.13 | 2026-08-02 | @@ -296,7 +291,6 @@ removed after current-main verification; it is not missing recommended work. | #251 | P3 | rec | Handoff checklist should pair gates skill with verification-router | **Outcome:** every PR handoff picks the smallest correct gate and pastes the decisive proof line, using verification-router when scope is unclear. **Next:** add one line to handoff/gates productivity defaults: after flightplan, run verification-router (or gates) before claiming green; never report exit 0 alone. **Stop:** do not stack verify:cheap + verify:ui + verify:release by default. | session 2026-08-05 fastest-wins plan | 2026-08-05 | | #253 | P3 | task | #1606 needs a hand-merge against merged PR #1615, not a rebase | SUPERSEDED IN PART 2026-08-07: the component both PRs rewrite no longer exists. `MobileResultFilterControl` — the native `