2.7 KiB
RB-03 CRITICAL: transcript provenance can reference deleted profiles
Severity: CRITICAL
Path: crates/storage/src/migrations.rs:208-216, crates/storage/src/database.rs:61-89, :697-708
Source: 2026-04-22 code review
Labels: release-blocker, critical, data-integrity, storage
Status: RESOLVED (2026-04-22)
Resolution
Chose the strict provenance path:
- Migration v9 rebuilds
transcriptswithprofile_id REFERENCES profiles(id) ON DELETE RESTRICT. - Existing orphaned transcript
profile_idvalues are reconciled ontoDEFAULT_PROFILE_IDduring the copy into the rebuilt table. - Because SQLite table renames rewrite dependent references, the
migration also rebuilds
segments, recreates the transcript FTS virtual table/triggers, and repopulates FTS from the rebuilt transcript rows inside the same transaction.
Application-layer behaviour now matches the schema:
insert_transcriptrejects unknownprofile_idvalues with a clear storage error before attempting the insert.delete_profilereturns a human-readable reassign-first error when transcripts still reference that profile.
Regression tests:
migration_v9_reconciles_orphaned_transcript_profiles_and_adds_fkinsert_transcript_rejects_unknown_profile_iddelete_profile_rejects_when_transcripts_reference_it
Problem
v8 migration adds transcripts.profile_id but without a FOREIGN KEY constraint. insert_transcript accepts any profile_id string without validation. delete_profile doesn't guard against existing transcript references. The combined result: persisted transcripts can keep orphaned profile IDs indefinitely, breaking provenance integrity.
Acceptance
- A v9 migration adds
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE RESTRICT(orON DELETE SET NULLif soft-orphaning is preferred — decide during the fix). - The migration reconciles existing orphans: either backfill with
DEFAULT_PROFILE_ID, or null them, per the chosen FK semantic. insert_transcriptpasses the FK check — no behaviour change on the happy path.delete_profilereturns a meaningful error when transcripts reference the profile being deleted (or cascades to null, matching the FK semantic).- Regression tests: (a) delete_profile with transcript references behaves per the chosen semantic; (b) insert_transcript with a non-existent profile_id errors; (c) existing orphans are reconciled on first migration to v9.
Fix scope
Large. FK constraint design decision + migration + reconciliation + database.rs updates + tests.
Dependencies
- Blocked by: RB-02 (migrations atomicity — the v9 migration must be transactional).