From 2780a8ae7b8a8d4ebf9d8796aec2004278619b34 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Fri, 31 Jul 2026 12:46:56 -0700 Subject: [PATCH] test(core): pin the open-undo query's finished-lane exclusion (#3233) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What Pins the **open-undo query's finished-lane exclusion** in `packages/core/src/store.ts`. Test-only. `findOpenRevertTaskForSource` answers *"is there an OPEN undo task for this source?"* — the question behind the dashboard's Undo affordance. It answers by **excluding the finished lanes**, so a prior undo that already landed does not keep rendering as open. Blinding that exclusion back to `ne(column,"archived"), ne(column,"done")` left the entire 16-file lane-detector set green. **No test in `packages/core` reaches this method at all.** The dashboard-side twin (`taskRevert.ts`, #3129) is tested; the store-side query behind it was not. ## Measured | | default (control) | renamed complete | renamed archived | working lane | |---|---|---|---|---| | converted | pass | pass | pass | pass | | blinded to `["done","archived"]` | pass | **FAIL** | **FAIL** | pass | ``` converted: Test Files 1 passed (1) / Tests 4 passed (4) blinded: Test Files 1 failed (1) / Tests 2 failed | 2 passed (4) lint clean; fnxc-future-dates: none added; census unchanged ``` Blind confirmed applied with `git diff --stat` before the run. ## What breaks without it On a board whose complete lane is `shipped`, neither literal matches, so a **done** undo task is never excluded and the query keeps returning it. The card shows an undo already in flight *forever*, and the real affordance is unreachable. Nothing errors — the button is just permanently wrong, which is why it went unnoticed. ## Includes the paired positive An undo still in a **working** lane IS reported as open. Excluding the finished lanes must not degrade into excluding everything, or the affordance breaks in the other direction and no undo is ever reported in flight. Both new failing cases are renamed-lane cases; both survivors are cases that should survive. ## Where this came from Per-site blinding of all 14 remaining `resolveProjectColumnsForRoles` call sites in `core`, run against a 16-file detector set. **9 covered, 5 uncovered:** | site | verdict | |---|---| | `store.ts:1135` | **uncovered** → pinned here | | `async-mission-store.ts:1179` (archived) | **uncovered** — its neighbour `:1178` (complete) is covered | | `branch-and-pr-entities.ts:445` | **uncovered** | | `branch-and-pr-entities.ts:484` | **uncovered** | | `task-id-integrity.ts:502` | **uncovered** | | `reads.ts` ×3, analytics ×3, `eval-automation`, `task-artifacts-ops`, `async-mission-store:1178` | covered | The first run of that probe was **invalid and I nearly published it**: it reported all 14 sites "COVERED" with *zero failing tests*. zsh does not word-split unquoted parameter expansions, so `vitest run $DET` passed 16 paths as one argument and vitest exited 1 with "No test files found" — which my script read as a failing test. The re-run treats that string as `INVALID` rather than a result. Third time this session a wrong reading came from test *selection* rather than from blinding. ## Flagged, not guessed The four remaining uncovered sites are named above rather than quietly left; `async-mission-store` shows the same adjacent-pair split as `team-analytics` in #3227, which is now the third confirmed instance of that shape. --- .../open-revert-task-lanes.pg.test.ts | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 packages/core/src/__tests__/postgres/open-revert-task-lanes.pg.test.ts diff --git a/packages/core/src/__tests__/postgres/open-revert-task-lanes.pg.test.ts b/packages/core/src/__tests__/postgres/open-revert-task-lanes.pg.test.ts new file mode 100644 index 0000000000..be8858a40d --- /dev/null +++ b/packages/core/src/__tests__/postgres/open-revert-task-lanes.pg.test.ts @@ -0,0 +1,123 @@ +/* +FNXC:WorkflowResolvedColumns 2026-07-31-22:50: +THE OPEN-UNDO QUERY, on a RENAMED board. + +`findOpenRevertTaskForSource` answers "is there an OPEN undo task for this source?" — the question +behind the dashboard's Undo affordance. It answers it by EXCLUDING the finished lanes, so a prior +undo attempt that already landed does not keep rendering as open. + +WHY THIS FILE EXISTS. That exclusion was converted to +`resolveProjectColumnsForRoles(this, ["complete", "archived"])`, and blinding it back to +`ne(column,"archived"), ne(column,"done")` left the whole 16-file lane-detector set green — no test +in `packages/core` reaches this method at all. The dashboard-side twin (`taskRevert.ts`, #3129) has +tests; the store-side query behind it did not. + +WHAT BREAKS WITHOUT THE CONVERSION. On a board whose complete lane is `shipped`, neither literal +matches, so a DONE undo task is never excluded and `findOpenRevertTaskForSource` keeps returning it. +The card shows an undo already in flight forever, and the real affordance is unreachable. Nothing +errors — the button is just permanently wrong. + +DIFFERENTIAL. The same seeded rows are queried under two vocabularies whose traits are identical and +only the ids differ; `shipped` collides with no legacy id, so a surviving `'done'` cannot pass by +luck. The default-vocabulary cases are the control: they pass with or without the conversion. +*/ + +import { afterAll, afterEach, beforeAll, beforeEach, expect, it } from "vitest"; +import { sql } from "drizzle-orm"; +import { + createSharedPgTaskStoreTestHarness, + pgDescribe, + type SharedPgTaskStoreHarness, +} from "../../__test-utils__/pg-test-harness.js"; +import { BUILTIN_CODING_WORKFLOW_IR } from "../../builtin-coding-workflow-ir.js"; + +const AT = "2026-06-15T12:00:00.000Z"; + +pgDescribe("findOpenRevertTaskForSource under a renamed board vocabulary", () => { + const h: SharedPgTaskStoreHarness = createSharedPgTaskStoreTestHarness({ + prefix: "fusion_open_revert_lanes", + }); + + beforeAll(h.beforeAll); + beforeEach(h.beforeEach); + afterEach(h.afterEach); + afterAll(h.afterAll); + + /** The builtin workflow with only its column ids renamed; traits are untouched. */ + async function seedRenamedWorkflow(): Promise { + const RENAME: Record = { + todo: "drafting", + "in-progress": "building", + "in-review": "checking", + done: "shipped", + archived: "vaulted", + }; + const rename = (id: string | undefined) => (id && RENAME[id]) ?? id; + const ir = JSON.parse(JSON.stringify(BUILTIN_CODING_WORKFLOW_IR)) as { + id: string; + nodes?: { column?: string }[]; + columns?: { id: string }[]; + }; + ir.id = "custom:renamed-open-revert"; + for (const node of ir.nodes ?? []) node.column = rename(node.column); + for (const column of ir.columns ?? []) column.id = rename(column.id) as string; + + const ids = (ir.columns ?? []).map((column) => column.id); + expect(ids).toContain("shipped"); + expect(ids).not.toContain("done"); + + await h.store().createWorkflowDefinition({ name: "Renamed", kind: "workflow", ir } as never); + } + + /** A source task plus an undo task pointing at it, parked in `revertLane`. */ + async function seedRevertPair(revertLane: string): Promise { + const store = h.store(); + const adminDb = h.adminDb(); + for (const id of ["KB-SRC", "KB-REV"]) { + await store.createTaskWithReservedId( + { description: id, column: "todo" }, + { taskId: id, createdAt: AT, updatedAt: AT, applyDefaultWorkflowSteps: false }, + ); + } + /* Seeded directly: `sourceMetadata.revertOf` is the join this query reads, and moveTask would + reject a target column the default workflow does not declare. */ + await adminDb.execute(sql` + UPDATE project.tasks + SET "column" = ${revertLane}, source_metadata = ${JSON.stringify({ revertOf: "KB-SRC" })}::jsonb + WHERE id = 'KB-REV'`); + store.taskCache.delete("KB-REV"); + } + + it("default vocabulary: a FINISHED undo task is not reported as open", async () => { + await seedRevertPair("done"); + + expect(await h.store().findOpenRevertTaskForSource("KB-SRC")).toBeNull(); + }); + + it("renamed vocabulary: an undo task in the RENAMED complete lane is not reported as open", async () => { + await seedRenamedWorkflow(); + await seedRevertPair("shipped"); + + expect(await h.store().findOpenRevertTaskForSource("KB-SRC")).toBeNull(); + }); + + it("renamed vocabulary: an undo task in the RENAMED archived lane is not reported as open", async () => { + await seedRenamedWorkflow(); + await seedRevertPair("vaulted"); + + expect(await h.store().findOpenRevertTaskForSource("KB-SRC")).toBeNull(); + }); + + /* + The paired positive. Excluding the finished lanes must not degrade into excluding everything — an + undo that really IS open still has to be found, or the affordance breaks in the other direction + and no undo is ever reported in flight. + */ + it("renamed vocabulary: an undo task still in a WORKING lane IS reported as open", async () => { + await seedRenamedWorkflow(); + await seedRevertPair("building"); + + const open = await h.store().findOpenRevertTaskForSource("KB-SRC"); + expect(open?.id).toBe("KB-REV"); + }); +});