Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
- [ ] `npm run verify:ui` when UI, routing, styling, browser behavior, reduced-motion, or forced-colors behavior changed
- [ ] `npm run verify:release` before release or handoff confidence claims
- [ ] `npm run format:check`
- [ ] **`npm run eval:retrieval:quality` (must stay 23/23) when retrieval, ranking, selection, chunking, or scoring behavior changed** — CI cannot run it (needs live keys), so run it locally and paste the summary. A metadata/governance-weighting change once buried correct docs (recall 1.0→0.76) and only this eval caught it.
- [ ] **`npm run eval:retrieval:quality` (must stay 36/36) when retrieval, ranking, selection, chunking, or scoring behavior changed** — CI cannot run it (needs live keys), so run it locally and paste the summary. A metadata/governance-weighting change once buried correct docs (recall 1.0→0.76) and only this eval caught it.
- [ ] `npm run eval:rag -- --limit 15` + `npm run eval:quality -- --rag-only` when answer generation, the synthesis prompt, or answer post-processing changed (grounded-supported must not drop; citation-failure 0)
- [ ] `npm run check:production-readiness` when clinical workflow, privacy, environment, Supabase, source governance, or deployment behavior changed
- [ ] `npm run check:deployment-readiness` when deployment startup, hosting, or rollout behavior changed
Expand Down
25 changes: 16 additions & 9 deletions docs/ingestion-concurrency-fix-workorder.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,25 @@ Sequenced, operator-applied plan for the state-machine violations that could

Author date: 2026-07-08. All facts below were read from the **live** project
`Clinical KB Database` (`sjrfecxgysukkwxsowpy`) via read-only `execute_sql`;
nothing here was applied to live. Function bodies are quoted from
nothing here was applied to live at author time. **Status refresh 2026-07-09:** July 8 migrations are merged to `main` but pending live apply — see [`docs/operator-apply-july8-batch.md`](operator-apply-july8-batch.md). Function bodies are quoted from
`pg_get_functiondef` so migrations are derived from live truth, not `schema.sql`
(which is known-drifted — see R24e and `docs/database-drift-detection.md`).

## Already landed (do not redo)
## Already landed on `main` (do not redo)

- **R11, R15/R16, R22, R24d-gate** — merged (PR #346).
- **R11 janitor-side guard** (`scripts/cleanup-storage.ts` +
`src/lib/storage-cleanup-safety.ts`) and **R1 lease heartbeat**
(`shouldPersistJobProgress` in `src/lib/ingestion.ts`, worker refresh of
`locked_at`) — merged (PR #369).
- **R1/R2/R7/R9/R23 RPC hardening** — migration `20260708130000` merged (PR #380); **pending live apply**.
- **R24e** — phantom `ingestion_job_stages.job_id` FK dropped from `schema.sql` (PR #380 batch); **pending live apply** (no-op on live).
- **R17** — partial unique index + reindex-route 409 handling merged (PR #405); migration `20260708170000` — **pending live apply** (normal `db push` when queue quiet).
- **R5** — metadata deep-merge RPC + worker merged (PR #408); **pending live apply** + worker redeploy.

## Still open (not merged or needs design)

- **deep-memory delete-scoping** — design required (see bottom of this doc).

## Global rules for this release

Expand Down Expand Up @@ -298,13 +306,12 @@ naive scope — it can crash live enrichment.**

## Suggested landing order

1. **R24e** (repo-only, unblocks fresh/preview envs) — via drift backlog #8.
2. **R9**, **R23** (self-contained, behavior-preserving RPCs, DB-only).
3. **R2 fences** (expand/contract: migration, then worker deploy) — the root fix.
4. **R7** (small; pick option b if retry/recovery is the source).
5. **R17** (with the reindex-route 409 change) — via drift backlog #7.
6. **R5** (coordinated worker + commit RPC).
7. **deep-memory scoping** — only after the section-ownership design is agreed.
**Superseded for merged items** — use [`docs/operator-apply-july8-batch.md`](operator-apply-july8-batch.md)
for live apply of R24e → RPC hardening → fail-closed → R5 → R17 (`20260708170000`).

Remaining repo work:

1. **deep-memory scoping** — only after the section-ownership design is agreed.

Every DB item ends with `npm run check:drift` green and (for retrieval-affecting
items) `npm run eval:retrieval:quality` unchanged.
86 changes: 86 additions & 0 deletions docs/operator-apply-july8-batch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Operator apply — July 8 ingestion & tenancy batch

Consolidated runbook for migrations merged to `main` in PRs **#380**, **#405**,
**#408**, and **#409** that are **in the repo but not yet verified on live** as
of 2026-07-09. Companion to
[`docs/supabase-migration-reconciliation.md`](supabase-migration-reconciliation.md)
and [`docs/ingestion-concurrency-fix-workorder.md`](ingestion-concurrency-fix-workorder.md).

## Pre-flight

- Live env vars must target **production** (verify with `npm run check:supabase-project`). `npm run check:july8-live-batch` refuses staging or other non-production targets so a green probe cannot mask an unapplied production database. Set `SUPABASE_ENVIRONMENT=production` (or leave unset — production is the default).

```bash
npx supabase migration list --linked
npm run supabase:recovery-status # or npm run check:supabase-project
npm run reindex:health # jobs_pending = 0 AND jobs_processing = 0 before R17
```

Do **not** redeploy the ingestion worker from current `main` until step 2 below
is live — `worker/main.ts` already passes `p_worker_id` to completion RPCs.

## Apply order

All steps below are safe through a single `supabase db push` when the ingestion
queue is quiet. R17 uses its **own migration version** (`20260708170000`, not
`20260708160000`) so history/repair cannot collide with the fail-closed tenancy
migration at `20260708160000`.

| Step | Migration | What | How |
| ---- | --------------------------------------------------------- | -------------------------------------------- | ---------------------------------------------- |
| 1 | `20260708140000_drop_ingestion_job_stages_job_id_fk.sql` | R24e — drop phantom FK from fresh-env schema | Normal `supabase db push` (no-op on live) |
| 2 | `20260708130000_ingestion_concurrency_rpc_hardening.sql` | R1/R2 lease fences, R7/R9/R23 RPC hardening | Normal push — **apply before worker redeploy** |
| 3 | `20260708150000_ensure_retrieval_owner_matches.sql` | Ensure helper exists before fail-closed | Normal push |
| 4 | `20260708160000_retrieval_owner_matches_fail_closed.sql` | Tenancy fail-closed (#409) | Normal push |
| 5 | `20260708310000_r5_document_metadata_merge.sql` | R5 metadata deep-merge (#408) | Normal push (safe before worker) |
| 6 | `20260708170000_ingestion_jobs_one_open_per_document.sql` | R17 one-open-job index (#405) | Normal push when queue quiet — see below |

Reindex routes for R17 (409 on duplicate job) are already on `main`.

### R17 on a busy live queue (optional)

When `jobs_pending` / `jobs_processing` are non-zero, or you want a lock-free
build, apply the index manually **before** recording migration history:

```sql
create unique index concurrently if not exists ingestion_jobs_one_open_per_document_uidx
on public.ingestion_jobs (document_id)
where status in ('pending', 'processing');
```

Then mark only the R17 version as applied (never `20260708160000`):

```bash
supabase migration repair --linked --status applied 20260708170000
```

If the queue is quiet, skip the manual SQL — step 6 in the table applies the
transactional `CREATE UNIQUE INDEX` via `db push`.

## Post-apply verification

```bash
SUPABASE_ENVIRONMENT=production npm run check:july8-live-batch # requires live keys — fails if absent or staging
npm run check:drift
npm run check:indexing
npm run eval:retrieval:quality # must stay 36/36 (retrieval-affecting: step 4 only)
```

Then redeploy the ingestion **worker** so R5 merge + `p_worker_id` fences are
active end-to-end.

## Probe semantics (`check:july8-live-batch`)

| Check | Pass means |
| ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `retrieval_owner_matches(null, …)` | Returns **false** (fail-closed live) |
| `jsonb_merge_deep` | RPC exists and merges objects |
| `complete_ingestion_job` + `p_worker_id` | Accepts the lease-fence parameter (returns `ok:false`, not signature error) |
| R17 index | Named in `schema_drift_snapshot` with the partial-unique definition on `ingestion_jobs(document_id)` for **only** `pending`/`processing`, duplicate open-job insert rejected with a violation citing `ingestion_jobs_one_open_per_document_uidx`, probe rows tagged with stage `july8-live-batch-probe` and swept before/after enforcement (cleanup errors fail the check) |

## Still open (not this batch)

- Forward-codify live-ahead retrieval RPC bodies (`docs/database-drift-detection.md` backlog #0)
- deep-memory delete scoping design (`docs/ingestion-concurrency-fix-workorder.md`)
- Staging soak (`docs/staging-setup.md`, `docs/capacity-review.md`)
- `registry:seed` per owner (`docs/process-hardening.md`)
17 changes: 13 additions & 4 deletions docs/process-hardening.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,16 +93,16 @@ All approved render-surface modules are extracted. `ClinicalDashboard.tsx` went
- **Known follow-up debts (documented, not actioned):**
- Live migration history has duplicate-version churn (two each of `api_rate_limits`, `audit_logs`, `rag_queries_retention`, `audit_logs_service_role_policy`, `indexing_reliability_recovery`) from the same raw-apply habit. Do not rewrite history; treat as a caution for future applies.
- Auth server is capped at 10 absolute DB connections (Supabase advisor); switch to percentage-based allocation in the dashboard before scaling instance size (not settable via SQL/MCP).
- `storage_cleanup_jobs` live indexes drifted from `supabase/schema.sql`: live carries legacy auto-names (`storage_cleanup_jobs_document_id_idx`, `storage_cleanup_jobs_owner_id_idx`, a non-partial `storage_cleanup_jobs_status_created_idx`) that the hardening defs superseded. Migration `20260703030000_reconcile_storage_cleanup_jobs_indexes` is **prepared but NOT applied** — it drops the legacy names and (re)creates the intended named/partial indexes to match schema.sql. Functional-not-broken (the document_id FK is covered), so apply to live only with explicit approval. `20260703000000`/`010000` are also absent from live `schema_migrations` and will self-heal on the next `supabase db push`.
- **`storage_cleanup_jobs` indexes: RESOLVED 2026-07-08** — `20260703030000` + `20260708000000_reapply_storage_cleanup_jobs_indexes` applied and verified on live (see `docs/archive/operator-decisions-2026-07-06.md`).

## Live database drift reconciliation (2026-07-05)

- **RESOLVED:** `indexing_v3_agent_jobs` table + `claim_indexing_v3_agent_jobs` + `update_indexing_v3_agent_job_status` were recorded as applied but absent on live. Migration `20260705230000_reconcile_live_database_drift` idempotently re-applied them; live verified post-push.
- **RESOLVED:** `match_document_embedding_fields_text` codified with service_role-only execute.
- **Follow-up (2026-07-06):** the codified `match_document_embedding_fields_text` kept the legacy `(owner_filter is null or d.owner_id = owner_filter)` predicate instead of `retrieval_owner_matches`, so it ignores the public-owner sentinel (anonymous sentinel would match zero rows; a real owner id excludes public docs). Migration `20260706130000_fix_embedding_fields_text_owner_sentinel` recreates it with the shared predicate — **prepared but NOT applied to live**; latent until the `_text` RPC is wired into app code, so apply with the next approved push.
- **RESOLVED (2026-07-08):** `match_document_embedding_fields_text` owner-sentinel fix (`20260706130000`) applied to live; latent until the `_text` RPC is wired into app code.
- **RESOLVED:** `rag_visual_eval_*` tables codified with service_role-only RLS.
- **RESOLVED:** Live-only `20260705133000_tighten_search_document_chunks_owner_scope` mirrored in `schema.sql`.
- **Edge function follow-up:** deploy `indexing-v3-agent` after merge so JSONB status RPC parsing is live.
- **RESOLVED (2026-07-08):** `indexing-v3-agent` edge function deployed (version 53, JSONB status-RPC parsing live — see `docs/archive/operator-decisions-2026-07-06.md`).
- **Operator-only:** publishable key rotation (`docs/archive/operator-decisions-2026-07-04.md`).

## Full-inventory drift detection & DR rehearsal (2026-07-07)
Expand All @@ -119,6 +119,15 @@ All approved render-surface modules are extracted. `ClinicalDashboard.tsx` went
- **Live is under active concurrent multi-session editing** — during this work a new `corpus_topic_term_stats` function appeared and `search_schema_health` re-drifted minutes after apply. The drift allowlist is therefore an explicit point-in-time snapshot needing periodic regeneration.
- `apply_migration` records history rows under a current-UTC version with the passed name (documented churn), not the repo file's version number; re-applying the repo files later is a harmless idempotent no-op.

### July 8 ingestion & tenancy batch — merged to main, pending live apply (2026-07-09)

PRs **#380**, **#405**, **#408**, **#409** landed ingestion RPC hardening (R1/R2/R7/R9/R23), R17
one-open-job index, R5 metadata deep-merge, and `retrieval_owner_matches` fail-closed. Migrations are
in `supabase/migrations/` but **not yet verified on live** as of 2026-07-09. **Do not redeploy the
ingestion worker from current `main` until `20260708130000` is live** — `worker/main.ts` already
passes `p_worker_id`. Ordered apply steps, R17 manual `CONCURRENTLY` index, and post-apply probes:
[`docs/operator-apply-july8-batch.md`](operator-apply-july8-batch.md) · `npm run check:july8-live-batch`.

## PR merge gate: tiered CI + required checks (2026-07-02)

- CI is now two parallel PR jobs instead of one serial 6-7 minute job: `verify` (runtime alignment, edge typecheck, CI-safe production readiness, lint, typecheck, unit tests with coverage gate, build — ~3 min) and `ui-smoke` (Chromium Playwright smoke against its own dev server — ~4.5 min). Wall-clock PR feedback drops to the slower of the two, and a flaky smoke rerun no longer repeats lint/typecheck/tests/build.
Expand Down Expand Up @@ -169,7 +178,7 @@ All approved render-surface modules are extracted. `ClinicalDashboard.tsx` went

## Retrieval changes must pass the golden eval before merge (2026-07-03)

- **Any PR that touches retrieval, ranking, selection, chunking, or scoring MUST run `npm run eval:retrieval:quality` (23/23) locally before merge** and paste the summary in the PR. CI cannot run it (it needs live Supabase + OpenAI keys), so it is a manual gate — now a checkbox in the PR template.
- **Any PR that touches retrieval, ranking, selection, chunking, or scoring MUST run `npm run eval:retrieval:quality` (36/36) locally before merge** and paste the summary in the PR. CI cannot run it (it needs live Supabase + OpenAI keys), so it is a manual gate — now a checkbox in the PR template.
- **Why (measured):** PR #118 caught a main-side change (uncapped candidate score + blanket source-governance metadata weighting in `retrieval-selection.ts`) that regressed the golden set 23/23 → 16/23 (doc-recall@5 1.0 → 0.76) on the partially-enriched corpus. `verify:cheap` was green throughout — only the golden retrieval eval surfaced it. Unit tests do not exercise live ranking, so they cannot substitute.
- **Standing constraint (do not relearn):** source-governance metadata (`document_status`/`clinical_validation_status`/`extraction_quality`) must NOT weight retrieval **selection ordering**, and candidate relevance scores must stay clamped. Live scores saturate at 1.0 and the corpus is only partially enriched (unenriched → unknown/unverified), so metadata weighting buries correct documents. Governance belongs in ranking penalties and the answer/source-governance layer. See [[no-governance-weighting-in-retrieval-selection]] and `docs/rag-hybrid-findings-and-todo.md` (RC8).
- **Answer-generation changes** (synthesis prompt, post-processing) additionally run `eval:rag --limit 15` + `eval:quality --rag-only` (grounded-supported must not drop; citation-failure 0). A new opt-in `npm run eval:answer-quality` reports a structural per-intent **targeting** metric (informational) for measuring how precisely answers hit the asked question.
Expand Down
7 changes: 6 additions & 1 deletion docs/redesign/04-deferred.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ There was a lockfile/install mismatch around ESLint that predated and was indepe
- **Mitigation used:** redesign code was additionally linted via the eslint 9.39.4 engine against the same flat config — all changed TS/TSX files are lint-clean.
- **Recommended fix:** run the `dependency` shortcut, or bump `eslint-plugin-react` to a release compatible with ESLint 10, then reconcile the lockfile.

## 2. ClinicalDashboard decomposition (Tier 2, see decision log D5)
## 2. ClinicalDashboard decomposition (resolved July 6, 2026)

**DONE** — all approved render-surface modules extracted to
`src/components/clinical-dashboard/`; admin surfaces cut over 2026-07-06. See
`docs/process-hardening.md` (Phase 3) for the full progress log (~8.8k → ~3.5k
lines in the orchestrator). Original deferral note kept below for history.

The 4,655-line `src/components/ClinicalDashboard.tsx` was not split into `clinical-dashboard/` modules this pass. It remains fully functional; this is maintainability-only.

Expand Down
7 changes: 6 additions & 1 deletion docs/supabase-migration-reconciliation.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,9 @@ npm run check:indexing

## Operator follow-ups

Manual key rotation and live migration apply decisions are recorded in [`docs/archive/operator-decisions-2026-07-04.md`](archive/operator-decisions-2026-07-04.md). Do not execute those steps from CI or agent automation without explicit operator approval.
Manual key rotation and live migration apply decisions are recorded in
[`docs/archive/operator-decisions-2026-07-04.md`](archive/operator-decisions-2026-07-04.md)
and [`docs/archive/operator-decisions-2026-07-06.md`](archive/operator-decisions-2026-07-06.md).
The **July 8 ingestion & tenancy batch** (merged to `main`, pending live apply as of
2026-07-09) is in [`docs/operator-apply-july8-batch.md`](operator-apply-july8-batch.md).
Do not execute live applies from CI or agent automation without explicit operator approval.
Loading
Loading