From 3d28e264c17d486c324d27af9262d2c0ed7ed57b Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Thu, 30 Jul 2026 03:25:06 -0700 Subject: [PATCH] =?UTF-8?q?test(self-healing):=20three=20suites=20seeded?= =?UTF-8?q?=20a=20column=20id=20the=20product=20stopped=20declaring=20(7?= =?UTF-8?q?=20red=20=E2=86=92=200)=20(#2695)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What was red `self-healing-advanced-triage`, `self-healing-agent-link-drift`, and `self-healing-starved-refinement` — **7 failed / 19 passed** on clean `origin/main`. All three fail the same way: the sweep returns `0` recoveries where the test expects `1`. ## Cause Those three sweeps were converted from `listTasks({ column: "triage" })` to **role** filters (`filterByPreWipRole(..., ["intake"])`). Each store fake has no workflow-selection readers, so the sweep resolves the **default IR** — in which `triage` is not a declared column at all. The seeded cards carried no intake role, the filters returned no candidates, and the sweeps did nothing. The product change was correct; the fixtures were asserting against a column id the product had stopped declaring. `self-healing.ts` even warns about this exact shape in its own comment: *"Converting a predicate while leaving its source query on a literal produces a sweep that LOOKS converted and does nothing."* The fixtures are the mirror image — a seed on the old literal makes a correctly-converted sweep look broken. **Verified, not assumed.** I resolved the default IR and printed the flags rather than reasoning from column names: ``` todo {"intake":true,"hold":true,"resetOnEntry":true} in-progress {"countsTowardWip":true,"abortOnExit":true,"timing":true} in-review {"mergeBlocker":true,"humanReview":true,...} done {"complete":true} archived {"archived":true,"hiddenFromBoard":true} ``` `todo` is the intake column post-U11 (the merged Planning column). `triage` does not appear. ## Measured | Check | Before | After | |---|---|---| | the three files | 7 failed / 19 passed | **26 passed** | | all 40 `self-healing-*` files | 3 failed files / 7 failed tests | **40 passed / 695 tests** | | `pnpm test:gate` | — | **726 passed** | | `pnpm lint` | — | clean | Restoring the deleted column id reintroduces the failures, so the seeds are load-bearing rather than cosmetic. **Pre-existing, untouched:** the same 9 vitest *"unhandled errors"* and the identical non-zero exit appear on clean main. Verified by reverting only these three test files and re-running — unrelated to the fixtures, so flagged rather than folded in. ## One deliberately surgical spot In `starved-refinement` only the starved refinement's own seed moved. My first pass renamed the file's default seed and **broke a test that was passing** — the auto-approve-all case calls `recoverApprovedTask` directly (no role filter) and asserts a move **into** `todo`, so seeding it in `todo` makes that move degenerate. I reverted and moved one seed instead. That leaves a real question I did **not** answer: post-U11 intake and hold are one column, so a `triage → todo` move may no longer encode anything. Deciding that means changing what the test is *about*, which is a behaviour judgement on someone else's assertion. **Flagged in-file, not guessed.** Also worth noting for whoever converts the remaining backlog: line 43 of that file pairs a `triage` seed against an explicit `todo` seed as a contrast. U11 collapsed those two columns, so the contrast the fixture was drawing no longer exists in the product — not a defect, but any fixture built on intake-vs-hold being distinct is now suspect. Census unchanged (722) — test files are not scanned by the census. ## Summary by CodeRabbit * **Tests** * Updated self-healing workflow test scenarios to reflect the current intake column. * Improved test coverage for triage, agent-link drift, and starved refinement handling. * Added documentation clarifying workflow column and role-based filtering assumptions. --- .../self-healing-advanced-triage.test.ts | 16 +++++++++++++++- .../self-healing-agent-link-drift.test.ts | 3 ++- .../self-healing-starved-refinement.test.ts | 16 +++++++++++++++- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/packages/engine/src/__tests__/self-healing-advanced-triage.test.ts b/packages/engine/src/__tests__/self-healing-advanced-triage.test.ts index c29e79ec33..3a9b908cb2 100644 --- a/packages/engine/src/__tests__/self-healing-advanced-triage.test.ts +++ b/packages/engine/src/__tests__/self-healing-advanced-triage.test.ts @@ -4,12 +4,26 @@ import type { Settings, Task, TaskStore } from "@fusion/core"; import { activeSessionRegistry } from "../active-session-registry.js"; import { SelfHealingManager } from "../self-healing.js"; +/* +FNXC:WorkflowResolvedColumns 2026-07-30-17:40: +The INTAKE column of the default lineage, which post-U11 is `todo` — the merged Planning column +carrying intake+hold+resetOnEntry. `triage` is not a declared column on any current workflow. + +Why this fixture went stale rather than merely renamed: `recoverAdvancedTriageTasks` was converted +from `listTasks({ column: "triage" })` to a ROLE filter (`filterByPreWipRole(..., ["intake"])`). +This store fake has no workflow-selection readers, so the sweep resolves the DEFAULT IR, in which +`triage` does not appear — the seeded card therefore carried no intake role, the filter returned no +candidates, and the sweep reported 0 recoveries. The test was asserting against a column id the +product had stopped declaring. +*/ +const INTAKE_COLUMN = "todo"; + function task(id: string, overrides: Partial = {}): Task { return { id, title: id, description: id, - column: "triage", + column: INTAKE_COLUMN, status: null, paused: false, worktree: `/tmp/${id}`, diff --git a/packages/engine/src/__tests__/self-healing-agent-link-drift.test.ts b/packages/engine/src/__tests__/self-healing-agent-link-drift.test.ts index bd4fe04cd0..0ad21fa492 100644 --- a/packages/engine/src/__tests__/self-healing-agent-link-drift.test.ts +++ b/packages/engine/src/__tests__/self-healing-agent-link-drift.test.ts @@ -83,7 +83,8 @@ describe("FN-4296: self-healing agent link drift", () => { const agents = [makeAgent("agent-backend", "FN-7001", "running")]; const queuedTask = { id: "FN-7001", - column: "triage", + // FNXC:WorkflowResolvedColumns 2026-07-30-17:40: the intake column post-U11 is `todo`. + column: "todo", status: "queued", overlapBlockedBy: "FN-6827", } as Task; diff --git a/packages/engine/src/__tests__/self-healing-starved-refinement.test.ts b/packages/engine/src/__tests__/self-healing-starved-refinement.test.ts index ad3b793a92..7a76108d9b 100644 --- a/packages/engine/src/__tests__/self-healing-starved-refinement.test.ts +++ b/packages/engine/src/__tests__/self-healing-starved-refinement.test.ts @@ -30,8 +30,22 @@ describe("SelfHealingManager.recoverStarvedRefinementTriageTasks", () => { vi.useFakeTimers(); vi.setSystemTime(new Date("2026-05-15T11:00:00.000Z")); + /* + FNXC:WorkflowResolvedColumns 2026-07-30-17:55: + The starved refinement rests in the INTAKE column, which post-U11 is `todo` — the merged + Planning column carrying intake+hold. `recoverStarvedRefinementTriageTasks` filters by ROLE, and + this store fake has no workflow-selection readers, so it resolves the DEFAULT IR in which + `triage` is not a declared column: the old fixture therefore carried no intake role, the filter + returned no candidates, and the sweep reported 0 escalations. + + Only THIS test's seed moved. The file's default stays `triage` on purpose: the case at the bottom + ("auto-approve-all overrides stored workflow approval") calls `recoverApprovedTask` directly, with + no role filter, and asserts a move INTO `todo` — seeding it in `todo` makes that move degenerate. + Whether asserting a triage -> todo move still encodes anything post-U11 is a question about that + test's subject, not this fix: FLAGGED, not guessed. + */ const tasks: Task[] = [ - task({ id: "FN-R1", sourceType: "task_refine", createdAt: "2026-05-15T10:00:00.000Z", updatedAt: "2026-05-15T10:00:00.000Z", priority: "low" }), + task({ id: "FN-R1", column: "todo", sourceType: "task_refine", createdAt: "2026-05-15T10:00:00.000Z", updatedAt: "2026-05-15T10:00:00.000Z", priority: "low" }), task({ id: "FN-P1", column: "todo", sourceType: "dashboard_ui", updatedAt: "2026-05-15T10:15:00.000Z" }), task({ id: "FN-P2", column: "todo", sourceType: "dashboard_ui", updatedAt: "2026-05-15T10:16:00.000Z" }), task({ id: "FN-P3", column: "todo", sourceType: "dashboard_ui", updatedAt: "2026-05-15T10:17:00.000Z" }),