docs(core): scope the archived three-encoding decision — it is not 52-or-nothing (#3147)

The `archived` family is the largest unclaimed cluster (52 sites) and
its blocker is that **nobody has scoped it**. This scopes it. It
converts nothing.

## The two options both read as enormous because 52 sites are counted as
one lump

They are not one lump. The sites answer **two different questions**:

| question | renameable? |
|---|---|
| **LANE** — "is this row resting in the board's archive lane?" | yes —
must resolve |
| **STATE** — "did Fusion archive this row?" (the marker `archiveTask`
writes) | **no** |

`async-maintenance.ts` already draws that line and marks its own site
DELIBERATE-LITERAL:

> `'archived'` is the STATE marker here, not a lane. This sweep collects
rows Fusion itself archived or soft-deleted; a card merely sitting in a
workflow's archived-TRAIT lane is live work and must not be collected.
Widening to the resolved archived set would pull real cards into a
cleanup pass.

**Converting that site would be a bug, not progress.**
`async-archive-lineage.ts`'s soft-delete path is the same shape —
`column = 'archived', deleted_at IS NOT NULL` is the storage state it
has just written.

So the first question is a **triage**, not a conversion: which of the 52
are lane questions? Nobody has answered it, which is exactly why the
cost reads as unbounded.

## Measured: the SQL half, which the existing note calls the hard part

8 Drizzle sites across 7 files.

**Four already have `store` in scope** — they could take a resolved set
today with no signature change:

- `branch-group-ops.ts` — `clearNearDuplicateReferencesToImpl(store,
...)`
- `branch-and-pr-entities.ts` —
`findRecentTasksByContentFingerprintImpl(store, ...)` (2 sites)
- `task-mutation-ops.ts` — `cleanupArchivedTasksImpl(store)`

**Four need one parameter each**, the same optional-lane-set shape used
throughout this program:

- `async-lifecycle.ts` — `liveLineageChildFilter(parentId, projectId?)`
- `async-search.ts` — `liveSearchPredicate(includeArchived, projectId?)`
- `async-self-healing.ts` — `listSoftDeletedColumnDriftCandidates(db,
...)`
- `store.ts` — the revert-lookup conditions (already holds
`this.asyncLayer`)

That is not *"threading a resolver into the persistence layer"*. It is
four call sites that already have what they need, plus four
one-parameter widenings — **before** any triage removes the STATE sites
from the count entirely.

## What I did not do, and why

The triage itself: a per-site judgement about what each guard *means*.
That belongs to whoever owns this gate, not to a passing fleet lane —
and getting it wrong in the STATE direction pulls live cards into a
cleanup sweep, which is the one failure mode here that destroys work
rather than hiding an affordance.

What was cheap and missing was the **shape** of the problem.

## Measured

- Comment-only; parity test **2/2**.
- `tsc --noEmit -p packages/core` clean; census `--strict` clean.
- `check-fnxc-future-dates` is red from `main`'s own #3128 stamps —
**#3139** fixes that; this branch inherits and does not add to it.

## Census

No movement.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-07-31 06:55:03 -07:00
committed by GitHub
parent 920d68e10f
commit db71b4fffb

View File

@@ -113,6 +113,34 @@ NOT CONVERTED HERE, and deliberately: the gate requires all three encodings to m
conversion is one coordinated change with its inventories updated in the same commit. This supplies
the classification that change needs; it does not pre-empt it.
FNXC:WorkflowResolvedColumns 2026-07-31-23:55 (SCOPING — the choice is not 52-or-nothing):
"Convert all three encodings" and "declare `archived` non-renameable" are the two options offered, and
both sound enormous because 52 sites are counted as one lump. They are not one lump: the sites answer
TWO DIFFERENT QUESTIONS, and only one of them is a lane question.
LANE: "is this row resting in the board's archive lane?" — renameable, must resolve.
STATE: "did Fusion archive this row?" — the marker `archiveTask` writes, NOT renameable.
`async-maintenance.ts` already draws that line and marks its site DELIBERATE-LITERAL: "the STATE
marker here, not a lane... a card merely sitting in a workflow's archived-TRAIT lane is live work and
must not be collected." Converting that site would be a BUG, not progress.
MEASURED, on the SQL half this file calls the hard part — 8 Drizzle sites across 7 files:
FOUR already have a `store` in scope and could take a resolved set with no signature change:
branch-group-ops.ts clearNearDuplicateReferencesToImpl(store, ...)
branch-and-pr-entities.ts findRecentTasksByContentFingerprintImpl(store, ...) [2 sites]
task-mutation-ops.ts cleanupArchivedTasksImpl(store)
FOUR need one parameter each, the optional-lane-set shape used throughout this program:
async-lifecycle.ts liveLineageChildFilter(parentId, projectId?)
async-search.ts liveSearchPredicate(includeArchived, projectId?)
async-self-healing.ts listSoftDeletedColumnDriftCandidates(db, ...)
store.ts the revert-lookup conditions (already holds `this.asyncLayer`)
That is not "threading a resolver into the persistence layer". It is four call sites that already have
what they need plus four one-parameter widenings — before the triage above removes the STATE sites
from the count. (Two of the four "already have a store" sites turned out to be STATE on inspection;
the triage note above is the authority, this is the reachability survey that preceded it.)
FNXC:WorkflowResolvedColumns 2026-07-31-23:50 (one wrong reason for picking the cheap option, removed):
The second option looks like it has already been taken — `trait-types.ts` annotates the flag
"RESTRICTED (built-in only)", which reads as "a custom board cannot have its own archive lane". It does