Stabilize live Proceed-action handoffs and re-admit the Planning Mode flow suite.
- Settle hydration and re-query the Proceed action before direct-create test clicks.
- Remove the Planning Mode test quarantine and record its rescue in the testing ledger.
Files changed:
.../suite-only-flakes-observed-register.md | 4 ++++
docs/testing.md | 3 +++
.../PlanningModeModal.planning-flow.test.tsx | 20 ++++++++++++++++----
packages/dashboard/vitest.config.ts | 5 -----
scripts/lib/test-quarantine.json | 5 -----
5 files changed, 23 insertions(+), 14 deletions(-)
Fusion-Task-Id: FN-8936
Fusion-Task-Lineage: ed869b67-9394-458b-879c-54da0d7d327e
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Document the decision to preserve Kimi K3 quarantine coverage until its deletion-ratchet deadline.
- Retain the supplemental route test, Vitest exclusion, and ledger entry through 2026-08-15.
- Clarify that early removal requires regression evidence and a root-cause fix.
Files changed:
docs/testing.md | 3 +++
1 file changed, 3 insertions(+)
Fusion-Task-Id: FN-8788
Fusion-Task-Lineage: e1644a4b-1632-4cf7-a0a9-42ecc2b027a9
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
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>
Earned the hard way this week: three separate causes in `App.test.tsx`
and `board-mobile-view-switch.test.tsx` all presented **identically** as
a missing element, each with a DOM that looked healthy.
| what the test said | what was actually wrong |
|---|---|
| `Unable to find "+ New Task"` | `ListView` rendered its workflow
**skeleton**, which carries the same `list-view` class as the real body
— so the preceding `waitFor(".list-view")` passed |
| `Unable to find role="heading" "New Task"` | `NewTaskModal` **threw**
— an incomplete `vi.mock` was missing `isShortViewport` — and an
`ErrorBoundary` swallowed it |
| `Unable to find [data-testid="switch-to-board"]` | an uncaught render
error **unmounted the entire React root**; the DOM was already empty
three lines earlier |
The part worth recording is the hit rate. **Three theories were offered
before any probe — "the board renders nothing", "i18n is returning
keys", "the FloatingWindow rework" — and all three were wrong.** Three
probes each landed the cause on the first try. Those wrong theories cost
days; the probe is four lines.
The doc records the snippet, what each signal means (`error-boundary` in
the DOM = a swallowed throw, and its text names a missing mock export
exactly; empty DOM with no boundary = unmounted root, so trust the
*first* failing assertion not the reported one; container present but
contents absent = a skeleton standing in), and the corollary for writing
assertions:
> Wait on a marker only the real thing has.
A class shared with a loading or empty state turns *"the list rendered"*
into *"something rendered"*, and the test then fails one line later
against a DOM that looks fine. That is why `list-view-body` exists
(#2834).
Placed under the dashboard testing sections in `docs/testing.md`. Docs
only — no changeset.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Taken after asking twice for reassignment with no reply, and after the
same failure bit a **third** time. No open PR touches the census CLI, so
this is unowned in practice — **U12, say so if you have started and I
will close this in favour of yours.**
## What changed
A **drop** now tightens the baseline instead of failing. Failing hard
was defensible in isolation — a stale allowance is a hole, since those
guards can return up to the old count while the check stays green. What
it missed:
**The drop is almost never the failing author's to fix.** Eleven files
dropped during one merge wave, none of those PRs re-recorded, and none
of their authors did anything wrong. Measured three times since CI began
gating this: `columnRoles.ts` 0 → 1, then `executor.ts` twice.
A permanently-red gate is a bigger hole than a stale allowance, because
it gets ignored and then nothing is guarded at all. **The rise check —
the ratchet's actual purpose — is untouched and still fails hard.**
## The residual, named rather than glossed
In CI the write is discarded with the runner, so the committed baseline
stays stale until someone commits a tightened one. The exposure is
bounded (regrowth only up to the old count), printed on every run, and
strictly smaller than the exposure from a check people route around.
`--strict --exact` restores hard failure for the pinned end state.
**One writer:** the write is now a named `writeBaseline()` shared by the
tighten path and `--update-baseline`, rather than a second
`writeFileSync`. Two writers for one artifact is how they drift — a
lesson this file already learned once.
## Exercised end to end
| scenario | result |
|---|---|
| drop, `--strict` | exit **0**, `TIGHTENED`, allowance rewritten 9 → 6
|
| drop, `--strict --exact` | exit **1**, baseline untouched |
| rise, `--strict` | exit **1** |
| clean | exit **0** |
Pinned through the real CLI with an isolated baseline. Revert proof:
restoring the hard failure fails **1 of 32**.
## Two of my own mistakes, recorded
**A vacuous assertion, in the case that guards against vacuity.** I
first wrote `expect(allowedAfter).toBeLessThan(4 + allowedAfter)` — true
for every number. Replaced with a comparison against the inflated value
the fixture started from. This file documents that trap repeatedly and I
still walked into it, which is the argument for the mechanical revert
check over careful reading.
**The env override is `FUSION_CENSUS_BASELINE_PATH`**, not the
`FUSION_CENSUS_BASELINE` I used in the first draft — so the first
version of these cases silently ran against the **real** baseline and
passed for the wrong reason. A test whose fixture never took effect is
the same failure as a test whose fixture can't fail.
## Verification
32/32 census suites, `pnpm test:gate` **71/71**, `--strict` exits 0,
`pnpm lint` clean, `docs/testing.md` updated.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---
## Update — the base-ref ratchet (review round 2, commit `4895845579`)
The first version of this PR shipped a **named residual**: the
tightening write dies with the CI runner, so the committed allowance
stays high and a later PR can regrow guards up to it while `--strict`
prints green. I called the exposure bounded and moved on. Greptile
flagged it P1 and was right — naming a hole is not closing one.
`--strict` now stops trusting the committed number for files the branch
touched. It measures each **changed** file at the base commit
(`FUSION_CENSUS_BASE_REF`, else the PR base branch, else `origin/main`)
and fails if the file carries more guards than the base ref has. **The
enforced ceiling is what main has today**, so a stale, missing, or
long-unrecorded baseline no longer opens a window.
| decision | why |
|---|---|
| changed files only, `<ref>...HEAD` | untouched files have main's
counts by construction; censusing all ~400 at the base ref is ~400 `git
show` calls to re-derive numbers that cannot have moved. Three-dot also
stops charging this branch for guards that landed on main after the
fork. |
| a new file's base allowance is **0** | "absent at the base ref" as
unbounded would make a new file the cheapest place to hide a fresh guard
|
| fails **open** on an unresolvable ref, printing `SKIPPED` | a shallow
clone cannot produce an honest comparison; a degraded run must not read
as a clean one. The baseline comparison still applies. |
| merged into the existing `regressions` list | one failure per file,
and `--update-baseline` keeps working as the deliberate escape hatch. No
new exit path. |
**Revert proof, measured both ways.** With the base-ref block removed,
the regrowth fixture — base commit 2 guards, HEAD 5, baseline allowing 9
— exits **0** with `TIGHTENED`, which is precisely the reported
scenario. With it: exit **1**, `column-guard count ROSE`, `above its
count on the base ref`, baseline left at 9. **3 of the 4** end-to-end
cases go red on revert. The fourth passes without the fix by design — it
is the genuine-conversion case the auto-tighten exists to keep green,
and a case that reddens either way proves nothing.
The end-to-end suite builds a throwaway two-commit `git init` repo under
the temp dir, because this exploit is a property of the **plumbing**,
not of the comparison: resolving a ref, working out the changed set,
reading base source through `git show`. The comparator itself is pure
with the reader injected (`findRegrowthAgainstBase`), with its own cases
in `lifecycle-column-census-ast.test.ts` — including the one that would
silently pass everything, looking up the wrong key in
`summarize().byFile`.
**Rebased onto `origin/main` @ bc782d8d92** (the branch was forked
before the recent merge wave; its baseline read 746 against a tree of
722).
Verification on the rebased branch: census **722** / `--strict` exit 0 ·
**70/70** across both census suites · `pnpm test:gate` **71/71** · `pnpm
lint` clean.
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The program's completion bar is "`column === "triage"` reaches zero".
This measures what that bar actually covers, and checks the measurement
in so it cannot drift.
## The number, measured by the checked-in tool
```
lifecycle-column-census: scanned 1956 source files
COLUMN guards (the backlog): 1031
ROLE comparisons (not guards): 10
DELIBERATE-LITERAL (reviewed): 4
by column id:
313 done
217 in-review
201 in-progress
177 archived
83 todo
40 triage
top files:
151 packages/engine/src/executor.ts
136 packages/engine/src/self-healing.ts
50 packages/dashboard/app/components/TaskCard.tsx
44 packages/core/src/task-store/moves.ts
34 packages/dashboard/app/components/TaskDetailModal.tsx
```
**`triage` is under 4% of the class.** Every one of those 1031 sites is
the same defect: a lifecycle decision made by column NAME, which stops
matching the moment a board renames a column. The bar can be met in full
while 991 identical guards remain — and two files hold a quarter of
them.
## The tracked count is wrong in three directions at once
Each of these cost real work this week, which is why this is a PR and
not a comment.
1. **Vocabulary.** It measures one of six legacy ids.
2. **Receiver.** It is anchored on locals named
`column`/`toColumn`/`fromColumn`, so it never saw the three real guards
in `executor.ts` written against `from` and `originColumn`. One of those
meant completed-but-stranded work was never recovered on a renamed
board, with nothing else owning that state (converted in #2628).
3. **Collision.** `role === "triage"`, `agentType === "triage"`,
`entry.agent === "triage"` compare an **AGENT ROLE**. The planner *lane*
is named `triage` and keeps that name — U11 removed the *column*. Ten
such sites were counted as backlog, and the "obvious" fix (renaming the
role) silently empties the planner's prompt template and mis-binds its
model markers.
A count that is too high and too low simultaneously sends work to the
wrong files while hiding the files that need it. So the census reports
**three separate numbers** and never nets them.
## Proven to fail on the original defect
Not asserted — exercised:
```
$ # reintroduce `task.column === "triage" || task.column === "todo"` into live-agent-count.ts
$ node scripts/lifecycle-column-census.mjs --strict; echo "exit=$?"
packages/core/src/live-agent-count.ts: 10 -> 12
exit=1
$ # restore the file
$ node scripts/lifecycle-column-census.mjs --strict >/dev/null; echo "exit=$?"
exit=0
```
The CLI also exits 1 when its own file list comes back empty — a guard
that reports success without checking anything is worse than no guard.
## 12 regression cases, split by what they defend
Must catch: all six ids; a guard on a local named `from`/`originColumn`
(verbatim the executor.ts shape); single quotes; negation; several
comparisons on one line.
Must **not** catch: role comparisons; comment prose (two tracked
"guards" in `replan-target.ts` were prose about a filter that lives in
another file); a trailing `// … === "triage"` on a code line; sites
carrying a `DELIBERATE-LITERAL` marker.
Plus: **one marker cannot launder a distant guard in the same file** —
that is how allowlists rot.
## Report-only, deliberately
`--strict` compares per-file counts against
`scripts/lib/lifecycle-column-census-baseline.json` and fails when any
file's count **rises**. It is **not** wired into the merge gate: a
thousand-site backlog cannot be a blocking check the day it is first
measured, and a guard nobody can pass is a guard everyone disables.
Owners tightening their own area re-record the baseline in the PR that
lowers it. This is the ratchet shape the `DELIBERATE-LITERAL` markers
scattered through the program already anticipate.
## Stated limitation
Classification is by receiver **name**, so a future field named `agent`
that holds a column would be misclassified as a role comparison.
Recorded at the site, and it is precisely why the two classes are
reported separately instead of netted into one figure.
## Verification
- 12/12 new cases
(`packages/engine/src/__tests__/lifecycle-column-census.test.ts`)
- `pnpm test:gate` **71/71**; `pnpm lint` clean
- `pnpm census:lifecycle-columns`, `--json`, and `--strict` all
exercised end to end
- documented in `docs/testing.md`; no production code touched
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Refresh the weekly test velocity publication with the latest measurements.
- Record updated gate, boot smoke, and changed-only test timings.
- Append the W29 velocity history entry and update the published summary.
- Point testing guidance to the canonical weekly velocity workflow.
Files changed:
docs/test-velocity-baseline.md | 16 +++---
docs/testing.md | 10 +---
scripts/test-velocity-history.json | 115 +++++++++++++++++++++++++++++++++++++
3 files changed, 124 insertions(+), 17 deletions(-)
Fusion-Task-Id: FN-8495
Fusion-Task-Lineage: db432471-13a2-41b1-a951-f735c96456e9
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Add a report-only script that surfaces flaky-test quarantine entries approaching their 14-day deletion clock, so maintainers can make deliberate rescue-or-expire decisions before entries silently expire.
- Add scripts/check-quarantine-ledger.mjs: reads scripts/lib/test-quarantine.json, computes days-remaining against the existing 14-day deletion clock (shared DELETION_CLOCK_DAYS from scripts/test-velocity-baseline.mjs), and buckets each entry as expired/near/healthy/unknown
- Support --warn-within=<days> (default 5) to tune the near-deadline window, --json for machine-readable output, and --strict as an opt-in local/CI gate (exits 1 on expired/near entries) while default mode stays exit-0 and non-blocking
- Wire pnpm check:quarantine-ledger script in package.json
- Add scripts/__tests__/check-quarantine-ledger.test.mjs covering deadline bucketing/sorting, empty/missing ledger handling, --strict behavior, and --json output shape
- Document the new command and its flags in docs/testing.md under the quarantine ledger/deletion ratchet section
Files changed:
docs/testing.md | 10 +
package.json | 1 +
scripts/__tests__/check-quarantine-ledger.test.mjs | 159 ++++++++++++++++
scripts/check-quarantine-ledger.mjs | 202 +++++++++++++++++++++
4 files changed, 372 insertions(+)
Fusion-Task-Id: FN-7912
Fusion-Task-Lineage: c08e2e09-473a-4ad0-8c27-43cbc3355168
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Prototype and land a rebuilt-every-run esbuild bundle of the @fusion/core gate-safe barrel closure, collapsing the engine-core gate's per-fork Vite SSR import-phase cost (18 forks x ~430-file closure re-resolved from scratch) into a single file load per fork.
- Add scripts/build-engine-core-gate-bundle.mjs: esbuild-bundles packages/core/src/index.gate.ts (220 first-party files, packages:"external" so third-party/node: imports stay external, treeShaking:false to preserve side effects) into packages/core/.gate-bundle/core.mjs + core.meta.json
- Wire the builder into packages/engine/vitest.config.ts's engine-core project globalSetup (alongside the existing vitest-teardown hook) so the bundle is rebuilt fresh before every gate invocation, and repoint the @fusion/core resolve.alias at the bundled output instead of index.gate.ts source
- Place the bundle output at packages/core/.gate-bundle/ as a sibling of packages/core/node_modules/ (not nested inside it) to avoid Vite SSR's external-dep heuristic, which would otherwise silently defeat vi.mock interception for imports nested in the bundle
- Gitignore packages/core/.gate-bundle/ and add a matching ESLint ignore entry so the generated bundle text is never linted or committed
- Add esbuild ^0.25.12 as a root devDependency (pnpm-lock.yaml updated accordingly)
- Document the pre-bundling rationale, placement constraints, and measured A/B wall-time results in docs/testing.md
Verified: pnpm test:gate passes (335/335 engine-core tests, 63/63 CLI ci-shape tests), engine package typecheck clean, eslint clean on touched files.
Files changed:
.gitignore | 11 ++
docs/testing.md | 3 +
eslint.config.mjs | 10 ++
package.json | 1 +
packages/engine/vitest.config.ts | 50 ++++++++-
pnpm-lock.yaml | 3 +
scripts/build-engine-core-gate-bundle.mjs | 174 ++++++++++++++++++++++++++++++
7 files changed, 247 insertions(+), 5 deletions(-)
Fusion-Task-Id: FN-7669
Fusion-Task-Lineage: 62b06b2a-4ac6-45ae-ac79-9771132bc303
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Introduces a project-scoped @fusion/core barrel used only by the engine-core
gate project, so new feature modules added to the full barrel don't silently
inflate the gate's transform/import cost.
- Add packages/core/src/index.gate.ts, a copy of the full @fusion/core barrel
minus export statements for modules added since the last re-audit baseline
(i.e. it still re-exports everything the full barrel does except newly
added, gate-irrelevant feature modules).
- Update packages/engine/vitest.config.ts to add a project-scoped
resolve.alias mapping @fusion/core -> packages/core/src/index.gate.ts for
the engine-core project only; engine-default/engine-reliability/engine-slow
and @fusion/engine continue to resolve the full barrel.
- Document the gate-safe barrel and its audit procedure in docs/testing.md.
Files changed:
docs/testing.md | 3 +
packages/core/src/index.gate.ts | 2102 ++++++++++++++++++++++++++++++++++++++
packages/engine/vitest.config.ts | 17 +
3 files changed, 2122 insertions(+)
Fusion-Task-Id: FN-7667
Fusion-Task-Lineage: 054ec89a-d973-44dd-b9ac-ad266f553f01
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Add a focused engine regression guard for user-configured command paths.
- Add a static Vitest registry that slices protected configured-command helpers and fails on execSync usage.
- Assert each protected command path keeps bounded async safeguards such as timeout, maxBuffer, or maxLifetimeMs.
- Document the guard and its deliberate git-plumbing exclusions in the testing guide.
Files changed:
docs/testing.md | 5 +
.../user-configured-command-no-execsync.test.ts | 288 +++++++++++++++++++++
2 files changed, 293 insertions(+)
Fusion-Task-Id: FN-7056
Fusion-Task-Lineage: b92f19cf-526e-4345-b18e-869a278e0e10
`pnpm test`'s changed-affected lane ran `vitest run --changed <base>` for the
heavy packages (@fusion/engine, @fusion/dashboard). `vitest --changed` does
unbounded transitive module-graph expansion: one changed hub source file
selects ~8,393 test entries (79s just to list), which at the OOM-pinned
workers=1 exceeds the engine's 15-min VERIFICATION_TIMEOUT_WORKSPACE_MS. The
engine SIGKILLs and restarts the task, producing the observed loop of nine
15-min verification timeouts (~2.8h) on a single task.
Guard the lane with a git-only predictor: when a heavy package has changed
non-test source in its graph, run only the directly-changed test files;
when no test files changed, delegate cross-cutting coverage to the merge gate
(already run in changed mode). Test-only diffs keep normal --changed.
Mirrors the existing reverse-dependent blast cap one level down. No widened
timeouts, retries, or worker bumps. test:full remains the explicit full sweep.
Bounded engine run: 2.79s vs 79s. Regression suite 111/111.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## What this adds
A **test-free verification command** — `pnpm verify:fast`
(`scripts/verify-fast.mjs`) — that gives deterministic, flake-free
signal without running the test suite. It is fully **additive**: `pnpm
test`, the merge gate (`test:gate`), and CI are untouched.
`docs/testing.md` observes the broad test gate "caught no recalled real
bugs while consuming ~70% of shipping time in flake triage."
`verify:fast` is the opt-in path for non-test verification, suitable as
a project `testCommand`/verification command.
## What verify:fast runs
1. **typecheck — scoped to the changed packages** (each package's
`typecheck` script, or `pnpm --filter <pkg> exec tsc --noEmit -p .` when
none exists).
2. **build — scoped to the changed packages** (`pnpm --filter <pkg>
build`, only for packages that declare a build script).
3. **boot smoke once** (`scripts/boot-smoke.mjs`: CLI `--help` + a real
`fn serve` answering `GET /api/health`), after builds so it runs against
fresh artifacts.
Change-detection **reuses `scripts/test-changed.mjs`** (`getBaseBranch`
/ `detectComparisonBase` / `changedFilesSince` /
`resolveAffectedPackages` / workspace resolution — newly `export`ed)
instead of reinventing git-diff, so it scopes to exactly the packages a
changed-only test run would. With no affected package (root/docs-only
diff) it runs the boot smoke only. Each step is bounded by the existing
`runWithWatchdog` (class `changed`) so a hung tsc/build/serve fails
fast; it streams progress and exits nonzero on the first failing step.
`@fusion/desktop` and `@fusion/mobile` are skipped, mirroring the root
`build`/`typecheck` exclusions.
## Measured wall-time
On this branch's diff (which resolves to the heaviest package,
`@fusion/dashboard`), end-to-end:
```
[verify:fast] plan: typecheck:@fusion/dashboard -> build:@fusion/dashboard -> boot-smoke
[verify:fast] OK typecheck @fusion/dashboard (~44s)
[verify:fast] OK build @fusion/dashboard (26.2s)
[verify:fast] OK boot smoke (CLI --help + real serve /api/health) (19.6s)
[verify:fast] PASS — 3 step(s) green in 90.3s (no tests run).
```
**~90s total**, deterministic and flake-free. By contrast a typical
**scoped test run for the same package** is far heavier and flake-prone:
`docs/testing.md` notes a dashboard task "otherwise re-ran all 822
dashboard test files (~5-8 min)", and `pnpm test` additionally runs the
merge-gate suite first. verify:fast trades that test-suite cost (and its
flake-triage tax) for a typecheck+build+boot signal in ~1.5 min.
## Doc additions
- `AGENTS.md` + `docs/testing.md` testing-commands lists now include
`pnpm verify:fast`, described as the recommended **test-free
verification** (typecheck + build + boot-smoke), suitable as a project
`testCommand`/verification command; the full suite stays available and
runs non-blocking.
## Tests / verification
- New `scripts/__tests__/verify-fast.test.mjs` (11 tests) pins the pure
planning / arg-construction logic — scoped typecheck/build selection,
build-script gating, desktop/mobile exclusion, boot-smoke-only fallback,
and reuse of `resolveAffectedPackages`. It never spawns real
tsc/build/vitest.
- `pnpm verify:fast` runs end-to-end and exits 0 (output above).
- Lint clean on all new/changed files; `agents-md-invariants`,
`check-test-inventory`, `verify-fast`, and `test-changed` script tests
all green (132 tests).
No changeset (scripts + docs + CI-tooling, behavior-additive;
`@runfusion/fusion` runtime unaffected).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- stage-review-badge-begin -->
---
<a href="https://stagereview.app/Runfusion/Fusion/pull/1777">
<picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://stagereview.app/assets/gh-open-in-stage-dark.svg">
<img src="https://stagereview.app/assets/gh-open-in-stage-light.svg"
alt="Open in Stage">
</picture>
</a>
<!-- stage-review-badge-end -->
Adds scripts/verify-fast.mjs + root `pnpm verify:fast`, an opt-in, flake-free
verification path that runs typecheck + build scoped to the changed packages
(reusing test-changed.mjs git-diff / changed-package resolution) plus the
existing boot smoke once, with no test suite. Each step is bounded by the
shared runWithWatchdog (class "changed"); exits nonzero on the first failure.
No default changed: pnpm test, the merge gate, and CI are untouched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Strengthen the fn_run_verification allowFullSuite parameter description, add an
AGENTS.md standing rule, and update docs/testing.md so agents default to a
file-scoped verification command and reserve allowFullSuite for genuinely full
runs with no targetable test set. allowFullSuite is the main way verification
balloons past its budget; the thin merge gate is the cross-cutting safety net.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Remove the orphaned session-cross-tab quarantine ledger entry after confirming the test remains active and rescued.
- Document FN-6937's rescue proof and ledger/config lockstep expectation in testing guidance.
- Keep the dashboard Vitest quarantine list empty with a note explaining the stale ledger-only cleanup.
- Remove the session-cross-tab row from the flaky-test quarantine ledger.
Files changed:
docs/testing.md | 8 ++++++--
packages/dashboard/vitest.config.ts | 3 +++
scripts/lib/test-quarantine.json | 8 +-------
3 files changed, 10 insertions(+), 9 deletions(-)
Fusion-Task-Id: FN-6937
Fusion-Task-Lineage: 34a67bf3-2a7f-49ea-93ab-68fadc81f893