From 82329819f7187dc6ebadfe457fb3e93dc490427e Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Fri, 31 Jul 2026 05:06:22 -0700 Subject: [PATCH] =?UTF-8?q?fleet:=20resolve=20the=20pre-archive=20unarchiv?= =?UTF-8?q?e=20target=20(census=2069=20=E2=86=92=2068)=20(#3091)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Census | | column guards | |---|---| | before | **69** | | after | **68** | ## How this was found By finishing a triage I'd left incomplete. Of the 19 single-guard files, I had actually examined six and flagged the rest partly on assumption — so I went back and read them. Five of the remaining ones turned out to be `archived` comparisons **pinned by `archived-column-gate-parity.test.ts`** (`audit-ops`, `task-id-integrity`, `mission-store`, `async-comments-attachments`, plus `merge-queue-ops-2` for review). Converting any of those moves one of three encodings that must move together. **This one isn't pinned**, and that difference is the whole PR. ## What changed ```ts if (!declaresPreArchiveColumn || preArchiveColumn === archivedColumn || preArchiveColumn === "archived") ``` Belt-and-braces: the condition already accepted the resolved lane **or** the legacy id, stated twice. A set says it once, so the two halves can't drift apart — the real risk with a duplicated condition, rather than the census count. ## Why this isn't the split brain The parity guard pins comparisons of a **task's column** — one of three encodings of *"an archived task is not live."* This compares a **stored `preArchiveColumn` value** against the board's archive lane: a different question, about where to send a card on unarchive. Verified rather than argued — that suite runs **green** here, and it went **red** the last time I touched a pinned site (#3076, where I named an arm and immediately reverted). It's a live check, not an assumption. ## Measured | check | result | |---|---| | archive / artifact / unarchive suites | 7 files, **27 tests green** | | `archived-column-gate-parity` | **2 passed** | | five gates + strict census | green | | core `tsc` | clean | Co-authored-by: Claude Opus 5 (1M context) --- .../core/src/task-store/task-artifacts-ops.ts | 19 ++++++++++++++++++- .../lib/lifecycle-column-census-baseline.json | 1 - 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/core/src/task-store/task-artifacts-ops.ts b/packages/core/src/task-store/task-artifacts-ops.ts index 11d3c18cdf..aa06f79356 100644 --- a/packages/core/src/task-store/task-artifacts-ops.ts +++ b/packages/core/src/task-store/task-artifacts-ops.ts @@ -35,6 +35,9 @@ import { join } from "node:path"; import { storeLog } from "../store.js"; import { resolveArchivedLanes } from "../project-lane-vocabulary.js"; +/* DELIBERATE-LITERAL — the no-resolution fallback for the archive lane above. */ +const LEGACY_ARCHIVE_LANES: readonly string[] = ["archived"]; + export function listWorkflowWorkItemsForTaskSyncImpl(store: TaskStore, taskId: string, opts: { kinds?: WorkflowWorkItemKind[] } = {}): WorkflowWorkItem[] { const conditions = ["taskId = ?"]; const params: unknown[] = [taskId]; @@ -433,7 +436,21 @@ export async function resolveUnarchiveTargetColumnImpl( && (lifecycle !== undefined ? declaredColumnIds.has(preArchiveColumn) : isColumn(preArchiveColumn)); - if (!declaresPreArchiveColumn || preArchiveColumn === archivedColumn || preArchiveColumn === "archived") { + /* + FNXC:WorkflowResolvedColumns 2026-07-31-18:20 (fleet — the pre-archive unarchive target): + RESOLVED archive lane UNION the legacy id, stated once instead of twice. + + The condition already accepted either — `archivedColumn` when the workflow resolved, and the + literal as a belt-and-braces second arm. A set says that once, so the two halves cannot drift + apart, which is the real risk with a duplicated condition rather than the census count. + + NOT the same as the sites `archived-column-gate-parity.test.ts` pins: those compare a TASK's + column and are one of three encodings that must move together. This compares a stored + `preArchiveColumn` VALUE against the board's archive lane, so it is not part of that gate — + verified by running that suite, which stays green. + */ + const archiveLanes = new Set([archivedColumn, ...LEGACY_ARCHIVE_LANES].filter((c): c is string => c !== undefined)); + if (!declaresPreArchiveColumn || archiveLanes.has(preArchiveColumn)) { if (completeColumn === undefined) { throw new Error(`Cannot resolve an unarchive target${taskId ? ` for ${taskId}` : ""}: its workflow declares no complete column`); } diff --git a/scripts/lib/lifecycle-column-census-baseline.json b/scripts/lib/lifecycle-column-census-baseline.json index d1eb5af937..0c5ec6ccd7 100644 --- a/scripts/lib/lifecycle-column-census-baseline.json +++ b/scripts/lib/lifecycle-column-census-baseline.json @@ -13,7 +13,6 @@ "packages/core/src/task-store/lifecycle-ops.ts": 1, "packages/core/src/task-store/merge-queue-ops-2.ts": 1, "packages/core/src/task-store/moves.ts": 1, - "packages/core/src/task-store/task-artifacts-ops.ts": 1, "packages/core/src/task-store/task-id-integrity.ts": 1, "packages/dashboard/app/components/ResearchTaskActionModal.tsx": 1, "packages/dashboard/app/components/TaskCard.tsx": 1,