fix(implement-loop): guard against a task discarding sibling tasks' uncommitted work
During a multi-task run nothing commits between tasks, so one task's file-level `git checkout -- <file>` / `git restore <file>` silently destroys an earlier task's uncommitted DONE work in a shared file. The loss was structurally invisible: spec- and quality-reviewers are scoped to the current task's footprint, diff_fingerprint only detects same-task repair cycles, and the end-of-iteration tree gate counts files (the surviving changes keep it nonzero). Observed in the wild as a PARTIAL run whose DONE reports did not match the tree. Two layers, both plugin-side (harness-level prevention is ruled out — decision log on the issue): Prose guard — implementer.md Step 8 no longer sanctions file checkout for scope curation: over-reach is undone by editing back, a broken intermediate state is repaired forward or reported BLOCKED (new fourth BLOCKED bucket in the status protocol). A matching Iron Law line in implement/SKILL.md covers all in-loop agents, whole-file and --patch; docs/conventions.md now marks the checkout discard idiom as the orchestrator's, between iterations. Mechanical guard — after every task of a multi-task run a snapshot agent records `git stash create` (a dangling commit; HEAD, index, and tree untouched — semantics verified empirically in a scratch repo) plus the `git diff HEAD --name-only --no-renames` path set. A path that was HEAD-modified at one boundary and gone at the next trips a hard BLOCKED naming the lost paths, the boundary, and the recovery snapshot sha (`git show <sha>:<path>`); the discard verdict outranks per-task outcomes in blocked_detail since the reports and the tree have diverged. Coarse by design, in both directions, and documented as such: a checkout-then-re-edit or a --patch hunk restore escapes the comparison (the snapshot keeps it diagnosable); a legitimate back-to-HEAD edit trips it (the verdict says adjudicate against the snapshot). --no-renames keeps a staged rename from reading as a loss; untracked files are outside the threat model (checkout cannot discard them). Cost: one sonnet/medium call per task, multi-task runs only; single-task and mini runs are unchanged. Verified: node --check on the async-wrapped script and a stub-agent harness — discard trips the hard BLOCKED and stops the loop; an accumulating happy path, a plan-intended deletion, a single-task run, and a dead snapshot agent all pass without a false positive. closes #23
This commit is contained in:
@@ -159,8 +159,8 @@ pin exists to remove. Effort follows the model split:
|
||||
|
||||
Workflow scripts pin a third tier inline: schema-bound
|
||||
extraction/verification stages that author no code (preflight,
|
||||
plan-index, plan-extract, mini-verify, tree-check, finalize, build/suite verify)
|
||||
run `medium` via the `agent()` call's `effort:` option. As with
|
||||
plan-index, plan-extract, snapshot, mini-verify, tree-check, finalize,
|
||||
build/suite verify) run `medium` via the `agent()` call's `effort:` option. As with
|
||||
`model:`, every `agent()` call passes `effort:` explicitly — whether
|
||||
frontmatter effort propagates through an `agentType` dispatch is
|
||||
undocumented, so the scripts do not rely on it (see the policy header
|
||||
|
||||
+8
-2
@@ -130,8 +130,14 @@ too.
|
||||
- **main HEAD is sacrosanct — below the session anchor.** Nobody runs
|
||||
`git reset` / `git revert` on user-ratified main history (or any
|
||||
protected branch). main moves forward only via orchestrator commits; a
|
||||
wrong agent diff is discarded with `git checkout -- <paths>` /
|
||||
`git stash`, never by rewinding ratified history.
|
||||
wrong agent diff is discarded — by the orchestrator, between
|
||||
iterations — with `git checkout -- <paths>` / `git stash`, never by
|
||||
rewinding ratified history. That discard idiom is the orchestrator's
|
||||
alone: an agent running INSIDE a multi-task `implement` iteration
|
||||
never checkouts/restores a file — nothing commits between tasks, so
|
||||
it would silently destroy sibling tasks' uncommitted work; in-loop
|
||||
recovery is forward-editing or `BLOCKED` (`implement/SKILL.md` Iron
|
||||
Law, issue #23).
|
||||
**One narrow `/boss` exception — the rollback sandbox:** inside an
|
||||
autonomous run the orchestrator anchors on the `main` HEAD it started
|
||||
from (the last user-ratified state) and MAY `git reset --hard` its
|
||||
|
||||
Reference in New Issue
Block a user