-- Phase 1 schema covers the full Vetting domain so future phases -- only add data, never restructure. CREATE TABLE IF NOT EXISTS hosts ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE, mac TEXT NOT NULL UNIQUE, -- lowercase colon form wol_broadcast_ip TEXT NOT NULL, wol_port INTEGER NOT NULL DEFAULT 9, expected_spec_yaml TEXT NOT NULL, pdu_config_json TEXT, ipmi_config_json TEXT, notes TEXT NOT NULL DEFAULT '', created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS runs ( id INTEGER PRIMARY KEY AUTOINCREMENT, host_id INTEGER NOT NULL REFERENCES hosts(id) ON DELETE CASCADE, state TEXT NOT NULL, result TEXT, -- pass|fail|null failed_stage TEXT, next_boot_target TEXT, -- linux|memtest|linux-post-memtest (Phase 2+) agent_token_hash TEXT NOT NULL, started_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, completed_at TIMESTAMP, report_path TEXT, hold_ip TEXT, override_flags_json TEXT ); CREATE INDEX IF NOT EXISTS idx_runs_host ON runs(host_id); CREATE INDEX IF NOT EXISTS idx_runs_state ON runs(state); CREATE TABLE IF NOT EXISTS stages ( id INTEGER PRIMARY KEY AUTOINCREMENT, run_id INTEGER NOT NULL REFERENCES runs(id) ON DELETE CASCADE, name TEXT NOT NULL, ordinal INTEGER NOT NULL, state TEXT NOT NULL, -- pending|running|passed|failed|skipped started_at TIMESTAMP, completed_at TIMESTAMP, summary_json TEXT ); CREATE INDEX IF NOT EXISTS idx_stages_run_ordinal ON stages(run_id, ordinal); CREATE TABLE IF NOT EXISTS measurements ( id INTEGER PRIMARY KEY AUTOINCREMENT, run_id INTEGER NOT NULL REFERENCES runs(id) ON DELETE CASCADE, stage_id INTEGER REFERENCES stages(id) ON DELETE SET NULL, ts TIMESTAMP NOT NULL, kind TEXT NOT NULL, -- temp|power|iperf|fio|smart_attr key TEXT NOT NULL, value REAL, unit TEXT ); CREATE INDEX IF NOT EXISTS idx_measurements_run_kind_ts ON measurements(run_id, kind, ts); CREATE TABLE IF NOT EXISTS artifacts ( id INTEGER PRIMARY KEY AUTOINCREMENT, run_id INTEGER NOT NULL REFERENCES runs(id) ON DELETE CASCADE, stage_id INTEGER REFERENCES stages(id) ON DELETE SET NULL, kind TEXT NOT NULL, path TEXT NOT NULL, sha256 TEXT NOT NULL, size_bytes INTEGER NOT NULL ); CREATE TABLE IF NOT EXISTS spec_diffs ( id INTEGER PRIMARY KEY AUTOINCREMENT, run_id INTEGER NOT NULL REFERENCES runs(id) ON DELETE CASCADE, field TEXT NOT NULL, expected TEXT, actual TEXT, severity TEXT NOT NULL, -- critical|warning|info ignored INTEGER NOT NULL DEFAULT 0 ); CREATE TABLE IF NOT EXISTS events ( id INTEGER PRIMARY KEY AUTOINCREMENT, run_id INTEGER REFERENCES runs(id) ON DELETE CASCADE, host_id INTEGER REFERENCES hosts(id) ON DELETE CASCADE, ts TIMESTAMP NOT NULL, level TEXT NOT NULL, kind TEXT NOT NULL, message TEXT NOT NULL, data_json TEXT ); CREATE TABLE IF NOT EXISTS settings ( key TEXT PRIMARY KEY, value TEXT NOT NULL );