Skip to content

Ingestion hardening: Retry route and heartbeat (part a) - #3707

Merged
gitar-bot[bot] merged 14 commits into
mainfrom
claude/project-thread-8liwcc-ingest-code
Oct 21, 2026
Merged

gitar-bot[bot] merged 14 commits into
mainfrom
claude/project-thread-8liwcc-ingest-code

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Oct 21, 2026 •

Copy link
Copy Markdown
Owner

Before: a single missed worker check-in threw away a whole job's paid work. The admin Retry button used the older, less careful recovery step. A job stranded on its final attempt could hide behind old failed jobs in the health probe.

After:

  • ING-A-03 The heartbeat tolerates transient failures for a set tolerance (a third of the stale window). Definite rejections still revoke at once. startIngestionHeartbeat takes the job lease and refuses to start unless the tolerance is positive and strictly shorter than it (owner decision, 21 Oct 14:42), so an attempt always stands down before another worker can reclaim the job. Tests cover this.
  • DB-01 The Retry route calls the hardened recover_ingestion_job path. Both of its job reads join documents!inner on the owner and are declared in the derived-query inventory. Refusals are clear 404/409 responses.
  • ING-A-02 reindex-health counts jobs stranded on their final attempt directly. The delete refusal no longer tells the admin to stop a worker that does not exist.
  • EVAL-09 reindex-health counts indexed documents with no chunks and chunks with no embedding, and lists any non-zero count under integrityProblems.
  • ING-A-04 The re-index runbook now says the eval gate is advisory and done by hand until the harness is applied.

No supabase/** change.

RAG impact: no behaviour change. Retrieval, ranking and answers are untouched; this is worker lease handling, the Retry route and ops reporting.

Clinical Governance Preflight: no clinical content, cautions, thresholds or refusal logic in this PR (the OCR caution moved to #3715).

Evidence (local, offline): focused Vitest passes for worker-ingestion-attempt, worker-shadow-extraction, private-access-routes, worker-admin-source-runtime, ingestion, source-governance, table-facts and table-fact-ranking. Typecheck is clean, and the owner-scope check passes both phases. The new reindex-health counts have no unit test, because the script reads the live database. Hosted CI has not yet been observed.

Left out:

🤖 Generated with Claude Code

https://claude.ai/code/session_011qMFt8SZBmLuAmkH6ko6nd

Split (owner-approved, 21 Oct 2026): this PR is now part (a), the Retry route plus heartbeat (with the stranded-job and integrity reporting). Part (b), the OCR caution, scanner warning, table-fact label carry-forward (ING-B-09) and deleted-document titles, moved to #3715. The table-row grouping (RAG-A-02) moved to #3711.

Audit 2026-10-20 ING-A-03: one failed heartbeat cancelled the whole
attempt and discarded its paid work while the 45-minute lease still had
most of its time left. The heartbeat writes nothing, so an uncertain or
transiently rejected heartbeat is now tolerated until none has been
confirmed for a third of the stale window. A lost lease, an invalid
response or a confirmed non-transient rejection still revokes at once.
Every other mutation still revokes on any uncertain outcome.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qMFt8SZBmLuAmkH6ko6nd
…overy RPC

Audit 2026-10-20 DB-01: Retry called retry_ingestion_job_if_idle, which
skips the document authority lock, the managed-evidence hold and the
sibling-job check. The route now reads the job's current row state and
calls recover_ingestion_job, the same atomic path the operator recovery
scripts use, guarded on that state. Each refusal becomes a plain 409
message the admin panel already shows. No database change: the older
function stays in place, now unused by the app.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qMFt8SZBmLuAmkH6ko6nd
Audit 2026-10-20 CH-04: without WORKER_ADMIN_SOURCE_RUNTIME_MODULE there
is no scanner verdict, so every ingestion provider call is refused and
each job fails as scanner_unavailable. The worker now logs one warning
at start-up saying so (never the value), and the runbook's re-enable
checklist names that warning.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qMFt8SZBmLuAmkH6ko6nd
…up ledger

Audit 2026-10-20 ING-A-05: delete_document_if_idle copies the title into
storage_cleanup_jobs, and nothing cleared it, though the delete route
itself treats titles as possibly identifying. The delete route and the
storage janitor now clear it once the document row is gone. An aborted
delete leaves the live document's title alone. The privacy impact
assessment's retention table gains the row. Clearing rows that finished
earlier needs a database change and is not part of this commit.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qMFt8SZBmLuAmkH6ko6nd
…by OCR

Audit 2026-10-20 ING-B-07: OCR can list a value apart from its
qualifier or misread a decimal, and nothing at answer time said a
passage's document came from a scan. A new governance caution
(ocr_text_source, warning severity, never a refusal) appears whenever a
supporting source's index quality records OCR pages. No retrieval,
ranking or extraction change.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qMFt8SZBmLuAmkH6ko6nd
…ising ones

Audit 2026-10-20 RAG-A-02: table-fact grouping replaced a chunk's match
only when a later fact scored higher, so lower-ranked facts were dropped
and about one structured row survived per chunk. Every fact is now kept,
deduplicated by id and capped at five, while the chunk keeps its best
fact's score. Scores and ordering are unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qMFt8SZBmLuAmkH6ko6nd
@coderabbitai

coderabbitai Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

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

Next included review available in 15 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available. Your 87 included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository: BigSimmo/PsychSift/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Team
  • Run ID: e849e4e7-48fd-4732-9d96-c07fa66ed860
📥 Commits

Reviewing files that changed from the base of the PR and between 9ed4dc8 and 6a3a856.

📒 Files selected for processing: 16
  • data/repo-awareness-snapshot.json
  • docs/audit/tenancy-defense-in-depth-review.md
  • docs/reindex-shadow-harness-design.md
  • docs/worker-deploy-runbook.md
  • scripts/lib/tenancy-scan.mjs
  • scripts/reindex-health.ts
  • src/app/api/documents/[id]/route.ts
  • src/app/api/ingestion/jobs/[id]/retry/route.ts
  • tests/private-access-routes.test.ts
  • tests/worker-generation-attempt-control.test.ts
  • tests/worker-ingestion-attempt.test.ts
  • tests/worker-lease-transition-contract.test.ts
  • tests/worker-shadow-extraction.test.ts
  • tests/worker-visual-capture.test.ts
  • worker/behavior.ts
  • worker/main.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@supabase

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

Comment thread src/lib/source-governance.ts Resolved
gitar-bot[bot]
gitar-bot Bot previously approved these changes Oct 21, 2026

@gitar-bot gitar-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gitar has auto-approved this PR (configure)

…-read to the owner

OCR_TEXT now sorts after other warnings of the same severity so it cannot push
DOCUMENT_CAUTION or REVIEW_DUE out of the 8-item cap (Gitar). The Retry route's
post-recovery re-read now joins documents!inner on the owner, and both queries
are declared in the derived-query inventory (owner-scope phase 2).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qMFt8SZBmLuAmkH6ko6nd
…meter (ING-B-09)

PyMuPDF leaves a vertically merged label cell blank on later rows, so those
facts were labelled with the table title. The last label now carries into
clinical_parameter, with metadata.row_label_inherited_from recording where it
came from. row_label, cells and chunk text keep the extracted value. Also
formats the Retry route test mock.

RAG impact: behaviour change for table facts of newly indexed documents only;
before/after canary belongs around the re-index.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qMFt8SZBmLuAmkH6ko6nd
gitar-bot[bot]
gitar-bot Bot previously approved these changes Oct 21, 2026

@gitar-bot gitar-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gitar has auto-approved this PR (configure)

…ng search data

ING-A-02: reindex-health counts processing jobs whose lease expired on their
final attempt directly, instead of relying on the 25-oldest preview, and the
delete refusal no longer tells the admin to stop a worker that does not exist.
EVAL-09: reindex-health also counts indexed documents with no chunks and chunks
missing an embedding, and lists any non-zero count under integrityProblems.
ING-A-04: the re-index runbook states the eval gate is advisory and by hand.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qMFt8SZBmLuAmkH6ko6nd
Comment thread scripts/reindex-health.ts Resolved
gitar-bot[bot]
gitar-bot Bot previously approved these changes Oct 21, 2026

@gitar-bot gitar-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gitar has auto-approved this PR (configure)

document_chunks.embedding is NOT NULL, so the missing-embedding count was always 0 and gave false assurance.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qMFt8SZBmLuAmkH6ko6nd
gitar-bot[bot]
gitar-bot Bot previously approved these changes Oct 21, 2026

@gitar-bot gitar-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gitar has auto-approved this PR (configure)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qMFt8SZBmLuAmkH6ko6nd
gitar-bot[bot]
gitar-bot Bot previously approved these changes Oct 21, 2026

@gitar-bot gitar-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gitar has auto-approved this PR (configure)

Copy link
Copy Markdown
Owner Author

Static PR checks is red on check:clinical-hazard-controls (CLINICAL_HAZARD_CONTROLS_CONTENT_DRIFT). tests/source-governance.test.ts is a sealed clinical-hazard path, and this PR adds the scanned-page caution tests to it. Clearing it needs either the owner's re-review and reseal, or an owner-approved temporary drift exception. An agent can't add either on its own, so I've asked the owner and left this blocked until they answer.


Generated by Claude Code

…ce tests

Owner approved by decision card 21 Oct 2026. Not a reseal or clinical sign-off.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qMFt8SZBmLuAmkH6ko6nd
gitar-bot[bot]
gitar-bot Bot previously approved these changes Oct 21, 2026

@gitar-bot gitar-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gitar has auto-approved this PR (configure)

Copy link
Copy Markdown
Owner Author

Owner wording approval recorded: at 05:18 UTC on 21 Oct 2026, in the project thread, Josh (BigSimmo) said "yes" to both texts as written.

  • The scanned-page source caution, which ends "Check numbers against the PDF page."
  • The delete-refusal message: "Wait for it to finish, or recover a job stuck on its last attempt, before deleting."

This covers the wording only. The hazard-control seal on tests/source-governance.test.ts still rests on the temporary drift exception until the owner reseals it.


Generated by Claude Code

@BigSimmo
BigSimmo marked this pull request as ready for review October 21, 2026 05:26

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-21T05:29:53.462199Z 0f7b568 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@claude

claude Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Claude encountered an error after 2s —— View job


I'll analyze this and get back to you.

@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: 0f7b568347

ℹ️ 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-candidate-sources.ts Resolved
Restores searchTableFactCandidates' inline grouping exactly as on main and
drops tests/table-fact-grouping.test.ts. The grouping change moves to a
separate draft PR so its canary pair can run on its own. worker/table-facts.ts
is kept.
@cubic-dev-ai

cubic-dev-ai Bot commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

Auto-fix isn't included in your plan ↗

Upgrade to Team

gitar-bot[bot]
gitar-bot Bot previously approved these changes Oct 21, 2026

@gitar-bot gitar-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gitar has auto-approved this PR (configure)

…t tolerance by the lease

Owner-approved split (21 Oct 2026, 14:42 AWST). The OCR caution, scanner
start-up warning, table-fact label carry-forward and deleted-document title
changes move to their own PR off main; restored here to main's versions by
a normal commit.

startIngestionHeartbeat now takes the job lease and refuses to start unless
the tolerance is positive and strictly shorter than it, so an attempt always
stands down before another worker can reclaim the job. Tests cover the
guard and revocation inside the lease. Source-contract tests are updated to
the fail-closed shape of runIngestionMutation (it still revokes by default;
only the heartbeat opts out).

@gitar-bot gitar-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gitar has auto-approved this PR (configure)

@gitar-bot

gitar-bot Bot commented Oct 21, 2026

Copy link
Copy Markdown
Contributor
Code Review ✅ Approved 2 closed / 2 findings

🟡 Medium risk · Heartbeat failures are tolerated until a bounded timeout, which could delay worker cancellation during outages; admin retries also now use the recovery RPC.

Ingestion hardening for the worker heartbeat, admin Retry route, OCR source cautions, table-fact row labels and reindex-health counts. Two issues were resolved: the OCR caution could push document-caution warnings out of the 8-item cap, and the chunks_missing_embedding count could never be non-zero because the column is NOT NULL. No open findings remain.

✅ 2 closed
✅ Edge Case: OCR caution can push document-caution warnings out of the 8-item cap

📄 src/lib/source-governance.ts:236-248 📄 src/lib/source-governance.ts:307-310 📄 src/lib/source-governance.ts:41-42
sourceGovernanceWarnings adds one warning per document and code, stable-sorts them by severity, then keeps only the first args.limit ?? 8 (lines 307-310). answer-response.ts and search/route.ts both call it with that default. The new OCR_TEXT warning has severity warning, the same as UNVERIFIED, REVIEW_DUE and DOCUMENT_CAUTION, and it is added early in each source's pass. Most cited documents are unverified, so four OCR'd sources already fill all eight slots (unverified + OCR for each). A fifth source's DOCUMENT_CAUTION (a known error in the cited document) or REVIEW_DUE warning is then cut before it reaches the reader. Before this PR, the same answer had room for it. Possible fixes: give the OCR caution a lower sort rank than other warnings of the same severity, collapse OCR into one answer-level entry before the cap, or apply the cap after groupSourceGovernanceWarnings.

✅ Quality: chunks_missing_embedding can never be non-zero: column is NOT NULL

📄 scripts/reindex-health.ts:152-155
document_chunks.embedding is declared extensions.vector(1536) not null in supabase/schema.sql, and no migration drops that constraint. So .is("embedding", null) always returns 0. EVAL-09's "chunks with no embedding" check can never appear in integrityProblems, yet operators will read a clean result as confirmed integrity. A chunk that lacks search data is either never inserted or sits in a separate store, so this probe misses it. Either delete the check or point it at something that can actually be missing, such as indexed documents with chunk_count > 0 but no document_chunks rows, or missing document_embedding_fields.

Verification

Rules No rules evaluated

Functional validation Not enabled

Set up

Auto-approval Approved

For more verification details, comment gitar display:verbose.

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing compact verification results.

Comment with these commands to change the behavior for this request:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@BigSimmo BigSimmo changed the title Ingestion hardening before mass ingestion (code only) Ingestion hardening: Retry route and heartbeat (part a) Oct 21, 2026
@gitar-bot
gitar-bot Bot merged commit 8f476ab into main Oct 21, 2026
40 checks passed
@gitar-bot
gitar-bot Bot deleted the claude/project-thread-8liwcc-ingest-code branch October 21, 2026 07:36
BigSimmo added a commit that referenced this pull request Oct 21, 2026
Keep every matching table row per chunk (RAG-A-02, split from #3707)
gitar-bot Bot added a commit that referenced this pull request Oct 21, 2026
…cts-titles

OCR caution, scanner warning, table-fact labels and deleted-document titles (split from #3707)
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