From 6eeeb43d4bada485135282e6619ca21931e16475 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Fri, 31 Jul 2026 04:59:44 -0700 Subject: [PATCH] =?UTF-8?q?test(engine):=20pin=20#3047's=20archive-sweep?= =?UTF-8?q?=20conversion=20=E2=80=94=20measured=20uncovered=20(825=20tests?= =?UTF-8?q?=20passed=20against=20the=20reverted=20fix)=20(#3115)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- .../engine/src/__tests__/self-healing.test.ts | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/packages/engine/src/__tests__/self-healing.test.ts b/packages/engine/src/__tests__/self-healing.test.ts index bdd3c021e2..bd3015c99f 100644 --- a/packages/engine/src/__tests__/self-healing.test.ts +++ b/packages/engine/src/__tests__/self-healing.test.ts @@ -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).mockResolvedValue({ + autoArchiveDoneTasksEnabled: true, + autoArchiveDoneAfterMs: 24 * 60 * 60 * 1000, + doneAutoArchiveDays: 0, + } as unknown as Settings); + (store.listWorkflowDefinitions as ReturnType | 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).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).mockResolvedValue({