
junhanliu-dev/espalier-engineering
Developer toolsTrain your AI coders the way you'd train a vine — discover your codebase's actual patterns, then encode them as Claude Code rules, skills, agents, hooks, and a guided pipeline so generated code lands...
Overview
An espalier trains a fruit tree to grow flat along a wall — pruned, wired, productive. it reads your codebase, discovers the patterns already in it, and encodes them as machine-enforced guardrails. Generated code grows along your conventions on the first try, not the fifth. That's it. One ~10–15 minute setup per repo, then every future change reuses it. AI coders write plausible code that . They invent helpers you already have, pick libraries you don't use, and throw where your repo standardised on Result years ago. It's not a model-intelligence problem — it's an problem. The model can't read your team's Slack history. 1. — parallel scout agents read your code and extract the conventions nobody wrote down. 2. — as a per-project espalier/ directory: always-loaded rules, phase-loaded skills, sub-agent definitions, a wiki, and programmatic hooks. 3.
README
An espalier trains a fruit tree to grow flat along a wall — pruned, wired, productive. Espalier does the same for your AI coding agents: it reads your codebase, discovers the patterns already in it, and encodes them as machine-enforced guardrails. Generated code grows along your conventions on the first try, not the fifth.
Quick start
/plugin marketplace add Junhanliu-dev/espalier-engineering
/plugin install espalier-engineering@espalier-engineering
/espalier-init
That’s it. One ~10–15 minute setup per repo, then every future change reuses it.
The problem
AI coders write plausible code that doesn’t fit your codebase. They invent helpers you already have, pick libraries you don’t use, and throw where your repo standardised on Result years ago. It’s not a model-intelligence problem — it’s an unwritten rules problem. The model can’t read your team’s Slack history.
How it works
/espalier-initdiscovers — parallel scout agents read your code and extract the conventions nobody wrote down.- It writes them down — as a per-project
espalier/directory: always-loaded rules, phase-loaded skills, sub-agent definitions, a wiki, and programmatic hooks. - Every change is gated — a coder agent writes, a separate reviewer agent (different tool set) checks against the same rules, and hooks block pushes that skip the gates.
- It stays honest — drift detection flags docs the code has outgrown; refreshes are gated, never silent.
espalier/
├── rules/ # always loaded: coding standards, security, production standards
├── skills/ # the pipelines: /espalier, /espalier-fix, /espalier-ask, …
├── agents/ # harness-coder / harness-reviewer / harness-security
├── wiki/ # architecture, data models, critical paths
├── hooks/ # programmatic gates: layer checks, pre-push, drift detect
├── maps/ # multi-session decision maps
└── changes/ # typed audit trail — one folder per requirement
The commands
| Command | What it does |
|---|---|
/espalier-init [--outside] |
One-time setup: discover conventions, generate + wire the guardrails. --outside keeps every espalier file out of the project repo, in a sibling espalier-main// with its own local git |
/espalier [--simple] |
Full 10-stage pipeline: grilled requirements → your approval → code → independent review → tests → gated push. --simple: grill → spec → your approval → code → review → tests, then stop with everything uncommitted for your own review — no records kept, no PR |
/espalier-fix [--simple] |
Slimmer bug-fix lane that auto-links each fix to the change that caused it; --simple as above |
/espalier-ask |
Read-only Q&A — answers from your docs, verified against the code, every claim sourced |
/espalier-audit |
Repo-wide security audit of existing code; findings hand off into fix runs |
/espalier-simplify [scope] |
Evidence-first simplification survey of existing code — proven cuts file as refactor changes the pipeline runs; the docs that described them get flagged for prune |
/espalier-map |
Plan work bigger than one session (epics, greenfield) as a decision map |
/espalier-maprun |
Execute a cleared map: headless workers in isolated worktrees, reviewable slice PRs |
/espalier-doctor / /espalier-prune |
Detect doc drift / refresh a stale doc (gated) |
/espalier-migrate |
Upgrade an existing install to the current version |
/espalier-repot |
Move an existing install outside the project repo (espalier-main//, local git, nothing tracked) — or back in |
Highlights along the way:
- Grilled requirements — before any code, Stage 1 interrogates the request exactly as hard as its vagueness warrants, then stops for your explicit sign-off. Later stages execute a spec they can’t misread.
- Separate judge — the reviewer is always a different agent with read-only tools. It can’t rubber-stamp its own work, and a security auditor joins it on every change’s trust boundary.
- Causal links — six months later, “why does this feature have 4 fixes against it?” is answered by the audit trail, automatically.
- Team-ready — on multi-dev repos, upkeep collapses to one rotating person’s ~15-minute weekly job (how it works).
Works with your agent — whichever it is
The espalier/ directory is platform-neutral; init wires it into any subset of:
- Claude Code — auto-loaded via
.claude/symlinks + settings hooks - OpenAI Codex — repo skills,
AGENTS.mdsection,.codex/agents + hooks (guide) - GitHub Copilot — Agent Skills,
copilot-instructions.md,.github/agents + hooks (guide)
Same rules, same gates, on all three. Add a platform later with one re-wire — nothing is ever unwired.
What it costs
Init is a one-time ~$2–5 on a medium repo (Opus main + Sonnet scouts + cache). Without Espalier, every request re-discovers your conventions and burns 3–5 review rounds — you typically earn the init cost back within 5–10 feature requests. Full token/cost breakdown, plan budgets, and per-command cost classes: docs/usage-cost.md.
Skip Espalier for: throwaway prototypes, single-file scripts, solo projects where consistency doesn’t matter. For anything you’ll iterate on for weeks, it pays back fast.
Philosophy
When an agent makes an error, engineer its elimination — not with prompt tweaks, but with files, rules, automated checks, and system structure.
- Discover, don’t prescribe — extract patterns from the actual code; never impose templates.
- Quality gates must be programmatic —
ci_status == 'success', not “check if CI passes”. - Separate execution from judgment — coder and reviewer are different agents with different tool sets.
- Context layering — rules always loaded; skills per phase; wiki on demand.
- Every rule has a reason — an observed pattern, or a known failure mode it prevents.
Latest release
v0.30.0 — the human-review lane and the outside install. Two independent additions, both opt-in, both contract-equal elsewhere. --simple on /espalier and /espalier-fix runs the grill, the spec, your approval, the coder, a reviewer-only review panel and the exit gate exactly as today — then stops. The coder never runs git, nothing is committed, no PR is opened, no record is kept: the working files live in a gitignored espalier/.simple// that is deleted when you approve the result, and the tree you are left with is exactly the agents’ uncommitted work, for you to read and commit by hand (a dirty tree at start is refused, so the diff is never mixed with yours). --outside on /espalier-init puts the whole espalier/ tree one level up, in espalier-main// with its own remote-less local git repo, reached through a locally excluded symlink; the .claude/ wiring goes to settings.local.json and CLAUDE.local.md, so the project repo never tracks one espalier byte and the espalier PR becomes a local commit in that folder. /espalier-repot moves an existing install out or back in, staging the git changes and leaving the commit to you. Migration #41; validation totals unchanged.
v0.29.0 — the PR split. A pipeline run now ends in two sibling pull requests instead of one branch carrying both the work and its bookkeeping: a code PR with every path outside espalier/, and an espalier PR with all of espalier/**, so the diff a reviewer reads is the change itself. Both target the canonical branch — a stacked design was probed first and rejected, because GitHub closes a child PR irrecoverably when its base branch is deleted. The new espalier/hooks/espalier-pr.sh projects the records in a temp worktree (the main checkout never switches branches), pushes by refspec so pre-push-gate.sh stays untouched, refuses a code branch that leaked espalier/ commits, and its ci-scan reports how the espalier PR lands against pull_request path filters and required checks. Between the two PRs, a Stage 7 doc sweep (harness-reviewer in WIKI SWEEP: mode) reads the change’s own diff against espalier/wiki/ and espalier/rules/, cites the hunk behind every patch, never deletes, and commits what that diff falsified onto the espalier PR. MAINTENANCE COMMITS v1 → v2: drift your change caused rides your espalier PR. Every gate, rubric, sentinel, round cap and escalation path is contract-equal to v0.28.1. Migration #40, validation checks 71–72 (62 / 67 / 72 by platform set).
v0.28.1 — the trim. Every shipped agent, skill, rule, and stage template says the same rules in fewer lines (8,723 → 8,611 template lines, −1.3%, across 23 files) with every rule, gate, rubric, sentinel, tag, cap, path, heading, and code fence byte-identical — only restated rationale, history notes, doubled sentences, and section announcers went; point-of-use repeats, exceptions, and the one worked example per format stayed on purpose. Migration #39 introduces a reusable mechanism for text-only refreshes: the release ships its exact template diff and applies it per file with git apply, all-or-nothing per file — a pristine file trims byte-for-byte, a customised file is left untouched and recorded (nothing to port). No new check, key, or hook.
v0.28.0 — the ceiling ledger. Second idea-level transplant from DietrichGebert/ponytail (MIT), inside the same rule as the first: conventions first, correctness within them, clarity then brevity break ties; every gate, rubric, verdict sentinel, round cap, and escalation path is contract-equal to v0.27.0, and every new finding is advisory P3. A deliberate shortcut with a KNOWN ceiling (a global lock, an unbounded scan, a naive heuristic, a cache with no eviction) now carries ONE line at the site — ceiling: ; — the one comment the budget already allows, so the upgrade path stays attached to the code it constrains instead of buried in a change folder; the reviewer files [ceiling] / [no-trigger] at P3, espalier-stats.sh prints the ledger (ceilings: markers=N no-trigger=M, one row per marker with its blame date), and /espalier-simplify hands every marker to its scouts as a lead (a fired trigger is a cut or an upgrade change). The ladder’s rung 4 opens a shipped references/platform-native.md lookup (“you think you need X / the platform has Y”) when conventions are silent; the floor keeps physical-world calibration knobs; a fix lands where every caller routes through, never on the reported path alone; the reviewer gains a shrink: tag that is invalid whenever the shorter form would draw a Readability row; the grill counts an over-specified mechanism as a signal and lands the requester’s choice as a Scope out-line or a kept criterion. No new lane, skill, or command. Migration #38, validation check 70 (60 / 65 / 70 by platform set). Design and the rejected pieces (intensity modes, “ship lazy and question”, a one-check test minimum, net: -N lines): docs/ponytail-v4-transplant-plan.md.
v0.27.0 — finding the unknowns. The map (requirements.md) is not the territory (the code); v0.27 gives the pipeline a channel for what the territory teaches during and after implementation — with the contract still frozen and every resolution a human’s. The coder logs each departure from an approved criterion under ### Deviations (the conservative option, the path:line contradiction, what was left undone) or stops with - BLOCKED-ON-REQUIREMENT: for the human to resolve — it never edits requirements.md; the reviewer’s Deviation Review verifies the block every round ([deviation] P1), the auditor files a relaxed control as P0, the Stage 4 PASS prints the deviations, Stage 10 presents an assembled delivery-brief.md and offers a quiz. Before coding, the grill reads the map to an unfamiliar requester first (Step 1.6, cited, never a proposal), shows its candidate builds at full tier, and records an undecided approach instead of choosing; ## References carries the requester’s named model; the approval gate leads with the decisions most likely to change. Contract-equal to v0.26.0 — no gate, rubric, sentinel, or cap changes; nothing sets a budget. Migration #37; validation 59/64/69. Design: docs/unknowns-plan-v1.md.
v0.26.0 — turn economy. Fewer agent turns and fewer spawns for the same gates, measured on real runs (a coder spawn is ~37 model turns at ~15s each; model latency is flat across context size; the contract phase was a second cold coder spawn that mostly re-verified tests already in the diff). Coders write the abuse test for every sensitive field they classify with the code and verify each cycle with ONE call — exit_gate, the gate’s own commands. The auditor’s contract carries covered_by: per entry, so a fully covered contract spawns no contract-phase coder and goes straight to the delta review, which still proves every entry itself (a covered_by that does not hold is its P0). The session-boundary preference is asked once at the approval gate (- Session-Boundary:); the Stage 2 and Stage 4 boundaries read it instead of asking. The bash blocks the orchestrator retyped every run are helpers now — regression_verify, record_commits, certificate_write, drift_index, stage85_drift, backlink_all. exit_gate scopes monorepo ((cd WS && CMD) || return 1 per line) and uv run pytest test bodies and starts tests as soon as the build is green; the push hook runs build ∥ lint by default. espalier-stats.sh shows contract phases covered at Stage 3 vs coder-spawned. Every gate, rubric, sentinel and round cap is contract-equal to v0.25; nothing sets a budget. Design + field data: docs/pipeline-speed-plan-v4.md, docs/pipeline-field-report-2026-09-14.md.
Full history and migration guides: CHANGELOG.md · upgrade any install with /espalier-migrate.
License
MIT — see LICENSE.
Recommended Tools
Try a different keyword or remove a filter.
Install
npx skillfish add junhanliu-dev/espalier-engineering