const AUTONOMY_SCHEMA_VERSION = 2; function initAutonomySchema(db) { if (db.dialect === 'postgres') return; db.exec(` CREATE TABLE IF NOT EXISTS autonomy_schema ( version INTEGER PRIMARY KEY, applied_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE TABLE IF NOT EXISTS autonomy_jobs ( id INTEGER PRIMARY KEY AUTOINCREMENT, job_type TEXT NOT NULL, lane TEXT NOT NULL CHECK (lane IN ('live', 'historical', 'maintenance')), priority INTEGER NOT NULL DEFAULT 0, entity_type TEXT NOT NULL, entity_id TEXT NOT NULL, idempotency_key TEXT NOT NULL UNIQUE, status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'leased', 'complete', 'failed', 'dead_letter')), attempts INTEGER NOT NULL DEFAULT 0, available_at TEXT NOT NULL DEFAULT (datetime('now')), leased_by TEXT, lease_expires_at TEXT, last_error TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')), completed_at TEXT ); CREATE INDEX IF NOT EXISTS idx_autonomy_jobs_claim ON autonomy_jobs(status, lane, priority DESC, available_at); CREATE TABLE IF NOT EXISTS autonomy_cursors ( key TEXT PRIMARY KEY, value INTEGER NOT NULL DEFAULT 0, updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE TABLE IF NOT EXISTS autonomy_proposals ( id INTEGER PRIMARY KEY AUTOINCREMENT, event_id INTEGER, payload TEXT NOT NULL, information_cutoff TEXT NOT NULL, coordinator_model TEXT, prompt_version TEXT, status TEXT NOT NULL DEFAULT 'candidate' CHECK (status IN ('candidate', 'accepted', 'rejected', 'superseded')), rejection_reason TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')), reviewed_at TEXT ); CREATE TABLE IF NOT EXISTS autonomy_instruments ( symbol TEXT PRIMARY KEY, broker TEXT NOT NULL, asset_class TEXT NOT NULL DEFAULT 'us_equity', active INTEGER NOT NULL DEFAULT 0, tradable INTEGER NOT NULL DEFAULT 0, shortable INTEGER NOT NULL DEFAULT 0, fractionable INTEGER NOT NULL DEFAULT 0, updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE TABLE IF NOT EXISTS autonomy_legacy_records ( id INTEGER PRIMARY KEY AUTOINCREMENT, source_table TEXT NOT NULL, source_id INTEGER NOT NULL, payload TEXT NOT NULL, calibration_eligible INTEGER NOT NULL DEFAULT 0, imported_at TEXT NOT NULL DEFAULT (datetime('now')), UNIQUE(source_table, source_id) ); CREATE TABLE IF NOT EXISTS autonomy_predictions ( id INTEGER PRIMARY KEY AUTOINCREMENT, proposal_id INTEGER NOT NULL REFERENCES autonomy_proposals(id), event_id INTEGER, instrument TEXT NOT NULL, direction TEXT NOT NULL CHECK (direction IN ('positive', 'negative')), event_type TEXT NOT NULL, causal_channel TEXT, horizon_days INTEGER NOT NULL, information_cutoff TEXT NOT NULL, evidence_article_ids TEXT NOT NULL, invalidation_condition TEXT, learning_eligible INTEGER NOT NULL DEFAULT 0, strategy_version TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'resolved', 'unresolvable', 'invalidated')), created_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX IF NOT EXISTS idx_autonomy_predictions_resolution ON autonomy_predictions(status, information_cutoff, instrument); CREATE TABLE IF NOT EXISTS autonomy_outcomes ( prediction_id INTEGER PRIMARY KEY REFERENCES autonomy_predictions(id), price_0 REAL, price_horizon REAL, benchmark_0 REAL, benchmark_horizon REAL, excess_return REAL, direction_correct INTEGER, error_type TEXT, evaluated_at TEXT NOT NULL DEFAULT (datetime('now')), notes TEXT ); CREATE TABLE IF NOT EXISTS autonomy_calibration_snapshots ( id INTEGER PRIMARY KEY AUTOINCREMENT, cohort_key TEXT NOT NULL, sample_size INTEGER NOT NULL, effective_sample_size REAL NOT NULL, directional_probability REAL NOT NULL, expected_excess_return REAL, lower_return REAL, upper_return REAL, parent_cohort_key TEXT, version TEXT NOT NULL, created_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX IF NOT EXISTS idx_autonomy_calibration_lookup ON autonomy_calibration_snapshots(cohort_key, created_at DESC); CREATE TABLE IF NOT EXISTS autonomy_decisions ( id INTEGER PRIMARY KEY AUTOINCREMENT, prediction_id INTEGER NOT NULL REFERENCES autonomy_predictions(id), action TEXT NOT NULL CHECK (action IN ('BUY', 'SELL', 'HOLD', 'ABSTAIN')), calibrated_probability REAL, expected_excess_return REAL, rationale TEXT NOT NULL, strategy_version TEXT NOT NULL, created_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE TABLE IF NOT EXISTS autonomy_order_intents ( id INTEGER PRIMARY KEY AUTOINCREMENT, decision_id INTEGER NOT NULL REFERENCES autonomy_decisions(id), client_order_id TEXT NOT NULL UNIQUE, instrument TEXT NOT NULL, side TEXT NOT NULL CHECK (side IN ('buy', 'sell')), notional REAL NOT NULL CHECK (notional > 0), status TEXT NOT NULL DEFAULT 'shadow' CHECK (status IN ('shadow', 'pending', 'submitted', 'filled', 'partially_filled', 'rejected', 'cancelled')), broker_order_id TEXT, attempts INTEGER NOT NULL DEFAULT 0, last_error TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE TABLE IF NOT EXISTS autonomy_broker_events ( id INTEGER PRIMARY KEY AUTOINCREMENT, broker TEXT NOT NULL, event_type TEXT NOT NULL, broker_id TEXT, payload TEXT NOT NULL, occurred_at TEXT NOT NULL DEFAULT (datetime('now')), UNIQUE(broker, event_type, broker_id, occurred_at) ); CREATE TABLE IF NOT EXISTS autonomy_account_snapshots ( id INTEGER PRIMARY KEY AUTOINCREMENT, broker TEXT NOT NULL, account_id TEXT, equity REAL, cash REAL, buying_power REAL, payload TEXT NOT NULL, captured_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE TABLE IF NOT EXISTS autonomy_position_snapshots ( id INTEGER PRIMARY KEY AUTOINCREMENT, broker TEXT NOT NULL, instrument TEXT NOT NULL, quantity REAL, market_value REAL, unrealized_pl REAL, payload TEXT NOT NULL, captured_at TEXT NOT NULL DEFAULT (datetime('now')) ); -- Historical replay is a separate evidence path. It deliberately never -- writes to autonomy_decisions or order intents. CREATE TABLE IF NOT EXISTS autonomy_replay_runs ( id INTEGER PRIMARY KEY AUTOINCREMENT, status TEXT NOT NULL DEFAULT 'running' CHECK (status IN ('running', 'paused', 'complete', 'superseded', 'failed')), start_at TEXT, watermark_at TEXT, cursor_article_id INTEGER NOT NULL DEFAULT 0, cursor_effective_at TEXT, processed_articles INTEGER NOT NULL DEFAULT 0, strategy_version TEXT NOT NULL, prompt_version TEXT NOT NULL, coordinator_model TEXT, price_provider TEXT NOT NULL DEFAULT 'yahoo', created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')), completed_at TEXT, last_error TEXT ); CREATE INDEX IF NOT EXISTS idx_autonomy_replay_runs_active ON autonomy_replay_runs(status, cursor_article_id); CREATE TABLE IF NOT EXISTS autonomy_replay_evaluations ( prediction_id INTEGER PRIMARY KEY REFERENCES autonomy_predictions(id), replay_run_id INTEGER NOT NULL REFERENCES autonomy_replay_runs(id), snapshot_cutoff TEXT NOT NULL, sample_size INTEGER NOT NULL, action TEXT NOT NULL CHECK (action IN ('BUY', 'SELL', 'HOLD', 'ABSTAIN')), calibrated_probability REAL, expected_excess_return REAL, rationale TEXT NOT NULL, created_at TEXT NOT NULL DEFAULT (datetime('now')) ); INSERT OR IGNORE INTO autonomy_schema(version) VALUES (${AUTONOMY_SCHEMA_VERSION}); `); for (const statement of [ 'ALTER TABLE autonomy_order_intents ADD COLUMN attempts INTEGER NOT NULL DEFAULT 0', 'ALTER TABLE autonomy_order_intents ADD COLUMN last_error TEXT', 'ALTER TABLE autonomy_predictions ADD COLUMN learning_eligible INTEGER NOT NULL DEFAULT 0', "ALTER TABLE autonomy_predictions ADD COLUMN origin TEXT NOT NULL DEFAULT 'live'", 'ALTER TABLE autonomy_predictions ADD COLUMN replay_run_id INTEGER', 'ALTER TABLE autonomy_replay_runs ADD COLUMN cursor_effective_at TEXT', "ALTER TABLE autonomy_calibration_snapshots ADD COLUMN source TEXT NOT NULL DEFAULT 'live'", 'ALTER TABLE autonomy_calibration_snapshots ADD COLUMN replay_run_id INTEGER', ]) { try { db.exec(statement); } catch (_) {} } } module.exports = { AUTONOMY_SCHEMA_VERSION, initAutonomySchema };