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
53 KiB
Fusion Architecture
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— seepackages/cli/src/commands/dashboard-tui/)
High-level runtime diagram
┌──────────────────────────────┐
│ 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.
@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/enginehas a workspace dependency on@fusion/core(packages/engine/package.json)@fusion/dashboardhas workspace dependencies on@fusion/coreand@fusion/engine(packages/dashboard/package.json)@runfusion/fusionhas workspace development dependencies on@fusion/core,@fusion/engine, and@fusion/dashboardfor composition/build packaging (packages/cli/package.json)@fusion/plugin-sdkdeclares a peer dependency on@fusion/core(packages/plugin-sdk/package.json)@fusion/desktopembeds dashboard assets at build time via script (packages/desktop/scripts/build.ts) but does not declare workspace deps inpackage.json@fusion/mobiletriggers dashboard build/sync via scripts (packages/mobile/package.json) but does not declare workspace deps inpackage.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.)
- Columns:
- 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.statuslifecycle includesdraft(pre-start planning session), thengenerating,awaiting_input, terminalcomplete/error
- SQLite (
- 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
RoadmapStorefor CRUD operations, deterministic ordering, and atomic reorder/move operations- Dashboard API routes in
packages/dashboard/src/roadmap-routes.ts - Exported from
@fusion/corefor downstream persistence/API/UI work
- Roadmap-first entity types (
- 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 historyMissionStore(mission-store.ts) — mission/milestone/slice/feature hierarchyAutomationStore(automation-store.ts) — scheduled jobs with global/project scope isolationMessageStore(message-store.ts) — mailbox/inbox/outbox messagingChatStore(chat-store.ts) — session/message persistence for agent chatInsightStore(insight-store.ts) — project insight persistence + dedupe/run trackingReflectionStore(reflection-store.ts) — agent reflection records and performance snapshotsPluginStore(plugin-store.ts) — plugin registry/state/settings persistenceRoutineStore(routine-store.ts) — recurring routine definitions and run historyRoadmapStore(roadmap-store.ts) — standalone roadmap CRUD with deterministic ordering and atomic reorder/move operationsTodoStore(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) andchat-types.tsprovide session-oriented chat state (chat_sessions,chat_messagestables)- Dashboard chat UX lives in
packages/dashboard/app/components/ChatView.tsxand hooksuseChat.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
AgentCreateInputand 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+InsightStoretransition guards:- single active run per
projectId + trigger(pending|runningconflict) - 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
- single active run per
- 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, andproject_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_runsandresearch_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) withResearchView.tsxin 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 (pluginstable)PluginLoader(plugin-loader.ts) loads/unloads plugin modules and emits lifecycle events- Plugin contributions now include both embedded
uiSlotsand top-leveldashboardViews - Discovery endpoints:
GET /api/plugins/ui-slotsGET /api/plugins/dashboard-views
- Dashboard management routes are implemented in
packages/dashboard/src/plugin-routes.ts
Prompt Overrides
prompt-overrides.tsdefines prompt key catalogs and per-role override validation- Provides override resolution/validation helpers (
resolvePrompt,resolveRolePrompts,assertValidPromptOverrideMap)
Agent Permissions
agent-permissions.tsnormalizes 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):
RoadmapStoreprovides 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.tsfor 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
normalizeRoadmapMilestoneOrderandnormalizeRoadmapFeatureOrder - 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 withroadmapIdFKroadmap_features— feature data withmilestoneIdFKidxRoadmapMilestonesRoadmapOrder— covering index for deterministic milestone orderingidxRoadmapFeaturesMilestoneOrder— 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:
- Layered backend runtime memory (
memory-backend.ts,project-memory.ts)- canonical long-term + layered memory access used by agents and dashboard APIs
- 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 implementationsmemory-compaction.ts— summarization/compaction automationmemory-dreams.ts— background dream processing for agent and project memorymemory-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:
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 promptsmemoryBackendType: Select which backend to use (file,readonly,qmd, or custom). Unknown types are accepted and persisted verbatim; runtime resolution falls back toDEFAULT_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 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 (createdAtascending) 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 (createdAtascending) within each priority tier.StepSessionExecutor(step-session-executor.ts) — per-step sessions + parallel wave executionTaskCompletion(task-completion.ts) — completion gate helpersSpecStaleness(spec-staleness.ts) — stale spec detection utilitiesMissionExecutionLoop(mission-execution-loop.ts) — validator/fix loop orchestrationMissionFeatureSync(mission-feature-sync.ts) — feature↔task status synchronizationMissionAutopilot(mission-autopilot.ts) — mission slice auto-progression
Routine + cron automation
RoutineRunner(routine-runner.ts) — executes routine stepsRoutineScheduler(routine-scheduler.ts) — schedules due routinesCronRunner(cron-runner.ts) — cron-based AI/script jobs
Execution context + skills
SkillResolver(skill-resolver.ts) — resolves active skill sets for sessionsSessionSkillContext(session-skill-context.ts) — skill context materialization per runContextLimitDetector(context-limit-detector.ts) — context-window pressure checksTokenCapDetector(token-cap-detector.ts) — token-cap enforcement checksPluginRunner(plugin-runner.ts) — runtime plugin callback executionAgentRuntime(agent-runtime.ts) — runtime adapter interface contractRuntimeResolution(runtime-resolution.ts) — runtime selection and fallback logicAgentSessionHelpers(agent-session-helpers.ts) — runtime-aware session creation helpers
Concurrency, recovery, and resiliency
AgentSemaphore(concurrency.ts) — slot acquisitionRecoveryPolicy(recovery-policy.ts) — retry/recovery decision policyStuckTaskDetector(stuck-task-detector.ts) — inactivity/loop stall detectionGridlockDetector(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 classificationSelfHealingManager(self-healing.ts) — auto-unpause/maintenance recovery actionsrecoverGhostReviewTasks()is a fallback only for idle, non-terminalin-reviewstates. Terminal/actionable states (notablystatus: "failed") are preserved and not auto-kicked back totodo.recoverMergeableReviewTasks()only re-enqueues truly eligible tasks; retry-exhausted review tasks are skipped to avoid re-enqueue/no-op loops that keep refreshingupdatedAt.
ProjectEnginesettings lifecycle handlers (project-engine.ts) treatenginePausedas a soft pause: clearing it dispatches runtime resume and, whenautoMergeis enabled, performs anin-revieweligibility sweep to requeue mergeable review tasks.UsageLimitPauser(usage-limit-detector.ts) andwithRateLimitRetry(rate-limit-retry.ts)
Worktree and naming helpers
WorktreePool(worktree-pool.ts) — idle worktree reuseWorktreeNames(worktree-names.ts) — deterministic worktree/branch naming
Observability and reflection
AgentLogger(agent-logger.ts) — structured per-agent run loggingRunAudit(run-audit.ts) — mutation audit tracking (DB/git/filesystem)Notifier(notifier.ts) — legacy ntfy compatibility shim (NtfyNotifier) plus shared ntfy helpers- Runtime ownership:
NtfyNotifierno longer owns an independent task-lifecycle listener graph;ProjectEngineinjects the canonicalNotificationServiceinstance so task lifecycle notifications (task:moved,task:updated,task:merged) are emitted through a single path. - Compatibility scope:
NtfyNotifierremains 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.
- Runtime ownership:
NotificationService(notification/notification-service.ts) — provider lifecycle + event dispatch orchestrationNotificationProviderinterface (@fusion/corenotification/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:
HeartbeatMonitorHeartbeatTriggerScheduler(timer, assignment, on-demand triggers)WakeContext/ per-agent runtime config support
Node/mesh runtime services
NodeHealthMonitor(node-health-monitor.ts) — remote node liveness/metrics checksPeerExchangeService(peer-exchange-service.ts) — peer sync orchestration- Process lifecycle ownership:
fn serve/fn dashboardstart a single process-levelPeerExchangeServiceand stop it during shutdown.CentralCore.startDiscovery()is invoked from CLI startup only after HTTP bind completes so discovery advertises the actual listening port.InProcessRuntimestays project-scoped and intentionally does not own mesh startup/shutdown.
Remote access runtime
Operator setup + troubleshooting guide: Remote Access runbook.
remote-access/tunnel-process-manager.tsowns tunnel lifecycle orchestration withspawn-based, non-blocking process supervision.remote-access/types.tsdefines 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.
- Providers:
remote-access/provider-adapters.tsprovides 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>(ephemeraltrycloudflare.comURL, no token)
- Named tunnel mode:
- 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_failedterminal status. ProjectEngine.start()instantiates a per-project tunnel manager and applies startup restore policy fromremoteAccess.lifecycle:- restore is attempted only when
rememberLastRunningis 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.
- restore is attempted only when
- Manual lifecycle remains explicit: only
startRemoteTunnel()/stopRemoteTunnel()transitions mutate runtime state; provider/settings updates do not auto-start tunnels. ProjectEngineexposes restore diagnostics viagetRemoteTunnelRestoreDiagnostics()(applied|skipped|failed+ machine-readable reason).
Multi-runtime support + IPC
- Runtime contracts:
project-runtime.ts - Orchestration:
ProjectManagerandHybridExecutor - Runtime implementations:
runtimes/in-process-runtime.tsruntimes/child-process-runtime.tsruntimes/remote-node-runtime.ts
- IPC protocol/transport:
ipc/ipc-protocol.tsipc/ipc-host.tsipc/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()inpackages/dashboard/src/server.ts - Primary API router:
createApiRoutes()inpackages/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 /api/remote/tunnel/start,/api/remote/tunnel/stop, and/api/remote/tunnel/kill-externalcover tunnel lifecycle and external funnel cleanup./api/remote/statusincludes tunnel status, external funnel detection (externalTunnelwhen managed tunnel is stopped), plus restore diagnostics (restore.outcome+restore.reason) with parity between dashboard and headlessfn serveruntimes.
- Operational runbook (prereqs/security/troubleshooting):
- Remote auth handoff endpoints:
POST /api/remote-access/auth/login-url(daemon-auth protected) issues a tokenized phone-login URL for eitherpersistentorshort-livedmode.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
401JSON 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(DevServerProcessManagerfor spawn/stop/restart/url-detection)dev-server-store.ts(durable.fusion/dev-server.jsonstate + 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; seedocs/dev-server-module-boundary-audit.mdfor the FN-2212 boundary/consolidation audit covering paralleldevserver-*modules.
- Plugin management routes (
plugin-routes.ts) - Insights routes (
insights-routes.ts) - Research routes (
research-routes.ts) —/api/researchsurface 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
RuntimeLoggercontract (packages/dashboard/src/runtime-logger.ts) instead of ad hocconsole.*calls. createServer()acceptsServerOptions.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.ttlMsplus issued expiry metadata. - Remote login links carry auth material in query params (
rtthentokenon 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 dashboardandfn serveremains 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
- Emits
- 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|allreturns an array) - Quick Chat resume uses targeted lookup params:
agentId, optionalmodelProvider+modelId, plusresume=1 - Validation requires
modelProviderandmodelIdtogether; partial model pairs return400 - Targeted lookup returns only the newest matching active session (or
null) to avoid scanning every active session client-side
- Existing list behavior is unchanged (
- 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, andfailedevents - initial connection replays persisted
logHistoryand then follows live process output - companion endpoints:
/api/dev-server/detect,/config,/status,/start,/stop,/restart,/preview-url
- SSE stream emits
- Badge WebSocket:
/api/ws(server.ts,websocket.ts)- Scope-keyed channels (
badge:{scopeKey}:{taskId}) prevent cross-project collisions
- Scope-keyed channels (
- 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 fromTaskDetailModal.tsx): desktop/tabletListViewrenders it inline in the split right pane, while mobile and non-list entry points continue usingTaskDetailModal. - 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
@mediaoverrides and breakpoint definitions
- Per-component stylesheets (56+ files in
packages/dashboard/app/components/)- Each component has a co-located
ComponentName.cssfile - Each
ComponentName.tsximports its stylesheet:import "./ComponentName.css"; - Component-specific CSS rules live in the component's
.cssfile, not in the root stylesheet
- Each component has a co-located
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,ResearchViewDevServerView,InsightsView,DocumentsView,SkillsViewSetupWizardModal,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|filesystemfor filtering - Requires agent ownership or admin access
- Query params:
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/tuipackage - Invoked as part of the
fncommand (no separate package orpnpm tuicommand)
Project selection
packages/cli/src/project-resolver.ts- Resolution order: explicit
--project→ CWD detection (.fusion) → default/fallback logic - Integrates
CentralCoreandProjectManager
- Resolution order: explicit
Pi extension
packages/cli/src/extension.ts- Registers tool set for in-chat task/mission operations
- Uses
TaskStoredirectly for extension-side actions
Binary identity
- Published package defines
fnbinary (packages/cli/package.json) - Running
fnwith 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.lastModifiedfor change detection/polling
Central storage (multi-project)
- Central DB:
~/.fusion/fusion-central.db - Schema in
packages/core/src/central-db.tsprojects,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
.bakbackups (for exampletask.json.bak,config.json.bak,archive.jsonl.bak)
Archive system
- Archived task snapshots are stored in SQLite
archivedTasks TaskStorearchive 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
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:
TaskExecutorperforms 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 thereview_steptool 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:
- Task pipeline agents (planning/executor/reviewer/merger) managed by engine runtime
- 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 orchestratorProjectManagerinstantiates per-project runtimes and forwards events with project attribution
Runtime abstraction
Defined in project-runtime.ts:
ProjectRuntimeinterfaceRuntimeStatusandRuntimeMetrics
Implementations:
InProcessRuntimeChildProcessRuntimeRemoteNodeRuntime
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
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:
- Task routing resolution (
packages/engine/src/effective-node.ts)resolveEffectiveNode(task, settings)applies precedence:Task.nodeId→task-overrideProjectSettings.defaultNodeId→project-default- no node set →
local
- Runtime selection (
packages/engine/src/project-manager.ts)child-processisolation always usesChildProcessRuntimein-processisolation usesRemoteNodeRuntimewhen 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.nodeHealthMonitoris configured.
Behavior summary:
block(default): unhealthy node status (offline,error,connecting) blocks dispatch for that poll cycle and keeps the task intodo.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 isin-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
- Task Management → Node Routing
- Multi-Project → 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 andnotificationProviders)
Project scope
- Stored in per-project config (
configtable + compatibility file.fusion/config.json) - Includes engine/runtime controls (
maxConcurrent,autoMerge, worktree and workflow behavior, etc.)
Merged view
Settingscombines global + project values- Defaults in
DEFAULT_GLOBAL_SETTINGSandDEFAULT_PROJECT_SETTINGS - Scope key lists in
GLOBAL_SETTINGS_KEYSandPROJECT_SETTINGS_KEYS
Model controls
- Per-task model overrides on task fields:
modelProvider/modelIdvalidatorModelProvider/validatorModelIdplanningModelProvider/planningModelIdthinkingLevel
- 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) WorktreePoolcan recycle idle worktrees when enabled
Merge strategies
- Setting type:
MergeStrategy = "direct" | "pull-request"(types.ts) aiMergeTask()inmerger.tsperforms 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.tsandpr-comment-handler.ts - Dashboard GitHub APIs + webhook route in
routes.ts - Badge snapshots are streamed via
/api/wsanduseBadgeWebSocket.ts
14) Key Design Decisions
-
SQLite + WAL for local-first reliability
- Chosen for simple deployment and strong transactional behavior
- WAL mode enables concurrent readers/writers with low ops overhead
-
Hybrid persistence (DB + filesystem blobs)
- Structured metadata in SQLite, large text/artifacts in task directories
- Keeps DB efficient while preserving inspectable task artifacts
-
Git worktree isolation as core execution primitive
- Prevents cross-task interference
- Makes concurrent task execution safer
- Enables deterministic cleanup/retry/recovery
-
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.)
- Engine tools (
-
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
- SSE for broad board/missions/session state updates (
-
Multi-project control plane with runtime abstraction
CentralCoredecouples registry/health/concurrency from per-project executionProjectRuntimeinterface 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