docs(lanes): audit the last three files the census points at with no reason attached (#2908)
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) <noreply@anthropic.com>
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -81,6 +81,19 @@ export async function validateWorkflowDoneMergeProof(
|
||||
options: { result?: MergeResult; checkWorkflowSteps?: boolean } = {},
|
||||
): Promise<WorkflowDoneMergeProofVerdict> {
|
||||
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" };
|
||||
|
||||
Reference in New Issue
Block a user