diff --git a/BURNIN.md b/BURNIN.md index f154018..6ae193e 100644 --- a/BURNIN.md +++ b/BURNIN.md @@ -2848,3 +2848,102 @@ unchanged. "ready_for_verification"}`. It is the whole lever for this rung. - Ingest turns every issue label into a required capability, so a rig issue must carry no labels or it will not schedule. + +## Run 20, 2026-08-29: the mismatch and replan ladder, and three defects + +Task `06G4WJ9T4F35NZC4Z8QQXM9Z6G` (issue 29) on `de18f37`, plus a first attempt +on `06G4WDJ06G7FK00QF73Z9DDBT4` (issue 27) on `c587f2c` that found F65. + +The lever throughout is the agent's own file, written by the operator with the +worker stopped: `.orchestra/plan-mismatch.json`, carrying `phase_id`, +`observed`, `contradicts`, `evidence` and `requested_action`. The worker fills +`plan_ref` and `at_sha` from what it can verify, never from the file. + +### F65: a task that stops for a human never stopped + +Found on the first attempt, live: + +```text +16:04:25 PlanMismatchRecorded phase-2 human_decision +16:04:25 worker: plan mismatch refused: task version conflict +16:04:48 PlanPhaseVerified phase-1 work continues as if nothing happened +``` + +`Store.Append` fences every lifecycle event on a leased task against the +current owner and epoch. `blockForPlanMismatch` carried neither `harness_id` +nor `lease_epoch`, so the block was refused on every task that was actually +running, which is every task that can produce a mismatch. The observation was +durable and the transition silently did not happen: the repo's own documented +failure shape. `raiseTrajectoryGate` had the identical omission, so the +trajectory gate could never have stopped a live task either. + +`RecordPlanMismatch` also returns that error after the record is already +appended, so the worker told the agent its report was refused for a report +Orchestra had accepted. + +Fixed in `de18f37` with one `fenceToLease` helper, called by all three stops. +The human-decision path already did it correctly and explained why in a +comment; that comment is now the helper. + +**Why no test caught it.** `planWith` never leased its task, so every +plan-progress and plan-mismatch test ran in a state no agent can be in. The +`lease` helper in the same package carries a comment warning about exactly +that. It leases now, and the mismatch block test fails without the fence with +the same `task version conflict`. + +### Proven: human_decision stops, and the reply resumes the same plan + +```text +16:24:21 PlanMismatchRecorded phase-2 human_decision +16:24:21 TaskBlocked plan_mismatch, lifecycle awaiting_human +18:24:46 HumanDecisionRecorded operator_instruction (Gitea's timestamp) +16:25:16 TaskCorrected resumed +16:25:16 TaskLeased plan_ref unchanged at 0bc8ef84 +16:25:21 TaskPickupValidated +``` + +55 seconds from block to resumed, on the same plan, with no reseal. The +blocker packet states the observation, the plan's claim and the evidence. + +This is also the live proof of F64: before `6cb2f93` nothing read a blocked +task's comments, because the reconciler ran only before a lease and at a turn +boundary, and a blocked task is never leased. + +### Proven: replan keeps the old plan until a replacement seals + +```text +16:26:07 PlanMismatchRecorded phase-3 replan +16:26:07 WorkPhaseChanged plan reopen=PlanMismatchRecorded + during planning: plan_ref 0bc8ef84 accepted, phase-1 and phase-2 counted + after the seal: + plan_ref 922f293b + plan_history [0bc8ef84] + progress empty + successor in implement: four phases, all "not started" +``` + +The successor's launch context carries the replacement whole, with files, +changes and verification per phase. The old progress stops counting because +`PlanPhases()` drops records whose plan ref is not the accepted one. An +abandoned replan would have cost nothing. + +### F66: the planner replans blind + +The planning session convened by a replan is given the ordinary plan brief and +an empty template. Its launch context says nothing about the plan that already +exists, the contradiction that reopened the phase, the phase id, the evidence, +or the fact that its output supersedes an accepted plan. The only human input +it carried was an unrelated earlier correction. + +Everything it needs is already durable. `PlanMismatchRecorded` holds the phase, +the observation, the contradiction and the evidence, and `reopenPhase` writes +`reopen_phase_id` onto the `WorkPhaseChanged` payload. None of it reaches the +agent, so nothing stops the replacement from sealing with the same +contradiction in it. + +### Smaller observation + +The implement session kept working after the reopen. It verified phase-2 +against the old plan 40 seconds after the task had moved to `plan`. Harmless, +because supersession discards it, but the reopen does not end the session that +reported the contradiction. That session rotates at its own next boundary.