test(engine): pin the agent sweep's terminal skip (21st resolver, after a corrected fixture) (#3206)

`agentLinkTerminalColumns` was uncovered on the #3115 map. The existing
case uses `todo` and `in-progress`, so the terminal skip is never the
deciding branch.

## The corrected fixture is the lesson

An earlier attempt of mine put the card in a renamed **wip** lane and
stayed green when blinded — **correctly**. Such a card is caught by the
wip∪review set first, so the terminal resolver never decides anything.
The card has to rest in a renamed **complete** lane for this guard to be
the one that matters.

That is the same class as my two discards on the branch-conflict sweeps:
**the fixture has to reach the branch the resolver gates.** A test can
exercise the sweep, pass, and still never touch the line under test.

## What the literal costs

A finished task's agent is not skipped, so the sweep **unlinks an agent
from a task that completed normally** — churn on a row that needed no
repair, and a lost link if that agent was about to be reused.

## Observable

Asserts `syncExecutionTaskLink` — the action the guard prevents — rather
than a return value, per the rule from #3202.

## Measured

421 pass; blinding `agentLinkTerminalColumns` fails exactly this case.

**21 of 26 pinned** across 20 merged PRs.

## Still open, with the obstacle recorded

`reclaimHoldColumns` / `reclaimReviewColumns` resist: both audit paths
in that sweep emit the same `branch:auto-reclaim` type, differing only
by a `trigger` string the branch-level scan also produces, so no
observable I found isolates the bucket resolvers from the branch scan.
`agentParkedColumns` needs a fixture where parked-ness changes the
outcome — mine forced the proof true via a fresh run.

## Verification

`self-healing.test.ts` **421 passed** · `pnpm test:gate` full pass ·
lint — green.
This commit is contained in:
gsxdsm
2026-07-31 10:33:25 -07:00
committed by GitHub
parent 5c5f6d8155
commit 23403e1426

View File

@@ -1949,6 +1949,55 @@ describe("SelfHealingManager", () => {
expect(agentStore.updateAgentState).not.toHaveBeenCalledWith("agent-keep", "active");
managerWithAgents.stop();
});
/*
FNXC:WorkflowResolvedColumns 2026-07-31-23:55:
`agentLinkTerminalColumns` was UNCOVERED on the #3115 map. The case above uses `todo` and
`in-progress`, so the terminal skip is never the deciding branch.
An earlier attempt of mine put the card in a renamed WIP lane and stayed green when blinded —
correctly, because that card is caught by the wip∪review set first and the terminal resolver never
decides anything. The card has to rest in a renamed COMPLETE lane for this guard to be the one
that matters.
What the literal costs: a finished task's agent is not skipped, so the sweep unlinks an agent from
a task that completed normally — churn on a row that needed no repair, and a lost link if the
agent was about to be reused.
*/
it("skips an agent whose task rests in a RENAMED complete lane", async () => {
const now = Date.now();
const agents: Agent[] = [
{ id: "agent-done", state: "running", taskId: "FN-SHIPPED", updatedAt: new Date(now - 120_000).toISOString() } as Agent,
];
const getTask = vi.fn(async () => ({ id: "FN-SHIPPED", column: "shipped" } as Task));
const agentStore = {
listAgents: vi.fn(async () => agents),
getActiveHeartbeatRun: vi.fn(async () => null),
updateAgentState: vi.fn(async () => undefined),
syncExecutionTaskLink: vi.fn(async () => undefined),
} as unknown as AgentStore;
const store = createMockStore({ getTask });
(store as unknown as { listWorkflowDefinitions: unknown }).listWorkflowDefinitions = vi.fn(async () => [{
id: "custom:renamed",
ir: {
version: "v2",
id: "custom:renamed",
nodes: [],
edges: [],
columns: [
{ id: "building", name: "building", traits: [{ trait: "wip", config: { limitSetting: "maxConcurrent" } }] },
{ id: "shipped", name: "shipped", traits: [{ trait: "complete" }] },
],
},
}]);
const managerWithAgents = new SelfHealingManager(store, { rootDir: "/tmp/test-project", agentStore });
await managerWithAgents.recoverAgentsRunningOnInactiveTasks();
/* The unlink is the action this guard prevents; asserting it is what discriminates. */
expect(agentStore.syncExecutionTaskLink).not.toHaveBeenCalled();
managerWithAgents.stop();
});
});
describe("recoverStaleHeartbeatRuns", () => {