v0.23.0 — round economy

Train your AI coders like you'd train a vine.

An espalier trains a fruit tree to grow flat along a wall — pruned, wired, productive, impossible to mistake for a wild one. Espalier does the same for your AI coding agents: discover the patterns already in your codebase, encode them as constraints, get code that lands inside your conventions on the first try, not the fifth.

claude code
$/plugin marketplace add Junhanliu-dev/espalier-engineering
$/plugin install espalier-engineering@espalier-engineering
$/espalier-init
PLATE I · ESPALIER EN GOBLET Vitis methodicus ․ trained 2026 50 40 30 20 10 0 CM harvest tier training ii training i wiki/ on-demand hooks/ programmatic agents/ delegated skills/ phase-loaded rules/ always loaded /ESPALIER-INIT ․ ROOTSTOCK Fig. 1a — Pattern discovery & encoding
02 The problem

Plausible code that doesn't fit.

AI coders write plausible-looking code that doesn't match your codebase. It's not a model intelligence problem. It's an unwritten rules problem — patterns every experienced developer on the team knows but nobody documented. The model can't read your team's Slack history.

// invented

It writes a new formatDate() helper when you already have one three folders away.

// split

It carves a component into four files when the team keeps colocated views in one.

// picked

It reaches for winston when the project standardised on pino two years ago.

// threw

It handles errors with throw when your repo committed to Result<T> in 2022.

The model can't read your team's Slack history.

03 The solution

Train the vine, then let it grow.

Espalier reads your code, extracts those rules, and writes them down as machine-enforceable constraints. The next time the AI codes, it loads your project's rules automatically and follows them. A reviewer agent — different agent, different tool set — checks against the same rules before any commit lands.

Rework cycles
3–5 typically 1
The first attempt lands inside your conventions because the conventions are loaded with the prompt and enforced by the reviewer. The middle four rounds vanish.
i. Discover

Read your code first.

Ten concurrent scouts inspect architecture, coding patterns, testing posture, CI, unwritten rules, and per-layer specs. An oracle pulls vendor docs in parallel. No templates from other projects.

ii. Encode

Write the rules down.

Patterns become rules/ always-loaded into Claude Code, skills/ loaded per phase, wiki/ on demand. Every rule cites the observed pattern that justifies it.

iii. Enforce

Gate every stage.

A separate reviewer agent (Read/Grep only — no Write) checks each output. Pre-push hooks block layer-boundary violations. Failed gates roll back; rollback counters escalate to humans.

04 What it generates

Inside the espalier/ directory.

After /espalier-init, a single per-project directory wires into Claude Code via symlinks. Hover any path to see what it does and when it loads.

espalier/
├── rules/ # structure, coding, security, production standards ├── skills/ # coding, review, testing, /espalier-fix, /espalier-ask, /espalier-audit… ├── agents/ # harness-coder, harness-reviewer, harness-security ├── wiki/ # architecture, data models, critical paths ├── hooks/ # layer boundary, pre-push, post-merge drift ├── pipeline.md # 10-stage workflow with gates & rollback ├── maps/ # decision maps — tickets, fog, findings digests └── changes/ # feat/, fix/, refactor/, docs/ — one dir per req
05 The two pipelines

One full lane, one bug lane.

After init, your repo exposes two orchestrators. Each stage has a programmatic gate, each gate either passes deterministically or rolls back. Brass dots are gates; failed gates accumulate into a counter that triggers human escalation.

/espalier <requirement>
Full 10-stage pipeline
For features, refactors, and large fixes. Requirement → reqs review → coding with tests (sub-agent) → two-agent panel review of code + tests → security contract phase (or SKIPPED on a non-sensitive change) → push → CI verify → deploy verify → user confirmation. test-mode: serial restores the pre-fold flow.
01 requirement 02 reqs review 03 code + tests sub-agent 04 panel review 2 agents, code + tests 05 contract abuse tests · or SKIPPED 06 delta review 07 push 08 CI verify 09 deploy verify 10 confirm
/espalier-fix <bug>
7-stage bug-fix lane
Slimmer than the full pipeline — same stage numbering (no Stage 2), so gates and stats read both lanes identically. Stage 0 auto-link discovery walks git blame + reverse-lookup cache + squash-merge mapping to bind the fix back to the feature change that caused it; the Stage 3 exit gate re-proves the regression test on every coder return.
00 auto-link causal binding 01 reproduce 03 fix + tests sub-agent · regression proof 04 panel review 2 agents 05/06 contract or SKIPPED 07 push back-link
Prefix Type Output directory
feat: (or no prefix) Feature espalier/changes/feat/<slug>/
refactor: Refactor espalier/changes/refactor/<slug>/
docs: Docs espalier/changes/docs/<slug>/
fix: Bug fix use /espalier-fix instead
06 v0.8.0 · requirements approval gate

Sign off the requirement before a line of code is written.

Stage 1 → Stage 2 → Stage 3 used to chain automatically — coding began the moment the requirement doc existed. Now both pipelines stop after the requirement is written and reviewed, and wait for your explicit go-ahead. A Stage 2 PASS no longer authorizes coding on its own.

i
write — Stage 1 produces the requirement, grilled into requirements.md.
ii
review — Stage 2 checks it for completeness and scope.
iii
gate — the pipeline halts on Approve / Edit / Abort. The full pipeline gates between review and coding; the fix lane gates after Stage 1, downstream of its escalation gate.
iv
then code — only an explicit Approve lets Stage 3 begin.
Interactive-only — an unattended (no-TTY) run auto-approves so the pipeline never hangs. No new skill, no new stage.

v0.23.0 — round economy: the review rounds get cheaper without getting softer. Under test-mode: folded (the default, both lanes) the coder writes the change’s interface and failure-mode tests with the code, the Stage 3 exit gate runs build + lint + the scoped tests on every coder return — the panel never reviews unexecuted tests — and the two-agent Stage 4 panel reviews code and tests in one verdict, with the code in view, where the old Stage 6 test review was code-blind. Stages 5/6 become the security abuse-test contract phase (contract spawn → gate re-run → one delta-scoped review) or plain SKIPPED rows on a non-sensitive change — zero post-panel spawns on the happy path, stage numbers untouched for every integer consumer (push gate, stats, maprun). A contract-phase fix that touches non-test code routes back to a full panel round. The fix lane moves regression verification into the same exit gate, re-proven on every coder return. The map loop learns from failure: FAIL rounds snapshot one bracketed finding line per agent, charted changes write a findings digest under espalier/maps/{slug}/findings/, and slice adoption folds the newest P0/P1 patterns into sibling requirements — facts for more scrutiny, never a checklist ceiling. The CLEARED → FILED handoff now scores each slice’s crispness with grill’s new mode=score; a would-be-full slice is refused and its fog becomes tickets. test-mode: serial keeps the pre-fold flow per repo. Existing users run /espalier-migrate.

v0.22.0 — pipeline speed II: every change is dispatch-order, read-scope, or wait-scheduling — same gates, sentinels, round caps, certificates, and both human checkpoints. The test-writer joined the round-1 review panel speculatively with quarantine-on-FAIL (since retired by v0.23’s fold), Stage 6 re-review rounds gained delta scope, and espalier-stats.sh learned per-stage wall-clock split human-wait / agent-work — the field data the fold decision was designed against. Pre-flight drift signals ride the approval gate instead of their own prompt, deploy targets pre-authorize at that same gate, Stage 8 waits on CI in one chunked watch call, and opt-in hook-parallel-gates runs the push gate’s build/lint/tests concurrently. Field-found fix: the certificate read is line-anchored, so prose quoting a token can never outrank the real Status line. v0.22.1 follows with the comment diet — the default is NO comment, one plain line only for a constraint the code cannot show, always subordinate to the project’s own discovered convention.

v0.21.0 — pipeline speed: the redundant reads and redundant human waits removed, nothing about the quality machinery changed. A context pack assembled once at Stage 3 entry replaces the 5–11 per-spawn discovery cold starts (paths and facts only, never conclusions — every agent still reads the named files itself); re-review rounds are delta-scoped (a floor, not a ceiling — the whole-diff verdict duty stands, and the Reviewed-Diff fingerprint still blocks unreviewed edits at push); disjoint sub-tasks may code in parallel; the approval gate pre-authorizes the Stage 7 push target; grill batches independent light-tier questions. v0.21.1 starts the comment-brevity line.

v0.20.0 — slice PRs: the run lane’s work becomes reviewable one ticket at a time, without changing its merge topology (the motivating 14-slice production run ended in a single 103-file integration PR — mergeable, not reviewable). Opt-in per map: each PASSED ticket pushes its branch and opens a PR against the integration branch before the local merge, so the forge closes it as merged on its own — the slice’s diff and CI verdict preserved as the review surface; at run completion an assembly PR links every slice PR, and merging it stays a human act. worker_mode: inline arrives too — the master works tickets in-session with no headless CLI, the one mode a Copilot-only install can use. Plus the shared-config push-block poisoning fix (latent in v0.19, fatal once the master must push).

v0.19.0 — the run lane: a cleared map executes itself, survivably. /espalier-maprun drives a map’s FILED slices to completion over hours and days — an interactive master pass (reap → halt-on-escalation → relay parked questions → merge → grill-then-dispatch → verify → report, then STOP), up to N headless workers in isolated git worktrees with pushes blocked at git-config level, and disk state beside the map, so a master killed at any instant loses at most the pass it was in. Workers park questions instead of answering them; death diagnosis distinguishes resumable quota exhaustion from real failure; maprun.py watch is a live TUI dashboard and feed streams a worker’s whole thought process. Field-built inside a real 14-slice production run.

v0.18.0 — the map lane: multi-session planning above the pipeline, and a road into greenfield. /espalier-map charts an effort too big for one session as a decision map — destination grilled first, one file per ticket (no cross-ticket merge conflicts; claims are the lock), fog tracked explicitly, a 9-open-ticket anti-waterfall cap. map-guard.sh makes plan-don’t-do machine-verified: while a map session is active, writes outside espalier/maps/ are blocked at hook level. A cleared map hands off as FILED change skeletons the pipeline adopts, so the audit chain reads decision → ticket → change → commit; greenfield repos chart first, then bind init’s discovery from the decisions. Concept adapted from wayfinder (MIT); the enforcement layer is Espalier’s.

v0.16.0 + v0.17.0 — multi-dev maintenance: espalier upkeep becomes team-shaped. A tracked one-line doctor stamp (last-writer-wins, deliberately never union-merged) makes freshness team-wide; convention observations move to file-per-key TSVs, where a concurrent same-key decision surfaces as an ordinary ~5-line git conflict — which is the race detection; a weekly gardener rota runs the ~15-minute doctor → prune → restamp loop as one maintenance PR while everyone else’s pre-flight defaults to Proceed; conv_fold is the single width-tolerant reader over legacy and per-key state (readers shipped before the writer — a v0.16 clone reads v0.17 files fine). Canonical-remote/branch detection, a union merge attribute for ask-gaps, and an optional CODEOWNERS block round out the compatibility floor.

v0.15.0 — Espalier learns Copilot; the wiring layer goes three-platform. --platforms now takes any subset of claude / codex / copilot. For GitHub Copilot: the 12 skills land as Agent Skills in .github/skills/ (VS Code, Copilot CLI, and the cloud coding agent all read it; invoked /espalier…), the always-loaded rules ride a generated .github/copilot-instructions.md section, coder/reviewer/security separation becomes @harness-* custom agents, and the two quality gates install as Copilot hooks in .github/hooks/espalier-gates.json — a small adapter translates Copilot’s camelCase payload so the same two wrapper scripts now gate all three platforms (non-zero preToolUse exits fail closed, matching the exit-2 contract). Additive wiring, byte-stable claude-only output. Suites: bootstrap 117/117, hooks 86/86; validation 46/51/56 by platform set. Existing users run /espalier-migrate.

v0.14.0 — Espalier learns Codex. The same discovered guardrails wire into OpenAI Codex as a first-class target: the 12 skills land as repo skills in .agents/skills/ (invoked $espalier, $espalier-fix, …), the always-loaded rules ride a generated AGENTS.md section, coder/reviewer/security separation becomes .codex/agents/harness-*.toml subagents, and the two quality gates install as PreToolUse/PostToolUse hooks in .codex/config.toml — Codex shares Claude Code’s hook contract, so the same wrapper scripts serve both platforms. Init asks which platform(s) to wire (Claude Code / Codex / both); adding codex to an existing install is one additive --wire-only run that never unwires anything. Suites: bootstrap 97/97, hooks 73/73; validation grows to 51 checks on codex installs while claude-only output stays byte-stable. Existing users run /espalier-migrate.

v0.13.0 — the coder gets a laziness ladder; the reviewer gets a minimalism lens. Espalier enforced fit but had no notion of size: a convention-perfect 200-line date picker passed every gate while <input type="date"> was never considered. The coder now climbs a Solution Selection Ladder before choosing a change’s shape — reuse what the project has → the convention-named mechanism → stdlib / native / installed dep → the leanest compliant implementation; a NEW dependency needs a requirements.md line. The reviewer runs an advisory Minimalism Review: delete:/stdlib:/native:/yagni: findings capped at P2/P3 so they can never re-open the Stage 4 loop, with one objectively-checkable P1 — a new dependency covering what stdlib or an installed dep already provides. Governing rule everywhere: conventions first, correctness within them, brevity only breaks ties. Idea adapted from ponytail (MIT), re-grounded convention-first. Gated on both eval suites (coder 4/4 incl. an overbuild trap; review 8/8, catch-rate 1.00, FP 0) and quality-scored by independent agents — avg 82.3 → 85.9 after the fix round. Existing users run /espalier-migrate.

v0.12.0 — Stage 1 now cross-references your own conventions. A blind-spot pass (grill Step 1.5) checks every requirement against your espalier/rules/ and espalier/wiki/ and surfaces the collision you couldn’t: an approach that violates an encoded rule (throw where the repo standardised on Result<T>), a capability the wiki already documents, an unstated ripple across a documented critical path. Each confirmed collision becomes a Stage 1 question citing the exact rules/<file>#section, and floors the tier so a crisp-but-colliding requirement can’t skip grilling — the reviewer’s convention check, pulled a full rework round earlier. It verifies each doc claim against the code first, so a stale doc is flagged (mark_stale), never raised as a false collision — and stays silent when there’s no map to collide with, so nothing changes on an uninitialised input. The grill eval harness is hardened too (all four known-issues fixed); the golden set now runs 24/24, catch-rate 1.00. Existing users run /espalier-migrate.

v0.11.0 — the push gate actually blocks now. Claude Code hooks block only on exit code 2 with the message on stderr — the old gates’ exit 1 was a silent no-op. Every gate script and wrapper speaks the real contract (the wrapper fails closed without python and matches git -C … push and multi-line commands), the Stage 4/6 gates read the verdict word — closing the FAIL-with-P1-only and ESCALATION_REQUIRED-as-PASS holes — the reviewer/security agents get a record-file-scoped Write tool, and .claude/ symlinks go relative so a moved repo keeps working. Existing users run /espalier-migrate.

v0.9.1 — the review-round and rollback hard stops are now configurable. How many coder↔reviewer rounds run before the pipeline stops and asks a human used to be hardcoded prose (Max 2 P0 rounds) in the pipeline templates. It now lives in a tracked espalier/.espalier-configmax-req-rounds, max-code-rounds, max-test-rounds, max-rollbacks — that the orchestrator reads at runtime, falling back to 3 when a key is absent so a missing file never blocks. Defaults unify to 3 (code and test caps rise 2→3). Existing users run /espalier-migrate.

v0.9.0 — Stage 4 now runs a two-agent panel. Alongside the correctness reviewer, a new harness-security auditor checks the change's trust boundary on one rule — never trust data the frontend sent. It hard-blocks any sensitive field (price, userId, role, orderId, status) the backend fails to re-derive, re-authorize, or recompute, and requires an abuse test proving the tampered value is rejected and never persisted. The coder reads the same taxonomy while writing, so security shifts left. /espalier-audit runs the same auditor repo-wide over your existing code, dispatchable to /espalier-fix. The release also adds an always-loaded production-standards bar — resilience, observability, data-safety — the coder writes to and the reviewer enforces at tiered severity, plus a fail-closed push gate and a programmatic build/lint gate.

v0.8.2 — code review is now a fixpoint loop. When the reviewer files a P0 and the coder fixes it, the fix is re-reviewed — the only way out is a fresh review of the current code returning zero P0, so a new bug introduced by the fix can't slip through unreviewed. A push-gate certificate binds the verdict to a fingerprint of the reviewed source, so a fix that skips re-review fails closed at push time.

v0.8.1 — the coder and reviewer sub-agents now reason about every surface a change touches — admin / CRUD UIs, API validation, client forms, persisted data, other callers — not just the happy path. Closes a class of fix-round where a now system-derived value left "required" on a UI blocks the user before the server-side hook ever runs.

Existing users run /espalier-migrate — it auto-detects your version and applies the chain (… v0.21.0 → v0.22.0 → v0.22.1 → v0.23.0) in order.

New pipeline runs get the loop; in-flight changes are unaffected. Non-breaking.

07 v0.7.0 · read-only ask lane

Ask the codebase. Answered from your docs, checked against the code.

/espalier-ask <question> answers "how does this work / where is it / why is it built this way / what changed recently" — reading the espalier/ docs you already paid to build, instead of crawling the whole codebase from scratch every time.

i
classify — where / how / why / what-changed, so the question hits the right source: the wiki for how/where, the change history for why.
ii
read the docs first — wiki, rules, and changes/ records, before any from-scratch codebase search.
iii
verify against code — every doc claim is confirmed by reading the cited file before it lands in the answer. The wiki is the map; the code is the truth.
iv
answer with sources — doc path and file:line on every claim; fall back to a code search when the docs come up short.
Strictly read-only — never edits a doc, never opens a changes/ folder. No stages, no gates.

Two notify-only byproducts: a stale wiki it trips over while answering gets flagged (the same signal /espalier-doctor produces, pointing you at /espalier-prune), and a question the docs can't answer is logged to a git-tracked .ask-gaps.tsv — a backlog of what the wiki should cover next.

Existing users run /espalier-migrate — it auto-detects your version and applies the chain (… v0.6 → v0.7) in order.

Purely additive — no pipeline change. Non-breaking.

08 v0.6.0 · Stage 1 grilling

Interrogate the spec before the code.

A vague requirement — or an unconfirmed bug diagnosis — that passes Stage 1 is trusted by every later stage, and no later gate audits it. /espalier now grills that input first, turning an under-specified ask into one a coding agent can execute without guessing.

i
score the ambiguity — count concrete signals: undefined terms, unstated actors, missing failure behaviour, hidden quantifiers, unscoped edge cases.
ii
choose a depth — the signal count maps to a tier, so a crisp spec gets waved through and a fuzzy one gets pressed.
iii
ask, adaptively — a short sequential interrogation, only as many questions as the vagueness warrants. Each answer lands in requirements.md.
iv
two modesspec for /espalier requirements, diagnosis for /espalier-fix root causes — so the lane never patches a symptom on an unconfirmed theory.
On by default. --no-grill skips one invocation; an unattended (no-TTY) run auto-skips so the pipeline never hangs.

Change folders are now date-prefixed — changes/feat/2026-06-02-add-login/ — so a directory listing sorts chronologically.

Existing users run /espalier-migrate — it auto-detects your version and applies the chain (… v0.5 → v0.6) in order.

Stage 1 grilling is additive and interactive-only. Non-breaking.

09 v0.5.0 · doc-drift detection

Keep the guardrails honest.

The artifacts /espalier-init generates describe your codebase on init day. As code evolves, v0.5.0 keeps them in sync — without ever silently overwriting anything you wrote.

i
post-merge hook detects drift in generated docs and writes a gitignored sidecar — your tree stays clean.
ii
reviewer agent captures convention shifts a file diff can't see and adds them to a cross-PR convention index.
iii
Stage 0 pre-flight on every pipeline run surfaces all drift in one place before any new work begins.
iv
/espalier-prune <path> refreshes a flagged artifact via a single scout and a gated diff.
v
/espalier-doctor runs a periodic scan, re-scouting a handful of artifacts to catch drift no diff caught.
Nothing is ever auto-overwritten. Every refresh is gated by you.

Existing users on v0.4.x or earlier run /espalier-migrate — it auto-detects your installed version and applies the migration chain (v0.1 → v0.2 → v0.4 → v0.5) in order.

Drift detection is additive. Non-breaking for v0.4 installs.

10 Cost, honestly

Once-per-repo vs. every-request-forever.

/espalier-init is a heavy one-time tax — typically 10–15 minutes on a medium codebase (~150 source files). Every subsequent /espalier and /espalier-fix reuses what's generated.

Without Espalier

Re-discovery every request Agent re-reads source files into context each time. Same exploration, same tokens, every time.
3–5 review rounds Code doesn't fit, reviewer flags it, agent retries — across the full conversation per request.
Human re-explains You catch "this doesn't match our patterns" and re-articulate them by hand each round.
Silent drift Implicit rules forgotten across the codebase as the model picks whatever-it-felt-like-today.

With Espalier

Conventions loaded once ~3K tokens from rules/, always cached, no re-discovery.
Typically 1 round Code lands inside conventions on the first attempt because the conventions are in the prompt.
Reviewer agent enforces You review business logic only. Pattern adherence is automatic.
Drift detected, not silent Post-merge hook + reviewer + /espalier-doctor flag artifacts as they age.

Per /espalier-init in USD

Medium repo, ~150 source files. Across ~5–10 features you earn this back via dropped rework rounds.

Setup Per run
Opus everywhere (no cache)$10–20
Opus main + Sonnet scouts (no cache)$4–8
Opus main + Sonnet scouts + cache hits (typical)$2–5
Sonnet everywhere + cache$1–3
11 Philosophy

Five principles.

When an agent makes an error, engineer its elimination — not with prompt tweaks, but with files, rules, automated checks, and system structure.

i.

Discover,
don't prescribe.

Read the actual code, extract patterns. Never impose templates from other projects.

ii.

Gates are programmatic.

ci_status == 'success' AND tests_passed == total, not "check if CI passes".

iii.

Separate execution from judgment.

Coder and reviewer are different invocations with different tool sets. Reviewer gets Read/Grep only.

iv.

Context in layers.

Rules always loaded. Skills per phase. Agents see only their scope. Wiki on demand.

v.

Every rule has a reason.

Either reflects an observed pattern, or prevents a known failure mode. Nothing arbitrary.

An espalier is a tree that hasn't forgotten what shape it's meant to hold.

Anonymous · 1839
12 Install

Three ways in.

Marketplace install is the standard path. Manual clone is for hacking on Espalier itself. Project-scoped drops a single skill into one repo without going global.

Recommended. Updates flow in via /plugin update espalier-engineering.

claude code · marketplace
$/plugin marketplace add Junhanliu-dev/espalier-engineering
$/plugin install espalier-engineering@espalier-engineering
$# in any project:
$/espalier-init

For users not on the plugin path, or while iterating on Espalier itself.

shell · manual
$git clone https://github.com/Junhanliu-dev/espalier-engineering ~/repos/espalier-engineering
$ln -sfn ~/repos/espalier-engineering/skills/espalier-init \
~/.claude/skills/espalier-init
$# restart Claude Code, then in any project:
$/espalier-init

Update with cd ~/repos/espalier-engineering && git pull.

Drop the skill inside a single project rather than installing globally.

shell · project-scoped
$mkdir -p .claude/skills
$ln -sfn /path/to/espalier-engineering/skills/espalier-init \
.claude/skills/espalier-init