Skip to content

Replay the migration chain onto staging to parity, and record that the chain does not reproduce schema.sql (Phase 2, #056) - #2093

Merged
BigSimmo merged 6 commits into
mainfrom
claude/db-remediation-phase-2-a0c20b
Aug 18, 2026
Merged

Replay the migration chain onto staging to parity, and record that the chain does not reproduce schema.sql (Phase 2, #056)#2093
BigSimmo merged 6 commits into
mainfrom
claude/db-remediation-phase-2-a0c20b

Conversation

@BigSimmo

@BigSimmoBigSimmo commented Aug 18, 2026

Copy link
Copy Markdown
Owner

Summary

Phase 2 of docs/database-remediation-plan.md, executed in an owner-authorized staging-only
window. Two independent items, each its own commit:

  • 5067c4aad — staging replayed to migration parity (#056). The gap re-measured as 28
    versions at the start of the window, not the 26 recorded on 2026-08-17. The whole chain was
    replayed in version order; staging went from 166 rows to 194, latest 20260814151000, zero
    statements IS NULL, and a two-way version diff against supabase/migrations/ is empty in both
    directions. Every replayed row was read back and md5-compared against its repository file — all 28
    match byte-for-byte.

  • 523f8a55bcheck:drift against staging now runs, and is red with 19 findings. This is the
    substantive result. Because staging carries the complete, byte-verified chain, this is not staging
    staleness: the committed migration chain and supabase/schema.sql disagree, and check:drift
    builds its expected side from schema.sql. Getting the check to target staging at all required a
    six-line fix to scripts/check-drift.ts (below).

The drift result

Seven match_*def_hash mismatches are SET work_mem, which pg_get_functiondef renders and
def_hash does not strip. grep -c work_mem supabase/schema.sql returns 0, and only
20260724000000 sets it — so schema.sql is the stale side and the fix is repo-side, not a
production deploy. The eighth work_mem target, match_document_table_facts_text, is absent from
the drift list precisely because 20260724120000 re-created it without restating work_mem. The
exception confirms the mechanism.

Eight further objects are declared in schema.sql that no migration creates or drops: five
document_embedding_fields indexes, documents_status_idx, and the documents_updated_at /
ingestion_jobs_updated_atset_updated_at triggers. Three table column-set mismatches and one
index def mismatch remain unclassified.

Programme consequence: until schema.sql and the chain are reconciled, a production drift
finding cannot be assumed to mean production drifted — for the seven work_mem functions the opposite
holds. That argues for reconciling schema.sql to the chain before spending a production window
on Phase 3.

Code change

scripts/check-drift.ts passed only three of five identity keys to checkSupabaseProjectConfig,
dropping SUPABASE_STAGING_PROJECT_REF / SUPABASE_STAGING_PROJECT_NAME. With those absent
resolveStagingProject returns null, expected falls back to production, and any staging URL is
rejected as a mismatch — even though requireServerEnv() on the line above is already staging-aware
and scripts/check-supabase-project.ts passes all five. Proven against identical env:

AFTER FIX -> ready | environment: staging | expected: ikoiolksxqxfxgiyqpnu
BEFORE FIX -> mismatch | environment: production | expected: sjrfecxgysukkwxsowpy

RAG impact: no retrieval behaviour change — no src/lib/rag/**, ranking, selection, eval harness or
golden-fixture file is touched. The only executable change is the drift check's project-identity
plumbing. The seven match_* functions appear here as measurements of existing divergence, and no
RPC body was created, altered, or deployed anywhere.

Safety

  • Staging only. Target ikoiolksxqxfxgiyqpnu, ref re-verified before every mutating call.
    Production sjrfecxgysukkwxsowpy was never a mutation target; the only production interaction in
    the window was list_projects.
  • No production clinical document copied to staging; no ingestion worker started. documents and
    document_chunks remain 0. The staging environment was not recreated.
  • No migration file edited, no supabase/schema.sql, no drift-manifest.json. Six clean-replay
    findings are recorded rather than patched.
  • supabase db push was unavailable (SUPABASE_ACCESS_TOKEN absent, #183); MCP apply_migration
    was rejected because it stamps connector-generated versions, which docs/staging-setup.md forbids.
    Each migration ran verbatim via execute_sql with an explicit history row carrying the
    repository's own version and name.

Verification

  • npm run verify:pr-local on 5067c4aad: green through check:runtime, check:installed-lock-parity,
    format:changed, sitemap:check, all four docs checks, check:branch-review-ledger,
    check:outstanding-issues, check:ledger-write-discipline, lint, typecheck.
  • npm run test: Test Files 2 failed | 638 passed | 2 skipped. Both pre-existing and unrelated —
    run-to-run failure sets were disjoint, worker-observability passes in isolation (contention
    timeout), and session-start-hook.test.ts fails on a Git-Bash-on-Windows path-format mismatch;
    neither it nor the hook it exercises is in this diff.
  • check:outstanding-issues, check:ledger-write-discipline, docs:check-links re-run green on
    523f8a55b.

Known scope limit — follow-up required

Measurement base is ed43a64f2. origin/main advanced 121 commits during the window and now
carries 195 migrations, including 20260818090000_schema_drift_snapshot_history_probe.sql,
which redefines the schema_drift_snapshot() function this check calls; schema.sql and
drift-manifest.json also changed. So: staging is one migration behind again, and the 19 findings
were measured against the previous manifest. The structural findings (work_mem absent from
schema.sql; objects no migration creates) are base-independent, but re-measuring against 195 is
owed and is recorded as follow-up rather than asserted here.

#056 is updated by queued inbox request; the earlier request was cancelled and replaced rather
than edited, per the immutable-request rule. #316 was deliberately not updated — Phase 1.2
owns it.

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

Notes against the items above, since this PR ran a hosted mutation:

  • Supabase target unchanged. No env, config, or project.ts default was altered, so the
    repository's configured target remains Clinical KB Database (sjrfecxgysukkwxsowpy). The
    staging mutation happened in a separate owner-authorized window against
    Clinical KB Staging (ikoiolksxqxfxgiyqpnu), declared through
    SUPABASE_STAGING_PROJECT_REF/_NAME in an uncommitted local env. Production was never a
    mutation target; the only production interaction was list_projects.
  • Service-role scope. The scripts/check-drift.ts change only forwards two existing
    project-identity env keys into an existing guard call. It grants no new access, widens no scope,
    and touches no client bundle — check:drift was already service-role, server-side, CLI-only.
  • No clinical content or decision-support behaviour changed. No retrieval, ranking, answer,
    ingestion or source-governance code is touched; no migration, supabase/schema.sql, or
    drift-manifest.json is modified. Staging's corpus stayed empty (documents = 0,
    document_chunks = 0) and no production clinical document was copied to it. Nothing here alters
    what a clinician sees, so the SaMD classification is unaffected.

Risk and rollout

Risk: low, and the hosted part is already done and reversible by inaction. The repository diff
is documentation plus a six-line env-forwarding fix in a CLI-only gate script; nothing ships to
users, no migration or schema artifact changes, and no client bundle is affected.

The one irreversible-by-diff action is the staging replay, which already happened in its authorized
window. Rollback for it is not a revert of this PR — the PR does not perform it. Should staging ever
need to be returned to its prior state, the 28 applied versions are enumerated in the forensics
document and staging holds no data to lose (documents = 0, document_chunks = 0); the supported
recovery is to re-run the chain, not to hand-delete history rows.

Rolling back the repository change is an ordinary git revert, whose only effect is that
check:drift can no longer target staging.

Production is unaffected by merging this. No production DDL, data change, config, or deploy is
involved, and the drift findings recorded here are measurements, not fixes.

Verification

  • npm run verify:pr-local — green through check:runtime, check:installed-lock-parity,
    format:changed, sitemap:check, all four docs checks, check:branch-review-ledger,
    check:outstanding-issues, check:ledger-write-discipline, lint, typecheck
  • check:outstanding-issues, check:ledger-write-discipline, docs:check-links re-run green
    on the evidence commit
  • npm run test2 failed | 638 passed | 2 skipped; both pre-existing and unrelated
    (run-to-run failure sets were disjoint, worker-observability passes in isolation, and
    session-start-hook.test.ts fails on a Git-Bash-on-Windows path-format mismatch; neither it
    nor the hook it exercises is in this diff)
  • npm run check:drift against staging — exit 1 by design: the red result is the finding
    this PR reports, not a failed gate
  • Verification not run: verify:ui / verify:release — no UI, routing, styling or build-affecting
    change in the diff, so no plausible regression path for browser gates to detect.

…se 2, #56)
Phase 2 of docs/database-remediation-plan.md, run in an owner-authorized
staging window. Target was Clinical KB Staging (ikoiolksxqxfxgiyqpnu) via the
Supabase MCP connector, with the ref re-verified on every call; production
(sjrfecxgysukkwxsowpy) was never a mutation target.
The gap re-measured as 28 versions at the start of the window, not the 26
recorded on 2026-08-17 — ten earlier history holes plus eighteen versions after
20260719055623. The whole chain was replayed in version order. Staging now
holds 194 rows in supabase_migrations.schema_migrations, latest 20260814151000,
zero statements IS NULL, and a two-way version diff against supabase/migrations
is empty. Every replayed row was read back and its md5 compared against md5sum
of the repository file: all 28 match byte-for-byte.
supabase db push was unavailable (SUPABASE_ACCESS_TOKEN absent per #183, staging
DB password operator-only) and MCP apply_migration was rejected because it
stamps connector-generated versions, which docs/staging-setup.md forbids. Each
migration was executed verbatim via execute_sql with an explicit schema_migrations
row carrying the repository's own version and name.
check:drift against staging is not yet proven. scripts/check-drift.ts forwarded
only three of the five identity keys to checkSupabaseProjectConfig, so any
staging URL was rejected as a production mismatch; that narrowing is fixed here,
mirroring scripts/check-supabase-project.ts. The remaining blocker is the staging
service-role key, which is not in this environment and which the MCP connector
cannot supply. The gate was deliberately not run rather than risk pointing it at
production via the only local .env.local.
Six findings from the clean replay are recorded, none patched — notably that the
20260804110240 guard passed with no prebuild because staging never lost the
indexes production lost, that four migrations exist as duplicate earlier/later
version pairs which a plain --include-all push would apply in the wrong effective
order, and that 20260724120000 silently drops the work_mem setting 20260724000000
applies.
No schema file, migration, or drift manifest was changed. No production clinical
document was copied to staging and no ingestion worker was started.
…uce schema.sql
check:drift now runs against staging and is red with 19 findings. Because
staging carries the complete, byte-verified migration chain, this is not
staging staleness: it is the committed chain and supabase/schema.sql
disagreeing, and check:drift builds its expected side from schema.sql.
Seven match_* def_hash mismatches are SET work_mem, which pg_get_functiondef
renders and def_hash does not strip. grep -c work_mem supabase/schema.sql
returns 0 and only 20260724000000 sets it, so schema.sql is the stale side and
the fix is repo-side, not a production deploy. The eighth work_mem target,
match_document_table_facts_text, is absent from the drift list precisely
because 20260724120000 re-created it without restating work_mem — the
exception confirms the mechanism.
Eight further objects are declared in schema.sql that no migration creates or
drops: five document_embedding_fields indexes, documents_status_idx, and the
documents_updated_at and ingestion_jobs_updated_at set_updated_at triggers.
Three table column-set mismatches and one index def mismatch remain
unclassified.
This bears on #316's recorded work_mem hypothesis, now measured on staging for
seven of production's ten mismatched RPCs with no production call. It does not
close Phase 1.2 and #316 was deliberately not updated from this session.
The superseded #56 inbox request is cancelled and replaced rather than edited,
per the immutable-request rule.
Measurement base: ed43a64. origin/main has since advanced to 6b72b3a (195
migrations, including a schema_drift_snapshot history probe); re-measuring
against that manifest is recorded as follow-up.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitaiBot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

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:36 minutes

Limit details: You’ve used all 1 included review currently available under your plan. You completed 101 included PR reviews in the past 7 days; at that activity level, included reviews refill at 1 review per hour.

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: 0a783e2f-783d-44ba-98ef-91cc8e1d96fd

📥 Commits

Reviewing files that changed from the base of the PR and between 79e5d2a and 7859b9f.

📒 Files selected for processing (7)
  • docs/audit/live-drift-forensics-2026-08.md
  • docs/branch-review-records/d54d14f00e65448e6ced1f60805bc47334ea1e7147a46a5f13b0b8e4e7ef818e.record.md
  • docs/outstanding-issues-inbox/6697868a-cc70-48e3-ad6f-598ff4031110.json
  • docs/outstanding-issues-inbox/a1c319d6-a098-460b-8b42-6794ca6690fd.json
  • docs/outstanding-issues-inbox/d958d671-362d-41ed-b72f-102357dbf156.json
  • docs/staging-setup.md
  • scripts/check-drift.ts

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

@supabase

supabaseBot commented Aug 18, 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 ↗︎.

@BigSimmo
BigSimmo enabled auto-merge (squash) August 18, 2026 05:35
@BigSimmo
BigSimmo merged commit e546840 into mainAug 18, 2026
24 checks passed
@BigSimmo
BigSimmo deleted the claude/db-remediation-phase-2-a0c20b branch August 18, 2026 06:12
BigSimmo added a commit that referenced this pull request Aug 18, 2026
Applies the queued inbox requests into docs/outstanding-issues.md, including the
Phase 2 staging-parity result for #56: staging replayed to 194/194 parity with
every row md5-verified, and check:drift run against staging returning 19 findings
showing the migration chain does not reproduce supabase/schema.sql.
Four cancellation decisions are honoured, including the superseded #56 request
that recorded check:drift as blocked before the gate was actually run.
Re-run from base dda4956 so the batch is complete: the previous attempt was
cut against 9d83245 and left one later-arriving request pending, which the
write-discipline guard correctly rejected as a partial transaction.
BigSimmo added a commit that referenced this pull request Aug 18, 2026
* docs(issues): reconcile 17 queued ledger requests after PR #2093
Applies the queued inbox requests into docs/outstanding-issues.md, including the
Phase 2 staging-parity result for #56: staging replayed to 194/194 parity with
every row md5-verified, and check:drift run against staging returning 19 findings
showing the migration chain does not reproduce supabase/schema.sql.
Four cancellation decisions are honoured, including the superseded #56 request
that recorded check:drift as blocked before the gate was actually run.
Re-run from base dda4956 so the batch is complete: the previous attempt was
cut against 9d83245 and left one later-arriving request pending, which the
write-discipline guard correctly rejected as a partial transaction.
* docs(ledger): supersede the reconciliation review record after the re-cut
BigSimmo pushed a commit that referenced this pull request Aug 18, 2026
…nchor
Two stale pointers in the playbook's context briefing, both of which misdirect a
fresh session before it reads anything else.
The phase status block still said Phase 1.2 was outstanding and gating Phase 3,
and named "complete 1.2, then Phase 2" as the next step. Both have since landed:
1.2 in PR #2087 (all ten mismatches attribute-only SET work_mem, zero body
divergence, zero repo-ahead, zero UNCLASSIFIED) and Phase 2 in PR #2093 (staging
at full parity, check:drift against staging red with 19 items — the finding the
phase existed to produce, with a re-measure owed once staging carries
20260818090000). Phase 6 is repo-side complete with its migration deploy still
owed. The block now records that and defers to
docs/database-remediation-coordination.md as the live board rather than becoming
a second source of truth beside it.
It also flags that Phase 3 has been reframed: zero repo-ahead bodies means no
eval canary, which the Phase 3 prompt further down this file still assumes. That
prompt and the traps list are deliberately left untouched here — the reframing
and its owner decisions belong to the coordination board, and duplicating them
would recreate the drift this change is repairing.
The tracking anchor quoted the row title "Live DB is missing 21 repo-defined
indexes and 10 retrieval RPC bodies diverge". The row has since been retitled to
"Live DB has 20 currently missing repo-defined indexes and 10 retrieval RPC
bodies diverge; weekly live-drift has been red since 2026-07-26 with no routing",
so the exact-title lookup the playbook instructs could no longer match. The "21"
was preserved deliberately in #1979 because it was the lookup key; correcting the
row is what broke it. Anchor updated to the current title, verified character for
character against the row on main.
No issues:update queued: #316 and #56 are owned by other running chats, and a
second pending mutation on either row would make the inbox refuse the batch.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BKNFogaYfCQBvFVqFQnfRt
Sign up for freeto 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

@BigSimmo