-- Abandoned/interrupted attempt handling โ€” Phase 4.1, docs/PHASE4_1_PREPRODUCTION_HARDENING.md -- ยง13. 'abandoned' was already a valid scenario_attempts.status value (migration 012's CHECK -- constraint) but nothing ever set it โ€” an attempt left open when a Lab was reaped, an -- environment destroyed, a session ended, or a student moved on to a different scenario just -- stayed 'in_progress' forever, an honest but incomplete signal. This is NOT an academic -- failure marker โ€” an abandoned attempt has no `result`, doesn't count against the student, -- and the row is never deleted (see src/attempts.js abandonAttempt for the enforcement of -- that). It exists so instructor views and needs-attention logic can tell "still working" from -- "stopped, for a reason we know." ALTER TABLE scenario_attempts ADD COLUMN IF NOT EXISTS abandoned_at TIMESTAMPTZ; ALTER TABLE scenario_attempts ADD COLUMN IF NOT EXISTS abandoned_reason TEXT; DO $$ BEGIN ALTER TABLE scenario_attempts ADD CONSTRAINT scenario_attempts_abandoned_reason_check CHECK (abandoned_reason IS NULL OR abandoned_reason IN ('environment_destroyed', 'session_ended', 'scenario_switched')); EXCEPTION WHEN duplicate_object THEN NULL; END $$;