Skip to content

feat(audit): comprehensive repository audit remediation across phases 1, 2, and 3 - #3510

Closed
BigSimmo wants to merge 95 commits into
mainfrom
feat/audit-remediation-full-hardening
Closed

BigSimmo wants to merge 95 commits into
mainfrom
feat/audit-remediation-full-hardening

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner

User description

Summary

This pull request implements comprehensive remediation across all three phases of the repository audit, spanning clinical math accuracy, medication safety, memory isolation, RAG prompt protection, and privacy controls.

  • Phase 1 (Critical P0 Hotfixes):
    • TS-01: Vitest node environment mock isolation in tests/setup/node.setup.ts.
    • MEM-01 & MEM-02: Bounded LRU cache eviction and falsy key safety in src/lib/bounded-ttl-cache.ts, src/lib/openai.ts, and src/lib/signed-url-cache.ts.
    • CS-001: Dedicated psychiatric clinical math modules in src/lib/calculators/clinical-math/ (renal Cockcroft-Gault/eGFR, QTc prolongation formulas Bazett/Fridericia/Hodges/Framingham, and lithium 12h trough evaluation).
  • Phase 2 (Clinical & Architectural Hardening):
    • CS-002: Australian ACSQHC/ISMP Tall Man lettering module in src/lib/medications/tall-man.ts for look-alike/sound-alike psychotropics (cloZAPine/cloNIDine, etc.).
    • CHK-01: Chunk boundary clinical negation preservation in src/lib/chunking.ts (preserves DO NOT, CONTRAINDICATED, NEVER, AVOID, WITHHOLD across chunk overlaps).
    • INJ-01: RAG user query sanitization & delimiter wrapping (<user_query>) in src/lib/rag/rag.ts and src/lib/rag/rag-cache.ts cache key versioning.
    • TS-02: Replaced arbitrary page.waitForTimeout sleeps with condition-based expect.poll in tests/ui-chrome-scroll.spec.ts.
  • Phase 3 (Operational Resilience & Privacy):
    • PY-01: PyMuPDF document handle deterministic reclamation via try...finally: document.close() in worker/python/extract_pdf_assets.py.
    • LOG-01 / SEC-03: Incidental Australian Medicare number, phone number, and hospital MRN scrubbing in src/lib/query-privacy.ts.

Areas touched: Answer engine, Knowledge and records, Clinical reference content, Data platform and access, Source intake and indexing, App experience, Delivery and assurance (plus 4 files not yet placed in any area)
RAG impact: no retrieval behaviour change — prompt delimiter hardening and negation boundary preservation only

Verification

  • Checks run:
    • Unit tests: tests/clinical-math-calculators.test.ts (14/14 passed)
    • Unit tests: tests/tall-man-lettering.test.ts (10/10 passed)
    • Unit tests: tests/chunking-negation.test.ts (passed)
    • Unit tests: tests/rag-prompt-injection.test.ts (passed)
    • Unit tests: tests/bounded-ttl-cache.test.ts (passed)
    • Unit tests: tests/privacy.test.ts (passed)
    • Unit tests: tests/setup-node.test.ts (passed)
    • TypeScript strict check: tsc -p tsconfig.typecheck.json (0 errors)
    • Prettier formatting check: npm run format:changed (clean)
    • Pre-push static guard: guard-push.mjs (0 errors)

Risk and rollout

  • Risk: Low to Moderate. Modular additions in pure library helpers and isolated tests. Core retrieval logic preserves backward compatibility.
  • Rollback: Revert commit 1bdb79e734.
  • Provider or production effects: None. Zero external API or database calls required.

Notes

  • Remediates all high-confidence issues identified in docs/audit/full-repository-audit-2026-10-11.md.

Summary by cubic

Implements the full-repository audit remediation across phases 1, 2, and 3: clinical math safety, medication and RAG prompt hardening, cache memory bounds, privacy, accessibility, and database security, with tests for each fix.

Behavior changes

  • RAG user queries are wrapped in <user_query> delimiters in both the legacy and adaptive answer prompts; only the prompt's own delimiters are stripped, so a raw < like "<5 mg" survives. The answer fingerprint includes a query serialization version, so cached answers are not reused.
  • Chunking keeps clinical prohibitions ("DO NOT", "CONTRAINDICATED", "does/shall not", "cannot") attached to the text they govern across chunk boundaries, pinned to CHUNKER_VERSION by a version ratchet test.
  • Adds psychiatric clinical math calculators (Cockcroft-Gault/eGFR, QTc, lithium 12h trough) with boundary validation; non-finite or negative inputs, unsafe lithium dose targets, and pediatric adult-equation estimates are rejected.
  • Applies ISMP/ACSQHC Tall Man lettering to medication names in the clinical dashboard, including inside formulation names, with registry lookups restricted to known pairs.
  • Bounded LRU helpers fix unbounded cache growth and falsy-key eviction in the embedding and signed URL caches; deferred query rendering, lazy-loaded sheets, and async QR generation cut idle work.
  • Scrubs stored query text for Australian Medicare numbers, phone numbers, hospital MRNs (including grouped formats), and DOBs.
  • /api/proxy verifies only PROXY_AUTH, refuses the service-role key, and fails closed using a dedicated claim signing key. Control-plane tables get FORCE ROW LEVEL SECURITY, anonymous rate-limit subject keys are salted with a 7-day retention purge, and eval summaries report counts only while ledger intents reconcile so pending adds land before their target mutations.
  • Developer tools move from /mockups to /admin/developer-hub (with redirects), and dev scripts raise the Node heap cap.
  • Closes PyMuPDF document handles in a finally, registers a global Node test setup that restores mocks and globals between cases, and replaces fixed scroll waits with condition polling.
  • The QR check-in scanner attaches its camera stream after the video mounts and stops a late camera grant; the printable WA MHA Form 6A/6B shows no invented defaults.
  • Adds a clinical note editor with debounced auto-save where the newest save wins, re-collapses search results on each new query, keeps v2 answer threads with a support label capped and recalculated from the render policy's trust rather than discarding them, adds an attendance roster with a gaps-only filter, and steps the on-call safe-number schedule every weekday.
  • The on-call safe-number editor fails its optimistic lock closed without a version token and on-call views are unified through a single view resolver; registrar teaching writes are locked down so assessed assessments are no longer registrar-editable.
  • Adds <main> landmarks to the DSM and First Nations layouts without duplicating the shell's #main-content id, and replaces chat-import wording in therapy descriptions with regenerated catalogue data that resets unsupported review claims. The site-content owner manifest points at the regenerated hashed therapy assets so CI scope checks pass, and catalogue responses carry a private cache.

Written for commit d131594. Summary will update on new commits.

View guided diff Turn on auto-fix


CodeAnt-AI Description

Add clinical safety safeguards to calculations, medication displays, and answers

What Changed

  • Adds renal, QTc, and lithium calculations with checks that return no result for missing, invalid, or under-18 renal inputs; severe lithium results call for urgent assessment without treating the level alone as a dialysis instruction.
  • Distinguishes look-alike medicines with Tall Man lettering in prescribing results and medication details.
  • Protects clinical answers from instructions embedded in user queries and keeps prohibitions with their governing guidance when text is split into chunks.
  • Redacts supported Australian identifiers from stored queries when raw query storage is enabled.
  • Marks five edited therapy records as needing fresh review instead of carrying forward their previous sign-off.

Impact

✅ Fewer unsafe adult-equation estimates for children
✅ Clearer distinction between look-alike medicines
✅ Less risk of exposed query identifiers

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.


Summary by Gitar

  • Clinical safety validation:
    • Renal, QTc, and lithium calculators reject non-finite inputs and return no result for under-18 renal estimates
  • Medication safety:
    • Tall Man lettering distinguishes look-alike psychotropics in prescribing results and medication details
    • Later-added NICE references in therapy sources no longer expose their reference text
  • Proxy and indexing auth:
    • /api/proxy accepts only PROXY_AUTH and fails closed with 503 when unset
    • Indexing agent requires both a service secret and a valid JWT
    • Proxy claim signing uses dedicated PROXY_AUTH_SIGNING_KEY, never PROXY_AUTH
  • Clinical notes and on-call:
    • Adds note editor with debounced auto-save, newest save wins
    • Adds optimistic-locked safe-number editor for on-call cover
    • Registrar teaching writes locked down by new migration
    • Adds registrar assessment and milestone tables with row-level security
    • Adds attendance roster with a gaps-only filter
    • Parses assessment-specific queries in My Work search
  • Developer hub:
    • Adds corpus health, hazard register, ledger, settings, and answer-failure panels

This will update automatically on new commits.

@supabase

supabase Bot commented Oct 13, 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 ↗︎.

@greptile-apps greptile-apps 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.

BigSimmo has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@codeant-ai

codeant-ai Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Incremental review completed f2939a9 Oct 13, 2026 · 16:41 16:42
✅ Incremental review completed f53e428 Oct 13, 2026 · 15:36 15:38
✅ Incremental review completed 0577eb1 Oct 13, 2026 · 14:32 14:34
✅ Incremental review completed 527258c Oct 13, 2026 · 13:31 13:33
✅ Incremental review completed 7a0b3e8 Oct 13, 2026 · 12:18 12:20

@codeant-ai

codeant-ai Bot commented Oct 13, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@coderabbitai

coderabbitai Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Important

Review skipped

We couldn't safely recover the incremental review. No full review was started, and the last reviewed checkpoint was preserved. Retry later, or explicitly request a full review by commenting @coderabbitai full review.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Summary by CodeRabbit

  • New Features
    • Added clinical calculators for kidney function, QTc risk, and lithium levels and dose adjustments.
    • Medication names in prescribing results and records now use Tall Man lettering for selected look-alike medicines.
  • Improvements
    • Clinical prohibitions are better preserved when content is divided into chunks, and user queries receive safeguards before being used in generated answers.
    • Supported personal identifiers are redacted from stored queries, and page landmarks have been improved.
  • Bug Fixes
    • Improved cache handling, test isolation, and PDF cleanup, including when extraction encounters an error.

Walkthrough

The PR adds clinical calculators and medication notation, changes clinical-text chunking and RAG query handling, and updates query-storage privacy, cache behavior, test cleanup, page markup, PDF resource handling, therapy catalogue content, and audit documentation.

Changes

Clinical calculations and medication notation

Layer / File(s) Summary
Clinical-math contracts and calculations
docs/superpowers/plans/2026-10-12-phase-1-critical-hotfixes.md, src/lib/calculators/clinical-math/*, tests/clinical-math-calculators.test.ts
Adds renal, QTc, and lithium input/result types and calculations. The tests cover formula outputs, risk categories, dose adjustment, and invalid inputs.
Tall Man registry and medication displays
docs/superpowers/plans/2026-10-13-phase-2-hardening.md, src/lib/medications/tall-man.ts, src/components/clinical-dashboard/*, tests/tall-man-lettering.test.ts, docs/codebase-index.md
Adds Tall Man formatting and membership helpers. Medication search results and record titles use the formatter. Tests cover matching and fallback behavior.

RAG and query-data safeguards

Layer / File(s) Summary
Chunk boundaries and clinical prohibitions
docs/superpowers/plans/2026-10-13-phase-2-hardening.md, src/lib/chunking.ts, tests/chunking-negation.test.ts
Chunking preserves governing prohibition sentences, keeps indivisible sentences intact, and applies a 135% size budget.
Prompt query handling and answer fingerprinting
src/lib/rag/rag.ts, src/lib/rag/rag-answer-instructions.ts, src/lib/rag/rag-versioning.ts, src/lib/rag/rag-cache.ts, tests/rag-prompt-injection.test.ts, tests/rag-generation-fingerprint.test.ts
Sanitizes and delimits user queries in prompt input, marks delimited queries as untrusted, and includes a separate answer-input version in fingerprints.
Query-text identifier redaction
docs/superpowers/plans/2026-10-13-phase-3-refinement.md, src/lib/query-privacy.ts, tests/privacy.test.ts
Redacts supported identifiers from stored and normalized query text when raw retention applies, except for answer-request-context queries.

Cache, test, and PDF resource handling

Layer / File(s) Summary
Bounded caches and cache tests
docs/superpowers/plans/2026-10-12-phase-1-critical-hotfixes.md, src/lib/bounded-ttl-cache.ts, src/lib/openai.ts, src/lib/signed-url-cache.ts, tests/bounded-ttl-cache.test.ts
Adds bounded LRU helpers and uses them for query embeddings. Signed-URL writes prune near-expiry entries and continue eviction until the cache is within its limit.
Vitest mock and global cleanup
docs/superpowers/plans/2026-10-12-phase-1-critical-hotfixes.md, tests/setup/node.setup.ts, tests/setup-node.test.ts
Restores mocks and unstubs globals after each test. A test checks that both are reset between cases.
PDF document cleanup
docs/superpowers/plans/2026-10-13-phase-3-refinement.md, worker/python/extract_pdf_assets.py
PDF extraction closes the opened document in a finally block on success and failure.

Page landmarks and browser test waits

Layer / File(s) Summary
Page landmark and shell markup
docs/superpowers/plans/2026-10-13-phase-2-hardening.md, src/app/(search-app)/dsm/layout.tsx, src/app/(search-app)/first-nations/layout.tsx, src/components/dsm/dsm-diagnosis-page.tsx, tests/documents-search-landmark.test.ts
The DSM and First Nations layouts now wrap children in main elements. The DSM diagnosis page sets the information shell to render as a div.
Condition-based UI test waits
docs/superpowers/plans/2026-10-13-phase-2-hardening.md, tests/ui-chrome-scroll.spec.ts
Three browser tests replace fixed waits with polling assertions for visible page elements.

Therapy catalogue content and assets

Layer / File(s) Summary
Therapy source text and catalogue references
src/data/therapies-source.json, src/data/therapy-catalogue-assets.ts, src/lib/site-content/site-content-change-owners.json
Updates source, evidence, and reference text for six therapy records, along with their reviewed-content hashes and catalogue asset references.

Repository audit and remediation records

Layer / File(s) Summary
Audit findings and remediation roadmap
docs/audit/full-repository-audit-2026-10-11.md
Adds a dated report with findings, assessed strengths, and a three-phase remediation roadmap.
Audit scope, safeguards, and verification
docs/superpowers/plans/2026-10-11-parallel-repository-audit.md
Adds a read-only audit plan covering seven domains, evidence capture, finding synthesis, and verification.
Outstanding-issue cancellation records
docs/outstanding-issues-inbox/*.json
Adds two version 2 cancellation records with request IDs and resolution details.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: 🟡 Moderate · up to 4b4ec

Two new test suites currently fail against the implementation. The new chunking can separate "should not" warnings from the actions they govern. Space-separated MRNs can still be stored in query text. Resolve these before merging.

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

Failed Failed checks (1 warning, 2 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 27.03% which is insufficient. The required threshold is 70.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 37 functions across 33 files. (10 skipped… Write docstrings for the functions missing them to satisfy the coverage threshold.
Verification Claims Inconclusive The visible verification section names each test file and reports results, and it gives the TypeScript, formatting, and static-guard checks with outcomes. However, the supplied PR description is trunc… Provide the complete, untruncated PR description, including the remainder of its generated summary, so all verification claims can be checked against the required command/check and result wording.
Supabase Project And Schema Safety Inconclusive The review-scoped base and head refs are unavailable in this checkout, so I could not inspect the authoritative PR diff. The supplied change summary lists no Supabase project configuration, migration,… Provide the review base/head refs or the complete PR diff. Then verify all changed files for Supabase project refs, migrations, schema/data changes, RLS or policy changes, and production database behavior; check any applicable migration pat…
Passed Passed checks (8 passed)
Check name Status Explanation
Title check Passed The title clearly identifies the main change: repository audit remediation across three phases.
Description check Passed The description includes the required Summary, Verification, Risk and rollout, and Notes sections. It lists affected areas and RAG impact, reports checks, and states risk, rollback, and provider effec…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Generated And Sensitive Files Passed No explicit sensitive-file condition is introduced. The changed-file inventory contains no .env file, credential file, log, cache, dependency tree, build output, browser artifact, database dump, or …
Risky Git Or Deployment Actions Passed No changed instructions recommend force-push, hard reset, destructive clean, branch deletion, rebasing shared branches, production data mutation, or deployment without confirmation. The audit plan req…
Runtime And Package Manager Integrity Passed The PR preserves the established npm and runtime policy. The package.json and lockfile/configuration diff is empty. At both base and head, package.json specifies npm@11.17.0 and Node >=24.15.0 <25 wit…
Api Route Failure Handling Passed No changed code introduces an API failure-handling gap. The PR changes RAG query serialization and cache versioning, plus cache helpers; it does not change the answer API route handlers. The RAG path …
Full details: Docstring Coverage

Explanation

Docstring coverage is 27.03% which is insufficient. The required threshold is 70.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 37 functions across 33 files. (10 skipped: 10 unsupported.)

Full details: Verification Claims

Explanation

The visible verification section names each test file and reports results, and it gives the TypeScript, formatting, and static-guard checks with outcomes. However, the supplied PR description is truncated, and the remaining text may contain additional verification claims. The review-scoped base/head refs are also unavailable in this checkout, so I could not inspect the full changed text.

Full details: Supabase Project And Schema Safety

Explanation

The review-scoped base and head refs are unavailable in this checkout, so I could not inspect the authoritative PR diff. The supplied change summary lists no Supabase project configuration, migration, schema, or RLS files, but it cannot confirm the complete changed-file set or rule out database-impacting changes. I cannot verify whether this check’s failure conditions were introduced.

Resolution

Provide the review base/head refs or the complete PR diff. Then verify all changed files for Supabase project refs, migrations, schema/data changes, RLS or policy changes, and production database behavior; check any applicable migration path, rollback/safety plan, and verification.

✨ Finishing Touches
✨ Simplify code
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 13, 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-10-13T09:35:03.253806Z a4a8e6d 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.

@claude

claude Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Claude encountered an error after 2s —— View job


I'll analyze this and get back to you.

@qodo-code-review

qodo-code-review Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Harden clinical safety, RAG, privacy, and repository reliability

✨ Enhancement 🐞 Bug fix 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Add renal, QTc, and lithium utilities and distinguish confusable medication names in prescribing
 views.
• Preserve clinical negations during chunking and harden RAG prompts, caches, and stored-query
 privacy.
• Improve PDF cleanup, accessibility, test reliability, and therapy wording; document audit
 findings.
Diagram

graph TD
  Chunker["Clinical chunker"] --> Rag["RAG answers"] --> Cache["Answer cache"]
  Query["User query"] --> Rag
  Query --> Privacy["Query privacy"]
  TallMan["Tall Man registry"] --> Views["Medication views"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Hash all persisted queries
  • ➕ Avoids relying on best-effort identifier patterns for stored query text.
  • ➖ Removes the explicitly enabled raw-query retention capability and its diagnostic value.
2. Integrate calculators into prescribing now
  • ➕ Makes the new calculations available at the point of medication review.
  • ➖ Expands clinical UX, validation, and governance scope beyond these standalone utilities.

Recommendation: The incremental approach limits disruption to existing retrieval and prescribing workflows. Retain hashed queries as the default and treat raw-mode scrubbing as best-effort, not de-identification. Clinically validate calculations and their intended UX before presenting them as prescribing guidance.

Files changed (42) +2084 / -79

Enhancement (11) +562 / -5
layout.tsxAdd DSM main landmark +5/-1

Add DSM main landmark

• Wraps route content in a focusable, identified main landmark for navigation and accessibility.

src/app/(search-app)/dsm/layout.tsx

layout.tsxAdd First Nations main landmark +3/-1

Add First Nations main landmark

• Wraps child content in a focusable main landmark within the existing mode band.

src/app/(search-app)/first-nations/layout.tsx

medication-prescribing-workspace.tsxShow Tall Man names in medication results +3/-2

Show Tall Man names in medication results

• Formats medication names in two search-result presentations without changing the underlying result names or query.

src/components/clinical-dashboard/medication-prescribing-workspace.tsx

medication-record-page.tsxShow Tall Man name on medication detail +2/-1

Show Tall Man name on medication detail

• Applies the lettering formatter to the medication record heading.

src/components/clinical-dashboard/medication-record-page.tsx

bounded-ttl-cache.tsAdd bounded non-expiring LRU helpers +21/-0

Add bounded non-expiring LRU helpers

• Introduces helpers that refresh entry order on read and evict the oldest entry when writes exceed capacity.

src/lib/bounded-ttl-cache.ts

index.tsExport clinical math utilities +4/-0

Export clinical math utilities

• Provides one import surface for renal, QTc, lithium, and shared clinical math types.

src/lib/calculators/clinical-math/index.ts

lithium.tsAdd lithium level and dose helpers +131/-0

Add lithium level and dose helpers

• Evaluates serum levels against indication- and age-dependent targets with toxicity guidance. Adds a proportional dose calculation that rejects invalid or extreme adjustments.

src/lib/calculators/clinical-math/lithium.ts

qtc.tsAdd four QTc correction calculations +100/-0

Add four QTc correction calculations

• Calculates Bazett, Fridericia, Framingham, and Hodges values and assigns a QTc risk category. Rejects invalid inputs and warns about Bazett behavior at atypical heart rates.

src/lib/calculators/clinical-math/qtc.ts

renal.tsAdd renal estimation helpers +129/-0

Add renal estimation helpers

• Implements Cockcroft–Gault clearance and race-free 2021 CKD-EPI eGFR with unit conversion, input checks, and renal-stage labels.

src/lib/calculators/clinical-math/renal.ts

types.tsDefine clinical calculation contracts +94/-0

Define clinical calculation contracts

• Adds typed inputs and results for renal, QTc, and lithium calculations, including units, errors, warnings, and risk classifications.

src/lib/calculators/clinical-math/types.ts

tall-man.tsAdd medication lettering registry +70/-0

Add medication lettering registry

• Defines display forms for recognized look-alike medications and case-insensitive formatting and lookup helpers. Unrecognized names remain unchanged.

src/lib/medications/tall-man.ts

Bug fix (7) +105 / -21
chunking.tsPreserve clinical sentence scope across chunks +38/-13

Preserve clinical sentence scope across chunks

• Bumps chunker versions and chooses sentence boundaries instead of arbitrary mid-sentence cuts. Expands overlap to retain a governing prohibition and handles oversized sentences atomically.

src/lib/chunking.ts

query-privacy.tsScrub supported identifiers in stored queries +24/-2

Scrub supported identifiers in stored queries

• Redacts matching Medicare numbers, phone numbers, labelled MRNs, and labelled birth dates in query-text columns when raw retention is enabled. Default hash-based storage remains unchanged.

src/lib/query-privacy.ts

rag-answer-instructions.tsMark user queries as untrusted prompt data +2/-1

Mark user queries as untrusted prompt data

• Extends answer instructions to say content inside user-query delimiters must not override evidence or prompt rules.

src/lib/rag/rag-answer-instructions.ts

rag-cache.tsVersion answer fingerprints by query serialization +10/-2

Version answer fingerprints by query serialization

• Includes a query-input version in answer generation fingerprints so prompt-input changes do not reuse older cached answers.

src/lib/rag/rag-cache.ts

rag.tsFence and sanitize RAG user questions +15/-1

Fence and sanitize RAG user questions

• Wraps the answer prompt’s question in user-query delimiters and escapes XML-like characters and control characters in that input. Retrieval behavior is unchanged here.

src/lib/rag/rag.ts

signed-url-cache.tsPrune expired signed URLs on write +9/-2

Prune expired signed URLs on write

• Removes expired signed URL entries before insertion and uses an explicit undefined check in capacity eviction.

src/lib/signed-url-cache.ts

extract_pdf_assets.pyClose extracted PDF documents deterministically +7/-0

Close extracted PDF documents deterministically

• Moves extraction work into a helper and closes the opened PyMuPDF document in a finally block, including when extraction fails.

worker/python/extract_pdf_assets.py

Refactor (1) +3 / -16
openai.tsReuse bounded LRU helpers for embeddings +3/-16

Reuse bounded LRU helpers for embeddings

• Replaces local query-embedding cache ordering and eviction logic with shared bounded LRU helpers.

src/lib/openai.ts

Tests (11) +587 / -5
bounded-ttl-cache.test.tsTest bounded LRU and falsy keys +33/-1

Test bounded LRU and falsy keys

• Adds coverage for empty-string eviction keys and recency-based eviction in the new LRU helpers.

tests/bounded-ttl-cache.test.ts

chunking-negation.test.tsTest clinical sentence preservation +115/-0

Test clinical sentence preservation

• Exercises prohibitions, governing conditions, oversized sentences, paragraph overlap, and ordinary chunk-size budgets.

tests/chunking-negation.test.ts

clinical-math-calculators.test.tsTest renal, QTc, and lithium calculations +247/-0

Test renal, QTc, and lithium calculations

• Adds representative formula vectors, risk classifications, invalid-input checks, and lithium target and dose-adjustment cases.

tests/clinical-math-calculators.test.ts

documents-search-landmark.test.tsCheck DSM and First Nations landmarks +16/-1

Check DSM and First Nations landmarks

• Extends source-level landmark assertions to the two changed layouts.

tests/documents-search-landmark.test.ts

privacy.test.tsTest stored-query identifier scrubbing +36/-0

Test stored-query identifier scrubbing

• Covers Medicare, phone, MRN, and birth-date patterns in raw and normalized stored queries, plus an ordinary-text non-match.

tests/privacy.test.ts

rag-generation-fingerprint.test.tsTest answer-input cache invalidation +7/-0

Test answer-input cache invalidation

• Checks that changing the input version changes the fingerprint and that new fingerprints differ from the pre-versioned form.

tests/rag-generation-fingerprint.test.ts

rag-prompt-injection.test.tsExercise query sanitization cases +33/-0

Exercise query sanitization cases

• Adds cases for XML delimiter attempts, source-fence-like text, and control characters while retaining clinical Unicode.

tests/rag-prompt-injection.test.ts

setup-node.test.tsTest opt-in Node mock cleanup +22/-0

Test opt-in Node mock cleanup

• Imports the setup helper and verifies a stubbed global and spy are restored between tests in that suite.

tests/setup-node.test.ts

node.setup.tsProvide opt-in Node test cleanup +8/-0

Provide opt-in Node test cleanup

• Restores spies and global stubs after each test when imported. This diff does not register it globally in Vitest configuration.

tests/setup/node.setup.ts

tall-man-lettering.test.tsSpecify medication lettering expectations +43/-0

Specify medication lettering expectations

• Adds mapping, case-insensitive lookup, and fallback tests. Several expected capitalizations differ from registry values shown in this diff, so passing status should be verified.

tests/tall-man-lettering.test.ts

ui-chrome-scroll.spec.tsPoll for settled search chrome +27/-3

Poll for settled search chrome

• Replaces three fixed waits with assertions that poll for the expected header or search-composer state.

tests/ui-chrome-scroll.spec.ts

Documentation (9) +822 / -29
full-repository-audit-2026-10-11.mdRecord repository-wide audit findings +215/-0

Record repository-wide audit findings

• Adds a severity matrix, domain analyses, and prioritized remediation roadmap. The report also records issues outside this PR’s implemented scope.

docs/audit/full-repository-audit-2026-10-11.md

codebase-index.mdIndex Tall Man medication module +1/-0

Index Tall Man medication module

• Adds the medication lettering registry to the codebase index.

docs/codebase-index.md

0d5cdc6b-7d56-4e37-988b-ead1816d1d21.jsonRecord landmark issue cancellation +10/-0

Record landmark issue cancellation

• Adds an inbox cancellation event for the resolved main-landmark issue; its reason text is truncated in this diff.

docs/outstanding-issues-inbox/0d5cdc6b-7d56-4e37-988b-ead1816d1d21.json

19dc1828-67ee-4a7f-ac44-ec01a32375dd.jsonRecord therapy wording issue cancellation +10/-0

Record therapy wording issue cancellation

• Adds an inbox cancellation event documenting resolution of chat-import wording in therapy records.

docs/outstanding-issues-inbox/19dc1828-67ee-4a7f-ac44-ec01a32375dd.json

2026-10-11-parallel-repository-audit.mdPlan parallel forensic audit +274/-0

Plan parallel forensic audit

• Documents seven audit domains, evidence requirements, synthesis steps, and read-only verification procedures.

docs/superpowers/plans/2026-10-11-parallel-repository-audit.md

2026-10-12-phase-1-critical-hotfixes.mdPlan critical cache, test, and calculator work +125/-0

Plan critical cache, test, and calculator work

• Specifies proposed Phase 1 tasks and verification for memory bounds, Node mock cleanup, and clinical math. It does not establish that every listed step was implemented.

docs/superpowers/plans/2026-10-12-phase-1-critical-hotfixes.md

2026-10-13-phase-2-hardening.mdPlan clinical and RAG hardening +122/-0

Plan clinical and RAG hardening

• Documents proposed Tall Man, chunking, prompt, hydration, and Playwright work. Some proposed work is not present in this diff.

docs/superpowers/plans/2026-10-13-phase-2-hardening.md

2026-10-13-phase-3-refinement.mdPlan resource and privacy refinements +36/-0

Plan resource and privacy refinements

• Describes PDF handle cleanup, stored-query identifier scrubbing, and bundle-budget verification.

docs/superpowers/plans/2026-10-13-phase-3-refinement.md

therapies-source.jsonRemove chat-import language from therapy records +29/-29

Remove chat-import language from therapy records

• Rephrases source and evidence notes in several therapy records to remove references to an uploaded guide or drafting conversation. Updates associated reviewed-content hashes.

src/data/therapies-source.json

Other (3) +5 / -3
therapy-catalogue-assets.tsPoint catalogue to regenerated therapy asset +2/-2

Point catalogue to regenerated therapy asset

• Advances the versioned full-catalogue filename and retains the prior version as a fallback reference.

src/data/therapy-catalogue-assets.ts

rag-versioning.tsDefine RAG answer-input version +2/-0

Define RAG answer-input version

• Adds a dedicated version constant for changes to user-query serialization.

src/lib/rag/rag-versioning.ts

site-content-change-owners.jsonTrack ownership of regenerated therapy asset +1/-1

Track ownership of regenerated therapy asset

• Replaces the obsolete therapy asset path with the newly versioned file in the content ownership list.

src/lib/site-content/site-content-change-owners.json

@codeant-ai

codeant-ai Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

🏁 CodeAnt Quality Gate Results

Commit: d1315941
Scan Time: 2026-10-14 03:25:36 UTC

❌ Overall Status: FAILED

Quality Gate Details

Quality Gate Status Details
Secrets ❌ FAILED 1 secret found, 33 false positive secrets suppressed
Duplicate Code ✅ PASSED 14.7% duplicated
SAST ✅ PASSED No security issues
Bugs ✅ PASSED Rating S: No bugs
IAC ✅ PASSED Rating S: No issues

View Full Results

Fix in Cursor Fix in VSCode Claude

View Failure Result
🔐 Secrets — 1 findings
File Line Type Confidence
tests/proxy-auth.test.ts 271 Bearer Token MEDIUM

@codspeed

codspeed Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Merging this PR will improve performance by ×3

⚡ 1 improved benchmark
✅ 7 untouched benchmarks

Performance Changes

Benchmark BASE HEAD Efficiency
⚡ catalog-search: rankCatalogRecords 400 records 154.8 ms 51.8 ms ×3

Tip

Curious why performance improved? Comment @codspeedbot explain why performance improved on this PR, or directly use the CodSpeed MCP with your agent.


Comparing feat/audit-remediation-full-hardening (d131594) with main (6f3712c)

Open in CodSpeed

Comment thread src/lib/chunking.ts Resolved
Comment thread src/lib/chunking.ts Resolved
@codeant-ai codeant-ai Bot added the size:XXL This PR changes 1000+ lines, ignoring generated files label Oct 13, 2026
@qodo-code-review

qodo-code-review Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (5) 📘 Rule violations (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (1)

⚠️ 25 lower-priority findings omitted to fit the comment size limit; re-run the review or view the findings in the Qodo portal.

Grey Divider


Action required

1. Partial eval totals omit unseen cases 🔴 High 📜 Skill insight ≡ Correctness ⭐ New
Description
extractDomainCases synthesizes entries only for supplied pass and fail counts, even when their sum
is less than case_count. For a count-only summary with five cases and only failed_count: 1, the
release report shows 0/1 rather than accounting for the four cases whose outcomes were not supplied.
Code

scripts/eval-reporter.ts[R92-98]

+    const failedCount = Math.max(0, summary.failed_count ?? summary.case_count - (summary.passed_count ?? 0));
+    const passedCount = Math.max(0, Math.min(summary.passed_count ?? 0, summary.case_count - failedCount));
+    for (let index = 0; index < passedCount; index += 1) {
+      results.push({ id: `case-${index + 1}`, status: "PASS" });
+    }
+    for (let index = 0; index < failedCount; index += 1) {
+      results.push({ id: `case-${passedCount + index + 1}`, status: "FAIL" });
Relevance

●●● Strong

Partial count handling violates report completeness; prior eval reporting work accepted fixes
preventing incomplete results appearing clean.

PR-#2863
PR-#2764

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The data skill requires count invariants and tests for missing or partial inputs. The new fallback
loops only over passedCount and failedCount, while its new test covers only a summary in which
both counts sum exactly to case_count.

scripts/eval-reporter.ts[88-100]
tests/eval-reporter.test.ts[13-19]
Skill: data

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Count-only eval summaries can specify `case_count` while omitting one outcome count, causing the report total to exclude cases.

## Fix Focus Areas
- scripts/eval-reporter.ts[92-98]
- tests/eval-reporter.test.ts[13-19]

## Recommended Fix
Ensure the synthesized entries always account for `case_count`. Do not infer passes from an omitted pass count; conservatively classify unaccounted cases as failures or unknown outcomes, and add tests for summaries with either outcome count missing and with counts that do not add up.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Dismiss ↗ | View ↗



Remediation recommended

2. Passing eval cases appear as failures 🟠 Medium 🐞 Bug ≡ Correctness ⭐ New
Description
extractDomainCases treats an absent failed_count as case_count and an absent passed_count as
zero when it synthesizes results. The answer and retrieval eval producers emit case_count and a
failure-only failed_cases array without either count, so a run with no failed cases is rendered as
entirely failed.
Code

scripts/eval-reporter.ts[R92-93]

+    const failedCount = Math.max(0, summary.failed_count ?? summary.case_count - (summary.passed_count ?? 0));
+    const passedCount = Math.max(0, Math.min(summary.passed_count ?? 0, summary.case_count - failedCount));
Relevance

●●● Strong

Clear false-green eval bug; count-only summary fixes are explicitly part of the PR’s stated commit
intent.

PR-#2863
PR-#2602

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Both producers return case_count and failed_cases but no aggregate pass/fail counts. The
existing canary summary interprets an empty failure list as every case passing; the new fallback
instead generates one FAIL result per case.

scripts/eval-quality.ts[712-737]
scripts/eval-retrieval.ts[780-824]
scripts/eval-canary-case-summary.mjs[29-34]
scripts/eval-reporter.ts[88-98]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Generated eval summaries contain a total and a failure-only list, but omit passed and failed counts. The count-only fallback currently marks every case as failed when that list is empty.
## Fix Focus Areas
- scripts/eval-reporter.ts[88-98]
- tests/eval-reporter.test.ts[13-19]
## Recommended Fix
Derive the missing failed count from `failed_cases.length` and the missing passed count from `case_count` minus failures, while retaining explicit counts when present. Add a test using the generated report shape with `case_count` and an empty `failed_cases` array.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Dismiss ↗ | View ↗


3. Schema snapshot misses most new migrations 🟠 Medium 🐞 Bug ⚙ Maintainability
Description
supabase/schema.sql gains only the ENABLE ALWAYS trigger line. It lacks the six FORCE RLS
statements, the api_rate_limit_subjects retention function, and the new
registrar_assessments/registrar_training_milestones tables with their policies. Any environment
built or diffed from the snapshot will be missing these objects.
Code

supabase/migrations/20261013160000_teaching_assessments_and_milestones.sql[R11-13]

+-- 1. Create table public.registrar_assessments
+create table public.registrar_assessments (
+  id uuid primary key default gen_random_uuid(),
Relevance

●●● Strong

Recent schema snapshot and drift-manifest omissions were explicitly accepted as deployment and
environment consistency defects.

PR-#3332
PR-#2816

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The only schema.sql change is the trigger line near 12817, and the snapshot has no definitions for
the new tables or the retention function.

supabase/migrations/20261013140000_force_rls_control_plane.sql[22-27]
supabase/migrations/20261013150000_api_rate_limit_subjects_retention.sql[1-44]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
schema.sql is not in sync with three of the four new migrations.

## Fix Focus Areas
- supabase/schema.sql[12810-12820]
- supabase/migrations/20261013140000_force_rls_control_plane.sql[22-27]
- supabase/migrations/20261013150000_api_rate_limit_subjects_retention.sql[1-44]
- supabase/migrations/20261013160000_teaching_assessments_and_milestones.sql[11-120]

## Recommended Fix
Regenerate or hand-add the FORCE RLS statements, the retention function and grants, and both teaching tables (indexes, triggers, grants, policies) to supabase/schema.sql.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Dismiss ↗ | View ↗



Informational

4. Dev server command breaks on Windows shells 🟡 Low 🐞 Bug ☼ Reliability
Description
The dev script uses POSIX-only ${NODE_OPTIONS:...} expansion and an inline environment
assignment that Windows npm script shells cannot parse. On Windows, npm run dev fails before Node
starts; when NODE_OPTIONS is already set on other platforms, the script also omits the heap flag
despite the launcher supplying a memory default.
Code

package.json[12]

+    "dev": "NODE_OPTIONS=\"${NODE_OPTIONS:---max-old-space-size=4096}\" node --max-old-space-size=4096 scripts/dev-free-port.mjs",
Relevance

●●● Strong

Windows shell incompatibility is a deterministic reliability bug in a cross-platform npm command.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The changed package.json command contains POSIX-only shell expansion and an inline environment
assignment, unlike the other npm scripts; the repository also explicitly supports win32 elsewhere,
including in the Playwright preflight. The invoked launcher independently constructs and passes
NODE_OPTIONS, so the shell syntax is unnecessary for setting the child process’s memory default.

package.json[11-15]
scripts/playwright-browser-preflight.mjs[217-227]
package.json[12-12]
scripts/dev-free-port.mjs[166-185]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The `dev` script uses POSIX shell syntax that Windows npm script shells cannot execute, and an existing `NODE_OPTIONS` value bypasses its heap default.

## Fix Focus Areas
- package.json[12-12]
- scripts/dev-free-port.mjs[160-190]

## Recommended Fix
Restore the shell-independent command `node --max-old-space-size=4096 scripts/dev-free-port.mjs` in `package.json`. In `dev-free-port.mjs`, append `--max-old-space-size=4096` to the child process’s `env.NODE_OPTIONS` when that flag is not already present, preserving any existing options.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Dismiss ↗ | View ↗


5. New admin hub links back to legacy pages 🟡 Low 🐞 Bug ⚙ Maintainability
Description
The new /admin/developer-hub pages copy the existing /mockups/development/* pages, but the hub's
panel links (HUB_PANELS) and the new pages' alert and back links still point at
/mockups/development/.... Clicking through from the new hub lands on the legacy copy, and there
are now two sets of pages to keep in sync.
Code

src/app/(admin)/admin/developer-hub/clinical-trust/page.tsx[R15-20]

+      <Link
+        href="/mockups/development"
+        className="inline-flex min-h-12 w-fit items-center gap-2 text-sm font-bold text-[color:var(--text-muted)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)]"
+      >
+        <ArrowLeft aria-hidden="true" className="size-icon-sm" />
+        Developer hub
Relevance

●●● Strong

Recent route-migration precedents accept replacing legacy links with canonical shared destinations.

PR-#3454
PR-#2310

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The panel route definitions target the legacy subtree, the new pages link back to it, and the legacy
routes still exist.

src/lib/developer-area/hub-panels.ts[24-27]
src/app/(admin)/admin/developer-hub/page.tsx[142-150]
src/app/mockups/development/page.tsx[1-16]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new admin developer hub duplicates /mockups/development but still links to the legacy routes.

## Fix Focus Areas
- src/lib/developer-area/hub-panels.ts[20-150]
- src/app/(admin)/admin/developer-hub/page.tsx[142-150]
- src/app/(admin)/admin/developer-hub/clinical-trust/page.tsx[15-20]

## Recommended Fix
Pick one canonical subtree. Either point HUB_PANELS and the in-page links at /admin/developer-hub and redirect the legacy routes, or drop the duplicate admin pages.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Dismiss ↗ | View ↗


6. Very high lithium levels miss a dialysis prompt 🟡 Low 🐞 Bug ≡ Correctness
Description
evaluateLithiumLevel gives every result above 2.0 mmol/L the same guidance that haemodialysis
depends on clinical criteria, “not this level alone.” At concentrations above 5.0 mmol/L, this omits
the level-only threshold at which EXTRIP suggests extracorporeal treatment, so a patient at 5.1
receives no indication that their concentration itself warrants that discussion.
Code

src/lib/calculators/clinical-math/lithium.ts[R51-52]

+      "URGENT: Serum lithium > 2.0 mmol/L. Stop lithium and arrange urgent medical assessment. Discuss with a toxicologist or Poisons Information Centre (13 11 26).",
+      "Whether to use haemodialysis depends on clinical criteria, not this level alone: severity and clinical features (reduced consciousness, seizures, dysrhythmias, confusion), renal function, the trajectory of repeat levels, and acute versus chronic exposure (EXTRIP).",
Relevance

● Weak

Owner explicitly chose clinical-criteria dialysis guidance, rejecting a concentration-only prompt
despite EXTRIP’s >5.0 threshold.

PR-#3157

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new guidance covers all levels above 2.0 mmol/L and says the level alone is insufficient. EXTRIP
explicitly suggests extracorporeal treatment when lithium concentration exceeds 5.0 mEq/L; lithium's
mmol/L and mEq/L concentrations are numerically equal.

src/lib/calculators/clinical-math/lithium.ts[45-53]
tests/clinical-math-calculators.test.ts[329-342]
🌐 EXTRIP suggests extracorporeal treatment if lithium concentration is above 5.0 mEq/L.

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The lithium guidance treats all levels above 2.0 mmol/L alike, although EXTRIP suggests extracorporeal treatment for a level above 5.0 mmol/L even without other criteria.
## Fix Focus Areas
- src/lib/calculators/clinical-math/lithium.ts[45-53]
- tests/clinical-math-calculators.test.ts[329-343]
## Recommended Fix
Add distinct guidance for levels above 5.0 mmol/L stating that EXTRIP suggests discussing extracorporeal treatment based on the concentration itself. Keep the clinical-assessment guidance for lower levels and test both thresholds.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Dismiss ↗ | View ↗


Grey Divider


Resolved findings

1. Registrars can sign off themselves ✗ Dismissed
Description
The registrar_assessments insert and update policies check only registrar_id = auth.uid(), while
table-wide grants let registrars write status, assessor_id, ratings, and feedback; milestone
policies also let them write achieved_at and evidence. A signed-in registrar can create or edit
their own assessment as signed_off without assessor approval and mark their own milestones
achieved.
Code

supabase/migrations/20261013160000_teaching_assessments_and_milestones.sql[R93-97]

+create policy "registrar_assessments_registrar_update"
+  on public.registrar_assessments
+  for update to authenticated
+  using (registrar_id = auth.uid())
+  with check (registrar_id = auth.uid());
Relevance

●●● Strong

Recent teaching workflow findings were accepted when users could alter assessment state without the
required reviewer-controlled transition.

PR-#3387
PR-#3206

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The authenticated role has table-wide insert and update permission, and the assessment policies
constrain ownership through registrar_id without restricting assessor-controlled columns or status
changes. The status constraint permits signed_off, while the milestone policies allow registrars
to write achievement fields on their own records.

supabase/migrations/20261013160000_teaching_assessments_and_milestones.sql[14-22]
supabase/migrations/20261013160000_teaching_assessments_and_milestones.sql[67-97]
supabase/migrations/20261013160000_teaching_assessments_and_milestones.sql[12-23]
supabase/migrations/20261013160000_teaching_assessments_and_milestones.sql[68-120]
Skill: access

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Registrars can insert or update their own assessments with `signed_off` status, a chosen `assessor_id`, and self-written feedback or ratings, and can mark their own milestones achieved.

## Fix Focus Areas
- supabase/migrations/20261013160000_teaching_assessments_and_milestones.sql[67-120]

## Recommended Fix
Restrict registrar writes to draft fields, such as title, category, and draft content, using column-level privileges or controlled functions. Add `WITH CHECK (status IN ('draft', 'submitted'))` to registrar insert and update policies. Move sign-off, assessor feedback and ratings, and milestone `achieved_at` to assessor- or supervisor-authorized writes, either through appropriate policies or a security-definer RPC that checks the caller's role. Add an offline access test proving a registrar cannot set `signed_off` or assessor-controlled fields.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Existing answer threads are deleted ✗ Dismissed
Description
normalizePersistedAnswerThread now rejects every stored version except 3, and
loadPersistedAnswerThread removes rejected records. When an existing user returns with a valid
version 1 or 2 session snapshot, bootstrapping cannot restore the prior answers and deletes the
snapshot instead.
Code

src/lib/answer-thread-storage.ts[R147-148]

+export function normalizePersistedAnswerThread(value: Record<string, unknown>): PersistedAnswerThread | null {
+  if (value.version !== 3) return null;
Relevance

●●● Strong

Storage migration and rejected-session cleanup findings were accepted when persisted state could be
lost or incorrectly retained.

PR-#3297
PR-#3460

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The normalizer rejects older versions, the loader deletes failed records, and the bootstrap caller
has no recovery path.

src/lib/answer-thread-storage.ts[147-148]
src/lib/answer-thread-storage.ts[193-200]
src/components/clinical-dashboard/use-answer-thread-bootstrap.ts[120-153]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Existing version 1 and 2 answer threads are rejected and deleted on load.
## Fix Focus Areas
- src/lib/answer-thread-storage.ts[147-148]
- src/lib/answer-thread-storage.ts[193-209]
## Recommended Fix
Restore version-aware loading and migrate valid older turns to version 3. Conservatively populate any newly required support flag, then save the migrated snapshot before returning it.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Migrations stop at duplicate version ✗ Dismissed
Description
20261013140000_force_rls_control_plane.sql and
20261013140000_enable_always_release_records_immutability.sql share a migration version and both
contain the same ENABLE ALWAYS trigger statement. When the migrations run, Supabase can record
only one file under that version, while the repository’s PR policy flags the duplicate, so
deployment can fail or leave part of the hardening unapplied.
Code

supabase/migrations/20261013140000_force_rls_control_plane.sql[1]

+-- Forward migration to apply FORCE ROW LEVEL SECURITY on six control-plane tables (#AD3F5B)
Relevance

●●● Strong

Recent migration findings were accepted when migration-chain and deployment metadata could diverge
or leave hardening unapplied.

PR-#2826
PR-#2816

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The two migration filenames share the 20261013140000 prefix, and the applied-migration-hashes
manifest records both files. Supabase tracks applied migrations by version, and
scripts/pr-policy.mjs treats multiple paths for one version as a duplicate-version violation;
the migration files also contain the same ENABLE ALWAYS trigger statement.

supabase/migrations/20261013140000_force_rls_control_plane.sql[1-2]
supabase/migrations/20261013140000_enable_always_release_records_immutability.sql[1-4]
supabase/applied-migration-hashes.json[306-310]
scripts/pr-policy.mjs[730-757]
supabase/migrations/20261013140000_force_rls_control_plane.sql[22-30]
Skill: migrate

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Two new SQL migrations share version `20261013140000`, which can interrupt the migration sequence or leave part of the intended hardening unapplied. Both files also contain the same `ENABLE ALWAYS` trigger statement.

## Fix Focus Areas
- supabase/migrations/20261013140000_force_rls_control_plane.sql[1-30]
- supabase/migrations/20261013140000_enable_always_release_records_immutability.sql[1-4]
- supabase/applied-migration-hashes.json[306-310]

## Recommended Fix
Delete the standalone enable-always migration if its statement is fully covered by the force-RLS migration, or give one file a distinct, later migration timestamp. Regenerate the affected applied-migration-hashes entries and verify the sequence with the repository’s offline migration checks.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View resolved (6)
4. Registrars can forge milestones ✗ Dismissed
Description
The registrar_training_milestones_registrar_insert and update policies let a registrar write their
own milestone rows without restricting achieved_at or evidence_summary. Any authenticated
registrar can insert an achieved milestone or change a pending one to achieved, bypassing
independent verification of training progress.
Code

supabase/migrations/20261013160000_teaching_assessments_and_milestones.sql[R110-114]

+
+create policy "registrar_training_milestones_registrar_insert"
+  on public.registrar_training_milestones
+  for insert to authenticated
+  with check (registrar_id = auth.uid());
Relevance

●●● Strong

Recent security-policy findings restricting unauthorized state changes were accepted, especially
authenticated mutation controls.

PR-#3485
PR-#2960

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The table stores achievement directly in achieved_at; authenticated users have insert and update
grants, with policies that check only the matching registrar ID.

supabase/migrations/20261013160000_teaching_assessments_and_milestones.sql[25-35]
supabase/migrations/20261013160000_teaching_assessments_and_milestones.sql[106-120]
Skill: access

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Registrar-scoped insert and update policies allow self-certification of training milestones.
## Fix Focus Areas
- supabase/migrations/20261013160000_teaching_assessments_and_milestones.sql[25-35]
- supabase/migrations/20261013160000_teaching_assessments_and_milestones.sql[106-120]
## Recommended Fix
Keep registrar read access, but restrict creation and achievement changes to a verified approver or controlled server-side path. Add an offline access test showing a registrar cannot insert or update a non-null `achieved_at`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Live catalogue data is cached publicly for a day ✗ Dismissed
Description
The medications and differentials routes pass fixture: !canonical.degraded, treating healthy
canonical database results as uninitialized seed responses and assigning them `public, max-age=300,
s-maxage=3600, stale-while-revalidate=86400`. For anonymous requests, visitors and CDNs can
therefore serve withdrawn or corrected catalogue records for hours after publication.
Code

src/app/api/medications/route.ts[R277-279]

+      // `fixture` lengthens public caching for canonical public responses, but is withheld when
+      // `canonical.degraded` is true to avoid pinning a temporary seed fallback.
+      { request, fixture: !canonical.degraded },
Relevance

●●● Strong

Canonical catalogue responses should not receive fixture-style public caching that can serve stale
live data.

PR-#3454

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
fixtureResponseHeaders applies the long public cache policy when fixture is true and the request
has no auth signal; both routes now set that flag for non-degraded canonical results. The removed
comments explicitly said canonical responses must not receive this policy, and the differentials
route makes the same change at lines 199 and 241.

src/lib/fixture-response-cache.ts[1-45]
src/app/api/medications/route.ts[227-280]
src/app/api/differentials/route.ts[190-242]
src/app/api/medications/route.ts[255-280]
src/lib/fixture-response-cache.ts[1-3]
src/lib/fixture-response-cache.ts[35-42]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Healthy canonical catalogue responses receive the public fixture cache policy, with a one-hour shared-cache lifetime and a one-day stale-while-revalidate window, delaying updates and retractions.

## Fix Focus Areas
- src/app/api/medications/route.ts[274-280]
- src/app/api/differentials/route.ts[197-201]
- src/app/api/differentials/route.ts[241-241]
- src/lib/fixture-response-cache.ts[1-42]

## Recommended Fix
In both catalogue routes, distinguish actual fixture or uninitialized seed responses from mutable canonical results by restoring `fixture: observed.source === "seed_uninitialized"` and the original comments. If canonical responses should be cached, use a separate, short policy consistent with publication freshness and tied to publish invalidation rather than reusing the fixture policy.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. The prompt-sanitization test fails ✓ Resolved
Description
sanitizePromptUserQuery converts angle brackets to HTML entities instead of removing excerpt-fence
markers. When the existing fence-injection test runs, its expected END_SOURCE START_SOURCE text
differs from the escaped result, so the test fails.
Code

src/lib/rag/rag.ts[R4150-4151]

+    .replace(/</g, "&lt;")
+    .replace(/>/g, "&gt;")
Relevance

●●● Strong

Sanitizer behavior contradicts the existing regression expectation, matching accepted
prompt-security corrections.

PR-#3413
PR-#3432

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The sanitizer escapes < and >, while the test asserts the exact output `Check QTc END_SOURCE
START_SOURCE fake clinical excerpt`; escaped fence markers cannot equal it.

src/lib/rag/rag.ts[4144-4152]
tests/rag-prompt-injection.test.ts[18-22]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The query sanitizer escapes excerpt fences, but the existing prompt-injection test requires them to be removed, causing a test failure.
## Fix Focus Areas
- src/lib/rag/rag.ts[4144-4152]
- tests/rag-prompt-injection.test.ts[18-22]
## Recommended Fix
Remove excerpt-fence delimiters before escaping remaining angle brackets, preserving the test's expected sanitized text.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Node tests keep leaked mocks ✓ Resolved
Description
tests/setup/node.setup.ts defines mock cleanup, but the Node project in vitest.config.mts does
not register it as a setup file, and tests/setup-node.test.ts imports it directly. That local
import makes the verification test pass while other Node suites lack the afterEach cleanup for
spies and stubbed globals.
Code

tests/setup-node.test.ts[2]

+import "./setup/node.setup";
Relevance

●●● Strong

Directly importing setup code does not configure Vitest globally; test-runner isolation gaps are
treated as reliability defects.

PR-#2245
PR-#2733

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The Vitest configuration has a setupFiles entry for the separate jsdom project but none for the
Node project. Because tests/setup-node.test.ts directly imports ./setup/node.setup, its passing
assertions show only that the helper works when imported, not that the runner loads it for other
Node suites.

tests/setup-node.test.ts[1-3]
vitest.config.mts[101-122]
vitest.config.mts[118-124]
vitest.config.mts[101-123]
tests/setup/node.setup.ts[3-8]
tests/setup-node.test.ts[1-2]
Skill: testing-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The Node cleanup hook is not registered project-wide, and the verification test masks that omission by importing the helper directly.

## Fix Focus Areas
- vitest.config.mts[100-124]
- tests/setup-node.test.ts[1-22]
- tests/setup/node.setup.ts[1-8]

## Recommended Fix
Add `setupFiles: ["tests/setup/node.setup.ts"]` to the Node project in `vitest.config.mts`. Remove the direct `import "./setup/node.setup"` from `tests/setup-node.test.ts` so the test verifies runner-level loading. Run the full Node suite and check fixture lifetimes, particularly spies created in `beforeAll` or at module scope that the new `afterEach` cleanup would restore after the first test.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. Invalid lithium results seem therapeutic ✓ Resolved
Description
calculateQtc, calculateCockcroftGault, calculateCkdEpi2021, and evaluateLithiumLevel rely on
range comparisons without checking that numeric inputs are finite. A NaN input bypasses those
checks and propagates through calculations, allowing QTc to report isValid: true and
riskCategory: "normal", renal calculations to report a valid NaN result classified as
kidney_failure, and lithium to return status: "therapeutic", severity: "none", and
routine-monitoring guidance.
Code

src/lib/calculators/clinical-math/lithium.ts[R59-62]

+  } else {
+    status = "therapeutic";
+    clinicalGuidance.push(
+      "Level is within standard therapeutic window. Ensure routine 3-6 monthly monitoring of lithium level, eGFR, electrolytes, and thyroid function (TSH).",
Relevance

●●● Strong

Non-finite clinical inputs bypass fail-closed validation and produce misleading valid results;
similar invalid-input guards were accepted.

PR-#3297
PR-#2601

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Comparisons against NaN are false, so range-based validation does not reject it. QTc corrections
and renal formulas then propagate NaN into their success paths; QTc risk comparisons fall through
to normal, while classifyRenalStage labels the renal result kidney_failure. Lithium has no
validity check before classification, so its status comparisons fall through to the therapeutic
branch.

src/lib/calculators/clinical-math/lithium.ts[14-30]
src/lib/calculators/clinical-math/lithium.ts[54-72]
src/lib/calculators/clinical-math/qtc.ts[12-14]
src/lib/calculators/clinical-math/qtc.ts[75-88]
src/lib/calculators/clinical-math/lithium.ts[29-64]
src/lib/calculators/clinical-math/renal.ts[3-9]
docs/superpowers/plans/2026-10-12-phase-1-critical-hotfixes.md[26-27]
src/lib/calculators/clinical-math/qtc.ts[56-98]
src/lib/calculators/clinical-math/lithium.ts[14-34]
src/lib/calculators/clinical-math/lithium.ts[36-72]
src/lib/calculators/clinical-math/renal.ts[3-8]
src/lib/calculators/clinical-math/renal.ts[20-69]
src/lib/calculators/clinical-math/renal.ts[80-119]
Skill: clinical

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Non-finite numeric inputs can bypass validation in the QTc and renal calculators and reach successful results; a non-finite lithium reading can reach therapeutic guidance.

## Fix Focus Areas
- src/lib/calculators/clinical-math/qtc.ts[12-53]
- src/lib/calculators/clinical-math/renal.ts[3-8]
- src/lib/calculators/clinical-math/renal.ts[20-47]
- src/lib/calculators/clinical-math/renal.ts[76-95]
- src/lib/calculators/clinical-math/lithium.ts[14-72]
- src/lib/calculators/clinical-math/types.ts[69-81]
- tests/clinical-math-calculators.test.ts[172-187]
- tests/clinical-math-calculators.test.ts[190-231]

## Recommended Fix
Check every numeric input with `Number.isFinite` before calculation or classification, including QTc inputs, renal age, weight and creatinine values, and lithium age and concentration. Reject invalid QTc and renal inputs through their invalid-result paths, and give lithium an explicit invalid-input result rather than a therapeutic status; also validate renal results before stage classification. Add tests for `NaN`, Infinity, and other invalid values.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. Unsafe lithium doses appear valid ✓ Resolved
Description
calculateProportionalLithiumDose checks only for positive inputs before scaling the dose, with no
guard for toxic targets, extreme adjustment ratios, or absolute doses. A 2.0 mmol/L target is
accepted despite the module classifying levels above 1.2 mmol/L as toxic, while low current levels
can produce valid suggestions of 9600 mg or 10000 mg.
Code

src/lib/calculators/clinical-math/lithium.ts[R88-90]

+  if (input.targetLevelMmolL <= 0) {
+    errors.push("Target lithium level must be greater than zero mmol/L");
+  }
Relevance

●●● Strong

Unbounded clinical dosing suggestions create clear safety risk, matching the repository’s accepted
preference for validated clinical ranges.

PR-#3395
PR-#2601

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The helper validates only positivity, then applies an unbounded proportional formula and returns the
result as valid. That permits a 750 mg dose at 0.5 mmol/L to yield 3000 mg for a toxic 2.0 mmol/L
target, and permits low-level inputs to yield 9600 mg or 10000 mg suggestions; the cited plan calls
for bounded results and clinical warnings for extreme values.

src/lib/calculators/clinical-math/lithium.ts[42-47]
src/lib/calculators/clinical-math/lithium.ts[82-109]
src/lib/calculators/clinical-math/lithium.ts[79-111]
docs/superpowers/plans/2026-10-12-phase-1-critical-hotfixes.md[26-28]
src/lib/calculators/clinical-math/lithium.ts[82-90]
src/lib/calculators/clinical-math/lithium.ts[101-110]
Skill: clinical

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The proportional lithium dose helper accepts toxic targets and presents extreme calculated doses as valid suggestions.

## Fix Focus Areas
- src/lib/calculators/clinical-math/lithium.ts[79-111]
- src/lib/calculators/clinical-math/types.ts[83-94]
- tests/clinical-math-calculators.test.ts[233-245]

## Recommended Fix
Reject non-finite inputs and targets above 1.2 mmol/L before calculating a dose. Apply clinically governed limits to adjustment ratios and absolute doses; fail closed (`isValid: false`) for unsafe suggestions, with clinical warnings or a requirement for review where appropriate. Add a `warnings` array to the result if needed, and add regression tests for a 2.0 mmol/L target and low-current-level scenarios that produce extreme doses.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 9 rules
✅ Skills: 8 invoked
  review
  clinical
  rag
  data
  testing-review
  ai-architecture-review
  code-quality-review
  source-governance
✅ Cross-repo context — repo relationships
  Explored: repo: BigSimmo/Medications (sha: f0fe3d04) — View relationship
Review mode: Auto: ⚖️ Balanced: Downgraded extended -> standard: change is below the extended eligibility bar (hunks 20/18, lines 152/200; both must reach the floor). Router rationale: Multiple independent runtime fixes span reporting, ledger, trust, caching, and medication safety.

Grey Divider

Tip of the day
💡 Did you know, you can switch off images and animations for a plain-text comment

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/lib/medications/tall-man.ts Resolved
Comment thread src/lib/medications/tall-man.ts Resolved
Comment thread src/lib/chunking.ts Resolved
Comment thread src/lib/rag/rag.ts Resolved
Comment thread src/lib/medications/tall-man.ts Resolved
Comment thread tests/setup-node.test.ts Resolved
Comment thread src/lib/rag/rag.ts Resolved
- 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
@qodo-code-review

Copy link
Copy Markdown
Contributor

Qodo Fixer

✅ Committed (9) · ☑ Fixed (9)

Grey Divider

Commits pushed directly to this PR — no separate fix PR opened.

Process — 9 fixed
  • ☑ Fixed: Node tests keep leaked mocks
  • ☑ Fixed: Invalid lithium results seem therapeutic
  • ☑ Fixed: Unsafe lithium doses appear valid
  • ☑ Fixed: Children receive adult renal estimates
  • ☑ Fixed: Tall Man spellings contradict the ISMP list
  • ☑ Fixed: Nested tags escape the user-query fence
  • ☑ Fixed: Chunk overlap grows from earlier sentences
  • ☑ Fixed: Medication screens omit new lettering
  • ☑ Fixed: Some medication names return a function

@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: 1bdb79e734

ℹ️ 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/chunking.ts Resolved
Comment thread tests/setup-node.test.ts Resolved
@codeant-ai

codeant-ai Bot commented Oct 13, 2026

Copy link
Copy Markdown

CodeAnt Nitpicks

4 code suggestions

1. If an aborted entry is replaced before its promise settles, this unconditional delete removes the new same-key flight, breaking deduplication for later callers.

Race condition · docs/audit/full-repository-audit-2026-10-11.md:175


2. vi.restoreAllMocks() and vi.clearAllTimers() leave vi.stubGlobal replacements installed, so mocked fetch or Date still leak between Node tests.

Incomplete implementation · docs/audit/full-repository-audit-2026-10-11.md:177


3. For inherited names such as constructor, this lookup returns a built-in function instead of the input string, breaking the documented fallback and string return contract.

Type error · src/lib/medications/tall-man.ts:59-60


4. If chunk boundaries split this phrase, no chunk matches the condition, so the test skips its assertion and passes without checking whether the prohibition stayed with the instruction.

Logic error · tests/chunking-negation.test.ts:22-24

@BigSimmo
BigSimmo disabled auto-merge October 13, 2026 22:25
@gitar-bot
gitar-bot Bot enabled auto-merge (squash) October 13, 2026 22:46

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

@BigSimmo
BigSimmo disabled auto-merge October 13, 2026 22:48
@gitar-bot
gitar-bot Bot enabled auto-merge (squash) October 13, 2026 23:09

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

@BigSimmo
BigSimmo disabled auto-merge October 13, 2026 23:10
@gitar-bot
gitar-bot Bot enabled auto-merge (squash) October 13, 2026 23:32

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

@BigSimmo
BigSimmo disabled auto-merge October 13, 2026 23:42
@gitar-bot
gitar-bot Bot enabled auto-merge (squash) October 14, 2026 03:03

@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

qodo-code-review Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

No code changes since the last review — review skipped

Qodo Logo

@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: Three CI error templates across 157 logs point to tooling and infrastructure issues: a strict design-system check failing on arbitrary font sizes (relation to this PR unclear), a Supabase drift-manifest mismatch, and a bundle-budget route regression; none are clearly caused by the PR's diff.

Overview

Three unique error templates were found across 157 logs for commit d131594. Each template is consistent with a tooling or CI-check failure rather than a compile or runtime break, and the supplied findings do not link any failure directly to the PR's changes.

Failures

Strict design-system check: arbitrary font sizes present (confidence: high)

  • Type: tooling
  • Affected jobs: 115552937637, 115571620430, 115580175739, 115588378564, 115597682735, 115658188233
  • Related to change: unclear
  • Root cause: The strict design-system check exits with code 1 because arbitrary font-size utilities are present. The log does not name the file or class, and the diff statistics do not show a specific font-size change that explains it.
  • Suggested fix: Locate the arbitrary font-size utilities flagged by the check and replace them with existing named @theme text-size tokens in src/app/globals.css, or define a new token there if none fits. Rerun the check.

Supabase drift manifest is stale (confidence: high)

  • Type: configuration
  • Affected jobs: 115658188379
  • Related to change: unclear
  • Root cause: The generated Supabase schema artifact differs from the committed one in schema_sha256 and other fields, so the drift check fails. The log shows the schema was not regenerated and committed alongside its source change.
  • Suggested fix: Download the drift-manifest artifact from the job, commit the regenerated schema manifest, and rerun the check.

Bundle budget regression on one route (confidence: medium)

  • Type: build
  • Affected jobs: 115658188224
  • Related to change: unclear
  • Root cause: Route client JavaScript grew beyond the configured tolerance versus baseline. The baseline commit also could not be resolved locally, so the comparison is weakened and the failure may reflect baseline drift rather than a real regression.
  • Suggested fix: Fetch the recorded baseline commit so ancestry can be checked. If the growth is intentional and reviewed, refresh the baseline with npm run check:bundle-budget -- --update; otherwise reduce the route's client bundle.

Summary

  • Change-related failures: None confirmed. All three are marked unclear, so they need developer verification against the diff.
  • Infrastructure/flaky failures: None identified. Registry rate limits and warnings in the logs were not treated as failures.
  • Recommended action: Check whether the font-size utilities, schema artifact, and route bundle growth come from this PR's changes. Fix the font-size tokens and regenerate the schema manifest, then fetch the baseline commit so the bundle budget can be verified.
Code Review 👍 Approved with suggestions 9 closed / 11 findings

🔴 High risk · New proxy/indexing authorization and teaching-table RLS/write policies govern access to sensitive records; mistakes could permit unauthorized disclosure or mutation.

Adds clinical calculators, Tall Man lettering, RAG query delimiting, chunk negation preservation, and query PII scrubbing, with proxy auth and on-call safe-number fixes resolved. Approved with suggestions: a late save can mark the note "saved" while newer edits remain unsaved in src/components/clinical-notes/note-editor.tsx, and overlapping BarcodeDetector calls can submit the same check-in twice in src/components/teaching/qr-check-in-helper.tsx.

💡 Bug: A late save marks the note "saved" even though newer edits haven't saved

📄 src/components/clinical-notes/note-editor.tsx:101-109 📄 src/components/clinical-notes/note-editor.tsx:118-132 📄 src/components/clinical-notes/note-editor.tsx:148-153

The sequence number only goes up when a save starts, not on each keystroke. Say save A (text X) is in flight and the user types Y. handleChange sets the status to "unsaved" and starts the debounce timer, but no new save has begun yet, so A still holds the current sequence. When A resolves it sets lastSavedValue = X and the status to "saved". The editor then reads "saved" while Y exists only in memory. That status also hides the problem on blur, because handleBlur only flushes when the status is "unsaved". If the clinician closes the tab inside the debounce window, the latest edit is lost. Fix: bump the sequence in handleChange too (or only settle when textToSave equals the current value), so a save that finishes after newer edits cannot set "saved".

Invalidate in-flight saves on every edit so a late completion cannot report the note as saved
const handleChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
  const newValue = e.target.value;
  recordSelection();
  // Any edit makes an in-flight save stale for status purposes.
  saveSequenceRef.current += 1;
  if (!isControlled) setInternalValue(newValue);
  onChange?.(newValue);
  setSaveStatus("unsaved");
  if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
  saveTimerRef.current = setTimeout(() => {
    performSave(newValue);
  }, debounceMs);
};
💡 Edge Case: Overlapping BarcodeDetector calls can submit the same check-in twice

📄 src/components/teaching/qr-check-in-helper.tsx:152-166

setInterval starts a new async detector.detect() every 300 ms and never waits for the previous one to finish. On slower phones a detection can take longer than 300 ms, so two detections can be running at once. If both find the QR code, each one calls stopCamera() and onScanCode(rawValue), and the check-in is submitted twice. stopCamera clears the interval but does not cancel a detection that has already started. Fix: capture cameraRequestRef.current when the scan starts and ignore any result whose value has since changed, or keep an in-flight flag so only one detection runs at a time.

Allow one detection at a time and ignore results from a stopped scan
const scanRequest = cameraRequestRef.current;
let detecting = false;
scanIntervalRef.current = setInterval(async () => {
  if (detecting || !videoRef.current || videoRef.current.readyState < 2) return;
  detecting = true;
  try {
    const barcodes = await detector.detect(videoRef.current);
    const rawValue = barcodes?.[0]?.rawValue;
    if (rawValue && scanRequest === cameraRequestRef.current) {
      stopCamera();
      onScanCode(rawValue);
    }
  } catch {
    // Ignore transient frame detection errors
  } finally {
    detecting = false;
  }
}, 300);
✅ 9 closed
✅ Bug: Prohibition lookback regex matches the whole window, not the last sentence

📄 src/lib/chunking.ts:398-412
The regex /(?:(?<=[.!?])\s+|^)(.*?)$/ has no m flag, so the ^ alternative matches at index 0 first and (.*?)$ then takes the rest of the window. sentenceFragment is therefore the full lookback window, not the sentence after the last boundary. I checked this in node: for "Avoid alcohol with this drug. Take the tablet..." the capture is the whole string. exec also returns the first prohibition rather than the last. As a result, any prohibition word in the previous max(overlap*3,160) characters, including one in an earlier, finished sentence, pulls the overlap start back to that word. Overlaps get much larger, chunks duplicate more text, and the 'within the sentence' guarantee in the comment does not hold. Fix: find the last sentence boundary explicitly, then search only the text after it. Add a test where the prohibition sits in a previous sentence.

✅ Bug: Stray }; and } at end of qtc.ts cause a syntax error

📄 src/lib/calculators/clinical-math/qtc.ts:100-102
This commit added two lines (}; and }) at lines 101-102, after calculateQtc has already closed at line 100. Neither brace has a matching opener, so TypeScript and Vite can't parse the module. tsc fails, and every test that imports @/lib/calculators/clinical-math/qtc (including tests/clinical-math-calculators.test.ts) fails at import. Fix: delete the two trailing lines.

✅ Quality: Chunk boundary changes alter indexed text without version bump or canary

📄 src/lib/chunking.ts:11-12 📄 src/lib/chunking.ts:426-437 📄 src/lib/chunking.ts:395-409
chunkTextBySentence now extends end forward by up to 35% over chunkSize before the old trim-back. readableOverlapStart also extends overlaps. Together these change chunk boundaries and content for every newly ingested document. The PR body still declares RAG impact: no retrieval behaviour change. src/lib/chunking.ts is a protected RAG text producer, and changing chunk content changes what retrieval can rank. The repo rules call for a behaviour change RAG-impact line and a live eval-canary before/after pair. CHUNKER_VERSION / DOCUMENT_CHUNKER_VERSION are also unchanged, so chunks from the old and new algorithms sit together under one version stamp, and nothing marks existing documents for re-chunking. Fix: bump the chunker versions, correct the RAG impact line, and get the canary pair (with user approval) before merging.

✅ Edge Case: Clinical calculators report NaN/non-finite inputs as valid results

📄 src/lib/calculators/clinical-math/renal.ts:20-34 📄 src/lib/calculators/clinical-math/renal.ts:80-85 📄 src/lib/calculators/clinical-math/qtc.ts:12-26 📄 src/lib/calculators/clinical-math/lithium.ts:14-28
Every guard is a comparison such as <= 0 or > 120, and all of them are false for NaN. A NaN or blank-parsed value therefore gets past validation. With NaN age, weight or creatinine, calculateCockcroftGault returns isValid: true, crClMlMin: NaN and stage: 'kidney_failure'. calculateQtc returns NaN values with riskCategory: 'normal'. evaluateLithiumLevel has no input validation at all: a NaN level fails every branch and is reported as status: 'therapeutic', with isWithinTarget false and the 'within standard therapeutic window' guidance. Infinity passes as well. These are safety helpers that should fail closed, so add Number.isFinite checks on every numeric input. Give evaluateLithiumLevel an invalid result path for non-finite or negative levels.

✅ Bug: Statutory form defaults to a fake patient and to consent being obtained

📄 src/components/forms/wa-mha-form-print.tsx:63-73
WaMhaFormPrint gives every patient and order field a synthetic default ("Smith, John", DOB 1985-04-12, UMRN-984210, "Dr. C. Reynolds", made-up clinical justifications). It also defaults chiefPsychiatristConsent = true. If a caller leaves out any prop, the printed Form 6A/6B shows invented identifiers and justifications, and Form 6B says Chief Psychiatrist consent was "Obtained and documented in clinical record". That is a false statutory statement. In a clinical prototype, missing data should print as blank lines and consent should default to false/pending.

...and 4 more closed from earlier reviews

🤖 Prompt for agents
Code Review: Adds clinical calculators, Tall Man lettering, RAG query delimiting, chunk negation preservation, and query PII scrubbing, with proxy auth and on-call safe-number fixes resolved. Approved with suggestions: a late save can mark the note "saved" while newer edits remain unsaved in `src/components/clinical-notes/note-editor.tsx`, and overlapping BarcodeDetector calls can submit the same check-in twice in `src/components/teaching/qr-check-in-helper.tsx`.

1. 💡 Bug: A late save marks the note "saved" even though newer edits haven't saved
   Files: src/components/clinical-notes/note-editor.tsx:101-109, src/components/clinical-notes/note-editor.tsx:118-132, src/components/clinical-notes/note-editor.tsx:148-153

   The sequence number only goes up when a save starts, not on each keystroke. Say save A (text X) is in flight and the user types Y. `handleChange` sets the status to "unsaved" and starts the debounce timer, but no new save has begun yet, so A still holds the current sequence. When A resolves it sets `lastSavedValue = X` and the status to "saved". The editor then reads "saved" while Y exists only in memory. That status also hides the problem on blur, because `handleBlur` only flushes when the status is "unsaved". If the clinician closes the tab inside the debounce window, the latest edit is lost. Fix: bump the sequence in `handleChange` too (or only settle when `textToSave` equals the current value), so a save that finishes after newer edits cannot set "saved".

   Fix (Invalidate in-flight saves on every edit so a late completion cannot report the note as saved):
   const handleChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
     const newValue = e.target.value;
     recordSelection();
     // Any edit makes an in-flight save stale for status purposes.
     saveSequenceRef.current += 1;
     if (!isControlled) setInternalValue(newValue);
     onChange?.(newValue);
     setSaveStatus("unsaved");
     if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
     saveTimerRef.current = setTimeout(() => {
       performSave(newValue);
     }, debounceMs);
   };

2. 💡 Edge Case: Overlapping BarcodeDetector calls can submit the same check-in twice
   Files: src/components/teaching/qr-check-in-helper.tsx:152-166

   `setInterval` starts a new async `detector.detect()` every 300 ms and never waits for the previous one to finish. On slower phones a detection can take longer than 300 ms, so two detections can be running at once. If both find the QR code, each one calls `stopCamera()` and `onScanCode(rawValue)`, and the check-in is submitted twice. `stopCamera` clears the interval but does not cancel a detection that has already started. Fix: capture `cameraRequestRef.current` when the scan starts and ignore any result whose value has since changed, or keep an in-flight flag so only one detection runs at a time.

   Fix (Allow one detection at a time and ignore results from a stopped scan):
   const scanRequest = cameraRequestRef.current;
   let detecting = false;
   scanIntervalRef.current = setInterval(async () => {
     if (detecting || !videoRef.current || videoRef.current.readyState < 2) return;
     detecting = true;
     try {
       const barcodes = await detector.detect(videoRef.current);
       const rawValue = barcodes?.[0]?.rawValue;
       if (rawValue && scanRequest === cameraRequestRef.current) {
         stopCamera();
         onScanCode(rawValue);
       }
     } catch {
       // Ignore transient frame detection errors
     } finally {
       detecting = false;
     }
   }, 300);

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

Copy link
Copy Markdown
Owner Author

Closing in favour of #3538, which carries the same audit remediation and fixes (including the review threads still open here). Closed at the owner's direction.


Generated by Claude Code

@BigSimmo BigSimmo closed this Oct 14, 2026
auto-merge was automatically disabled October 14, 2026 03:23

Pull request was closed

BigSimmo pushed a commit that referenced this pull request Oct 14, 2026
…ds_review (owner decision 13 Oct 10:23 PM AWST)

Ported from #3510 (eddedf4) onto this branch's own data: the status change was applied
to src/data/therapies-source.json and the hashed catalogue files, index, asset list and
site-content baseline were regenerated with the repo's own generators, not copied across.
No re-signing; old sign-off hashes and dates are cleared. Sign-off pins: therapy 48 -> 53,
signable 22 -> 27. New test pins the needs_review state.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KpmSAVMAELX796AzEujHSw
BigSimmo pushed a commit that referenced this pull request Oct 14, 2026
…ed from #3510)

planRequestBatch now orders adds first, so a close/update whose UUID sorts before the
add that creates its issue no longer aborts the batch. Test: 'pending add ordering'.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KpmSAVMAELX796AzEujHSw
BigSimmo pushed a commit that referenced this pull request Oct 14, 2026
…rust derivation (ported from #3510)

Exports deriveTrust from answer-render-policy and uses it in the recalculation script,
replacing a duplicate that ignored source backing, faithfulness warnings, unverified
numbers and evidence-gap mode and could label stronger than the app renders.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KpmSAVMAELX796AzEujHSw
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔴 Large blast radius size:XXL This PR changes 1000+ lines, ignoring generated files 🕐 40+ Minutes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant