Expand task update lane coverage across every direct and failure-isolated producer.
- Inventory direct and safe task:updated producer routes.
- Exercise warm and cold lane metadata through public producer operations.
- Cover lifecycle, workflow integrity, and completion update paths.
Files changed:
.../task-updated-lanes-emit-surfaces.test.ts | 308 +++++++++++++++++----
1 file changed, 256 insertions(+), 52 deletions(-)
Fusion-Task-Id: FN-8658
Fusion-Task-Lineage: 7d93593c-6f5a-4299-96f3-f4ee8f260e41
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Refresh canonical live-task claims at serialized scheduler, planning, and merge admission boundaries, including workflow-step leases and reservation handoffs. Back off capacity-denied merges safely across abort and restart lifecycles. Render queued planning cards in the header badge family with compact reason-specific icons.
Route durable planning and review continuations through shared project admission, retain reservations through execution, and prevent duplicate continuations from releasing another run's slot.
Quarantine the timing-sensitive Kimi K3 SDK catalog test without changing timeout budgets.
- Reuse the native model registry once per test file.
- Add the observed CI timeout to the dashboard quarantine ledger and config.
- Document validation and timeout-budget preservation requirements.
Files changed:
docs/testing.md | 8 ++++++++
...ister-model-routes-kimi-k3-supplemental.test.ts | 23 ++++++++++++++++++++--
packages/dashboard/vitest.config.ts | 8 ++++++++
scripts/lib/test-quarantine.json | 5 +++++
4 files changed, 42 insertions(+), 2 deletions(-)
Fusion-Task-Id: FN-8647
Fusion-Task-Lineage: 31e79677-d923-4003-a8e8-082159334e65
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
The active-worktree slot-accounting fix removed two deliberate scheduler
literals (done/archived: 3 -> 2); re-record so the ratchet follows the count
down.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Retained directories on queued, paused, blocked, or terminal tasks no longer
consume scheduler slots. Agent concurrency and worktree capacity now count the
same canonical live-task population through one project admission ceiling
(resolveActiveTaskCapacityLimit) with an atomic reserveIfAvailable claim, so
planning, execute, and merge lanes cannot each observe and claim the final
worktree slot independently.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The field was defined in persistence and serialization, the executor's Plan
Review replan-cap park wrote it, the triage manual gate null-cleared it, and
the dashboard special-cases it (isReviewBudgetExhaustedApproval badge + detail
explanation) — but updateTask's field-by-field merge never applied the key, so
every writer silently dropped it. FN-8647's 15-cycle non-converging Plan Review
loop therefore parked with a generic 'needs approval' and no hint it was a cap
escalation.
Merge contract, pinned by tests with a measured revert proof (3/4 fail
pre-fix): set persists, explicit null clears, a status write that leaves
awaiting-approval without addressing the reason auto-clears it so an approved
or replanned card cannot carry a stale escalation reason into its next park,
and unrelated updates leave it untouched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The right-padding bug recurred three times because 'Task Detail modal' names ONE
surface with THREE shells and fixes kept landing in the wrong file. Disambiguate:
- Rename the just-introduced .floating-window--tablet marker to
.floating-window--tablet-viewport and document the naming contract next to it:
--tablet-viewport = viewport MODE classifies tablet (touch or not, styling
surface); --touch-geometry = tablet AND touch (enlarged 44px targets only).
- Add SHELL NAMING MAP breadcrumbs at the top of TaskDetailModal.css and
TerminalModal.css pointing inset/padding fixes at the FloatingWindow shell
that tablet popups and floating terminals actually render through.
Comment wording deliberately avoids dot-prefixed class tokens because
FloatingWindow.test.tsx scans raw CSS (comments included) with selector regexes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Allow queued tasks to reuse worktrees they already hold when the durable worktree ledger is at or above capacity. Preserve independent agent and semaphore limits, and avoid releasing a worktree slot that a rejected transfer never acquired.
Third recurrence of the Task Detail right-padding bug (FN-8630/FN-8634): those
fixes only covered the .modal-overlay shells, while every tablet task popup and
floating terminal renders through FloatingWindow, whose shared body carries
FN-8015's margin-inline-end scrollbar gutter — a 16px right border with a 0px
left one. Tablet-mode windows (.floating-window--tablet, keyed on viewport MODE
so non-touch tablet widths match too) now zero the gutter; touch never grabs
scrollbar thumbs, so the desktop hot-zone conflict FN-8015 solves cannot occur.
GitHub-import's detail panel, which used the gutter as its right inset,
compensates locally. Desktop keeps the FN-8015 contract.
Also per operator request: the floating terminal is draggable from the empty
strip space behind the tabs and anywhere in the top toolbar, not only the
FN-8633 grip. Tab presses keep stopPropagation (scoped to .terminal-tab) so
they never start a window drag; the tablet floating header supersedes the
pan-x contract with touch-action: none (an overflowing strip is replaced by
the mobile-tabs dropdown, so no visible strip pans horizontally).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
## Census
**Before: `COLUMN guards (the backlog): 2`, `--strict` RED. After:
`BACKLOG ZERO`, all five gates green.**
Two commits from last night's `maxWorktrees` rollout copied the same
holder ledger, both with literals:
| commit | file | gate |
|---|---|---|
| `374956ef23` | `triage.ts` | planning admission |
| `6c7467a78d` | `executor.ts` | `fn_spawn_agent` |
```ts
t.column !== "done" && t.column !== "archived"
```
## What it costs
Both exclude terminal lanes because a finished card's worktree is
**cleanup-owned, not capacity**. On a renamed board neither literal
matches, so every finished card keeps counting as a live holder. The
count only grows, the gate reaches zero room on a board with free slots,
and planning admission is withheld forever / every spawn is refused.
That is the **mirror** of the breach these commits fixed, and strictly
worse: 8 planners on a 4-slot board is visible; a permanent stall is
silent. The recorded reason even names the worktree budget, which the
operator then checks and finds has room.
## The conversion
`resolveProjectColumnsForRoles(store, ["complete", "archived"])` —
project-level, because the ledger spans the whole board with no single
task to resolve against. Matches triage's existing use in
`sweepStalePlanningStatuses` and executor's at the wip gates.
Legacy-seeded, so a default board still excludes exactly `done` and
`archived` — byte-identical there.
## Both conversions were UNCOVERED when written
Measured with #3214's blinding procedure **before** writing tests:
reverting either to the literals left **all 19 tests in the capacity
suites green**. Nothing in the tree could tell the conversion from what
it replaced — which is how the literals got there in the first place.
Each now has a renamed-board case that fails when blinded:
```
triage converted 2 passed | BLINDED 1 failed | 1 passed | restored 2 passed
executor converted 8 passed | BLINDED 1 failed | 7 passed | restored 8 passed
```
## The pairing earned itself immediately
Both new cases assert an **absence** (no throttle / no refusal), so each
is paired with a positive proving the gate still fires on the same
renamed board when a card genuinely holds the last worktree.
That caught a real defect in my own fixture: the candidate scan resolves
each task's **own workflow selection**, not `listWorkflowDefinitions`,
so my first version fell back to the default board where `drafting`
isn't a hold lane. No card was eligible, nothing throttled, and the
absence assertion **passed for the wrong reason**. The positive failed
and exposed it. Recorded at the fixture so the next reader doesn't
reintroduce it.
## Verification
```
42 tests across 6 capacity suites pass
check-fnxc-future-dates green
check-inert-sync-lane-conversions green
check-lane-wiring green
check-sql-column-literals green
census --strict green (BACKLOG ZERO restored)
```
No changeset: internal engine fix, no published-package surface change.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Completes the family. `check-fnxc-future-dates` was #3287,
`lifecycle-column-census` is #3289, and this is the third and last gate
that rewrote its baseline during a plain check.
## Reproduction
```
inflated one allowance by 6, ran the gate with NO flags
rc=0
entry RESET to 1 ← the check modified the tree it was checking
```
## Why it matters
The tightening is right in substance — an allowance nobody spends is a
hole a literal can be regrown into. Performing it as a **side effect of
checking** handed every worker a byte-identical uncommitted diff they
had not authored, which they then reasonably committed.
Measured across the family: nine PRs chased three defects on
2026-07-31/08-01, two of them (#3283/#3285, five minutes apart, `+0/-1`
each) deleting the **same line neither author wrote**.
#3289 states the class best — *a check that writes turns every reader
into an author*. Two of us separately mis-attributed a gate-written
baseline to our own work while debugging something else.
## Measured, all four directions
| scenario | result |
|---|---|
| plain run, stale baseline | `rc=0`, reports `allowed 7, now 1`;
**inflation survived** — read-only |
| a new SQL literal added | **`rc=1`**, names `__sql_probe.ts` —
regression detection intact |
| `--update-baseline` | `rc=0`, entry written |
| clean tree, plain run | `rc=0`, **zero files dirty** |
Row 2 is the one worth checking: a read-only change to a gate is
worthless if it also stops catching the thing it exists for. The rise
path is untouched.
`census --strict` 0, `check-fnxc-future-dates` 0, eslint clean.
## Correcting my own delay
I measured this defect family on #3267 and then **declined to fix two of
the three**, reasoning that the census *"deliberately fails on a drop"*
so the port might be unsafe. That was wrong: it tightened and exited
`0`, exactly as its own test asserts — *"TIGHTENS on a drop and exits 0,
so somebody else's merge cannot redden the gate."* I had read the
`--exact` contract and attributed it to the default path.
The caution cost hours and prevented nothing. #3289 was written by
someone else in the meantime; this finishes what I should have finished
then.
## What
My blind-spot table in #3251 audited **one axis**. Adds the one that
missed three defects. Docs only.
That table records what each of the five lifecycle ratchets can and
cannot **see**. I probed that carefully — several spellings per tool —
and then wrote *"nothing found; sound"* for two of them.
Within a day, three of those same tools turned out to share a completely
different defect: **they wrote to the tree they were checking**,
auto-tightening their own baseline during a plain check run.
| gate | wrote during a check | fixed by |
|---|---|---|
| `check-fnxc-future-dates` | yes | #3287 |
| `lifecycle-column-census` | yes, under `--strict` | #3289 |
| `check-sql-column-literals` | yes | #3292 |
**No number of detection probes could have surfaced that.** The table
asserted one property carefully and said nothing about the other *while
reading as comprehensive* — which is precisely the failure it documents
in the tools it audits.
## The rule it adds
1. **What can it see?** — probe each spelling of the thing it claims to
catch.
2. **Can it fail at all?** — invoke it as `package.json` does; a
report-only run exits 0 forever (#3255).
3. **Does it write?** — `git status --porcelain` before and after, on a
clean tree.
With the trap on the third spelled out: these gates write only when a
tightening is **available**, so a clean tree after a run proves the
*trigger* is absent, not that the tool is read-only. Inflate a baseline
entry first, then run it. I hit exactly this while reviewing #3292 — ran
all three gates on main, saw a clean tree, and had to stop myself
concluding the SQL gate was fine.
## Why the pattern, not the people
Three tools converged on write-during-check independently. That argues
the design is **attractive**, not that three authors were careless: the
tightening is correct, the write saves a step, and the message even
tells you to commit it. It only becomes a defect at the moment a second
person runs the same gate — which is invisible from inside any one of
them.
What it cost, measured: #3283 and #3285 are the same `+0/-1`, five
minutes apart, by two authors, **neither of whom wrote that line**.
```
lint clean; fnxc-future-dates clean
```
Last loose thread from the 2026-07-31 stamp-repointing wave. Two comment
lines.
```
line 1027 ConcurrencyAdmission 2026-07-31-09:00 -> 2026-07-21-22:30 (eef5eb751e)
line 1471 WorkflowLifecycleColumns 2026-07-31-05:00 -> 2026-07-30-20:55 (109204c590)
```
## Not a revert — neither value was ever right
| stamp | originally | after #3280 | authoring commit (UTC) |
|---|---|---|---|
| `ConcurrencyAdmission` | `2026-08-06-09:00` (16 days ahead) |
`2026-07-31-09:00` (10 days late) | **2026-07-21 22:30** |
| `WorkflowLifecycleColumns` | `2026-08-01-05:00` (~1.5 days ahead) |
`2026-07-31-05:00` (~1 day late) | **2026-07-30 20:55** |
Both were written **ahead of their own commits** to begin with. Three
lanes then repointed stamps to turn `main` green (#3261, #3269, #3280),
moving the **date** back a day while keeping the clock time — which
converts an hours-off stamp into a days-off one in the opposite
direction. #3282 reverted the batch it owned; these two were outside its
scope.
So restoring the originals would be wrong too. The defensible values are
the authoring commits' UTC timestamps, per the `date -u` rule #3281
settled.
## Why now
`scheduler.ts` is a hot file. This survived two successive claimants — I
flagged it on #3262 and again on #3288 rather than opening a conflicting
PR, and said I'd take it once the file was unclaimed.
`check-file-claimed` now reports UNCLAIMED, so here it is.
## Scope
The gate is **green either way** — #3277 fixed the comparison, so
nothing is blocked by this. It is purely about the FNXC trail recording
when the work actually happened, which is the only reason the trail
exists. A stamp that satisfies a check while misstating the date by ten
days is worse than no stamp.
## Verification
```
check-fnxc-future-dates green
check-inert-sync-lane-conversions green
check-lane-wiring green
check-sql-column-literals green
census --strict green
```
Diff is two comment lines — no executable change. No changeset.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Extends the doc from #3255/#3273 with the failure that cost the most in
a single session: **one stale install produced five wrong reports on one
issue** (#3264).
## What happened
A `node_modules` that had drifted from the lockfile — `jsdom@29.0.1`
installed, `29.1.1` pinned — generated failures that existed on no CI
machine and no other checkout. They were not subtle: deterministic,
reproducible on demand, with plausible stack traces and real-looking
assertion diffs.
Each round of triage got **more precise about the wrong data**:
| round | claim | why it was wrong |
| --- | --- | --- |
| 1 | "4 deterministic failures" | measured in a 4-file batch, called it
isolation |
| 2 | "3 deterministic, 2 order-dependent" | isolated correctly, but a
race is not deterministic |
| 3 | "TaskCard is broken" | stale jsdom; the CSS assertion was correct
|
| 4 | "no contamination" | true of four app files; published unqualified
|
| 5 | "quarantine these two" | never read the failure text — both were
timeouts |
The through-line is not carelessness about the code. **The environment
was never treated as part of the claim**, so no amount of care about the
analysis could recover it.
## The checks, in the order they cost the most
```bash
pnpm install --frozen-lockfile # node_modules is not evidence until it matches the lockfile
<run the file ALONE, 3+ times> # isolation and repetition answer different questions
<read the failure TEXT> # a timeout and an assertion failure need opposite responses
uptime # a loaded box manufactures timeouts that mean nothing
```
## Why the load check earned its place
Two tests "failing" in a full-suite run were `Test timed out in 15000ms`
on a box at **load average 9.7 with 84 users**. Under AGENTS.md's
quarantine-on-sight rule that reads as a flake to quarantine — and the
ledger's **14-day deletion ratchet would have made the lost coverage
permanent**.
The rule presumes the failure is a property of the test, not of the
machine. A wall-clock budget crossed under local contention is evidence
about the hardware. I was one comment away from deleting healthy
coverage on that basis.
## The tell
A finding is environment-derived when it is **local, recent, and
unshared**: nobody else has reported it, CI is green, and it appeared
without a commit that could explain it. Any two of those should stop a
report before it is written. All three applied here, and the report went
out anyway — five times.
## Verification
Docs only; no code paths change. `fnxc-future-dates`,
`lifecycle-columns`, `quarantine-ledger` exit 0. No changeset — internal
docs are excluded.
Context: the one finding in #3264 that survived all five rounds is #3286
(merged), and it survived because it was verified by **reverting the
product change** rather than by trusting a red — 3/3/2 failures without
the fix, 27/27 across four runs with it.