Repository navigation
Split the bundle budget from mockup scratch, restore local browser gates, and pin the factsheet heading census - #1779
Conversation
The hero <h1> and the portaled print sheet's <h1> are correct and mutually
exclusive by construction: on screen `.factsheet-print-sheet { display: none }`
removes the print subtree, and in print
`html.factsheets-printing body > *:not(.factsheet-print-portal)` removes the
shell that owns the hero. Neither state exposes two headings to the
accessibility tree, and the printed PDF is a separate document whose section
headings are already <h2>, so demoting its title would leave it with no
top-level heading.
The real gap was that the existing assertion was scoped to the page testid, so
the document-level invariant was asserted nowhere and a stray third <h1> would
not have been caught. Pin the census instead: exactly two, one per container,
both carrying the title, plus a non-empty <h2> outline in the print sheet.
jsdom applies no stylesheet, so a census is the right guard rather than a
visibility assertion.
FactsheetPrintSheet stays in factsheet-detail-page.tsx —
design-system-contract-utils.mjs scopes its raw-colour exemption to the literal
factsheet-print-sheet marker and fails closed if it moves.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YEFowCVUVrybKvKvReQ924
… gates The remote/Cloud drift note said to delegate browser proof to CI and left the impression that local gates were unrecoverable. They are recoverable; the blocker was two separate image faults, and the second is why the obvious fix looks impossible. The baked node_modules is stale or incomplete — containers have shipped none at all, and earlier ones reported playwright 1.62.0 against a locked 1.62.1 with tailwind-merge absent entirely, which is an incomplete install rather than a version skew, so the lockfile pin was never wrong. And npm ci cannot repair it because jsdom@30.0.1 requires node ^22.22.2 || ^24.15.0 || >=26.0.0 while images have shipped v24.13.0, so the install dies on EBADENGINE under engine-strict. Installing Node 24.19.0 clears that, npm ci then exits 0 and parity reports all seven pinned packages, and `npx playwright install` supplies Chromium 1234 (images ship only 1194). Verified end to end this session, launch included. Keeps the existing Stop intact and makes it cheap to honour: install the matching revision rather than forcing a run against 1194. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YEFowCVUVrybKvKvReQ924
One number could not honestly answer two questions. totalGzipBytes summed every built client chunk, including src/app/mockups/** design scratch that 404s in production, against a ceiling named as though it were production weight. #13 held that mockup chunks are not a production bundle; this gate charged them anyway, which is how PR #1580 blocked at +10.1% for chunks no user can load. #252 recorded the contradiction and left the metric undecided. Measured on a clean build of main at af85cbc, the blur had become the whole signal: 1546.5 KiB total was +9.96% of the 1406.4 KiB baseline — 576 bytes from failing Build — while production-only was 1279.1 KiB, 9.06% BELOW that same baseline. Every byte of the apparent regression was design scratch (267.5 KiB across 76 chunks over 66 mockup routes) and production had actually shrunk. latency-audit-2026-07-28 corroborates: 1,309,274 bytes then against 1,309,772 production-only now, flat to +0.04%, so the 2026-08-04 bump to 1,440,201 had absorbed mockup growth as production growth. Raising the ceiling again would have hidden that permanently, so this splits rather than ratchets. production (10%) covers every chunk a non-mockup route reaches plus chunks no manifest claims — framework, polyfills, runtime. mockups (25%) covers chunks reachable only from /mockups/**, as a runaway detector rather than a per-mockup gate; a ceiling tight enough to fire on the next mockup would just be --update'd reflexively. A chunk shared by both counts as production because it would be built either way. Attribution reads the per-route *_client-reference-manifest.js files under .next/server/app, since Next 16 webpack emits no app-build-manifest.json, and fails closed when that tree is missing or resolves no routes so the buckets can never silently collapse. Both fail paths proven against the real build. Also captures #296: pr-handoff-stop.test.ts fails in any root container because it chmods a fixture dir to force a write failure and root ignores permission bits — pre-existing, reproduced on clean af85cbc. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YEFowCVUVrybKvKvReQ924
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 26 minutes Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (9)
Comment |
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f77e8db59f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Pull request overview
Splits production and mockup bundle budgets, documents browser-gate recovery, and strengthens factsheet heading coverage.
Changes:
- Adds route-based bundle attribution and separate budgets.
- Documents local Playwright restoration.
- Adds a document-wide factsheet heading census.
A blocking issue remains: malformed manifests can bypass fail-closed attribution.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
scripts/check-bundle-budget.mjs |
Implements split attribution and enforcement. |
bundle-budget.json |
Defines production and mockup baselines. |
tests/bundle-budget.test.ts |
Tests attribution and CLI behavior. |
tests/factsheet-detail-header.dom.test.tsx |
Adds heading census coverage. |
docs/testing.md |
Documents browser-gate recovery. |
docs/plans/document-viewer-phase3-handover.md |
Updates budget guidance. |
docs/outstanding-issues.md |
Records resolutions and follow-ups. |
AGENTS.md |
Documents the two-budget contract. |
CLAUDE.md |
Updates mockup budget guidance. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7a28a89854
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@cursoragent Please can you review all the comments on this page and resolve all of them logically grounded in the current repository and the current branch for me. Check you agree with the suggestion also and ensure no regression with smallest required fix. |
|
You need to increase your spend limit or enable usage-based billing to run background agents. Go to Cursor |
Review feedback on PR #1779: treat unparseable route manifests as fatal attribution errors instead of counting them as resolved empty routes, and handle a zero mockup/production baseline without NaN percentage math. Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
…ates and scripts that no longer exist (#2632) * docs(ingestion): describe the heartbeated, lease-fenced worker lease (L72) Defect: ingestion-state-machine.md, worker-deploy-runbook.md and deployment-architecture.md still said the ingestion lease has no heartbeat and that any job over WORKER_STALE_AFTER_MINUTES is reclaimed while its worker is alive. The worker has refreshed locked_at (guarded by locked_by = workerId) since 2026-07-08 and the completion/failure RPCs are fenced on p_worker_id, so the 45-minute window is a liveness signal. Trigger: reading any of the three documents for replica-sizing or incident diagnosis. Proof: docs-only; `npm run docs:check-links` and `npm run docs:check-scripts` pass on the edited files. The state-machine document now carries a dated status banner and marks each historical "no heartbeat" statement as closed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FSPY4VSqg7WVukCvmHQP9t * docs(care-plan): index the Care Plan authorities and repair three renamed links (L77) Defect: docs/README.md, which CLAUDE.md names as the categorised map of maintained docs, had no entry for the binding Care Plan design spec, glossary, plan or ledgers; and claude-build-handover-2026-08-21.md linked three files under their pre-rename `ed-care-plans` names, which no longer exist. Trigger: a resuming agent following the handover's "Existing planning artifacts" table, or looking for the Care Plan product authority from the docs index. Proof: docs-only; `npm run docs:check-links` resolves the three repaired links and the new index row (5466 references). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FSPY4VSqg7WVukCvmHQP9t * docs(ward-flow): describe the built 17-route sandbox instead of the pre-sandbox world (L79) Defect: ward-management-mode-map.md said the role-first design "has not been built yet" and listed 8 of 17 routes as "what is currently live"; ward-flow-context.md listed `/ward-management` paths that 404 everywhere since the sandbox move; the Phase 3 workspace README claimed 32 files where the directory holds 50. Trigger: any route-coverage or navigation reasoning that starts from those documents. Proof: docs-only. The added table names the nine Phase 3-5 routes from `src/components/ward-management/ward-nav.ts` (the source of truth `tests/ward-landmarks.test.ts` already pins at 17); `npm run docs:check-links` resolves every cited path. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FSPY4VSqg7WVukCvmHQP9t * docs(mockups): say the two page-level mockup redirects are development-only (L83) Defect: mockups/README.md "Production behavior" and the generated site-map caveat said /mockups/favourites-hub and /mockups/medication-prescribing forward in production. Both are page redirect() calls; src/proxy.ts returns 404 for every non-developer-gated /mockups/** path before a page renders, so only /mockups/document-search-command (in staticRouteRedirects) still redirects in production. Trigger: a production request to either path, or a reader relying on the documented compatibility guarantee. Proof: docs and generator string only; `npm run sitemap:update` then `npm run sitemap:check` pass with the regenerated docs/site-map.md. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FSPY4VSqg7WVukCvmHQP9t * docs(site-map): correct the mockup robots/404 caveat to match crawler policy (L84) Defect: the generator's "Known caveats" line said robots.txt disallows indexing of /mockups and that every /mockups route 404s in production. robots.txt (PRIVATE_APP_ROBOTS_TXT) allows everything; the control is the per-response X-Robots-Tag: noindex, nofollow header in next.config.ts, and the four developer-gated subtrees are exempt from the production 404. Trigger: every `npm run sitemap:update`, which re-emitted the false statement into docs/site-map.md. Proof: generator string only; `npm run sitemap:update` regenerated docs/site-map.md and `npm run sitemap:check` passes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FSPY4VSqg7WVukCvmHQP9t * docs(mockups): update the shell-suppression, folder-count and bundle-gate orientation (L85) Defect: mockups/README.md described the pre-#1779 bundle gate (one total, +9.4% against 10%), carried a hard-coded folder count, and said only Favourites/Tools mockups hide the composer and that standalone mockups must move outside /mockups. mockups-layout-client.tsx is now the suppression registry with dozens of route rules and two shell bypasses (Ward Flow, Developer Hub), and bundle-budget.json keeps mockups in a separate 25% bucket. Trigger: anyone adding a mockup or reasoning about why a scratch route was deleted. Proof: prose only; statements checked against src/app/mockups/mockups-layout-client.tsx, bundle-budget.json and commit 89245e7 (#1779). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FSPY4VSqg7WVukCvmHQP9t * brand(llms): name the product PsychSift in llms.txt and move the smoke-test pin with it (L86) Defect: public/llms.txt still called the product "Clinical Guide" (last edited 2026-07-07, before the 2026-08-28 rename) and described /services, /forms and /differentials as opening records although each bare path is a proxy redirect to /?mode=<id> or, when submitted, to <mode>/search. tests/ui-smoke.spec.ts pinned the stale name, so the file could not be corrected alone. Trigger: any agent or crawler reading /llms.txt for product orientation. Proof: the one ui-smoke assertion now expects the brand name (BRAND_NAME in src/lib/brand.ts); route statements checked against src/lib/consolidated-mode-home-redirect.ts and the (search-app) route tree. No unit test covers public/llms.txt; the brand-copy test scans src/ only and is outside this change's owned files. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FSPY4VSqg7WVukCvmHQP9t * docs(pwa): note that the /apple-icon allow-list row is network-only in practice (L87) Defect: docs/pwa.md said /apple-icon is served stale-while-revalidate from the shell cache. Next emits the generated Apple icon as /apple-icon?<contenthash>, and public/sw.js rejects every request with a query string before the allow-list is consulted, so the browser's actual request is never cached. Trigger: reviewing the cache contract or the manual icon checklist against observed service-worker behaviour. Proof: docs-only; traced through public/sw.js (hasSensitiveRequestSignals: url.search !== "") and Next's next-metadata-image-loader hashQuery. `npm run docs:check-links` passes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FSPY4VSqg7WVukCvmHQP9t * docs(scripts-index): catalogue the 44 uncatalogued top-level scripts (L89) Defect: docs/scripts-index.md said every top-level script was named below, but 44 top-level .mjs/.ts scripts — including gate scripts the repo depends on (check-diff-integrity, check-ledger-write-discipline, check-privacy-readiness, check-dead-code-candidate, ledger-inbox) — had no entry, so the doc's live/infra/one-shot triage convention was unreliable for them. Trigger: retirement triage or orientation from the scripts index. Proof: a basename/stem scan of scripts/*.{mjs,ts,cjs} against the doc now reports zero misses; every npm script name cited exists in package.json; `npm run docs:check-scripts`, `docs:check-inventory` and `docs:check-links` pass. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FSPY4VSqg7WVukCvmHQP9t * docs(gates): describe verify:cheap and static-pr as the gate manifest defines them (L96) Defect: README.md listed verify:cheap as six named gates plus lint/typecheck/test and said static-pr always runs action-pin, lint and typecheck; docs/process-hardening.md listed verify:cheap as check:runtime, sitemap:check, lint, typecheck and unit tests. The chain runs 37 static gates before lint/typecheck/test (check:gate-manifest pins the count), and static-pr's unconditional baseline is runtime, installed-lock parity, the CI-scope and verification-plan self-tests, diff integrity and changed-file format; lint/typecheck are scope-gated. Trigger: a reader sizing local verification or CI coverage for a docs/workflow-only PR. Proof: prose only; counts checked against `node scripts/check-gate-manifest.mjs` ("37 static gates") and the `if:` conditions on each static-pr step in .github/workflows/ci.yml. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FSPY4VSqg7WVukCvmHQP9t * docs(archival-policy): quote the records-directory size with its method (L101) Defect: branch-review-archival-policy.md stated the records directory totals 2.4 MB with no method, two lines above its own rule that a figure must carry its method; 2.4 MB is the allocated-block size, the apparent size is 0.39 MB. The src/docs comparison figures had also drifted. Trigger: any reader of the "Growth is the price of the design" section. Proof: prose only; figures re-measured on this branch with `du -sh` and `du -sh --apparent-size`, entry count with `ls | wc -l`, tracked Markdown count with `git ls-files docs`. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FSPY4VSqg7WVukCvmHQP9t * docs(design-qa): mark the root Ward Flow constellation QA handoff as superseded (L97) Defect: design-qa.md at the repository root reported "final result: passed" for a constellation view that Phase 2 retired to a redirect stub, and cited untracked artifacts/ and workstation-local evidence paths; no gate scans root files other than README.md and AGENTS.md. Trigger: a reader taking the root QA verdict as current. Proof: a dated superseded banner naming the retirement and the untracked evidence; the file is kept in place (not moved or deleted) per the package notes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FSPY4VSqg7WVukCvmHQP9t * docs(site-map): describe the nine product routes that showed the generator fallback (L100) Defect: docs/site-map.md rendered "Route discovered from app directory" for /forms/search, /formulation/search, /services/search, /specifiers/search and the five Sources catalogue routes, because the generator's description map had no entry for them; sitemap:check passed because the placeholder is the generator's own output. Trigger: every `npm run sitemap:update` since the Sources mode landed. Proof: descriptions taken from each page's metadata in src/app/(search-app)/**/page.tsx and added to the generator's source of truth; `npm run sitemap:update` regenerated docs/site-map.md and `npm run sitemap:check` passes; no non-mockup, non-API route carries the fallback text now. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FSPY4VSqg7WVukCvmHQP9t * docs(index): catalogue every tracked document under docs/ (L99) Defect: CLAUDE.md sends readers to docs/README.md for "every maintained doc, categorised", but the index listed 115 of 587 documents; the 18 docs/agents rule files AGENTS.md delegates to, 30 root-level maintained docs (operations runbooks, database remediation, RAG evaluation, design and filter contracts, the Ward Flow context and ledgers) and every sub-folder document were reachable only by directory listing. Trigger: any reader or agent starting from the documentation index. Proof: an "Also catalogued" list under each existing category names every remaining tracked document, one line each, with the document's own title and opening sentence; the candidate list was generated from the repo-awareness snapshot's document inventory and re-checked against the index's link targets (zero uncatalogued documents besides the index itself, which now lists itself). `npm run docs:check-links`, `docs:check-scripts`, `docs:check-index` and `check:skills` pass. `check:repo-awareness-snapshot` reports the committed snapshot behind the index; that snapshot is regenerated by the ledger-housekeeping package, not here. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FSPY4VSqg7WVukCvmHQP9t * docs(scripts-index): catalogue six scripts landed after the L89 fix (L89) Defect: docs/scripts-index.md claims "Every top-level .mjs/.ts/.cjs script is named below," but check-chain-mirror-parity.ts, check-dependency-drift.mjs, check-design-drift-ratchet.mjs, stamp-service-worker.mjs, check-ward-data.mjs and run-ward-tests.mjs landed on main (2026-09-03/04, after the L89 commit) and were never added, making the doc's own completeness claim false again on this branch. github-action-pins.mjs is left out deliberately: it is a data module imported only by the already-catalogued check-github-action-pins.mjs, matching the doc's own "small shared helpers grouped rather than itemised" carve-out. Trigger: reviewing L89 by re-deriving the uncatalogued set against this branch's actual scripts/ directory rather than trusting the commit message. Proof: npm run docs:check-scripts and npm run docs:check-inventory both pass after the edit; a script re-diffing docs/scripts-index.md against scripts/*.{mjs,ts,cjs} finds one remaining top-level file (the deliberately grouped helper above). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DHSyfuC6mS98ystWFiitAR * docs(gates): update the static-gate count from 37 to 38 (L96) Defect: README.md and docs/process-hardening.md said verify:cheap runs 37 static/consistency gates, matching the count at the time L96 was fixed (2026-09-02). A gate landed on main since then (the chain now runs check:runtime through check:owner-scope inclusive, 38 gates before lint/typecheck/test), and npm run check:gate-manifest already asserts 38 — so the two prose copies were wrong again on this branch. CLAUDE.md already said 38 and needed no change. Trigger: reviewing L96 by re-running check:gate-manifest against this branch's package.json rather than trusting the commit's counted total. Proof: `npm run check:gate-manifest` reports "the 38 static gates are documented consistently" after this edit; `node -e` confirms check:owner-scope is gate index 38 of 41 in verify:cheap:internal, immediately before lint. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DHSyfuC6mS98ystWFiitAR * Revert "docs(archival-policy): quote the records-directory size with its method (L101)" The programme's own scope table excludes docs/branch-review-* from this package's owned files, and docs/branch-review-archival-policy.md matches that pattern. The content of the reverted change was verified accurate and the repository's ledger guards (check:branch-review-ledger, check:ledger-write-discipline) both pass on it, so this is a scope decision, not a correctness one: L101 is listed in this package's findings while its file is excluded from its owned list. Deferring to the boundary rather than deciding unilaterally on a ledger-adjacent file. L101 is reported to the owner for a ruling and can be re-applied in one commit if it is judged in scope. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DHSyfuC6mS98ystWFiitAR * docs(ward-flow): correct stale route count/list and patients path (L79) A large merge from origin/main expanded Ward Flow from 17 to 32 routes (31 renderable + 1 redirect) and renamed /mockups/ward-flow/patients/[patientId] to /mockups/ward-flow/movements/[movementId], but ward-management-mode-map.md and ward-flow-context.md still said 17 routes and cited the old path. Verified against, in order: (1) tests/ward-landmarks.test.ts's RENDERABLE_ROUTES (31 entries) + REDIRECT_ONLY_ROUTES (1) and its own route-count assertions; (2) a direct filesystem scan of src/app/mockups/ward-flow (32 page.tsx files, including the constellation redirect), which matched the test's list exactly; (3) docs/site-map.md on this branch, which lists the same 32 routes with the same paths. All three sources agreed, so no route was invented. Updated the coordinator-view/role-screen/specialist-board tables and route counts in both docs, replaced every /patients/[patientId] reference with /movements/[movementId], and corrected the "Routes today" summary in ward-flow-context.md to list the full current route set instead of the Phase 3-5-only subset. Left the historical Phase-status narrative (§4's worktree/phase table, §9-§11) untouched — it documents a past build checkpoint, not a current-state claim, and is outside this finding's scope. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DHSyfuC6mS98ystWFiitAR * style: prettier formatting for the p11 remediation package Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DHSyfuC6mS98ystWFiitAR * chore(snapshot): regenerate the repo-awareness snapshot for the newly catalogued docs The L99 and L89 fixes in this package catalogue every tracked document under docs/ and the previously uncatalogued top-level scripts. data/repo-awareness-snapshot.json records a catalogued flag per document, so those fixes make the committed snapshot stale and check:repo-awareness-snapshot fails closed on it. Regenerated with npm run snapshot:repo-awareness; the diff is the catalogued flags flipping to true plus the captured revision, with no route or module changes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DHSyfuC6mS98ystWFiitAR * docs(site-map): keep the legacy-compatibility wording the site-map test pins (L83/L84) The L83/L84 caveat rewrite replaced "legacy compatibility route" with "page-level compatibility redirects", which is more accurate but drops the exact phrase tests/site-map.test.ts:207 pins. That test file is not owned by this package, so the caveat now carries both: the routes are named legacy compatibility routes, and the sentence still says their redirects work in development only while production returns 404 first. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DHSyfuC6mS98ystWFiitAR * chore(snapshot): regenerate the repo-awareness snapshot after the main merge The snapshot records the revision it was captured at, so regenerating it before the merge commit left it pointing at a sha that is no longer HEAD and check:repo-awareness-snapshot failed closed on it. Regenerated on the merged tree. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DHSyfuC6mS98ystWFiitAR --------- Co-authored-by: Claude <noreply@anthropic.com>
Summary
Three unrelated follow-ups from PR #1769, kept as three separately revertible commits rather than one blended diff. The repo's own
classifyPullRequestFilesreturnsclinicalRisk: false,operationalRisk: false,ragRanking: false,ui: falsefor the combined file list, and none of the paths fall in the broader PR-risk list, so all three are independently low-risk under both bundling checks inAGENTS.md.fix(bundle-budget): split production weight from mockup scratch (#252, half of#013).totalGzipBytessummed every built client chunk — includingsrc/app/mockups/**design scratch that 404s in production — against a ceiling named as though it were production weight.#013held that mockup chunks are not a production bundle; the gate charged them anyway, which is how PR Add three Clinical KB guide wireframe mockups #1580 blocked at+10.1%for chunks no user can load. Measured on a clean build ofmainataf85cbc, the blur had become the entire signal: 1546.5 KiB total was +9.96% of the 1406.4 KiB baseline — 576 bytes from failingBuild— while production-only was 1279.1 KiB, 9.06% below that same baseline. Every byte of the apparent regression was design scratch (267.5 KiB across 76 chunks over 66 mockup routes, 17.29% of the total); production had actually shrunk.docs/audit/latency-audit-2026-07-28.mdcorroborates — 1,309,274 bytes then against 1,309,772 production-only now, flat to +0.04% — so the 2026-08-04 bump to 1,440,201 had absorbed mockup growth as production growth. This therefore splits rather than ratchets:production(10%) covers every chunk a non-mockup route reaches plus chunks no manifest claims (framework, polyfills, runtime);mockups(25%) covers chunks reachable only from/mockups/**, as a runaway detector rather than a per-mockup gate. A chunk shared by both counts as production, because it would be built either way. Attribution reads the per-route*_client-reference-manifest.jsfiles under.next/server/app(Next 16 webpack emits noapp-build-manifest.json) and fails closed when that tree is missing or resolves no routes, so the buckets can never silently collapse.docs(testing): record the verified recipe for restoring local browser gates (#255). The drift note implied local browser gates were unrecoverable. They are recoverable, and the reason the obvious fix looked impossible was a second, separate image fault: the bakednode_modulesis stale or incomplete (containers have shipped none at all; earlier ones reportedplaywright1.62.0 against a locked 1.62.1 withtailwind-mergeabsent entirely — an incomplete install, not a version skew, so the lockfile pin was never wrong), andnpm cicannot repair it becausejsdom@30.0.1requires^22.22.2 || ^24.15.0 || >=26.0.0while images have shipped v24.13.0, so it dies onEBADENGINEunderengine-strict. Installing Node 24.19.0 clears that;npm cithen exits 0 and parity reports all seven pinned packages;npx playwright installsupplies Chromium 1234 (images ship only 1194). Verified end to end, launch included. No dependency or lockfile change — the container fix is not committable, so this commit is documentation only.test(factsheets): census everyh1in the factsheet detail document (#295). Investigated the reported double-<h1>and concluded it is correct and should stay. The two headings are mutually exclusive by construction: on screen.factsheet-print-sheet { display: none }removes the print subtree, and in printhtml.factsheets-printing body > *:not(.factsheet-print-portal)removes the shell that owns the hero, so neither state exposes two headings to the accessibility tree. The printed PDF is a separate document whose section headings are already<h2>, so demoting its title would leave it with no top-level heading — a regression, not a fix. The real gap was that the existing assertion was scoped to the page testid, leaving the document-level invariant asserted nowhere; that is what this closes. No component moved:FactsheetPrintSheetstays infactsheet-detail-page.tsx, sincedesign-system-contract-utils.mjsscopes its raw-colour exemption to the literalfactsheet-print-sheetmarker and fails closed if it moves.Ledger:
#252and#295closed,#013and#255updated,#296captured (below).Verification
npm run verify:pr-localThe single failure is
tests/pr-handoff-stop.test.ts > emits handoff context only when the marker file exists, and it is not caused by this diff. It chmods a fixture git dir to0o555to force a marker write to fail; root ignores permission bits, so the write succeeds and the assertion flips. Reproduced on a clean checkout ofaf85cbcwith every working change stashed —Test Files 1 failed (1) / Tests 1 failed | 10 passed (11)— andid -ureturns0in this container. CI is unaffected because its runner is non-root. Captured as ledger#296with a portable fix rather than papered over here.check:rag:fixtureswas reached separately and passed:Offline RAG fixture and manifest validation passed (36 golden cases, 23 suites).Bundle budget, against a clean
rm -rf .next && npm run build(.next/BUILD_IDmtime07:31:53, build exit 0):Both new fail paths were proven to exit 1 against the real build by temporarily lowering each baseline — a gate that cannot fail is not a gate:
Focused:
npx vitest run tests/bundle-budget.test.ts tests/factsheet-detail-header.dom.test.tsx→Test Files 2 passed (2) / Tests 28 passed (28).npm run verify:ui— not required: no UI, routing, styling, reduced-motion, or forced-colors behaviour changed. The onlysrc/-adjacent change is a test file; no component or stylesheet was touched.npm run verify:release— not run; no release or handoff-confidence claim is being made, and it is provider-backed.npm run eval:retrieval:quality— not applicable: no retrieval, ranking, selection, chunking, or scoring behaviour changed.npm run eval:rag/eval:quality— not applicable: no answer generation, synthesis prompt, or post-processing changed.npm run check:production-readiness— not applicable: no clinical workflow, privacy, environment, Supabase, source governance, or deployment behaviour changed.npm run check:deployment-readiness— not applicable: no deployment startup, hosting, or rollout behaviour changed.No provider-backed gate was run, and none was needed.
Risk and rollout
mockupstolerance is deliberately loose (25%) because a scratch ceiling tight enough to fire on the next mockup would be--updated reflexively, which is worse than no gate. Second-order risk:productionis now enforced against a baseline 9.06% tighter than the number it replaced, so it is a stricter gate than before, not a laxer one. The other two commits are documentation and a test-only assertion.scripts/check-bundle-budget.mjs+bundle-budget.json(restoretotalGzipBytes: 1440201and drop theproduction/mockupskeys to return to the old single-total behaviour).Notes
claude/playwright-browser-drift-fix-1bczxf) and never to push to another without explicit permission. Those two constraints pull opposite ways; splitting into three PRs would require three branches. Resolved by keeping one branch and one PR but three separately revertible commits, each with its own bullet above — which is whatAGENTS.md§ "PR bundling" describes as bundling done correctly, and it also avoids three concurrently open PRs all editingdocs/outstanding-issues.md, a file with no merge driver where manual conflict resolution has previously dropped rows (#152,#168). Happy to split into three PRs if you would rather review them separately — say the word and grant the extra branches.#255is updated, not closed. Local browser gates are restorable, but the container is ephemeral, so the ~5 minutes and ~330 MB are paid per session. The durable fix is still an image shipping Node ≥ 24.15.0, a completenpm ci, and the locked Chromium revision.#013is updated, not closed. Only its mockup half is settled; the route-scoped catalogue-payload work (/specifiers,/formulation) is still measurement-gated on#017. The split also supplies the separate mockup-share reporting that row asked for before any prune — and it removed the false pressure to prune, it did not authorise pruning.AGENTS.mdgains a "Bundle budget" section carrying the two-budget contract and the stale-.nextmeasurement trap:npm run buildreuses a cached.next, and the check then reads stale output and reports byte-identical numbers, which can report a pass that is not real.docs/plans/document-viewer-phase3-handover.mdreferenced the old single 1,440,201-byte total and is updated to the two new baselines.Generated by Claude Code