fix: have executor self-report worktree metadata (#1349)
This commit is contained in:
6
.changeset/1297-worktree-metadata.md
Normal file
6
.changeset/1297-worktree-metadata.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
type: Fixed
|
||||
pr: 1349
|
||||
---
|
||||
|
||||
**Parallel worktree execution now has executor-authored cleanup metadata** — executor agents capture their worktree path, branch, and expected base before task commits and return a parseable metadata block for execute-phase to prefer over runtime harness metadata. (#1297)
|
||||
@@ -103,6 +103,24 @@ PLAN_START_EPOCH=$(date +%s)
|
||||
```
|
||||
</step>
|
||||
|
||||
<worktree_metadata_capture>
|
||||
If running inside a git worktree, capture authoritative worktree identity before
|
||||
any task commit changes HEAD. The execute-phase orchestrator consumes this from
|
||||
your final `<worktree_metadata>` return block to build the wave cleanup manifest
|
||||
without relying on runtime harness metadata (#1297).
|
||||
|
||||
```bash
|
||||
GSD_WORKTREE_PATH=""
|
||||
GSD_WORKTREE_BRANCH=""
|
||||
GSD_WORKTREE_EXPECTED_BASE=""
|
||||
if [ -f .git ]; then
|
||||
GSD_WORKTREE_PATH=$(git rev-parse --show-toplevel)
|
||||
GSD_WORKTREE_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
GSD_WORKTREE_EXPECTED_BASE=$(git rev-parse HEAD)
|
||||
fi
|
||||
```
|
||||
</worktree_metadata_capture>
|
||||
|
||||
<step name="determine_execution_pattern">
|
||||
```bash
|
||||
grep -n "type=\"checkpoint" [plan-path]
|
||||
@@ -761,6 +779,10 @@ into the user's project history.
|
||||
**Tasks:** {completed}/{total}
|
||||
**SUMMARY:** {path to SUMMARY.md}
|
||||
|
||||
<worktree_metadata>
|
||||
{"agent_id":"{phase}-{plan}","worktree_path":"${GSD_WORKTREE_PATH:-}","branch":"${GSD_WORKTREE_BRANCH:-}","expected_base":"${GSD_WORKTREE_EXPECTED_BASE:-}"}
|
||||
</worktree_metadata>
|
||||
|
||||
**Commits:**
|
||||
- {hash}: {message}
|
||||
- {hash}: {message}
|
||||
|
||||
@@ -635,6 +635,7 @@ increases monotonically across waves. `{status}` is `complete` (success),
|
||||
this commit — the orchestrator force-removes the worktree after you return, and
|
||||
any uncommitted SUMMARY.md will be permanently lost (#2070).
|
||||
REQUIRED ORDER: Write SUMMARY.md → commit → only then any narration. No text between Write and commit (truncation risk; #2070 rescue is not primary defense).
|
||||
|
||||
</parallel_execution>
|
||||
|
||||
<execution_context>
|
||||
@@ -682,7 +683,7 @@ increases monotonically across waves. `{status}` is `complete` (success),
|
||||
)
|
||||
```
|
||||
|
||||
Immediately after each worktree `Agent()` spawn returns metadata, atomically append `{agent_id, worktree_path, branch, expected_base}` to `WAVE_WORKTREE_MANIFEST`. If any field is missing, stop and ask for recovery instead of scanning all agent worktrees.
|
||||
After each `Agent()` returns, parse executor-returned worktree metadata (`<worktree_metadata>`) before harness metadata, then atomically append `{agent_id, worktree_path, branch, expected_base}` to `WAVE_WORKTREE_MANIFEST`. Missing: stop and ask for recovery instead of scanning worktrees.
|
||||
|
||||
> **Worktree recovery policy (#48 + #1292):** See `execute-phase/steps/worktree-recovery-policy.md` — FAIL-CLOSED rule for base/HEAD-namespace mismatches AND isolated-run fail-safe recovery.
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"gsd-domain-researcher.md": 6938,
|
||||
"gsd-eval-auditor.md": 7761,
|
||||
"gsd-eval-planner.md": 7008,
|
||||
"gsd-executor.md": 42519,
|
||||
"gsd-executor.md": 43343,
|
||||
"gsd-framework-selector.md": 6778,
|
||||
"gsd-integration-checker.md": 15148,
|
||||
"gsd-intel-updater.md": 18122,
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"docs-update.md": 54770,
|
||||
"edit-phase.md": 12883,
|
||||
"eval-review.md": 9923,
|
||||
"execute-phase.md": 93122,
|
||||
"execute-phase.md": 93157,
|
||||
"execute-plan.md": 31365,
|
||||
"explore.md": 10497,
|
||||
"extract-learnings.md": 12849,
|
||||
|
||||
@@ -684,6 +684,29 @@ describe('bug #3384: worktree cleanup workflow contracts', () => {
|
||||
assert.doesNotMatch(content, /done < <\(git worktree list --porcelain \| grep "\^worktree " \| grep "\\\.claude\/worktrees\/agent-"/);
|
||||
});
|
||||
|
||||
test('#1297 gsd-executor self-reports authoritative worktree metadata', () => {
|
||||
const content = fs.readFileSync(EXECUTOR_AGENT_PATH, 'utf8');
|
||||
assert.match(content, /<worktree_metadata_capture>/);
|
||||
assert.match(content, /git rev-parse --show-toplevel/);
|
||||
assert.match(content, /git rev-parse --abbrev-ref HEAD/);
|
||||
assert.match(content, /GSD_WORKTREE_EXPECTED_BASE=\$\(git rev-parse HEAD\)/);
|
||||
assert.match(content, /<worktree_metadata>/);
|
||||
assert.match(content, /"worktree_path":/);
|
||||
assert.match(content, /"branch":/);
|
||||
assert.match(content, /"expected_base":/);
|
||||
});
|
||||
|
||||
test('#1297 execute-phase consumes executor-returned worktree metadata before harness metadata', () => {
|
||||
const content = fs.readFileSync(EXECUTE_PHASE_PATH, 'utf8');
|
||||
assert.match(content, /<worktree_metadata>/);
|
||||
assert.match(content, /executor-returned worktree metadata/i);
|
||||
assert.match(content, /harness metadata/i);
|
||||
assert.ok(
|
||||
content.indexOf('executor-returned worktree metadata') < content.indexOf('harness metadata'),
|
||||
'execute-phase must prefer executor-returned worktree metadata before runtime harness metadata (#1297)'
|
||||
);
|
||||
});
|
||||
|
||||
test('quick contract requires a cleanup manifest instead of global worktree discovery', () => {
|
||||
const content = fs.readFileSync(QUICK_PATH, 'utf8');
|
||||
assert.match(content, /WAVE_WORKTREE_MANIFEST|QUICK_WORKTREE_MANIFEST/);
|
||||
|
||||
Reference in New Issue
Block a user