🦌 Autonomous Engineering. Manifested Intent. 🌊
"We are no longer just programmers; we are Vibe Coders and Agentic Orchestrators." — Inspired by Andrej Karpathy, March 2026
VibeFlow is an experimental take on an autonomous engineering system that implements the Vibe Coding paradigm via DeerFlow. You write high-level intent in a vibe.md manifest (or vibe one) — VibeFlow decomposes it into tasks, executes them via MCP tool calls, and self-corrects using the AutoResearch ratchet loop. It runs indefinitely until your success metrics are met.
To work with Snowflake authentication and the Cortex REST API, we've forked the main DeerFlow project here: https://github.com/patreilly/deer-flow. If you're not using Snowflake for your inferences, use the main repository here.
- Directory Layout
- Architecture
- Prerequisites
- First-time Setup
- Subsequent Runs
- Quick Start
- The Vibe Manifest
- Acceptance Criteria
- Configuration
- How It Works
- Troubleshooting
- Source Architecture
home/vibeflow/
├── README.md ← you are here
├── .gitignore
├── vibeflow/ ← VibeFlow source (package.json lives HERE)
│ ├── src/
│ ├── vibe.md
│ ├── math-helper-vibe.md
│ ├── docker-compose.deerflow.yml
│ └── ...
└── ../deer-flow/ ← patreilly/deer-flow fork (cloned alongside)
Common mistake: running
npm startfromvibeflow/instead ofvibeflow/vibeflow/.
┌─────────────────────────────────────────────────────────────────────┐
│ npm start │
│ (tsx src/index.ts) │
└────────────────────────────┬────────────────────────────────────────┘
│
┌──────────────▼──────────────┐
│ index.ts │
│ Pipeline orchestration │
│ Interactive prompts │
│ Env var configuration │
└──┬──────────┬──────────┬───┘
│ │ │
┌────────────▼──┐ ┌────▼──────┐ └──────────────┐
│ mcp-client.ts │ │ratchet.ts │ ┌────────▼────────┐
│ │ │ │ │ orchestrator.ts │
│ Connects MCP │ │ Quality │ │ │
│ servers at │ │ gate — │ │ Task tree mgmt │
│ startup: │ │ only │ │ Checkpoint save/ │
│ • filesystem │ │ commits │ │ restore │
│ • context7 │ │ if pass │ │ Short/long path │
│ • fetch │ │ rate │ │ routing │
│ • duckduckgo │ │ improved │ │ File writes via │
│ │ │ │ │ fs.promises │
│ Audit log → │ └───────────┘ └────────┬────────┘
│ .vibeflow/ │ │
│ logs/ │ ┌────────────────────────┘
└───────────────┘ │
┌──────────▼──────────┐
│ verifier.ts │
│ │
│ generateCriteria() │
│ → LLM derives │
│ AcceptanceCriteria│
│ from manifest │
│ │
│ verify() │
│ • executable: │
│ bash cmd exit 0 │
│ • evaluative: │
│ LLM judge │
│ │
│ verifyAndFix() │
│ Option B fix loop │
│ (one persistent │
│ DeerFlow thread) │
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ deerflow-client.ts │
│ │
│ HTTP client for │
│ ByteDance DeerFlow: │
│ • POST /threads │
│ • /runs/stream │
│ (short path) │
│ • /runs + polling │
│ (long path) │
└─────────────────────┘
Source files:
| File | Role |
|---|---|
src/index.ts |
Entry point — prompts, pipeline sequencing, env config |
src/orchestrator.ts |
Task tree, DeerFlow dispatch, checkpoint save/restore, file writes |
src/verifier.ts |
AcceptanceCriteria generation, criterion evaluation, fix loop |
src/deerflow-client.ts |
DeerFlow HTTP client (short-path streaming + long-path polling) |
src/mcp-client.ts |
MCP server connections, tool dispatch, audit logging |
src/ratchet.ts |
Quality gate — tracks pass rate history, prevents regressions |
| Requirement | Version | Notes |
|---|---|---|
| Node.js | >= 20.x | ESM support required |
| Python | >= 3.9 | For research_loop.py |
| Docker | >= 24.x | Required to run DeerFlow |
| Anthropic API key | — | Or Snowflake Cortex credentials |
VibeFlow runs on top of DeerFlow — ByteDance's orchestration engine.
To work with Snowflake authentication and the Cortex REST API, we've forked the main DeerFlow project here: https://github.com/patreilly/deer-flow.
The fork is built locally from source; docker-compose.deerflow.yml handles this automatically. If you've got your own API keys from Anthropic or OpenAI, just clone the original, main repository and use vanilla DeerFlow.
# Run from the directory that contains vibeflow/
git clone https://github.com/patreilly/deer-flow ../deer-flow
# The Snowflake Cortex provider lives on a feature branch — check it out
cd ../deer-flow
git checkout feature/snowflake-cortex-provider# still in deer-flow directory
# Generate config.yaml from the template
# macOS: must use python3, not python
make config PYTHON=python3Open ../deer-flow/config.yaml and add at least one active model under models:. Without this the langgraph container crashes at startup.
Option A — Direct Anthropic API:
# ../deer-flow/config.yaml
models:
- name: claude-sonnet-4-6
display_name: Claude Sonnet 4.6
use: deerflow.models.claude_provider:ClaudeChatModel
model: claude-sonnet-4-6
api_key: $ANTHROPIC_API_KEY
max_tokens: 16384
supports_vision: true
supports_thinking: true
when_thinking_enabled:
thinking:
type: enabledexport ANTHROPIC_API_KEY=sk-ant-...Option B — Claude via Snowflake Cortex (PAT auth):
models:
- name: snowflake-claude-sonnet
display_name: Claude Sonnet 4.6 (Snowflake Cortex)
use: deerflow.models.snowflake_claude_provider:SnowflakeClaudeChatModel
model: claude-sonnet-4-6
snowflake_account: $SNOWFLAKE_ACCOUNT
snowflake_pat_token: $SNOWFLAKE_PAT_TOKEN
max_tokens: 16384
supports_vision: true
supports_thinking: true
when_thinking_enabled:
thinking:
type: enabledexport SNOWFLAKE_ACCOUNT=myorg-myaccount # first segment only, no region suffix
export SNOWFLAKE_PAT_TOKEN=pat-...Option C — Claude via Snowflake Cortex (key-pair JWT, auto-refreshes):
models:
- name: snowflake-claude-sonnet
display_name: Claude Sonnet 4.6 (Snowflake Cortex)
use: deerflow.models.snowflake_claude_provider:SnowflakeClaudeChatModel
model: claude-sonnet-4-6
snowflake_account: $SNOWFLAKE_ACCOUNT
snowflake_user: $SNOWFLAKE_USER
snowflake_private_key_path: $SNOWFLAKE_PRIVATE_KEY_PATH
snowflake_private_key_passphrase: $SNOWFLAKE_PRIVATE_KEY_PASSPHRASE # omit if unencrypted
max_tokens: 16384
supports_vision: trueexport SNOWFLAKE_ACCOUNT=myorg-myaccount
export SNOWFLAKE_USER=SERVICE_USER
export SNOWFLAKE_PRIVATE_KEY_PATH=/path/to/rsa_key.p8cd ../vibeflow/vibeflow # where package.json lives
# Create extensions_config.json from the example BEFORE starting Docker.
# If this file is missing, Docker will create it as a directory and both
# containers will fail to start with "[Errno 21] Is a directory" errors.
cp ../../deer-flow/extensions_config.example.json ../../deer-flow/extensions_config.json
# First run — builds Docker images from source (takes a few minutes)
docker compose -f docker-compose.deerflow.yml up -d --build
# Verify both services are healthy
curl http://localhost:2024/ok # → {"status":"ok"}
curl http://localhost:8001/health# Still in vibeflow/vibeflow/
npm ci # clean install from package-lock.json (use npm install only when adding/updating packages)
uv sync # installs Python deps from uv.lock (anthropic, etc.)VIBE_MANIFEST=math-helper-vibe.md npm startcd vibeflow/vibeflow
# Start DeerFlow (no rebuild needed unless code changed)
docker compose -f docker-compose.deerflow.yml up -d
# Rebuild after pulling fork updates
docker compose -f docker-compose.deerflow.yml up -d --build
# Run VibeFlow
VIBE_MANIFEST=math-helper-vibe.md npm start# Run against the default vibe.md
npm start
# Custom manifest
VIBE_MANIFEST=my-project-vibe.md npm start
# Dry run — prints the task plan without executing anything
VIBE_DRY_RUN=1 VIBE_MANIFEST=my-project-vibe.md npm start
# Force a fresh run — clears checkpoints AND output directory without prompting
VIBE_FRESH=1 VIBE_MANIFEST=my-project-vibe.md npm start
# Skip the output directory prompt (useful in CI or scripted workflows)
VIBE_OUTPUT_DIR=/path/to/my-project VIBE_MANIFEST=my-project-vibe.md npm start
# Point at a remote DeerFlow instance
DEERFLOW_URL=http://my-server:2024 npm startVibeFlow checks DeerFlow is reachable on startup and exits immediately with an error if not.
npm run research
# Or directly
python3 research_loop.py --manifest vibe.md --max-iterations 5
python3 research_loop.py --dry-runvibe.md is the source of truth for your project. VibeFlow reads it before every task cycle. You can start with something vague and ambiguous and let VibeFlow make some reasonable assumptions. Or, ask your favorite coding assistant to make one for you with the below minimum useful structure:
## Project Goal
Build a REST API for user authentication with JWT tokens.
## Success Metrics
| Metric | Target | Measurement Method |
|---|---|---|
| Tests pass | 100% | `npm test` |
| Builds clean | yes | `npm run build` |
## Deterministic Guardrails
1. No deleting files without confirmation
2. All changes within the project directory
## Current Phase
- [ ] Scaffold the project
- [ ] Implement /login endpoint
- [ ] Add JWT middlewareThe unchecked items in Current Phase are the active work queue. See math-helper-vibe.md for a full example.
Before writing a single line of code, VibeFlow attempts to answer the question: "What does success look like for this project?" The answer becomes the AcceptanceCriteria — the specification that every task is built to satisfy, and that the verification phase evaluates after the build completes.
This works for any project type, not just code:
| Project type | Verification example |
|---|---|
| Web app | npm run build exits 0; curl localhost:PORT returns HTTP 200 |
| dbt pipeline | dbt run && dbt test both exit 0 |
| ML training script | python train.py --smoke-test exits 0; metrics.json has loss key |
| Rust CLI | cargo build --release exits 0; ./binary --help exits 0 |
| Blog post | Word count ≥ 800; LLM judge confirms topic coverage and reading level |
| Research report | LLM judge confirms argument coherence and citation quality |
VibeFlow calls DeerFlow with the full manifest and asks it to derive criteria. Whether the manifest is a one-liner ("build me a todo app") or a comprehensive spec, the same process runs:
- Explicit criteria are extracted verbatim from the manifest (success metrics, guardrails, output requirements).
- Implicit criteria are inferred from domain knowledge — things any reasonable user would expect. These are flagged
[inferred]and shown with the assumption the LLM made. - Each criterion is assigned a weight (0–1, summing to 1.0) and a verification mode:
executable— verified by running a bash command; exit 0 = passevaluative— verified by an LLM judge reading the output files against a rubrichybrid— both
Criteria are shown before anything is built. On an interactive terminal you'll see:
Acceptance Criteria
───────────────────
Intent: Build a browser-based math practice app for 2nd-grade students using vanilla TypeScript and Vite
1. [executable] npm install exits 0 w=0.10 [explicit]
2. [executable] npm run build exits 0 w=0.15 [explicit]
3. [executable] Dev server responds HTTP 200 on localhost:5173 w=0.20 [inferred]
Assumed: a Vite project is expected to be locally runnable
4. [evaluative] App implements all 4 core features from the manifest w=0.30 [explicit]
5. [evaluative] Guardrails respected (no negative language, no persistent storage) w=0.25 [explicit]
Build to these criteria? [Y/n]
If the inferred intent is wrong, exit here, edit vibe.md, and re-run. This catches bad inference cheaply — before the build, not after.
Set VIBE_AUTO_APPROVE_CRITERIA=1 to skip the prompt in CI or scripted workflows.
After all tasks complete, VibeFlow evaluates every criterion. If any fail, it opens one persistent DeerFlow thread and iterates:
Attempt 1: "These criteria failed: [details + file listing]. Fix them."
→ DeerFlow updates files
→ re-verify failed criteria
Attempt 2: "Attempt 1 still has failures: [new details]. Fix remaining."
→ same thread — LLM sees its previous attempt in context
→ re-verify
Attempt 3: (same pattern, last chance)
Because the thread persists across attempts, the LLM has full history of what it already tried and won't repeat the same approach. The final weighted pass rate goes into the ratchet as the real testPassRate.
DeerFlow connection:
| Variable | Default | Description |
|---|---|---|
DEERFLOW_URL |
http://localhost:2024 |
LangGraph server |
DEERFLOW_GATEWAY_URL |
http://localhost:8001 |
Gateway API |
DEERFLOW_ASSISTANT_ID |
lead_agent |
Agent name to target |
DEERFLOW_POLL_MS |
3000 |
Background run poll interval |
VibeFlow behaviour:
| Variable | Default | Description |
|---|---|---|
VIBE_MANIFEST |
vibe.md |
Path to intent manifest |
VIBE_OUTPUT_DIR |
(prompted) | Output directory — skips the interactive prompt |
VIBE_DRY_RUN |
false |
Print task plan without executing |
VIBE_MAX_TASKS |
20 |
Max tasks per run |
VIBE_FRESH |
false |
Clear checkpoints and output directory without prompting, then start fresh |
VIBE_AUTO_APPROVE_CRITERIA |
false |
Skip criteria review prompt (CI / non-interactive use) |
VIBE_SKIP_VERIFY |
false |
Skip the verify+fix phase entirely |
VIBE_MAX_FIX_RETRIES |
10 |
Max iterations of the criteria fix loop |
VIBE_FEEDBACK |
(unset) | Free-form feedback string — VibeFlow asks DeerFlow to update the manifest before running; shows a Refinement Log for confirmation |
MCP_FILESYSTEM_SERVER |
(unset) | Command to launch MCP filesystem server (legacy fallback) |
MCP_SSE_URL |
(unset) | URL for SSE-transport MCP server (legacy fallback) |
Snowflake Cortex (optional):
| Variable | Purpose |
|---|---|
SNOWFLAKE_ACCOUNT |
Account identifier — first segment only, e.g. myorg-myaccount |
SNOWFLAKE_PAT_TOKEN |
Programmatic Access Token (simplest auth) |
SNOWFLAKE_USER |
Username for key-pair JWT auth |
SNOWFLAKE_PRIVATE_KEY_PATH |
Path to PEM private key |
SNOWFLAKE_PRIVATE_KEY_PASSPHRASE |
Passphrase if key is encrypted |
Snowflake credentials are passed into the Docker containers automatically — set them in your shell before running docker compose.
VibeFlow connects to MCP servers at startup for DeerFlow's tool use (fetching docs, searching the web, etc.). Configure them in vibeflow/mcp.config.json.
Output directory
VibeFlow prompts you for an output directory at the start of every run:
Where should VibeFlow write the generated project files?
Enter an absolute path (/Users/you/projects/my-app)
or a path relative to the current directory (../my-app, ./output).
Press Enter to use the default: /Users/you/code/math-helper
Output directory:
The default is derived from your manifest filename — math-helper-vibe.md → ../math-helper. Press Enter to accept it, or type any path.
To skip the prompt entirely (CI or scripted runs), set VIBE_OUTPUT_DIR:
VIBE_OUTPUT_DIR=../my-app VIBE_MANIFEST=my-project-vibe.md npm startThe {OUTPUT_DIR} token in mcp.config.json is substituted with the resolved path at runtime, so the filesystem MCP server always points at the same directory VibeFlow is writing to:
{
"servers": [
{
"id": "filesystem",
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "{OUTPUT_DIR}"]
}
]
}VibeFlow creates the output directory automatically before starting MCP servers. No manual mkdir needed.
Popular servers pre-configured in the example:
| Server | Package | What it gives VibeFlow |
|---|---|---|
filesystem |
@modelcontextprotocol/server-filesystem |
Read/write files in the project (uses {OUTPUT_DIR}) |
context7 |
@upstash/context7-mcp |
Up-to-date library docs (resolves package → versioned docs) |
fetch |
@modelcontextprotocol/server-fetch |
Fetch URLs and convert to Markdown |
duckduckgo |
mcp/duckduckgo (Docker) |
Web search |
All npx-based servers run via npx -y — no global installs needed.
mcp.config.json is gitignored. The committed mcp.config.json uses {OUTPUT_DIR} and works for any project out of the box.
VibeFlow delegates all LLM work to DeerFlow, which runs as a local Docker service. Understanding what DeerFlow adds — versus what a direct API call would give you — explains why the architecture is structured the way it is.
Multi-step agent loop, not a single LLM call
When VibeFlow sends a task to DeerFlow it doesn't get back a single response. DeerFlow runs a LangGraph lead_agent graph that can make multiple model calls, reason through intermediate steps, use tools between steps, and decide when it's actually done. For a task like "implement the practice screen," the agent may read existing files, look up library docs, generate code, review it, and revise — all before returning.
Tool use during execution
DeerFlow can invoke MCP servers mid-task: reading and writing files via the filesystem server, fetching up-to-date library documentation via context7, and searching the web via DuckDuckGo. This means the agent isn't limited to its training data — it can look things up as it works.
Thread persistence = memory across retries
DeerFlow threads accumulate conversation history. The Option B fix loop relies on this: by the third fix attempt, the LLM has its two previous attempts and their failure details in context, so it doesn't repeat the same approach. Stateless API calls can't do this without manually managing and truncating conversation history.
Model provider abstraction
VibeFlow has no direct dependency on Anthropic, OpenAI, or Snowflake. DeerFlow's config.yaml handles authentication and provider-specific API formats. Switching providers is a config change — no VibeFlow code changes needed.
Where DeerFlow adds less value
For simple, deterministic tasks ("write a tsconfig.json") the multi-step agent loop is overhead you don't need. The short-path / long-path split exists partly for this reason — short tasks get a streaming response with a hard timeout; only tasks that genuinely benefit from the agent loop run as long-path background jobs.
VibeFlow runs a six-phase pipeline on every npm start:
manifest
│
├─ 0. OUTPUT DIRECTORY
│ Prompts for (or reads VIBE_OUTPUT_DIR / derives from manifest name).
│ Creates the directory. Starts MCP servers with the path substituted in.
│ VIBE_FRESH=1 wipes both checkpoints and the output directory first.
│
├─ 1. DEFINE CRITERIA
│ DeerFlow reads the manifest and produces AcceptanceCriteria.
│ Explicit goals are extracted; implicit expectations are inferred and flagged.
│ User reviews and confirms before anything is built.
│ Criteria are stored in the checkpoint — resumed runs skip re-generation.
│
├─ 2. DECOMPOSE
│ DeerFlow decomposes the manifest into a task tree, with the acceptance
│ criteria injected as context so tasks are built to satisfy known standards.
│
├─ 3. EXECUTE TASKS
│ Each task runs via DeerFlow (short path: streaming; long path: background poll).
│ Files emitted as <vibeflow:file> blocks are written directly to the output
│ directory via fs.promises. Checkpoints are saved between tasks.
│
├─ 4. VERIFY + FIX
│ Every acceptance criterion is evaluated against the output:
│ • executable criteria → bash command (cwd = output dir), exit 0 = pass
│ • evaluative criteria → LLM judge reads artifacts, returns PASS/FAIL + reason
│ If any fail, a single DeerFlow thread is opened for the fix loop.
│ Each fix attempt appends to the same thread (Option B) — the LLM sees
│ its full history and doesn't repeat failed approaches.
│ Up to VIBE_MAX_FIX_RETRIES (default 3) iterations.
│
└─ 5. RATCHET
The weighted pass rate from verification (not fake task completion %)
is proposed to the ratchet. Only commits if metrics improved.
Tasks are routed by the LLM's estimated duration:
- Short path — tasks expected to finish in < 30 s run synchronously with a streaming timeout guard
- Long path — longer tasks run async with periodic checkpointing; send
SIGINTto pause cleanly and resume on next start
VibeFlow has four distinct retry layers — each targeting a different failure mode.
Short-path tasks (estimated < 30 s) run synchronously with a 120 s wall-clock timeout. If a task exceeds that, retrying with the same approach would just time out again. Instead VibeFlow promotes the task to the long path, switching to a background run with polling and no hard deadline:
Short-path task starts (streaming, 120 s limit)
↓ timeout fires
↑ promoted to long-path background run (no hard deadline, exponential backoff)
You will see ↑ timed out — promoting to long-path background run… in the console when this happens.
For errors that are not timeouts (bad response, parse failure, HTTP error), the short-path retries up to task.maxRetries times on the same streaming approach. These are transient failures where re-trying the same method is reasonable.
Long-path background runs retry on any failure with exponential backoff:
| Attempt | Wait before retry |
|---|---|
| 1 → 2 | 2 s |
| 2 → 3 | 4 s |
| 3 → 4 | 8 s |
After task.maxRetries attempts the task is marked FAILED.
If a mandatory upstream task fails and a downstream task is about to run, VibeFlow retries the upstream task once before deciding whether to skip the dependent. This handles the case where an intermittent failure earlier in the graph would otherwise cascade into skipping large parts of the tree.
Task B depends on Task A (mandatory)
Task A → FAILED
VibeFlow retries Task A
→ COMPLETED: Task B runs normally
→ FAILED again: Task B is skipped with [SKIPPED] prefix
The short-path streaming connection has a secondary check independent of the wall-clock timeout: if no SSE chunk arrives from DeerFlow for 60 seconds, the stream is considered stalled and aborted. This catches silently dropped TCP connections quickly rather than waiting for the full 120 s limit.
A slow-but-active stream (tokens arriving at any rate) keeps the timer resetting and is never interrupted by this check.
After all tasks complete, failed acceptance criteria enter the fix loop (see The fix loop). This loop is not purely count-driven — it also has stuck detection:
- Keeps retrying as long as the weighted pass rate improves between attempts
- Stops early after 2 consecutive attempts with no improvement (stuck)
- Hard cap at
VIBE_MAX_FIX_RETRIES(default: 10) attempts
This means a run that steadily improves from 40% → 60% → 80% → 100% uses all the attempts it needs, while a run that hits a ceiling at 60% after 2 tries stops spending token budget on a dead end.
A physical ratchet only turns one direction. VibeFlow's ratchet applies the same constraint to progress: each run can only advance the baseline — it can never silently make things worse.
This matters because autonomous agents are generative. Without a gate, a run that produces lower-quality output than the previous one would overwrite the good state and nobody would notice until much later. The ratchet makes regression impossible to ignore.
Every run goes through the same three steps:
1. PROPOSE After verification, VibeFlow submits the measured pass rate
as a candidate MetricSnapshot. Nothing is committed yet.
2. EVALUATE The ratchet compares the proposal against the last committed
baseline across all tracked metrics.
3. COMMIT If at least one metric improved and none regressed beyond the
or tolerance → the proposal becomes the new baseline.
ROLLBACK If any metric regressed → the proposal is discarded and the
previous baseline is restored.
The first run always commits — there is no prior baseline to regress from.
| Metric | Direction | What it measures |
|---|---|---|
testPassRate |
higher is better | Weighted pass rate from criteria evaluation |
apiLatencyP95Ms |
lower is better | 95th-percentile latency of DeerFlow calls |
criticalSecurityFindings |
lower is better | Security issues found in generated output |
ratchetCommitRate |
higher is better | % of all proposals that have been committed |
All metrics are normalised internally so the comparison logic is uniform regardless of direction.
A 2% tolerance prevents the ratchet from blocking on noise. If testPassRate drops from 95 to 94.1 between runs that is within tolerance and does not count as a regression. A drop from 95 to 80 does.
ratchetCommitRate is itself a tracked metric — it measures what fraction of all proposals have been committed over the lifetime of the project. A high commit rate means the agent is consistently making progress. A declining commit rate is an early warning that the system is struggling to improve and warrants investigation.
The ratchet keeps the last 20 committed baselines on a rollback stack. If a run commits and is later found to be bad, you can restore a known-good state without losing the full history.
State is persisted to .vibeflow/ratchet/ratchet_state.json after every propose, commit, and rollback, so the baseline survives process restarts.
All file and code operations go through the MCP client:
- Retry logic — 3 attempts with exponential backoff
- Rate limiting — token-bucket (configurable RPS)
- Audit logging — every call appended to
.vibeflow/mcp-audit.jsonl
When DeerFlow decomposes a manifest into tasks it classifies each one as mandatory or optional.
- Mandatory (default) — the task's output is required for the overall goal. If a mandatory task fails, any downstream task that depends on it will attempt one full retry of the blocking task before deciding whether to skip.
- Optional — the task is an enhancement or polish step. If it fails or is skipped, downstream tasks are told the impact and continue anyway.
At runtime you will see one of three prefixes in the console output:
| Prefix | Meaning |
|---|---|
[MANDATORY DEP RETRY] |
A required upstream task failed; VibeFlow is re-running it before proceeding |
[MANDATORY DEP RETRY OK] |
The retry succeeded; the dependent task will now run |
[OPTIONAL DEP SKIPPED] |
A non-critical task was skipped; the impact is printed and execution continues |
[SKIPPED] |
A task was skipped because one or more mandatory dependencies could not be resolved even after a retry |
Starting fresh vs. resuming
Checkpoints are written to .vibeflow/checkpoints/. On the next npm start, VibeFlow detects them and offers to resume or start fresh.
Use VIBE_FRESH=1 to skip the prompt and always start clean — it wipes both the checkpoints and the output directory:
VIBE_FRESH=1 VIBE_MANIFEST=my-project-vibe.md npm startOr answer y at the interactive prompt when asked "Clear checkpoints and start fresh?".
Because npm start runs tsx src/index.ts directly (no compiled binary), source changes take effect immediately on the next run — no build step needed.
# Graceful stop — finishes current task, saves checkpoint
kill -SIGINT <pid>The next npm start restores from the latest checkpoint automatically.
For a deeper dive into the source code internals, see vibeflow/ARCHITECTURE.md.
| Symptom | Fix |
|---|---|
make config fails: python: No such file or directory |
Use make config PYTHON=python3 |
npm error: Could not read package.json |
Wrong directory — cd vibeflow/vibeflow/ first |
deer-flow-langgraph crashes on startup |
models: in config.yaml is empty — add at least one model entry |
Containers fail with [Errno 21] Is a directory: '.../extensions_config.json' |
extensions_config.json was missing when Docker first ran, so Docker created it as a directory. Fix: rm -rf ../deer-flow/extensions_config.json && cp ../deer-flow/extensions_config.example.json ../deer-flow/extensions_config.json then docker compose -f docker-compose.deerflow.yml down && docker compose -f docker-compose.deerflow.yml up -d |
[SKIPPED] shown for many tasks |
A mandatory upstream task failed and its retry also failed — check the [MANDATORY DEP RETRY] error above it for the root cause |
[OPTIONAL DEP SKIPPED] shown |
Expected behaviour — the optional task didn't produce output but execution continues; check the impact line to decide if you need to fix it |
First --build hangs for minutes |
Normal — Docker is installing Python deps; wait it out |
