-- Migration 046: publication semantics (ADDITIVE ONLY). -- -- Every statement is ADD COLUMN IF NOT EXISTS or CREATE INDEX. Nothing is -- dropped, renamed, retyped or backfilled. Historical rows keep NULL. -- -- ── WHY 045 WAS NOT ENOUGH ──────────────────────────────────────────────── -- Migration 045 gave `model_snapshots` a lineage. Tracing the publication path -- afterwards showed the lineage would have described the wrong population: -- `gradeSlateService` fires its retention hook with BOTH sides, graded AND -- refused, BEFORE any filtering, and only the higher-confidence graded side -- becomes the served Read. -- -- MEASURED, mlb 2026-08-26: of 13,012 captured rows, 3,859 were refusals and -- only 4,569 matched a prop that reached the ledger. **8,443 rows (64.9%) -- describe a state no user was ever shown**, and nothing on the row said which -- was which. Building chronology over all of them would have produced -- "publication history" for model activity that was never published. -- -- `published` is the smallest missing link. It is set by the collector's -- publication signal at the one place that knows the answer — the moment the -- winner is selected — and defaults FALSE, because a capture is not a -- publication until something says it is. ALTER TABLE model_snapshots ADD COLUMN IF NOT EXISTS published boolean; -- ── WHY THE PUBLISHED STATE ADVANCED ────────────────────────────────────── -- The revision ordinal says WHEN. It cannot say WHY, and the difference is the -- whole product question: VYNDR's belief can hold still while the market moves. -- A replay of one real date found 4,507 digest changes of which only 301 moved -- the grade — 4,206 were price alone. One undifferentiated "revision" count -- conflates a changed opinion with a changed price. ALTER TABLE model_snapshots ADD COLUMN IF NOT EXISTS change_type text; -- ── CLAIM SEMANTICS ARE VERSIONED ───────────────────────────────────────── -- A digest is only interpretable against the claim definition that produced it. -- The first digest listed only `locked_odds` while this table stores -- `over_odds`/`under_odds`, so it was blind to price on exactly the rows it -- digested — caught by a replay crashing on a missing column, i.e. by luck. -- These stamps make the next such change visible instead of silent. A -- historical digest is never recomputed under a newer definition. ALTER TABLE model_snapshots ADD COLUMN IF NOT EXISTS claim_schema_version text; ALTER TABLE model_snapshots ADD COLUMN IF NOT EXISTS digest_algorithm_version text; -- Publication chronology is read published-only; the partial index matches. CREATE INDEX IF NOT EXISTS model_snapshots_published_lineage_idx ON model_snapshots (read_id, revision_ordinal) WHERE published IS TRUE; COMMENT ON COLUMN model_snapshots.published IS 'TRUE only for the side that actually became the served Read. Measured: 64.9% of captured rows describe a state no user was shown, and before this column nothing on the row distinguished them. NULL on rows written before migration 046 - unknown, not reconstructed.'; COMMENT ON COLUMN model_snapshots.change_type IS 'INITIAL_PUBLICATION | BELIEF_CHANGE | MARKET_REPRICE | MARKET_LINE_CHANGE | COMPARISON_CHANGE | PROVENANCE_CHANGE | PRESENTATION_CHANGE | MIXED_CHANGE | NO_MATERIAL_PUBLISHED_CHANGE. Ordinal says when; this says why.'; COMMENT ON COLUMN model_snapshots.claim_schema_version IS 'The claim definition in force when the digest was computed. Never recompute a historical digest under a newer definition and present it as original.';