From aacd18e8477d1b6ac2a10ff06161b69bf7aa64a6 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Thu, 30 Jul 2026 18:05:52 -0700 Subject: [PATCH] docs(lanes): audit the last three files the census points at with no reason attached (#2908) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Second pass of #2873's sweep, over the files that still carry lifecycle guards and **zero** audit notes. No source change — every literal stays counted, none gets an exemption marker. ## `project-store-ops.ts` (1) — dead sync path, do **not** convert The literal would leak a merge-queue entry on a renamed board: a card leaving review would never be dequeued. Except the function cannot run — it reaches for `store.db.prepare`, which throws in PostgreSQL backend mode. The live path is `dequeueMergeQueueOnColumnExitInTransaction` (`async-merge-coordination.ts`, called from `moves.ts`), and it is **already converted** — it takes `moveReviewColumns` and the caller supplies them. Recorded so the census entry is not mistaken for unconverted debt, and so it can be deleted alongside the rest of the sync SQLite residue. ## `task-id-integrity.ts` (2) — one real, one sentinel, and the real one must not go alone ```ts return cached?.column === "archived"; // ← board lane: real if (live === "archived") return true; // ← getLiveTaskColumn's manufactured value: sentinel ``` Converting the first while `getLiveTaskColumn` still keys on the literal would leave the two disagreeing about what "archived" means. It waits for that one, which is the single highest-leverage line in this cluster — fixing it makes five downstream sentinel checks correct without touching any of them. ## `auto-merge-finalization.ts` (3) — one real but diagnostic-only, two non-defects `task.column === "done"` selects which **reason string** is reported; both arms return `{ ok: false }`. So a renamed board is refused with the generic `missing-merge-confirmation` instead of the specific `done-without-merge-confirmation`. Real, and worth less than the signature change required to fix it — the resolver two functions up already computes `isCompleteColumn`, but this function does not receive it. The other two are **not** defects and it is worth saying so explicitly: the `columnId === "done"` near the top is the resolver's documented degraded fallback (the live arm calls `columnHasFlag`), and the `step.status` comparison is a **step status**, not a column. ## The pattern across both passes Of **6 files and 15 guards** audited: **2** were live defects worth converting, **4** were sentinels or dead paths that would have *broken* a renamed board if converted, and the rest were diagnostics or misfiled step statuses. That ratio is the argument for these notes existing. A file's census count is an upper bound on convertible sites, not a work estimate — and in this cluster the naive reading of the number would have made things worse more often than better. ## Verification - `pnpm test:gate` — 161 / 487 / 13 / 71 passed - `pnpm lint` — clean - `tsc --noEmit` (`@fusion/core`, `@fusion/engine`) — clean - census `--strict` — exit 0, counts unchanged (that is the point) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 (1M context) --- .../core/src/task-store/project-store-ops.ts | 14 ++++++++++++++ .../core/src/task-store/task-id-integrity.ts | 16 ++++++++++++++++ packages/engine/src/auto-merge-finalization.ts | 13 +++++++++++++ 3 files changed, 43 insertions(+) diff --git a/packages/core/src/task-store/project-store-ops.ts b/packages/core/src/task-store/project-store-ops.ts index 2a22488069..13c460a9b6 100644 --- a/packages/core/src/task-store/project-store-ops.ts +++ b/packages/core/src/task-store/project-store-ops.ts @@ -504,6 +504,20 @@ export function getRunAuditEventsImpl(store: TaskStore, options: RunAuditEventFi return rows.map((row) => store.rowToRunAuditEvent(row)); } +/* +FNXC:WorkflowLifecycleColumns 2026-07-31-02:45 (audited — DEAD SYNC PATH, do not convert): +The literal below would leak a merge-queue entry on a renamed board — a card leaving review would +never be dequeued — except that this function does not run in production. + +It is the SQLite-mode twin. The live path is `dequeueMergeQueueOnColumnExitInTransaction` +(`async-merge-coordination.ts`), called from `moves.ts`, and it is ALREADY converted: it takes +`moveReviewColumns` and the caller supplies them. This body reaches for `store.db.prepare`, which +throws in PostgreSQL backend mode, so a renamed board never gets far enough to be mis-dequeued. + +Converting it would mean threading a lane set into a function whose first statement cannot execute. +Recorded instead, so the census entry is not mistaken for unconverted debt — and so that whoever +finally deletes the sync SQLite residue can take this with it. +*/ export function dequeueMergeQueueOnColumnExitImpl(store: TaskStore, taskId: string, previousColumn: ColumnId, nextColumn: ColumnId, now: string): void { if (previousColumn !== "in-review" || nextColumn === "in-review") { return; diff --git a/packages/core/src/task-store/task-id-integrity.ts b/packages/core/src/task-store/task-id-integrity.ts index 7c00c71d04..4552c36c68 100644 --- a/packages/core/src/task-store/task-id-integrity.ts +++ b/packages/core/src/task-store/task-id-integrity.ts @@ -422,6 +422,22 @@ export function isTaskArchivedImpl(store: TaskStore, id: string): boolean { Sync isTaskArchived cannot query PostgreSQL. Prefer isTaskArchivedAsyncImpl from async callers. In backend mode use the in-memory task cache when the row is already hydrated; otherwise false (caller should have used async). + */ + /* + FNXC:WorkflowLifecycleColumns 2026-07-31-02:45 (audited — REAL, and narrow): + `cached.column` is a real board lane, so a renamed archived column is not recognised and this + sync check answers false for a card the board shows as archived. + + Narrow because of what it already concedes: the note above says this path exists only for a row + that happens to be hydrated in the cache, and every async caller is told to use + `isTaskArchivedAsyncImpl` instead. The authoritative path (below) reads `getLiveTaskColumn`, whose + own comparison is the one worth converting — fixing it there makes this file's sentinel check + correct without touching it. + + Left counted so the census keeps pointing here, and deliberately NOT converted in isolation: a + sync function with no store-scoped workflow read cannot resolve a lane, and converting this one + while `getLiveTaskColumn` still keys on the literal would leave the two disagreeing about what + archived means. */ const cached = store.taskCache.get(id); return cached?.column === "archived"; diff --git a/packages/engine/src/auto-merge-finalization.ts b/packages/engine/src/auto-merge-finalization.ts index 4b9274acf2..ebf770726e 100644 --- a/packages/engine/src/auto-merge-finalization.ts +++ b/packages/engine/src/auto-merge-finalization.ts @@ -81,6 +81,19 @@ export async function validateWorkflowDoneMergeProof( options: { result?: MergeResult; checkWorkflowSteps?: boolean } = {}, ): Promise { const hasProof = hasDurableMergeProof(task, options.result); + /* + FNXC:WorkflowLifecycleColumns 2026-07-31-02:45 (audited — REAL but DIAGNOSTIC-ONLY): + This literal selects which REASON STRING is reported, not which branch runs. Both arms return + `{ ok: false }`, so on a renamed board a card sitting in the complete lane is refused with the + generic `missing-merge-confirmation` instead of the specific `done-without-merge-confirmation`. + + Worth recording rather than converting from here: the resolver two functions up already computes + `isCompleteColumn` for exactly this workflow, and threading it in is the right fix — but this + function does not receive it, and widening the signature to improve an error string is a change + whose cost outweighs the diagnosis it sharpens. The other two census entries in this file are NOT + defects: the `columnId === "done"` at the top is the resolver's documented degraded fallback (the + live arm calls `columnHasFlag`), and the `step.status` comparison is a STEP status, not a column. + */ if (!hasProof) return { ok: false, reason: task.column === "done" ? "done-without-merge-confirmation" : "missing-merge-confirmation" }; if (options.checkWorkflowSteps !== false && hasIncompleteWorkflowSteps(task)) { return { ok: false, reason: "incomplete-workflow-steps" };