Skip to content

perf: cut fixed per-request round trips and add preamble Server-Timing - #1376

Merged
BigSimmo merged 13 commits into
mainfrom
claude/latency-fixes-2026-07-29
Jul 29, 2026
Merged

perf: cut fixed per-request round trips and add preamble Server-Timing#1376
BigSimmo merged 13 commits into
mainfrom
claude/latency-fixes-2026-07-29

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Summary

Five latency fixes from the 2026-07-28 audit (docs/audit/latency-audit-2026-07-28.md), rebased fresh on main. Supersedes #1312, which was closed because it bundled an index migration without synchronized schema.sql/drift proof — that review was right, and the migration is split out of this change (tracked as #102, to land in one operator window that does apply → mirror → register → regenerate together).

RAG impact: no retrieval behaviour change — the only src/lib/rag/** edit defers a process-local cache write off the response path. No scoring, ordering, selection, alias, or citation logic is touched, and the mid-request staleness guard that discards the write when the corpus moves is preserved.

  • Server-Timing where it was missing. /api/answer/stream emitted none at all, and stream/route.ts:262 confirms it is the route the UI actually uses — the largest measurement gap in the repo. Adds auth/ratelimit there and to /api/search, plus scope on /api/answer. On a streaming route only pre-flush stages can reach a header; routing in-stream stages through the SSE contract would put instrumentation inside a governed clinical payload, so they are deliberately omitted.
  • Shared-cache hits no longer await the cache write (rag.ts:3234). setCachedAnswer forces an uncached documents read, so the fastest path in the system paid a round trip before responding. Deferred, not dropped.
  • /api/answer overlaps scope resolution with the rate-limit RPC and aborts it on deny, so a throttled caller still costs nothing. Threading the signal also fixes scope queries never receiving .abortSignal().
  • document_table_facts: three select("*") narrowed to explicit projections, keeping the generated search_tsv and owner_id off the wire. The PATCH response shape is unchanged — the projection matches the TableFactRow DTO field for field.
  • 10 of 11 ssr:false dashboard surfaces had no loading fallback and rendered nothing between HTML arrival and chunk execution; all now use the shared LoadingPanel (role="status" + accessible name). Adds preconnect/dns-prefetch for the Supabase origin, which AuthProvider contacts on mount with no connection warm-up.

Four planned fixes were retired during verification rather than shipped — an in-process auth memo (would dedupe nothing), a batched rate-limit RPC (needs a migration first), a differential_records bound (push-down is a no-op against the check constraint), and 8 further cache-write deferrals (change abort semantics and widen a post-await clone window). Each is recorded in the report with its evidence.

Ledger #098#105 record the 19 findings not addressed here.

Verification

  • npm run verify:pr-localTest Files 422 passed (422) · Tests 4272 passed | 3 skipped (4275), on a fresh npm ci against this base
  • npm run verify:ui — UI verification not run: the cross-worktree heavy lock was contended throughout; the client changes are a loading fallback and two resource hints, both zero-payload. Worth running before merge.
  • Retrieval/ranking evals not required: no retrieval, ranking, selection, chunking, or scoring behaviour changed (see RAG impact above).

Risk and rollout

  • Risk: Low. The cache-write deferral is the only behavioural change and is confined to cache timing — it cannot alter a score, ordering, selection, or citation for any request. The scope/rate-limit overlap is bounded by an abort on deny.
  • Rollback: Single commit; revert cleanly.
  • Provider or production effects: None. No migration, no schema change, no provider call.

Clinical Governance Preflight

  • Source-backed claims still require linked source verification before clinical use
  • No patient-identifiable document workflow was introduced or expanded
  • Supabase target remains Clinical KB Database (sjrfecxgysukkwxsowpy) — unchanged, no Supabase config touched
  • Service-role keys and private document access remain server-only — the layout now reads process.env.NEXT_PUBLIC_SUPABASE_URL directly rather than the server-only env contract, keeping it out of the client module graph (tests/client-secret-surface.test.ts guards this)
  • 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 checked — no clinical decision-support behaviour changed

Notes

Server-Timing emits durations only, never query text or identifiers — see the trust-boundary constraint in src/lib/server-timing.ts.

Summary by CodeRabbit

  • New Features

    • Added server timing information to answer, streaming, and search requests for improved performance visibility.
    • Added connection hints to help pages load faster.
    • Added loading placeholders for dynamically loaded dashboard and administration tools.
  • Bug Fixes

    • Prevented outdated cached answers from being restored after underlying data changes.
    • Ensured rate-limit decisions occur before scope processing.
  • Documentation

    • Added a latency audit and updated related documentation, issue tracking, and review records.

Five latency fixes from the 2026-07-28 audit (docs/audit/latency-audit-2026-07-28.md).
Rebased fresh on main; the index migration that sank PR #1312 is split out.

RAG impact: no retrieval behaviour change — the only src/lib/rag/** edit defers a
process-local cache write off the response path. No scoring, ordering, selection,
alias, or citation logic is touched, and the mid-request staleness guard that
discards the write when the corpus moves is preserved.

- Server-Timing: /api/answer/stream emitted none at all, and stream/route.ts:262
  confirms it is the route the UI actually uses. Adds auth/ratelimit there and to
  /api/search, plus scope on /api/answer. On a streaming route only pre-flush
  stages can reach a header; routing in-stream stages through the SSE contract
  would put instrumentation inside a governed clinical payload.
- Shared-cache hits no longer await the cache write. setCachedAnswer forces an
  uncached documents read, so the fastest path in the system paid a round trip
  before responding. Deferred, not dropped.
- /api/answer overlaps scope resolution with the rate-limit RPC and aborts it on
  deny, so a throttled caller still costs nothing. Threading the signal also
  fixes scope queries never receiving .abortSignal().
- document_table_facts: three select("*") narrowed to explicit projections,
  keeping the generated search_tsv and owner_id off the wire. The PATCH response
  is unchanged — the projection matches the TableFactRow DTO field for field.
- 10 of 11 ssr:false dashboard surfaces had no loading fallback and rendered
  nothing between HTML arrival and chunk execution; all now use the shared
  LoadingPanel. Adds preconnect/dns-prefetch for the Supabase origin, which
  AuthProvider contacts on mount with no connection warm-up.

Ledger #98-#105 record the 19 findings not addressed here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@supabase

supabase Bot commented Jul 29, 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 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 29 minutes

Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: cbfae93f-1e14-4be8-96d6-3d802b975c61

📥 Commits

Reviewing files that changed from the base of the PR and between a6cb463 and 823a67f.

📒 Files selected for processing (9)
  • docs/audit/latency-audit-2026-07-28.md
  • docs/branch-review-ledger.md
  • docs/outstanding-issues.md
  • docs/rag-behaviour/refuted-approaches.md
  • src/app/api/answer/route.ts
  • src/app/api/answer/stream/route.ts
  • src/lib/rag/rag-cache.ts
  • tests/private-rag-access.test.ts
  • tests/rag-cache-invalidation.test.ts
📝 Walkthrough

Walkthrough

The PR adds a dated latency audit and related issue records, instruments timing for answer and search APIs, narrows table-facts projections, protects deferred RAG cache writes, and adds Supabase connection hints plus loading fallbacks for client-only surfaces.

Changes

Latency audit and performance changes

Layer / File(s) Summary
Latency audit records
docs/audit/latency-audit-2026-07-28.md, docs/README.md, docs/codebase-index.md, docs/outstanding-issues.md, docs/branch-review-ledger.md
The audit records latency findings, guardrails, applied and retired remediations, verification methods, limitations, and corresponding documentation and ledger updates.
Server timing and request orchestration
src/lib/server-timing.ts, src/app/api/answer/route.ts, src/app/api/answer/stream/route.ts, src/app/api/search/route.ts, tests/private-rag-access.test.ts
Answer, streamed answer, and search routes emit preamble timing data; answer scope resolution receives disconnect cancellation and runs after rate-limit admission, with regression coverage.
Database projections and cache writes
src/app/api/documents/[id]/table-facts/route.ts, src/lib/document-detail.ts, src/lib/rag/rag-cache.ts, src/lib/rag/rag.ts, tests/rag-cache-invalidation.test.ts
Table-facts queries use explicit projections, while deferred shared-answer cache writes become non-blocking and are discarded when invalidation occurs during version lookup.
Client connection and loading states
src/app/layout.tsx, src/components/clinical-dashboard/clinical-dashboard-lazy.tsx, src/components/clinical-dashboard/dashboard-nav.tsx
The layout conditionally renders Supabase preconnect and dns-prefetch hints, and client-only dynamic surfaces render labeled LoadingPanel fallbacks.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant AnswerRoute
  participant RateLimiter
  participant ScopeResolution
  participant ServerTiming
  Client->>AnswerRoute: Submit answer request
  AnswerRoute->>RateLimiter: Authenticate and consume rate limit
  RateLimiter-->>AnswerRoute: Return admission result and duration
  AnswerRoute->>ScopeResolution: Resolve scope with request signal
  ScopeResolution-->>AnswerRoute: Return scope or cancellation
  AnswerRoute->>ServerTiming: Build timing header
  ServerTiming-->>Client: Return response with Server-Timing
Loading

Possibly related PRs

Suggested reviewers: cursoragent

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.81% 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
Title check ✅ Passed The title is concise and accurately summarizes the main performance and Server-Timing changes.
Description check ✅ Passed The description follows the template with Summary, Verification, Risk and rollout, Clinical Governance Preflight, and Notes, including the pending UI check.
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.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch

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

@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: c0b4844e6a

ℹ️ 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/app/api/answer/route.ts Outdated
@BigSimmo

Copy link
Copy Markdown
Owner Author

@codex resolve actionable Codex review findings for this pull request and current head using the repository instructions. This is the pull request's single automatic repair pass: do not perform a fresh review, create new standalone findings, or request another review. Work only the existing unresolved Codex threads on the current head. Always fix P0 and P1 findings. For P2 and lower findings, fix only clear, scoped, low-risk issues; otherwise disposition them with a concise reason. After fixing or dispositioning a thread, reply in that thread with as the first line, followed by a concise summary; that marker authorizes the workflow to close that exact thread. If human input or new authorization is required, do not use the marker and leave the thread open with the blocker. Finish only after every actionable thread is fixed or dispositioned and closed, or explicitly left open for a human decision. Do not update the branch from main, address unrelated reviews, broaden scope, or create more than one scoped fix commit. Do not use external APIs, paid services, credentials, dependency changes, or broad refactors unless explicitly authorized. Add targeted tests where behavior changes and run the narrowest relevant validation.

@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: c0b4844e6a

ℹ️ 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/rag/rag.ts
Comment thread src/app/api/answer/route.ts Outdated
@github-actions

Copy link
Copy Markdown
Contributor

CI triage

CI failed on this PR. Automated classification of the 2 failed job(s):

  • Static PR checksneeds investigation: inspect the failing step and uploaded diagnostics; rerun only after classifying the cause.
  • PR requiredneeds investigation: inspect the failing step and uploaded diagnostics; rerun only after classifying the cause.

Compared with main CI run #6447 (success).

Classification is evidence routing, not permission to ignore a failure. Exact quarantined Playwright identities remain governed by the flake ledger.

@BigSimmo
BigSimmo enabled auto-merge (squash) July 29, 2026 04:04
@BigSimmo

Copy link
Copy Markdown
Owner Author

@cursoragent please resolve failing CI testing and do bugbot and resolve comments and merge conflicts

@cursor

cursor Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Taking a look!

Open in Web Open in Cursor 

@BigSimmo

Copy link
Copy Markdown
Owner Author

@cursoragent Inspect the PR and repository state, identify the root cause of every failing CI check, unresolved review comment, merge conflict, and related defect. Apply the smallest robust fix using existing project patterns and best practices, without unrelated changes. Run Bugbot and all relevant lint, type-check, test, build, and CI-equivalent commands, fix any issues found, then resolve only the review comments that are fully addressed. Summarise the root causes, changes made, validation results, and any remaining blockers.

@cursor

cursor Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

PR #1376 is mergeable with 0 unresolved review threads. Required static CI is green; only Production UI (Playwright) was still in progress at last poll, which keeps the aggregate BLOCKED.

Root causes found

Area Root cause
Static PR / PR required docs:check-links failed on a backtick path to unauthored supabase/migrations/20260728000000_…sql in #102. Follow-up failure: Prettier on 3 edited files.
Codex P1 (scope) Scope overlapped the rate-limit RPC, so 429 paths could still start document/label enumeration.
Codex P1 (cache) Deferred setCachedAnswer could repopulate after invalidateRagCachesForOwner because review/table-fact mutations don’t bump the documents indexing stamp.
GitHub DIRTY Staleness vs advancing main (merge-tree clean), not a content conflict.
Bugbot P2 (follow-up) Temporary await of shared-cache I/O put cold generation on the rag_response_cache path.

Changes made

  • Admit-before-scope on /api/answer; abort signal on stream scope
  • Owner-scoped invalidation epochs + post-commit shared-row cleanup; shared promote fire-and-forget again
  • Empty-scope Server-Timing; docs-link + Prettier CI fixes
  • Merged origin/main (incl. #1378); resolved all review threads

Validation

  • Local: verify:pr-local 422 files / 4274 tests pass; typecheck / lint pass; focused cache/access tests 15/15
  • Hosted: Static PR, unit coverage, build, migration replay, CircleCI verify, Semgrep/Gitleaks/PR policy — pass
  • Bugbot: prior P1s hold; cold-path shared-await P2 fixed

Remaining blockers

  1. Production UI still running Chromium journeys → PR required / merge state stay BLOCKED until it finishes
  2. verify:ui not run locally (heavy-lock contention noted in the PR)
  3. PR body is stale (still says scope “overlaps” the limiter) — not agent-managed, so it couldn’t be updated via tooling; worth a manual edit before merge

RAG impact: cache timing/invalidation only — no retrieval scoring, ordering, or selection changes.

Open in Web Open in Cursor 

@BigSimmo

Copy link
Copy Markdown
Owner Author

@codex review PR

@BigSimmo

Copy link
Copy Markdown
Owner Author

@CodeRabbit review PR

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

@BigSimmo, I’ll review PR #1376.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Keep resolveSearchScope behind the answer rate limiter so denied callers
cannot enumerate documents/labels, and discard deferred shared-cache
promotions that resume after invalidateRagCachesForOwner via an
invalidation epoch. Drop the outstanding-issues reference to the
unauthored L2-3 migration path that broke docs:check-links.

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

@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: c0b4844e6a

ℹ️ 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/app/api/answer/route.ts Outdated
cursoragent and others added 3 commits July 29, 2026 04:12
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>

@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: 6

🧹 Nitpick comments (2)
tests/rag-cache-invalidation.test.ts (1)

89-157: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Assert that the shared-cache promotion is discarded too.

This test only calls getCachedAnswer, which exercises the process-local cache. It can pass while the deferred promotion still repopulates rag_response_cache; capture the shared write/delete calls or perform a shared-cache read and assert that no stale row remains.

🤖 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/rag-cache-invalidation.test.ts` around lines 89 - 157, The test around
setCachedAnswer and invalidateRagCachesForOwner must also verify the shared
rag_response_cache is not repopulated with the stale answer. Capture the mocked
shared-cache insert/delete calls or read the shared cache after awaiting
promotion, and assert that no stale row remains in addition to the existing
getCachedAnswer assertion.
src/lib/rag/rag-cache.ts (1)

33-40: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Scope the invalidation epoch by owner.

ragCacheInvalidationEpoch is global while invalidateRagCachesForOwner(ownerId) is owner-scoped. A mutation for one tenant cancels unrelated tenants’ deferred promotions, causing avoidable cache misses under concurrent traffic. Use owner-specific generations plus a separate all-owner generation.

Also applies to: 676-678

🤖 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/rag-cache.ts` around lines 33 - 40, Replace the global
ragCacheInvalidationEpoch with owner-scoped invalidation generations and a
separate all-owner generation. Update invalidateRagCachesForOwner and all
deferred setCachedAnswer staleness checks to capture and compare the requesting
owner’s generation together with the all-owner generation, so invalidating one
owner does not cancel unrelated owners’ promotions.
🤖 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/audit/latency-audit-2026-07-28.md`:
- Line 309: Remove the leading and trailing spaces inside the inline code span
in the RAG impact statement, while preserving the displayed text and surrounding
Markdown.

In `@docs/outstanding-issues.md`:
- Line 143: Update the `#099` row in docs/outstanding-issues.md to state that
admission via the rate-limit RPC occurs before scope resolution, with scope
resolution using the request abort signal. Remove the claim that scope
resolution overlapped with the rate-limit RPC, while preserving the remaining
status and cross-references.

In `@src/app/api/answer/route.ts`:
- Around line 100-108: Update the empty-scope success return in the answer route
to construct and attach the timing headers for auth, rate limit, scope
resolution, and total duration before returning. Keep the same headers for the
existing non-empty response path, reusing the timing values from the surrounding
scope and request timing logic.

In `@src/app/api/answer/stream/route.ts`:
- Around line 337-343: Update the scope-resolution flow around
resolveSearchScope to accept and forward streamSignal as its abort signal,
including through resolveRetrievalAccessScope if that wrapper is used before
streamAnswer. Preserve the existing scope behavior while ensuring client
cancellation aborts paginated scope queries.

In `@src/lib/rag/rag-cache.ts`:
- Around line 186-204: Close the race between the epoch check and shared-cache
commit in the cache-write flow around setSharedCachedAnswer and
invalidateRagCachesForOwner. Coordinate invalidation with the shared write using
a shared invalidation generation, or perform conditional post-write cleanup, so
any write overlapping invalidation cannot leave stale data. Add a race test that
interleaves invalidation with the shared write and verifies the stale answer is
absent.

In `@tests/private-rag-access.test.ts`:
- Around line 477-479: Update the rate-limit test around resolveSearchScope and
consumeSubjectApiRateLimit so the mock resolves a limiterStarted promise when
limiter entry begins; await that promise before asserting resolveSearchScope has
not been called, replacing the unreliable single Promise.resolve
synchronization.

---

Nitpick comments:
In `@src/lib/rag/rag-cache.ts`:
- Around line 33-40: Replace the global ragCacheInvalidationEpoch with
owner-scoped invalidation generations and a separate all-owner generation.
Update invalidateRagCachesForOwner and all deferred setCachedAnswer staleness
checks to capture and compare the requesting owner’s generation together with
the all-owner generation, so invalidating one owner does not cancel unrelated
owners’ promotions.

In `@tests/rag-cache-invalidation.test.ts`:
- Around line 89-157: The test around setCachedAnswer and
invalidateRagCachesForOwner must also verify the shared rag_response_cache is
not repopulated with the stale answer. Capture the mocked shared-cache
insert/delete calls or read the shared cache after awaiting promotion, and
assert that no stale row remains in addition to the existing getCachedAnswer
assertion.
🪄 Autofix (Beta)

❌ Autofix failed (check again to retry)

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

Run ID: 46c593e2-2908-44ab-b4fa-cb0c044d1c47

📥 Commits

Reviewing files that changed from the base of the PR and between 0adad0a and a6cb463.

📒 Files selected for processing (18)
  • docs/README.md
  • docs/audit/latency-audit-2026-07-28.md
  • docs/branch-review-ledger.md
  • docs/codebase-index.md
  • docs/outstanding-issues.md
  • src/app/api/answer/route.ts
  • src/app/api/answer/stream/route.ts
  • src/app/api/documents/[id]/table-facts/route.ts
  • src/app/api/search/route.ts
  • src/app/layout.tsx
  • src/components/clinical-dashboard/clinical-dashboard-lazy.tsx
  • src/components/clinical-dashboard/dashboard-nav.tsx
  • src/lib/document-detail.ts
  • src/lib/rag/rag-cache.ts
  • src/lib/rag/rag.ts
  • src/lib/server-timing.ts
  • tests/private-rag-access.test.ts
  • tests/rag-cache-invalidation.test.ts

Comment thread docs/audit/latency-audit-2026-07-28.md Outdated
Comment thread docs/outstanding-issues.md
Comment thread src/app/api/answer/route.ts
Comment thread src/app/api/answer/stream/route.ts
Comment thread src/lib/rag/rag-cache.ts
Comment thread tests/private-rag-access.test.ts Outdated
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

⚠️ Branch updated during autofix.

The branch was updated while autofix was in progress. Please try again.

@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: d6a30aedf6

ℹ️ 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/rag/rag-cache.ts Outdated
cursoragent and others added 3 commits July 29, 2026 04:30
Scope invalidation epochs per owner, await shared-cache promotion with
post-write cleanup on raced invalidation, thread the stream abort signal
into resolveSearchScope, emit Server-Timing on empty-scope answers, and
tighten the rate-limit gating test synchronization.

Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
…BigSimmo/Database into claude/latency-fixes-2026-07-29

Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
@BigSimmo
BigSimmo disabled auto-merge July 29, 2026 04:34
…ation

Corrections found while planning the remaining work — each was verified at the
cited line, and each REDUCES the work the audit implied:

- L3-3 was wrong twice: the Therapy Compass index and full payloads are mutually
  exclusive (mode-home first paint is 707 KB, not 3.16 MB), and the index split
  the finding implied was missing already ships via build-therapies-index.mjs
  with its own gate. The real residual is only the missing /public Cache-Control
  and the proxy matcher not excluding .json.
- L3-6(b) overstated a 3-hop first-paint waterfall. The 4-way fan-out is gated on
  drawer visibility, and readLocalProjectIdentity is a guaranteed no-op off
  localhost. Real production win is ~1 hop.
- L3-6(c) proposed batching as new work, but /api/images/signed-urls already
  exists with zero call sites. Records the hard constraint that signed URLs must
  never reach SearchResult.images[].signed_url, because rag-cache persists
  results into a shared cross-owner cache table.
- The measurement plan named test-cache-path.mjs as an offline fixture; it is the
  vitest/tsc cache-directory helper. Names the real offline surface and the
  vi.doMock seam instead.

Also adds Refutation 6 to docs/rag-behaviour/refuted-approaches.md: token
streaming as a latency fix. Unlike Refutations 1-5 this was not re-attempted —
it is recorded because the defect it would "fix" is real and visible, so the
wrong fix is the one a future task reaches for first. answer-stream-contract.ts
removed token/revising as a clinical-safety control; raw tokens bypass the
numeric-faithfulness gate. Only admissible shape is progressive disclosure of
already-verified sections over the existing progress event.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@BigSimmo
BigSimmo enabled auto-merge (squash) July 29, 2026 04:38
cursoragent and others added 2 commits July 29, 2026 04:41
Awaiting replaceSharedCacheRow inside setCachedAnswer blocked every
generation path on a Sydney DB round trip. Restore fire-and-forget
shared promote while keeping owner-scoped epoch cleanup after commit.

Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
…BigSimmo/Database into claude/latency-fixes-2026-07-29

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

@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: 9510a05d95

ℹ️ 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/rag/rag-cache.ts Outdated
…2026-07-29

Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
@BigSimmo
BigSimmo merged commit 96a4c76 into main Jul 29, 2026
27 checks passed
@BigSimmo
BigSimmo deleted the claude/latency-fixes-2026-07-29 branch July 29, 2026 04:58
BigSimmo pushed a commit that referenced this pull request Jul 29, 2026
Following up my own reply on the offline-harness thread: restoring the script
names was only half the fix. #1376 had added real implementation guidance to
measurement-plan item 2 on main, and re-landing this audit overwrote it. That
detail is what makes #98 buildable rather than merely described.

Restored, with every reference verified rather than copied:
- scripts/fixtures/rag-offline-contract-tests.json — exists.
- The seam: vi.doMock("@/lib/supabase/admin", ...) before importing rag.ts.
  main cited tests/rag-offline-answer.test.ts:79-108; the admin doMock is
  actually at :102 (a second doMock at :108), so this cites :102.
- Why it is the only zero-production-edit seam: rag.ts and rag-cache.ts
  construct their client inline per phase rather than accepting an injected
  one — verified, 4 and 8 createAdminClient() call sites respectively.
- Ship as a vitest suite, not an npm script, so it rides npm run test instead
  of forcing a ci.yml edit via check-gate-manifest.mjs.

Kept from this branch: the admission-cost direction guard, which main's copy
lacks — any generalisation must assert scope starts only after the limiter
admits and a denial dispatches zero scope queries, never the reverse.

Also records why test-cache-path.mjs / check-rag-fixtures.mjs were the wrong
reference, so the substitution is not repeated.

Verification: verify:cheap exit 0 (427 files, 4386 passed / 4 skipped).

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants