| name | figure-bake |
| description | Drive one figure from design idea to manuscript-ready vector PDF + outline-ready PNG. Loops `@figure-descriptor` → `@analysis-implementer` → `@reviewer` against a single fig-id in `figures.json`, with the `cropfig` skill auto-invoked at the end of each implement phase to keep manuscript + outline artifacts in lockstep. The third Phase 2 engine — a 3-agent loop, more complex than `/iterate-revision`'s 2-agent loop, and the only Phase 2 engine that executes real code per iteration. Safe to re-run; safe to resume after BLOCKED or HALT. |
| writes | ["figures","paper"] |
| cost_estimate_tokens | 45000 |
/figure-bake
This engine bakes a single figure from a one-line concept to a manuscript-grade artifact. It is the 3-agent loop demonstration of the orchestration pattern: each iteration runs design (@figure-descriptor) → implementation (@analysis-implementer, which actually executes Python/matplotlib code via Bash) → critique (@reviewer, which reads the rendered PNG/PDF multimodally), with a verdict at the end of every iter.
If you are reading this because Claude Code's skill auto-discovery surfaced it: invoke it via /figure-bake <fig-id>. Do not edit figures.json, the rendered figure files, or the per-figure script directory by hand while a run is in flight.
When this skill is invoked, immediately execute the workflow below. Do not only restate or summarize these instructions back to the user.
Signature
/figure-bake <fig-id> [--max-iter N] [--data <path>] [--code-dir <path>]
| Flag | Default | Purpose |
|---|
--max-iter | 3 | Hard cap. Reaching it without DONE/BLOCKED → HALT. Override via CLI or via ## Research stack field figure_bake.max_iter. |
--data | (resolved) | Dataset root. Three-layer resolution: CLI --data > env CLAUDE_RESEARCH_DATA_ROOT > CLAUDE.md ## Research stack data_root > hardcoded ./data/ (Phase 2 decision §3). |
--code-dir | null | Optional script directory the implementer writes its renderer(s) into. If unset, the implementer chooses (typically <manuscript_root>/figures/code/ or alongside vector_path). |
Examples:
/figure-bake fig1 — defaults; data resolves from env / CLAUDE.md
/figure-bake fig2 --max-iter 5 --data ~/datasets/2026-cohort/
/figure-bake fig3 --code-dir code/figures/
The loop
phase 01 — precheck (resolve fig-id, resolve data_root, validate path)
phase 02 — brief (@figure-descriptor → design brief, no images) ┐
phase 03 — implement (@analysis-implementer → render code + cropfig) │ repeated up to --max-iter
phase 04 — critique (@reviewer reads the rendered figure) │
phase 05 — evaluate (verdict rule on critique severity list) ┘
phase 06 — finalize (user summary + _run-log.jsonl append + embed hint)
Loop body is phases 02 → 03 → 04 → 05. The brief produced in phase 02 of iter 1 is reused in iter 2+ unless @reviewer flags a structural design issue that bumps brief_status back to "drafted"; that triggers a fresh brief on the next iter (see phase 02 step 4 for the rule).
The loop is driven by ../orchestrate/phases/04-loop.md; this engine hands it a dispatch plan + a verdict rule and lets the primitive run them.
Phase execution
Execute phases sequentially. For each phase, read the linked file and follow its instructions exactly.
- Phase 1 — Precheck. Read
phases/01-precheck.md.
- Phase 2 — Brief. Read
phases/02-brief.md.
- Phase 3 — Implement. Read
phases/03-implement.md.
- Phase 4 — Critique. Read
phases/04-critique.md.
- Phase 5 — Evaluate. Read
phases/05-evaluate.md.
- Phase 6 — Finalize. Read
phases/06-finalize.md.
Phases 02 → 03 → 04 → 05 form the loop body. The orchestrate loop primitive runs them up to --max-iter times.
Composition
This engine imports the following primitives from ../orchestrate/:
phases/01-state-read.md — read figures.json in phase 01 (and re-read in phases 03, 04, 05 when status updates land back on disk) and paper.json in phase 01 (manuscript_root field is needed to resolve relative vector_path defaults).
phases/02-dispatch.md — dispatch @figure-descriptor (phase 02), @analysis-implementer (phase 03), and @reviewer (phase 04) via the Agent tool with inlined persona bodies.
phases/03-evaluate.md — apply the severity-threshold verdict rule defined in phases/05-evaluate.md of this engine.
phases/04-loop.md — drive the 3-step dispatch plan through up to max_iter iterations; own _run-log.jsonl writes; honor budget_tokens post-hoc.
In addition, this engine calls one non-engine skill: ../cropfig/SKILL.md. cropfig is invoked from phase 03 (after the implementer succeeds) to produce the manuscript-grade vector PDF and outline-grade raster PNG from one cropped source. This is allowed under Phase 2 decision §5 — engines may invoke skills; they may not invoke other engines.
This engine does not invent its own state-read, dispatch, or loop mechanics. If you find yourself replicating that logic inside a phase file, you're solving the wrong problem — fix the primitive, do not fork it.
Three-layer data_root resolution
Phase 01 resolves data_root in this order (first non-empty wins):
- CLI
--data <path> — one-off override for this invocation.
- Env
CLAUDE_RESEARCH_DATA_ROOT — useful for CI / batch runs.
- CLAUDE.md
## Research stack block, field data_root — the project-default. Set once during /start-research or hand-edited.
- Hardcoded fallback
./data/ — last-resort relative path.
The resolved path is then validated: phase 01 aborts if the path does not exist on disk. --data is the only layer that overrides the validation order; an env or CLAUDE.md value that doesn't exist still aborts (the user must fix their config). See Phase 2 decision §3 for the rationale (mirrors the env → CLAUDE.md → default pattern already used elsewhere in the plugin).
Verdict rule
The verdict is computed at the end of each iteration's @reviewer critique. Canonical rule (passed as a severity-threshold rule spec to ../orchestrate/phases/03-evaluate.md):
| iter | critical | major | result |
|---|
| any | ≥ 1 | any | BLOCKED — critical issue requires structural redesign |
| < max | 0 | ≥ 1 | CONTINUE — loop another iteration |
| < max | 0 | 0 | DONE — figure approved |
| = max | 0 | ≥ 1 | HALT — iterations exhausted, majors remain |
| = max | 0 | 0 | DONE — finished on the last allowed iter |
minor and nit issues never gate the verdict. They are logged to figures.json.figures[<fig-id>].critiques[] and surfaced in the phase 06 summary.
The verdict drives the per-figure status fields in figures.json.figures[<fig-id>]:
| Verdict | critique_status | impl_status | brief_status |
|---|
| DONE | done | approved | approved |
| BLOCKED | done | drafted | unchanged (typically drafted — the reviewer's critical flag may target the brief; if so, set drafted so phase 02 re-runs on resume) |
| HALT | done | drafted | unchanged |
| CONTINUE | done | drafted | unchanged (or drafted if the reviewer flagged a brief-level issue — see phase 05 step 4) |
Phase 05 owns these writes. Phase 06 only reads them.
Cost model
Each iteration is 3 Agent-tool dispatches (descriptor + implementer + reviewer) plus one cropfig invocation. With max_iter = 3, a typical run is ≤ 9 subagent calls + 3 cropfig passes. The cost_estimate_tokens: 45000 frontmatter field is a coarse upper-bound for /supervisor-drive budget gating (Phase 3 §6); actuals land in _run-log.jsonl post-hoc per Phase 0 decision §6.
Per-iter cost is dominated by:
- the persona bodies (descriptor ~200 lines, implementer ~170 lines, reviewer ~200 lines) inlined into each dispatch (Phase 0 decision §5),
- the rendered figure's bytes when
@reviewer reads the PNG via the Read tool (multimodal — non-trivial token weight),
- the implementer's actual code-execution tokens (Bash output, error tracebacks, plotting library prints).
Why the default max_iter = 3 is deliberately small: each iter triggers real code execution by @analysis-implementer, which is the most expensive operation in the engine and the slowest. Bumping max_iter higher than 3 should be a conscious call, not an accident — pass it explicitly.
What this engine does NOT do
- Does not insert
\includegraphics{...} into any .tex file. The implementer writes the rendering script + the PDF; embedding the PDF into the manuscript is the user's call. Phase 06 suggests /sync as the right tool for that, because /sync already understands the outline-side embed pattern through cropfig func 3.
- Does not crop manually. The crop pass is one call to the
cropfig skill, which owns the band-classification heuristic + the dual PDF/PNG output. This engine doesn't duplicate that logic.
- Does not mutate any other figure's entry in
figures.json. One run = one fig-id.
- Does not call
/iterate-revision, /respond-reviewer, /outline-expand, or /literature-sweep. Engines are leaves (Phase 2 decision §5). If a reviewer comment says "this figure proves nothing about the methods section," that's a hint to run /iterate-revision sections/methods.tex afterward — the user, not this engine, makes that call.
- Does not commit to git between iterations by default.
on_iter_end is left unset on the loop primitive.
- Does not retry the implementer on a Python traceback inside the same iter. A failed render becomes a BLOCKED verdict and the user fixes the data / script before re-running.
- Does not auto-resolve the dataset path against a default if the user-configured path is missing. Phase 01 aborts; the user must fix the CLAUDE.md / env /
--data value.
Re-running policy
- Already-DONE figure (status fields all
approved / done) + first invocation in a fresh session → phase 01 lets the run proceed (the user re-invoked for a reason — perhaps the data changed). Iter counter continues; no early-exit flag currently.
- BLOCKED figure → phase 01 lets the run proceed; the user is expected to have addressed the critical issue (revised the brief, fixed the data, or moved to a different
vector_path) before re-running. Iter counter continues.
- HALT figure → phase 01 lets the run proceed; iter counter continues from where it left off.
- Missing fig-id in
figures.json → phase 01 creates the entry (with brief_status: "missing", impl_status: "missing", critique_status: "pending", iter: 0, and a templated vector_path of <manuscript_root>/figures/<fig-id>.pdf) and proceeds into phase 02.
See also