-- Goal conditions for mission phases. -- -- Until now a phase completed when its topology_runs reached a terminal -- state -- purely structural, with no notion of whether the work was any -- good. `close_finished_phases` marked a phase `completed` whether the -- agents produced the artifact or wrote nothing at all. -- -- `done_when` is a natural-language completion condition, judged after each -- pass by a model (see crates/cm-api/src/evaluator.rs). It follows the -- constraint the evaluator operates under: the judge cannot run commands, so -- the condition must be demonstrable from what the agents surfaced in their -- turn output. -- -- NULL `done_when` preserves today's behaviour exactly: terminal runs -> -- completed, no evaluation, no extra model spend. Existing missions are -- unaffected. ALTER TABLE mission_phases -- The completion condition. NULL = no evaluation (legacy behaviour). ADD COLUMN done_when TEXT, -- Upper bound on passes. 1 = run once, matching current behaviour. -- Capped server-side as well; this is a backstop against a runaway loop. ADD COLUMN max_iterations INT NOT NULL DEFAULT 1, -- Which pass the phase is on, 0-based. ADD COLUMN iteration INT NOT NULL DEFAULT 0; -- One verdict per (phase, iteration). Modelled on mission_phase_summaries -- (0060): the model emits structured JSON, we persist it with the model name -- so a verdict can be attributed, and keep the reason because it is both the -- explanation shown to the operator AND the guidance fed into the next pass. CREATE TABLE mission_phase_evaluations ( id UUID PRIMARY KEY, mission_id UUID NOT NULL REFERENCES missions(id) ON DELETE CASCADE, phase_id UUID NOT NULL REFERENCES mission_phases(id) ON DELETE CASCADE, iteration INT NOT NULL, -- Whether the condition held. Fail-closed: an unparseable or missing -- verdict is recorded as false, never as "done". met BOOLEAN NOT NULL, reason TEXT NOT NULL, -- The model that judged, e.g. "runtime:coordinator" or "claude-opus-4-8". model TEXT NOT NULL, -- Set when the evaluator itself failed (transport, parse). `met` is false -- in that case; this distinguishes "judged not done" from "could not judge". error TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE (phase_id, iteration) ); CREATE INDEX mission_phase_evaluations_phase_idx ON mission_phase_evaluations (phase_id, iteration DESC); CREATE INDEX mission_phase_evaluations_mission_idx ON mission_phase_evaluations (mission_id, created_at DESC); -- Which pass produced a run. Without this, "are this phase's runs all -- finished?" matches pass 1's completed rows forever and a second pass would -- be declared done the instant it was enqueued. -- -- (An `iteration` column existed on this table once and was dropped in 0053 -- along with the legacy loops backend. This one is for mission phases.) ALTER TABLE topology_runs ADD COLUMN iteration INT NOT NULL DEFAULT 0; CREATE INDEX topology_runs_phase_iteration_idx ON topology_runs (mission_phase_id, iteration) WHERE mission_phase_id IS NOT NULL;