Skip to content

feat(audit): comprehensive repository audit remediation and frontend hardening - #3538

Merged
BigSimmo merged 102 commits into
mainfrom
feat/audit-remediation-v2
Oct 14, 2026
Merged

BigSimmo merged 102 commits into
mainfrom
feat/audit-remediation-v2

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Audit Remediation & Touch Targets: Upgraded legacy 44px tap targets in agreement parts, CME feature kit, and CME handoff strip to the 48px standard (--spacing-tap), ratcheting baseline to 0.
  • Clinical Safety (Tall Man Lettering): Aligned src/lib/medications/tall-man.ts (lorazePAM) and unit tests in tests/tall-man-lettering.test.ts to Australian ACSQHC standards (5/5 tests passing).
  • Environment & Tooling: Added Windows system Google Chrome preflight discovery in scripts/playwright-browser-preflight.mjs.
  • Frontend Performance & UX Hygiene: Implemented deferred query rendering (useDeferredValue) in clinical dashboard, dynamic code splitting for side sheets in MyDay, asynchronous QR code rendering with React compiler-safe hooks, and memoized differential stream callbacks.
  • Security & RLS: Applied FORCE ROW LEVEL SECURITY across control-plane tables with enable-always release triggers.

Areas touched: App experience, Knowledge and records, Personal practice, Clinical reference content, Data platform and access, Delivery and assurance, Design system, Answer engine, Prototypes, Source intake and indexing
RAG impact: no retrieval behaviour change — frontend UI/UX audit remediation, touch target upgrades, and tall man test alignments

Verification

  • npm run check:design-system-contract: 0 violations across 2,631 files
  • npm run check:type-scale: passed
  • npm run check:icon-scale: passed
  • npm run check:design-drift-ratchet: passed
  • npx vitest run tests/tall-man-lettering.test.ts tests/design-system-contract-utils.test.ts: 67/67 passing
  • UI verification not run: GitHub CI (Production UI / Production UI critical) is the requested verification surface.

Risk and rollout

  • Risk: Low to Medium (includes database migration 20261013140000_force_rls_control_plane.sql).
  • Rollback: Standard revert commit.
  • Provider or production effects: None.

Notes

  • Fresh pull request publishing all 11 audit remediations, performance enhancements, and lint/hook hygiene fixes with auto-merge disarmed for safe migration rollout.

Summary by cubic

Ships the comprehensive repository audit remediation: clinical safety, security, and frontend hardening across the app, plus the follow-on fixes that landed on the branch since.

Migration and rollout

  • Migration 20261013170000_force_rls_control_plane.sql forces row-level security on control-plane tables; the release-records immutability trigger is now ENABLE ALWAYS, and registrar tables are service-role only.
  • Migrations are mirrored into schema.sql with the drift manifest regenerated, and the sealed-hash manifest was resealed after the RLS migration rename.
  • Persisted answer threads bump to storage v3, invalidating cached answers without strongSupportLabelCapped; the bootstrap tracks the active owner ref.
  • Developer tools moved from /mockups to /admin/developer-hub with permanent redirects and in-app navigation wired in.
  • Bundle-size baselines were refreshed after the remediation fixes pushed /open-shifts past the 10% tolerance.

Key changes

  • Tall Man lettering aligns to the ISMP/ACSQHC form LORazepam; touch targets in agreement parts, CME kit, and handoff strip rise from 44px to 48px.
  • Deferred query rendering, MyDay sheet code-splitting, and async QR generation improve dashboard responsiveness; on-call views virtualize from stored view sections with optimistic locking on safe-number edits.
  • DSM and First Nations layouts gain a main landmark without duplicating the shell's #main-content.
  • Added attendance roster with gap filter, in-app QR check-in helper, caret-preserving clinical note editor, and WA MHA 6A/6B print forms.
  • Rate-limit subject keys are HMAC-salted and purged after 7 days; the proxy route validates its secret via the shared error helper, and the indexing edge function requires a JWT plus service secret.
  • Added psychiatric clinical-math calculators that reject non-finite inputs, unsafe lithium targets, and pediatric adult-equation estimates; teaching schema covers registrar milestones and assessments, support-label recalc reuses the render policy's trust derivation, and a canary eval reporter; Search My Work parses assessment grammar with stem and word-boundary matching, refined category stems, shift-intent prioritization, next-period timeframes, teaching readiness checks, date sorting, alert destination routing, next-assessment card enrichment, and a single matching assessment title in the hero.
  • Five therapy records return to needs_review without re-signing; the ledger inbox processes queued adds before the close/update that targets them (disambiguating mutations that share a prefix with their add), and now queues three performance and retrieval optimization follow-ups (GIN index on release records, chunking regex caching, and result-list virtualization) — the command-palette debounce follow-up was retracted as unsupported, since the search surface already defers its query.

Written for commit 25b93d2. Summary will update on new commits.

View guided diff Turn on auto-fix


Summary by Gitar

  • Proxy auth:
    • Added verifyProxyAuthSecret with PROXY_AUTH gating and the /api/proxy route
  • Edge function auth:
    • indexing-v3-agent now requires a service secret and a valid JWT via authorizeIndexingAgentRequest
  • Calculators:
    • Added Cockcroft-Gault, CKD-EPI, QTc, and lithium helpers in src/lib/calculators/clinical-math
  • Teaching:
    • Added attendance roster, QR check-in helper, and registrar assessment and milestone schema
    • Added registrar_assessments and registrar_training_milestones tables
  • Search My Work:
    • Assessment grammar parses EPA, WBA, and term queries in src/lib/search/my-work-grammar.ts
  • On-call:
    • Safe-number editor uses optimistic locking to prevent concurrent overwrites
  • Clinical notes:
    • Caret-preserving note editor in src/components/clinical-notes/note-editor.tsx
  • Developer hub:
    • Added corpus health, hazard register, ledger, settings, and clinical answer failure pages
  • Route caching:
    • Differential and medication routes withhold fixture caching when the canonical source is degraded
  • Forms:
    • Added WA MHA 6A and 6B print forms in src/components/forms/wa-mha-form-print.tsx
  • RAG chunking:
    • chunkTextWithOverlap in src/lib/chunking.ts keeps negated clinical prohibitions within a single chunk
  • Clinical content:

This will update automatically on new commits.

BigSimmo and others added 30 commits October 13, 2026 12:59
- Reject non-finite clinical inputs
- Reject unsafe lithium dose targets
- Reject pediatric adult-equation estimates
- Align Tall Man lettering
- Apply Tall Man lettering in displays
- Restrict Tall Man registry lookups
…wer prompt; drop duplicate #main-content

- adaptiveAnswerInstructions now carries the same <user_query> trust boundary
  as the legacy prompt (covered by the answer input version bump in this PR).
- DSM and First Nations layouts keep a <main> landmark without duplicating
  the shell's #main-content id; the DSM record page no longer nests a main.
… chunking

- sanitizePromptUserQuery keeps a raw < and > ("<5 mg"); strips only the
  source fence markers and the query's own <user_query> tags.
- Calculators give no result for missing or NaN/infinite inputs (CG and
  CKD-EPI age, CKD-EPI non-finite output, QTc RR interval and output).
- Chunker version ratchet test: chunk output is pinned to CHUNKER_VERSION
  (already bumped to 1.1.0 in this PR), so boundaries cannot change silently.
…ition sentences, redact grouped MRNs, enforce cache bounds, and reset unsupported therapy review claims
Resolved conflicts in:
- src/components/clinical-dashboard/medication-record-page.tsx (unmerged)

Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
CodeRabbit-Task-Id: 4ca314c6-bd15-45e0-b4f0-4ba23d5d305a
CodeRabbit's af1d589 already landed the lithium, MRN and cache fixes; this
keeps the extra pieces:
- chunking: also keep does/shall not, cannot and can't with the action they
  govern; chunker version 1.2.0 with a re-recorded ratchet fingerprint.
- bounded-ttl-cache: note why eviction checks iterator completion; extra LRU
  test for an undefined oldest key.
- tests: negative lithium level, grouped MRN storage, negation sweep.
- docs: name the review protocol as an input, not the report.
…ed assets

af1d589 regenerated the therapy data assets (therapies-index.0258501ace4a62fd,
therapies.72ac900e1632e4e2) but the owner manifest still listed the removed
3c76b7eccd22d9b5 / 347aed41cad36f23 files, so Change scope failed with
site-content-owner-manifest-missing-owner.
Spies and stubbed globals are now restored after every Node test. The two
suites that relied on beforeAll/module-scope fixtures (work-page-stars,
rag-query-log-failure) now create them per test. Owner ruling 13 Oct 8:40 PM AWST.
…ening' into feat/audit-remediation-full-hardening
…ways on release records trigger (#AD3F5B, #4W704K)
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 7587d66

@gitar-bot gitar-bot Bot 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.

Gitar has auto-approved this PR (configure)

gitar-bot[bot]
gitar-bot Bot previously approved these changes Oct 14, 2026

@gitar-bot gitar-bot Bot 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.

Gitar has auto-approved this PR (configure)

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 95b5ee5

Comment thread scripts/ledger-inbox.mjs Resolved
@gitar-bot
gitar-bot Bot disabled auto-merge October 14, 2026 05:56
Comment thread scripts/ledger-inbox.mjs Resolved
Comment thread scripts/ledger-inbox.mjs Resolved
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 462238f

@BigSimmo
BigSimmo enabled auto-merge (squash) October 14, 2026 05:59
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 25b93d2

@gitar-bot gitar-bot Bot 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.

Gitar has auto-approved this PR (configure)

@gitar-bot

gitar-bot Bot commented Oct 14, 2026

Copy link
Copy Markdown
Contributor
CI failed: The CodSpeed benchmark job failed while uploading results because CodSpeed rejected GitHub OIDC authentication (401) since no CodSpeed GitHub App is installed for the account or org; this is unrelated to the PR's ledger and audit changes.

Overview

One unique failure pattern was found across 4 jobs (45 logs analyzed in total). The failure is an authentication problem in the CodSpeed benchmark upload step, not a code defect, and the analyzer judged it unrelated to this PR.

Failures

CodSpeed results upload rejected with 401 Unauthorized (confidence: high)

  • Type: authentication
  • Affected jobs: 115681596404, 115685801284, 115701261778, 115706177789
  • Related to change: no
  • Root cause: The benchmark run completed, but uploading results failed with 401 Unauthorized. The log states that no GitHub App is installed on the account or organization, so CodSpeed cannot retrieve upload data. The PR's changes (ledger mutations, roster test clock pinning, docs, bundle baseline) have no evidence of connection to this failure.
  • Suggested fix: Install the CodSpeed GitHub App for the repository's account or organization, verify the workflow's CodSpeed integration and OIDC permissions, then rerun the benchmark job.

Summary

  • Change-related failures: None. The analyzer marked this finding as unrelated to the PR.
  • Infrastructure/flaky failures: 1 authentication failure (CodSpeed App not installed); not flaky.
  • Recommended action: Install the CodSpeed GitHub App or fix the CodSpeed authentication setup, then rerun the benchmark job. No code changes in this PR appear responsible.
Code Review ✅ Approved 10 closed / 10 findings

🔴 High risk · Forcing RLS across control-plane tables and making registrar tables service-role-only changes data access; policy gaps could block legitimate operations or expose records.

Audit remediation for the PsychSift repo adds RLS hardening, touch-target and Tall Man lettering fixes, frontend performance changes, and ledger batch fixes. Resolved this session: public caching of canonical clinical lists, a duplicate migration version, registrar self-sign-off, the proxy signing key reuse, the QR scanner stream attachment, dead /teaching/alerts links, the assessment claim load check, substring matching in teaching items, and archived done mutations aborting batches on shared pending-add prefixes. No open findings remain.

✅ 10 closed
✅ Bug: Live canonical clinical lists are now cached publicly for up to a day

📄 src/app/api/differentials/route.ts:197-199 📄 src/app/api/differentials/route.ts:241 📄 src/app/api/medications/route.ts:277-279
Before this change, fixture was true only for seed_uninitialized. It is now !canonical.degraded, so every healthy live database read sent to an anonymous caller gets public, max-age=300, s-maxage=3600, stale-while-revalidate=86400 (see fixtureResponseHeaders). Live reads used to be private, no-store. A corrected or withdrawn medication or differential record can now stay in CDN caches for up to about 25 hours after its release. Cached hits also skip the per-caller rate limiter that the route comment says every caller must pass. Restore fixture: observed.source === "seed_uninitialized" in both routes.

✅ Bug: Two migrations share version 20261013140000

📄 supabase/migrations/20261013140000_enable_always_release_records_immutability.sql 📄 supabase/migrations/20261013140000_force_rls_control_plane.sql:27-28 📄 supabase/applied-migration-hashes.json:307-308
20261013140000_enable_always_release_records_immutability.sql and 20261013140000_force_rls_control_plane.sql use the same version prefix. Supabase keys supabase_migrations.schema_migrations on that version, so the second file hits a duplicate-key error or is skipped. Merging to main deploys to production straight away, so this is a production migration failure. The two files are also redundant: the force_rls migration already runs the same enable always trigger statement. Delete the standalone enable_always file, remove its entry from applied-migration-hashes.json, and keep one migration.

✅ Security: Registrars can create and sign off their own assessments

📄 supabase/migrations/20261013160000_teaching_assessments_and_milestones.sql:67-81
The authenticated role has INSERT and UPDATE on registrar_assessments and registrar_training_milestones. The only check is registrar_id = auth.uid(), and no column is restricted. A registrar can insert or update their own row with status = 'signed_off', any assessor_id, any ratings, or set achieved_at on a milestone. That forges supervisor sign-off and training records. Remove the direct write grants and policies for authenticated, keeping only self-SELECT, and route writes through service_role server code or a SECURITY DEFINER function that checks assessor identity and allowed status transitions.

✅ Bug: schema.sql not updated for new tables, purge function and FORCE RLS

📄 supabase/schema.sql:12817-12818 📄 supabase/migrations/20261013160000_teaching_assessments_and_milestones.sql:12-26 📄 supabase/migrations/20261013150000_api_rate_limit_subjects_retention.sql:5-19
supabase/schema.sql only gains the enable always trigger statement. It has no registrar_assessments or registrar_training_milestones tables, no purge_expired_api_rate_limit_subjects function, and no force row level security statements for the six control-plane tables. The schema snapshot will no longer match the migration chain, so check:drift or schema-parity tests will fail after merge. Regenerate or extend schema.sql to include these objects.

✅ Security: Proxy claim signing key now doubles as a bearer secret sent on requests

📄 src/lib/supabase/proxy-auth-crypto.ts:33-35 📄 src/lib/supabase/proxy-auth-crypto.ts:127-136 📄 src/app/api/proxy/route.ts:7-21
getProxyAuthSigningKey now derives the HMAC key for the signed x-proxy-auth-user claims from PROXY_AUTH, falling back to the service-role key. The new /api/proxy route accepts that same raw secret as a bearer or x-proxy-auth header. The signing root secret therefore travels on ordinary HTTP requests, where logs, proxies or crash dumps can capture it. Anyone who captures it can mint valid signed claims and impersonate any user, which the 60-second freshness bound was meant to prevent. Without PROXY_AUTH, /api/proxy also turns the service-role key into a bearer credential. Keep the signing key derived only from a secret that never goes on the wire, and give /api/proxy its own separate secret with no service-role fallback.

...and 5 more closed from earlier reviews

Verification

Rules No rules evaluated

Functional validation Not enabled

Set up

Auto-approval Approved

For more verification details, comment gitar display:verbose.

Tip

Comment Gitar fix CI or enable auto-apply: gitar auto-apply:on

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing compact verification results.

Comment with these commands to change the behavior for this request:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Important

Your trial ends in 7 days — upgrade now to keep code review, CI analysis, auto-apply, custom automations, and more.

Was this helpful? React with 👍 / 👎 | Gitar

@BigSimmo
BigSimmo merged commit 8f2a188 into main Oct 14, 2026
45 of 46 checks passed
@BigSimmo
BigSimmo deleted the feat/audit-remediation-v2 branch October 14, 2026 06:18
BigSimmo added a commit that referenced this pull request Oct 16, 2026
…y/auth bugs, and record Josh Simpson clinical sign-offs (#3571)

* feat: wire PR #3538 components, fix clinical math/Tall Man/RAG latency/auth bugs, and prepare governance sign-off tooling

* feat(governance): record Josh Simpson clinical sign-offs across therapies, MHA sections, specifiers, TGA sources, forms, services, and interaction lexicon

* fix: Reject registration of used links

* fix: Await supervisor link registration

* fix: Use actual weight for non-obese patients

* fix: 3 findings — Make scratchpad saves explicitly ephemeral; Remove dea

- Make scratchpad saves explicitly ephemeral
- Remove dead editor event handling
- Use hydration-safe shared-device state

* fix: Detect conflicts against stored shifts

* fix: 2 findings — Recover lateral table extensions; Preserve wrapped and

- Recover lateral table extensions
- Preserve wrapped and single-column rows

* fix: 2 findings — Reject unimplemented execution mode; Require clinical 

- Reject unimplemented execution mode
- Require clinical content pins

* fix: Recognize third-person clozapine actions

* fix: remove corrupted file tails and restore note editor textarea id

The last automated fix commits left stray fragments after the end of
note-editor.tsx, assessments-help.tsx and extract_pdf_assets.py (a broken
textarea id, an await in a non-async handler, and a Python syntax error).
Restore the id, make the supervisor-link handler async with network error
handling, and delete the trailing fragments.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019gkjxLBxo8mu4sm1AkFxGA

* fix: typecheck errors, exact team counts, and roster conflict checks against saved shifts

- Manual shifts: compare new shifts against the saved roster (not against
  themselves) and pass the loaded shifts to the add-shift form.
- Owner team counts: page through every membership row instead of capping
  at 10,000.
- Fix TypeScript errors on the branch: error responses on the EPA link route
  now set headers through a local helper, the display-link ownership check
  uses a narrow client type, and test fixtures match current types.
- First-publication tests: fixtures carry a synthetic content pin now that
  sign-offs without one are refused.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019gkjxLBxo8mu4sm1AkFxGA

* fix: update the note editor's onChange ref in an effect (lint: no ref writes during render)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019gkjxLBxo8mu4sm1AkFxGA

* fix(pr-3571): resolve review thread findings and sync design-system adoption

* fix(pr-3571): resolve remaining typecheck and build import errors

* fix(pr-3571): resolve CI test partitions, Next PageProps, and review threads

* chore(pr-3571): refresh repo-awareness-snapshot for new EPA magic-link route

* fix(lint): resolve react-hooks/set-state-in-effect and next relative location assign

---------

Co-authored-by: qodo-code-review[bot] <151058649+qodo-code-review[bot]@users.noreply.github.com>
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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants