feat(learning): abandoned attempt handling
scenario_attempts.status = 'abandoned' (a valid value since Phase 3B, never previously set by anything) now gets set on: environment destroy/reset, logout, session expiry (piggybacked on the existing hourly session sweeper - no new background job), and switching to a different scenario while one is still open. Never an academic failure marker - result stays NULL, history is preserved. Call-site wiring in server.js/admin-routes.js lands in the next commit alongside other platform hardening in those same files.
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
-- 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 $$;
|
||||
Reference in New Issue
Block a user