Require bug-fix specs and reviews to enumerate affected surfaces and reject repro-only regression coverage. - add a required `## Surface Enumeration` section to triage prompt templates and bug-fix planning guidance - tighten reviewer guidance to block missing surface enumeration and repro-only regression tests - document the canonical surface checklist in `docs/testing.md` and cover the new wording with prompt/reviewer tests Files changed: AGENTS.md | 6 ++-- docs/testing.md | 9 +++++ packages/core/src/__tests__/agent-prompts.test.ts | 22 ++++++++++++ packages/core/src/agent-prompts.ts | 16 +++++++++ packages/engine/src/__tests__/reviewer.test.ts | 17 ++++++++++ packages/engine/src/__tests__/triage.test.ts | 41 ++++++++++++++++++++--- packages/engine/src/reviewer.ts | 3 ++ packages/engine/src/triage.ts | 24 +++++++++++++ 8 files changed, 131 insertions(+), 7 deletions(-) Fusion-Task-Id: FN-5928 Fusion-Task-Lineage: 717ddcbe-f3a6-4589-ad90-4e640f7a9ff2
10 KiB
Project Guidelines
Essential rules
Spec Generation Hygiene
- Do not cite
.fusion/tasks/<id>/<file>paths in Context/Steps/File Scope unless the file already exists, is explicitly created as a(new)Artifact, or is siblingPROMPT.md/task.json/attachments/*. - Dangling task-local file references are a blocking spec REVISE.
- Save planning scratch and interim notes via
fn_task_document_writeinstead of inventing on-disk task-local files.
External-integration evidence
Any task integrating a third-party tool (CLI, daemon, downloadable binary, installer-managed dependency) must cite, in PROMPT.md:
- Canonical upstream repo URL.
- Docs/homepage URL.
- Release/download URL.
- Binary/CLI name in backticks.
- Checksum or
upstream-pending-verificationmarker.
Missing evidence is a blocking REVISE. Never invent release URLs, binary names, or hashes.
Finalizing Changes
When a change affects published @runfusion/fusion, add a changeset (example: .changeset/<name>.md with "@runfusion/fusion": patch).
Bump types:
- patch — bug fixes/internal
- minor — new features/CLI/tools
- major — breaking changes
Do NOT create changesets for AGENTS.md/README/internal docs, CI config, or behavior-preserving refactors. @fusion/core, @fusion/dashboard, and @fusion/engine are private.
Releasing
Use only:
pnpm release --yes
scripts/release.mjs is the source of truth. Do not substitute with manual changeset version, pnpm publish, or git tags.
Package Structure
@fusion/core— domain model/task store (private)@fusion/dashboard— web UI + API server (private)@fusion/engine— triage/executor/reviewer/merger/scheduler (private)@runfusion/fusion— CLI + pi extension (published)
Only @runfusion/fusion is published; @fusion/* packages are bundled into it.
Importing across @fusion/* packages
@fusion/* imports must be statically analyzable. Anti-pattern:
const engineModule = "@fusion/engine";
const engine = await import(/* @vite-ignore */ engineModule);
Rules:
- Default to static imports.
@fusion/coreuses DI (setCreateFnAgent) instead of dynamicimport("@fusion/engine")due to circularity.- Never reintroduce the
engineModule = "@fusion/engine"trick. vi.mock("@fusion/engine", ...)remains valid.
Testing commands
Tests are required. Typechecks/manual checks are not substitutes.
pnpm test
pnpm test:full
pnpm lint
pnpm build
pnpm verify:workspace
Standing Rule: Do Not Add Slow Tests (FN-5048)
- Prefer narrow seams, in-memory fakes, shared harnesses, and targeted assertions.
- Prefer fake timers over real polling/time waits.
- Do not mask slowness by raising worker/concurrency knobs.
- Do not add new real-network calls, real polling loops, or mock-the-world shells when a narrower seam exists.
- Use the testing taxonomy in
docs/testing.mdwhen deciding trim vs keep.
Standing Rule: Fix the Invariant, Not the Repro (FN-5893)
- When fixing a bug, the regression test must assert the general invariant across ALL known surfaces — not only the single reported reproduction.
- Surface enumeration is now an enforced bug-fix artifact: the spec must include a
## Surface Enumerationsection, planning must REVISE when that section is missing, and review must REVISE any repro-only regression test. - Enumerate the surfaces before filing or closing the fix: every provider/bridge for streaming and agent paths, both desktop and mobile breakpoints for UI behavior, empty/undefined/duplicate/populated data states, and every shared hook/component/module/helper that reuses the affected logic.
- Use the canonical checklist in
docs/testing.md→ Surface Enumeration checklist so planning and review enumerate the same surfaces. - Motivating incidents: streamed-response spacing was fixed three times before the invariant was fully covered (FN-5787, FN-5789, FN-5803), the usage "Show hidden" button regressed three times before broader coverage stuck (FN-5797, FN-5875, FN-5919), and the auto-merge blank-dashboard fix re-opened after desktop-only coverage missed mobile Android (FN-5751).
- If a regression test only proves the exact reported case, it is incomplete; extend it until the invariant holds across all known surfaces.
Port 4040 is Reserved
Never kill processes on port 4040 and never start test servers on 4040. Use --port 0 or another free port.
Engine Process Rules
Never use execSync for user-configured commands
Run user-configured commands (test/build/workflow scripts) via async exec with timeout. execSync is only acceptable for short deterministic git plumbing.
Move-Task contract
User moveTask(in-progress → todo) is a hard cancel: abort active sessions/subprocesses and park task in todo with user-paused semantics. Engine rebounds must not set userPaused.
Process supervision
Use superviseSpawn(...) from @fusion/core for managed child processes; do not use raw detached spawn/nohup patterns unless explicitly allowlisted. eslint.config.mjs + scripts/check-no-nohup.mjs enforce this.
Git Conventions
- Commit prefixes:
feat(FN-XXX):,fix(FN-XXX):,test(FN-XXX): - One commit per step boundary
- Include task ID prefix
- Fusion task-worktree commits should carry
Fusion-Task-Id: FN-NNNNtrailers
Merging Branches Into Main
- Drop duplicate commits before merging. Rebase away duplicates already on main.
- Squash is now the project default; history-preserving merge paths require opt-in. New projects default
directMergeCommitStrategy="always-squash". To preserve multi-commit history, explicitly set projectdirectMergeCommitStrategyto"auto"or"always-rebase", or set a per-task**Direct Merge Commit Strategy:** ...override inPROMPT.md. - Empty cherry-picks are no-ops. Do not create empty commits.
- Already-on-main classifier applies. Allow finalize/self-healing recovery when lineage is landed.
- Contamination auto-recovery is bounded. First pass can auto-drop upstream foreign commits; repeated/ambiguous cases escalate.
- Run post-squash audit policy. Respect
postMergeAuditMode(warn/block/off) and auto-recovery stages. - Enforce pre-commit diff-volume gate. Block suspicious shrinkage before squash commit.
- Smart-prefer-main overlap guard. Recent overlapping main commits can flip to prefer-branch.
- Layer-3 scope partition. Out-of-scope conflicts resolve to main before AI arbitration unless
task.scopeOverride=true. - Auto-prerebase on divergence/hot files. Fail-soft and continue normal conflict stack.
Gitignored-path guard on squash merges
Never force-add ignored artifacts (for example git add -f .fusion/...). Use task documents for findings/notes.
File-Scope invariant on squash merges
Every squash commit must overlap task ## File Scope (unless scope is empty). Violations must fail with FileScopeViolationError and reset pre-squash state.
Per-task opt-out exists: task.scopeOverride = true (log the reason).
autoMerge: false callout (FN-5147)
When settings.autoMerge: false, in-review is terminal-until-merged by a human. Lifecycle-mutating self-healing must not move these tasks backward, pause/fail them, or re-enqueue them for execution.
Scoped exception (FN-5819): shared-branch-group members (branchContext.assignmentMode === "shared") still run the member→shared-branch local integration step while auto-merge is off. This exception is only for assembling branch_groups.branchName; shared-branch → default-branch promotion remains gated by group/global auto-merge.
Mock provider (test mode)
testMode?: boolean is now available in both project and global settings. If project testMode === true (or the resolved default provider is "mock" at any tier), every AI lane is forced to mock/scripted, overriding per-task and per-lane model selections. The dashboard exposes this via the Settings Modal "Enable test mode" toggle and a persistent "Test mode — no real AI calls" banner.
Run Audit
- FN-5419: git run-audit now includes
pull:fast-forwardandstash:pop-conflict; dashboard git surfaces now include the extendedPOST /api/git/pullintegration-worktree path plus companionPOST /api/git/stash-resolve,POST /api/git/stash-drop, andPOST /api/git/stash-applyroutes.
Reference docs (deeper detail)
./docs/architecture.md— lifecycle invariants, self-healing rules, reliability interaction backstops, run-audit internals../docs/testing.md— full testing lanes, worker fanout guidance, test taxonomy, and file organization../docs/dashboard-guide.md— dashboard behavior and Styling Guide details. User-facing docs for Merge Advance Notice and Smart Pull live here../docs/agents.md— pi extension scope, coordination tools, checkout leasing, runtime config../docs/settings-reference.md— model-selection hierarchy, mock provider mode, token budget precedence, presets../docs/storage.md— hybrid storage model details../docs/multi-project.md— central/per-project DB and isolation modes../docs/missions.md— mission/milestone/slice/feature model../docs/workflow-steps.md— prompt/script gates and merge-blocking behavior../docs/secrets.md— secrets policy and tooling behavior../docs/diagnostics.md— engine diagnostic logging conventions../docs/task-management.md— archive cleanup and restore semantics../docs/soft-delete-verification-matrix.md— mandatory soft-delete verification matrix../docs/cli-reference.md— CLI and terminal UI reference../docs/contributing.md— contributing conventions and release-adjacent context.
Lazy-Loaded Heavy Views
These 19 views are lazy-loaded via React.lazy() with <Suspense fallback={null}>.
Keep this AGENTS inventory in sync with App lazy imports and packages/dashboard/app/__tests__/lazy-loaded-views-docs.test.ts.
AgentsViewNodesViewChatViewMemoryViewDevServerViewSecretsViewInsightsViewDocumentsViewSkillsViewResearchViewReliabilityViewEvalsViewTodoViewGoalsViewStashRecoveryViewSetupWizardModalPluginManagerPiExtensionsManagerAgentDetailView