fix(auto): guard startAuto() against concurrent invocation (#2923)
checkAutoStartAfterDiscuss() fire-and-forgets startAuto() when a milestone is ready. The headless runner then chains `/gsd auto`, calling startAuto() a second time. Two concurrent auto-loops on the same AutoSession singleton corrupt shared state (counters, dispatch maps), causing planning/execution to never run after research. Add an early `s.active` check at the top of startAuto() so the second call no-ops. Add source-scanning test to enforce the guard exists. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
eb2cfa580c
commit
86f97885cc
2 changed files with 23 additions and 0 deletions
|
|
@ -1061,6 +1061,11 @@ export async function startAuto(
|
|||
verboseMode: boolean,
|
||||
options?: { step?: boolean },
|
||||
): Promise<void> {
|
||||
if (s.active) {
|
||||
debugLog("startAuto", { phase: "already-active", skipping: true });
|
||||
return;
|
||||
}
|
||||
|
||||
const requestedStepMode = options?.step ?? false;
|
||||
|
||||
// Escape stale worktree cwd from a previous milestone (#608).
|
||||
|
|
|
|||
|
|
@ -1195,6 +1195,24 @@ test("startAuto calls selfHealRuntimeRecords before autoLoop (#1727)", { skip: "
|
|||
);
|
||||
});
|
||||
|
||||
test("startAuto guards against concurrent invocation (#2923)", () => {
|
||||
const src = readFileSync(
|
||||
resolve(import.meta.dirname, "..", "auto.ts"),
|
||||
"utf-8",
|
||||
);
|
||||
const fnIdx = src.indexOf("export async function startAuto");
|
||||
assert.ok(fnIdx > -1, "startAuto must exist in auto.ts");
|
||||
// The guard must appear before any other logic in the function body
|
||||
const fnBody = src.slice(fnIdx, fnIdx + 500);
|
||||
const activeGuard = fnBody.indexOf("if (s.active)");
|
||||
assert.ok(activeGuard > -1, "startAuto must check s.active to prevent concurrent auto-loops");
|
||||
const returnIdx = fnBody.indexOf("return;", activeGuard);
|
||||
assert.ok(
|
||||
returnIdx > -1 && returnIdx < activeGuard + 120,
|
||||
"s.active guard must early-return to prevent a second concurrent loop",
|
||||
);
|
||||
});
|
||||
|
||||
test("agent_end handler calls resolveAgentEnd (not handleAgentEnd)", () => {
|
||||
const hooksSrc = readFileSync(
|
||||
resolve(import.meta.dirname, "..", "bootstrap", "register-hooks.ts"),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue