Files
fusion/docs/workflow-owned-lifecycle-closing-verification.md
gsxdsm 13bf7e001d Closing-bar verification pass on origin/main — one tree, one report (+ the E2E red it found) (#2660)
**Closing-bar item 4, run on one clean tree at `origin/main`
(`be63e72f1`).** Nobody was assigned this and my own work is merged, so
I took it.

> **This PR is now REPORT-ONLY — net zero file changes.** I found the
planning-lane E2E red, fixed it, then discovered **#2658 (gsxdsm) makes
byte-for-byte the same change** to the same helper and was opened first.
I reverted mine rather than leave two identical edits to one function to
conflict. **The E2E result below depends on #2658 landing** — on
`origin/main` without it, that suite is 2 failed / 5 passed.
>
> The duplication is worth one note for the fleet: two workers
independently hit the same control-card failure and independently traced
it to FN-7648's unplanned-seed gate plus a fixture that never wrote a
spec. Independent confirmation of the diagnosis, but also ~an hour spent
twice — the census-style work order exists to stop exactly that, and E2E
fixture defects are not on it.

## Report — all four, one tree

| Check | Result |
|---|---|
| `pnpm test:gate` | **PASS** (132 + 10 + 487 + 71 tests) |
| `pnpm verify:fast` | **PASS** — 13 steps green in 89.9s, boot smoke
`GET /api/health 200`, clean shutdown |
| E2E families | **13 files / 109 tests PASS** — *after* the fix below;
**2 failed** before it |
| census | total **787**, triage **10** |

## Two corrections to the bar itself

**1. It is not "all-8 E2E" any more — there are 13 families.** The suite
grew while the bar was being written:

```
agent-count · agent-link · lease-rebound · lifecycle · merge-family · merge-rebound
merge-safeguards · merged-board · planner-lane · planner-lane-resolution
planning-lane · rebound-family · stranded-column
```

A verification pass scoped to 8 would have skipped 5 families —
including the one that was red. Worth fixing the number in the bar so
the final pass globs rather than counts.

**2. `DELIBERATE-LITERAL (reviewed)` reads 3, and I chased it —
RESOLVED, no gap.** I flagged the drop from an earlier "7" as a possible
fleet-safety hole. It is not one. Reconciled against `--json byFile`:

| File | markers | counted `deliberate` | counted `column` |
|---|---|---|---|
| `hold-release.ts` | 2 | **2** | **0** |
| `live-agent-count.ts` | 1 | **1** | 6 |
| `replan-target.ts` | 2 | 0 | 4 |

`deliberate: 3` = hold-release 2 + live-agent-count 1, which is exactly
the set of marker-covered **comparisons**. `replan-target.ts`'s two
markers sit above `return "triage"` **return-value** literals, not
comparisons — the census correctly does not count those as guards at
all, so they are neither `deliberate` nor `column`. The earlier "7" was
simply a different tree state before conversions landed; I was quoting a
stale number.

Worth noting the marker matcher is already hardened for the subtle case:
`hasDeliberateMarker` walks every **ancestor** rather than the enclosing
statement, because the real markers sit above the enclosing *function*
while the comparison is a `return` inside it — a statement-only lookup
"silently reclassified three reviewed literals as backlog". That is the
guard-cannot-fire pattern, already caught and fixed by whoever wrote the
AST version.

**Consequence for the fleet: the census's categories are trustworthy
as-is.** No pre-launch action needed on this.

## The red it found

`workflow-planning-lane-live-e2e.pg.test.ts` — **2 failed / 5 passed**,
including its own **control** case:

```
releases an ordinary held card on a default board (the control)
  → AssertionError: expected [] to include 'FN-OK'
```

`seedHeldTask` never wrote a `PROMPT.md`, so task creation's bootstrap
seed stood, and FN-7648's `isUnplannedForExecution` correctly refused to
release an unspecified card. **The sweep was right; the fixture was
asking it to release a card that had never been specified.**

**This is the second instance of the identical defect** — same cause and
same fix as `workflow-lifecycle-live-e2e`'s `seedTask` in #2634. This
suite was written after that fix and did not inherit it. The graph-entry
contract doc already states the rule: *"Scheduler/release test fixtures
must model a card that cleared the gate ... A held unreviewed card is
the gate working."*

Both failures had one cause — the mid-sweep approval-park case was
downstream of the control never releasing. **5 → 7 passed**, and cards
that are *supposed* to be held still are, held by their own
status/marker, which is what those cases assert.

Given it has now happened twice, a shared `seedPlannedTask` helper in
the E2E fixture module would prevent a third. I did not add one here: it
touches suites owned by U7 and U11 mid-consolidation, and this PR should
stay the verification pass plus its one finding.

## Bar status after this

- **gate / verify:fast / E2E** — green on one tree, with this commit.
- **triage → 0** — still **10**, all in U12's `moves.ts` (4) and
`register-task-workflow-routes.ts` (1) per file scan; flag resolution in
flight.
- **ratchet tightened (item 2)** — not done, U12's.
- Once triage hits 0 and the ratchet lands, re-running this exact pass
is a ~4-minute job and I can produce the final report.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 01:38:29 -07:00

4.7 KiB

Closing-bar verification — workflow-owned lifecycle

The programme's closing bar requires one verification pass on one tree: gate, verify:fast, the live E2E families, and the census. This file is the runbook and the record, so the pass is reproducible rather than re-derived each time.

How to run it

git checkout --detach origin/main            # one tree, no local commits
pnpm test:gate
pnpm verify:fast
# GLOB the E2E families — do not hard-code a count (see "Findings" below)
pnpm --filter @fusion/engine exec vitest run \
  $(cd packages/engine && ls src/__tests__/*live-e2e*.test.ts | tr '\n' ' ') --reporter=dot
node scripts/lifecycle-column-census.mjs

Recorded passes

2026-07-30, origin/main @ a6138abeff (latest)

Check Result
pnpm test:gate PASS
E2E families 14 files / 112 tests PASS, exit 0
census COLUMN 748 · ROLE 5 · STATUS 186 · DELIBERATE-LITERAL 17 · QUERY 83 · IR-node 43

2026-07-30, origin/main @ be63e72f1 (first pass)

Check Result
pnpm test:gate PASS — 132 + 10 + 487 + 71 tests
pnpm verify:fast PASS — 13 steps in 89.9s; boot smoke GET /api/health 200, clean shutdown
E2E families 13 files / 109 tests — 2 failed until the planning-lane seed fix (#2658)
census COLUMN 787 · ROLE 0 · STATUS 182 · DELIBERATE-LITERAL 3 · triage 10

Both numbers moved within a few hours, which is the point of recording the commit alongside the result: a bare count in an instruction is stale by the time it is read. The family count went 13 → 14 and the backlog 787 → 748 between these two passes, and the census grew two categories (QUERY filters, IR node definitions) that did not exist in the first run.

Findings

1. Glob the E2E families; never count them

The bar said "all-8 E2E". There were 13 at the time of this pass:

agent-count · agent-link · lease-rebound · lifecycle · merge-family · merge-rebound
merge-safeguards · merged-board · planner-lane · planner-lane-resolution
planning-lane · rebound-family · stranded-column

A pass scoped to eight would have skipped five — including the only one that was red. The suite grows as evidence lands, so the count in any instruction is stale on arrival. The command above globs for this reason.

2. A release-path E2E fails when its fixture never wrote a spec

Symptom: runHoldReleaseSweep(...).released comes back EMPTY, often for the suite's own control card.

Cause: task creation leaves a bootstrap-seed PROMPT.md (# <id>\n\n<description>), and FN-7648's isUnplannedForExecution reads that file for any card resting in an intake- or hold-trait column and refuses to move an unplanned card into a processing column. The sweep is correct; the fixture is asking it to release a card that was never specified.

This has now occurred twice in independently written suites — workflow-lifecycle-live-e2e (fixed in #2634) and workflow-planning-lane-live-e2e (fixed in #2658, and independently by a second worker, wasting the duplicate effort). The graph-entry contract doc already states the rule:

Scheduler/release test fixtures must model a card that cleared the gate ... A held unreviewed card is the gate working.

Any new suite that drives the release sweep must seed a planned PROMPT.md. A shared seedPlannedTask helper in _workflow-vocabulary-fixture.ts would end the pattern; it was not added while several consolidation branches still touch those files.

Diagnostic order that identifies this quickly, and two hypotheses it kills:

  1. sweep() returns held: [{ reason: "move-rejected-or-no-slot" }].
  2. Adding maxConcurrent/maxWorktrees to the settings changes nothing — not the in-transaction capacity gate.
  3. A direct store.moveTask(id, wip) succeeds — the move is not the blocker.
  4. isTaskBlockedOnApproval is false, isUnplannedForExecution is true.
  5. Read the seeded PROMPT.md: it is the stub.

3. DELIBERATE-LITERAL is internally consistent

deliberate: 3 reconciles exactly: hold-release.ts 2 + live-agent-count.ts 1. replan-target.ts carries two markers above return "triage" return-value literals, which the census correctly does not count as guards at all — so they appear in neither deliberate nor column. A lower number than a previously quoted one is not evidence of lost markers; check --json byFile before treating it as a gap.

hasDeliberateMarker walks every ancestor, not the enclosing statement, because real markers sit above the enclosing function while the comparison is a return inside it. A statement-only lookup silently reclassified three reviewed literals as backlog.