Record run 20: the mismatch and replan ladder holds, and F66

Both halves proven live. A human_decision mismatch stops the task, the
blocker packet states the contradiction, and a Gitea reply resumes the same
plan in 55 seconds with no reseal. A replan reopens into plan while the old
plan stays accepted with its progress, and sealing the replacement moves
plan_ref, retains the old one in history, stops the old progress counting,
and hands the successor the replacement whole.

F66 recorded: the planning session a replan convenes is told nothing about
the contradiction it exists to fix, though all of it is durable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVbaKucEYBjMqVeUgJUsc1
This commit is contained in:
2026-08-29 20:39:04 +04:00
parent de18f372d3
commit cb80dd1952
+99
View File
@@ -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.