feat(missions): surface goal conditions and per-pass verdicts in the UI
Makes the completion evaluator usable and observable.
- GET /api/missions/{id}/phases/{phase_id}/evaluations returns every verdict
for a phase, newest pass first, scoped like the summary endpoint.
- MissionPhase gains done_when / max_iterations / iteration, so the phase card
can show what the phase is working toward and which pass it is on.
- PhaseStatus gains 'evaluating' (amber) -- the state between "runs finished"
and "phase done" that only conditioned phases enter.
- New PhaseGoalStrip renders on the phase card, and renders NOTHING for phases
without a condition so unconditioned missions look exactly as before. It
polls only while the phase is running or being judged.
- Mission wizard step 2 gains the condition + a max-passes field.
Two deliberate emphases in the UI:
The evaluator's `reason` is the most prominent element, because it is both the
explanation of why a phase iterated and the literal text handed back to the
agents as guidance -- it is what tells an operator whether the condition is
written well.
The hint copy states the constraint that actually governs whether a condition
works: the judge cannot run commands, it only reads what the agents wrote, so
the condition has to be provable from their output. "cargo test reported 0
failures" works; "the code is well factored" does not. Getting this wrong is
the difference between a phase that converges and one that burns every pass.
An evaluator error is rendered distinctly from a negative verdict, so a judge
outage doesn't read as a judgement on the work.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
f848248fac
commit
fe57ce4ed1
@@ -47,6 +47,7 @@ import { MissionLivePane } from "./MissionLivePane";
|
||||
import { MissionOutputReader } from "./MissionOutputReader";
|
||||
import { MissionTeamTab } from "./MissionTeamTab";
|
||||
import { MissionWizard } from "./MissionWizard";
|
||||
import { PhaseGoalStrip } from "./PhaseGoalStrip";
|
||||
import { PhaseRunsList } from "./PhaseRunsList";
|
||||
import { PhaseSummaryCard } from "./PhaseSummaryCard";
|
||||
import { RefineDiffModal } from "./RefineDiffModal";
|
||||
@@ -64,6 +65,8 @@ const STATUS_COLOR: Record<MissionStatus, string> = {
|
||||
const PHASE_STATUS_COLOR: Record<PhaseStatus, string> = {
|
||||
pending: "#6a6a72",
|
||||
running: "#5ec8d8",
|
||||
// Amber: work is done but the completion condition is being judged.
|
||||
evaluating: "#e8b465",
|
||||
completed: "#5fd08a",
|
||||
failed: "#ff8a7a",
|
||||
skipped: "#8a8a92",
|
||||
@@ -834,6 +837,8 @@ export function MissionCanvas({
|
||||
: ""}
|
||||
</span>
|
||||
)}
|
||||
{/* Renders only when the phase carries a done_when. */}
|
||||
<PhaseGoalStrip missionId={mission.id} phase={p} />
|
||||
<PhaseRunsList runs={runsByPhase.get(p.id) ?? []} />
|
||||
{(p.status === "completed" || p.status === "failed") && (
|
||||
<PhaseSummaryCard missionId={mission.id} phaseId={p.id} />
|
||||
|
||||
Reference in New Issue
Block a user