test(engine): pin #3047's archive-sweep conversion — measured uncovered (825 tests passed against the reverted fix) (#3115)

Not a conversion — the fleet's conversions are landing faster than their
coverage, and this is the audit that shows which ones actually have any.

## Method

For each of today's fleet commits to `self-healing.ts`: revert that
single commit, re-run the file's suites, see whether anything fails. If
nothing fails, the conversion has no regression protection and the "N
tests passed" cited on its PR was measuring something else.

| commit | reverted | verdict |
|---|---|---|
| #3075 pause-abort recovery | suites **fail** | covered |
| **#3047 archiveStaleDoneTasks** | **825 tests all pass** |
**uncovered** |
| #3078 (mine) | 204 tests all passed | was uncovered — closed by #3090,
#3102 |

Every fixture in the `archiveStaleDoneTasks` describe block uses the id
`done`, where the literal is correct, so none of them could see the
conversion at all.

## What the literal cost

The sweep's dependent scan skips tasks in terminal lanes. On a renamed
board **nothing matched `done`/`archived`, so every task read as
active** — which means every archive candidate looked like it had active
dependents, and the sweep archived **nothing**. The board quietly stops
auto-archiving: no error, no log line, no failing test.

## The case covers both halves of #3047

- a stale card in a **renamed complete lane** is archived — the
`complete` role
- a card whose dependent is still live in the **renamed wip lane** is
**not** archived, and a dependent already in the **renamed archive
lane** does not count as live — the `terminal` role

That second assertion is the one that matters: it stops the fix from
degenerating into "archive everything", which is the failure mode a
one-sided test would miss.

## Measured

**413 pass** on current main; reverting #3047 fails **exactly this
test**.

## Remaining audit

I have now audited 3 of ~13 fleet conversions to this file this way. The
method is cheap (one revert, one 17s suite run) and I will keep working
through the rest unless someone else picks it up. #3049 could not be
auto-reverted — later commits overlap its hunks — so it needs a manual
read rather than a mechanical revert.

## Verification

`self-healing.test.ts` **413 passed** · `pnpm test:gate` 13 + 161 + 487
+ 71 · lint — green.
This commit is contained in:
gsxdsm
2026-07-31 04:59:44 -07:00
committed by GitHub
parent ec2921b958
commit 6eeeb43d4b

View File

@@ -2310,6 +2310,53 @@ describe("SelfHealingManager", () => {
expect(store.archiveTaskAndCleanup).not.toHaveBeenCalledWith("FN-002");
});
/*
FNXC:WorkflowResolvedColumns 2026-07-31-20:10:
#3047 converted this sweep's two lane guards to the COMPLETE and TERMINAL roles and merged with no
case that could see the difference — measured: reverting that commit leaves all 825 self-healing
tests passing, because every fixture above uses the id `done`, where the literal is correct.
What the literal cost on a renamed board: the dependent scan treated EVERY task as active (nothing
matched `done`/`archived`), so every candidate looked like it had active dependents and the sweep
archived nothing. A silent no-op — the board just quietly stops auto-archiving.
*/
it("archives a stale card in a RENAMED complete lane, and skips one whose dependent is still live", async () => {
vi.setSystemTime(new Date("2026-01-04T00:00:00.000Z"));
(store.getSettings as ReturnType<typeof vi.fn>).mockResolvedValue({
autoArchiveDoneTasksEnabled: true,
autoArchiveDoneAfterMs: 24 * 60 * 60 * 1000,
doneAutoArchiveDays: 0,
} as unknown as Settings);
(store.listWorkflowDefinitions as ReturnType<typeof vi.fn> | undefined)?.mockResolvedValue?.([]);
(store as unknown as { listWorkflowDefinitions: unknown }).listWorkflowDefinitions = vi.fn(async () => [{
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" }] },
{ id: "vault", name: "vault", traits: [{ trait: "archived" }] },
],
},
}]);
(store.listTasks as ReturnType<typeof vi.fn>).mockResolvedValue([
{ id: "FN-OLD", column: "shipped", columnMovedAt: "2026-01-02T00:00:00.000Z", updatedAt: "2026-01-02T00:00:00.000Z" },
{ id: "FN-BLOCKED", column: "shipped", columnMovedAt: "2026-01-02T00:00:00.000Z", updatedAt: "2026-01-02T00:00:00.000Z" },
/* A LIVE dependent in the renamed wip lane must still protect its blocker from archiving. */
{ id: "FN-LIVE", column: "building", dependencies: ["FN-BLOCKED"], updatedAt: "2026-01-03T00:00:00.000Z" },
/* Already in the renamed archive lane: terminal, so neither a candidate nor an active dependent. */
{ id: "FN-FILED", column: "vault", dependencies: ["FN-OLD"], updatedAt: "2026-01-03T00:00:00.000Z" },
]);
const result = await manager.archiveStaleDoneTasks();
expect(result).toBe(1);
expect(store.archiveTaskAndCleanup).toHaveBeenCalledWith("FN-OLD");
expect(store.archiveTaskAndCleanup).not.toHaveBeenCalledWith("FN-BLOCKED");
});
it("uses doneAutoArchiveDays threshold and logs task age", async () => {
vi.setSystemTime(new Date("2026-03-01T00:00:00.000Z"));
(store.getSettings as ReturnType<typeof vi.fn>).mockResolvedValue({