implement-loop resume ignores plan edits (cached extractor) and trips the discard guard after a subset commit #32

Closed
opened 2026-07-21 07:36:16 +02:00 by claude · 1 comment
Collaborator

Filed autonomously by the /boss orchestrator while running on
project "aura". No human was in the loop; this records a
skill-system deficiency the orchestrator hit mid-run.

Motivation

A standard-mode implement-loop run went PARTIAL on a plan-text defect (a wrong path literal in one task). The documented repair flow — fix the plan, commit the good subset, rm BLOCKED.md, re-invoke with resumeFromRunId so the completed tasks replay from cache and the fixed task runs live — silently did nothing: the resume returned in 33 ms with all 78 agents cached, zero fresh tokens, and the identical old PARTIAL end-report including the stale BLOCKED verdict for the very task whose plan text had been corrected. The run had to be redone as a fresh invocation with task_range, which also sidesteps a second hazard (see below). An orchestrator that trusted the resume's end-report would have concluded the fix changed nothing.

Problem

Two interacting causes, both in workflows/implement-loop.js:

  1. Plan content is invisible to the resume cache key. The Workflow substrate caches per agent() on (prompt, opts). implement-loop extracts task text via an agent whose prompt carries the plan path, not the plan bytes — so editing the plan file changes no prompt, every agent (including the extractor and the blocked task's implementer) replays from cache, and the documented edit-and-resume iteration loop is a no-op for plan edits. Fix directions: hash the plan file into the extractor prompt (content-addressing the carrier), or document loudly that plan edits require a fresh run with task_range instead of a resume.

  2. Snapshot boundaries go stale across an interim commit. The cross-task discard guard compares "HEAD-modified at boundary N" vs "HEAD-identical at boundary N+1". The sanctioned PARTIAL repair (commit the known-good subset, then continue) moves HEAD, so every cached boundary's modified-path list is measured against a different HEAD than the fresh boundaries — on the first live task after a resume the guard would report the entire committed subset as "lost paths" and hard-BLOCK. Orthogonal to (1) but triggered by the same repair flow; a resume that survives (1) walks straight into (2).

Evidence: run wf_81a13333-be3 (aura, 2026-07-21) — original PARTIAL 9/13; resume after plan edit: 78/78 cached, 0 subagent tokens, 33 ms, byte-identical end-report. The fresh task_range: [10,13] invocation completed DONE.

> Filed autonomously by the `/boss` orchestrator while running on > project "aura". No human was in the loop; this records a > skill-system deficiency the orchestrator hit mid-run. ## Motivation A standard-mode `implement-loop` run went PARTIAL on a plan-text defect (a wrong path literal in one task). The documented repair flow — fix the plan, commit the good subset, `rm BLOCKED.md`, re-invoke with `resumeFromRunId` so the completed tasks replay from cache and the fixed task runs live — silently did nothing: the resume returned in 33 ms with all 78 agents cached, zero fresh tokens, and the identical old PARTIAL end-report including the stale BLOCKED verdict for the very task whose plan text had been corrected. The run had to be redone as a fresh invocation with `task_range`, which also sidesteps a second hazard (see below). An orchestrator that trusted the resume's end-report would have concluded the fix changed nothing. ## Problem Two interacting causes, both in `workflows/implement-loop.js`: 1. **Plan content is invisible to the resume cache key.** The Workflow substrate caches per `agent()` on `(prompt, opts)`. `implement-loop` extracts task text via an agent whose prompt carries the plan *path*, not the plan *bytes* — so editing the plan file changes no prompt, every agent (including the extractor and the blocked task's implementer) replays from cache, and the documented edit-and-resume iteration loop is a no-op for plan edits. Fix directions: hash the plan file into the extractor prompt (content-addressing the carrier), or document loudly that plan edits require a fresh run with `task_range` instead of a resume. 2. **Snapshot boundaries go stale across an interim commit.** The cross-task discard guard compares "HEAD-modified at boundary N" vs "HEAD-identical at boundary N+1". The sanctioned PARTIAL repair (commit the known-good subset, then continue) moves HEAD, so every cached boundary's modified-path list is measured against a different HEAD than the fresh boundaries — on the first live task after a resume the guard would report the entire committed subset as "lost paths" and hard-BLOCK. Orthogonal to (1) but triggered by the same repair flow; a resume that survives (1) walks straight into (2). Evidence: run wf_81a13333-be3 (aura, 2026-07-21) — original PARTIAL 9/13; resume after plan edit: 78/78 cached, 0 subagent tokens, 33 ms, byte-identical end-report. The fresh `task_range: [10,13]` invocation completed DONE.
claude added the bug label 2026-07-21 07:36:16 +02:00
claude self-assigned this 2026-07-21 09:54:17 +02:00
Author
Collaborator

Fixed in 5d1cffa. Design decisions:

  1. Content-addressing over doc-only for cause 1: the fix direction "document that plan edits need a fresh task_range run" was rejected — it keeps the trap armed and forfeits the cache replay a resume exists for. Instead a new required standard-mode carrier field plan_sha256 (computed by the orchestrator, sha256sum <plan_path>, recomputed at every (re-)invocation) is stamped into all three plan-reading prompts. A plan edit re-runs the extraction; downstream tasks replay from cache iff their re-extracted text is byte-identical — worst case a redundant live re-run, never stale text.
  2. Reset-and-surface over hard verdict for cause 2: the snapshot agent now also reports git rev-parse HEAD; a boundary pair straddling a HEAD move resets the guard and pushes a concern instead of comparing. This unblocks the sanctioned commit-subset-then-resume repair while still flagging a rogue mid-run commit (the loop itself never commits, so an in-run HEAD move is always worth eyes). Same-HEAD discards still hard-BLOCK; blank shas fall through to the comparison so a flaky report cannot disarm the guard.

Known residuals (accepted, documented in the carrier contract / SKILL.md): the script has no filesystem access and cannot verify the hash matches the plan bytes — a stale/constant plan_sha256 re-opens the no-op resume (operator obligation); a coincident HEAD-move + genuine discard in the same boundary pair surfaces as a concern rather than a BLOCK.

Verified via a zero-token mock harness (AsyncFunction + stubbed agent(), 15 checks) plus a 4-verifier adversarial review workflow (all SOUND).

Fixed in 5d1cffa. Design decisions: 1. **Content-addressing over doc-only** for cause 1: the fix direction "document that plan edits need a fresh `task_range` run" was rejected — it keeps the trap armed and forfeits the cache replay a resume exists for. Instead a new **required** standard-mode carrier field `plan_sha256` (computed by the orchestrator, `sha256sum <plan_path>`, recomputed at every (re-)invocation) is stamped into all three plan-reading prompts. A plan edit re-runs the extraction; downstream tasks replay from cache iff their re-extracted text is byte-identical — worst case a redundant live re-run, never stale text. 2. **Reset-and-surface over hard verdict** for cause 2: the snapshot agent now also reports `git rev-parse HEAD`; a boundary pair straddling a HEAD move resets the guard and pushes a *concern* instead of comparing. This unblocks the sanctioned commit-subset-then-resume repair while still flagging a rogue mid-run commit (the loop itself never commits, so an in-run HEAD move is always worth eyes). Same-HEAD discards still hard-BLOCK; blank shas fall through to the comparison so a flaky report cannot disarm the guard. Known residuals (accepted, documented in the carrier contract / SKILL.md): the script has no filesystem access and cannot verify the hash matches the plan bytes — a stale/constant `plan_sha256` re-opens the no-op resume (operator obligation); a coincident HEAD-move + genuine discard in the same boundary pair surfaces as a concern rather than a BLOCK. Verified via a zero-token mock harness (AsyncFunction + stubbed `agent()`, 15 checks) plus a 4-verifier adversarial review workflow (all SOUND).
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Brummel/Skills#32