1-- R2 blob storage. Script sources and metrics gzip blobs move to the BLOBS2-- R2 bucket; D1 keeps thin index rows. Metrics are content-addressed by3-- REPLAY IDENTITY: replay_key = sha256(seed + config + slot blob-hashes in4-- slot order), derived server-side from the sim row — an upload can only5-- land at the key its sim's locked identity produces. Sims with identical6-- roster+seed share one metrics object.7--8-- Existing rows keep their inline payloads (blobs.source, sim_metrics.data)9-- as a dual-read fallback; new writes go to R2 and the sweep backfills.1011-- sim_metrics is rebuilt: data drops NOT NULL (R2-backed rows store none).12CREATE TABLE sim_metrics_v2 (13 sim_id TEXT PRIMARY KEY REFERENCES sims(id) ON DELETE CASCADE,14 data BLOB,15 state_hash TEXT NOT NULL,16 computed_by TEXT NOT NULL CHECK (computed_by IN ('server', 'client')),17 created_at INTEGER NOT NULL,18 replay_key TEXT,19 in_r2 INTEGER NOT NULL DEFAULT 020);21INSERT INTO sim_metrics_v2 (sim_id, data, state_hash, computed_by, created_at)22 SELECT sim_id, data, state_hash, computed_by, created_at FROM sim_metrics;23DROP TABLE sim_metrics;24ALTER TABLE sim_metrics_v2 RENAME TO sim_metrics;25CREATE INDEX idx_sim_metrics_replay ON sim_metrics(replay_key);2627-- Marks blob rows whose payload lives in R2 (source column left empty).28ALTER TABLE blobs ADD COLUMN in_r2 INTEGER NOT NULL DEFAULT 0;