fix(missions): a coding phase that delivers nothing is a failure
The last open item in the silent-success class: a coding phase that changed no files reported `completed` — the same status a phase gets for delivering tested, reviewed, pushed work. Mission `019fcf62` completed that way with its agents silently unpinned from the repo, and nothing in the platform disagreed; it was found by a script diffing the forge. The verdict is applied at capture rather than at completion, because capture selects on `status = 'completed'` — the platform does not know whether a phase produced anything until after it has already finished. Three conditions must hold before failing a phase, because a false positive here fails honest work: the phase is a coding phase (research phases legitimately write nothing to the tree), the diff was actually computed (an uncomputable diff also reports zero files — blaming the agent for a platform fault is the same defect wearing different clothes), and `allow_empty` is not set. Only an explicit `true` opts out, so a typo leaves the check armed. Registered in phase_config with its reader named, per the seam-2 rule. Also closes an ordering hazard this exposed: capture is batched and runs after a phase completes, so a backlogged mission could close as 'completed' and only then have capture discover an empty phase — leaving a 'completed' mission holding a 'failed' phase, unfixable because the mission-close CASE only touches 'running' rows. A repo-bearing mission now waits for its work to be captured before closing. Adds a `noop` scenario to the harness: a phase told to change nothing, which PASSES only when the phase comes back `failed`. Same discipline as the uid self-test — a check that has never been seen to fire has not been shown to work. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
f8ca0ced9a
commit
da3731d753
@@ -109,6 +109,10 @@ pub struct Capture {
|
||||
/// The phase changed nothing. Still recorded — "this coding phase wrote no
|
||||
/// code" is currently invisible to an operator, and it is worth saying.
|
||||
pub empty: bool,
|
||||
/// Why the diff could not be computed, if it could not be. `empty` is only
|
||||
/// meaningful when this is `None`: otherwise the tree was never read, and
|
||||
/// callers deciding anything on the strength of "no changes" must not.
|
||||
pub diff_error: Option<String>,
|
||||
pub truncated: bool,
|
||||
pub patch_path: PathBuf,
|
||||
/// Set once the work has been committed to a mission branch.
|
||||
@@ -463,6 +467,7 @@ pub async fn capture_phase_diff_at(
|
||||
insertions,
|
||||
deletions,
|
||||
empty,
|
||||
diff_error,
|
||||
truncated,
|
||||
patch_path,
|
||||
}))
|
||||
|
||||
Reference in New Issue
Block a user