fix(gsd): use project root for prior-slice dispatch guard (#2863)
Resolve the prior-slice completion guard against originalBasePath when auto-mode is running in a worktree. This keeps completed upstream milestones from blocking new dispatches because their SUMMARY state lives at the project root, not the stale worktree snapshot. Closes #2838 Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
27f66100e5
commit
36930694e4
2 changed files with 69 additions and 2 deletions
|
|
@ -45,6 +45,17 @@ export function _resolveReportBasePath(s: Pick<AutoSession, "originalBasePath" |
|
|||
return s.originalBasePath || s.basePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the authoritative project base for dispatch guards.
|
||||
* Prior-milestone completion lives at the project root, even when the active
|
||||
* unit is running inside an auto worktree.
|
||||
*/
|
||||
export function _resolveDispatchGuardBasePath(
|
||||
s: Pick<AutoSession, "originalBasePath" | "basePath">,
|
||||
): string {
|
||||
return s.originalBasePath || s.basePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate and write an HTML milestone report snapshot.
|
||||
* Extracted from the milestone-transition block in autoLoop.
|
||||
|
|
@ -667,9 +678,10 @@ export async function runDispatch(
|
|||
prompt = preDispatchResult.prompt;
|
||||
}
|
||||
|
||||
const guardBasePath = _resolveDispatchGuardBasePath(s);
|
||||
const priorSliceBlocker = deps.getPriorSliceCompletionBlocker(
|
||||
s.basePath,
|
||||
deps.getMainBranch(s.basePath),
|
||||
guardBasePath,
|
||||
deps.getMainBranch(guardBasePath),
|
||||
unitType,
|
||||
unitId,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -260,6 +260,61 @@ test("runDispatch emits dispatch-stop when dispatch returns stop action", async
|
|||
assert.equal(stopEvents[0].flowId, ic.flowId);
|
||||
});
|
||||
|
||||
test("runDispatch checks prior-slice completion against the project root in worktree mode", async () => {
|
||||
const capture = createEventCapture();
|
||||
const guardCalls: Array<{ fn: string; args: unknown[] }> = [];
|
||||
const deps = makeMockDeps(capture, {
|
||||
getMainBranch: (basePath: string) => {
|
||||
guardCalls.push({ fn: "getMainBranch", args: [basePath] });
|
||||
return "main";
|
||||
},
|
||||
getPriorSliceCompletionBlocker: (
|
||||
basePath: string,
|
||||
mainBranch: string,
|
||||
unitType: string,
|
||||
unitId: string,
|
||||
) => {
|
||||
guardCalls.push({
|
||||
fn: "getPriorSliceCompletionBlocker",
|
||||
args: [basePath, mainBranch, unitType, unitId],
|
||||
});
|
||||
return null;
|
||||
},
|
||||
});
|
||||
const ic = makeIC(deps, {
|
||||
s: {
|
||||
...makeSession(),
|
||||
basePath: "/tmp/project/.gsd/worktrees/M029-xoklo9",
|
||||
originalBasePath: "/tmp/project",
|
||||
} as any,
|
||||
});
|
||||
const preData: PreDispatchData = {
|
||||
state: {
|
||||
phase: "executing",
|
||||
activeMilestone: { id: "M029-xoklo9", title: "Test", status: "active" },
|
||||
activeSlice: { id: "S01", title: "Slice 1" },
|
||||
registry: [{ id: "M029-xoklo9", status: "active" }],
|
||||
blockers: [],
|
||||
} as any,
|
||||
mid: "M029-xoklo9",
|
||||
midTitle: "Test Milestone",
|
||||
};
|
||||
|
||||
const result = await runDispatch(ic, preData, {
|
||||
recentUnits: [],
|
||||
stuckRecoveryAttempts: 0,
|
||||
});
|
||||
|
||||
assert.equal(result.action, "next");
|
||||
assert.deepEqual(guardCalls, [
|
||||
{ fn: "getMainBranch", args: ["/tmp/project"] },
|
||||
{
|
||||
fn: "getPriorSliceCompletionBlocker",
|
||||
args: ["/tmp/project", "main", "execute-task", "M001/S01/T01"],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test("runUnitPhase emits unit-start and unit-end with causedBy reference", async () => {
|
||||
const capture = createEventCapture();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue