Problem
Two fail-closed-too-hard shapes in crates/stella-pipeline/src/pipeline/witness_stage.rs:
- Budget aborts are
rejected (author turn and repair turn arms): witness_on_demand propagates Err, and run_candidate turns it into CandidateResult::aborted(.., DeliberateStop) — the worker's finished diff is thrown away because the scaffolding ran out of money. Invariant 6 (budget aborts at safe boundaries) explains stopping, but stopping and discarding are different: the completed change could finish on the unauthored ladder as Unverified and still respect the budget by suppressing further paid calls. Needs a design decision — the interaction with the verify loop's own paid calls is the hard part (degrading the witness but then buying verdict calls would overspend).
- Reachable
expect()s in the one stage whose contract is "degrade, never discard": baseline.workspace.expect("witness authoring requires a pristine baseline workspace"), the grafting expect, and the identity expects. Each is caller-upheld today; a future caller wiring CandidateSurface { workspace: None } panics the run instead of losing a witness. Typed degradable aborts fit the file's own WitnessAbort vocabulary.
(The sibling misclassification — author created no file → was rejected — was fixed separately with WitnessArtifactError::NothingCreated; this issue is the remaining two.)
Verify
1: scripted run whose budget exhausts exactly during witness authoring currently returns an aborted candidate; after the fix it completes Unverified with the witness unproven and no further paid calls. 2: a CandidateSurface with workspace: None reaching witness_stage degrades instead of panicking.
Problem
Two fail-closed-too-hard shapes in
crates/stella-pipeline/src/pipeline/witness_stage.rs:rejected(author turn and repair turn arms):witness_on_demandpropagatesErr, andrun_candidateturns it intoCandidateResult::aborted(.., DeliberateStop)— the worker's finished diff is thrown away because the scaffolding ran out of money. Invariant 6 (budget aborts at safe boundaries) explains stopping, but stopping and discarding are different: the completed change could finish on the unauthored ladder asUnverifiedand still respect the budget by suppressing further paid calls. Needs a design decision — the interaction with the verify loop's own paid calls is the hard part (degrading the witness but then buying verdict calls would overspend).expect()s in the one stage whose contract is "degrade, never discard":baseline.workspace.expect("witness authoring requires a pristine baseline workspace"), the grafting expect, and the identity expects. Each is caller-upheld today; a future caller wiringCandidateSurface { workspace: None }panics the run instead of losing a witness. Typed degradable aborts fit the file's ownWitnessAbortvocabulary.(The sibling misclassification — author created no file → was
rejected— was fixed separately withWitnessArtifactError::NothingCreated; this issue is the remaining two.)Verify
1: scripted run whose budget exhausts exactly during witness authoring currently returns an aborted candidate; after the fix it completes
Unverifiedwith the witnessunprovenand no further paid calls. 2: aCandidateSurfacewithworkspace: Nonereachingwitness_stagedegrades instead of panicking.