Skip to content

Split the bundle budget from mockup scratch, restore local browser gates, and pin the factsheet heading census - #1779

Merged
BigSimmo merged 9 commits into
mainfrom
claude/playwright-browser-drift-fix-1bczxf
Aug 9, 2026
Merged

BigSimmo merged 9 commits into
mainfrom
claude/playwright-browser-drift-fix-1bczxf

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Summary

Three unrelated follow-ups from PR #1769, kept as three separately revertible commits rather than one blended diff. The repo's own classifyPullRequestFiles returns clinicalRisk: false, operationalRisk: false, ragRanking: false, ui: false for 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 in AGENTS.md.

  • fix(bundle-budget): split production weight from mockup scratch (#252, half of #013). 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. #013 held 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 of main at af85cbc, the blur had become the entire 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, 17.29% of the total); production had actually shrunk. docs/audit/latency-audit-2026-07-28.md 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. 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.js files under .next/server/app (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.
  • 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 baked node_modules is stale or incomplete (containers have shipped none at all; earlier ones reported playwright 1.62.0 against a locked 1.62.1 with tailwind-merge absent entirely — an incomplete install, not a version skew, so the lockfile pin was never wrong), and npm ci cannot repair it because jsdom@30.0.1 requires ^22.22.2 || ^24.15.0 || >=26.0.0 while images have shipped v24.13.0, so it 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; npx playwright install supplies 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 every h1 in 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 print html.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: FactsheetPrintSheet stays in factsheet-detail-page.tsx, since design-system-contract-utils.mjs scopes its raw-colour exemption to the literal factsheet-print-sheet marker and fails closed if it moves.

Ledger: #252 and #295 closed, #013 and #255 updated, #296 captured (below).

Verification

  • npm run verify:pr-local
- completed: check:runtime, check:installed-lock-parity, format:changed, sitemap:check, docs:check-index,
  docs:check-inventory, docs:check-scripts, docs:check-links, check:branch-review-ledger,
  check:outstanding-issues, check:github-actions, check:ci-scope, check:gitleaks-pinned, check:ci-triage,
  check:pr-policy, check:gate-manifest, check:pr-mergeability, check:verification-plan, test:ci-workflows,
  check:codex-autofix-workflow, lint, typecheck
- failed: test (exit 1)   <- one pre-existing, environment-only failure, see below
Test Files  1 failed | 546 passed (547)
     Tests  1 failed | 5893 passed | 4 skipped (5898)

The 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 to 0o555 to force a marker write to fail; root ignores permission bits, so the write succeeds and the assertion flips. Reproduced on a clean checkout of af85cbc with every working change stashed — Test Files 1 failed (1) / Tests 1 failed | 10 passed (11) — and id -u returns 0 in this container. CI is unaffected because its runner is non-root. Captured as ledger #296 with a portable fix rather than papered over here.

check:rag:fixtures was 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_ID mtime 07:31:53, build exit 0):

[bundle-budget] client chunks: 309 files, 1546.5 KiB gzip (5528.3 KiB raw) across 167 routes.
[bundle-budget] production (what users download, 101 routes): 1279.1 KiB gzip — baseline 1279.1 KiB, within tolerance.
[bundle-budget] mockups (design scratch, 404s in production, 66 routes, 76 exclusive chunks): 267.5 KiB gzip — baseline 267.5 KiB, within tolerance.

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:

[bundle-budget] FAIL — production bundle +25.0% vs baseline (tolerance 10%). This is user-facing weight; find the regression before refreshing the baseline.
[bundle-budget] FAIL — mockups scratch +100.0% vs baseline (tolerance 25%). No user loads these chunks, so this is a hygiene ceiling: ...

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 only src/-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

  • Risk: Concentrated in the bundle-budget commit, and it is a verification-gate change, not runtime behaviour — no shipped code path is affected. The real risk is mis-attribution: if a production chunk were wrongly classed as mockup-exclusive, the production number would understate real weight. Three things bound that. Attribution is per-build from the route manifests rather than from path heuristics; a chunk shared with any non-mockup route is production, as are chunks no manifest claims at all, so both ambiguous cases resolve toward production; and a missing or empty manifest tree fails the check instead of collapsing the buckets. The mockups tolerance 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: production is 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.
  • Rollback: Each commit reverts independently while this PR is open. Post-merge the squash folds them, so a single-item revert means reverting that commit's hunks by hand; the bundle-budget item is self-contained to scripts/check-bundle-budget.mjs + bundle-budget.json (restore totalGzipBytes: 1440201 and drop the production/mockups keys to return to the old single-total behaviour).
  • Provider or production effects: None. No dependency, lockfile, migration, environment, deployment, or provider-facing change; no OpenAI/Supabase/live-eval call was made.

Notes

  • Bundling rationale, since these are three unrelated items. The task brief asked that they not be forced into one PR, and the session is also instructed to develop and push on exactly one designated branch (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 what AGENTS.md § "PR bundling" describes as bundling done correctly, and it also avoids three concurrently open PRs all editing docs/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.
  • #255 is 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 complete npm ci, and the locked Chromium revision.
  • #013 is 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.md gains a "Bundle budget" section carrying the two-budget contract and the stale-.next measurement trap: npm run build reuses 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.md referenced the old single 1,440,201-byte total and is updated to the two new baselines.

Generated by Claude Code

claude added 3 commits August 9, 2026 07:51
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
@coderabbitai

coderabbitai Bot commented Aug 9, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: cbee1be9-f159-4cbd-8dcd-f40e963ccf5e

📥 Commits

Reviewing files that changed from the base of the PR and between 450690f and b723d05.

📒 Files selected for processing (9)
  • AGENTS.md
  • CLAUDE.md
  • bundle-budget.json
  • docs/outstanding-issues.md
  • docs/plans/document-viewer-phase3-handover.md
  • docs/testing.md
  • scripts/check-bundle-budget.mjs
  • tests/bundle-budget.test.ts
  • tests/factsheet-detail-header.dom.test.tsx

Comment @coderabbitai help to get the list of available commands.

@supabase

supabase Bot commented Aug 9, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project sjrfecxgysukkwxsowpy because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread scripts/check-bundle-budget.mjs Resolved

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread scripts/check-bundle-budget.mjs Resolved

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread scripts/check-bundle-budget.mjs Resolved
@BigSimmo

BigSimmo commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

@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.

@cursor

cursor Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

You need to increase your spend limit or enable usage-based billing to run background agents. Go to Cursor

BigSimmo and others added 4 commits August 9, 2026 16:24
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>
@BigSimmo
BigSimmo enabled auto-merge (squash) August 9, 2026 08:56
@BigSimmo
BigSimmo disabled auto-merge August 9, 2026 08:57
@BigSimmo
BigSimmo enabled auto-merge (squash) August 9, 2026 08:58
@BigSimmo
BigSimmo disabled auto-merge August 9, 2026 08:58
@BigSimmo
BigSimmo enabled auto-merge (squash) August 9, 2026 09:07
@BigSimmo
BigSimmo merged commit 89245e7 into main Aug 9, 2026
24 checks passed
@BigSimmo
BigSimmo deleted the claude/playwright-browser-drift-fix-1bczxf branch August 9, 2026 09:11
BigSimmo added a commit that referenced this pull request Sep 5, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants