Build the hazard register, the hub's last unbuilt clinical card - #2745
Conversation
`hazard-register` had sat at phase 4 since the 2026-08-25 placeholder sweep, kept deliberately when five other placeholders were removed. It is now built. Two decisions shape the whole page, both taken from the documents it renders rather than invented here. **It leads with what is not controlled.** The registers hold 44 rows, and a page opening with "44 hazards" reads as thoroughness -- the opposite of what it is reporting. The four rows with no control at all come first, before any total: no named clinical safety officer, no lived-experience review of the patient-visible messages, no Aboriginal cultural safety review, and an unconfirmed hospital referral feed. Each blocks a real-patient pilot and none is closable by code. **It never merges the three registers.** They have three different authorities. `docs/clinical-hazard-controls.json` is machine-checked by `check:clinical-hazard-controls` and says in its own words that it establishes static evidence only. `docs/caring-contacts/hazard-log.md` is a draft no clinician has signed. Ward Flow has no register at all. Rendering them as one list would manufacture a uniform coverage claim no document here makes, so each section carries its own authority sentence, quoted rather than summarised. Ward Flow's absence renders as a finding, not an empty section -- a heading with nothing under it reads as "no hazards". The four blocking ledger rows naming Ward Flow sit beside it, typed `ledgerMentions` and never `hazards`, labelled as neither a register nor a complete list. That list is matched on P1 summaries only: searching detail text at every priority returned a flaky demo-clock test and a missing print stylesheet, and listing those under a hazard heading trains the reader to skim the one section that must never be skimmed. Everything fails loud rather than partial. `loadHazardSnapshot` throws on an unrecognised version, matching `loadLedgerSnapshot`: a hazard page rendering a short list because the snapshot shape moved would under-report clinical risk, which is the failure it exists to prevent. An unrecognised status is passed through and rendered as its own text rather than rounded to "controlled". Verified: verify:cheap; lint and typecheck pass (7470 input files each); prettier --check . clean whole-tree; 43 tests across the three affected suites, 24 of them new. The generated snapshot is proved in step with the source documents by rebuilding it inside the test rather than trusting the commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K8kRjars99uHE46MRH28rg
The cherry-pick auto-merged this generated file across a main that had moved (9f5b07d..35f600c). Regenerated with `npm run snapshot:repo-awareness` so the committed snapshot is the tool's own output for this base rather than a merge of two generations; `check:repo-awareness-snapshot` now reports in step. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K8kRjars99uHE46MRH28rg
|
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: Advanced 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. |
# Conflicts: # docs/scripts-index.md
CI triageCI failed on this PR. Automated classification of the 2 failed job(s):
Compared with main CI run #16407 (failure). Classification is evidence routing, not permission to ignore a failure. Exact quarantined Playwright identities remain governed by the flake ledger. |
`Static PR checks` failed on this branch with `check:repo-awareness-snapshot`: "The committed snapshot is behind the repository: documentation differs from the repository" (run 34242930639). `PR required` then went red as the roll-up of that one job, not as a second failure. The cause is the merge of origin/main into this branch, which brought documents the committed snapshot predates. Regenerated with the prescribed command, `npm run snapshot:repo-awareness`; the check now reports "in step with data/repo-awareness-snapshot.json (225 pages, 906 documents, 2718 reviews)". The hazard snapshot regenerated to an identical body with only its `generatedAt` date advanced, so that file is deliberately left alone: it is not the failure, nothing gates its freshness, and its own test rebuilds against the committed date. A date-only churn on a branch being driven to green is noise. Verified: verify:cheap exit 0 with prettier --check . clean whole-tree; the static gates that failed now pass. One pre-existing failure is visible in the full unit suite and is NOT this branch's: tests/ranking-snapshot-provenance expires on a wall-clock 30-day window that lapsed today, against a fixture this branch does not touch. Closing it needs a live provider-backed eval canary and the owner's approval, so it is reported rather than worked around. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K8kRjars99uHE46MRH28rg
Main had moved 24 commits since this branch last merged it, and its copies of the generated indexes had diverged from this branch's. Squash-merging as it stood would have replaced main's current `data/repo-awareness-snapshot.json` with this branch's older one and left main failing `check:repo-awareness-snapshot` -- the same staleness this branch was just red for, pointed the other way. Fixed before the merge rather than after it. Merged origin/main (no conflicts) and regenerated with `npm run snapshot:repo-awareness`. `docs/scripts-index.md` and `docs/site-map.md` needed no change once the merge brought main's versions in; only the snapshot moved. The check now reports "in step with data/repo-awareness-snapshot.json (225 pages, 914 documents, 2719 reviews)". Verified: verify:cheap exit 0; prettier --check . clean whole-tree; docs:check-inventory, sitemap:check and check:repo-awareness-snapshot all green; the hazard register's own three suites still pass (43 tests) against the new base. The one remaining unit failure is unchanged and is not this branch's: tests/ranking-snapshot-provenance expires on a wall-clock 30-day window that lapsed on 2026-09-08, against a fixture this branch does not touch. Ledger row #304 predicted that exact boundary on 2026-08-13. Closing it needs a newer successful eval-canary artifact -- provider-backed, owner approval required -- so it stays reported rather than worked around, and the test is not weakened. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K8kRjars99uHE46MRH28rg
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_dd2580ad-c28b-4d1c-8685-c5eed6093975) |
There was a problem hiding this comment.
🟡 Changes recommended
Seven unresolved review findings remain, including six moderate issues and one nit.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Adds a gated developer-hub hazard register at /mockups/development/hazards, backed by separate build-time sources and a committed snapshot.
Changes:
- Adds separate hazard representations for PsychSift, Caring Contacts, and Ward Flow.
- Adds snapshot generation, loading, route, and hub-card integration.
- Adds tests, documentation, and generated metadata updates.
File summaries
| File | Summary |
|---|---|
tests/hazard-register.test.ts |
Covers parsing, snapshot invariants, and status handling. |
tests/developer-hub-panels.test.ts |
Covers hub-card activation, link, and wording. |
tests/developer-hazards-page.dom.test.tsx |
Covers hazard-page rendering. |
src/lib/developer-area/hub-panels.ts |
Activates the hazard-register card. |
src/lib/developer-area/hazard-register.ts |
Defines snapshot helpers; moderate finding (1 vote): unknown statuses are not promoted as blocking hazards. |
src/app/mockups/development/hazards/page.tsx |
Renders the register; moderate finding (2 votes) on stale/UTC expiry handling; nit (1 vote) on total counts appearing before blockers. |
scripts/generate-hazard-register-snapshot.mjs |
Generates snapshots; moderate findings: fail-open ledger handling (1 vote), incomplete Caring Contacts authority wording (2 votes), and malformed markdown-row handling (1 vote). |
package.json |
Adds snapshot scripts; moderate finding (1 vote): wire snapshot:hazards into prebuild. |
docs/site-map.md |
Documents the new route. |
docs/scripts-index.md |
Documents the snapshot generator. |
data/repo-awareness-snapshot.json |
Updates generated route metadata. |
data/hazard-register-snapshot.json |
Adds the committed hazard snapshot. |
Review details
Suppressed comments (5)
package.json:117
- The new generator is wired into
docs:update, butprebuildstill regenerates only the ledger snapshot. Editing any hazard source document and runningnpm run build(including the Docker build) can therefore succeed with an olddata/hazard-register-snapshot.json, leaving this clinical panel with stale rows and counts; the existing developer-hub snapshot convention runs generation in bothdocs:updateandprebuild(seedocs/codebase-index.md:699-714). Addsnapshot:hazardstoprebuildas well.
"snapshot:hazards": "node scripts/generate-hazard-register-snapshot.mjs",
"snapshot:repo-awareness": "node scripts/run-tsx.mjs scripts/generate-repo-awareness-snapshot.ts",
"prebuild": "node scripts/generate-outstanding-issues-snapshot.mjs --with-pending",
scripts/generate-hazard-register-snapshot.mjs:210
- Using
ledger.open ?? []makes this generator fail open when the dependent ledger snapshot is missing/renamed or has a matching-but-incomplete shape: it still emits Ward Flow as a missing register but silently drops all P1 mentions. That directly recreates the under-reporting this page is meant to prevent. Validate the dependent snapshot version and requireopento be an array before filtering, then throw with thesnapshot:issuesremediation.
const mentions = (ledger.open ?? [])
.filter((item) => item.priority === "P1" && /ward flow/i.test(item.summary))
scripts/generate-hazard-register-snapshot.mjs:70
- This parser is not fail-closed for the human-maintained markdown table: it silently skips a row with fewer than eight cells and blindly assigns the first eight when there are extra columns. A missing/added/reordered column (or a
|inside a cell, whichtableCellscurrently splits) can therefore make snapshot generation succeed with a shorter or misaligned hazard record, under-reporting or relabelling clinical risk. Validate the expected header/column count and throw on malformed rows, with fixtures for missing, extra, and inner-pipe cells.
if (cells.length < 8 || !/^H-[A-Z0-9]+$/i.test(cells[0])) continue;
const [id, hazard, cause, harm, control, residualRisk, owner, status] = cells;
rows.push({ id, hazard, cause, harm, control, residualRisk, owner, status });
src/app/mockups/development/hazards/page.tsx:132
- The PR description says the four uncontrolled rows come before any total, but this total tile is rendered before the
Nothing controls thesesection. A reader scanning from the top therefore sees44 hazards recordedbefore the four blocker rows, which leads with the reassuring total the page is intended to defer. Move the total/count summary below the unmitigated section or otherwise keep all total counts after those rows.
<CountTile testId="developer-hazards-count-total" value={snapshot.counts.hazards} label="hazards recorded" />
src/lib/developer-area/hazard-register.ts:98
- Preserving an unknown status avoids falsely labelling it as controlled, but this selector still only promotes the exact
unmitigatedvalue. A human-edited row whose status changes to a new value or typo therefore disappears from the prominent “Nothing controls these” section, andStatusPillrenders it with the neutral tone; the page can under-report an uncontrolled hazard while still showing the row lower down. Treat unknown statuses as an explicit blocking/needs-classification state (or fail snapshot generation) and cover that path.
export function unmitigatedHazards(snapshot: HazardSnapshot): { register: HazardRegister; hazard: HazardRow }[] {
return snapshot.registers.flatMap((register) =>
register.hazards.filter((hazard) => hazard.status === "unmitigated").map((hazard) => ({ register, hazard })),
);
- Files reviewed: 12/12 changed files
- Comments generated: 2
- Review effort level: Lite
💡 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: 3e9af262c2
ℹ️ 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".
…hazard-register # Conflicts: # data/repo-awareness-snapshot.json # docs/scripts-index.md
Main had moved 177 commits ahead and this branch developed a real merge conflict in two generated files. Both resolved by taking main's copy and regenerating, never by hand-merging: a hand-merged generated file can parse cleanly while disagreeing with what its generator would produce, which is what `check:repo-awareness-snapshot` exists to catch. `docs/scripts-index.md` needed one repair beyond that. Only its generated count line conflicted, but taking main's whole file also discarded the hand-written entry this branch adds for `generate-hazard-register-snapshot.mjs` -- the one line in that file a generator does not produce. Restored, and `docs:check-scripts` passes with it. The hazard snapshot regenerated to an identical body with only its `generatedAt` date advanced, so it is left alone: the content really is as of the earlier date, and a date-only churn on a branch being driven to green is noise. Verified on the merged base: check:repo-awareness-snapshot "in step (225 pages, 916 documents, 2739 reviews)", docs:check-inventory and docs:check-scripts green, prettier --check . clean whole-tree, and the hazard register's own three suites still pass (43 tests). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K8kRjars99uHE46MRH28rg
…ster' into claude/developer-hub-hazard-register
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_b565053c-68c1-4b72-834b-48b7570f7f3a) |
docs(ledger): prlanded record for #2745
Kept separate from #2742 (passwordless developer-area access) on purpose: that PR is green and ready to merge, and a credential change and a clinical-safety surface should not wait on one another's review.
Summary
hazard-registerhad sat at phase 4 since the 2026-08-25 placeholder sweep — deliberately kept when five other placeholders were removed, on the owner's 2026-08-26 ruling that the register belongs in the developer hub. It is now built at/mockups/development/hazards.docs/clinical-hazard-controls.jsonis machine-checked bycheck:clinical-hazard-controlsand states in its own words that it establishes static evidence only;docs/caring-contacts/hazard-log.mdis a draft no clinician has signed; Ward Flow has no register at all. Rendering them as one list would manufacture a uniform coverage claim no document here makes, so each section carries its own authority sentence, quoted rather than summarised.ledgerMentionsand neverhazards, and labelled as neither a register nor a complete list. That list is matched on P1 summaries only: searching detail text at every priority returned a flaky demo-clock test and a missing print stylesheet, and listing those under a hazard heading trains the reader to skim the one section that must never be skimmed.loadHazardSnapshotthrows on an unrecognised version, matchingloadLedgerSnapshot: a hazard page rendering a short list because the snapshot shape moved would under-report clinical risk, which is the failure it exists to prevent. An unrecognised status is passed through and rendered as its own text rather than rounded to "controlled".snapshot:hazardsscript, wired intodocs:updatebesidesnapshot:issuesand documented indocs/scripts-index.md. The card's summary deliberately avoids "all"/"every"/"complete", pinned by a test.Verification
npm run verify:cheap— exit 0, including the full offline unit suite:Test Files 1277 passed | 1 skipped (1278),Tests 18182 passed | 2 expected fail | 3 skipped (18187).npm run lint—[gate-receipts] recorded a pass for "lint:internal" (7468 input files), re-run on this branch's base.npm run typecheck—[gate-receipts] recorded a pass for "typecheck:internal" (7468 input files), re-run on this branch's base.npx prettier --check .—All matched files use Prettier code style!(whole tree)tests/hazard-register.test.ts(19 new),tests/developer-hazards-page.dom.test.tsx(5 new) andtests/developer-hub-panels.test.ts. The committed snapshot is proved in step with the source documents by rebuilding it inside the test, rather than trusting the commit.checkDatabase-awareness-snapshot—[repo-awareness] in step with data/repo-awareness-snapshot.json (225 pages, 905 documents, 2718 reviews).no leaks found.npm run verify:pr-local— Verification not run: deferred to CI, which runs the same risk-routed selection on this diff.npm run verify:ui— UI verification not run: one new Server Component page built from existing hub primitives, no shared chrome, tokens, or client behaviour touched. CI runs the browser suite on this diff.npm run check:production-readiness— Verification not run: provider-backed, needs explicit approval. Nothing in this diff changes production, privacy, or deployment behaviour.Risk and rollout
Clinical Governance Preflight
Clinical KB Database(sjrfecxgysukkwxsowpy)Governance notes (not checklist items): claims/source verification unchanged; page renders repository documents only (no patient-identifiable workflow expansion); Supabase target untouched; page reads no database so service-role stays server-only; each register's authority is stated verbatim with November review expiry and absent registers named as absences; no clinical decision-support behavior changed — the page asserts (and a test pins) it is not clinical assurance.
Notes
🤖 Generated with Claude Code
https://claude.ai/code/session_01K8kRjars99uHE46MRH28rg
Generated by Claude Code
Note
Medium Risk
Adds a clinical-safety visibility surface where under-reporting (dropped rows, merged registers, or coerced statuses) would misstate risk; mitigated by fail-loud loading and tests, but the page still influences governance decisions.
Overview
Adds a developer hub hazard register at
/mockups/development/hazards, backed by a new build-time snapshot pipeline rather than live reads.Snapshot generation:
npm run snapshot:hazards(generate-hazard-register-snapshot.mjs) writesdata/hazard-register-snapshot.jsonfrom PsychSiftclinical-hazard-controls.json+ analysis titles, Caring Contactshazard-log.md(table parsing), and Ward Flow as an explicit missing register with P1 ledgerledgerMentionsonly. Registers stay separate; counts include unmitigated rows and unsigned/missing registers.docs:updatenow runs this step alongside other snapshots.UI & hub: New Server Component page leads with uncontrolled hazards, quotes each register’s authority verbatim, recalculates review expiry in Australia/Perth at render time, and surfaces Ward Flow’s absence as a finding.
hub-panelspromotes Hazard register to phase 1 with the new href and wording that avoids completeness claims.Library & safety behavior:
hazard-register.tsloads the committed JSON with version fail-closed (like the ledger snapshot) and helpers for unmitigated hazards, missing registers, and status breakdown; unknown markdown statuses pass through uncoerced.Tests & docs: Unit/DOM tests pin parsing, snapshot shape, page copy, and committed snapshot parity with regenerated output; repo-awareness, site map, and scripts index document the route and script.
Reviewed by Cursor Bugbot for commit ae0702f. Configure here.