Make the written record the gate on retiring a mockup, and retire seven - #2543
Conversation
…g a mockup The mockup surface reached ~430 files and ~98,000 lines, and the filenames advertise superseded generations of one screen — answer-chat-perfected, -perfected-v2, answer-chat-redesign, five document-navigation rounds, four document-phone rounds. A survey of all 20 design-scratch families found that reading is wrong in three ways that matter, and that nothing was keeping the one record we have honest. A mockup here is usually a provenance artifact, not a stale draft. In this repository a mockup and the production change it justifies are normally added in the SAME commit: search-heading with the search-band rewrite, universal-search-command with universal-search-command-surface.tsx, sidebar-live with ClinicalSidebar and use-sidebar-pins.ts, all nine document mockups with document-viewer/section-nav.tsx. Retirement is only ever right for a losing generation inside a multi-generation study. Filename supersession is systematically backwards. answer-chat-perfected-v2 imports answer-chat-perfected; so does answer-loading-redesign. Dictionary rounds 2 and 3 both import round 1. Services-filter round 2 imports round 1. The privacy study imports the privacy winner. In at least five families, deleting "the older generation" breaks the newer one. A third of the surface is not design scratch. /mockups/development, /mockups/caring-contacts, /mockups/care-plan and /mockups/ward-flow are live in production behind DeveloperAreaGate and linked from Settings; with their implementation modules that is ~199 files, 82 of which have no "mockup" in their name at all. So this adds a policy and a gate that make the record load-bearing: - docs/mockup-retirement-policy.md — three tiers decided by evidence of a consumer rather than by path, a six-point evidence bar drawn from the 2026-08-27 Favourites retirement (the only one this repo has performed), who decides, and where a retired mockup goes. The negative rule is the point: an unevidenced generation is not retired, it is asked about. - scripts/check-mockup-retirement.mjs + check:mockups, wired into verify:cheap and CI. It enforces index completeness and accuracy, and in --diff mode refuses a deletion that is unrecorded, under a developer-gated prefix, or still referenced by any survivor — including the base-module inversion above. It fails closed and never clears a candidate; reachability is exactly what proved insufficient in PR #2204. - mockups/README.md gains a "Retired mockups" table, seeded with the Favourites six and breadcrumb-header, and is corrected in five places the survey refuted: the answer family (three shipped, cited in four commit bodies and three production comments), document navigation (6230c4d added all five drafts and the implementation together; document-navigation-perfected is the winner), document phone chrome (none shipped), calculators (promoted to production by PR #1227, the stalest line in the file), and Tools (#162 closed 2026-08-15). Two smaller claims corrected: source-overlays does not hand off into the real viewer, and the accessible-table fixture has no automated test. AGENTS.md and CLAUDE.md said mockups were exempt from two gates "and from nothing else". Reading the actual rule and config sources, they are also exempt from no-hardcoded-hex, require-z-index-ladder, require-lucide-icon-aria, check:icon-scale, check:design-system-contract, the required Playwright lane, CodeRabbit, and knip entirely — so the repo's own unused-code detector is blind to this surface. Corrected to match the code. No exemption is widened. docs/answer-page-redesign-handover.md still said "design settled, not built" after shipping across five PRs, and was edited twice on 2026-08-26 without the status changing. That is very likely what caused the answer family to be mis-triaged as parallel drafts. Two findings are filed rather than fixed: the calculators mockup still serves prescribing, ECT and admission directives that PR #2491 removed from production on clinical-safety grounds the day before (those routes 404 in production, so this is not a patient-facing exposure), and source-preview-popover.tsx plus a proxy redirect for a route that no longer exists are dead wiring beside this surface. No mockup is deleted in this commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015UPvNZYvyuDxc1o7xfTGCS
5,298 lines across two families where the winner is identifiable from code, not from filenames. Every retirement here meets the six-point bar in docs/mockup-retirement-policy.md and is recorded in the "Retired mockups" table in mockups/README.md, which npm run check:mockups now requires. Document navigation and document phone chrome. mockups/README.md said both families had "no recorded winner", so all nine were kept rather than guessed at. That was wrong, and one commit disproves it: 6230c4d (#1311) added all nine drafts AND the production implementation together, which is why the dates looked undifferentiated. The winner was never missing. document-navigation-perfected is the shipped design, and the evidence is exact code identity rather than resemblance: production document-viewer/section-nav.tsx carries a weighted segment track (style={{ flexGrow: section.weight }}) and a pending spinner, and grep across all nine drafts returns those in perfected and in no other. Rule 22 of docs/search-chrome-behaviour.md, added by that same commit, names a "weighted position track" — perfected and nothing else. The drafts also carry their own lineage: the final round names its predecessors in a lineage array, and the review round kills the final round's two-column grid ("grid labels truncated three of seven sections") where production is a single-column list. Retired: document-navigation-pane, -final, -final-review, document-phone-title, -title-refined, -fused-directions, -zero-chrome. Kept: document-navigation-perfected as the shipped design, and document-navigation-contract, which is superseded as a build but is the origin of rule 22 and keeps that rule's illustrated rationale — re-labelled active reference rather than retired. Answer. Three of six shipped and stay; two are retired. answer-evidence-popups is the five-tab Evidence sheet #2362 explicitly replaced, naming it as one of "four mental models for one question"; none of its distinctive components survives in production. Its heading-contract test read the page as source text, so it retires with its subject. answer-home-proposal never shipped: its corpus-name subtitle is absent from ui-copy.ts, its governance line appears nowhere, and the badge removal it argued for landed independently in #1512 hours before this mockup's own PR merged. Not retired from that family, deliberately: answer-chat-perfected is a shared base that answer-chat-perfected-v2 and answer-loading-redesign both import, and answer-chat-redesign is superseded as a design but is the three-way comparison the winner was chosen from, cited by the answer handover and by answer-chat-perfected-mockups.tsx. Verification, reported as it actually came back: - check:mockups --diff origin/main passes: 17 deleted mockup files, each recorded in the README table and unreferenced by any survivor. - check:dead-code-candidate --diff origin/main REFUSES, 64 of 201 candidates. Every refusal is a bare-name collision on a file-local or framework-convention identifier — sections, activeIndex, documentTitle, Canvas, and metadata, which exists in roughly 150 route files. Run per exported symbol instead, all 17 real component exports are CLEAR; the only export-level refusals are the seven metadata declarations. No threshold and no refusal-list entry was changed to reach that. - Full unit suite: 947 files, 12085 passed, 4 skipped. - docs/site-map.md and data/repo-awareness-snapshot.json regenerated; docs:check-links passes at 4741 resolved path references. Two gate fixes fell out of using the new check on its own first record. It read every code span in the Retired table, so the "Superseded by" column made the live winner look retired; it now reads only the Route column. And its fixtures named real repository files as examples, which its own path check correctly flagged; they are synthetic now. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015UPvNZYvyuDxc1o7xfTGCS
… and remove four dead route branches
Findings from an adversarial review of the previous two commits. Two of them are claims
this change itself introduced, which is the worst kind, so they lead.
The CodeRabbit exemption does not exist. The previous commit asserted in AGENTS.md,
CLAUDE.md and the policy that mockups are excluded from CodeRabbit review. They are not.
.coderabbit.yaml's "!mockups/**" is root-anchored and excludes only the repo-root mockups/
notes directory — one README, already covered by "!**/*.md". It does not match
src/app/mockups/** or *-mockups.tsx, verified with picomatch against all three shapes;
the file's own use of "**/*.md" elsewhere shows the matcher is anchored deliberately.
CodeRabbit reviews mockup source like production code. With the capped review allowance in
#CCZ4HB, believing the wrong version could have cost real coverage.
The knip claim was overstated in the same three places. "Exempt from knip entirely, so the
repo's own unused-code detector cannot see this surface" is wrong: knip.json's
"**/*mockup*" is a BASENAME filter, so *-mockups.tsx and mockups-layout-client.tsx are
ignored while the 70 src/app/mockups/<slug>/page.tsx routes and the */mockups/** subtrees
are not — knip --include files reports inside them today. What actually suppresses those
findings is check:knip running only --include dependencies,unlisted,unresolved,duplicates,
which omits unused-file and unused-export analysis repo-wide. That is not a mockup
carve-out, and the policy now says so rather than leaning on knip.
The gate advertised enforcement it was not performing. check:mockups ran --self-test plus
index mode only, and nothing in the repository ever passed --diff — so Tier B refusal,
import-graph safety and evidence-of-record, the three things the policy exists for, were
manual-only in both verify:cheap and CI. check:mockups now also runs --diff auto, which
resolves its base from MOCKUP_RETIREMENT_BASE or the merge base with origin/main and
refuses rather than skipping when neither resolves.
The gate missed most import forms, and the sweep in the previous commit walked straight
into it. moduleSpecifiersFor emitted only the @/ alias, and matching required a quote
immediately before the specifier, so a relative prefix hid the reference. Relative imports
(59 files in this surface use them), dynamic import(), CSS composes, and
/mockups/<slug> route literals were all invisible. Matching now anchors on quote-or-slash
and the route URL is a specifier. Proven against all five forms, and against an unrelated
module to show it does not over-match.
Which is how four dead branches survived: mockups-layout-client.tsx still tested
pathname === "/mockups/document-navigation-pane", startsWith("/mockups/document-phone-"),
startsWith("/mockups/document-navigation-final") and "/mockups/answer-home-proposal", all
of them now unreachable, and the hardened gate passed that tree clean. Removed, along with
their nine usages; the two live clauses beside them are kept.
Two more fail-open holes closed. listRouteSlugs returned [] when the route root was
missing, so auditIndex passed with "0 mockup route(s) indexed" — indistinguishable from a
healthy repository, and exactly the soft-skip this gate must not do; it now throws. And
retiredSlugs read the Retired table's Route column positionally, so reordering the columns
would silently change what the record means; the header is now verified.
mockups/README.md said 70 routes in the gate output and "79 folders" in prose two files
away. Corrected.
Tests: 34 to 47, covering every import form above, the route-URL specifier, the
reordered-header refusal, the missing-root refusal, and diff-base resolution including its
failure path.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015UPvNZYvyuDxc1o7xfTGCS
An adversarial review was asked to argue every candidate was still alive. It found two, and both come back. Nine retirements become seven. document-navigation-final is partly adopted, not superseded. Production src/components/document-viewer/section-nav.tsx:143 renders its section-index heading as `<div className="flex items-baseline justify-between px-0.5 pb-2">`. That exact string exists in two files in the whole repository: production, and this draft at line 278. document-navigation-perfected does NOT have it — it splits the heading into an outer px-0.5 pb-2 wrapper, an inner flex row and a caption paragraph, and production has neither the split nor the caption. Verified at the origin commit 6230c4d as well as at HEAD, so it is not later drift. Production is a hybrid: perfected's weighted track and pending spinner, this draft's heading shape. By the sweep's own standard — exact code identity — retiring it was wrong. document-phone-zero-chrome comes back UNCERTAIN, which is the condition it was admitted under. Its "zero new chrome, sheet not pane" contract is live, but the KEPT document-navigation-contract carries the same thesis verbatim ("A bottom sheet is the one overlay that adds no chrome, no reserve, and no scroll owner"), and both landed in the one squash 6230c4d so git cannot say which authored it. It also uniquely holds the numeric chrome-budget table that is the quantified case against the rejected fused row. The policy's negative rule decides it: an unevidenced generation is asked about, not retired. Restoring the routes without their chrome suppression would have shipped a real regression — both studies draw their own top bar and composer inside every frame, so the shared composer reads as a second real search bar over the study. mockups-layout-client.tsx gets the two branches back, narrowed to exactly the routes that survive rather than the startsWith prefixes that used to cover their retired siblings. Two claims in the previous commit messages were wrong and are corrected here rather than left standing: - "17 deleted mockup files, each recorded and unreferenced by any survivor" was not true as written. Four dead pathname branches in mockups-layout-client.tsx referenced deleted routes, and the gate passed because it did not yet generate /mockups/<slug> as a specifier. Both fixed in the preceding commit; the receipt is restated accurately here. - "its governance line appears nowhere" (of answer-home-proposal) is false. "Verify answers against the cited source." is at warning-line-mockups.tsx:143, a survivor added by the same PR d766d53. The conclusion is unchanged — the line never reached production, and answer-home-proposal stays retired — but the evidence sentence was overstated. - The claim that test fixtures "are synthetic now" covered the path fixtures only; the retired-table fixtures still used real slugs. They are synthetic now in fact. Also filed: retiring answer-evidence-popups took with it the repository's only source-text heading-hierarchy contract, added by #1294 for a regression that had already recurred once. The ~375 DOM-level heading assertions elsewhere never render a mockup gallery page, so that regression class is now unguarded. Deleting the test was correct — it readFileSync'd its own subject — but the gap is real and is queued rather than lost. Site map and repo-awareness snapshot regenerated. Full suite: 947 files, 12098 passed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015UPvNZYvyuDxc1o7xfTGCS
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Team Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
Two fresh agents reviewed this branch before push, and both found real defects. The lookup reported NOT REVIEWED at this head and scope, so the record was still owed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015UPvNZYvyuDxc1o7xfTGCS
The record appended in the previous commit failed check:branch-review-ledger with "filename must be the SHA-256 content address for its record row". The cause is an interaction between two repository rules rather than a mistake in either: docs/branch-review-records/*.record.md are content-addressed — the filename IS the hash of the row — and AGENTS.md requires `npm run format` before every ordinary push. Prettier rewrote the freshly appended row, so the file no longer hashed to its own name. Every existing record happens to be Prettier-clean, which is why this has not fired before; the trap only springs on a row whose prose Prettier wants to touch, and then it corrupts a record that is supposed to be immutable. Renames the record to the correct address for its current bytes, and adds docs/branch-review-records/ to .prettierignore so the next append cannot hit the same wall. Nothing is lost by ignoring them: check:branch-review-ledger verifies the content address, cell count, dates, conflict markers, mojibake and duplicates far more strictly than Prettier's formatting. Verified by re-running `npm run format` and confirming the record still hashes to its filename. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015UPvNZYvyuDxc1o7xfTGCS
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_917e3d96-8940-4431-bd20-d8c92f082116) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8a184b5aec
ℹ️ 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".
… new home `PR mergeability` went red on 8a184b5: main advanced by two commits and #2523 ("tier the always-loaded instruction surface into a core plus reference files") restructured AGENTS.md and CLAUDE.md, which this branch also edits. `git merge-tree` confirmed a real content conflict rather than mere staleness, so this is a merge, not a sync. Resolution takes main's structure everywhere and re-applies this branch's content on top: - AGENTS.md and CLAUDE.md resolve to main's tiered versions. The mockup gate-exemption block moved out of AGENTS.md into docs/agents/wiring-and-bundle-budget.md, so the correction this branch makes to it — the false CodeRabbit and knip exemptions, the rules mockups genuinely are exempt from, and the pointer to docs/mockup-retirement-policy.md — is ported there instead of being re-inserted into a file that no longer holds it. CLAUDE.md's own pointer already resolves to that file, so discoverability is unchanged. - CLAUDE.md's verify:cheap gate count returns to 35. Taking main's copy reverted it to 34, and check:gate-manifest caught that immediately, since this branch adds check:mockups to the chain. - data/repo-awareness-snapshot.json is generated, so it was regenerated rather than hand-merged. Verified on the merged tree: gate-manifest (38 gates, 35 static, consistent), check:mockups (all three modes), branch-review-ledger, docs:check-links, docs:check-index, sitemap:check, check:repo-awareness-snapshot, check:outstanding-issues, lint, typecheck, and the full unit suite at 947 files / 12100 passed / 4 skipped. The previous commit's .prettierignore fix earned itself here: `npm run format` ran twice over this merge and the content-addressed review record still hashes to its filename. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015UPvNZYvyuDxc1o7xfTGCS
CI triageCI failed on this PR. Automated classification of the 2 failed job(s):
Compared with main CI run #15071 (failure). Classification is evidence routing, not permission to ignore a failure. Exact quarantined Playwright identities remain governed by the flake ledger. |
…s, unnamed support files All three are real, and each was reproduced before it was fixed. They matter because the gate is this PR's actual deliverable — a policy whose enforcement can be walked around is worse than no policy, since it reads as protection. 1. A retirement row with a blank `Superseded by` or `Evidence` cell was accepted as a record. `retiredSlugs` read only the Route cell, so a row naming a route and nothing else licensed a deletion while saying nothing — bypassing the written-successor decision that is evidence-bar item 1. Both cells must now be populated, and a separator row cannot masquerade as one. 2. A route counted as live while its directory survived, even with no `page.tsx` left in it. A leftover stylesheet or asset therefore kept a slug "live" and silently suppressed the retirement-record check for a route nobody could load. Liveness is now a surviving `page.tsx`, found recursively. 3. Deletions were scoped by filename, so a support file with no "mockup" in its path was dropped before the survivor reference scan. That is precisely the wrong filter here: the survey behind this policy found 82 modules reachable only from mockup routes with no "mockup" anywhere in their path, `src/components/ward-management/**` being 58 of them. When a diff retires anything from the mockup surface, every deletion under `src`, `tests`, `scripts` and `worker` is now scanned; when it retires nothing, the scan is empty and the gate stays out of unrelated changes' way. Fix 3 immediately earned itself: the scan now covers 14 deleted files rather than 13, picking up `tests/answer-evidence-popups-heading-contract.test.ts`, which the old filter dropped because its name carries no "mockup". Each fix ships with regression tests, including the exact blank-cell row Codex named. The test helper needed updating too: its fixtures predated the page.tsx liveness rule, and two existing cases went green for the wrong reason until it was fixed — so the helper now supplies a page.tsx by default rather than the check being loosened. Verified: self-test, 53 tests in tests/mockup-retirement.test.ts, all three check:mockups modes on the real repo, lint, typecheck, and the full suite at 947 files / 12106 passed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015UPvNZYvyuDxc1o7xfTGCS
…ripts inventory `PR mergeability` went red twice on fa70134 as main advanced past it, and `Static PR checks` went red on a real failure of this branch's own making. Static PR checks failed on `docs:check-inventory`: "docs/scripts-index.md inventory is stale ... expected 284 script files and 285 npm scripts". This branch adds scripts/check-mockup-retirement.mjs and refreshed the index at 283; main then added another script, so the committed count was one behind. Regenerated with the repo's own tooling. Both merge conflicts were in generated files — data/outstanding-issues-snapshot.json and data/repo-awareness-snapshot.json — so both were regenerated with `npm run snapshot:issues` and `npm run snapshot:repo-awareness` rather than hand-merged. Main's #2542 reconciled nine queued ledger requests; this branch's two inbox requests were not among them and remain pending, as intended. The remaining red check is `Advisory UI`, which is not aggregated by `PR required` and is not this PR's: its single failure is tests/ui-tools-search-mode-mockup.spec.ts "matches the exact displayed tool title after normalising punctuation", and this diff touches no Tools file at all. Characterised in a follow-up rather than fixed by widening this PR. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015UPvNZYvyuDxc1o7xfTGCS
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_af3515d9-ddca-48f5-82cb-8bf810a530e7) |
Conflicts were confined to generated files — both snapshots and the docs scripts index. Resolved by taking main and re-running `npm run docs:update`, which also adopts c0ee4dd's fix for the recurring snapshot collision. Verified after the merge: check:mockups (all three modes), docs:check-inventory (285 script files, 288 npm scripts), and both snapshot gates. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015UPvNZYvyuDxc1o7xfTGCS
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_f3d5a43b-182d-49c6-bcbb-6b692d43ab5b) |
Re-verified at
|
|
The only conflict was docs/scripts-index.md, a generated file. Resolved by taking main and re-running its generator: 285 script files, 289 npm scripts. Re-verified after the merge: check:mockups (all three modes), both snapshot gates, sitemap:check, and prettier on every changed file. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015UPvNZYvyuDxc1o7xfTGCS
package.json was a real conflict, not a generated one: main added
check:diff-integrity to verify:cheap:internal while this branch added
check:mockups. Resolved by keeping both, and bumped the pinned counts the
gate manifest checks (CLAUDE.md 35 -> 36 static, gates SKILL.md 38 -> 39
total) so the documents match the chain.
data/repo-awareness-snapshot.json was generated: took main and re-ran its
generator, along with the issues snapshot and the docs inventory.
Verified: check:gate-manifest ("all 39 verify:cheap gates are enforced in CI
... and the 36 static gates are documented consistently"), check:mockups in
all three modes, main's own check:diff-integrity, both snapshot gates,
sitemap:check and docs:check-inventory.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015UPvNZYvyuDxc1o7xfTGCS
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_a7964f18-b9f8-4e5e-9013-6e20915f9072) |
.prettierignore was a real conflict, not a generated one: main's #2524 added docs/branch-review-index.md at the same place this branch added docs/branch-review-records/. Both entries are wanted — one protects a generated index from table repadding, the other protects content-addressed records whose filenames are SHA-256 hashes of their own content — so both were kept. check:branch-review-ledger passes on the result. The two snapshots and docs/scripts-index.md were generated: took main and re-ran their generators. Verified: check:mockups (all three modes), check:gate-manifest, the ledger guard, check:diff-integrity, both snapshot gates, sitemap:check, docs:check-inventory, and prettier on every changed file. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015UPvNZYvyuDxc1o7xfTGCS
Third conflict of the same shape: main added check:forms-pdf-manifest to
verify:cheap:internal while this branch has check:mockups. Kept both, and
bumped the pinned counts check:gate-manifest reads (CLAUDE.md 36 -> 37
static, gates SKILL.md 39 -> 40 total).
Verified: check:gate-manifest ("all 40 verify:cheap gates are enforced in CI
... and the 37 static gates are documented consistently"), check:mockups in
all three modes, main's own check:forms-pdf-manifest, docs:check-inventory,
both snapshot gates, sitemap:check, and prettier on every changed file.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015UPvNZYvyuDxc1o7xfTGCS
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_4bff095f-a570-4871-93b6-3cf53ca84ac8) |
Clean merge — three ledger commits, no conflicts. Regenerated the awareness snapshot, which the merge moved (2666 -> 2668 reviews). Verified: check:mockups, check:gate-manifest, both snapshot gates, docs:check-inventory, and prettier on the changed file. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015UPvNZYvyuDxc1o7xfTGCS
Summary
The mockup surface reached ~430 files and ~98,000 lines, and the filenames advertise repeated superseded generations of one screen. This adds a policy and a gate that make the written record load-bearing, then retires only what that policy clears on evidence — seven mockups, 4,235 lines across 14 deleted files, not the far larger number the filenames imply.
docs/mockup-retirement-policy.md— three tiers decided by evidence of a consumer rather than by path, a six-point evidence bar drawn from the 2026-08-27 Favourites retirement (the only one this repo has performed), who decides, and where a retired mockup goes.scripts/check-mockup-retirement.mjs/npm run check:mockups— wired intoverify:cheapand CI. Enforces index completeness and accuracy, and in--diff automode refuses a deletion that is unrecorded, under a developer-gated prefix, or still referenced by any survivor. Fails closed, and never clears a candidate.mockups/README.md— gains a## Retired mockupstable, and is corrected in five places the survey refuted.AGENTS.md/CLAUDE.md— the mockup gate-exemption list corrected to match the code, in both directions./issuesinbox requests for findings deliberately not fixed here.Why this is much smaller than it looks
A prior pass (PR #2423) already triaged this surface and declined most of it in writing. Six agents re-examined all 20 design-scratch families against production code and git history, and found the reason:
In this repo a mockup and the production change it justifies are usually added in the same commit.
search-headingarrived with the search-band rewrite,universal-search-commandwithuniversal-search-command-surface.tsx,sidebar-livewithClinicalSidebaranduse-sidebar-pins.ts, all nine document mockups withdocument-viewer/section-nav.tsx. A mockup here is normally a provenance artifact attached to a shipped commit, not a stale draft.Two further traps the policy encodes:
answer-chat-perfected-v2importsanswer-chat-perfected; so doesanswer-loading-redesign. Dictionary rounds 2 and 3 both import round 1. The privacy study imports the privacy winner. In at least five families, deleting "the older generation" breaks the newer one./mockups/development,/mockups/caring-contacts,/mockups/care-planand/mockups/ward-floware live in production behindDeveloperAreaGateand linked from Settings. 82 of their modules have no "mockup" in the name, so every glob in the repo misses them.What was retired, and against which winner
Document navigation and document phone chrome. The README said "no recorded winner", so all nine were kept. One commit disproves it:
6230c4db(#1311) added every draft and the production implementation together, which is why the dates looked undifferentiated.document-navigation-perfectedis the shipped design on exact code identity — productionsection-nav.tsxcarries a weighted segment track (style={{ flexGrow: section.weight }}) and apendingspinner, and a grep across all drafts returns those inperfectedand in no other. Rule 22 ofdocs/search-chrome-behaviour.md, added by that same commit, names a "weighted position track".Retired:
document-navigation-pane,document-navigation-final-review,document-phone-title,document-phone-title-refined,document-phone-fused-directions.Answer. Three of six shipped and stay.
answer-evidence-popupsis the five-tab Evidence sheet #2362 explicitly replaced, naming it as one of "four mental models for one question"; none of its distinctive components survives in production.answer-home-proposalnever shipped — its subtitle copy is absent fromui-copy.ts, and the badge removal it argued for landed independently in #1512 hours before its own PR merged.Kept deliberately:
answer-chat-perfectedis a shared base two newer files import;answer-chat-redesignis the three-way comparison the winner was chosen from, cited by the answer handover and byanswer-chat-perfected-mockups.tsx;document-navigation-contractis superseded as a build but is the origin of rule 22.Adversarial review — two candidates withdrawn
Two fresh agents reviewed before push: one asked to argue every candidate was still alive, one
frontend-ui-revieweron the full diff. Both found real defects.The "still alive" reviewer found two survivors, and both were restored:
document-navigation-final— productionsection-nav.tsx:143renders its heading as<div className="flex items-baseline justify-between px-0.5 pb-2">. That exact string exists in two files repo-wide: production, and this draft at line 278.perfecteddoes not have it. Production is a hybrid, so by the sweep's own standard of exact code identity this was partly adopted, not superseded.document-phone-zero-chrome— returned UNCERTAIN, the condition it was admitted under. Its "zero new chrome" contract is live, but the keptdocument-navigation-contractcarries the same thesis verbatim and both landed in one squash, so authorship cannot be established.The UI reviewer found two of my own doc corrections were wrong, both verified with picomatch before changing anything:
.coderabbit.yaml's!mockups/**is root-anchored and excludes only the repo-root notes directory — notsrc/app/mockups/**or*-mockups.tsx. With the capped allowance in#CCZ4HB, believing otherwise could have cost real review coverage.knipis not blind to this surface. Its ignore is a basename filter, so*-mockups.tsxis exempt but the routepage.tsxfiles and*/mockups/**subtrees are not. What suppresses findings ischeck:knipomitting unused-file analysis repo-wide — not a mockup carve-out.It also found the gate advertised enforcement it was not performing (nothing ever passed
--diff), that the gate missed relative imports, dynamic imports, CSScomposesand route-path literals, and that the sweep had consequently left four deadpathname === "/mockups/<slug>"branches inmockups-layout-client.tsxthat the gate passed clean. All fixed, with tests.Verification
npm run check:mockups— self-test passed;72 mockup route(s) indexed, 14 recorded as retired;13 deleted mockup file(s), each recorded and unreferencednpm run test—Test Files 947 passed (947),Tests 12098 passed | 4 skipped (12102)npm run lintandnpm run typecheck— both clean; the gate arbiter returnedRUNfor each (container scope never defers), so neither was deferrednpm run check:gate-manifest—all 38 verify:cheap gates are enforced in CI ... and the 35 static gates are documented consistentlynpm run sitemap:check,docs:check-links(4742 repo path references resolve),check:outstanding-issues,check:ledger-write-discipline— all passnpx prettier --checkon every changed file —All matched files use Prettier code style!rm -rf .nextfirst,BUILD_IDmtime checked against the clock each time): mockups 611.6 KiB gzip, 160 chunks, 133 routes, −0.3% against the committed baseline. Against the last recorded clean measurement (2026-09-01,/issues#QSHHGK: 645.1 KiB / 165 chunks / 138 routes) that is −33.5 KiB and −5 exclusive chunks. Production is 1738.3 KiB / +5.0%, unchanged by this work and already tracked as#QSHHGK.npm run check:dead-code-candidate -- --diff origin/mainREFUSES this diff, and I am not reporting it as green. 64 of 201 candidates refuse. Every refusal is a bare-symbol-name collision on a file-local or framework-convention identifier —sections,activeIndex,documentTitle,Canvas, andmetadata, which exists in roughly 150 route files — because that gate matches names repo-wide and whole-file deletion surfaces every module-local const. Run per exported symbol instead, every named component export comes back CLEAR and the only export-level refusals are the sevenmetadatadeclarations. The adversarial reviewer independently re-ran it, read every distinct refusal reason, and pulled the pinning file for the two that could plausibly have been real (SourcePreviewPopover, pinned by a test importing the production module;Composer, pinned by a synthetic fixture string) — both collisions. No threshold and no refusal-list entry was changed. One correction to an earlier commit message: that per-symbol run covered 24 declarations I extracted myself, not 17 candidates the gate emitted; the gate emits none fordefaultexports.mockups-layout-client.tsx, which is the/mockupsroute-group shell and 404s in production. Chromium mockup journeys run in the advisorychromium-mockupslane, not the required one.eval:*,verify:release,check:supabase-project,test:live). None applies.Risk and rollout
/mockupsshell. The residual risk is a wrong supersession call — mitigated by requiring exact code identity, by the adversarial review that withdrew two candidates, and by git history remaining the archive.git reverteither commit independently. The policy commit contains no deletions, so it can stand alone if the sweep is reverted.src/lib/rag/rag-eval-cases.tsandsrc/lib/eval-document-matching.tswere identified during the survey as mockup-exclusive by route reachability while being imported by nine scripts and seven tests; the policy classifies them Tier C explicitly so they can never become cleanup candidates.Clinical Governance Preflight
Clinical KB Database(sjrfecxgysukkwxsowpy)No clinical behaviour changes in this PR. The preflight is completed because the diff touches paths the policy classifies as clinical-risk, not because clinical output moved.
Notes
Three findings are filed as
/issuesinbox requests rather than fixed here, because each belongs to someone else or to another change:src/components/calculators/calculator-pathways.tsfrom 296 lines to 65 on clinical-safety grounds; the mockup copy still carries all of it — 15 matches forECT|admission|discharge|refer|prescrib|commence|titratagainst 0 in production, including "Initiate pharmacotherapy; consider psychiatry referral" and "Assess psychotic features and ECT indications". This is not a patient-facing exposure:/mockups/calculators-*is not inDEVELOPER_GATED_PATH_PREFIXES, so it 404s in production. It is visible in dev and preview. The calculators tree is interconnected and can only come out as one unit, and the fix is a call for whoever owns fix(calculators): enforce clinical safety and evidence governance #2491.answer-evidence-popupsremoved the repo's only source-text heading-hierarchy contract, added by Fix typography audit issues #1294 for a regression that had already recurred once. The ~375 DOM-level heading assertions elsewhere never render a mockup gallery page. Deleting the test was correct — itreadFileSync'd its own subject — but the gap is real.src/components/clinical-dashboard/source-preview-popover.tsxhas zero importers anywhere insrc/, andsrc/proxy.ts:49redirects/mockups/document-search-command, a route that no longer exists.Separately fixed here because it caused the original mis-triage:
docs/answer-page-redesign-handover.mdstill said "design settled, not built" after the design shipped across five PRs, and had been edited twice after the build began without the status changing.About 15 files (~10,000 lines) across therapy-navigation, universal-search, settings-search, the nine Tools layouts and the warning studies came back genuinely undecided. Per the policy, "parallel draft, no recorded winner" is a legitimate stable resting state rather than a backlog item. They stay, correctly labelled, and the new check stops the index drifting away from the tree again.
🤖 Generated with Claude Code
https://claude.ai/code/session_015UPvNZYvyuDxc1o7xfTGCS
Generated by Claude Code
Note
Low Risk
Changes are confined to dev-gated mockup routes and repo gates/docs; production patient flows are untouched, with residual risk limited to an incorrect retirement if the evidence bar were wrong (mitigated by the new check and recorded successors).
Overview
Introduces a written-record gate for mockup cleanup and uses it to retire seven design-scratch routes (~4k+ lines), not a broad filename-based sweep.
Policy and enforcement:
docs/mockup-retirement-policy.mddefines tiers (design scratch vs developer-gated live prototypes vs fixtures), a six-point evidence bar, andnpm run check:mockups(scripts/check-mockup-retirement.mjs). The check enforcesmockups/README.mdcompleteness, retired-table accuracy, and--diff autorules (no unrecorded deletes, no Tier B removals, import/CSS/route-literal references). It is added toverify:cheapand CI when docs/static-heavy paths change; gate docs now count 37 static checks inverify:cheap.Sweep: Removes seven mockups (
answer-evidence-popups,answer-home-proposal, document-navigation pane/final-review, three document-phone variants) plus their component modules; updatesmockups/README.mdwith a## Retired mockupstable and corrects several families (answer, document nav, calculators, tools).mockups-layout-client.tsxdrops chrome-suppression branches for retired paths.Docs/hygiene: Corrects mockup exemption claims (CodeRabbit, knip, ESLint carve-outs); links the policy from
docs/README.mdand wiring docs; marks answer redesign handover as shipped; ignoresdocs/branch-review-records/in Prettier so content-addressed ledger rows are not reformatted away; refreshes sitemap and repo-awareness snapshot counts (197 pages, 130 mockup routes).Reviewed by Cursor Bugbot for commit fce054c. Configure here.