Skip to content

Build the hazard register, the hub's last unbuilt clinical card - #2745

Merged
BigSimmo merged 12 commits into
mainfrom
claude/developer-hub-hazard-register
Sep 12, 2026
Merged

Build the hazard register, the hub's last unbuilt clinical card#2745
BigSimmo merged 12 commits into
mainfrom
claude/developer-hub-hazard-register

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Sep 8, 2026

Copy link
Copy Markdown
Owner

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-register had 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.
  • It leads with what is not controlled. The registers hold 44 rows, and a page opening with "44 hazards" reads as thoroughness, which is the opposite of what it reports. 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; none is closable by code.
  • It never merges the three registers. docs/clinical-hazard-controls.json is machine-checked by check:clinical-hazard-controls and states 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, 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.
  • 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".
  • New snapshot:hazards script, wired into docs:update beside snapshot:issues and documented in docs/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)
  • Affected suites: 43 tests across tests/hazard-register.test.ts (19 new), tests/developer-hazards-page.dom.test.tsx (5 new) and tests/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).
  • Gitleaks 8.24.3 (the pinned version the workflow installs), over this branch's range: no leaks found.
  • npm run verify:pr-localVerification not run: deferred to CI, which runs the same risk-routed selection on this diff.
  • npm run verify:uiUI 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-readinessVerification not run: provider-backed, needs explicit approval. Nothing in this diff changes production, privacy, or deployment behaviour.

Risk and rollout

  • Risk: the failure mode here is clinical, not cosmetic — a row silently dropped, a status quietly rounded to "controlled", or the three registers merged into one list, each of which under-reports unmitigated risk on the page whose purpose is to report it. The tests are written around those three failures rather than around rendering: every row is asserted present in its own register's section, the status breakdown is asserted to lose nothing to an unrecognised value, and the register order is pinned.
  • Rollback: revert the commit. The page is additive; the card returns to phase 4 and nothing else changes.
  • Provider or production effects: None. The page reads a build-time snapshot generated from committed documents — no provider call, no database read, no migration.
  • RAG impact: none — no retrieval, ranking, selection, chunking, or answer-contract surface is touched.

Clinical Governance Preflight

  • Source-backed claims still require linked source verification before clinical use
  • No patient-identifiable document workflow was introduced or expanded without explicit governance approval
  • Supabase target remains Clinical KB Database (sjrfecxgysukkwxsowpy)
  • Service-role keys and private document access remain server-only
  • Demo/synthetic content remains clearly separated from real clinical sources
  • Source metadata, review status, and outdated/unknown-source behavior remain conservative
  • Deployment classification/TGA SaMD impact was checked when clinical decision-support behavior changed

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

  • The page is behind the existing developer-area gate, so it is reachable only by a signed-in administrator (or, once Open the developer area with a bookmarked link instead of a sign-in #2742 lands, the bookmarked link).
  • What it shows is a reconstruction in one case and a machine-checked register in another; it does not attempt to reconcile them, and the differing authorities are the point rather than a defect to tidy away.

🤖 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) writes data/hazard-register-snapshot.json from PsychSift clinical-hazard-controls.json + analysis titles, Caring Contacts hazard-log.md (table parsing), and Ward Flow as an explicit missing register with P1 ledger ledgerMentions only. Registers stay separate; counts include unmitigated rows and unsigned/missing registers. docs:update now 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-panels promotes Hazard register to phase 1 with the new href and wording that avoids completeness claims.

Library & safety behavior: hazard-register.ts loads 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.

`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
@coderabbitai

coderabbitai Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 462e2575-30a0-42db-b7b3-d267dcf2b934


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.

❤️ Share

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

@supabase

supabase Bot commented Sep 8, 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 ↗︎.

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

CI triage

CI failed on this PR. Automated classification of the 2 failed job(s):

  • Static PR checksneeds investigation: inspect the failing step and uploaded diagnostics; rerun only after classifying the cause.
  • PR requiredneeds investigation: inspect the failing step and uploaded diagnostics; rerun only after classifying the cause.

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
@BigSimmo
BigSimmo marked this pull request as ready for review September 12, 2026 04:30
@BigSimmo
BigSimmo requested a lite review from Copilot September 12, 2026 04:30
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 12, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-12T04:35:55.878669Z 3e9af26 Draft marked ready
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@cursor

cursor Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Bugbot couldn't run - usage limit reached

Bugbot 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)

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.

🟡 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, but prebuild still regenerates only the ledger snapshot. Editing any hazard source document and running npm run build (including the Docker build) can therefore succeed with an old data/hazard-register-snapshot.json, leaving this clinical panel with stale rows and counts; the existing developer-hub snapshot convention runs generation in both docs:update and prebuild (see docs/codebase-index.md:699-714). Add snapshot:hazards to prebuild as 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 require open to be an array before filtering, then throw with the snapshot:issues remediation.
  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, which tableCells currently 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 these section. A reader scanning from the top therefore sees 44 hazards recorded before 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 unmitigated value. A human-edited row whose status changes to a new value or typo therefore disappears from the prominent “Nothing controls these” section, and StatusPill renders 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.

Comment thread scripts/generate-hazard-register-snapshot.mjs Outdated
Comment thread src/app/mockups/development/hazards/page.tsx Outdated

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

Comment thread src/lib/developer-area/hub-panels.ts
Comment thread scripts/generate-hazard-register-snapshot.mjs Outdated
Comment thread src/app/mockups/development/hazards/page.tsx Outdated
Comment thread src/app/mockups/development/hazards/page.tsx Outdated
claude and others added 5 commits September 12, 2026 04:49
…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
@cursor

cursor Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Bugbot couldn't run - usage limit reached

Bugbot 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)

@BigSimmo
BigSimmo merged commit eef2112 into main Sep 12, 2026
31 checks passed
@BigSimmo
BigSimmo deleted the claude/developer-hub-hazard-register branch September 12, 2026 07:07
BigSimmo added a commit that referenced this pull request Sep 12, 2026
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.

3 participants