test(core): pin the open-undo query's finished-lane exclusion (#3233)

## 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.
This commit is contained in:
gsxdsm
2026-07-31 12:46:56 -07:00
committed by GitHub
parent dd09e57511
commit 2780a8ae7b

View File

@@ -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<void> {
const RENAME: Record<string, string> = {
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<void> {
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");
});
});