Architecture
Contents
- Overview
- Main Components
- Stuck Detection
- Crash Recovery
- Tool Adapters
- Source Modules
- Core Data Flow
- Canonical Context Principle
Overview
VibeFlow is a local-first tool composed of four main layers:
npm CLI Launcher
↓
Local Web UI
↓
Workflow Orchestrator Core
↓
Tool Adapters: Claude Code / Codex CLI / Copilot CLI / OpenCode / Antigravity CLI
The system should run on the user’s machine and should not send source code to a remote service controlled by the tool owner unless the user explicitly configures it.
Main components
1. npm CLI Launcher
Responsibilities:
- Start the local web server.
- Open the browser automatically.
- Check local dependencies.
- Install or guide installation of optional tools.
- Initialize workflow files inside the target repo.
Example commands:
npx @magicpro97/vibeflow
vf doctor
vf init
vf ui
vf run claude
vf run codex
vf run copilot
vf run antigravity
vf skills list
vf tools status
2. Local Web UI
Responsibilities:
- Collect project information.
- Ask structured questions.
- Let user connect sources.
- Show detected skills and missing skills.
- Show generated instructions.
- Show execution logs, diffs, tests, risks, and final report.
3. Workflow Orchestrator Core
Responsibilities:
- Act as the main agent coordinator.
- Classify task type and risk level.
- Resolve sources and file readers.
- Select local or external skills.
- Generate project context files.
- Generate tool-specific adapters.
- Dispatch Claude Code, Codex, Copilot, OpenCode, or Antigravity CLI.
- Verify output.
- Propose skill updates.
Stuck Detection
The orchestrator runs a StuckDetector per in-flight work unit to surface hung engines
without aborting sibling lanes. Three configurable detection patterns:
- Stalled: no progress event within
stallSeconds(default 120s). - Looping: same engine output repeated
loopThresholdtimes (default 3). - Evidence-stuck: evidence count unchanged across
evidenceStallRounds + 1checks (default 2 rounds → 3 checks).
The detector is driven by recordProgress(), recordOutput(), and recordEvidenceCount() calls
from the orchestrator’s per-unit dispatch loop. check() returns a StuckState with a reasons
array — consumer decides whether to warn, throttle, or escalate.
See src/orchestrator/stuck-detector.ts.
Crash Recovery
The orchestrator persists a marker (~/.vibeflow/markers/<unit>.json) for every unit
it dispatches, plus an append-only timeline ledger (<unit>.timeline) next to it. These
files are the source of truth for “what was the engine doing when the process died” — they
survive a crash or Ctrl-C intact.
vf status reads them back (never re-running anything): a table of UNIT / STATUS / CONF /
EVID / UPDATED / ISSUE across all units, highlighting the running unit (the crash point)
and flagging a done marker that published no evidence. vf status timeline <unit> dumps
that unit’s full transition ledger; vf status --json emits machine-readable output.
See src/commands/status.ts, src/orchestrator/marker.ts, src/orchestrator/timeline.ts.
Dispatch captures the engine’s session_id (claude JSON envelope) into DispatchMarker.engineSessionId, persisted for crash-resume. PR2a (#618 PR2a) wires resumeSessionId through the dispatch layer so a claude unit can resume its prior session (claude -p -r <id>) instead of a fresh run. PR2b-1 wires vf orchestrate --resume: a crashed unit (marker running/blocked/failed) with a persisted engineSessionId resumes that claude session via the PR2a dispatch path; without --resume, or for codex/copilot (no persisted id), the unit re-runs fresh. PR2b-2 extends capture+resume to codex (codex exec --json - → thread_id; codex exec resume <id>). Copilot has no by-id resume and always runs fresh. The exact per-engine invocation flags, output-shape assumptions, verified CLI versions, and a re-verify procedure for CLI bumps live in docs/ENGINE-COMPAT.md.
Wave Handoff
Units declare depends_on (carried from the planner’s proposal onto the WorkUnit).
scheduleWaves topologically orders them into dependency waves: each wave holds only
units whose deps are already satisfied, and units within a wave run concurrently.
dispatchInWaves runs the waves in order — after every wave, each finished unit’s
derived one-line summary (deriveHandoff: name + status + evidence count, sanitized and
capped at 500 bytes) is recorded and injected as an ## Upstream context block into its
dependents’ dispatch prompt in the next wave. This is best-effort context, not a contract.
With no depends_on, scheduleWaves returns a single wave ⇒ one dispatch call ⇒ identical
to the pre-#612 behavior.
See src/orchestrator/waves.ts, src/orchestrator/handoff.ts, src/orchestrator/plan.ts.
Tool Adapters
Adapters translate canonical workflow context into each engine’s expected format. Each
adapter also exposes a quota() and probe() capability used by the preflight gate
(see src/preflight-delegate.ts).
Canonical Context
↓
Claude Adapter → CLAUDE.md + .claude/agents + .claude/skills
Codex Adapter → AGENTS.md + .codex/config.toml + prompt injection
Copilot Adapter → AGENTS.md + .github/copilot-instructions.md + prompt injection
OpenCode Adapter → AGENTS.md + opencode.json + .opencode/plugins/vf-guard.ts
Antigravity Adapter → AGENTS.md + .agents/agents + .agents/skills + .agents/mcp_config.json + .agents/hooks.json
Interactive Plan Review (PR1)
The plan review subsystem persists plan markdown as file-backed immutable revisions
under .vibeflow/plan-review/. Each revision is a write-once JSON file keyed by UUID;
index.json tracks the current revision pointer per workflow. Blocks are parsed
server-side into typed segments (heading, paragraph, list-run, fenced-code,
fenced-mermaid) and rendered by the client through a safe semantic renderer
(plan-render.ts) that HTML-escapes all content — no v-html.
Selection anchors (BlockAnchor) provide the groundwork for threaded comments (PR2)
without storing comment data in PR1. Mermaid sources are preserved as fallback text;
no mermaid runtime is loaded.
API surface: GET /api/plan-review and POST /api/plan-review/revisions, both
CSRF-guarded, with scope caps (1,000 blocks, 1 MB markdown, 100 KB per block).
See src/plan-review/, src/server/plan-review.ts, src/ui/src/lib/plan-render.ts,
src/ui/src/lib/plan-anchor.ts, and docs/adr/ADR-007-interactive-plan-review.md.
Source modules
The web UI also exposes a read-only diff preview endpoint (GET /api/dashboard/diff)
that returns workflow-level changed-file summaries and scope-limited work-unit diffs.
Git operations use spawnSync with argv arrays (no shell interpolation).
See src/server/dashboard-diff.ts and docs/WEB_UI_DESIGN.md section 10.
src/probe-cache.ts # 60s stable / 5s short-TTL probe-result cache (vf doctor)
src/engine-quota.ts # parse claude / codex / copilot quota JSON; exhaustion signal
src/preflight-delegate.ts # 3-layer gate (presence → auth → quota) with auto-fallback
src/skills/sync.ts # canonical .vibeflow/skills → engine mirrors (pointer | full)
src/skills/importer.ts # Context7 + local-dir import (temp → validate → promote → sync)
src/skills/validator.ts # Anthropic skill-creator standard validation
src/ai-init.ts # writes canonical context files + engine instruction files
src/plan-review/ # immutable revision store, blocks parser, types
Core data flow
User input
↓
Intake schema
↓
Source resolver
↓
Skill resolver
↓
Document/file reader skills
↓
Normalized context
↓
Planning + debate + task split
↓
Engine adapter
↓
CLI execution
↓
Hooks + verification
↓
Result report
↓
Skill evolution proposal
Pipeline observability data flow (ADR-006)
Registry + WORKFLOW_STATE + durable logs (current.log)
→ buildDashboardItems() — read-only aggregation
→ GET /api/dashboard/workflows — snapshot JSON
→ GET /api/dashboard/logs — selected workflow durable events
→ SSE /api/dashboard/logs/stream — live tail of selected workflow log
→ Vue WorkflowDashboard (polling composable)
→ PipelineGraph (CSS Grid + SVG) + WorkflowLogPane (scoped drawer)
Events carry optional workflowId (state.task_id) and repoPath for
correlation. Legacy events without these fields are still parseable and
visible within their repo’s log file. The selection resolver validates
repoPath against the registry, workflowId against the state, and
unit against known unit names — all server-side.
Canonical context principle
The system should not maintain three independent instruction systems. It should maintain one canonical source:
.vibeflow/PROJECT_CONTEXT.md
.vibeflow/REQUIREMENTS.md
.vibeflow/TASK_CONTEXT.md
.vibeflow/WORKFLOW_POLICY.md
.vibeflow/SKILL_INDEX.md
Then it generates:
CLAUDE.md
AGENTS.md
.github/copilot-instructions.md
.github/instructions/*.instructions.md
This prevents instruction drift between Claude Code, Codex, and Copilot CLI.
Related: Security Model · Agent Orchestration Policy Edit this page on GitHub