Skip to content

fix: audit P2/P3 hardening (M13 guard, upload rate limit, P0 carry-over) - #296

Merged
BigSimmo merged 6 commits into
mainfrom
cursor/audit-p2-p3-hardening-b54f
Jul 5, 2026
Merged

fix: audit P2/P3 hardening (M13 guard, upload rate limit, P0 carry-over)#296
BigSimmo merged 6 commits into
mainfrom
cursor/audit-p2-p3-hardening-b54f

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Jul 5, 2026

Copy link
Copy Markdown
Owner

Summary

Continues the repo audit remediation after P0/P1 work, on top of latest main.

P0 carry-over (commit 1): RAG cache owner/indexing-version guards, synopsis parity in ranking/detectors, DELETE TOCTOU re-check, worker cache invalidation on job completion, and RAG_QUERY_HASH_SECRET required in production-like readiness checks.

P2 (commit 2):

  • M13: search_schema_health() now reports commit_document_index_generation.preserve_legacy_artifacts_migration when the live DB is still running the pre-20260702 commit body. New migration 20260706010000_search_schema_health_m13_guard.sql and npm run check:m13-migration helper.
  • Upload hardening: /api/upload consumes the document_upload rate-limit bucket (12/min owner, 3/min anonymous).

P3 disposition (documented in docs/process-hardening.md):

  • L9 searchable-only image_count comment in worker/main.ts
  • L11 triple readFile already documented at ingestion site
  • L18 duplicate audit_logs policy — do not edit applied migrations
  • L19 CSP unsafe-inline — accepted risk, deferred

Verification

  • Targeted tests: tests/rag-cache-utils.test.ts, tests/supabase-schema.test.ts (60/60 in targeted run; schema M13 guard assertions included)
  • Full npm run verify:cheap — blocked in the audit worktree by a slow/incomplete npm ci (next/server resolution); CI should run the full gate on the PR
  • npm run eval:retrieval:quality — required before merge because P0 touches RAG cache/selection paths; needs live Supabase + OpenAI keys
  • npm run check:m13-migration — run after applying 20260702000000_commit_generation_preserve_legacy_artifacts.sql and 20260706010000_search_schema_health_m13_guard.sql to live Supabase

Clinical Governance Preflight

  • Source-backed claims still require linked source verification before clinical use
  • No patient-identifiable document workflow was introduced or expanded
  • 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
  • No clinical decision-support scoring/ranking semantics changed in the P2 commit (P0 cache/synopsis parity only)

Live migration follow-up

Apply via normal Supabase migration workflow (if not already live):

  1. 20260702000000_commit_generation_preserve_legacy_artifacts.sql
  2. 20260706010000_search_schema_health_m13_guard.sql

Then: npm run check:m13-migration, npm run reindex:health, npm run check:indexing

Open in Web Open in Cursor 

@supabase

supabase Bot commented Jul 5, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project sjrfecxgysukkwxsowpy due to reaching the limit of concurrent preview branches.
Go to Project Integrations Settings ↗︎ if you wish to update this limit.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@BigSimmo
BigSimmo marked this pull request as ready for review July 5, 2026 18:00
@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.

@cursor cursor 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.

Cursor Bugbot has reviewed your changes using high effort and found 4 potential issues.

Fix All in Cursor

Bugbot Autofix prepared fixes for all 4 issues found in the latest run.

  • ✅ Fixed: Inflight answers repopulate stale cache
    • setCachedAnswer now skips writes when the indexing version captured at retrieval start no longer matches, preventing stale inflight answers from repopulating the cache after invalidation.
  • ✅ Fixed: Synopsis omitted from numeric exemption
    • hasNumericOrTableEvidence now uses clinicalResultEvidenceHaystack so synopsis-only numeric dosing qualifies for numericEvidenceExempt penalties.
  • ✅ Fixed: Late delete abort wipes cache
    • The late active-job TOCTOU check now runs before deleteDocumentIndexTraceRows so a 409 abort no longer leaves the owner-wide shared cache cleared while the document remains.
  • ✅ Fixed: Index unit delete partial wipe
    • document_index_units are deleted first so a failed delete throws before memory cards and sections are removed, avoiding inconsistent partial state.

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit 1864748. Configure here.

Comment thread src/lib/rag.ts
Comment thread src/lib/clinical-search.ts
Comment thread src/app/api/documents/[id]/route.ts
Comment thread src/lib/deep-memory.ts
…elete ordering

- Skip setCachedAnswer when indexing version changed during inflight retrieval
- Include retrieval_synopsis in hasNumericOrTableEvidence haystack
- Run late active-job check before destructive trace/cache cleanup on delete
- Delete document_index_units before memory cards/sections to avoid partial wipe
@BigSimmo
BigSimmo enabled auto-merge July 5, 2026 18:36
BigSimmo and others added 2 commits July 6, 2026 02:41
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@BigSimmo
BigSimmo merged commit e3cb199 into main Jul 5, 2026
5 checks passed
@BigSimmo
BigSimmo deleted the cursor/audit-p2-p3-hardening-b54f branch July 8, 2026 16:24
BigSimmo added a commit that referenced this pull request Aug 9, 2026
…tes, and pin the factsheet heading census (#1779)

* test(factsheets): census every h1 in the factsheet detail document

The hero <h1> and the portaled print sheet's <h1> are correct and mutually
exclusive by construction: on screen `.factsheet-print-sheet { display: none }`
removes the print subtree, and in print
`html.factsheets-printing body > *:not(.factsheet-print-portal)` removes the
shell that owns the hero. Neither state exposes two headings to the
accessibility tree, and the printed PDF is a separate document whose section
headings are already <h2>, so demoting its title would leave it with no
top-level heading.

The real gap was that the existing assertion was scoped to the page testid, so
the document-level invariant was asserted nowhere and a stray third <h1> would
not have been caught. Pin the census instead: exactly two, one per container,
both carrying the title, plus a non-empty <h2> outline in the print sheet.
jsdom applies no stylesheet, so a census is the right guard rather than a
visibility assertion.

FactsheetPrintSheet stays in factsheet-detail-page.tsx —
design-system-contract-utils.mjs scopes its raw-colour exemption to the literal
factsheet-print-sheet marker and fails closed if it moves.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YEFowCVUVrybKvKvReQ924

* docs(testing): record the verified recipe for restoring local browser gates

The remote/Cloud drift note said to delegate browser proof to CI and left the
impression that local gates were unrecoverable. They are recoverable; the
blocker was two separate image faults, and the second is why the obvious fix
looks impossible.

The baked node_modules is stale or incomplete — containers have shipped none at
all, and earlier ones reported playwright 1.62.0 against a locked 1.62.1 with
tailwind-merge absent entirely, which is an incomplete install rather than a
version skew, so the lockfile pin was never wrong. And npm ci cannot repair it
because jsdom@30.0.1 requires node ^22.22.2 || ^24.15.0 || >=26.0.0 while images
have shipped v24.13.0, so the install dies on EBADENGINE under engine-strict.

Installing Node 24.19.0 clears that, npm ci then exits 0 and parity reports all
seven pinned packages, and `npx playwright install` supplies Chromium 1234
(images ship only 1194). Verified end to end this session, launch included.

Keeps the existing Stop intact and makes it cheap to honour: install the
matching revision rather than forcing a run against 1194.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YEFowCVUVrybKvKvReQ924

* fix(bundle-budget): split production weight from mockup scratch

One number could not honestly answer two questions. totalGzipBytes summed every
built client chunk, including src/app/mockups/** design scratch that 404s in
production, against a ceiling named as though it were production weight. #13
held that mockup chunks are not a production bundle; this gate charged them
anyway, which is how PR #1580 blocked at +10.1% for chunks no user can load.
#252 recorded the contradiction and left the metric undecided.

Measured on a clean build of main at af85cbc, the blur had become the whole
signal: 1546.5 KiB total was +9.96% of the 1406.4 KiB baseline — 576 bytes from
failing Build — while production-only was 1279.1 KiB, 9.06% BELOW that same
baseline. Every byte of the apparent regression was design scratch (267.5 KiB
across 76 chunks over 66 mockup routes) and production had actually shrunk.
latency-audit-2026-07-28 corroborates: 1,309,274 bytes then against 1,309,772
production-only now, flat to +0.04%, so the 2026-08-04 bump to 1,440,201 had
absorbed mockup growth as production growth. Raising the ceiling again would
have hidden that permanently, so this splits rather than ratchets.

production (10%) covers every chunk a non-mockup route reaches plus chunks no
manifest claims — framework, polyfills, runtime. mockups (25%) covers chunks
reachable only from /mockups/**, as a runaway detector rather than a
per-mockup gate; a ceiling tight enough to fire on the next mockup would just
be --update'd reflexively. A chunk shared by both counts as production because
it would be built either way.

Attribution reads the per-route *_client-reference-manifest.js files under
.next/server/app, since Next 16 webpack emits no app-build-manifest.json, and
fails closed when that tree is missing or resolves no routes so the buckets can
never silently collapse. Both fail paths proven against the real build.

Also captures #296: pr-handoff-stop.test.ts fails in any root container because
it chmods a fixture dir to force a write failure and root ignores permission
bits — pre-existing, reproduced on clean af85cbc.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YEFowCVUVrybKvKvReQ924

* fix(bundle-budget): fail closed on bad manifests and zero baselines

Review feedback on PR #1779: treat unparseable route manifests as fatal
attribution errors instead of counting them as resolved empty routes, and
handle a zero mockup/production baseline without NaN percentage math.

Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
cursor Bot pushed a commit that referenced this pull request Aug 9, 2026
Resolve docs/outstanding-issues.md by keeping main's #295/#296/#252
archive updates, re-closing #218/#270 from this PR, and renumbering the
text-2xl-compact retirement task to #297 to avoid the id collision.

Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
BigSimmo added a commit that referenced this pull request Aug 9, 2026
Four rows, all measured against origin/main 199b303 in this session:

- #295 (P2 task) the ErrorState enforcement check. GATES.md:106 still
  reads "planned" and grep over scripts/ and eslint-rules/ returns zero
  references to ErrorState, so the component exists but nothing requires
  it. Deliberately not flipped to implemented.
- #296 (P3 task) adopting ErrorState at the three surfaces that genuinely
  hand-roll the guard. Live-look change, downstream of the redesign.
- #297 (P3 issue) the safe-area exemption in the new raw-value ratchet,
  which is by form rather than by absence of a literal.
- #298 (P2 issue) the three sites carried into M4 as guards that are not
  guards, so the next reader does not convert them.

Written with npm run issues:add, never by hand.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
BigSimmo pushed a commit that referenced this pull request Aug 10, 2026
…a duplicate of #296

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XDuNkNHU4zFn1cDquUXv5
cursor Bot pushed a commit that referenced this pull request Aug 12, 2026
Resolve docs/outstanding-issues.md by keeping this PR's archive of
#207/#226/#230/#261/#284/#296 and updated open-row notes; main had no
unique open ids beyond those already archived here.

Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
BigSimmo pushed a commit that referenced this pull request Aug 12, 2026
…system rows

Archived as verified-delivered or duplicate: #303, #284 (dup of #296), #149,
#159, #286, #287, #288, #256, #261, #291.
Re-scoped with re-measured evidence: #210 (gate was never missing; the defect
is the .next/dev types include), #262 (aliases 228 -> 193), #266 (counts moved,
23 unadopted holds), #275 (premise 'only one implementation' is false — the
values leaked to five files).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017paT42ZVMf8jaLtkjFxdy5
BigSimmo pushed a commit that referenced this pull request Aug 12, 2026
docs/outstanding-issues.md has no merge driver by design, so this conflicted
in seven hunks. Resolved by taking main's file as the base — it carries main's
own archives (#207, #226, #230, #261, #284, #296) — then re-applying all 23 of
this branch's changes through scripts/outstanding-issues.mjs: 15 archives and
8 detail rewrites. Verified afterwards that every one landed and that the
guard reports no ids deleted from base.

Two rows are deliberately NOT resurrected. This branch's #302 (style-contract
line-break artifacts) and #303 (issues:next-id sync) no longer exist anywhere
on main under any id — main has since reused both numbers for entirely
different live rows. Both of this branch's rows were verified-delivered trivia,
so nothing actionable is lost, but the disappearance is itself evidence for
#156/#168: a main-side merge dropped rows, which is the hazard those rows
describe.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017paT42ZVMf8jaLtkjFxdy5
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.

2 participants