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