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>
This commit is contained in:
102
docs/workflow-owned-lifecycle-closing-verification.md
Normal file
102
docs/workflow-owned-lifecycle-closing-verification.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# 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
|
||||
|
||||
```bash
|
||||
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.
|
||||
Reference in New Issue
Block a user