Skip to content

Harden auth and ingestion privacy/reliability paths - #175

Merged
BigSimmo merged 15 commits into
mainfrom
feature/sprint-2
Jul 2, 2026
Merged

Harden auth and ingestion privacy/reliability paths#175
BigSimmo merged 15 commits into
mainfrom
feature/sprint-2

Conversation

@BigSimmo

Copy link
Copy Markdown
Owner

Summary

  • Hardened private-route auth to fail closed and removed client token persistence assumptions tied to local storage scanning.
  • Applied caption/privacy redaction before worker persistence paths (cache + DB metadata) and normalized safe error logging, including modern Supabase key formats.
  • Aligned embedding dimension enforcement with runtime configuration and expanded regression coverage across auth, privacy, worker logging, and embedding-dimension behavior.

Verification

  • npm run verify:cheap
  • npm run verify:ui when UI, routing, styling, browser behavior, reduced-motion, or forced-colors behavior changed
  • npm run verify:release before release or handoff confidence claims
  • npm run format:check
  • npm run check:production-readiness when clinical workflow, privacy, environment, Supabase, source governance, or deployment behavior changed
  • npm run check:deployment-readiness when deployment startup, hosting, or rollout behavior changed
  • Focused suites for touched areas (auth/privacy/worker logging/embedding dimensions) were run and passed.

Clinical Governance Preflight

Complete this section when the change touches ingestion, answer generation, search/ranking, source rendering, document access, privacy, production env, or clinical output.

  • 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

Notes

  • npm run verify:cheap remains blocked by existing repository-wide type-check debt outside this scoped security/reliability work.
  • Untracked local directories (.impeccable/, docs/superpowers/plans/) were intentionally left out of this PR.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

BigSimmoand others added 2 commits July 2, 2026 19:46
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The fake sb_secret_live_ fixture in tests/privacy.test.ts exists to prove
safeErrorLogDetails redacts secrets; commit 92cd8ac in this branch's history
trips the generic-api-key rule on every scan even though the tip was already
rewritten to avoid it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@BigSimmo
BigSimmo enabled auto-merge (squash) July 2, 2026 12:03
@BigSimmo
BigSimmo merged commit b9ee0d0 into mainJul 2, 2026
4 checks passed
@BigSimmo
BigSimmo deleted the feature/sprint-2 branch July 2, 2026 16:31
BigSimmo pushed a commit that referenced this pull request Jul 31, 2026
Two small things, both about not hiding information.
A zero-count facet used `disabled`, which drops it out of the tab order. A
keyboard or screen-reader user then loses the row entirely and never learns why
it went quiet — and a `title` on a disabled control is not reliably announced.
It now uses `aria-disabled` with the click guarded, so the row stays focusable
and carries an sr-only explanation via `aria-describedby`. That is the
disabled-affordance pattern docs/wiring-conventions.md already describes.
`#175` corrects `#171`, which is already on main. `#171` claims the documents
source-type control duplicates the `Document type` facet group. It does not,
and the claim was made from a shared word rather than from the code:
`resultTypeTabs`/`filterMatchesByResultType` filter on artefact properties —
tableCount, imageCount, a .pdf extension — while the `Document type` facet
comes from `document_type` labels meaning policy, guideline, form. A guideline
containing a table is both. They are complementary axes.
The real duplication is the scope chip `tables` against the source-type
`Tables` tab. The merge `#171` recommends is still worth doing, but source-type
becomes its own group rather than being absorbed, and the scope chips are the
part that folds away. `#171`'s separate claim that `Sources` is navigation
stands.
The correction is appended rather than edited in: the ledger is append-only,
and `#158` is precedent for keeping a withdrawn finding on record so it is not
re-filed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Akwz3Sdms8uJ5AkDt3CduY
BigSimmo added a commit that referenced this pull request Jul 31, 2026
#179: the full catalogue silently went from a single minified line to 18,400
pretty-printed ones when the modality scrub routed it through syncTarget.
Measured +123 KB raw but only +3.4 KB gzipped, so the real cost is ~37,000
lines of git churn per data revision — set against reviewable diffs on a
205-record clinical dataset, which is probably the better trade. Recorded so
the format is a decision rather than a side effect.
#180: that same change made the generator write its curated output back over
the file it reads as source. It is stable only because curatedModality is a
fixed point, and it matters because #175 asks someone to hand-curate modality
values in exactly that file — an edit the next run could discard with no gate
to catch it, since --check compares against what the generator would produce.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
BigSimmo added a commit that referenced this pull request Aug 13, 2026
…#180) and serve aliases by rewrite (#177) (#1886)
* fix(therapies): stop the catalogue generator consuming its own output
build-therapies-index.mjs read public/therapy-compass-data/therapies.json as
its source and also wrote that same path as legacyFullTarget. Source and target
were one file, so every run consumed its own output.
That was not merely untidy. curatedFull nulls every tag-echo modality
(curatedModality), so the first run overwrote the author's raw modality values
with the scrubbed ones, and every later run re-read the scrubbed copy. It
survived only because the scrub happens to be idempotent — the raw input was
still destroyed, recoverable from git history alone.
Move the hand-edited catalogue to src/data/therapies-source.json, outside the
directory this generator writes, and read only from there. The file in
public/therapy-compass-data/ is now output, never input.
Also add the new path to .prettierignore. At its old path it was covered by the
public/therapy-compass-data/ entry; without an entry Prettier pretty-prints the
compact single-line catalogue into ~17k lines, which is the churn #179 fixed on
the generated side.
Verified: regenerated output is byte-identical to the previous alias (hashed
filenames unchanged), the source stays byte-identical to its original across
two further re-runs, check:therapy-data-index passes, and the 28 therapy
contract tests pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01STG6AU5J4gxrFJagP4pRti
* perf(therapies): serve catalogue aliases by rewrite instead of duplicating bytes
public/therapy-compass-data/ shipped each catalogue twice: therapies.json,
therapies-index.json and therapies-home.json were written byte-identical to
their content-addressed twin. Git stores one blob per identical pair, so history
was unaffected, but the working tree and every Docker image carried both — 2.81
MB, and 5.34 MB while the one-deploy grace generation is retained.
The alias URLs themselves are load-bearing: useTherapyData falls back to them
when a bundle older than the grace generation names a hashed file that no longer
exists, so they cannot simply be dropped. Serve them from next.config.ts
rewrites onto the current hashed filename instead, and stop writing the files.
afterFiles rather than beforeFiles: no file exists at the alias paths now, so
the rewrite is reached once the static handler finds nothing, and nothing
legitimate is shadowed. build-therapies-index.mjs --check now fails if an alias
file reappears, since a real file would win over an afterFiles rewrite and then
go stale at the next regeneration.
The generator writes content-addressed assets directly from the generated bytes
rather than copying a just-written alias, so projectionBytes/fullCatalogueBytes
are now the single definition of each payload's exact bytes.
Verified against a running server: all three alias URLs return 200 with content
byte-identical to their hashed asset; aliases keep Cache-Control max-age=0,
must-revalidate while the hashed asset keeps max-age=31536000, immutable, so the
alias does not inherit the destination's immutable policy; the retained grace
generation still returns 200. Hashed filenames are unchanged by this refactor,
proving the direct-write path is byte-identical. public/therapy-compass-data/
drops from 8.0M to 5.3M. npm run build succeeds and the stray-alias guard fires.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01STG6AU5J4gxrFJagP4pRti
* docs(issues): close#180 and #177, and correct the paths they moved
Both rows are resolved by the two preceding commits. Also corrects three rows
that cited the catalogue paths this work moved — docs:check-links caught them.
#175 mattered most: it instructed a future reader to curate modality values in
public/therapy-compass-data/therapies.json, which no longer exists. It now names
src/data/therapies-source.json, the only hand-edited catalogue file.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01STG6AU5J4gxrFJagP4pRti
* docs(ledger): record the review for this branch
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01STG6AU5J4gxrFJagP4pRti
* fix: ship generated therapy assets with app runner
* test: cover every next config runner import
* fix(docs): update therapy catalogue references
* fix(ci): reconcile therapy docs with ledger inbox
---------
Co-authored-by: Claude <noreply@anthropic.com>
BigSimmo added a commit that referenced this pull request Aug 17, 2026
Sign up for freeto 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.

1 participant

@BigSimmo