Crumb — Multi-Agent Game Prototyping Skill (v0.1)
Multi-agent collaboration tool for the Bagelcode new-IP team (mobile casual). One natural-language pitch and Planner Lead → Builder → qa_check effect → Verifier (CourtEval) collaborate to ship a Phaser 3.80 multi-file PWA under artifacts/game/ plus spec.md + DESIGN.md + tuning.json.
3-layer separation (
wiki/synthesis/bagelcode-host-harness-decision.md):
- NL interface = Claude Code (this skill's host) — top of the multi-host 4-entry list
- NL → backend routing = this SKILL.md +
agents/*.mdsandwiches + 5 procedural skills (skills/*.md)- Orchestration control plane =
src/{transcript,reducer,validator,adapter,dispatcher,loop}/v0.1 highlights (
wiki/concepts/bagelcode-system-architecture-v0.1.md):
- Engineering Lead → builder + verifier actor split (true cross-provider boundary)
--cross-providerflag →--preset <name>(named preset, BYO)- 38 kind → 39 kind (+
qa.resultdeterministic effect)- scores → D1-D6 source-of-truth matrix (reducer-auto / qa-check-effect / verifier-llm — single origin per dim; D3/D5 split into LLM + auto components combined deterministically in code)
- Multi-host 4-entry (Claude Code / Codex CLI / Gemini CLI / headless) + ambient fallback
- MCP Provider (cross-host fan-in) + auth-manager (
crumb doctor) + persistence boost (crumb resume)
When to trigger
NL trigger recognition:
- "60초 매치-3 게임 만들어줘", "make a swipe-to-merge game", "build a 30s clicker" — casual game pitches
- Explicit command: "/crumb <pitch>", "crumb start <pitch>"
- Preset intent: "키 없이 돌려봐" →
mock, "video research" / "YouTube 영상 분석" →video-research, "cross-vendor verifier" / "Codex 빌더" → per-actor--bindflags (no named cross-3way preset since 2026-05-07; explain the binding override path) - Mid-flight follow-up (right after the SessionStart hook surfaces an in-flight session): "지금 어디까지 갔어?", "진행 상황", "이거 끝났어?", "what's the status?" → route to
mcp__crumb__crumb_status+mcp__crumb__crumb_suggestinstead of spawning a new session
Do NOT activate on:
- Code review / debugging / library questions
- Non-casual games (3D / FPS / MMO etc. — Phaser single-file fit ❌)
- Generic multi-agent system design discussion (refer to
wiki/only)
In-flight session branching
The Claude Code SessionStart hook (.claude/hooks/session-start.cjs) scans ~/.crumb/projects/<id>/sessions/*/meta.json at startup and injects ULIDs + goals + last kinds for any session with status ∈ {running, paused} into the system context. When the user's utterance is ambiguous between a new game pitch and a follow-up:
- Context has an in-flight ULID AND the user uses "진행" / "상황" / "어디까지" / "끝났어" / "next" vocabulary → respond with
mcp__crumb__crumb_status+mcp__crumb__crumb_suggest(or guide the user to the/crumb-watch <ulid>slash command). - The user explicitly states a new game pitch → spawn a fresh
mcp__crumb__crumb_runregardless of existing sessions (concurrent sessions are fine). - Otherwise, ask the user once.
Additionally, the Stop hook (.claude/hooks/stop.cjs) emits one line per turn: [crumb] <ulid> · <last_kind> · agg=N verdict=X into the system context — keeps progress visibility inline without requiring the user to ask.
How to run
1. Extract pitch + preset
From the user's utterance:
- goal string — strip
/crumbprefix, Korean particles, sentence-ending markers like "만들어줘". - preset — only use what the user explicitly named. Otherwise ambient (follow the entry host; Crumb does NOT force a default).
- Only when the user asks "어떤 preset 있어?" / "추천해줘" → run
crumb doctorand show the preset list, then defer the choice. - When the user names a preset ("video-research", "mock") → pass it through verbatim. The
cross-3waynamed preset was retired 2026-05-07 — if user types it, recommend per-actor--bindflags instead (e.g.--bind builder=codex --bind verifier=gemini-cli:gemini-3.1-pro-preview). - When user intent ("키 없이 돌려봐") and an explicit name conflict, the explicit name wins.
- Only when the user asks "어떤 preset 있어?" / "추천해줘" → run
→ Core principle: provider × harness × model is the user's call. Crumb is a helper, never a default-setter (Anthropic 2026-03 "wrong tradeoff" lesson).
2. Start a Crumb session — mcp__crumb__crumb_run (preferred)
mcp__crumb__crumb_run({
goal: "<extracted goal>",
preset: "<name>" /* optional — pass only when user named one */,
// adapter / idle_timeout_ms / label are also optional
})
→ The MCP tool spawns crumb run as a detached subprocess and immediately returns { session_id, session_dir, log_path }. The host CLI is never blocked. Report the session ULID to the user and announce the next step.
Bash fallback — for environments where the MCP server is not active (use the installed
crumbbin ornpx tsx):crumb run --goal "$GOAL" ${PRESET:+--preset "$PRESET"} # Or, without npm link: npx tsx "$CLAUDE_PROJECT_DIR/src/index.ts" run --goal "$GOAL"Prefer
mcp__crumb__crumb_runwhenever possible — no permission prompt, one shot.
Environment doctor — when the user asks "어떤 preset 가능?" or you suspect the environment is incomplete:
npx tsx src/index.ts doctorOutputs 4 host entries × adapter health × installed/authenticated preset matrix. The user picks — Crumb only surfaces the runnable options.
3. Streaming the transcript
While the session runs, surface only meaningful events to the user:
tail -f sessions/<ulid>/transcript.jsonl | jq -r '
select(.kind | IN("goal","question.socratic","spec","build","qa.result","judge.score","done","error","handoff.requested")) |
"[\(.ts | split("T")[1] | split(".")[0])] \(.from)\t\(.kind)\t\(.body // (.data | tostring) | .[0:120])"
'
Skip these kinds: ack, audit, tool.call, tool.result, agent.thought_summary. Surface these: the 4 sub-steps of step.judge (grader/critic/defender/regrader) + qa.result (the v0.1 deterministic ground truth).
4. User natural-language intervention (mid-flight)
While the session runs, user intervention lands on the same transcript line regardless of which of the 5 surfaces is used — routing is source-agnostic (agents/coordinator.md Routing Rules).
Preference order (on top of Claude Code):
mcp__crumb__crumb_interveneMCP tool — direct call from NL. One permission prompt and done.- Slash commands —
/crumb-approve <ulid>,/crumb-veto <ulid> <reason>,/crumb-redo <ulid>,/crumb-pause <ulid> [@actor],/crumb-resume <ulid> [@actor],/crumb-watch <ulid>,/crumb-cancel <ulid>. Each is a thin wrapper around the MCP tool above. - Append directly to
inbox.txt(headless / other host). crumb eventCLI — direct JSON event input (low-level).crumb tuislash bar (only when the user has a separate terminal open).
Surface 1 — mcp__crumb__crumb_intervene (Claude Code preferred)
mcp__crumb__crumb_intervene({
session: "<ulid>",
action: "approve" | "veto" | "pause" | "resume" | "redo" | "goto" | "append" | "note" | "reset_circuit" | "swap" | "free",
body?: "<reason or free text>",
target_actor?: "builder" | "verifier" | ...,
swap_to?: "<adapter>" /* required only when action=swap */,
})
→ Internally appends one line to sessions/<ulid>/inbox.txt. The watcher (500 ms tick) parses it into a transcript event. Grammar matches src/inbox/parser.ts.
Surface 2 — inbox.txt (other hosts / fallback)
SESSION_ID="<active ulid>"
echo "@builder use red/green palette only" >> sessions/$SESSION_ID/inbox.txt
The 500 ms watcher parses each line and appends it to the transcript. This skill should map natural-language user input like "빌더한테 빨강/초록만 쓰라고 해" into the slash-bar grammar above and append to the inbox.
Surface 3 — TUI slash bar (only when the user has crumb tui open)
/approve /veto <id> /pause [@<a>] [reason]
/resume [@<a>] /goto <a> [body] /swap <from>=<adapter>
/reset-circuit <a|all> /append [@<a>] <text>
/note <text> /redo [body] /q /quit
@<a> <body> (free text mention)
The TUI and inbox.txt share one grammar (src/inbox/parser.ts). Bidirectional muscle-memory compatible.
Surface 4 — JSON event directly (low-level)
SESSION_ID="<active ulid>"
echo '{"from":"user","kind":"user.intervene","body":"<original text>","data":{"target_actor":"builder","sandwich_append":"phaser 3.80 only"}}' \
| CRUMB_TRANSCRIPT_PATH="sessions/$SESSION_ID/transcript.jsonl" \
CRUMB_SESSION_ID="$SESSION_ID" \
CRUMB_SESSION_DIR="sessions/$SESSION_ID" \
crumb event
data field semantics (shared by all 5 user.* events)
data.<key> |
Effect (reducer handling) |
|---|---|
target_actor: <actor> |
Fact tagged @<actor> — surfaced in the next <actor> spawn's sandwich (no routing change). |
goto: <actor> |
next_speaker = <actor> forced + immediate spawn (LangGraph Command(goto)). |
swap: { from: <a>, to: <adapter> } |
progress_ledger.adapter_override[a] = <adapter> (Paperclip BYO swap). |
reset_circuit: <a> | true |
Clears circuit_breaker[<a>] (or all when true). |
sandwich_append: <text> |
Adds a fact with category='sandwich_append' — the dispatcher concatenates it onto every subsequent matching spawn's system prompt (v0.2.0 G4). |
actor: <actor> (only on user.pause / user.resume) |
Pause/resume that single actor instead of the whole session. |
Frontier mapping: LangGraph
Command(goto/update={...})53/60 + Paperclip BYO swap 38/60 + CodexAPPEND_SYSTEM.md38/60. Background:wiki/synthesis/bagelcode-user-intervention-frontier-2026-05-02.md.
The reducer applies it and updates progress.next_speaker (typically falls back to planner-lead and emits spec.update; if data.goto is set, jumps directly to the named actor).
5. Surface the result
When kind=done arrives:
✅ session complete — sessions/<ulid>/
▸ artifacts/game/ (multi-file PWA — open game/index.html)
▸ artifacts/spec.md (acceptance criteria + rule book)
▸ artifacts/DESIGN.md (color / mechanics / motion)
▸ artifacts/tuning.json (balance numbers)
▸ transcript.jsonl (replay-deterministic, 40 kind × 11 field × 8 from)
▸ judge.score (D1-D6) (source-of-truth matrix: reducer-auto / qa-check-effect / verifier-llm)
If the user says "열어줘" / "play":
open sessions/<ulid>/artifacts/game/index.html
6. Resume after an interrupt (v0.1 persistence boost)
If the session is Ctrl-C'd or crashes mid-run:
npx tsx src/index.ts resume <session-id>
Reads the adapter-native session id (Claude Code --resume / Codex --thread) from metadata and restores host-harness state, preserving cache_carry_over.
Preset options (user choice, Crumb only recommends)
provider × harness × model is the user's call. Crumb only surfaces what crumb doctor says is runnable; it does not force a default.
| Preset | Composition | Install / auth required | Use case |
|---|---|---|---|
| (no preset) ambient ★ | Every actor follows the entry host (Claude Code → Opus 4.7) — Crumb does NOT decide bindings | One auth on the entry host | Recommended default — strongest model your env offers across every actor. |
video-research |
researcher=gemini-sdk (Gemini 3.1 Pro video Part) / rest=ambient | claude + GEMINI_API_KEY | Use case — video evidence path: gameplay clip → mechanic extraction. |
mock |
All actors = mock adapter, deterministic | 0 | Use case — CI / evaluator env: works without keys, replay-deterministic |
→ Detailed preset format: .crumb/presets/*.toml. The user picks. Crumb does not guess or force.
Cross-vendor verifier (self-bias mitigation) is no longer a named preset. PR-DropCross3Way (2026-05-07) retired cross-3way because baking builder=codex / verifier=gemini-cli into the default forced quality regression on Claude Code users (Codex's HTML/CSS prior < Opus 4.7). Use per-actor binding override:
crumb run --goal "..." --bind verifier=gemini-cli:gemini-3.1-pro-preview
For "Codex on builder" specifically: --bind builder=codex:gpt-5.5-codex. Both bindings stack.
Actor split (v0.1)
The v2 "4 inline sub-steps inside Engineering Lead" → v0.1's builder + verifier as separate actors (true cross-provider split).
| Actor | Sandwich | Step (sequential within actor) |
|---|---|---|
| coordinator | agents/coordinator.md |
(host-inline routing) |
| planner-lead | agents/planner-lead.md |
phase A: socratic + concept (handoff researcher); phase B (resumed): design + synth (2 specialist inline + game-design contract) |
| researcher ★ (v0.3.0) | agents/researcher.md |
step.research.video × N + step.research synthesis (gemini-cli: Gemini 3.1 Pro native YouTube URL ingestion) |
| builder ★ | agents/builder.md |
step.builder + step.qa (Builder + QA inline) |
| (effect) qa_check | (no sandwich) | dispatcher emits kind=qa.result (deterministic, no LLM) |
| verifier ★ | agents/verifier.md |
step.judge × 4 (grader/critic/defender/regrader, CourtEval) + reviewer persona |
| builder-fallback | agents/builder-fallback.md |
Substitute when builder fails (same contract as builder) |
→ 6 outer actors + 2 specialists (planner-internal, inline) + 1 contract (game-design.md, inline-read by 4+ actors) + 5 procedural skills:
[skills]
tdd_iron_law # superpowers: NO PRODUCTION CODE WITHOUT FAILING TEST FIRST
verification_before_completion # force verifier PASS before done
code_review_protocol # builder ↔ verifier handoff format
parallel_dispatch # specialist parallel-call pattern (planner inline)
subagent_spawn # host-native primitive abstraction
Multi-host (v0.1)
| Host | Entry path | Active trigger |
|---|---|---|
| Claude Code | .claude/skills/crumb/SKILL.md (this file) |
claude + NL game pitch |
| Codex CLI | ~/.codex/agents/crumb.toml |
codex + /crumb <pitch> |
| Gemini CLI | ~/.gemini/extensions/crumb/ |
gemini + NL game pitch |
| Headless | npx tsx src/index.ts run --goal "..." |
CI / no auth (evaluator) |
→ Whichever host the user enters from, the control plane (transcript / reducer / state) is identical. For cross-host scenarios, the MCP Provider (localhost:8765) fans in.
Swap cookbook — model / provider / actor swapping
All four dimensions (preset / actor binding / model / provider toggle) are the user's call. There are two kinds of swap surface — static (config.toml seed) vs dynamic (mid-session transcript) — and both flow through the same reducer.
A. Static swap — permanent change to .crumb/config.toml
Applies before the session starts / from the next session onward. Pick whichever of the 3 surfaces matches your muscle memory:
| Surface | Invocation | Use case |
|---|---|---|
MCP crumb_model ★ |
"verifier 모델을 gemini-3.1-pro-preview 로", "set builder model to gpt-4o-mini", "effort 다 high 로", "codex 비활성화", "어떤 모델 쓰고 있어?" (read-only) | One NL line from any host (Claude Code / Codex / Gemini) |
CLI crumb model |
npx tsx src/index.ts model (interactive blessed TUI) / --show (read-only) / --apply "<NL>" |
Direct from terminal, scripts |
| Direct edit | .crumb/config.toml (TOML, gitignored) |
When git-history tracking is needed in production env |
Value format (resolve order: config.toml → preset.actors → preset.[defaults] → ambient → fallback):
harness ∈ {claude-code, codex, gemini-cli, anthropic-sdk, openai-sdk, google-sdk, mock}(legacygemini-sdkstill parses for replay-compat but rebinds to gemini-cli at runtime)model— canonical form fromMODEL_CATALOG(per-provider, top-down high→low). Gemini IDs accept both dot and dash forms (gemini-3.1-pro↔gemini-3.1-pro-preview).effort ∈ {low, med, high}— maps to Anthropicbudget_tokens(8K/24K/64K) / OpenAIreasoning.effort/ Geminithinking_budget.providers.<id>.enabled(boolean) — when a provider is disabled, the dispatcher falls back toclaude-localand emitskind=noteto surface the substitution.
B. Dynamic swap — mid-session transcript event
Applies immediately during the running session (reducer → progress_ledger.adapter_override or task_ledger.facts[sandwich_append]). All 3 surfaces produce the same transcript line:
| Surface | Example | Effect |
|---|---|---|
| TUI slash bar | /swap builder=mock / /goto verifier / /append @verifier check D5 carefully |
Updates the actor's adapter / next_speaker / sandwich starting from the next spawn |
inbox.txt |
echo "/swap builder=mock" >> sessions/<id>/inbox.txt |
The 500 ms watcher converts the line into a user.intervene event |
| JSON event | echo '{"from":"user","kind":"user.intervene","data":{"swap":{"from":"builder","to":"mock"}}}' | crumb event |
Lowest-level — both TUI and inbox eventually arrive here |
data.<key> semantics (see agents/coordinator.md Routing Rules for the full table):
swap: { from, to }—progress_ledger.adapter_override[from] = to(Paperclip BYO swap)goto: <actor>— forcenext_speaker = <actor>+ immediate spawn (LangGraphCommand(goto))target_actor: <actor>— fact tagged@<actor>(surfaced in the next spawn's sandwich)sandwich_append: <text>(+ optionaltarget_actor) — dispatcher concatenates onto every subsequent matching spawn's system prompt (G4)reset_circuit: <actor> | true— clears circuit_breakeractor: <actor>(only on pause/resume) — pause/resume that single actor instead of the whole session
Frontier mapping: LangGraph
Command(goto/update={...})(53/60), Paperclip BYO swap (38/60), CodexAPPEND_SYSTEM.md(38/60). Detailed backing:wiki/synthesis/bagelcode-user-intervention-frontier-2026-05-02.md. Scoring + ratchet alignment:wiki/synthesis/bagelcode-scoring-ratchet-frontier-2026-05-02.md.
C. Startup swap — --preset <name>
crumb run --goal "..." --preset video-research (and per-actor --bind overlays) decides the session's bindings in one shot. Preset files live at .crumb/presets/*.toml; crumb config "<NL>" recommends which preset fits a use case (no force — the user picks). ambient (no --preset) is the recommended default — entry host follows every actor.
One-glance — "Swap X to Y"
| Situation | Nearest command |
|---|---|
| Pre-session, change verifier model only (permanent) | crumb_model "verifier 모델을 gemini-3.1-pro-preview 로" |
| Pre-session, set every actor effort at once | crumb_model "effort 다 high 로" |
| Pre-session, disable codex temporarily | crumb_model "codex 비활성화" |
| Pre-session, swap entire preset | crumb run --preset <name> |
| Mid-session, change builder adapter only | TUI: /swap builder=mock or inbox: echo "/swap builder=mock" >> sessions/<id>/inbox.txt |
| Mid-session, force the next step | TUI: /goto verifier |
| Mid-session, augment a sandwich | TUI: /append @verifier <text> |
| Mid-session, reset a circuit | TUI: /reset-circuit builder |
Skill enforcement (do NOT)
- ❌ Spawn Crumb actors directly via the Task tool — only the
crumb rundispatcher has spawn authority. - ❌ Write artifacts from the host — that's the spawned sub-agent's authority, not yours.
- ❌ Append to
transcript.jsonldirectly — go through thecrumb eventCLI. - ❌ Produce
kind=qa.resultfrom an LLM — only the dispatcher's effect emits it (deterministic). - ❌ Activate this skill on non-game tasks — only on the triggers named in
description. - ❌ Auto-pick / force / guess a preset — when the user hasn't named one, run ambient (follow the entry host). Before asking, show the runnable options from
crumb doctor; do NOT pick (Anthropic 2026-03 "wrong tradeoff" lesson). - ❌ Force
provider × harness × model— these three dimensions are the user's call. Crumb is a helper, not a decider.
References
AGENTS.md(repo root) — ★ universal Crumb identity (Linux Foundation Agentic AI Foundation standard). Architecture invariants, actors, schema, multi-host entries, preset philosophy, universal Don't / Must. Auto-imported byCLAUDE.md(this skill's parent context) via@AGENTS.md. Read this before any actor sandwich.CLAUDE.md(repo root) — Claude Code-specific augmentation (.skills/24-skill router + Korean policy + progress + verify gate). Auto-loaded by Claude Code; importsAGENTS.mdinline.wiki/concepts/bagelcode-system-architecture-v0.1.md— ★ canonical v0.1 system architecture (multi-host + 3-tuple + 5 actor + 3-layer scoring + MCP Provider + persistence)wiki/synthesis/bagelcode-host-harness-decision.md— Hybrid (Skill + headless CLI) lock decisionwiki/concepts/bagelcode-verifier-isolation-matrix.md— backing for the actor-level splitwiki/concepts/bagelcode-system-architecture.md(v2) — §3-§9 (envelope / cache / per-turn flow / control plane / OTel) still valid in v0.1wiki/references/bagelcode-frontier-cli-convergence-2026.md— 4 CLI × 7 primitive (multi-host backing)wiki/references/bagelcode-llm-judge-frontier-2026.md— academic backbone for the 3-layer scoringagents/coordinator.md/agents/planner-lead.md/agents/researcher.md/agents/builder.md/agents/verifier.md/agents/builder-fallback.md— 6 actor sandwichesagents/specialists/— concept-designer / visual-designer (planner inline) + game-design.md (binding contract: §1 envelope + §3 video evidence schema + §5 DESIGN.md synth format, inline-read by researcher / planner / builder / verifier)agents/_event-protocol.md—crumb eventusage specprotocol/schemas/message.schema.json— 39-kind transcript schema (v0.1).crumb/config.toml+.crumb/presets/{video-research,mock}.toml— preset system (ambient default = no preset)skills/{tdd-iron-law,verification-before-completion,code-review-protocol,parallel-dispatch,subagent-spawn}.md— 5 procedural skills