* test(#3795): the interrupted-agent read must precede the stale-id clear * fix(#3795): read the interrupted agent id before clearing the stale marker execute-plan's init_agent_tracking step ran `rm -f .planning/current-agent-id.txt` BEFORE the existence check that read it, so the interrupted-agent branch and the Task resume prompt it exists to offer were unreachable (#3795) — a kill -9 mid-executor left the file, and the next run deleted it before looking. The read now precedes the clear; fresh-run semantics (no stale id leaking into the new spawn) are preserved. A structural guard pins the order. Emitted-Drift-Ack-Growth: execute-plan.md — #3795: +bytes from reordering the interrupted-agent read before the rm plus the explaining comment * chore(#3795): changeset fragment (pr number backfilled after PR creation) * chore(#3795): backfill changeset PR number (4006) --------- Co-authored-by: sim <sim@local>
This commit is contained in:
5
.changeset/jolly-yaks-click.md
Normal file
5
.changeset/jolly-yaks-click.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
type: Fixed
|
||||
pr: 4006
|
||||
---
|
||||
**Interrupted executors can be resumed again** — execute-plan deleted `current-agent-id.txt` before the check that read it, so the interrupted-agent detection and its Task `resume` prompt were unreachable after a kill; the id is now captured before the stale marker is cleared. (#3795)
|
||||
@@ -155,11 +155,14 @@ Fresh context per subagent preserves peak quality. Main context stays lean.
|
||||
if [ ! -f .planning/agent-history.json ]; then
|
||||
echo '{"version":"1.0","max_entries":50,"entries":[]}' > .planning/agent-history.json
|
||||
fi
|
||||
rm -f .planning/current-agent-id.txt
|
||||
# #3795: read a SURVIVING id before clearing it — the clear used to run
|
||||
# first, so the check below was always false and the resume prompt at this
|
||||
# step's tail was never offered.
|
||||
if [ -f .planning/current-agent-id.txt ]; then
|
||||
INTERRUPTED_ID=$(cat .planning/current-agent-id.txt)
|
||||
echo "Found interrupted agent: $INTERRUPTED_ID"
|
||||
fi
|
||||
rm -f .planning/current-agent-id.txt
|
||||
```
|
||||
|
||||
If interrupted: ask user to resume (Task `resume` parameter) or start fresh.
|
||||
|
||||
50
tests/execute-plan-interrupted-agent-guard.test.cjs
Normal file
50
tests/execute-plan-interrupted-agent-guard.test.cjs
Normal file
@@ -0,0 +1,50 @@
|
||||
'use strict';
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// #3795 — execute-plan's interrupted-agent detection must be REACHABLE.
|
||||
//
|
||||
// The init_agent_tracking step cleared `.planning/current-agent-id.txt`
|
||||
// BEFORE the existence check that read it, so the interrupted-agent branch
|
||||
// and the Task `resume` prompt it exists to offer could never execute: a
|
||||
// kill -9 mid-executor left the file, and the very next run deleted it
|
||||
// before looking. The read must precede the clear.
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
const { test } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const EXECUTE_PLAN_MD = path.join(__dirname, '..', 'gsd-core', 'workflows', 'execute-plan.md');
|
||||
|
||||
// execute-plan.md is shipped workflow text — the bytes ARE what the runtime
|
||||
// loads; a structural scan over the step's own fenced block tests the
|
||||
// deployed contract (same shape as tests/config-get-raw-guard.test.cjs).
|
||||
function initAgentTrackingBlock() {
|
||||
const md = fs.readFileSync(EXECUTE_PLAN_MD, 'utf-8');
|
||||
const step = md.indexOf('<step name="init_agent_tracking">');
|
||||
assert.ok(step > 0, 'execute-plan.md must contain the init_agent_tracking step');
|
||||
const end = md.indexOf('</step>', step);
|
||||
assert.ok(end > step, 'init_agent_tracking step must close');
|
||||
return md.slice(step, end);
|
||||
}
|
||||
|
||||
test('#3795: the interrupted-agent read must precede the stale-id clear', () => {
|
||||
const block = initAgentTrackingBlock();
|
||||
const readCheck = block.indexOf('[ -f .planning/current-agent-id.txt ]');
|
||||
const clear = block.indexOf('rm -f .planning/current-agent-id.txt');
|
||||
assert.ok(readCheck > 0, 'the step must probe for a surviving current-agent-id.txt');
|
||||
assert.ok(clear > 0, 'the step must clear the stale id for the fresh run');
|
||||
assert.ok(
|
||||
readCheck < clear,
|
||||
'#3795: the existence check must run BEFORE the rm — delete-first made the interrupted-agent branch and its Task resume prompt unreachable',
|
||||
);
|
||||
assert.ok(
|
||||
/INTERRUPTED_ID=\$\(/.test(block),
|
||||
'the surviving id must be captured while the file still exists',
|
||||
);
|
||||
assert.ok(
|
||||
/resume/.test(block),
|
||||
'the resume-or-fresh prompt the read enables must stay present',
|
||||
);
|
||||
});
|
||||
@@ -107,7 +107,7 @@ const PROSE_ALLOWLIST = [
|
||||
{ file: 'agents/gsd-phase-researcher.md', line: 33, reason: 'package-legitimacy provenance rule names the command as the source of an OK verdict; descriptive' },
|
||||
{ file: 'agents/gsd-roadmapper.md', line: 642, reason: 'parenthetical "e.g." naming SDK queries a user *could* run; not an agent instruction' },
|
||||
{ file: 'agents/gsd-intel-updater.md', line: 40, reason: 'cross-platform note names the `gsd-tools intel <subcommand>` CLI surface descriptively ("CLI invocations go through..."); not an agent instruction' },
|
||||
{ file: 'gsd-core/workflows/execute-plan.md', line: 416, reason: 'describes the downstream SDK validation step (`validated downstream by ...`); names the mechanism, does not instruct the agent to type it' },
|
||||
{ file: 'gsd-core/workflows/execute-plan.md', line: 419, reason: 'describes the downstream SDK validation step (`validated downstream by ...`); names the mechanism, does not instruct the agent to type it' },
|
||||
];
|
||||
|
||||
// Resolver-snippet definition lines / probes that must never be flagged. A line
|
||||
|
||||
Reference in New Issue
Block a user