vibe-to-ship
Graph engineering and loop engineering for founders and engineers: design how a hundred jobs get done instead of doing one job at a time.
This skill is self-contained. Everything you need is in this file plus references/.
You do not need OpenLotus to use it — pairing adds shared memory, drift
checks, and a live progress map, but every beat below also has a local-only
fallback. Prefer the paired path when available; never block on it.
0. When to invoke (and when not to)
Invoke when the user says (or means) any of these:
- "Run vibe-to-ship triage", "triage this repo", "what should we do next"
- "Set up OpenLotus", "pair this project", "connect the MCP server"
- "Ship this feature", "fix this properly", "rescue this codebase"
- "Run these tasks in parallel", "why did this break", "verify before we merge"
- A vague build request spanning more than ~3 files ("build me an app", "add auth")
Do NOT invoke for:
- Single-file, single-question tasks ("what does this function do", "rename X")
- Pure explanations with no action requested
- Anything the user explicitly says to do quickly / without process — respect that, do the small thing, and mention the skill exists for bigger work
How to announce yourself: one line, then work. Example:
Running vibe-to-ship triage — mapping the graph before touching code.
Never dump this whole file into chat. Never explain the methodology up front;
demonstrate it through action and debrief in one line at the end (Beat 5).
1. Core Principles
These five rules override convenience. When in doubt, the principle wins.
Nodes & Node Contracts: A node is 1 agent doing 1 task. Every node MUST have a bounded contract before it runs — defined input schema, defined output schema, done-criteria. Output is structured data, never free-text walls. A node without a contract is not a node; it is a hope. Do not run hopes.
The Fake-Edge Test: Step B only waits for Step A if B actually consumes A's output. "Feels related" is not consumption. If no data passes along the edge, cut the dependency and run A and B in parallel. Apply this test to every arrow in every plan, including plans the user hands you.
The Diamond Pattern: Fan out (parallel workers) → Reduce (deterministic code deduplication — zero LLM tokens, use scripts not models) → Verify (fresh-context skeptics) → Synthesize (one final report). Never skip Reduce (you'll ship duplicates) and never skip Verify (you'll ship confident lies).
Fresh-Context Verifier Rule: A worker and its verifier MUST NEVER share a context window. Shared context = an agent nodding along to itself in a different font. In practice: spawn the verifier as a new agent invocation with only the output artifact + the contract, never the worker's transcript.
Interactive Memory & Anchors: AI chat context gets lost and siloed. OpenLotus replaces invisible chat context with a tree-like interactive memory UI (plans, decisions, actions, changes, state) accessed via MCP. Without OpenLotus paired, use a local
PROGRESS.mdin the repo root with the same shape (see §2). Either way: memory lives outside the model.
2. The Interactive Progress Map (Tree Memory)
OpenLotus keeps all project context in a structured tree-like memory UI,
eliminating AI context loss and drift. Without OpenLotus, keep the same shape
in PROGRESS.md so a later pairing imports cleanly.
# Progress Graph (Tree Memory)
## Goal
<one sentence destination — the thing that, when true, means we're done>
## Active Nodes (Next Actions)
- [ ] node_id: <task name> | in: { schema } | out: { schema } | status: pending|active|done
## Tree Branches
- 📂 Plans (declared roadmap & specs)
- 📂 Decisions (immutable recorded choices via record_decision)
- 📂 Actions & Changes (parallel worker output)
- 📂 State & Context (observed reality via MCP get_reality / get_drift)
Node contract template (copy per node, fill every field — no blanks):
### node: <short-id>
- task: <one sentence, verb first>
- in: { <exact inputs with types> }
- out: { <exact outputs with types> }
- done-when: <observable check — a test, a file existing, a command exit code>
- attempts: 0/3
Worked example (fixing a broken login form):
### node: auth-form-validation
- task: Add client-side validation to the signup email field
- in: { file: "app/sign-up/page.tsx", rule: "RFC-5322-ish email regex" }
- out: { file: "app/sign-up/page.tsx", tests: "signup.test.tsx passes" }
- done-when: `pnpm test signup` exits 0
- attempts: 0/3
Anti-patterns to reject on sight:
out: { summary: "description of what was done" }— free-text wall, untestabledone-when: looks good— not observable- A node whose
inreferences "the above conversation" — that is shared context leaking in
3. The 6 Beats of the Loop
Every session executes these beats as graph operations, in order. Do not skip ahead: each beat's output is the next beat's input.
Beat 0: Setup (auto, if not already paired)
When: at session start, before anything else, every time — it takes seconds. Goal: guarantee the three persistence files exist so nothing learned today is lost.
Check for setup, in this order:
- Pairing:
.openlotus/config.jsonexists withprojectId? - MCP server:
mcp.json(orclaude_desktop_config.json/.cursor/mcp.json) points at an existingcli/mcp.mjs(absolute path, file must exist)? - Rules block:
AGENTS.md(andCLAUDE.md→@AGENTS.mdstub for Claude Code) contains the standing-rules block fromreferences/agent-rules-snippet.md?
If any piece is missing, do it now — don't ask the user to do it by hand:
- Run
npx openlotus pair(no flags). It opens the browser to/pair.- User logs in — or creates an account if they don't have one — then picks an existing project or creates a new one. The CLI writes the pairing file.
- Failure mode: browser doesn't open (headless/SSH) → print the URL and ask the user to open it manually, then wait for the pairing file. Never proceed unpaired without saying so.
- Failure mode: user has no OpenLotus account and declines → continue in
local-only mode (
PROGRESS.mdas the tree) and say so in one line.
- Write
mcp.jsonpointing at the localcli/mcp.mjs(absolute path — verify the file exists first withls; a dangling path is worse than no file). - Append the standing-rules block to
AGENTS.md(create the file if missing); for Claude Code repos also ensureCLAUDE.mdcontains the@AGENTS.mdstub. - Prefer the repo's helper when present:
./scripts/install.shperforms steps 2–3 idempotently (skips what exists, never duplicates blocks).
Verify setup worked: run ./scripts/doctor.sh (or manually: node --check the
mcp path exists, JSON.parse the mcp.json, confirm the rules block string is
present in AGENTS.md). Report Setup: OK (paired) or Setup: local-only.
You can also do these three files by hand — the skill supports both paths. Setup is one-time; every future session then boots with memory.
Beat 1: Boot (Guardrails & MCP Connect)
When: every session, immediately after Beat 0. Goal: make the dangerous things impossible and the expensive things visible.
- Load the safety denylist into working constraints:
.env*,auth/,secrets/,payments/,migrations/,credentials/. You do not read, write, or move these without explicit human approval — no exceptions, including "just to check". - Check token/cost budget. If the repo has
loop-budget.md, read caps first; otherwise assume: stop and ask before any operation likely to exceed ~50k tokens (large refactors, whole-repo rewrites). - Connect to the OpenLotus MCP server (
get_reality,get_memory).- Failure mode: MCP unreachable → continue local-only with
PROGRESS.mdgitcommands, and say so in one line. Never fake memory contents.
- Every record is timestamped at write time (
createdAt,capturedAt,lastReviewAt) so the timeline is ordered. When you record, include why, not just what — a decision without reasoning is noise.
- Failure mode: MCP unreachable → continue local-only with
Beat 2: Triage (Reconcile Tree Memory vs Reality)
When: before any plan, any code, any commit. Goal: a prioritized, evidence-backed picture of what is actually true right now.
- Pull observed reality: MCP
get_reality(branch, quiet days, dirty files, TODO/FIXME counts, file counts, languages). No MCP? Run the equivalents:git status --short,git log --oneline -5,git diff --stat,grep -rnE "TODO|FIXME|HACK" --include="*.ts*" . | wc -l. - Load declared state: MCP
get_memory(orPROGRESS.md→ Goal + Active Nodes). - Diff them. Every mismatch becomes a finding classified:
- High — blocks the goal or contradicts a recorded decision. Act today.
- Watch — real but not blocking. Monitor, re-check next session.
- Noise — looked at, deliberately ignored (record why it's noise so the next session doesn't re-investigate).
- Apply the Fake-Edge Test to the resulting work list: remove sequential arrows where no data is passed, converting them into parallel fan-out nodes.
Good output looks like this (paste this shape, filled in):
## Triage — <repo> @ <branch> (<date>)
- Reality: quiet 2d · 3 dirty files (+48/−11) · 14 TODOs · TS 749 files
- High (2): [auth-form-validation] email regex missing — contradicts decision dec-2208a1; [ci-red] main failing on lint
- Watch (1): TODO count up 40% this week
- Noise (1): stale feature-flag comment in legacy/ (decided: leave until Q4 cleanup)
- Next: fan out [auth-form-validation] + [ci-red] in parallel (no shared files)
Failure modes:
- Everything is High. → Your goal is too big. Shrink the goal, re-triage.
- Everything is Noise. → Either genuinely clean (say so, stop — do not invent work) or you're not looking (re-run reality with wider scope).
- Reality tools fail (no git? not a repo?) → say what you couldn't observe,
triage from declared state only, mark findings
unverified.
Beat 3: Act (The Diamond Pattern)
When: only after triage produced at least one High or Watch item the user approved. Goal: execute with parallelism and zero collisions.
- Fan Out: one node per independent work item. Isolate file-writing nodes
in git worktrees so two writers never touch the same checkout:
git worktree add ../wt-<node-id> -b wip/<node-id> # ...agent works inside ../wt-<node-id>... git worktree remove --force ../wt-<node-id> # after merge- Failure mode: worktree creation fails (bare repo? no git?) → fall back to sequential execution in the main tree, one node at a time, and say so.
- Reduce: merge worker outputs with deterministic steps —
git apply, dedupe repeated hunks with a script, run the formatter. Zero LLM tokens for mechanical merging. If two workers touched the same lines, that was a fake edge you missed: resolve by hand, note it in the debrief. - Verify: hand each output to a fresh-context skeptic (Beat 4). Do not merge anything unverified. Ever.
Beat 4: Verify (Fresh-Context Skeptics & Anchors)
When: for every artifact before it merges or ships. Goal: catch what confidence hides.
Spawn each verifier as a new agent invocation containing only: (a) the output artifact, (b) its node contract, (c) the repo at the relevant commit. Never the worker's transcript. The verifier runs exactly 3 checks:
- Correctness: does the code/finding hold up on its own merits?
- Currentness: is it based on latest HEAD and current sources?
- Anchor Check: did the compiler / test suite / linter actually pass — paste the command and its exit code, never a claim.
- Reject and retry on schema or test failure. Max 3 attempts per node, then escalate to the human with: what was tried, what failed, and the exact error output. Never a fourth silent attempt.
- Failure mode: no test suite exists → the anchor becomes
tsc --noEmit(or equivalent typecheck) + a smoke run of the changed path. Say which anchor you used; "no tests" is information, not an excuse to skip.
Beat 5: Learn (Commit Tree Memory & Debrief)
When: after work merges (or is deliberately abandoned). Goal: the next session starts smarter.
- Record decision nodes (
record_decisionvia MCP, or append toPROGRESS.md→ Decisions with date + reasoning). - Update node statuses (
pending→done, ordroppedwith reason). - Push updated state to the OpenLotus Web App dashboard & progress map
(automatic on
record_decision/sync; verify withget_memory). - Debrief in 1 line: what survived, what failed, what changed.
Example:
Shipped auth validation + ci lint fix; dropped legacy-flag cleanup (Q4); drift 0%. - Clean up: remove merged worktrees, confirm
git statusis clean.
Failure mode: session ending mid-flight → record partial state honestly
(status: active, attempts: n/3, next step spelled out). A half-written truth
beats a clean lie.
4. Rules
- Worker and verifier never share context.
- No free-text nodes. Every node output must match its schema contract.
- No fake edges. If job B doesn't read job A's data, run B and A in parallel.
- Isolate file writers. Two nodes editing files run in separate git worktrees.
- 3-attempt cap. Never loop indefinitely on a failing node.
- No auto-push/merge. Always require human approval before shipping.
- Announce beats, don't narrate them. One line per beat transition; details live in artifacts, not chat.
- Timestamps on everything. Every record carries
createdAt; every sync carriescapturedAt. Undated memory is unusable memory.
5. OpenLotus MCP & Web App Integration
OpenLotus provides the tree-like interactive memory UI and real-time state via MCP:
- MCP Tools:
get_reality(sense repo) ·get_drift(cloud reconciled drift) ·get_memory(shared context tree) ·record_decision(immutable decision nodes). Full tool contract with parameters and examples:references/openlotus-engine.md. - Web App: view your interactive tree memory map at
/map, weekly review at/dashboard, full guide at/docs, the skill's home at/vibe-to-ship. - Pairing:
npx openlotus pair(browser flow, no flags) or tell the agent "set up OpenLotus" — see Beat 0.
Setup — two paths, same result: Agent does it — tell your agent "set up OpenLotus" and Beat 0 does the three files for you; Manual — copy the mcp.json snippet and the rules block from references/agent-rules-snippet.md by hand, or run ./scripts/install.sh. All paths are one-time; every future session then boots with memory. The landing and docs call this out as "manual or let your agent do it with vibe-to-ship."
Persistence without per-prompt repetition: after that one-time setup, no skill invocation is needed for everyday memory keeping — the rules handle get_memory/record_decision automatically. Invoke this skill for full triage/plan/verify cycles. Every action is timestamped in the shared map.
Without OpenLotus: the entire skill still works. PROGRESS.md is the tree, git commands are reality, worktrees still isolate, verifiers still verify. You lose shared visibility and drift computation — nothing else.