Files
fusion/docs/architecture.md
Fusion 56a407b52c feat(FN-3013): add durable insight lifecycle with bounded run executor
Adds durable insight lifecycle management to the Fusion system, including a bounded insight run executor, storage contracts for insight and research data, and API integration through the insights routes. Legacy schema compatibility is stabilized, and lifecycle safeguards are documented in the archit

Fusion-Task-Id: FN-3013
2026-05-03 02:36:07 -07:00

922 lines
53 KiB
Markdown

# Fusion Architecture
[← Docs index](./README.md)
This document describes the actual architecture of Fusion as implemented in this repository (`gsxdsm/fusion`). It is intended as a practical onboarding map for developers and AI agents.
---
## 1) Overview
Fusion is an AI-orchestrated task board. It takes tasks through a structured lifecycle (`planning → todo → in-progress → in-review → done → archived`) and automates planning, execution, review, merge, and operational recovery.
At a high level, Fusion is split into:
- **Core domain + persistence** (`@fusion/core`)
- **Execution engine** (`@fusion/engine`)
- **Dashboard API + SPA** (`@fusion/dashboard`)
- **CLI + Pi extension** (`@runfusion/fusion`)
- **Desktop shell** (`@fusion/desktop`)
- **Terminal dashboard** (part of `@runfusion/fusion` — see `packages/cli/src/commands/dashboard-tui/`)
### High-level runtime diagram
```text
┌──────────────────────────────┐
│ Human + AI Interactions │
│ (Dashboard SPA, CLI, Pi) │
└──────────────┬───────────────┘
┌──────────────────────┼──────────────────────┐
│ │ │
┌─────────▼─────────┐ ┌─────────▼─────────┐ ┌─────────▼─────────┐
│ Dashboard (API) │ │ CLI `fn` router │ │ Pi extension tools │
│ + React SPA │ │ + TUI component │ │ (extension.ts) │
│ (lazy-loaded) │ │ (commands/*) │ │ │
└─────────┬─────────┘ └─────────┬─────────┘ └─────────┬─────────┘
└──────────────┬────────┴──────────────┬───────┘
│ │
┌────────▼───────────────────────▼───────┐
│ Engine Runtime │
│ Scheduler / Planning / Executor / Merger │
│ Heartbeat / Self-healing / Autopilot │
└────────┬───────────────────────┬────────┘
│ │
┌───────────▼──────────┐ ┌────────▼─────────────┐
│ @fusion/core │ │ External systems │
│ stores + types │ │ git, GitHub, models │
└───────┬──────────────┘ └───────────────────────┘
┌────────────────▼────────────────┐
│ Persistence │
│ - .fusion/fusion.db (SQLite/WAL)
│ - .fusion/tasks/* (PROMPT/logs)
│ - ~/.fusion/fusion-central.db │
└──────────────────────────────────┘
```
---
## 2) Monorepo Structure
| Package | Published | Role | Key files |
|---|---|---|---|
| `@fusion/core` | Private | Domain model, stores, SQLite adapters, settings, shared types | `packages/core/src/types.ts`, `store.ts`, `db.ts`, `central-core.ts`, `agent-store.ts` |
| `@fusion/engine` | Private | AI orchestration runtime (planning, scheduler, executor, merger, recovery) | planning processor, `scheduler.ts`, `executor.ts`, `merger.ts`, `project-runtime.ts` |
| `@fusion/dashboard` | Private | Express API server + React app | `packages/dashboard/src/server.ts`, `routes.ts`, `sse.ts`, `websocket.ts`, `packages/dashboard/app/App.tsx` |
| `@runfusion/fusion` | **Published** | CLI binary (`fn`) + Pi extension | `packages/cli/src/bin.ts`, `commands/*`, `project-resolver.ts`, `extension.ts` |
| `@fusion/desktop` | Private | Electron shell around Fusion dashboard/client | `packages/desktop/src/main.ts`, `ipc.ts`, `preload.ts`, `scripts/build.ts` |
| `@fusion/mobile` | Private | Capacitor + PWA mobile packaging of dashboard assets | `packages/mobile/capacitor.config.ts`, `packages/mobile/src/*` |
| `@fusion/plugin-sdk` | Private | Plugin SDK for building Fusion extensions | `packages/plugin-sdk/src/*` |
---
## 3) Package Dependencies
### Workspace dependency graph
`A ──▶ B` means **A depends on B**.
```text
@fusion/engine ───────────────▶ @fusion/core
@fusion/dashboard ────────────▶ @fusion/core
@fusion/dashboard ────────────▶ @fusion/engine
@runfusion/fusion (CLI) ─────────▶ @fusion/core
@runfusion/fusion (CLI) ─────────▶ @fusion/engine
@runfusion/fusion (CLI) ─────────▶ @fusion/dashboard
@fusion/plugin-sdk (peerDep) ─▶ @fusion/core
@fusion/desktop: no workspace package dependencies
@fusion/mobile: no workspace package dependencies
```
Concrete references:
- `@fusion/engine` has a workspace dependency on `@fusion/core` (`packages/engine/package.json`)
- `@fusion/dashboard` has workspace dependencies on `@fusion/core` and `@fusion/engine` (`packages/dashboard/package.json`)
- `@runfusion/fusion` has workspace development dependencies on `@fusion/core`, `@fusion/engine`, and `@fusion/dashboard` for composition/build packaging (`packages/cli/package.json`)
- `@fusion/plugin-sdk` declares a peer dependency on `@fusion/core` (`packages/plugin-sdk/package.json`)
- `@fusion/desktop` embeds dashboard assets at build time via script (`packages/desktop/scripts/build.ts`) but does not declare workspace deps in `package.json`
- `@fusion/mobile` triggers dashboard build/sync via scripts (`packages/mobile/package.json`) but does not declare workspace deps in `package.json`
---
## 4) Core Package (`@fusion/core`)
### Responsibility
`@fusion/core` is the shared domain and persistence layer.
### Main components
- **Types and constants**: `packages/core/src/types.ts`
- Columns: `COLUMNS`
- Transition map: `VALID_TRANSITIONS`
- Settings defaults: `DEFAULT_GLOBAL_SETTINGS`, `DEFAULT_PROJECT_SETTINGS`
- Workflow types (`WorkflowStep`, `WorkflowStepPhase`, etc.)
- **TaskStore**: `packages/core/src/store.ts`
- Main task CRUD + lifecycle store
- Emits board events (`task:created`, `task:moved`, `task:updated`, ...)
- Hybrid model: SQLite metadata + filesystem blobs under `.fusion/tasks/{id}`
- **Database adapter**: `packages/core/src/db.ts`
- SQLite (`node:sqlite`) with WAL mode + foreign keys
- JSON helpers: `toJson`, `toJsonNullable`, `fromJson`
- Core schema tables include: `tasks`, `config`, `workflow_steps`, `activityLog`, `archivedTasks`, `automations`, `agents`, `agentHeartbeats`, `task_documents`, `task_document_revisions`, mission hierarchy tables (`missions`, `milestones`, `slices`, `mission_features`, `mission_events`), plugin/routine tables (`plugins`, `routines`), roadmap tables (`roadmaps`, `roadmap_milestones`, `roadmap_features`), insight tables (`project_insights`, `project_insight_runs`), todo tables (`todo_lists`, `todo_items`), `__meta`
- Migration-created tables include: `ai_sessions`, `messages`, `agentRatings`, `chat_sessions`, `chat_messages`, `runAuditEvents`, `mission_contract_assertions`, `mission_feature_assertions`, `mission_validator_runs`, `mission_validator_failures`, `mission_fix_feature_lineage`
- `ai_sessions.status` lifecycle includes `draft` (pre-start planning session), then `generating`, `awaiting_input`, terminal `complete` / `error`
- **Standalone roadmap model**: `packages/core/src/roadmap-types.ts`, `roadmap-ordering.ts`, `roadmap-store.ts`
- Roadmap-first entity types (`Roadmap`, `RoadmapMilestone`, `RoadmapFeature`)
- Pure ordering helpers for contiguous 0-based milestone/feature order and deterministic cross-milestone feature moves
- `RoadmapStore` for CRUD operations, deterministic ordering, and atomic reorder/move operations
- Dashboard API routes in `packages/dashboard/src/roadmap-routes.ts`
- Exported from `@fusion/core` for downstream persistence/API/UI work
- **CentralCore**: `packages/core/src/central-core.ts`
- Global project registry, health, central activity feed, global concurrency
- Backed by `packages/core/src/central-db.ts` (`~/.fusion/fusion-central.db`)
- **Specialized stores**:
- `AgentStore` (`agent-store.ts`) — filesystem-based agent metadata + heartbeat run history
- `MissionStore` (`mission-store.ts`) — mission/milestone/slice/feature hierarchy
- `AutomationStore` (`automation-store.ts`) — scheduled jobs with global/project scope isolation
- `MessageStore` (`message-store.ts`) — mailbox/inbox/outbox messaging
- `ChatStore` (`chat-store.ts`) — session/message persistence for agent chat
- `InsightStore` (`insight-store.ts`) — project insight persistence + dedupe/run tracking
- `ReflectionStore` (`reflection-store.ts`) — agent reflection records and performance snapshots
- `PluginStore` (`plugin-store.ts`) — plugin registry/state/settings persistence
- `RoutineStore` (`routine-store.ts`) — recurring routine definitions and run history
- `RoadmapStore` (`roadmap-store.ts`) — standalone roadmap CRUD with deterministic ordering and atomic reorder/move operations
- `TodoStore` (`todo-store.ts`) — project-scoped todo lists/items with completion, reorder, and composite list+items queries
### Chat System
- `ChatStore` (`packages/core/src/chat-store.ts`) and `chat-types.ts` provide session-oriented chat state (`chat_sessions`, `chat_messages` tables)
- Dashboard chat UX lives in `packages/dashboard/app/components/ChatView.tsx` and hooks `useChat.ts` / `useQuickChat.ts`
- Chat message submission uses SSE streaming responses from dashboard chat routes
### Agent Companies
- Import/export utilities: `agent-companies-parser.ts`, `agent-companies-exporter.ts`, `agent-companies-types.ts`
- Supports YAML-frontmatter manifests for company/team/agent/project/task/skill definitions
- Includes conversion helpers from parsed manifests to `AgentCreateInput` and export helpers for directory bundles
### Project Insights
- `InsightStore` (`insight-store.ts`, `insight-types.ts`) persists extracted project learnings
- Uses fingerprint-based deduplication and run tracking
- Run lifecycle is hardened through `insight-run-executor.ts` + `InsightStore` transition guards:
- single active run per `projectId + trigger` (`pending|running` conflict)
- terminal-state immutability for run rows
- persisted failure classification (`cancelled`, `timed_out`, `retryable_transient`, `non_retryable`) and retry lineage metadata
- append-only durable event trail in `project_insight_run_events`
- Dashboard routes (`insights-routes.ts`) consume the core executor/store APIs for run start, cancel, retry, and event inspection (`/api/insights/runs/:id/events`)
- Backed by `project_insights`, `project_insight_runs`, and `project_insight_run_events`
### Research Runs
- `ResearchStore` (`research-store.ts`, `research-types.ts`, `research-settings.ts`) persists bounded research runs, sources/events, and exports
- Backed by `research_runs` and `research_exports`
- Engine orchestration is implemented in `packages/engine/src/research-orchestrator.ts` + `research-step-runner.ts`
- Dashboard/API surface is implemented under `/api/research` (`packages/dashboard/src/research-routes.ts`) with `ResearchView.tsx` in the app
- **Boundary note:** research and insights are parallel subsystems sharing host infrastructure, not one table/store family
### Plugin System
- `PluginStore` (`plugin-store.ts`) stores plugin installation state and settings (`plugins` table)
- `PluginLoader` (`plugin-loader.ts`) loads/unloads plugin modules and emits lifecycle events
- Plugin contributions now include both embedded `uiSlots` and top-level `dashboardViews`
- Discovery endpoints:
- `GET /api/plugins/ui-slots`
- `GET /api/plugins/dashboard-views`
- Dashboard management routes are implemented in `packages/dashboard/src/plugin-routes.ts`
### Prompt Overrides
- `prompt-overrides.ts` defines prompt key catalogs and per-role override validation
- Provides override resolution/validation helpers (`resolvePrompt`, `resolveRolePrompts`, `assertValidPromptOverrideMap`)
### Agent Permissions
- `agent-permissions.ts` normalizes permissions and computes effective access state
- Core helpers: `normalizePermissions`, `computeAccessState`, `ROLE_DEFAULT_PERMISSIONS`
### Standalone roadmap model
Fusion now has two planning models in core:
- **Roadmap hierarchy** — `Roadmap → RoadmapMilestone → RoadmapFeature`
- **Mission hierarchy** — `Mission → Milestone → Slice → Feature → Task`
The roadmap model is intentionally lightweight and independent from `MissionStore`/mission lifecycle semantics. It is meant for standalone planning, ordering, drag-and-drop moves, and future conversion flows into missions or tasks without coupling roadmap data to slice activation, autopilot, or mission status rollups.
**Roadmap persistence (FN-1690/FN-1691):**
- `RoadmapStore` provides CRUD operations with atomic reorder/move semantics
- All list queries use deterministic ordering: `ORDER BY orderIndex ASC, createdAt ASC, id ASC`
- Covering indexes ensure efficient ordered reads without temp B-tree sorts
- Cross-milestone feature moves atomically renumber both source and destination milestone scopes
- FK cascade integrity: deleting a roadmap removes milestones and features
- Export/handoff DTO methods for integration with downstream systems:
- `getRoadmapExport()``RoadmapExportBundle` (flat export payload)
- `getMissionPlanningHandoff()``RoadmapMissionPlanningHandoff` (mission conversion)
- `listFeatureTaskPlanningHandoffs()``RoadmapFeatureTaskPlanningHandoff[]` (all features as task handoffs)
- `getRoadmapFeatureHandoff()``RoadmapFeatureTaskPlanningHandoff` (single feature task handoff)
- Pure handoff mapping helpers in `roadmap-handoff.ts` for read-only transformations
**Roadmap handoff contract boundary (FN-1674):**
- Handoffs are **read-only** transformations — no mission/task records are created
- Source lineage is preserved on every emitted item (roadmapId, milestoneId, featureId, titles, order indices)
- Ordering is deterministic using `normalizeRoadmapMilestoneOrder` and `normalizeRoadmapFeatureOrder`
- Not-found semantics: store handoff methods throw when roadmapId is unknown; routes map to HTTP 404
- The combined handoff endpoint (`GET /:roadmapId/handoff`) returns both mission and task handoffs
Key roadmap invariants:
- milestone ordering is scoped to a single roadmap and must remain contiguous + 0-based
- feature ordering is scoped to a single milestone and must remain contiguous + 0-based
- repair/normalization uses deterministic tie-breakers: `orderIndex ASC`, `createdAt ASC`, `id ASC`
- cross-milestone feature moves must renumber both the source and destination milestone deterministically
**Roadmap REST API endpoints (`/api/roadmaps`):**
- Roadmaps: `GET /`, `POST /`, `GET /:roadmapId`, `PATCH /:roadmapId`, `DELETE /:roadmapId`
- Milestones: `GET /:roadmapId/milestones`, `POST /:roadmapId/milestones`, `PATCH /milestones/:milestoneId`, `DELETE /milestones/:milestoneId`, `POST /:roadmapId/milestones/reorder`
- Features: `GET /milestones/:milestoneId/features`, `POST /milestones/:milestoneId/features`, `PATCH /features/:featureId`, `DELETE /features/:featureId`, `POST /milestones/:milestoneId/features/reorder`, `POST /features/:featureId/move`
- Export/Handoff: `GET /:roadmapId/export`, `GET /:roadmapId/handoff`, `GET /:roadmapId/handoff/mission`, `GET /:roadmapId/milestones/:milestoneId/features/:featureId/handoff/task`
**Database schema:**
- `roadmaps` — roadmap metadata (id, title, description, timestamps)
- `roadmap_milestones` — milestone data with `roadmapId` FK
- `roadmap_features` — feature data with `milestoneId` FK
- `idxRoadmapMilestonesRoadmapOrder` — covering index for deterministic milestone ordering
- `idxRoadmapFeaturesMilestoneOrder` — covering index for deterministic feature ordering
### Shared utilities
From `packages/core/src/index.ts` exports (selected high-impact modules):
- **Memory + knowledge**: `memory-backend.ts`, `memory-compaction.ts`, `memory-dreams.ts`, `project-memory.ts`, `memory-insights.ts`, `insight-store.ts`, `insight-types.ts`
- **Stores and plugin/routine helpers**: `chat-store.ts`, `routine-store.ts`, `plugin-store.ts`, `plugin-loader.ts`, `reflection-store.ts`
- **Execution/runtime helpers**: `run-command.ts`, `board.ts`, `task-merge.ts`, `archive-db.ts`
- **Settings + prompts + permissions**: `settings-schema.ts`, `prompt-overrides.ts`, `agent-permissions.ts`, `agent-prompts.ts`
- **Node/system infrastructure**: `node-connection.ts`, `node-discovery.ts`, `system-metrics.ts`, `migration-orchestrator.ts`
- **Identity/version/extensions**: `daemon-token.ts`, `app-version.ts`, `pi-extensions.ts`
- **Agent companies import/export**: `agent-companies-parser.ts`, `agent-companies-exporter.ts`, `agent-companies-types.ts`
### Memory System
Fusion uses OpenClaw-style project memory files and separates memory into two responsibilities:
1. **Layered backend runtime memory** (`memory-backend.ts`, `project-memory.ts`)
- canonical long-term + layered memory access used by agents and dashboard APIs
2. **Insight extraction automation** (`memory-insights.ts`, `InsightStore`)
- scheduled extraction/pruning workflows over project memory plus insight/audit artifacts
Both systems currently use `.fusion/memory/MEMORY.md` as the canonical working source-of-truth.
**Primary memory files:**
- Long-term: `.fusion/memory/MEMORY.md`
- Daily notes: `.fusion/memory/YYYY-MM-DD.md`
- Dream processing: `.fusion/memory/DREAMS.md`
**Memory subsystems:**
- `memory-backend.ts` — backend contracts + file/readonly/qmd implementations
- `memory-compaction.ts` — summarization/compaction automation
- `memory-dreams.ts` — background dream processing for agent and project memory
- `memory-insights.ts` + `InsightStore` — extracted insight synthesis and persistent insight/run storage
**Pluggable backends (`memory-backend.ts`):**
| Backend | Type | Capabilities |
|---------|------|-------------|
| `FileMemoryBackend` | `file` | Read/Write, Atomic writes, Persistent |
| `ReadOnlyMemoryBackend` | `readonly` | Read only, Non-persistent |
| `QmdMemoryBackend` | `qmd` | Read/Write, Persistent, CLI-based with file fallback |
**Backend registration:**
```typescript
import { registerMemoryBackend, resolveMemoryBackend } from "@fusion/core";
// Register custom backend
registerMemoryBackend(customBackend);
// Resolve based on settings
const backend = resolveMemoryBackend(settings);
```
**Settings integration:**
- `memoryEnabled`: Toggle controls whether memory instructions are injected into prompts
- `memoryBackendType`: Select which backend to use (`file`, `readonly`, `qmd`, or custom). Unknown types are accepted and persisted verbatim; runtime resolution falls back to `DEFAULT_MEMORY_BACKEND` (`qmd`).
**QMD Backend Behavior:**
The QMD backend (`qmd`) delegates read/write I/O to the file backend and schedules background QMD index refreshes. For search, it attempts QMD query first and falls back to local `.fusion/memory/` file search when QMD is unavailable, errors, or returns no matches.
**Dashboard API:**
- `GET /api/memory/backend` — Returns current backend status and capabilities
See [Memory Plugin Contract](./memory-plugin-contract.md) for the full plan.
---
## 5) Engine Package (`@fusion/engine`)
`@fusion/engine` executes the autonomous workflow.
### Agent roles
- **Planning**: the planning processor generates task plans (`PROMPT.md`) and selects eligible planning tasks by priority first, then FIFO (`createdAt` ascending) within each priority tier.
- **Executor**: `TaskExecutor` (`executor.ts`) implements tasks in worktrees
- **Reviewer**: `reviewStep()` (`reviewer.ts`) performs plan/code reviews
- **Merger**: `aiMergeTask()` (`merger.ts`) merges approved work
### Scheduling and execution
- `Scheduler` (`scheduler.ts`) — dependency-aware task scheduling that dispatches eligible todo tasks by priority first, then FIFO (`createdAt` ascending) within each priority tier.
- `StepSessionExecutor` (`step-session-executor.ts`) — per-step sessions + parallel wave execution
- `TaskCompletion` (`task-completion.ts`) — completion gate helpers
- `SpecStaleness` (`spec-staleness.ts`) — stale spec detection utilities
- `MissionExecutionLoop` (`mission-execution-loop.ts`) — validator/fix loop orchestration
- `MissionFeatureSync` (`mission-feature-sync.ts`) — feature↔task status synchronization
- `MissionAutopilot` (`mission-autopilot.ts`) — mission slice auto-progression
### Routine + cron automation
- `RoutineRunner` (`routine-runner.ts`) — executes routine steps
- `RoutineScheduler` (`routine-scheduler.ts`) — schedules due routines
- `CronRunner` (`cron-runner.ts`) — cron-based AI/script jobs
### Execution context + skills
- `SkillResolver` (`skill-resolver.ts`) — resolves active skill sets for sessions
- `SessionSkillContext` (`session-skill-context.ts`) — skill context materialization per run
- `ContextLimitDetector` (`context-limit-detector.ts`) — context-window pressure checks
- `TokenCapDetector` (`token-cap-detector.ts`) — token-cap enforcement checks
- `PluginRunner` (`plugin-runner.ts`) — runtime plugin callback execution
- `AgentRuntime` (`agent-runtime.ts`) — runtime adapter interface contract
- `RuntimeResolution` (`runtime-resolution.ts`) — runtime selection and fallback logic
- `AgentSessionHelpers` (`agent-session-helpers.ts`) — runtime-aware session creation helpers
### Concurrency, recovery, and resiliency
- `AgentSemaphore` (`concurrency.ts`) — slot acquisition
- `RecoveryPolicy` (`recovery-policy.ts`) — retry/recovery decision policy
- `StuckTaskDetector` (`stuck-task-detector.ts`) — inactivity/loop stall detection
- `GridlockDetector` (`gridlock-detector.ts`) — detects all-blocked todo pipelines and emits notification events (plus explicit clear signals when gridlock resolves)
- `TransientErrorDetector` (`transient-error-detector.ts`) — retriable error classification
- `SelfHealingManager` (`self-healing.ts`) — auto-unpause/maintenance recovery actions
- `recoverGhostReviewTasks()` is a fallback only for idle, non-terminal `in-review` states. Terminal/actionable states (notably `status: "failed"`) are preserved and **not** auto-kicked back to `todo`.
- `recoverMergeableReviewTasks()` only re-enqueues truly eligible tasks; retry-exhausted review tasks are skipped to avoid re-enqueue/no-op loops that keep refreshing `updatedAt`.
- `ProjectEngine` settings lifecycle handlers (`project-engine.ts`) treat `enginePaused` as a soft pause: clearing it dispatches runtime resume and, when `autoMerge` is enabled, performs an `in-review` eligibility sweep to requeue mergeable review tasks.
- `UsageLimitPauser` (`usage-limit-detector.ts`) and `withRateLimitRetry` (`rate-limit-retry.ts`)
### Worktree and naming helpers
- `WorktreePool` (`worktree-pool.ts`) — idle worktree reuse
- `WorktreeNames` (`worktree-names.ts`) — deterministic worktree/branch naming
### Observability and reflection
- `AgentLogger` (`agent-logger.ts`) — structured per-agent run logging
- `RunAudit` (`run-audit.ts`) — mutation audit tracking (DB/git/filesystem)
- `Notifier` (`notifier.ts`) — legacy ntfy compatibility shim (`NtfyNotifier`) plus shared ntfy helpers
- Runtime ownership: `NtfyNotifier` no longer owns an independent task-lifecycle listener graph; `ProjectEngine` injects the canonical `NotificationService` instance so task lifecycle notifications (`task:moved`, `task:updated`, `task:merged`) are emitted through a single path.
- Compatibility scope: `NtfyNotifier` remains responsible for gridlock-only compatibility notifications (`notifyGridlock`) and legacy helper APIs.
- Legacy gridlock ntfy delivery is cooldown-throttled: first detection notifies immediately, subsequent detections are suppressed for 15 minutes (even if blocked-task membership changes), and the cooldown resets as soon as gridlock fully clears.
- `NotificationService` (`notification/notification-service.ts`) — provider lifecycle + event dispatch orchestration
- `NotificationProvider` interface (`@fusion/core` `notification/provider.ts`) — pluggable provider contract
- Built-in providers: `NtfyNotificationProvider` (`notification/ntfy-provider.ts`), `WebhookNotificationProvider` (`notification/webhook-provider.ts`)
- `AgentReflection` (`agent-reflection.ts`) — reflection extraction and persistence
### Heartbeat execution
Implemented in `agent-heartbeat.ts`:
- `HeartbeatMonitor`
- `HeartbeatTriggerScheduler` (timer, assignment, on-demand triggers)
- `WakeContext` / per-agent runtime config support
### Node/mesh runtime services
- `NodeHealthMonitor` (`node-health-monitor.ts`) — remote node liveness/metrics checks
- `PeerExchangeService` (`peer-exchange-service.ts`) — peer sync orchestration
- Process lifecycle ownership:
- `fn serve` / `fn dashboard` start a single process-level `PeerExchangeService` and stop it during shutdown.
- `CentralCore.startDiscovery()` is invoked from CLI startup only after HTTP bind completes so discovery advertises the actual listening port.
- `InProcessRuntime` stays project-scoped and intentionally does not own mesh startup/shutdown.
### Remote access runtime
Operator setup + troubleshooting guide: **[Remote Access runbook](./remote-access.md)**.
- `remote-access/tunnel-process-manager.ts` owns tunnel lifecycle orchestration with `spawn`-based, non-blocking process supervision.
- `remote-access/types.ts` defines the runtime contract used by downstream API/TUI/headless layers:
- Providers: `"tailscale" | "cloudflare"`
- Lifecycle states: `"stopped" | "starting" | "running" | "stopping" | "failed"`
- Error codes: `invalid_config`, `start_failed`, `stop_failed`, `switch_failed`, `readiness_timeout`, `process_exit`, etc.
- `remote-access/provider-adapters.ts` provides provider-specific command composition + readiness parsing while enforcing config validation.
- Cloudflare has two command variants:
- Named tunnel mode: `cloudflared tunnel --no-autoupdate run <tunnelName>` (token from env)
- Quick tunnel mode: `cloudflared tunnel --url http://localhost:<dashboardPort>` (ephemeral `trycloudflare.com` URL, no token)
- Credential inputs are reference-based (`tokenEnvVar`, `credentialsPath`) and validated without logging raw secret values.
- Redaction is applied to command previews and emitted log lines before publishing status/log events.
- Deterministic stop semantics: graceful shutdown (`SIGTERM`) first, bounded wait, then force-kill fallback (`SIGKILL`).
- Safe provider switching is stop-first: active provider fully stops before target start is attempted; failed starts emit `switch_failed` terminal status.
- `ProjectEngine.start()` instantiates a per-project tunnel manager and applies startup restore policy from `remoteAccess.lifecycle`:
- restore is attempted only when `rememberLastRunning` is true, a prior-running marker exists, provider config is valid, and runtime prerequisites are available.
- restore skips/failures are non-fatal to engine startup and clear stale running markers to avoid restart loops.
- Manual lifecycle remains explicit: only `startRemoteTunnel()` / `stopRemoteTunnel()` transitions mutate runtime state; provider/settings updates do not auto-start tunnels.
- `ProjectEngine` exposes restore diagnostics via `getRemoteTunnelRestoreDiagnostics()` (`applied|skipped|failed` + machine-readable reason).
### Multi-runtime support + IPC
- Runtime contracts: `project-runtime.ts`
- Orchestration: `ProjectManager` and `HybridExecutor`
- Runtime implementations:
- `runtimes/in-process-runtime.ts`
- `runtimes/child-process-runtime.ts`
- `runtimes/remote-node-runtime.ts`
- IPC protocol/transport:
- `ipc/ipc-protocol.ts`
- `ipc/ipc-host.ts`
- `ipc/ipc-worker.ts`
- worker entrypoint: `runtimes/child-process-worker.ts`
---
## 6) Dashboard Package (`@fusion/dashboard`)
### Server layer
- Entry exports: `packages/dashboard/src/index.ts`
- Main server factory: `createServer()` in `packages/dashboard/src/server.ts`
- Primary API router: `createApiRoutes()` in `packages/dashboard/src/routes.ts`
Key server capabilities:
- REST APIs for tasks, git, GitHub, agents, missions, planning, automations/routines, settings
- System stats snapshot and vitest process controls APIs (`GET /api/system-stats`, `POST /api/kill-vitest`) exposing dashboard process/system telemetry (including host memory rendered as both numeric values and a visual usage bar in the System Stats modal), task/agent aggregates, and manual vitest process termination
- Remote access APIs (`/api/remote/*`) for provider config, activation, tunnel lifecycle, status, token issuance, authenticated URL generation, and QR payload generation
- Operational runbook (prereqs/security/troubleshooting): [`docs/remote-access.md`](./remote-access.md)
- `/api/remote/tunnel/start`, `/api/remote/tunnel/stop`, and `/api/remote/tunnel/kill-external` cover tunnel lifecycle and external funnel cleanup.
- `/api/remote/status` includes tunnel status, external funnel detection (`externalTunnel` when managed tunnel is stopped), plus restore diagnostics (`restore.outcome` + `restore.reason`) with parity between dashboard and headless `fn serve` runtimes.
- Remote auth handoff endpoints:
- `POST /api/remote-access/auth/login-url` (daemon-auth protected) issues a tokenized phone-login URL for either `persistent` or `short-lived` mode.
- `GET /remote-login?rt=<token>` (public) validates remote token strategy and redirects to dashboard auth handoff (`/?token=<daemonToken>` when daemon auth is enabled, otherwise `/`).
- Invalid/missing/expired remote tokens return `401` JSON with deterministic codes: `remote_token_invalid`, `remote_token_missing`, `remote_token_expired`.
- Chat APIs (`/api/chat/*`) with streaming response support (`routes.ts`, `chat.ts`)
- Dev-server lifecycle + persistence APIs (`/api/dev-server/*`) backed by:
- `dev-server-routes.ts` (router factory + per-project runtime registry)
- `dev-server-process.ts` (`DevServerProcessManager` for spawn/stop/restart/url-detection)
- `dev-server-store.ts` (durable `.fusion/dev-server.json` state + log ring buffer)
- `dev-server-detect.ts` (project/workspace script auto-detection + confidence scoring)
- Note: this **hyphenated `dev-server-*` family is the canonical runtime owner** today; see `docs/dev-server-module-boundary-audit.md` for the FN-2212 boundary/consolidation audit covering parallel `devserver-*` modules.
- Plugin management routes (`plugin-routes.ts`)
- Insights routes (`insights-routes.ts`)
- Research routes (`research-routes.ts`) — `/api/research` surface for runs, details, cancel/retry, exports, create-task, and attach-task actions; supports graceful degradation envelopes via availability payloads when capabilities are unavailable
- Roadmap routes (`roadmap-routes.ts`)
- Project-scoped store reuse via `project-store-resolver.ts`
- Rate limiting (`rate-limit.ts`)
- Static SPA hosting (Vite build output)
### Runtime diagnostics logging contract
- Dashboard/server runtime diagnostics use the shared `RuntimeLogger` contract (`packages/dashboard/src/runtime-logger.ts`) instead of ad hoc `console.*` calls.
- `createServer()` accepts `ServerOptions.runtimeLogger`; when omitted it defaults to a console-backed logger, preserving readable output in non-TTY/headless modes.
- CLI TTY dashboard sessions inject a logger backed by `DashboardLogSink`, so runtime diagnostics from server/routes are captured in the TUI log buffer.
- Sensitive remote-auth material is never logged raw; route/UI responses mask persistent token values unless explicitly requested by token-generation actions.
- Short-lived remote auth tokens are runtime-ephemeral (in-memory only, cleared on process restart) and TTL-enforced server-side against persisted `remoteAccess.tokenStrategy.shortLived.ttlMs` plus issued expiry metadata.
- Remote login links carry auth material in query params (`rt` then `token` on redirect). Treat links/QR screenshots as secrets: they can leak through history, screenshots, and chat logs; prefer short-lived mode for sharing.
- Intentional startup/banner text in `fn dashboard` and `fn serve` remains direct plain output for readability and backward-compatible scripting behavior.
### Real-time channels
- **SSE**: `/api/events` (`sse.ts`)
- Emits `task:*`, mission events, AI session updates, automation schedule events (`schedule:created`, `schedule:updated`, `schedule:deleted`, `schedule:run`), and research run lifecycle events (`research:run:created`, `research:run:updated`, `research:run:completed`, `research:run:failed`, `research:run:cancelled`) when available
- Project-scoped: resolves project context from query param or engine manager
- Canonical maintainer contract (ownership/lifecycle/scoping/pitfalls and shared-vs-dedicated stream boundaries): [`docs/dashboard-realtime.md`](./dashboard-realtime.md)
- **Chat streaming**: `/api/chat/sessions/:id/messages` (`routes.ts` + `chat.ts`)
- Streams assistant responses as SSE events for chat sessions
- **Chat session queries**: `/api/chat/sessions` (`routes.ts`)
- Existing list behavior is unchanged (`status=active|archived|all` returns an array)
- Quick Chat resume uses targeted lookup params: `agentId`, optional `modelProvider` + `modelId`, plus `resume=1`
- Validation requires `modelProvider` and `modelId` together; partial model pairs return `400`
- Targeted lookup returns only the newest matching active session (or `null`) to avoid scanning every active session client-side
- **Task log stream**: `/api/tasks/:id/logs/stream` (`server.ts`)
- SSE endpoint for live task log streaming with project scope resolution
- **Dev-server stream**: `/api/dev-server/logs/stream` (`dev-server-routes.ts`)
- SSE stream emits `history`, `log`, `stopped`, and `failed` events
- initial connection replays persisted `logHistory` and then follows live process output
- companion endpoints: `/api/dev-server/detect`, `/config`, `/status`, `/start`, `/stop`, `/restart`, `/preview-url`
- **Badge WebSocket**: `/api/ws` (`server.ts`, `websocket.ts`)
- Scope-keyed channels (`badge:{scopeKey}:{taskId}`) prevent cross-project collisions
- **Terminal WebSocket**: `/api/terminal/ws` (`server.ts`, `terminal-service.ts`)
- Project-scoped terminal session validation + safe unscoped fallback
### Frontend SPA layer
- App entry: `packages/dashboard/app/main.tsx`
- Root composition: `packages/dashboard/app/App.tsx`
- Core board components: `Board.tsx`, `Column.tsx`, `TaskCard.tsx`, `TaskDetailModal.tsx`, `ListView.tsx`
- Task detail surface is shared through `TaskDetailContent` (exported from `TaskDetailModal.tsx`): desktop/tablet `ListView` renders it inline in the split right pane, while mobile and non-list entry points continue using `TaskDetailModal`.
- Chat system UI: `ChatView.tsx`, `QuickChatFAB.tsx`
- Planning/roadmap/insight UI: `MissionManager.tsx`, `RoadmapsView.tsx`, `TodoView.tsx`, `InsightsView.tsx`, `DocumentsView.tsx`
- Dev server UI: `DevServerView.tsx` (controls + status/log panel + embedded preview with iframe fallback messaging)
### CSS Architecture
The dashboard's CSS is split between a consolidated global stylesheet and modular per-component files:
- **Global stylesheet** (`packages/dashboard/app/styles.css`, ~4,500 lines)
- Design tokens (spacing, colors, shadows, transitions, fonts)
- Primitive component classes (`.btn`, `.card`, `.modal`, `.form-input`)
- Cross-component `@media` overrides and breakpoint definitions
- **Per-component stylesheets** (56+ files in `packages/dashboard/app/components/`)
- Each component has a co-located `ComponentName.css` file
- Each `ComponentName.tsx` imports its stylesheet: `import "./ComponentName.css";`
- Component-specific CSS rules live in the component's `.css` file, not in the root stylesheet
**Lazy-loaded views** (bundle size optimization):
The following 15 views are lazy-loaded via `React.lazy()` with `<Suspense fallback={null}>`:
- `AgentsView`, `RoadmapsView`, `TodoView`, `NodesView`, `ChatView`, `MemoryView`, `ResearchView`
- `DevServerView`, `InsightsView`, `DocumentsView`, `SkillsView`
- `SetupWizardModal`, `PluginManager`, `PiExtensionsManager`, `AgentDetailView
A `prefetchLazyViews()` function runs once on mount via `requestIdleCallback` to warm chunks. Do not make these views eager — bundle size is carefully managed.
### Key hooks
- Task + realtime: `useTasks.ts`, `useBadgeWebSocket.ts`, `useAiSessionSync.ts`
- Chat: `useChat.ts`, `useQuickChat.ts`
- Documents/insights/memory: `useDocuments.ts`, `useInsights.ts`, `useMemoryBackendStatus.ts`, `useMemoryData.ts`
- Planning/roadmaps: `useRoadmaps.ts`
- Dev server: `useDevServer.ts` (status hydration, command controls, reconnect stream handling, project-scope reset)
- Project/agents/setup: `useProjects.ts`, `useCurrentProject.ts`, `useAgents.ts`, `useSetupReadiness.ts`
- UX/platform helpers: `useFavorites.ts`, `useAuthOnboarding.ts`, `useDeepLink.ts`, `useTerminal.ts`
### Planning and decomposition features
- Backend planners: `planning.ts`, `subtask-breakdown.ts`, `roadmap-suggestions.ts`
- UI modals: `PlanningModeModal.tsx`, `SubtaskBreakdownModal.tsx`, milestone interview flows
- Multi-task creation endpoints are wired under planning/subtask routes in `routes.ts`
### Health and monitoring endpoints
- **Health check**: `GET /api/health`
- Returns liveness status for load balancers and monitoring
- Response: `{ status: "ok", version: string, uptime: number }`
- No authentication required
### Node settings sync and update-check endpoints
| Method | Path | Description |
|---|---|---|
| GET | `/api/nodes/:id/settings` | Fetch settings from a remote node. |
| POST | `/api/nodes/:id/settings/push` | Push local settings to a remote node. |
| POST | `/api/nodes/:id/settings/pull` | Pull settings from a remote node. |
| GET | `/api/nodes/:id/settings/sync-status` | Get sync status and diff summary. |
| POST | `/api/nodes/:id/auth/sync` | Sync model auth credentials. |
| POST | `/api/settings/sync-receive` | Receive pushed settings (inbound). |
| POST | `/api/settings/auth-receive` | Receive auth credentials (inbound). |
| GET | `/api/settings/auth-export` | Export local auth credentials. |
| GET | `/api/update-check` | Read cached/TTL-guarded npm update status for `@runfusion/fusion` (respects `updateCheckEnabled`). |
| POST | `/api/update-check/refresh` | Clear cached update data and force a fresh npm update check. |
| GET | `/api/updates/check` | Perform an on-demand npm registry check for the latest `@runfusion/fusion` version (no cache). |
### Run Audit API
The run-audit system records every mutation performed by the engine across three domains:
- **Database** — task:create, task:update, task:move, etc.
- **Git** — worktree:create, commit:create, merge:resolve, etc.
- **Filesystem** — file:write, prompt:write, attachment:create, etc.
Events are tied to specific run IDs for end-to-end traceability.
**Run audit endpoint:**
- `GET /api/agents/:id/runs/:runId/audit` — Returns audit trail for a specific agent run
- Query params: `?domain=database|git|filesystem` for filtering
- Requires agent ownership or admin access
---
## 7) CLI Package (`@runfusion/fusion`)
### Command entrypoint
- `packages/cli/src/bin.ts`
- Bootstraps environment
- Parses global flags (including `--project`)
- Routes subcommands (`task`, `project`, `settings`, `git`, `backup`, `mission`, `agent`, `message`, etc.)
### Command modules
- `packages/cli/src/commands/*`
- Task operations, settings, git wrappers, backup operations, project/node management
- **TUI component** (`packages/cli/src/commands/dashboard-tui/`)
- Ink-based terminal UI (status panel, logs, cursor visibility, tail-follow)
- Merged from former `@fusion/tui` package
- Invoked as part of the `fn` command (no separate package or `pnpm tui` command)
### Project selection
- `packages/cli/src/project-resolver.ts`
- Resolution order: explicit `--project` → CWD detection (`.fusion`) → default/fallback logic
- Integrates `CentralCore` and `ProjectManager`
### Pi extension
- `packages/cli/src/extension.ts`
- Registers tool set for in-chat task/mission operations
- Uses `TaskStore` directly for extension-side actions
### Binary identity
- Published package defines `fn` binary (`packages/cli/package.json`)
- Running `fn` with no arguments defaults to dashboard (web UI by default)
---
## 8) Storage Architecture
Fusion uses a hybrid storage model.
### Per-project storage
- **SQLite DB**: `.fusion/fusion.db`
- **Filesystem blobs** (task-local artifacts):
- `.fusion/tasks/{TASK_ID}/PROMPT.md`
- `.fusion/tasks/{TASK_ID}/agent.log`
- `.fusion/tasks/{TASK_ID}/attachments/*`
SQLite schema is initialized in `packages/core/src/db.ts` and uses:
- WAL mode (`PRAGMA journal_mode = WAL`)
- Foreign keys (`PRAGMA foreign_keys = ON`)
- `__meta.lastModified` for change detection/polling
### Central storage (multi-project)
- **Central DB**: `~/.fusion/fusion-central.db`
- Schema in `packages/core/src/central-db.ts`
- `projects`, `projectHealth`, `centralActivityLog`, `globalConcurrency`, `nodes`, `peerNodes`, `settingsSyncState`, `__meta`
### Memory files
- OpenClaw-style memory workspace:
- `.fusion/memory/MEMORY.md`
- `.fusion/memory/YYYY-MM-DD.md`
- `.fusion/memory/DREAMS.md`
- The legacy top-level memory file is migration-compatibility only (seed/alias behavior) and is not canonical storage.
### File-based side stores
Some data remains intentionally filesystem-based:
- Agents: `.fusion/agents/*` (`AgentStore`)
- Messages: `.fusion/messages/*` (`MessageStore`)
### Migration from legacy file storage
- Detection + migration: `packages/core/src/db-migrate.ts`
- Migrates legacy task/config/log/archive/automation/agent data into SQLite
- Creates `.bak` backups (for example `task.json.bak`, `config.json.bak`, `archive.jsonl.bak`)
### Archive system
- Archived task snapshots are stored in SQLite `archivedTasks`
- `TaskStore` archive helpers:
- `archiveTaskAndCleanup()`
- `cleanupArchivedTasks()`
- `readArchiveLog()` / `findInArchive()`
- `unarchiveTask()` with restore behavior
---
## 9) Task Lifecycle
Lifecycle constants are defined in `packages/core/src/types.ts`:
- Columns: `planning`, `todo`, `in-progress`, `in-review`, `done`, `archived`
- Transition rules via `VALID_TRANSITIONS`
### Lifecycle flow
```text
planning
│ (Planning processor writes PROMPT.md)
todo
│ (Scheduler selects task, dependencies satisfied)
in-progress
│ (TaskExecutor runs in worktree)
in-review
│ (implementation complete + pre-merge workflow steps)
done
└──────────────▶ archived
```
### Execution detail
- **Planning phase**: the planning processor generates an executable plan
- **Execution phase**: `TaskExecutor` performs implementation, tool calls, tests/build commands
- **Review phase**: optional `reviewStep()` workflow depending on prompt review level (bypassed in fast mode)
- **Merge phase**: `aiMergeTask()` handles merge strategy and post-merge workflow steps
> **Fast Mode:** Tasks with `executionMode: "fast"` bypass the `review_step` tool injection and pre-merge workflow steps. Completion blockers (tests, build, typecheck from PROMPT.md) and post-merge workflow steps remain enforced.
### Step status model
Task steps use statuses: `pending`, `in-progress`, `done`, `skipped`.
### Workflow steps
- Defined in project config as `WorkflowStep`
- **Pre-merge** steps run in executor (`runWorkflowSteps()`) — bypassed in fast mode
- **Post-merge** steps run in merger (`runPostMergeWorkflowSteps()`)
---
## 10) Agent System
Fusion has two complementary agent models:
1. **Task pipeline agents** (planning/executor/reviewer/merger) managed by engine runtime
2. **Persistent registered agents** managed by `AgentStore`
### Persistent agent storage
`packages/core/src/agent-store.ts` persists to:
- `.fusion/agents/{id}.json`
- `.fusion/agents/{id}-heartbeats.jsonl`
- `.fusion/agents/{id}-keys.jsonl`
- `.fusion/agents/{id}-revisions.jsonl`
### Agent spawning from executor
`TaskExecutor` supports hierarchical child agents via:
- `createSpawnAgentTool()`
- `runSpawnedChild()`
- `terminateChildAgent()` / `terminateAllChildren()`
Limits are controlled by project settings (`maxSpawnedAgentsPerParent`, `maxSpawnedAgentsGlobal`).
### Heartbeat monitoring and triggers
`agent-heartbeat.ts` provides:
- Health monitoring and run tracking (`HeartbeatMonitor`)
- Trigger scheduling (`HeartbeatTriggerScheduler`) for:
- timer
- task assignment
- on-demand runs
### Custom instructions
`packages/engine/src/agent-instructions.ts` resolves per-agent instruction text/path with path-traversal and extension validation.
---
## 11) Multi-Project Architecture
Multi-project orchestration spans core + engine.
### Core control plane
- `CentralCore` (`packages/core/src/central-core.ts`) maintains:
- Project registry
- Health metrics
- Unified central activity feed
- Global concurrency state
- Node registry (`local` / `remote`)
### Engine orchestration
- `HybridExecutor` (`packages/engine/src/hybrid-executor.ts`) is the top-level orchestrator
- `ProjectManager` instantiates per-project runtimes and forwards events with project attribution
### Runtime abstraction
Defined in `project-runtime.ts`:
- `ProjectRuntime` interface
- `RuntimeStatus` and `RuntimeMetrics`
Implementations:
- `InProcessRuntime`
- `ChildProcessRuntime`
- `RemoteNodeRuntime`
### IPC protocol (child-process mode)
In `packages/engine/src/ipc/ipc-protocol.ts`:
- Host commands: `START_RUNTIME`, `STOP_RUNTIME`, `GET_STATUS`, `GET_METRICS`, `PING`
- Worker events: `TASK_CREATED`, `TASK_MOVED`, `TASK_UPDATED`, `ERROR_EVENT`, `HEALTH_CHANGED`
### Multi-project runtime diagram
```text
HybridExecutor
┌───────┴────────┐
│ ProjectManager│
└───┬─────────┬───┘
│ │
┌───────────▼───┐ ┌──▼──────────────┐
│InProcessRuntime│ │ChildProcessRuntime│
│(local process) │ │(fork + IPC host) │
└──────┬─────────┘ └──┬───────────────┘
│ │
TaskStore/Scheduler │
child-process-worker
+ InProcessRuntime
```
## Task Routing Architecture
Task dispatch routing is resolved in two layers:
1. **Task routing resolution** (`packages/engine/src/effective-node.ts`)
- `resolveEffectiveNode(task, settings)` applies precedence:
1. `Task.nodeId``task-override`
2. `ProjectSettings.defaultNodeId``project-default`
3. no node set → `local`
2. **Runtime selection** (`packages/engine/src/project-manager.ts`)
- `child-process` isolation always uses `ChildProcessRuntime`
- `in-process` isolation uses `RemoteNodeRuntime` when the registered project host node is remote
- otherwise uses `InProcessRuntime`
### Dispatch flow in scheduler
### Unavailable-node policy
`unavailableNodePolicy` is a validated/stored project setting (`block` default, `fallback-local` allowed) and is enforced during scheduler dispatch when both conditions are true:
- effective routing selected a remote node, and
- `SchedulerOptions.nodeHealthMonitor` is configured.
Behavior summary:
- **`block`** (default): unhealthy node status (`offline`, `error`, `connecting`) blocks dispatch for that poll cycle and keeps the task in `todo`.
- **`fallback-local`**: unhealthy remote node reroutes dispatch to local execution (`effectiveNodeId: null`, `effectiveNodeSource: "local"`).
- unknown node health (`undefined`) is treated as allow/continue.
### Active-task node-override guard
`packages/core/src/node-override-guard.ts` enforces immutable routing overrides for active tasks:
- `validateNodeOverrideChange()` blocks node override updates while task column is `in-progress`
- returns reason `task-in-progress`
`TaskStore.updateTask()` applies this guard before persisting `nodeId` changes.
### Routing activity visibility
Routing decisions are visible in task activity/log entries and in task metadata (`effectiveNodeId`, `effectiveNodeSource`), and surfaced in dashboard routing UI + `fn task show` output.
See also:
- [Settings Reference → Node Routing settings](./settings-reference.md#node-routing-settings-project-scope)
- [Task Management → Node Routing](./task-management.md#node-routing)
- [Multi-Project → Node Routing](./multi-project.md#node-routing)
---
## 12) Settings Hierarchy
Settings are split by scope.
### Global scope
- File: `~/.fusion/settings.json`
- Managed by `GlobalSettingsStore` (`packages/core/src/global-settings.ts`)
- Examples: `themeMode`, `colorTheme`, default model/provider, notification preferences (`ntfy*` legacy fields and `notificationProviders`)
### Project scope
- Stored in per-project config (`config` table + compatibility file `.fusion/config.json`)
- Includes engine/runtime controls (`maxConcurrent`, `autoMerge`, worktree and workflow behavior, etc.)
### Merged view
- `Settings` combines global + project values
- Defaults in `DEFAULT_GLOBAL_SETTINGS` and `DEFAULT_PROJECT_SETTINGS`
- Scope key lists in `GLOBAL_SETTINGS_KEYS` and `PROJECT_SETTINGS_KEYS`
### Model controls
- Per-task model overrides on task fields:
- `modelProvider` / `modelId`
- `validatorModelProvider` / `validatorModelId`
- `planningModelProvider` / `planningModelId`
- `thinkingLevel`
- Reusable presets via `ModelPreset`
- Agent prompt template overrides via `agentPrompts`
---
## 13) Git Integration
Git behavior is implemented primarily in engine executor/merger + dashboard/CLI git APIs.
### Worktree model
- Each active task runs in isolated worktree under `.worktrees/*`
- Executor creates branches like `fusion/{task-id}` (`executor.ts`)
- `WorktreePool` can recycle idle worktrees when enabled
### Merge strategies
- Setting type: `MergeStrategy = "direct" | "pull-request"` (`types.ts`)
- `aiMergeTask()` in `merger.ts` performs merge flow
- Supports workflow-step execution after merge (post-merge phase)
### Conflict handling
`merger.ts` includes conflict classification and auto-resolution helpers:
- lock files (`LOCKFILE_PATTERNS`)
- generated files (`GENERATED_PATTERNS`)
- whitespace-trivial conflicts
### PR and badge integration
- Engine PR monitor: `pr-monitor.ts` and `pr-comment-handler.ts`
- Dashboard GitHub APIs + webhook route in `routes.ts`
- Badge snapshots are streamed via `/api/ws` and `useBadgeWebSocket.ts`
---
## 14) Key Design Decisions
1. **SQLite + WAL for local-first reliability**
- Chosen for simple deployment and strong transactional behavior
- WAL mode enables concurrent readers/writers with low ops overhead
2. **Hybrid persistence (DB + filesystem blobs)**
- Structured metadata in SQLite, large text/artifacts in task directories
- Keeps DB efficient while preserving inspectable task artifacts
3. **Git worktree isolation as core execution primitive**
- Prevents cross-task interference
- Makes concurrent task execution safer
- Enables deterministic cleanup/retry/recovery
4. **Agent-as-tool-caller pattern**
- Engine tools (`task_update`, `task_log`, `review_step`, `spawn_agent`, etc.) create explicit, auditable state transitions
- Prompts are role-specific (`TRIAGE_SYSTEM_PROMPT`, `EXECUTOR_SYSTEM_PROMPT`, etc.)
5. **Separation of real-time channels by concern**
- SSE for broad board/missions/session state updates (`/api/events`)
- Dedicated badge WebSocket (`/api/ws`) for lightweight PR/issue badge snapshots
6. **Multi-project control plane with runtime abstraction**
- `CentralCore` decouples registry/health/concurrency from per-project execution
- `ProjectRuntime` interface allows multiple isolation strategies (in-process, child-process, remote node)
---
## Source Map (quick navigation)
- **Core exports:** `packages/core/src/index.ts`
- **Engine exports:** `packages/engine/src/index.ts`
- **Dashboard exports:** `packages/dashboard/src/index.ts`
- **CLI entry:** `packages/cli/src/bin.ts`
- **Pi extension:** `packages/cli/src/extension.ts`
- **Runtime abstraction:** `packages/engine/src/project-runtime.ts`
- **Multi-project orchestrator:** `packages/engine/src/hybrid-executor.ts`
- **Task routing resolver:** `packages/engine/src/effective-node.ts`
- **Node override guard:** `packages/core/src/node-override-guard.ts`