The merge normalizes button utility classes in AgentsView, swapping 12 lines of CSS class references for their standardized counterparts for consistency and maintainability. Fusion-Task-Id: FN-3448
64 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) - Mobile shell (
@fusion/mobile) - Terminal dashboard (part of
@runfusion/fusion— seepackages/cli/src/commands/dashboard-tui/)
Native shells expose a shared host-neutral bridge at window.fusionShell for first-run shell onboarding, connection profile persistence, and active shell mode/profile state. The dashboard consumes window.fusionShell when present and degrades cleanly in plain web/PWA mode.
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 - Main
useChatsession restore/recovery must not reset the active thread during session-list refresh orchat:session:updatedmetadata churn while a response is in flight. - When the active session is still generating after reload/reconnect (
isGenerating: true),useChatkeeps recovery streaming state alive ("Connecting…") until the assistant output is observed via SSE or reloaded from messages. - 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) POST /api/insights/:id/create-taskremains a draft-payload endpoint (returnssuggestedTitle/suggestedDescription); the dashboardInsightsViewnow uses that payload to create a real task through the normal app task-creation path (column: triage,sourceType: dashboard_ui, source metadata indicating insights origin)- 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, exports, lifecycle metadata, and retry/cancel state transitions.- Backed by
research_runs,research_exports, andresearch_run_events. - 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. - CLI surface is implemented in
packages/cli/src/commands/research.tswith six subcommands (create, list, show, export, cancel, retry). - Agent tool surface is exposed via
packages/cli/src/extension.ts(fn_research_run,fn_research_list,fn_research_get,fn_research_cancel,fn_research_retry). - Boundary contract (FN-3292):
ResearchStoreowns persistence and lifecycle writes (status transitions, lifecycle event log rows, sources/results snapshots).ResearchStepRunnerowns provider I/O concerns only (provider selection, timeout/abort/provider-error classification, synthesis call execution); it does not read/write run state.ResearchOrchestratorowns sequencing and failure policy (phase progression, provider fallback, partial-step continuation, terminal status choice) and interacts with store only through public store methods.- Provider substitution must remain data-driven: source metadata can carry provider identity, and fetching should resolve providers per source rather than relying on provider ordering.
- 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
Docker Node Provisioning
Fusion has a managed Docker node provisioning subsystem spanning @fusion/core services and dashboard routes.
Core services:
DockerClientService(packages/core/src/docker-client.ts)- Creates Dockerode clients from host settings.
- Supports default local daemon, named Docker
context, or explicithostwith optional TLS fields. - Host/TLS inputs:
context,host,tlsVerify,tlsCaPath,tlsCertPath,tlsKeyPath.
DockerProvisioningService(packages/core/src/docker-provisioning.ts)- Handles initial container lifecycle actions (provision/deprovision/start/stop/restart/status).
- Provisioning creates and starts a container first, then route-level orchestration registers metadata/node records.
MeshConfigGenerator(packages/core/src/mesh-config-generator.ts)- Generates mesh env/config, applies config by recreating the container, registers the node into mesh state, then health-checks until online or timeout.
Route boundary (dashboard):
register-docker-provisioning-routes.tsowns initial container lifecycle endpoints (/api/docker/provision,/api/docker/deprovision, and per-container start/stop/restart/status).register-docker-node-routes.tsowns managed-node metadata + mesh configuration endpoints (for example/api/docker/nodes/:managedId/apply-mesh-configand mesh-status checks) after a container is provisioned.
Provisioning lifecycle (implemented flow):
- Container provisioning: dashboard provisioning route calls
DockerProvisioningService.provision()to create/start a managed container. - Mesh config generation:
MeshConfigGenerator.generateConfig()resolves API key, reachable URL, and mesh env vars. - Mesh config application:
MeshConfigGenerator.applyConfig()callsDockerClientService.recreateContainer()so env vars are applied to a recreated container. - Node registration:
MeshConfigGenerator.registerInMesh()creates/links a remoteNodeConfigentry. - Health check: mesh registration flow polls
checkNodeHealth()until online or timeout.
Port convention:
- Managed Docker mesh-node containers default to
4041(DEFAULT_CONTAINER_PORTinmesh-config-generator.ts). 4040remains reserved for the production dashboard and should not be documented as the managed mesh-node default.
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.
QMD-backed memory behavior also applies to agent-private memory workspaces under .fusion/agent-memory/{agentId}/:
- Agent memory search normalizes QMD hit paths (including
qmd://..., absolute paths, and relative filenames) into canonical readable workspace paths (MEMORY.md,DREAMS.md,YYYY-MM-DD.md) so results can be passed directly intofn_memory_get. - Agent-memory writes from tool and non-tool paths (including
processAgentMemoryDreams()) schedule agent-specific QMD refreshes so new dreams/long-term updates remain discoverable without manual reindexing.
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- Canonical replication/write-coordination contract:
docs/shared-mesh-protocol.md- Defines protocol versioning, write classes, quorum/ack semantics, lease epochs/fencing, offline queue/replay, reconciliation outcomes, restart recovery hooks, and degraded-read staleness metadata.
- Existing
/api/mesh/syncand settings-sync payloads remain the active exchange primitives while follow-on runtime tasks implement full v1 coordinator/quorum behavior.
- 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 app CPU percentage and host memory rendered as numeric values with visual usage bars 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 - Board column ordering (board view only):
todocards mirror scheduler pickup order (priority descending, thencreatedAtascending/FIFO within each priority tier, then task ID ascending).triage,in-progress, andarchiveduse priority descending then task ID ascending, with missing/invalid priority normalized tonormal.doneis completion-recency ordered (columnMovedAt, thenupdatedAt, thencreatedAt, newest first). Inin-review, merge-active tasks (status === "merging","merging-pr", or"merging-fix") are pinned above non-merging tasks, with priority-then-ID ordering within each group. - 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. - In desktop split mode,
ListViewnow uses a compact sidebar-first control layout (count/actions/summary chips + collapsible "View options" panel) to keep list controls dense alongside the inline detail pane; mobile keeps the card-first flow with a toolbar "View options" entry point for the same visibility/filter toggles. - 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
Custom Provider endpoints
Custom-provider settings routes are registered in register-custom-provider-routes.ts.
| Method | Path | Description |
|---|---|---|
| GET | /api/custom-providers |
List configured custom providers from global settings with API keys masked in the response payload. |
| POST | /api/custom-providers |
Create a custom provider (name, apiType, baseUrl, optional apiKey and models) and return the new provider with masked API key. |
| PUT | /api/custom-providers/:id |
Update an existing custom provider by ID (partial updates supported) and return the sanitized provider payload. |
| DELETE | /api/custom-providers/:id |
Delete a custom provider by ID and return a success envelope. |
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). |
Docker provisioning endpoints
Initial container provisioning and lifecycle routes are registered by register-docker-provisioning-routes.ts.
| Method | Path | Description |
|---|---|---|
| POST | /api/docker/provision |
Provision and start a managed Docker container. |
| POST | /api/docker/deprovision |
Stop/remove a managed Docker container. |
| POST | /api/docker/containers/:containerId/start |
Start an existing container. |
| POST | /api/docker/containers/:containerId/stop |
Stop a running container. |
| POST | /api/docker/containers/:containerId/restart |
Restart a container. |
| GET | /api/docker/containers/:containerId/status |
Read runtime status for a container. |
Mesh configuration and post-provision managed-node operations are registered separately in register-docker-node-routes.ts (for example /api/docker/nodes/:managedId/apply-mesh-config and /api/docker/nodes/:managedId/mesh-status).
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.
Git REST API endpoints
Git dashboard routes are registered in register-git-github.ts.
| Method | Path | Description |
|---|---|---|
| GET | /api/git/remotes |
List GitHub remotes parsed from git remote -v output. |
| GET | /api/git/remotes/detailed |
List all remotes with fetch/push URLs. |
| POST | /api/git/remotes |
Add a new remote (name, url). |
| DELETE | /api/git/remotes/:name |
Remove an existing remote by name. |
| PATCH | /api/git/remotes/:name |
Rename a remote (newName). |
| PUT | /api/git/remotes/:name/url |
Update a remote URL. |
| GET | /api/git/status |
Return branch, short commit, dirty state, and ahead/behind counts. |
| GET | /api/git/commits |
Return recent commits (?limit= capped at 100). |
| GET | /api/git/commits/:hash/diff |
Return commit stat + patch for a validated commit hash. |
| GET | /api/git/commits/ahead |
Return local commits ahead of upstream (empty when upstream is not configured). |
| GET | /api/git/remotes/:name/commits |
Return commits for a remote ref (?ref= optional, ?limit= max 50, with remote HEAD/main/master fallback resolution). |
| GET | /api/git/branches |
List local branches with current/tracking metadata and last commit date. |
| GET | /api/git/branches/:name/commits |
Return commits for a branch (?limit= default 10, max 100). |
| GET | /api/git/worktrees |
List worktrees with branch/path metadata and task association when available. |
| POST | /api/git/branches |
Create a branch from HEAD or an optional base ref. |
| POST | /api/git/branches/:name/checkout |
Checkout an existing branch. |
| DELETE | /api/git/branches/:name |
Delete a branch (?force=true allows deleting unmerged branches). |
| POST | /api/git/fetch |
Fetch from a remote (remote defaults to origin). |
| POST | /api/git/pull |
Pull the current branch and return structured conflict metadata on merge conflicts. |
| POST | /api/git/push |
Push the current branch. |
| GET | /api/git/stashes |
List stash entries. |
| POST | /api/git/stashes |
Create a stash with an optional message. |
| POST | /api/git/stashes/:index/apply |
Apply a stash by index (optionally drop after apply via drop: true). |
| DELETE | /api/git/stashes/:index |
Drop a stash by index. |
| GET | /api/git/diff |
Return unstaged working-tree diff text. |
| GET | /api/git/diff/file |
Return staged or unstaged diff for one file (path + `staged=true |
| GET | /api/git/changes |
Return staged and unstaged file change summary. |
| POST | /api/git/stage |
Stage specified files. |
| POST | /api/git/unstage |
Unstage specified files. |
| POST | /api/git/commit |
Create a commit from staged changes with a required message. |
| POST | /api/git/discard |
Discard working-tree changes for specified files. |
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