From 5c5f6d81551af09f1a4fa45033bd922c587fd3db Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Fri, 31 Jul 2026 10:30:41 -0700 Subject: [PATCH] =?UTF-8?q?fix(core):=20mark=20the=20two=20archived=20STAT?= =?UTF-8?q?E=20sites=20at=20the=20site=20=E2=80=94=20converting=20them=20d?= =?UTF-8?q?estroys=20live=20work=20(#3157)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The LANE/STATE triage (#3154) found that **two of the eight** Drizzle `archived` sites are STATE markers that must never be resolved. That classification lived only in `archived-column-gate-parity.test.ts`. A coordinated three-encoding conversion **edits these files**. A converter working file-by-file sees the same `eq(column, "archived")` shape as the six LANE sites, with nothing in front of them to tell the two apart. So the markers go at the sites. ## `task-mutation-ops.ts` — `cleanupArchivedTasksImpl` Enumerates rows Fusion itself archived, then **removes their directories**. Widening it to the resolved archived-lane set would feed cards **merely resting in a board's archived-trait lane** into a filesystem delete. This is the only site in this family where a wrong conversion **destroys work** rather than hiding an affordance. ## `async-self-healing.ts` — `listSoftDeletedColumnDriftCandidates` Finds soft-deleted rows whose column **drifted** from the marker they are supposed to carry. Resolving it would classify a soft-deleted row in a renamed archive lane as drift and "repair" a row that is already correct. ## Why this is defensive rather than cosmetic The triage exists to make the conversion safe. A classification the converter **cannot see while editing the file** does not do that — it only helps someone who happens to read the gate's test file first, which is not how a file-by-file sweep proceeds. Both are marked DELIBERATE-LITERAL with the reason and a pointer to the parity test holding the full eight-site split. ## Measured - Comment-only. - Parity test **2/2**; archive + soft-delete suites — **5 files / 15 tests pass**. - `tsc --noEmit -p packages/core` clean; census `--strict` and `check-sql-column-literals` clean. - **No census movement** — a DELIBERATE-LITERAL marker on a STATE site is a classification, and these were never counted as lane debt. Co-authored-by: Claude Opus 5 (1M context) --- .../core/src/task-store/async-self-healing.ts | 11 +++++++++++ packages/core/src/task-store/task-mutation-ops.ts | 15 +++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/packages/core/src/task-store/async-self-healing.ts b/packages/core/src/task-store/async-self-healing.ts index ff8408bcf3..baa29a521e 100644 --- a/packages/core/src/task-store/async-self-healing.ts +++ b/packages/core/src/task-store/async-self-healing.ts @@ -55,6 +55,17 @@ interface SoftDeletedColumnDriftCandidate { export async function listSoftDeletedColumnDriftCandidates( db: AsyncDataLayer["db"], ): Promise { + /* + FNXC:WorkflowResolvedColumns 2026-07-31-23:59 DELIBERATE-LITERAL — STATE MARKER, DO NOT RESOLVE: + `"archived"` is the marker a soft-deleted row is SUPPOSED to carry, and this query finds rows whose + column DRIFTED away from it. Resolving it to the board's archived-lane set would classify a + soft-deleted row sitting in a renamed archive lane as drift and "repair" a row that is already + correct. + + Marked at the site for the same reason as `task-mutation-ops.ts`'s cleanup sweep: the LANE/STATE + classification lived only in `archived-column-gate-parity.test.ts`, and a coordinated conversion + edits this file. The shape here is indistinguishable from the six LANE sites without it. + */ const rows = await db .select({ id: schema.project.tasks.id, column: schema.project.tasks.column }) .from(schema.project.tasks) diff --git a/packages/core/src/task-store/task-mutation-ops.ts b/packages/core/src/task-store/task-mutation-ops.ts index 48442f93a7..472a4e9f38 100644 --- a/packages/core/src/task-store/task-mutation-ops.ts +++ b/packages/core/src/task-store/task-mutation-ops.ts @@ -1066,6 +1066,21 @@ export async function cleanupArchivedTasksImpl(store: TaskStore): Promise