From 66bdd28fea457f9f045a0481e01a035247c126d5 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Sat, 18 Jul 2026 21:59:55 +0800 Subject: [PATCH 1/2] docs: make migration rollback guidance safe --- docs/deploy-corrector-public-titles.md | 98 +++++++++++++------ ...r-apply-performance-latency-remediation.md | 28 ++++++ docs/operator-backlog.md | 18 ++-- ...-scalability-wip-remediation-2026-07-17.md | 10 ++ 4 files changed, 113 insertions(+), 41 deletions(-) diff --git a/docs/deploy-corrector-public-titles.md b/docs/deploy-corrector-public-titles.md index e15de5c85..9a0813ad6 100644 --- a/docs/deploy-corrector-public-titles.md +++ b/docs/deploy-corrector-public-titles.md @@ -1,7 +1,9 @@ -# Deploy runbook — public-titles corrector migration (F10) +# Deploy runbook — public-title corrector migrations (F10) -Applies migration `supabase/migrations/20260717120000_corrector_public_titles_only.sql` -to the live **Clinical KB Database** project (`sjrfecxgysukkwxsowpy`). +Covers the ordered corrector migrations +`supabase/migrations/20260717120000_corrector_public_titles_only.sql` and +`supabase/migrations/20260717171000_public_title_corrector.sql` for the live +**Clinical KB Database** project (`sjrfecxgysukkwxsowpy`). **What it does:** scopes `public.correct_clinical_query_terms` (the clinical query-term corrector) so its spell-correction vocabulary is built only from the **public @@ -9,13 +11,33 @@ corrector) so its spell-correction vocabulary is built only from the **public closes a cross-tenant side-channel where a `SECURITY DEFINER` read leaked private documents' title/alias tokens into other tenants' query corrections. -**Risk:** low and reversible. It is a `CREATE OR REPLACE FUNCTION` (+ `revoke`/`grant`) — -no data migration, no index build, no locks, signature unchanged. Validated on a -Supabase preview branch and by `npm run drift:manifest` (real Postgres replay). +**Risk:** medium and reversible with a reviewed forward migration. The `…120000` +privacy baseline is a signature-compatible `CREATE OR REPLACE FUNCTION`. The later +`…171000` implementation creates and backfills `document_title_words`, adds indexes +and a synchronization trigger, then replaces the same corrector without weakening +the public-only vocabulary boundary. It does not rewrite clinical source content, +but its DDL, index build, and backfill require the normal production-change controls. -**Status when this runbook was written:** the code side is fully merged to `main` — -migration (#697), plus `schema.sql` mirror + regenerated `drift-manifest.json` (#701). -The **only remaining step is applying the migration to the live project** (this step). +**Repository status:** both corrector migrations and their schema mirror are merged. +Live apply state is deliberately **unconfirmed** in this document. Never assume either +corrector migration is the sole pending migration; inspect the linked migration table +and review the complete pending sequence immediately before an authorized apply. + +## Required migration order + +Supabase applies migrations by filename. The relevant ordered sequence is: + +1. `20260717120000_corrector_public_titles_only.sql` — establishes the public-only + privacy baseline. +2. `20260717170000_registry_projection_cleanup.sql` — the earlier performance + migration in the current chain; see + [operator-apply-performance-latency-remediation.md](operator-apply-performance-latency-remediation.md). +3. `20260717171000_public_title_corrector.sql` — installs the indexed public-title + vocabulary while preserving the baseline's owner scoping. + +Other reviewed repository migrations can appear between or after these entries. Do +not selectively mark migrations applied, reorder filenames, or skip an earlier pending +migration to reach the corrector. --- @@ -56,21 +78,24 @@ npx supabase link --project-ref sjrfecxgysukkwxsowpy # may prompt for the D ```bash npm run check:supabase-project # must print: Clinical KB Database / sjrfecxgysukkwxsowpy -npx supabase migration list --linked # 20260717120000_corrector_public_titles_only must be the pending one +npx supabase migration list --linked # review every local/remote row and its order ``` -**Stop** if `check:supabase-project` shows staging/another project, if unexpected -migrations are pending, or if local/remote history looks divergent -(see `docs/supabase-migration-reconciliation.md`). +Confirm that any pending corrector entries use the exact filenames above, that +`…120000` precedes `…171000`, and that `…170000` is not skipped when it is pending. +It is valid for an earlier migration to already be recorded remotely. **Stop** if +`check:supabase-project` shows staging/another project, any pending migration has not +been reviewed and authorized as part of this rollout, or local/remote history looks +divergent (see `docs/supabase-migration-reconciliation.md`). -Optional — before applying, `npm run check:drift` will report the corrector as the -pending difference (manifest scoped, live still unscoped). That is expected and is what -the push clears. +Optional — before applying, `npm run check:drift` can identify repository/live schema +differences. Treat its output as evidence to review, not proof that the corrector is the +only pending change. ### 4. Apply ```bash -npx supabase db push # review the listed migration, confirm with `y` +npx supabase db push # applies every pending migration; review the full list before confirming ``` ### 5. Verify @@ -100,7 +125,9 @@ in step 1 will fail — then use Option A instead. Keep Codex's **"ask before running commands" / approval** setting ON. Start a task on `BigSimmo/Database` and paste: -> Deploy the already-merged migration `supabase/migrations/20260717120000_corrector_public_titles_only.sql` +> Deploy the already-merged corrector migrations +> `supabase/migrations/20260717120000_corrector_public_titles_only.sql` and +> `supabase/migrations/20260717171000_public_title_corrector.sql` > to the **live production** Supabase project **Clinical KB Database** (ref > `sjrfecxgysukkwxsowpy`), using the repo's linked-migration workflow. This is a > production clinical database — treat every step as confirmation-required and **pause @@ -108,14 +135,16 @@ Keep Codex's **"ask before running commands" / approval** setting ON. Start a ta > > 1. Run `npm run check:supabase-project` and show me the full output. Confirm it targets > `sjrfecxgysukkwxsowpy` (production), not staging. If it targets anything else, **stop**. -> 2. Run `npx supabase migration list --linked` and show me the output. Confirm -> `20260717120000_corrector_public_titles_only` is pending and local/remote history is -> aligned. If any other unexpected migration is pending, or history is divergent, -> **stop and ask me**. +> 2. Run `npx supabase migration list --linked` and show me the output. Confirm local +> and remote history are aligned; if pending, `20260717120000` must precede +> `20260717170000`, which must precede `20260717171000`. Enumerate every other +> pending migration. If any pending entry has not been reviewed for this rollout, +> or history is divergent, **stop and ask me**. > 3. If either command above fails because production credentials aren't configured in > this environment, **stop and tell me** — do not try to work around it. > 4. **PAUSE and wait for me to reply "go" before applying anything.** -> 5. After I say go: run `npx supabase db push` to apply the pending migration. +> 5. After I approve the exact list: run `npx supabase db push` to apply that reviewed +> pending sequence. Stop if the CLI proposes a different list. > 6. Verify: run `npm run check:drift` (must report "No unexpected schema drift") and > `npm run check:supabase-project`. Show me both outputs. > @@ -124,24 +153,29 @@ Keep Codex's **"ask before running commands" / approval** setting ON. Start a ta Your two decision points: - After steps 1–2: reply **"go"** only if the project is `Clinical KB Database / -sjrfecxgysukkwxsowpy` and the sole pending migration is this one. Otherwise stop and - review the output. +sjrfecxgysukkwxsowpy` and every pending migration, in order, is expected and + authorized. Otherwise stop and review the output. - After the push: ✅ `check:drift` = "No unexpected schema drift" → done. --- ## Rollback -Not expected — the scoped version is strictly safer and signature-compatible. If ever -needed, `CREATE OR REPLACE` the prior (unscoped) definition and regenerate the manifest; -but there is no functional reason to revert. +Do **not** restore the pre-`…120000` unscoped corrector: that would reopen the private +title/alias side channel. If the indexed `…171000` implementation must be backed out, +prepare and review a new forward migration that first restores the exact public-scoped +function definition from `20260717120000_corrector_public_titles_only.sql`, then removes +the title-word trigger/table/indexes only after dependency and lock review. Keep the +public-only predicate throughout, take the normal backup, and verify drift and the +function definition after the authorized apply. Never edit migration history or run an +ad hoc unscoped `CREATE OR REPLACE` in production. ## Why `check:drift` flips green after applying -Before the deploy, `main` carries the scoped corrector in `schema.sql` + the drift -manifest, while live still runs the unscoped version — so `check:drift` reports the -corrector as pending. `supabase db push` brings live into line, so the post-apply -`check:drift` matches the manifest and reports clean. +When live is behind the repository, `main` carries the final corrector and related +objects in `schema.sql` plus the drift manifest. Applying the complete reviewed pending +sequence brings live into line; only then should the post-apply `check:drift` match the +manifest and report clean. A clean result does not replace the migration-history check. ## Related diff --git a/docs/operator-apply-performance-latency-remediation.md b/docs/operator-apply-performance-latency-remediation.md index 9cb1faf3a..4345b48c2 100644 --- a/docs/operator-apply-performance-latency-remediation.md +++ b/docs/operator-apply-performance-latency-remediation.md @@ -3,6 +3,12 @@ This worktree does not apply migrations. Production rollout remains a separate, explicitly authorized operation after local replay, review, and backups. +The migration chain orders +`20260717170000_registry_projection_cleanup.sql` immediately before +`20260717171000_public_title_corrector.sql`. Apply them through the normal linked +migration workflow; do not mark either migration applied manually or skip the cleanup +migration to reach the corrector. + ## Registry projection index on a busy database `20260717170000_registry_projection_cleanup.sql` creates @@ -23,3 +29,25 @@ After the index is valid and ready, the migration's `create index if not exists` is a no-op. Do not mark the migration applied merely because the index exists: the cleanup function, hardened privileges, and three lifecycle triggers must still be installed through the normal authorized migration rollout. + +## Safe rollback + +Treat rollback as another reviewed forward migration; do not delete or repair the +recorded migration-history row. If registry-delete failures appear after rollout: + +1. Pause deletes from `clinical_registry_records`, `medication_records`, and + `differential_records` so removing cleanup cannot silently create new orphaned + corpus documents. +2. Preserve diagnostics and take the normal backup. Confirm whether the failure is in + the trigger function, the registry projection index, or unrelated application code. +3. In a new migration, drop the three delete triggers before dropping + `public.cleanup_registry_corpus_document()`. The projection index is harmless when + unused; leave it in place unless lock and dependency review justifies a separate + `DROP INDEX CONCURRENTLY` outside a migration transaction. +4. Reconcile any registry projection documents created or deleted during the incident + window before re-enabling registry deletes, then run the normal migration-history, + drift, privilege, and functional checks. + +Do not run these steps against a linked or live project without explicit production +approval. The rollback does not alter `20260717171000_public_title_corrector.sql` or +permit private titles in the corrector vocabulary. diff --git a/docs/operator-backlog.md b/docs/operator-backlog.md index 9a99254d3..c6e460240 100644 --- a/docs/operator-backlog.md +++ b/docs/operator-backlog.md @@ -17,15 +17,15 @@ Findings inventory for handover: [audit-handover-2026-07-14.md](audit-handover-2 ## Launch-gating actions -| Action | Status | Blocked by | Verify command | Runbook | -| -------------------------------------------------------------------------- | ---------- | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | -| Apply July-8 migration batch (a–g) to live | ✅ done | — | `SUPABASE_ENVIRONMENT=production npm run check:july8-live-batch` (2026-07-13: 6 live, apply=no-op) | [operator-apply-july8-batch.md](operator-apply-july8-batch.md) | -| Apply drift-codify forward migration (step 1h) | ✅ done | — | Applied and drift/readiness verified 2026-07-13; verify only unless new reviewed drift is found | [database-drift-detection.md](database-drift-detection.md) | -| Apply repo-ahead migrations to live (post-2026-07-13) | 🔎 verify | `20260713201542` · `20260714110000` · `20260717120000` committed, live-apply unconfirmed | `npm run check:drift`; then `eval:retrieval:quality` (36/36) for the corrector | [deploy-corrector-public-titles.md](deploy-corrector-public-titles.md) | -| Full release gate (bounded OpenAI spend) | ⏳ pending | migrations 1 applied | `npm run verify:release`; `npm run eval:quality -- --rag-only` | [launch-operator-runbook.md §2](launch-operator-runbook.md) | -| Provision staging Supabase project (`Clinical KB Staging`, ap-southeast-2) | ⏳ pending | — | `npm run check:indexing` after `db push` | [staging-setup.md](staging-setup.md) | -| Staging soak + rollback rehearsal on Railway | ⏳ pending | staging provisioned | `scripts/soak-test.ts --confirm-staging` (answer p95 ≤ 25 s) | [launch-operator-runbook.md §4](launch-operator-runbook.md) · [capacity-review.md](capacity-review.md) | -| Production deploy to Railway | ✅ done | — | App deployment recorded live 2026-07-14; re-verify with `GET /api/health` and deployment readiness | [deployment-architecture.md](deployment-architecture.md) | +| Action | Status | Blocked by | Verify command | Runbook | +| -------------------------------------------------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Apply July-8 migration batch (a–g) to live | ✅ done | — | `SUPABASE_ENVIRONMENT=production npm run check:july8-live-batch` (2026-07-13: 6 live, apply=no-op) | [operator-apply-july8-batch.md](operator-apply-july8-batch.md) | +| Apply drift-codify forward migration (step 1h) | ✅ done | — | Applied and drift/readiness verified 2026-07-13; verify only unless new reviewed drift is found | [database-drift-detection.md](database-drift-detection.md) | +| Apply repo-ahead migrations to live (post-2026-07-13) | 🔎 verify | `20260713201542` · `20260714110000` · `20260717120000` · `20260717170000` · `20260717171000` committed, live-apply unconfirmed | `npm run check:drift`; then `eval:retrieval:quality` (36/36) for the corrector | [deploy-corrector-public-titles.md](deploy-corrector-public-titles.md) · [operator-apply-performance-latency-remediation.md](operator-apply-performance-latency-remediation.md) | +| Full release gate (bounded OpenAI spend) | ⏳ pending | migrations 1 applied | `npm run verify:release`; `npm run eval:quality -- --rag-only` | [launch-operator-runbook.md §2](launch-operator-runbook.md) | +| Provision staging Supabase project (`Clinical KB Staging`, ap-southeast-2) | ⏳ pending | — | `npm run check:indexing` after `db push` | [staging-setup.md](staging-setup.md) | +| Staging soak + rollback rehearsal on Railway | ⏳ pending | staging provisioned | `scripts/soak-test.ts --confirm-staging` (answer p95 ≤ 25 s) | [launch-operator-runbook.md §4](launch-operator-runbook.md) · [capacity-review.md](capacity-review.md) | +| Production deploy to Railway | ✅ done | — | App deployment recorded live 2026-07-14; re-verify with `GET /api/health` and deployment readiness | [deployment-architecture.md](deployment-architecture.md) | ## Post-deploy actions diff --git a/docs/rag-scalability-wip-remediation-2026-07-17.md b/docs/rag-scalability-wip-remediation-2026-07-17.md index 3756ac818..04ef31938 100644 --- a/docs/rag-scalability-wip-remediation-2026-07-17.md +++ b/docs/rag-scalability-wip-remediation-2026-07-17.md @@ -1,5 +1,15 @@ # RAG-scalability WIP review — remediation report (2026-07-17) +> [!IMPORTANT] +> **Superseded historical record.** The deferred registry-cleanup and public-title +> corrector work described below was later implemented in +> `20260717170000_registry_projection_cleanup.sql` and +> `20260717171000_public_title_corrector.sql`. Do not use this report's status or next +> steps as current operator guidance. Use +> [operator-apply-performance-latency-remediation.md](operator-apply-performance-latency-remediation.md), +> [deploy-corrector-public-titles.md](deploy-corrector-public-titles.md), and +> [operator-backlog.md](operator-backlog.md) instead. + **Status:** F11 shipped and merged; all correctness findings verified moot on this branch; scalability build recorded and deferred pending go-ahead. **Responds to:** the "Handover — RAG scalability WIP review findings (2026-07-15)" From a7e3aa4d657d4359dfaa5a1b3405b43c14478a88 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Sat, 18 Jul 2026 22:08:34 +0800 Subject: [PATCH 2/2] docs: block unsafe title-word rollout --- docs/deploy-corrector-public-titles.md | 73 +++++++++++++++---- ...r-apply-performance-latency-remediation.md | 5 ++ docs/operator-backlog.md | 18 ++--- 3 files changed, 74 insertions(+), 22 deletions(-) diff --git a/docs/deploy-corrector-public-titles.md b/docs/deploy-corrector-public-titles.md index 9a0813ad6..38eee5c12 100644 --- a/docs/deploy-corrector-public-titles.md +++ b/docs/deploy-corrector-public-titles.md @@ -14,9 +14,9 @@ documents' title/alias tokens into other tenants' query corrections. **Risk:** medium and reversible with a reviewed forward migration. The `…120000` privacy baseline is a signature-compatible `CREATE OR REPLACE FUNCTION`. The later `…171000` implementation creates and backfills `document_title_words`, adds indexes -and a synchronization trigger, then replaces the same corrector without weakening -the public-only vocabulary boundary. It does not rewrite clinical source content, -but its DDL, index build, and backfill require the normal production-change controls. +and a synchronization trigger, then replaces the same corrector. It does not rewrite +clinical source content, but its DDL, index build, and backfill require the normal +production-change controls and the stale-row blocker below must be cleared first. **Repository status:** both corrector migrations and their schema mirror are merged. Live apply state is deliberately **unconfirmed** in this document. Never assume either @@ -33,12 +33,49 @@ Supabase applies migrations by filename. The relevant ordered sequence is: migration in the current chain; see [operator-apply-performance-latency-remediation.md](operator-apply-performance-latency-remediation.md). 3. `20260717171000_public_title_corrector.sql` — installs the indexed public-title - vocabulary while preserving the baseline's owner scoping. + vocabulary. Its trigger keeps new rows public-only, but the migration does not purge + unsafe rows inserted by the earlier `20260714180000` migration. Other reviewed repository migrations can appear between or after these entries. Do not selectively mark migrations applied, reorder filenames, or skip an earlier pending migration to reach the corrector. +## 🛑 Rollout blocker — stale title-word rows + +The repository migration chain includes +`20260714180000_patch_rag_and_corrector_scalability.sql`, which originally backfilled +`document_title_words` from **all** indexed documents. `20260717171000` uses +`CREATE TABLE IF NOT EXISTS` and a public-only `INSERT ... ON CONFLICT DO NOTHING`; it +does not remove private or non-indexed rows already in that table. Because the final +corrector reads every `document_title_words.word`, applying the chain as written can +reintroduce private-title vocabulary into the `SECURITY DEFINER` result. + +Do **not** authorize a linked/live apply that includes `20260717171000` until all of +the following are true: + +1. A separately reviewed forward migration is present in the pending chain and deletes + every `document_title_words` row whose joined document has `owner_id IS NOT NULL` or + `status IS DISTINCT FROM 'indexed'` before corrector traffic is re-enabled. +2. If `20260717171000` and that cleanup will apply in the same rollout, keep corrector + traffic disabled for the complete migration window so the intermediate unsafe state + cannot serve requests. +3. The following invariant returns `0` after the cleanup and before traffic resumes: + + ```sql + select count(*) as unsafe_title_word_rows + from public.document_title_words as title_word + join public.documents as source_document on source_document.id = title_word.document_id + where source_document.owner_id is not null + or source_document.status is distinct from 'indexed'; + ``` + +4. The cleanup migration, backup, lock plan, and verification output receive explicit + production approval. Do not paste the `DELETE` ad hoc into production or repair the + migration-history table. + +If the cleanup migration is absent or the invariant is non-zero, **stop**. This runbook +records the blocker; it does not itself authorize or implement the database cleanup. + --- ## Prerequisites @@ -86,7 +123,8 @@ Confirm that any pending corrector entries use the exact filenames above, that It is valid for an earlier migration to already be recorded remotely. **Stop** if `check:supabase-project` shows staging/another project, any pending migration has not been reviewed and authorized as part of this rollout, or local/remote history looks -divergent (see `docs/supabase-migration-reconciliation.md`). +divergent (see `docs/supabase-migration-reconciliation.md`). Also stop if the +stale-title-word cleanup described above is missing from the reviewed pending chain. Optional — before applying, `npm run check:drift` can identify repository/live schema differences. Treat its output as evidence to review, not proof that the corrector is the @@ -112,6 +150,9 @@ npx supabase db query --linked "select pg_get_functiondef('public.correct_clinic # expect: scoped = true ``` +Also run the zero-unsafe-row invariant from the rollout-blocker section. Both checks +must pass before corrector traffic is enabled. + Optional (retrieval-affecting): `npm run eval:retrieval:quality` — must stay 36/36 (needs live keys). --- @@ -140,22 +181,28 @@ Keep Codex's **"ask before running commands" / approval** setting ON. Start a ta > `20260717170000`, which must precede `20260717171000`. Enumerate every other > pending migration. If any pending entry has not been reviewed for this rollout, > or history is divergent, **stop and ask me**. -> 3. If either command above fails because production credentials aren't configured in +> 3. Confirm a separately reviewed forward migration purges private/non-indexed +> `document_title_words` rows. If it is absent, **stop**; do not apply `20260717171000`. +> Keep corrector traffic disabled until that cleanup and its zero-row invariant pass. +> 4. If either command above fails because production credentials aren't configured in > this environment, **stop and tell me** — do not try to work around it. -> 4. **PAUSE and wait for me to reply "go" before applying anything.** -> 5. After I approve the exact list: run `npx supabase db push` to apply that reviewed +> 5. **PAUSE and wait for me to reply "go" before applying anything.** +> 6. After I approve the exact list: run `npx supabase db push` to apply that reviewed > pending sequence. Stop if the CLI proposes a different list. -> 6. Verify: run `npm run check:drift` (must report "No unexpected schema drift") and -> `npm run check:supabase-project`. Show me both outputs. +> 7. Verify: run the zero-unsafe-title-word invariant, `npm run check:drift` (must report +> "No unexpected schema drift"), and `npm run check:supabase-project`. Show me all +> outputs before corrector traffic is re-enabled. > > Do not run OpenAI, modify other code, or push commits. Only the steps above. Your two decision points: -- After steps 1–2: reply **"go"** only if the project is `Clinical KB Database / +- After steps 1–4: reply **"go"** only if the project is `Clinical KB Database / sjrfecxgysukkwxsowpy` and every pending migration, in order, is expected and - authorized. Otherwise stop and review the output. -- After the push: ✅ `check:drift` = "No unexpected schema drift" → done. + authorized, including the forward title-word cleanup. Otherwise stop and review the + output. +- After the push: ✅ unsafe title-word count = `0` and `check:drift` = "No unexpected + schema drift" → done. --- diff --git a/docs/operator-apply-performance-latency-remediation.md b/docs/operator-apply-performance-latency-remediation.md index 4345b48c2..ac3eb6543 100644 --- a/docs/operator-apply-performance-latency-remediation.md +++ b/docs/operator-apply-performance-latency-remediation.md @@ -9,6 +9,11 @@ The migration chain orders migration workflow; do not mark either migration applied manually or skip the cleanup migration to reach the corrector. +The corrector has a separate stale-title-word rollout blocker caused by the earlier +`20260714180000` migration. Clear the invariant in +[deploy-corrector-public-titles.md](deploy-corrector-public-titles.md) before applying +or enabling `20260717171000`. + ## Registry projection index on a busy database `20260717170000_registry_projection_cleanup.sql` creates diff --git a/docs/operator-backlog.md b/docs/operator-backlog.md index c6e460240..ee79a9dfe 100644 --- a/docs/operator-backlog.md +++ b/docs/operator-backlog.md @@ -17,15 +17,15 @@ Findings inventory for handover: [audit-handover-2026-07-14.md](audit-handover-2 ## Launch-gating actions -| Action | Status | Blocked by | Verify command | Runbook | -| -------------------------------------------------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Apply July-8 migration batch (a–g) to live | ✅ done | — | `SUPABASE_ENVIRONMENT=production npm run check:july8-live-batch` (2026-07-13: 6 live, apply=no-op) | [operator-apply-july8-batch.md](operator-apply-july8-batch.md) | -| Apply drift-codify forward migration (step 1h) | ✅ done | — | Applied and drift/readiness verified 2026-07-13; verify only unless new reviewed drift is found | [database-drift-detection.md](database-drift-detection.md) | -| Apply repo-ahead migrations to live (post-2026-07-13) | 🔎 verify | `20260713201542` · `20260714110000` · `20260717120000` · `20260717170000` · `20260717171000` committed, live-apply unconfirmed | `npm run check:drift`; then `eval:retrieval:quality` (36/36) for the corrector | [deploy-corrector-public-titles.md](deploy-corrector-public-titles.md) · [operator-apply-performance-latency-remediation.md](operator-apply-performance-latency-remediation.md) | -| Full release gate (bounded OpenAI spend) | ⏳ pending | migrations 1 applied | `npm run verify:release`; `npm run eval:quality -- --rag-only` | [launch-operator-runbook.md §2](launch-operator-runbook.md) | -| Provision staging Supabase project (`Clinical KB Staging`, ap-southeast-2) | ⏳ pending | — | `npm run check:indexing` after `db push` | [staging-setup.md](staging-setup.md) | -| Staging soak + rollback rehearsal on Railway | ⏳ pending | staging provisioned | `scripts/soak-test.ts --confirm-staging` (answer p95 ≤ 25 s) | [launch-operator-runbook.md §4](launch-operator-runbook.md) · [capacity-review.md](capacity-review.md) | -| Production deploy to Railway | ✅ done | — | App deployment recorded live 2026-07-14; re-verify with `GET /api/health` and deployment readiness | [deployment-architecture.md](deployment-architecture.md) | +| Action | Status | Blocked by | Verify command | Runbook | +| -------------------------------------------------------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Apply July-8 migration batch (a–g) to live | ✅ done | — | `SUPABASE_ENVIRONMENT=production npm run check:july8-live-batch` (2026-07-13: 6 live, apply=no-op) | [operator-apply-july8-batch.md](operator-apply-july8-batch.md) | +| Apply drift-codify forward migration (step 1h) | ✅ done | — | Applied and drift/readiness verified 2026-07-13; verify only unless new reviewed drift is found | [database-drift-detection.md](database-drift-detection.md) | +| Apply repo-ahead migrations to live (post-2026-07-13) | ⏳ pending | Queue includes `20260717120000`, `20260717170000`, and `20260717171000`; a forward migration to purge private/non-indexed `document_title_words` rows must be merged and reviewed before apply | Zero unsafe title-word rows; `npm run check:drift`; then `eval:retrieval:quality` (36/36) for the corrector | [deploy-corrector-public-titles.md](deploy-corrector-public-titles.md) · [operator-apply-performance-latency-remediation.md](operator-apply-performance-latency-remediation.md) | +| Full release gate (bounded OpenAI spend) | ⏳ pending | migrations 1 applied | `npm run verify:release`; `npm run eval:quality -- --rag-only` | [launch-operator-runbook.md §2](launch-operator-runbook.md) | +| Provision staging Supabase project (`Clinical KB Staging`, ap-southeast-2) | ⏳ pending | — | `npm run check:indexing` after `db push` | [staging-setup.md](staging-setup.md) | +| Staging soak + rollback rehearsal on Railway | ⏳ pending | staging provisioned | `scripts/soak-test.ts --confirm-staging` (answer p95 ≤ 25 s) | [launch-operator-runbook.md §4](launch-operator-runbook.md) · [capacity-review.md](capacity-review.md) | +| Production deploy to Railway | ✅ done | — | App deployment recorded live 2026-07-14; re-verify with `GET /api/health` and deployment readiness | [deployment-architecture.md](deployment-architecture.md) | ## Post-deploy actions