docs(webhooks): operator runbook to wire up and activate the webhooks#1065
Conversation
The three webhook receivers merged in #968 are deployed but inert (verified live: POST /api/webhooks/railway returns 503 webhook_not_configured). Turning them on is operator/provider work, not code: - docs/webhooks.md §3: add a complete, fail-safe, Vault-based Postgres trigger (notify_document_change_ingestion_webhook + documents_ingestion_webhook) that actually calls the Supabase receiver. It gates in SQL to the receiver's actionable transitions, reads the secret from Supabase Vault (never the repo), and never aborts a document write if the secret is missing or the POST fails. Delivered as an operator-applied script rather than a committed migration because it needs the Vault secret and a drift-manifest regen against a live Supabase container. - Track the remaining activation + provider items in docs/outstanding-issues.md: #24 set the webhook secrets, #25 apply the Supabase trigger, #26 external uptime monitor, #27 runtime error tracking (Sentry). No application code, schema, or migration changes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BHMgBiXbH4Q5tUC7WxoyaX
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughDocumentation updates add four outstanding issues and define an operator-applied Supabase webhook setup with SQL trigger behavior, failure handling, delivery semantics, and managed alternatives. ChangesIssue ledger
Supabase webhook documentation
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d18ecae659
ℹ️ 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".
Two accuracy fixes to docs/webhooks.md §3 raised in review: - Do NOT apply the trigger as raw SQL to the live DB — that diverges from schema.sql/drift-manifest and fails check:drift. Reframe it as a committed migration (migration + schema.sql reconcile + `npm run drift:manifest`), with only the Vault secret + base-URL GUC as operator-applied live state. - Correct the delivery-durability claim: net.http_post is fire-and-forget with no retry, so the "500 -> provider retries" guarantee applies to the managed Supabase Database Webhook, not the raw trigger. Note the polling recovery backstop (hasIncompleteDocumentsWithoutOpenJobs / ingestion-recovery, currently read-only unless INGESTION_AUTOPILOT_APPLY=true) and recommend the managed webhook for at-least-once delivery. Docs-only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BHMgBiXbH4Q5tUC7WxoyaX
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 08654048a6
ℹ️ 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".
…ssues Second review pass: - Managed Supabase Database Webhooks wrap pg_net, whose http_post has no retry (supabase/pg_net#110 open), so they do NOT provide at-least-once delivery either. Remove the "built-in retry / recommended for durability" claim; frame the managed webhook as dashboard convenience only, and point durability at the recovery path / a transactional outbox. - Update outstanding-issues #25 to match the corrected runbook: the trigger lands via committed migration (+ schema.sql + drift manifest); only the Vault secret + GUC are operator-applied. Prevents the durable-memory row from steering an operator back into the live-only drift gap. Docs-only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BHMgBiXbH4Q5tUC7WxoyaX
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 80dbe9048e
ℹ️ 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".
Review verified: the scheduled Ingestion Autopilot's assessIngestionHealth only flags failed / stale-processing jobs — it never inspects `queued` documents with no ingestion_jobs row, so it does NOT recover a dropped-webhook insert (even with INGESTION_AUTOPILOT_APPLY=true). hasIncompleteDocumentsWithoutOpenJobs — the predicate that would catch it — is wired only into the manual scripts/reindex.ts CLI. So a dropped delivery for an outside-app insert has no automatic recovery. Reframe the durability guidance accordingly: for a document that must never be dropped, ingest via the app upload path (transactional enqueue) or add a transactional outbox / scheduled queued-without-job sweep; the trigger and any managed webhook are latency optimisations, not delivery guarantees. Align the managed-alternative note and outstanding-issues #25. Docs-only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BHMgBiXbH4Q5tUC7WxoyaX
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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/webhooks.md`:
- Around line 179-180: Update the reindex_requested check in the trigger logic
to compare metadata JSON values directly against the JSON boolean true, matching
the receiver’s strict check. Remove the text extraction and boolean cast from
both new and old metadata comparisons while preserving the existing coalesce and
change-detection behavior.
🪄 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: e78be0e2-f896-40a8-b137-6de581036b92
📒 Files selected for processing (2)
docs/outstanding-issues.mddocs/webhooks.md
Match the receiver's `reindex_requested === true`: compare the jsonb value directly to 'true'::jsonb instead of ->> ... ::boolean. The text-cast form treated the JSON string "true" as actionable (receiver would skip it) and could raise on a malformed value — swallowed by the fail-safe handler, silently skipping notification. Direct jsonb comparison matches the receiver exactly and never raises. Docs-only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BHMgBiXbH4Q5tUC7WxoyaX
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 00ba717208
ℹ️ 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".
Verified in src/app/api/upload/route.ts: the upload route inserts the document (status queued) then its own ingestion job, and on ANY job-insert error — including a 23505 from the one-open-job-per-document unique index — it DELETES the document and throws. A webhook trigger firing on INSERT could enqueue first, making the upload's own job insert conflict and turning a normal upload into an intermittent failure. Fix the runbook: the trigger is now AFTER UPDATE on the reindex_requested flip only (INSERT branch removed), so it never races the upload flow. Documents inserted outside the upload path set metadata.reindex_requested = true to be enqueued. Also warn that a managed Database Webhook must be scoped to UPDATE for the same reason, and align the receiver-policy note + outstanding-issues #25. Docs-only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BHMgBiXbH4Q5tUC7WxoyaX
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2d83bddbac
ℹ️ 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".
Two more review catches, both verified against the code: - hasIncompleteDocumentsWithoutOpenJobs requires queuedDocuments === 0 (src/lib/reindex-pipeline.ts), and scripts/reindex.ts only errors on it — so neither the autopilot nor the manual CLI recovers a stranded queued-without-job row. Drop the "manual reindex CLI is the backstop" claim: there is NO built-in recovery. To recover a stranded row, flip reindex_requested (fires the trigger); for guaranteed delivery use the app upload path or build a queued-without-job sweep. - The trigger is AFTER UPDATE with a not-true -> true predicate, so an INSERT with reindex_requested already set never fires it. Document the required insert-then- separate-UPDATE pattern (with a concrete UPDATE example) and confirm a job appears. Align outstanding-issues #25. Docs-only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BHMgBiXbH4Q5tUC7WxoyaX
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7157738b56
ℹ️ 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".
Review (line 128): the trigger's base URL coalesced to a hardcoded https://psychiatry.tools when the GUC was unset. Replayed on staging/local without the GUC, it would POST that environment's full documents row (to_jsonb(new)) to the PRODUCTION receiver — which is wired to a different Supabase project, so the row fails to enqueue and its metadata crosses environments (a privacy/cross-env concern for a clinical corpus). Make app.ingestion_webhook_base_url REQUIRED per environment with NO production default: if unset, the trigger no-ops (like a missing Vault secret), so an un-configured environment can never post to production. Step 3 now says set it explicitly for every environment, production included. Docs-only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BHMgBiXbH4Q5tUC7WxoyaX
Close the last operator trap in the insert-then-flag guidance: the row must be INSERTed with reindex_requested absent or false so the separate UPDATE to true is a real not-true -> true transition. An insert already carrying true never fires the AFTER UPDATE trigger, and a later true -> true update is a no-op (not distinct from the old value) that also won't fire. Also repair the broken blockquote around the UPDATE example (one line had lost its `>` prefix) and give the UPDATE its own fenced sql block. Docs-only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BHMgBiXbH4Q5tUC7WxoyaX
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6f63b05820
ℹ️ 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".
- Managed-alternative setup now says scope to UPDATE events only (do not tick INSERT) in the concrete instruction, not just a later warning — an INSERT webhook re-introduces the upload race. - outstanding-issues #25: the base-URL GUC is mandatory per environment, not optional (the trigger no-ops without it), so the activation checklist can't leave the trigger inert. Both are consistency fixes for earlier changes; neither touches the #24 row or next-id marker (the ledger-collision surface vs #1066). Docs-only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BHMgBiXbH4Q5tUC7WxoyaX
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c74e282d29
ℹ️ 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".
A row stranded because the original reindex_requested flip's delivery was lost already has the flag true, so "set it true to recover" is a no-op true -> true that won't re-fire the AFTER UPDATE trigger. Document a clear-then-flip recovery (set false, then true) with SQL, and note a sweep must do the same. Docs-only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BHMgBiXbH4Q5tUC7WxoyaX
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 13ab045cbd
ℹ️ 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".
- Renumber the webhook ledger rows #24-#27 -> #25-#28 (next-id 029) so they no longer collide with #1066's #24 (WebKit e2e). Per the consolidation decision, #1065 lands and the duplicate Codex webhook-doc PRs are closed; #1066 stays as a separate item. - Scope the "receiver 500 -> retries" claim in the receiver-behaviour bullet: it only holds for a caller that actually retries, and neither documented path (raw net.http_post trigger or UPDATE-only managed webhook) does, so a failed clear can leave reindex_requested stuck true; recover via clear-then-flip. Docs-only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BHMgBiXbH4Q5tUC7WxoyaX
…hook-recommendations-oviufb # Conflicts: # docs/outstanding-issues.md
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BHMgBiXbH4Q5tUC7WxoyaX
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e76823724e
ℹ️ 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".
The Railway receiver forwards deploy alerts via postChatNotification, which reads SLACK_WEBHOOK_URL/DISCORD_WEBHOOK_URL from server env; GitHub repo secrets only feed the CI-failure workflow. Repo-secret-only left the Railway webhook authenticated but returning forwarded:false. Clarify #25 checklist and webhooks.md §1. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BHMgBiXbH4Q5tUC7WxoyaX
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 67e59ec4c2
ℹ️ 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".
The receiver reads only id/owner_id/status/metadata.reindex_requested and then reloads the authoritative row by id, so the documented trigger no longer sends to_jsonb(new) + old_record. That avoids shipping file names, storage paths, and content hashes to whatever app.ingestion_webhook_base_url resolves to, shrinking the blast radius of a misconfigured GUC. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BHMgBiXbH4Q5tUC7WxoyaX
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 39665844bf
ℹ️ 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".
The managed Supabase Database Webhook auto-builds the body with the full record + old_record, unlike the minimized SQL trigger. For the private documents table, steer operators to the committed minimal trigger; the dashboard alternative is for non-sensitive tables only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BHMgBiXbH4Q5tUC7WxoyaX
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 92b38026e0
ℹ️ 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".
The upload route inserts the documents row and the ingestion_jobs row as two separate calls, deleting the document only on a job-insert error. A crash between the inserts can still strand a queued-without-job row, so the runbook no longer presents the upload path as a hard delivery guarantee; the true guarantees are an atomic enqueue or a queued-without-job sweep. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BHMgBiXbH4Q5tUC7WxoyaX
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 41a67f9688
ℹ️ 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".
…guarantee Match the corrected webhooks.md wording: the app upload route's two non-atomic inserts can strand a queued-without-job row on a crash between them, so the ledger now names only an atomic enqueue or a queued-without-job sweep as the guarantee. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BHMgBiXbH4Q5tUC7WxoyaX
Summary
Follow-up to the merged webhooks (#968). Those three receivers are deployed but inert — I verified live that
POST /api/webhooks/railwayreturns503 {"code":"webhook_not_configured"}, i.e. the code path works end-to-end in production and correctly fails closed until configured. Actually turning them on is operator/provider work, so this PR delivers the missing runbook + tracking rather than more code.docs/webhooks.md§3 — a complete, ready-to-apply Postgres trigger (notify_document_change_ingestion_webhook+documents_ingestion_webhook) that calls the Supabase receiver. It reads the secret from Supabase Vault (never the repo), ships as a committed migration (not raw SQL on live — that would failcheck:drift), firesAFTER UPDATEonreindex_requestedonly (neverINSERT, to avoid racing the app upload route, which deletes its own document on a job-insert conflict), and is fail-safe (no-op if the secret is missing or the POST fails;net.http_postis async so it never blocks the write).docs/outstanding-issues.md— logs the remaining activation items:#024set the webhook secrets,#025apply the Supabase trigger,#026external uptime monitor,#027runtime error tracking (Sentry).Verification
docs:check-index,check:docs-links— passsrc/app/api/upload/route.tsand the receiver logic during reviewRisk and rollout
reindex_requested.Clinical Governance Preflight
This change is documentation only — it does not modify ingestion, answer generation, search/ranking, source rendering, document access, privacy, or clinical output code. Items confirmed on that basis:
Clinical KB Database(sjrfecxgysukkwxsowpy) — unchangedNotes
Docs-only PR (operator runbook + issue-ledger memory). The webhook receivers themselves shipped in #968; this only documents how to activate them and tracks the operator/provider follow-ups. The runbook's SQL is operator-applied and fail-safe, and — after review — is scoped to
AFTER UPDATE/reindex_requestedso it cannot race the primary upload flow.🤖 Generated with Claude Code
Summary by CodeRabbit