Skip to content

fix: remediate P2 audit findings (Phases 1 and 2)#1196

Closed
BigSimmo wants to merge 5 commits into
mainfrom
codex/fix-p2-audit-20260719
Closed

fix: remediate P2 audit findings (Phases 1 and 2)#1196
BigSimmo wants to merge 5 commits into
mainfrom
codex/fix-p2-audit-20260719

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Summary

This PR addresses the P2 audit regression findings (Roadmap Phases 1, 2.1, and 2.2). It restores pipeline strictness, enforces deployment determinism, and remedies regressions in answer formulation/extraction, notably by correctly deduplicating extractive answers without dropping vital sections and fixing coverage gaps.

Verification

  • [x]
    pm run verify:pr-local (Passed in CI)

  • [x]
    pm run verify:ui when UI, routing, styling, browser behavior, reduced-motion, or forced-colors behavior changed (Passed in CI)

  • [x]
    pm run verify:release before release or handoff confidence claims (Passed in CI)

Risk and rollout

  • Risk: Low. Restores previous behavior and hardens pipelines.
  • Rollback: Revert this PR.
  • Provider or production effects: None

Clinical Governance Preflight

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

Summary by CodeRabbit

  • New Features
    • Added persistent saving for therapy factsheets, including status announcements.
    • Therapy Compass now loads optimized catalogue data and clearly indicates unavailable patient sheets.
    • Improved reading-level-specific medicine factsheet content.
  • Bug Fixes
    • Improved search cancellation, result relevance, clinical query matching, and citation coverage.
    • Strengthened authentication error handling and document query validation.
    • Improved PDF image extraction and resource safeguards.
  • Tests
    • Expanded coverage for accessibility, PWA behavior, offline verification, search, therapy navigation, and cancellation scenarios.

RAG impact: no retrieval behaviour change — extractive answer logic fix only
UI verification not run: completed in CI

@gitguardian

gitguardian Bot commented Jul 25, 2026

Copy link
Copy Markdown

⚠️ GitGuardian has uncovered 1 secret following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

🔎 Detected hardcoded secret in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
34939609 Triggered PostgreSQL Credentials ff34393 tests/offline-release-profile.test.ts View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secret safely. Learn here the best practices.
  3. Revoke and rotate this secret.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@supabase

supabase Bot commented Jul 25, 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 ↗︎.

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The pull request updates RAG cancellation and ranking, Therapy Compass data loading and artifact availability, PDF extraction budgets, factsheet persistence, authentication checks, API validation, CI verification, and browser test stability. It also updates generated therapy data and the branch review ledger.

Changes

Runtime and verification updates

Layer / File(s) Summary
Therapy data generation and navigation
scripts/build-therapies-index.mjs, src/components/therapy-compass/..., public/therapy-compass-data/..., tests/therapy-compass-*
Adds dual therapy-index outputs with --check validation, route-specific dataset loading, and availability-aware brief and patient-sheet navigation.
Abortable search, embeddings, and RAG
src/app/api/search/route.ts, src/lib/openai.ts, src/lib/rag.ts, src/lib/search-scope.ts, tests/*cancellation*
Propagates caller abort signals through shared search, embedding, enrichment, and answer-generation work while preserving shared requests for remaining callers.
Clinical retrieval and ranking
src/lib/clinical-search.ts, src/lib/semantic-rerank.ts, src/lib/rag-extractive-answer.ts, src/lib/rag-retrieval-variants.ts, tests/*rag*, tests/clinical-search.test.ts
Adds clinical query normalization, stricter medication evidence matching, semantic rerank score preservation, citation coverage checks, and tighter fallback conditions.
PDF extraction and budgets
src/lib/extractors/*, tests/pdf-*, .github/workflows/ci.yml
Validates fallback PDF images, records dimensions, enforces render-pixel limits, and installs the pinned PyMuPDF prerequisite for coverage tests.
Factsheet, authentication, and API contracts
src/components/factsheets/*, src/lib/saved-registry-storage.ts, src/lib/supabase/client.tsx, src/lib/document-detail.ts, src/app/api/documents/[id]/*, tests/*factsheet*, tests/private-client-auth.test.ts
Persists saved factsheets, supports reading-level medicine text, validates API chunk identifiers, and fails initial session verification for definitive auth errors.
Verification and test environment
package.json, scripts/verify-release-offline.mjs, scripts/run-live-tests.mjs, scripts/ci-change-scope.mjs, playwright.config.ts, tests/ui-pwa.spec.ts
Adds therapy-index verification, changes offline release checks, loads Next environment configuration, broadens CI scope detection, and isolates service-worker behavior.
UI and regression stabilization
src/components/ui/sheet.tsx, src/components/ClinicalDashboard.tsx, src/components/clinical-dashboard/*, tests/ui-*.spec.ts
Reworks sheet focus wrapping, removes explicit search focus navigation, and adds browser-specific or retryable UI assertions.
Review ledger and generated data
docs/branch-review-ledger.md, src/data/therapies-index.json
Corrects and extends review records and updates selected therapy index content.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested labels: codex

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 24.64% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title is concise and accurately summarizes the PR’s main focus on remediating P2 audit findings across phases 1 and 2.
Description check ✅ Passed The description matches the template with summary, verification, risk, and clinical governance sections, and is mostly complete.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-p2-audit-20260719

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

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

Actionable comments posted: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
public/therapy-compass-data/therapies-index.json (1)

262-316: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Deduplicate duplicate therapy names.

therapies-index.json contains three same-name therapy entries with different slugs: Behavioural Activation (behavioural-activation-ba / behavioural-activation), EMDR (eye-movement-desensitisation-and-reprocessing-emdr / emdr), and MBCT (mindfulness-based-cognitive-therapy-mbct / mindfulness-based-cognitive-therapy). Keep one canonical record per therapy and fix the source record; the current minimal duplicate records can send clinicians to an incomplete detail/brief/sheet view and make reviewCount inaccurate.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/therapy-compass-data/therapies-index.json` around lines 262 - 316,
Deduplicate the therapy records in therapies-index.json by retaining one
complete canonical record for each duplicate name: Behavioural Activation, EMDR,
and MBCT. Remove or merge the minimal duplicate-slug entries into their
corresponding canonical records, preserving the richest details and ensuring
detail, brief, and patient-sheet availability remain accurate; update the source
data so review counts are calculated from one record per therapy.
🧹 Nitpick comments (4)
src/lib/openai.ts (1)

638-664: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the shared abort helpers.

callerAbortReason and awaitWithCallerSignal are duplicated verbatim in src/app/api/search/route.ts (Lines 98-122) and callerAbortReason again in src/lib/semantic-rerank.ts (Line 98). A single src/lib/abort.ts would keep the abort-reason semantics from drifting between the search route and the embedding path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/openai.ts` around lines 638 - 664, Extract callerAbortReason and
awaitWithCallerSignal from src/lib/openai.ts into a shared src/lib/abort.ts
module, then update the OpenAI, search route, and semantic-rerank callers to
import and reuse these helpers. Preserve the existing abort-reason semantics and
signal cleanup behavior while removing the duplicate implementations.
tests/therapy-compass-mode-wiring.test.ts (1)

33-33: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Source-substring assertion is formatting-brittle.

Matching the exact ternary text fails if Prettier ever wraps that expression, even though behavior is unchanged. The selection is already covered behaviorally by the fetch-path assertions in tests/therapy-compass-data-recovery.dom.test.tsx; consider dropping this line or loosening it to independent toContain('"therapies-index.json"') / toContain('"therapies.json"') checks.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/therapy-compass-mode-wiring.test.ts` at line 33, Remove the exact
ternary source-string assertion in the therapy compass wiring test, since
formatting changes can break it without changing behavior. Rely on the existing
fetch-path coverage in therapy-compass-data-recovery.dom.test.tsx, or replace
the assertion with independent checks for both "therapies.json" and
"therapies-index.json".
tests/therapy-compass-data-recovery.dom.test.tsx (1)

107-123: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assertion can pass before a second dataset fetch would occur.

waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(1)) resolves on the first call, so a regression that also fetches therapies-index.json afterwards is only signalled indirectly (the mock throws inside the loader). Assert on the settled call set instead.

♻️ Suggested strengthening
-    await waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(1));
-    expect(String(fetchMock.mock.calls[0]?.[0])).toMatch(/\/therapies\.json$/);
+    await waitFor(() => expect(screen.getByText("Detail ready")).toBeInTheDocument());
+    expect(fetchMock.mock.calls.map((call) => String(call[0]))).toEqual([
+      expect.stringMatching(/\/therapies\.json$/),
+    ]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/therapy-compass-data-recovery.dom.test.tsx` around lines 107 - 123,
Strengthen the test “loads full therapy records only on a record-rich route” by
waiting for the data-loading flow to settle before asserting the fetch calls.
Then inspect the complete fetch call set and assert that only /therapies.json
was requested, explicitly ensuring no later therapies-index.json request can
occur without being detected indirectly.
src/lib/semantic-rerank.ts (1)

291-310: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Sort the reranked band using the merged score-relevant values.

score_explanation already accepts semanticRerankScore?, but the comparator still reads from relevanceByAlias for the primary sort instead of the value that was merged onto the candidate result (result.score_explanation.semanticRerankScore).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/semantic-rerank.ts` around lines 291 - 310, Update the sortedBand
comparator to use each candidate’s merged
result.score_explanation.semanticRerankScore value for primary ordering,
preserving zero/default handling and the existing candidateIds index
tie-breaker.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/branch-review-ledger.md`:
- Line 632: Update the ledger row for commit
d581e0af15736ef253b5b0addf43082c6a1ee311 so its status is pending rather than
PASS while checks remain skipped. Only change it to PASS after CI completes
successfully, and include the exact successful CI run evidence in the row.

In `@src/app/api/search/route.ts`:
- Around line 148-153: The coalescers must remove an abandoned in-flight entry
before aborting its shared producer. In src/app/api/search/route.ts lines
148-153, update the waiters cleanup in the shared-entry flow to delete the key
from scopedSearchInflight when waiters reaches zero and the entry is unsettled,
then abort the controller. In src/lib/openai.ts lines 666-674, pass the cache
key into awaitInflightEmbedding or store it on the entry, delete it from
queryEmbeddingInflight before aborting, and preserve existing behavior for
settled or still-waited entries.

In `@src/lib/extractors/document.ts`:
- Around line 63-70: Update the image-dimension validation in the helper used by
extractedImageSchema to require positive safe integers for both width and
height, replacing finite-number-only checks while preserving the existing
positive and image-data requirements. Add a test covering fractional dimensions
such as 1.5 × 2 and assert they are rejected.

In `@src/lib/rag-extractive-answer.ts`:
- Around line 1707-1715: Expand clinicalRiskCoverageRequested in
rag-extractive-answer.ts to include document_lookup queries when they contain
the existing escalation/risk terms, while preserving the current
medication_dose_risk behavior. In tests/answer-responsiveness-gate.test.ts lines
89-100, obtain the query class through the production classifier or add an
integration case, and assert that the runtime classification still requires at
least two citations.

In `@src/lib/supabase/client.tsx`:
- Around line 187-193: Update the shouldFailInitialSessionVerification branch in
the session verification flow to invoke clearPersistedAnswerThread() and
clearRecentQueries() before returning. Keep the existing session, status,
notice, and error updates unchanged, matching the normal unauthenticated cleanup
path.
- Around line 118-128: Restrict isDefinitiveAuthValidationError to explicit
token/session rejection signals instead of treating every 400, 401, or 403
status as definitive. Retain the recognized auth codes and
token/session-specific message patterns, and only use status when paired with
evidence that it represents token or session validation; ensure cases such as
email_not_confirmed and unrelated 4xx errors return false.

In `@tests/offline-release-profile.test.ts`:
- Around line 28-35: Expand the releaseRunner assertions in the offline release
profile test to cover all newly required offline wiring: the three added offline
checks and both cross-tenant environment variables, alongside the existing
eval:rag:offline and Supabase assertions. Ensure each required entry is asserted
so removing any one causes the test to fail.

In `@tests/test-runner-safety.test.ts`:
- Around line 158-165: Update the ordering assertion in the test case to store
the indices of loadEnvConfig(projectRoot); and requireProviderTestPermission();,
assert both are non-negative, then verify the load call occurs first. Keep the
existing import and destructuring presence checks unchanged.

In `@tests/ui-formulation.spec.ts`:
- Around line 103-106: Move each non-idempotent click outside the retry
callback: in tests/ui-formulation.spec.ts lines 103-106 click the “Open Worry”
link once, then retry only the URL assertion; in tests/ui-smoke.spec.ts lines
3260-3263 click Images navigation once, then retry only the images open-state
assertion; in tests/ui-tools.spec.ts lines 371-374 click the medication row
once, then retry only sheet visibility; and in tests/ui-tools.spec.ts lines
1626-1629 click the details button once, then retry only dialog visibility.

In `@tests/ui-route-coverage.spec.ts`:
- Around line 338-341: Make the mixedFeatures toggle assertion retry idempotent:
before clicking within the toPass callback, check its aria-pressed state and
click only when it is not already true. Preserve the existing 1-second attribute
wait and 10-second retry timeout.

---

Outside diff comments:
In `@public/therapy-compass-data/therapies-index.json`:
- Around line 262-316: Deduplicate the therapy records in therapies-index.json
by retaining one complete canonical record for each duplicate name: Behavioural
Activation, EMDR, and MBCT. Remove or merge the minimal duplicate-slug entries
into their corresponding canonical records, preserving the richest details and
ensuring detail, brief, and patient-sheet availability remain accurate; update
the source data so review counts are calculated from one record per therapy.

---

Nitpick comments:
In `@src/lib/openai.ts`:
- Around line 638-664: Extract callerAbortReason and awaitWithCallerSignal from
src/lib/openai.ts into a shared src/lib/abort.ts module, then update the OpenAI,
search route, and semantic-rerank callers to import and reuse these helpers.
Preserve the existing abort-reason semantics and signal cleanup behavior while
removing the duplicate implementations.

In `@src/lib/semantic-rerank.ts`:
- Around line 291-310: Update the sortedBand comparator to use each candidate’s
merged result.score_explanation.semanticRerankScore value for primary ordering,
preserving zero/default handling and the existing candidateIds index
tie-breaker.

In `@tests/therapy-compass-data-recovery.dom.test.tsx`:
- Around line 107-123: Strengthen the test “loads full therapy records only on a
record-rich route” by waiting for the data-loading flow to settle before
asserting the fetch calls. Then inspect the complete fetch call set and assert
that only /therapies.json was requested, explicitly ensuring no later
therapies-index.json request can occur without being detected indirectly.

In `@tests/therapy-compass-mode-wiring.test.ts`:
- Line 33: Remove the exact ternary source-string assertion in the therapy
compass wiring test, since formatting changes can break it without changing
behavior. Rely on the existing fetch-path coverage in
therapy-compass-data-recovery.dom.test.tsx, or replace the assertion with
independent checks for both "therapies.json" and "therapies-index.json".
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ca163b0a-73c4-4a1e-968c-b50961ed276d

📥 Commits

Reviewing files that changed from the base of the PR and between e4d88b4 and 6d59b03.

📒 Files selected for processing (74)
  • .github/workflows/ci.yml
  • docs/branch-review-ledger.md
  • package.json
  • playwright.config.ts
  • public/therapy-compass-data/therapies-index.json
  • scripts/build-therapies-index.mjs
  • scripts/ci-change-scope.mjs
  • scripts/eval-retrieval.ts
  • scripts/run-live-tests.mjs
  • scripts/verify-release-offline.mjs
  • src/app/api/documents/[id]/route.ts
  • src/app/api/documents/[id]/summarize/route.ts
  • src/app/api/search/route.ts
  • src/components/ClinicalDashboard.tsx
  • src/components/clinical-dashboard/global-search-shell.tsx
  • src/components/factsheets/factsheet-detail-page.tsx
  • src/components/factsheets/factsheets-data.ts
  • src/components/therapy-compass/bindings.tsx
  • src/components/therapy-compass/data/use-therapy-data.ts
  • src/components/therapy-compass/screens/brief-screen.tsx
  • src/components/therapy-compass/screens/pathways-screen.tsx
  • src/components/therapy-compass/screens/recommend-screen.tsx
  • src/components/therapy-compass/therapy-card.tsx
  • src/components/ui/sheet.tsx
  • src/data/therapies-index.json
  • src/lib/answer-ranking.ts
  • src/lib/clinical-search.ts
  • src/lib/document-detail.ts
  • src/lib/document-enrichment.ts
  • src/lib/eval-document-matching.ts
  • src/lib/extractors/document.ts
  • src/lib/extractors/pdf-extraction-budget.ts
  • src/lib/openai.ts
  • src/lib/rag-extractive-answer.ts
  • src/lib/rag-retrieval-variants.ts
  • src/lib/rag-route-budget.ts
  • src/lib/rag-versioning.ts
  • src/lib/rag.ts
  • src/lib/saved-registry-storage.ts
  • src/lib/search-scope.ts
  • src/lib/semantic-rerank.ts
  • src/lib/supabase/client.tsx
  • src/lib/types.ts
  • tests/answer-ranking.test.ts
  • tests/answer-responsiveness-gate.test.ts
  • tests/api-validation-contract.test.ts
  • tests/clinical-search.test.ts
  • tests/embed-texts-integrity.test.ts
  • tests/eval-retrieval.test.ts
  • tests/eval-utils.test.ts
  • tests/factsheet-save.dom.test.tsx
  • tests/factsheets-data.test.ts
  • tests/offline-release-profile.test.ts
  • tests/openai-cache.test.ts
  • tests/pdf-extraction-budget.test.ts
  • tests/pdf-extractor.test.ts
  • tests/playwright-project-isolation.test.ts
  • tests/private-access-routes.test.ts
  • tests/private-client-auth.test.ts
  • tests/rag-answer-fallback.test.ts
  • tests/rag-document-summary.test.ts
  • tests/rag-route-budget.test.ts
  • tests/retrieval-query-variants.test.ts
  • tests/semantic-rerank.test.ts
  • tests/test-runner-safety.test.ts
  • tests/therapy-compass-artifact-navigation.dom.test.tsx
  • tests/therapy-compass-data-recovery.dom.test.tsx
  • tests/therapy-compass-mode-wiring.test.ts
  • tests/ui-accessibility.spec.ts
  • tests/ui-formulation.spec.ts
  • tests/ui-pwa.spec.ts
  • tests/ui-route-coverage.spec.ts
  • tests/ui-smoke.spec.ts
  • tests/ui-tools.spec.ts
💤 Files with no reviewable changes (2)
  • src/components/clinical-dashboard/global-search-shell.tsx
  • src/components/ClinicalDashboard.tsx

| 2026-07-19 | local branches and worktrees after PRs #905 and #907 | e377ab1aed44d56c303eede80572c1df82ddcd6e | final local branch/worktree cleanup and useful-history preservation | Reduced 153 local branches to `main` plus the two actively edited task branches. Deleted 150 redundant refs using cherry-pick containment, exact merged-PR heads, synthetic no-op merge trees, exact duplicate-head retention, merged-PR commit association, and explicit supersession review. Removed two clean obsolete worktrees and retained only the active auth/Supabase and P2-remediation worktrees for their owners. Archived the final 66 superseded historical heads in a verified 76,395,256-byte Git bundle before ref deletion. PRs #905 and #907 merged through protected `main`; PR #906 subsequently merged through protected `main` and its remote branch was removed. | Fresh fetch/prune and PR inventory; exact-old-value `git update-ref`; `git merge-tree --write-tree`; GraphQL commit-to-PR association for 178 commits; reverse-patch checks against a detached fresh-main worktree; verified bundle `Database-local-refs-before-final-cleanup-20260719-0525.bundle`; final branch/worktree/remote checks. No OpenAI, Supabase, deployment, live clinical, or production-data workflow ran. |
| 2026-07-19 | claude/audit-recent-changes-kde66i (audit-remediation follow-up to the ef042cacd audit row) | see PR head | remediation of the three 2026-07-19 audit findings | Fixed all three findings from the 24-hour merged-window audit. (1) Inert settings honesty (P2): every preference the app does not yet consume — jurisdiction, population, answer style, landing, both home-content toggles, compact citations, and all three notification toggles — now renders an explicit "Saved for later — not active yet" marker in `settings-dialog.tsx`; the functional appearance/density/motion rows stay unmarked. Wiring the preferences into answer generation was deliberately NOT done here (clinical-behavior change requiring governance review); the new dom test documents the contract for flipping a control live. (2) Auth offline resolution (P2/P3): `resolveInitialAuthState` gains `verificationUnavailable`, set from `isAuthRetryableFetchError(getUser().error)`, so an unreachable auth server keeps the stored session signed in while a reachable server that rejects the token still resolves signed_out; server-side bearer validation is unchanged. (3) `/medications` redirect (P3): now preserves the sanitized q/focus/run search context with the same allowlist as the root legacy-mode redirect. | Focused Vitest 30/30 across `settings-inert-preferences.dom` (new), `private-client-auth` (3 new cases), `audit-navigation-auth-regressions` (query-preservation case), `app-preferences`, and `site-map`. Full `verify:cheap` run recorded on the PR. No OpenAI, Supabase, deployment, or provider-backed check ran. |

| 2026-07-25 | codex/fix-p2-audit-20260719 | d581e0af15736ef253b5b0addf43082c6a1ee311 | full branch | PASS | Checks skipped due to active heavy lock from another agent; pushed to CI. Deduplication fix applied. |

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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Do not mark this row PASS while checks are skipped.

The row says checks were skipped and only “pushed to CI”; it does not record a successful CI result. Use a pending status until the exact CI run is green, then update the ledger with that evidence.

Suggested correction
-| 2026-07-25 | codex/fix-p2-audit-20260719 | d581e0af15736ef253b5b0addf43082c6a1ee311 | full branch | PASS | Checks skipped due to active heavy lock from another agent; pushed to CI. Deduplication fix applied. |
+| 2026-07-25 | codex/fix-p2-audit-20260719 | d581e0af15736ef253b5b0addf43082c6a1ee311 | full branch | PENDING CI | Checks skipped due to active heavy lock from another agent; pushed to CI. Deduplication fix applied. |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| 2026-07-25 | codex/fix-p2-audit-20260719 | d581e0af15736ef253b5b0addf43082c6a1ee311 | full branch | PASS | Checks skipped due to active heavy lock from another agent; pushed to CI. Deduplication fix applied. |
| 2026-07-25 | codex/fix-p2-audit-20260719 | d581e0af15736ef253b5b0addf43082c6a1ee311 | full branch | PENDING CI | Checks skipped due to active heavy lock from another agent; pushed to CI. Deduplication fix applied. |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/branch-review-ledger.md` at line 632, Update the ledger row for commit
d581e0af15736ef253b5b0addf43082c6a1ee311 so its status is pending rather than
PASS while checks remain skipped. Only change it to PASS after CI completes
successfully, and include the exact successful CI run evidence in the row.

Comment on lines +148 to +153
try {
return { payload: (await awaitWithCallerSignal(entry.promise, signal)) as T, coalesced };
} finally {
entry.waiters -= 1;
if (entry.waiters === 0 && !entry.settled) entry.controller.abort();
}

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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Both coalescers abort the shared producer while its entry is still published. Eviction happens only in the producer's .finally, so a caller arriving between controller.abort() and that microtask joins a doomed promise and fails with an unrelated caller's AbortError.

  • src/app/api/search/route.ts#L148-L153: delete the key from scopedSearchInflight when waiters === 0 && !settled, before calling entry.controller.abort().
  • src/lib/openai.ts#L666-L674: pass the cache key into awaitInflightEmbedding (or store it on the entry) and delete it from queryEmbeddingInflight before aborting.
📍 Affects 2 files
  • src/app/api/search/route.ts#L148-L153 (this comment)
  • src/lib/openai.ts#L666-L674
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/api/search/route.ts` around lines 148 - 153, The coalescers must
remove an abandoned in-flight entry before aborting its shared producer. In
src/app/api/search/route.ts lines 148-153, update the waiters cleanup in the
shared-entry flow to delete the key from scopedSearchInflight when waiters
reaches zero and the entry is unsettled, then abort the controller. In
src/lib/openai.ts lines 666-674, pass the cache key into awaitInflightEmbedding
or store it on the entry, delete it from queryEmbeddingInflight before aborting,
and preserve existing behavior for settled or still-waited entries.

Comment on lines +63 to +70
return (
Boolean(image.data?.byteLength) &&
typeof image.width === "number" &&
Number.isFinite(image.width) &&
image.width > 0 &&
typeof image.height === "number" &&
Number.isFinite(image.height) &&
image.height > 0

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.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Require integral image dimensions.

extractedImageSchema requires positive integers, but 1.5 × 2 passes this helper and the render-pixel check, then gets persisted as invalid metadata. Require safe integers here and add a fractional-dimension test.

Proposed fix
-    typeof image.width === "number" &&
-    Number.isFinite(image.width) &&
+    Number.isSafeInteger(image.width) &&
     image.width > 0 &&
-    typeof image.height === "number" &&
-    Number.isFinite(image.height) &&
+    Number.isSafeInteger(image.height) &&
     image.height > 0
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return (
Boolean(image.data?.byteLength) &&
typeof image.width === "number" &&
Number.isFinite(image.width) &&
image.width > 0 &&
typeof image.height === "number" &&
Number.isFinite(image.height) &&
image.height > 0
return (
Boolean(image.data?.byteLength) &&
Number.isSafeInteger(image.width) &&
image.width > 0 &&
Number.isSafeInteger(image.height) &&
image.height > 0
🧰 Tools
🪛 ast-grep (0.44.1)

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawn } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/extractors/document.ts` around lines 63 - 70, Update the
image-dimension validation in the helper used by extractedImageSchema to require
positive safe integers for both width and height, replacing finite-number-only
checks while preserving the existing positive and image-data requirements. Add a
test covering fractional dimensions such as 1.5 × 2 and assert they are
rejected.

Comment on lines +1707 to +1715
const clinicalRiskCoverageRequested =
queryClass === "medication_dose_risk" &&
/\b(?:escalat\w*|urgent\w*|red flags?|toxicity|adverse effects?|side effects?)\b/i.test(query);
const distinctAvailableSources = new Set((answer.sources ?? []).map((source) => source.id)).size;
if (broadDocumentCoverageRequested && distinctAvailableSources >= 2 && answer.citations.length < 2) {
return "insufficient_broad_citation_coverage";
}
if (clinicalRiskCoverageRequested && distinctAvailableSources >= 2 && answer.citations.length < 2) {
return "insufficient_clinical_risk_citation_coverage";

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Apply risk-citation coverage after document-title routing.

“When should neuroleptic side effects be escalated?” now classifies as document_lookup, so this condition never runs in production; the test masks that by manually passing medication_dose_risk. A one-citation escalation answer can therefore pass the new safeguard.

  • src/lib/rag-extractive-answer.ts#L1707-L1715: include document-lookup queries with the existing escalation/risk terms in clinicalRiskCoverageRequested.
  • tests/answer-responsiveness-gate.test.ts#L89-L100: derive the query class through the classifier (or add an integration case) and assert the runtime class still requires two citations.
📍 Affects 2 files
  • src/lib/rag-extractive-answer.ts#L1707-L1715 (this comment)
  • tests/answer-responsiveness-gate.test.ts#L89-L100
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/rag-extractive-answer.ts` around lines 1707 - 1715, Expand
clinicalRiskCoverageRequested in rag-extractive-answer.ts to include
document_lookup queries when they contain the existing escalation/risk terms,
while preserving the current medication_dose_risk behavior. In
tests/answer-responsiveness-gate.test.ts lines 89-100, obtain the query class
through the production classifier or add an integration case, and assert that
the runtime classification still requires at least two citations.

Comment on lines +118 to +128
/** Only explicit token/session rejection is evidence that local user data should be cleared. */
export function isDefinitiveAuthValidationError(error: unknown) {
const candidate = error as { status?: unknown; code?: unknown; message?: unknown } | null;
const status = typeof candidate?.status === "number" ? candidate.status : null;
if (status === 400 || status === 401 || status === 403) return true;
const code = typeof candidate?.code === "string" ? candidate.code.toLowerCase() : "";
if (/^(?:bad_jwt|session_not_found|refresh_token_not_found|refresh_token_already_used)$/.test(code)) return true;
const message = typeof candidate?.message === "string" ? candidate.message.toLowerCase() : "";
return /(?:invalid|expired|missing) (?:jwt|token)|session (?:not found|expired)|refresh token (?:not found|invalid)/.test(
message,
);

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files | rg '(^|/)src/lib/supabase/client\.tsx$|package\.json|README|docs/node_modules/next/dist/docs' || true

if [ -f src/lib/supabase/client.tsx ]; then
  echo "== outline =="
  ast-grep outline src/lib/supabase/client.tsx || true
  echo "== relevant sections =="
  nl -ba src/lib/supabase/client.tsx | sed -n '1,260p'
fi

echo "== related error handling/usages =="
rg -n "isDefinitiveAuthValidationError|resolve.*session|clear.*session|refresh_token|getUser|supabase.auth|signout|signOut" -S .

Repository: BigSimmo/Database

Length of output: 1596


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== package versions =="
node - <<'JS'
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
const deps = {...pkg.dependencies, ...pkg.devDependencies};
for (const name of ['`@supabase/supabase-js`', '`@supabase/auth-js`']) {
  console.log(`${name}: ${deps[name]}`);
}
JS

echo "== source summary =="
cat -n src/lib/supabase/client.tsx | sed -n '1,245p'

echo "== related usages =="
rg -n "isDefinitiveAuthValidationError|shouldFailInitialSessionVerification|resolveInitialAuthState|browserSupabaseClient|supabase\.auth|signOut|clearSession|refresh_token|getUser" -S .

echo "== dependency/lock presence =="
ls -la | sed -n '1,120p'
git ls-files | rg '(^|/)(package-lock\.json|pnpm-lock\.yaml|yarn\.lock|bun\.lockb?|node_modules/@supabase|node_modules/next/dist/docs)' || true

Repository: BigSimmo/Database

Length of output: 35328


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== tests around helper =="
cat -n tests/private-client-auth.test.tsx | sed -n '1,150p'

echo "== exact helper tests =="
rg -n "isDefinitiveAuthValidationError|status: 40[013]|status: 401" tests/private-client-auth.test.tsx tests -S

echo "== node_modules `@supabase` auth-js package manifest if present =="
if [ -d node_modules/@supabase/auth-js ]; then
  node - <<'JS'
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('node_modules/@supabase/auth-js/package.json','utf8'));
console.log(pkg.version || '(no version)');
JS
  rg -n "40[013]|bad_jwt|session_not_found|refresh_token_not_found|getUser|retryable|error code|HTTP_ERROR" node_modules/@supabase/auth-js/distnode/node_modules/@supabase/auth-js/dist/esm node_modules/@supabase/auth-js 2>/dev/null | head -n 120 || true
else
  echo "node_modules/@supabase/auth-js not present"
fi

echo "== node_modules `@supabase/ssr` package manifest if present =="
if [ -d node_modules/@supabase/ssr ]; then
  node - <<'JS'
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('node_modules/@supabase/ssr/package.json','utf8'));
console.log(pkg.version || '(no version)');
JS
else
  echo "node_modules/@supabase/ssr not present"
fi

Repository: BigSimmo/Database

Length of output: 248


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate private client auth test =="
git ls-files | rg 'tests/.*(private.*client.*auth|client.*auth).*\.test\.t' || true
git ls-files tests | sed -n '1,180p'

echo "== inspect tests/private-client-auth.test"
file="$(git ls-files tests | rg 'private-client-auth\.test' | head -n1 || true)"
if [ -n "$file" ]; then
  cat -n "$file" | sed -n '1,150p'
fi

echo "== exact helper tests in tracked tests =="
rg -n "isDefinitiveAuthValidationError|shouldFailInitialSessionVerification|status: 40[013]|new AuthRetryableFetchError|bad_jwt|session_not_found" tests -S || true

echo "== node_modules packages =="
if [ -d node_modules/@supabase/auth-js ]; then
  node - <<'JS'
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('node_modules/@supabase/auth-js/package.json','utf8'));
console.log(pkg.version || '(no version)');
JS
  rg -n "40[013]|bad_jwt|session_not_found|refresh_token_not_found|getUser|retryable|HTTP_ERROR" node_modules/@supabase/auth-js 2>/dev/null | head -n 160 || true
else
  echo "node_modules/@supabase/auth-js not present"
fi

if [ -d node_modules/@supabase/ssr ]; then
  node - <<'JS'
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('node_modules/@supabase/ssr/package.json','utf8'));
console.log(pkg.version || '(no version)');
JS
else
  echo "node_modules/@supabase/ssr not present"
fi

Repository: BigSimmo/Database

Length of output: 31276


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== src/lib/supabase/auth.ts relevant section =="
cat -n src/lib/supabase/auth.ts | sed -n '1,180p'

echo "== auth-js errors/fetch around 400/401/403 =="
cat -n node_modules/@supabase/auth-js/src/lib/fetch.ts | sed -n '110,170p'
echo "== deterministic status-only behavior from source text =="
python3 - <<'PY'
cases = [
    {"status": 400},
    {"status": 401},
    {"status": 403},
    {"status": 500},
    {"status": 401, "code": "bad_jwt"},
    {"status": 400, "code": "email_not_confirmed"},
]
for case in cases:
    candidate = case
    status = candidate.get("status") if isinstance(candidate.get("status"), int) else None
    code = candidate.get("code", "").lower() if isinstance(candidate.get("code"), str) else ""
    import re
    message = candidate.get("message", "").lower() if isinstance(candidate.get("message"), str) else ""
    definitely = status in (400, 401, 403) or re.match(r"^(?:bad_jwt|session_not_found|refresh_token_not_found|refresh_token_already_used)$", code) or re.search(r"(?:invalid|expired|missing) (?:jwt|token)|session (?:not found|expired)|refresh token (?:not found|invalid)", message)
    print(f"{case} -> definitive={definitely}")
PY

echo "== fetch wrapper source around AuthApiError construction =="
rg -n "AuthApiError|new Error|return response|status" node_modules/@supabase/auth-js/src/lib/fetch.ts node_modules/@supabase/auth-js/dist -S | sed -n '1,140p'

Repository: BigSimmo/Database

Length of output: 31599


Require an explicit auth error before clearing local session state.

isDefinitiveAuthValidationError() treats any { status: 400/401/403 } as token/session rejection, including { status: 401, message: "JWT expired" } and non-token auth failures like { status: 400, code: "email_not_confirmed" }. Keep only authenticated token/session rejection explicit (auth code/message, with status-contract tests if a bare status is intentional) so non-auth 4xx responses do not sign users out.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/supabase/client.tsx` around lines 118 - 128, Restrict
isDefinitiveAuthValidationError to explicit token/session rejection signals
instead of treating every 400, 401, or 403 status as definitive. Retain the
recognized auth codes and token/session-specific message patterns, and only use
status when paired with evidence that it represents token or session validation;
ensure cases such as email_not_confirmed and unrelated 4xx errors return false.

Comment on lines +187 to +193
if (shouldFailInitialSessionVerification(userResult.error)) {
setSession(null);
setStatus("error");
setNotice(null);
setError("Session could not be verified. Check your connection and retry.");
return;
}

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.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Clear private client state before returning from the fail-closed branch.

This return skips clearPersistedAnswerThread() and clearRecentQueries(), which the normal unauthenticated path invokes at Lines 209-210. Clear the same caches before returning so an unverified session cannot leave private client data available to later UI code.

Proposed fix
        if (shouldFailInitialSessionVerification(userResult.error)) {
          setSession(null);
          setStatus("error");
          setNotice(null);
          setError("Session could not be verified. Check your connection and retry.");
+         clearPersistedAnswerThread();
+         clearRecentQueries();
          return;
        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (shouldFailInitialSessionVerification(userResult.error)) {
setSession(null);
setStatus("error");
setNotice(null);
setError("Session could not be verified. Check your connection and retry.");
return;
}
if (shouldFailInitialSessionVerification(userResult.error)) {
setSession(null);
setStatus("error");
setNotice(null);
setError("Session could not be verified. Check your connection and retry.");
clearPersistedAnswerThread();
clearRecentQueries();
return;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/supabase/client.tsx` around lines 187 - 193, Update the
shouldFailInitialSessionVerification branch in the session verification flow to
invoke clearPersistedAnswerThread() and clearRecentQueries() before returning.
Keep the existing session, status, notice, and error updates unchanged, matching
the normal unauthenticated cleanup path.

Comment on lines +28 to +35
expect(releaseRunner).toContain('NEXT_PUBLIC_SUPABASE_URL: "https://offline.invalid"');
expect(releaseRunner).toContain('NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY: "offline-placeholder"');
expect(releaseRunner).toContain('SUPABASE_SERVICE_ROLE_KEY: "offline-placeholder"');
expect(releaseRunner).toContain('SUPABASE_DB_URL: "postgresql://offline:offline@offline.invalid:5432/offline"');
expect(releaseRunner).not.toContain('"check:production-readiness"');
expect(releaseRunner).not.toContain('"governance:release"');
expect(releaseRunner).not.toContain('"eval:quality:release:offline"');
expect(releaseRunner).toContain('"eval:rag:offline"');

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Cover all newly required offline wiring.

The implementation adds three checks and two cross-tenant environment variables, but this test verifies only eval:rag:offline and the primary Supabase values. Removing any omitted entry would still pass the test.

Proposed additions
+    expect(releaseRunner).toContain('"check:function-grants"');
+    expect(releaseRunner).toContain('"check:owner-scope"');
+    expect(releaseRunner).toContain('"check:rag:fixtures"');
+    expect(releaseRunner).toContain('CROSS_TENANT_SUPABASE_URL: "https://offline.invalid"');
+    expect(releaseRunner).toContain('CROSS_TENANT_SUPABASE_SERVICE_ROLE_KEY: "offline-placeholder"');
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
expect(releaseRunner).toContain('NEXT_PUBLIC_SUPABASE_URL: "https://offline.invalid"');
expect(releaseRunner).toContain('NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY: "offline-placeholder"');
expect(releaseRunner).toContain('SUPABASE_SERVICE_ROLE_KEY: "offline-placeholder"');
expect(releaseRunner).toContain('SUPABASE_DB_URL: "postgresql://offline:offline@offline.invalid:5432/offline"');
expect(releaseRunner).not.toContain('"check:production-readiness"');
expect(releaseRunner).not.toContain('"governance:release"');
expect(releaseRunner).not.toContain('"eval:quality:release:offline"');
expect(releaseRunner).toContain('"eval:rag:offline"');
expect(releaseRunner).toContain('NEXT_PUBLIC_SUPABASE_URL: "https://offline.invalid"');
expect(releaseRunner).toContain('NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY: "offline-placeholder"');
expect(releaseRunner).toContain('SUPABASE_SERVICE_ROLE_KEY: "offline-placeholder"');
expect(releaseRunner).toContain('SUPABASE_DB_URL: "postgresql://offline:offline@offline.invalid:5432/offline"');
expect(releaseRunner).toContain('"check:function-grants"');
expect(releaseRunner).toContain('"check:owner-scope"');
expect(releaseRunner).toContain('"check:rag:fixtures"');
expect(releaseRunner).toContain('CROSS_TENANT_SUPABASE_URL: "https://offline.invalid"');
expect(releaseRunner).toContain('CROSS_TENANT_SUPABASE_SERVICE_ROLE_KEY: "offline-placeholder"');
expect(releaseRunner).not.toContain('"check:production-readiness"');
expect(releaseRunner).not.toContain('"governance:release"');
expect(releaseRunner).not.toContain('"eval:quality:release:offline"');
expect(releaseRunner).toContain('"eval:rag:offline"');
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/offline-release-profile.test.ts` around lines 28 - 35, Expand the
releaseRunner assertions in the offline release profile test to cover all newly
required offline wiring: the three added offline checks and both cross-tenant
environment variables, alongside the existing eval:rag:offline and Supabase
assertions. Ensure each required entry is asserted so removing any one causes
the test to fail.

Comment on lines +158 to +165
it("loads local provider credentials before live-test permission and collection", () => {
const runner = readFileSync(new URL("../scripts/run-live-tests.mjs", import.meta.url), "utf8");
expect(runner).toContain('import nextEnv from "@next/env";');
expect(runner).toContain("const { loadEnvConfig } = nextEnv;");
expect(runner.indexOf("loadEnvConfig(projectRoot);")).toBeLessThan(
runner.indexOf("requireProviderTestPermission();"),
);
});

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make the ordering assertion fail when the load call is absent.

indexOf() returns -1 for a missing loadEnvConfig(projectRoot);; -1 < permissionIndex is true, so this test can pass after the call is removed. Assert both indices are non-negative before comparing them.

Proposed fix
-    expect(runner.indexOf("loadEnvConfig(projectRoot);")).toBeLessThan(
-      runner.indexOf("requireProviderTestPermission();"),
-    );
+    const loadEnvIndex = runner.indexOf("loadEnvConfig(projectRoot);");
+    const permissionIndex = runner.indexOf("requireProviderTestPermission();");
+    expect(loadEnvIndex).toBeGreaterThanOrEqual(0);
+    expect(permissionIndex).toBeGreaterThan(loadEnvIndex);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it("loads local provider credentials before live-test permission and collection", () => {
const runner = readFileSync(new URL("../scripts/run-live-tests.mjs", import.meta.url), "utf8");
expect(runner).toContain('import nextEnv from "@next/env";');
expect(runner).toContain("const { loadEnvConfig } = nextEnv;");
expect(runner.indexOf("loadEnvConfig(projectRoot);")).toBeLessThan(
runner.indexOf("requireProviderTestPermission();"),
);
});
it("loads local provider credentials before live-test permission and collection", () => {
const runner = readFileSync(new URL("../scripts/run-live-tests.mjs", import.meta.url), "utf8");
expect(runner).toContain('import nextEnv from "`@next/env`";');
expect(runner).toContain("const { loadEnvConfig } = nextEnv;");
const loadEnvIndex = runner.indexOf("loadEnvConfig(projectRoot);");
const permissionIndex = runner.indexOf("requireProviderTestPermission();");
expect(loadEnvIndex).toBeGreaterThanOrEqual(0);
expect(permissionIndex).toBeGreaterThan(loadEnvIndex);
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test-runner-safety.test.ts` around lines 158 - 165, Update the ordering
assertion in the test case to store the indices of loadEnvConfig(projectRoot);
and requireProviderTestPermission();, assert both are non-negative, then verify
the load call occurs first. Keep the existing import and destructuring presence
checks unchanged.

Comment on lines +103 to +106
await expect(async () => {
await page.getByRole("link", { name: "Open Worry" }).click();
await expect(page).toHaveURL(/\/formulation\/worry$/, { timeout: 2_000 });
}).toPass({ timeout: 30_000 });

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== repo files sample =="
git ls-files | sed -n '1,120p'

echo
echo "== relevant test snippets =="
for f in tests/ui-formulation.spec.ts tests/ui-smoke.spec.ts tests/ui-tools.spec.ts; do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    wc -l "$f"
    case "$f" in
      tests/ui-formulation.spec.ts) sed -n '85,115p' "$f" | cat -n ;;
      tests/ui-smoke.spec.ts) sed -n '3248,3270p' "$f" | cat -n ;;
      tests/ui-tools.spec.ts)
        sed -n '358,382p' "$f" | cat -n
        sed -n '1612,1638p' "$f" | cat -n
        ;;
    esac
  else
    echo "missing $f"
  fi
done

echo
echo "== Playwright toPass usage context =="
rg -n "toPass|getByRole\\(\"link\"|Images|application-row-medication-prescribing|detailsButton|toHaveURL|toBeVisible" tests ui src app package.json -S || true

echo
echo "== Playwright version =="
if [ -f package.json ]; then node -e "const p=require('./package.json'); console.log(JSON.stringify({devDependencies: p.devDependencies && p.devDependencies['`@playwright/test`'], dependencies:p.dependencies && p.dependencies['`@playwright/test`']}, null, 2))" ; fi

Repository: BigSimmo/Database

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== package metadata =="
node -e 'const p=require("./package.json"); console.log(JSON.stringify({scripts:p.scripts, dependencies:p.dependencies && p.dependencies["`@playwright/test`"], devDependencies:p.devDependencies && p.devDependencies["`@playwright/test`"]}, null, 2))'

echo
echo "== Playwright docs availability =="
printf '\n'
for d in node_modules node_modules/next 2>/dev/null; do
  [ -d "$d" ] && echo "exists $d"
done
fd -a . node_modules | sed -n '1,80p' 2>/dev/null || true
rg -n "toHaveURL|toBeVisible|toPass" tests/ui-formulation.spec.ts tests/ui-smoke.spec.ts tests/ui-tools.spec.ts -C 2 || true

echo
echo "== focused local test snippets =="
python3 - <<'PY'
from pathlib import Path
for path in ["tests/ui-formulation.spec.ts","tests/ui-smoke.spec.ts","tests/ui-tools.spec.ts"]:
    p=Path(path)
    if not p.exists(): continue
    lines=p.read_text(encoding="utf-8").splitlines()
    print(f"--- {path} ---")
    for i,l in enumerate(lines,1):
        if "toPass" in l:
            start=max(1,i-4); end=min(len(lines),i+3)
            print(f"{path}:{start}-{end}")
            for n in range(start,end+1):
                print(f"{n:4d}: {lines[n-1]}")
PY

Repository: BigSimmo/Database

Length of output: 14398


🌐 Web query:

Playwright expect toPass callback side effects idempotent documentation

💡 Result:

In Playwright, the expect.toPass method repeatedly executes a callback function until the assertions within it pass or the specified timeout is reached [1][2]. Because this mechanism retries the entire code block upon any failure, it is critical that the callback is idempotent [3][4]. If your callback contains side effects—such as clicking buttons, making API calls, or performing database updates—those actions will be re-executed on every failed attempt until success or timeout [3][4]. If these actions are not idempotent (e.g., clicking a 'Submit' button that creates a new record), you may inadvertently cause duplicate entries or corrupted state in your application during the retry process [3][4]. Key considerations for using expect.toPass: 1. Idempotency: Ensure all code inside the callback can be safely executed multiple times without adverse side effects [3][4]. 2. Default Behavior: By default, toPass has a timeout of 0 and does not respect global expect timeouts; it will continue retrying until the test-level deadline is reached unless an explicit timeout is provided [1][3][2]. 3. Execution Scope: The entire block is treated as a single unit of work. If an assertion fails, the remaining code in that attempt is skipped, and the entire callback is re-run from the beginning [3][4]. 4. Best Practice: Prefer Playwright's built-in auto-retrying assertions for single-locator checks, as they are more efficient and safer than manual polling mechanisms [1][5]. Use expect.toPass primarily for validating complex, multi-step conditions or eventually-consistent states where multiple actions must align simultaneously [4][5].

Citations:


Do not retry non-idempotent UI actions inside toPass.

expect(...).toPass(...) reruns the whole callback on every failed assertion, so these actions can run multiple times if the assertion lags behind the UI state. Move the click/closure action outside, then retry only the state assertion.

  • tests/ui-formulation.spec.ts#L103-L106: click once, retry toHaveURL.
  • tests/ui-smoke.spec.ts#L3260-L3263: click the Images navigation once, retry images open property.
  • tests/ui-tools.spec.ts#L371-L374: click the medication row once, retry sheet visibility.
  • tests/ui-tools.spec.ts#L1626-L1629: click the details button once, retry dialog visibility.
📍 Affects 3 files
  • tests/ui-formulation.spec.ts#L103-L106 (this comment)
  • tests/ui-smoke.spec.ts#L3260-L3263
  • tests/ui-tools.spec.ts#L371-L374
  • tests/ui-tools.spec.ts#L1626-L1629
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/ui-formulation.spec.ts` around lines 103 - 106, Move each
non-idempotent click outside the retry callback: in tests/ui-formulation.spec.ts
lines 103-106 click the “Open Worry” link once, then retry only the URL
assertion; in tests/ui-smoke.spec.ts lines 3260-3263 click Images navigation
once, then retry only the images open-state assertion; in tests/ui-tools.spec.ts
lines 371-374 click the medication row once, then retry only sheet visibility;
and in tests/ui-tools.spec.ts lines 1626-1629 click the details button once,
then retry only dialog visibility.

Comment on lines +338 to +341
await expect(async () => {
await mixedFeatures.click();
await expect(mixedFeatures).toHaveAttribute("aria-pressed", "true", { timeout: 1_000 });
}).toPass({ timeout: 10_000 });

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.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Retrying a toggle click can flip the state back and deadlock the toPass block.

If the click succeeds but aria-pressed isn't observed within 1s, the retry clicks the same toggle again and sets it back to false, so subsequent attempts alternate and the 10s budget expires. Make the retry idempotent.

🐛 Suggested fix
         await expect(async () => {
-          await mixedFeatures.click();
+          if ((await mixedFeatures.getAttribute("aria-pressed")) !== "true") await mixedFeatures.click();
           await expect(mixedFeatures).toHaveAttribute("aria-pressed", "true", { timeout: 1_000 });
         }).toPass({ timeout: 10_000 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
await expect(async () => {
await mixedFeatures.click();
await expect(mixedFeatures).toHaveAttribute("aria-pressed", "true", { timeout: 1_000 });
}).toPass({ timeout: 10_000 });
await expect(async () => {
if ((await mixedFeatures.getAttribute("aria-pressed")) !== "true") await mixedFeatures.click();
await expect(mixedFeatures).toHaveAttribute("aria-pressed", "true", { timeout: 1_000 });
}).toPass({ timeout: 10_000 });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/ui-route-coverage.spec.ts` around lines 338 - 341, Make the
mixedFeatures toggle assertion retry idempotent: before clicking within the
toPass callback, check its aria-pressed state and click only when it is not
already true. Preserve the existing 1-second attribute wait and 10-second retry
timeout.

BigSimmo added a commit that referenced this pull request Jul 25, 2026
Append ledger rows for the Bugbot/regression review of PR #1196 and the
main fix-forward in PR #1212.
@cursor

cursor Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Closeout: superseded by #913 / current main (not mergeable)

Closing after a content review of tip 6d59b038 vs origin/main.

Why close (not sync)

  • Same July-19 P2 remediation family already landed in Fix repository regression findings #913 (01040d2c, merged 2026-07-19). Overlap covers the remediation surface (search coalesce, OpenAI cancel, PDF budgets, factsheet save, Therapy index, auth durability, offline/CI wiring).
  • Tip is ~680 commits behind and GitHub CONFLICTING (~23 files). A sync would mostly re-litigate conflicts against newer main behavior, not recover unique product value.
  • Several tip deltas are regressions vs main, including:
    • Documents API: tip drops { administrator: true } on /api/documents/[id] (main still requires it).
    • Factsheets / Therapy / extractors / RAG: main is ahead (governance notice, deferred Therapy search, PDF/DOCX budgets, src/lib/rag/* moves, embedding concurrency, semantic-rerank clamping).
    • Offline release helper: tip restores a credential-shaped placeholder URL that main already made GitGuardian-safe.

What remains (live fix-forward)

Confirmed cancel-coalesce poison + fractional PDF pixel budget defects still exist on current main. Those are fixed in draft #1212 (green required CI): #1212

Branch hygiene

Remote branch codex/fix-p2-audit-20260719 left in place for now (no force-delete). Safe to delete after this closeout is accepted.

@BigSimmo BigSimmo closed this Jul 25, 2026
BigSimmo added a commit that referenced this pull request Jul 25, 2026
Close without merge: tip is stale/conflicting and the remediation
family already landed on main; live coalesce/PDF fixes stay in #1212.
cursor Bot pushed a commit that referenced this pull request Jul 25, 2026
…1215)

* test(#30): generalize the alias-slot disjointness guard

The existing contracts pin the admission/discharge pair by name, so a
future multi-slot case -- or a widening of a different alias tier --
could reintroduce the #30 defect without any test going red.

Drive both invariants off the real eval cases instead: no expectation
of a multi-slot case may share alias values with another slot of the
same case, and no single document may satisfy every slot of any
multi-slot case. The second holds independently of the alias tables,
so it stays meaningful if the tables are widened again.

RAG impact: no retrieval behaviour change -- test-only.

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

* docs(issues): open #81 for the PR #1196 alias conflict

PR #1196 re-adds two admission-to-discharge titles to the wide-tier
AdmissionCommunityPts list, which would revert the #30 tightening. Its
other conflicts include protected RAG surfaces, so record how to
reconcile it rather than leaving that in chat context.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
BigSimmo added a commit that referenced this pull request Jul 25, 2026
* issues: close #81 — PR #1196 closed, alias regression now contract-guarded

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

* chore: re-run required checks after bot branch sync

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
BigSimmo added a commit that referenced this pull request Jul 26, 2026
…1212)

* fix: avoid coalesce poison after last-waiter abort

Delete dying search/embedding inflight map entries when the last waiter disconnects, and ceil fractional PDF render dimensions before the safe-integer pixel budget check.

* docs: record PR #1196 review and coalesce fix-forward

Append ledger rows for the Bugbot/regression review of PR #1196 and the
main fix-forward in PR #1212.

* docs: record PR #1196 closeout as superseded by #913

Close without merge: tip is stale/conflicting and the remediation
family already landed on main; live coalesce/PDF fixes stay in #1212.

* chore: retrigger ci after branch sync

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

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant