-- P014: fills a real, previously-undiscovered gap in the migration chain. -- sql/004_iceberg_write_boundary.sql's own comment already documented (but -- did not fix) that `opt_dataset_iceberg_mappings.dataset_id REFERENCES -- opt_datasets (dataset_id)` points at a table NO sql/ file ever defined -- -- meaning `psql -d opt < sql/001... < sql/002... < sql/004...` (exactly the -- flow docs/runbook/backup-restore.md's own "MÃ¥lbild" describes, and exactly -- what a real restore drill executes) has never actually been runnable -- end to end. This was found by running that exact sequence for real against -- a throwaway Postgres container during P014's restore drill, not by -- inspecting the code. -- -- Numbered 003 (the previously-unused gap between 002_source_registry.sql -- and 004_iceberg_write_boundary.sql) so the existing ordering -- 001 -> 002 -- -> 004 -> 005 -- becomes 001 -> 002 -> 003 -> 004 -> 005 without renumbering -- anything already shipped. -- -- Columns mirror src/services/dataset-service.ts's `DatasetRecord` exactly -- (that file's own doc comment: "Storage shape: nullable columns, exactly as -- the eventual `opt_datasets` table would hold them") -- this is the real -- application contract, not a guess. `dataset_type`/`status` CHECK -- constraints mirror src/types/dataset.ts's `DatasetType`/`DatasetStatus` -- unions verbatim. DatasetService itself remains in-memory-only in this -- pass (wiring it to this table is a separate, larger change than closing -- the migration-chain gap); this table exists so the SCHEMA a fresh -- database gets by replaying sql/*.sql matches what 004/005 already assume. CREATE TABLE IF NOT EXISTS opt_datasets ( dataset_id TEXT PRIMARY KEY, tenant_id TEXT NOT NULL, project_id TEXT NOT NULL, name TEXT NOT NULL, dataset_type TEXT NOT NULL CHECK (dataset_type IN ('raw', 'structured', 'enriched', 'curated', 'feature', 'derived')), status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'archived', 'deprecated')), description TEXT, correlation_id TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ, created_by TEXT ); CREATE INDEX IF NOT EXISTS opt_datasets_tenant_project_idx ON opt_datasets (tenant_id, project_id);