fix(core): mark the two archived STATE sites at the site — converting them destroys live work (#3157)

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) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-07-31 10:30:41 -07:00
committed by GitHub
parent dd3bf6a764
commit 5c5f6d8155
2 changed files with 26 additions and 0 deletions

View File

@@ -55,6 +55,17 @@ interface SoftDeletedColumnDriftCandidate {
export async function listSoftDeletedColumnDriftCandidates(
db: AsyncDataLayer["db"],
): Promise<SoftDeletedColumnDriftCandidate[]> {
/*
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)

View File

@@ -1066,6 +1066,21 @@ export async function cleanupArchivedTasksImpl(store: TaskStore): Promise<string
quarantine partition.
*/
const projectId = layer.projectId?.trim() || "__legacy_unscoped__";
/*
FNXC:WorkflowResolvedColumns 2026-07-31-23:59 DELIBERATE-LITERAL — STATE MARKER, DO NOT RESOLVE:
`"archived"` here is the marker `archiveTask` WROTE, not a board lane. This sweep enumerates rows
Fusion itself archived and then REMOVES THEIR DIRECTORIES (`rm` below). Widening it to the
resolved archived-lane set would feed cards merely RESTING in a board's archived-trait lane into
a filesystem delete — live work, destroyed.
Marked at the site because the classification previously lived only in
`archived-column-gate-parity.test.ts`, and a coordinated three-encoding conversion edits THIS
file. A converter working file-by-file would see the same `eq(column, "archived")` shape as the
six LANE sites and have nothing here telling them apart.
The sibling STATE site is `async-self-healing.ts`'s soft-deleted column-drift query; the LANE/
STATE split for all eight Drizzle sites is recorded in that parity test.
*/
const archivedRows = await layer.db
.select()
.from(schema.project.tasks)