A living knowledge substrate for AI-assisted engineering
emerik keeps the decisions behind your code honest — it harvests source-bound notes as you work, lets stewards author verifiable contracts, and surfaces drift as a failing check. It is a git-native CLI: no server, no database, just your repository.
What is emerik
As you build software with AI agents, the reasons behind the code — why a value is what it is, which invariant must hold, what was tried and rejected — evaporate. emerik captures that knowledge as small, content-addressed artifacts committed alongside your code, and gives you commands to reach it, verify it, and keep it from rotting.
Everything lives in a .emerik/ directory in your repo, stored append-only. There is
no external service: your git remote is the only source of truth, so knowledge travels with the
code and is versioned by the same history.
emerik is named for Emerik Blum, founder of Energoinvest — the same namesake as bloomteq, whose design system dresses these pages.
The mental model
Four ideas carry the whole system. Learn these and every command reads naturally.
Pin
A low-ceremony, source-bound note harvested automatically from a commit. When its source changes, emerik re-derives it; when the source is deleted, it is invalidated. Pins are how knowledge stays fresh.
Anchor
A steward-authored, reviewed contract that carries a check. When the check fails, drift surfaces at the severity you chose. Anchors are how knowledge stays true.
Lease
A time-bounded, exclusive-write claim on a territory, recorded on a dedicated git ref via compare-and-swap. Two machines can never hold the same turf — with no lock server.
The substrate
The append-only .emerik/ store. A change is a new artifact that supersedes the old; a deletion is a tombstone. The current graph is a projection over git history.
The load-bearing promise is honesty: a check that guards an invariant must be able to fail when the invariant breaks. You will see this proven end-to-end in the honesty loop.
Requirements
- Node.js 20 or newer. The CLI is tested on Node 20 and 22.
- git. emerik is git-native — every command operates on a git repository.
- pnpm. The project's package manager; the lockfile is committed. Install it with
corepack enableor from pnpm.io.
Installation
emerik v0.1.0 installs from source. Clone the repository, install dependencies, and build the CLI bundle.
Clone & build
git clone <emerik-repo-url> emerik
cd emerik
pnpm install
pnpm build # tsc --noEmit + tsup + oclif manifest → dist/
Verify the binary runs
./bin/run.js --version # → emerik/0.1.0 …
./bin/run.js --help # show the full command tree
Put emerik on your PATH (optional)
So you can call emerik from any repository instead of the full path:
# option A — symlink the launcher into a directory already on PATH
ln -s "$(pwd)/bin/run.js" /usr/local/bin/emerik
# option B — npm link (from the emerik project root)
npm link
The rest of this guide writes emerik <command>. If you skipped step 03, substitute
the path to bin/run.js.
Quickstart
From inside any git repository, three commands take you from nothing to a verified knowledge graph.
cd your-project
emerik init # create .emerik/, ignore rules, git hooks + wire your agent
git commit -am "chore: emerik init"
emerik harvest # harvest source-bound Pins from HEAD
emerik verify # run the convention + Anchor gate (exit 0 = clean)
Working inside a coding agent? The guided path is the front door — start with emerik-guide (orient, tour, or step-through). Starting from an empty repo? Start a new project walks the greenfield front door — brainstorm to first Goal, on emerik alone.
Do not run emerik init inside emerik's own source repo — a root
.emerik/ there pollutes emerik's self-hosted CI. Dogfood on a separate project.
Start a new project
emerik is the greenfield front door too: from an empty directory to a first implemented, harvested, verified Goal on emerik alone — no separate planning tool, no document that rots away from the code. The five planning skills carry an idea down to sequenced Goal briefs the engineering loop picks up directly. ⎯ Each step deposits durable substrate: a committed session doc harvested into Pins, constraint-Anchors on their stewards, the brief / prd / architecture heads, and the sequenced Goal briefs.
The mechanical path — init → the planning tool sequence → first Goal started →
work committed → verify green → Goal completed — is exercised end-to-end by a
deterministic CI proof, through the real MCP server and the real CLI on a throwaway fixture,
so the successor claim is demonstrated, not asserted. The narrative below is the
live version of that same path: what a founder does by talking to the five
planning skills inside their coding agent. Same tools, same order — a person driving instead
of a test.
Create the repo and initialize
Start from nothing but an empty directory and wire emerik in.
mkdir task-inbox && cd task-inbox
git init
emerik init # store, hooks + the planning skills, wired into your agent
An empty substrate — a fresh repo with zero Pins — is a valid entry state, not a special mode. Greenfield is simply what it looks like when the grounding reads come back empty.
Brainstorm the idea
Inside the coding agent, run emerik-brainstorm. It facilitates an honest ideation
session grounded in whatever the substrate already says (nothing, on a fresh repo — stated
plainly, never fabricated), and lands two things: a committed session doc under
.emerik-planning/brainstorm-<slug>.md that the harvest hook captures
as Pins, and any constraint stated in-session deposited as a constraint-Anchor
on its steward at the moment it is stated — queryable the instant it lands.
Grow the planning head
Three skills turn the idea into durable, append-only planning heads — each a committed markdown body the hook harvests, plus a minted artifact that references it:
emerik-brief— the idea becomes a committed brief artifact: problem, users, outcome, scope fence.emerik-prd— the brief grows into a prd, and each binding requirement is offered as a candidate constraint-Anchor at the moment of statement.emerik-architect— the prd becomes a durable architecture, and each decision that governs a code region lands as a governing Anchor (inverted CODEOWNERS) — the design document and the enforcement surface are the same thing.
A revision never edits in place: a changed head is a superseding artifact
(--supersedes), so the old one stays replayable in lineage.
Break it down into Goals
emerik-breakdown converts the live prd and architecture into an ordered sequence:
one committed initiative brief under
.emerik-planning/initiative-<slug>.md, then delivery-sized
Goal briefs under .emerik-goals/, each opening with a
Sequence block (Initiative: / Priority: P0/P1/P2
/ Depends on:) so the order and the dependencies are explicit.
Pick up the first Goal — and build
Now the planning path meets the engineering loop you already know. Pick up a brief with
emerik-goal (one brief → one Goal, its priority read from the Sequence block), or
hand the whole sequence to emerik-orchestrate to fan the briefs out in dependency
order. From there it is the shipped loop: plan grounded in retrieved Pins and
Anchors, implement test-first under a Lease, and emerik verify as the green gate —
then complete the Goal. That closes the path: mkdir to a first implemented, harvested,
verified Goal, on emerik alone.
Planning and engineering live on the same append-only substrate — the brief, prd and architecture heads, the constraint- and governing-Anchors, and the Pins harvested from every committed doc all feed the same retrieval cascade the implementation loop grounds in. See Use with an AI agent for the full skills grid and the tool surface.
Initialize a repo
emerik init is idempotent — safe to run again. It creates:
.emerik/— the append-only, content-addressed store (committed with your code)..emerik/.gitignoreand a root.gitignorerule for the ephemeral local index.- a
post-commithook (auto-harvest) and apre-pushhook (freshness re-derive).
It also wires your coding agent (additive and idempotent — pass --no-agents to skip):
.claude/skills/— the fourteen shipped skills, loaded by Claude Code and by Cursor (2.4+, which reads.claude/skills/natively):emerik-brainstorm,emerik-brief,emerik-prd,emerik-architect,emerik-breakdown,emerik-discover,emerik-goal,emerik-plan,emerik-implement,emerik-review,emerik-guide,emerik-retro,emerik-orchestrateandemerik-onboard..claude/agents/— the four reviewer roles theemerik-reviewskill fans out:blind-hunter,edge-case-hunter,acceptance-auditorandsubstrate-auditor..mcp.json— registers theemerik mcpserver for Claude Code (merged additively, other servers untouched)..cursor/mcp.json— the sameemerik mcpserver for Cursor, samemcpServersshape, merged additively the same way.- a delimited
<!-- emerik:begin -->…<!-- emerik:end -->block inAGENTS.md(created if absent) and inCLAUDE.md(only when it already exists), describing the substrate and the skills. Content outside the markers is never touched, and re-runninginitheals a missing piece without duplicating anything.
$ emerik init
✓ initialized .emerik/ — Pin, Anchor and Glacier store ready
created .emerik/config.yaml
created .emerik/pins/.gitkeep
created .emerik/anchors/.gitkeep
created .emerik/glacier/.gitkeep
created .emerik/goals/.gitkeep
created .emerik/dependencies/.gitkeep
created .emerik/reviews/.gitkeep
created .emerik/briefs/.gitkeep
created .emerik/prds/.gitkeep
created .emerik/architectures/.gitkeep
created .emerik/.gitignore
created .gitignore
created .git/hooks/post-commit
created .git/hooks/pre-push
created .claude/skills/emerik-plan/SKILL.md
created .claude/skills/emerik-plan/template.md
created .claude/skills/emerik-implement/SKILL.md
created .claude/skills/emerik-discover/SKILL.md
created .claude/skills/emerik-guide/SKILL.md
created .claude/skills/emerik-guide/steps/orient.md
created .claude/skills/emerik-guide/steps/step-through.md
created .claude/skills/emerik-guide/steps/tour.md
created .claude/skills/emerik-goal/SKILL.md
created .claude/skills/emerik-goal/checklist.md
created .claude/skills/emerik-goal/template.md
created .claude/skills/emerik-review/SKILL.md
created .claude/skills/emerik-review/checklist.md
created .claude/skills/emerik-review/template.md
created .claude/skills/emerik-retro/SKILL.md
created .claude/skills/emerik-retro/checklist.md
created .claude/skills/emerik-retro/template.md
created .claude/skills/emerik-orchestrate/SKILL.md
created .claude/skills/emerik-brainstorm/SKILL.md
created .claude/skills/emerik-brainstorm/template.md
created .claude/skills/emerik-brief/SKILL.md
created .claude/skills/emerik-brief/checklist.md
created .claude/skills/emerik-brief/template.md
created .claude/skills/emerik-prd/SKILL.md
created .claude/skills/emerik-prd/checklist.md
created .claude/skills/emerik-prd/template.md
created .claude/skills/emerik-architect/SKILL.md
created .claude/skills/emerik-architect/checklist.md
created .claude/skills/emerik-architect/template.md
created .claude/skills/emerik-breakdown/SKILL.md
created .claude/skills/emerik-breakdown/checklist.md
created .claude/skills/emerik-breakdown/template.md
created .claude/skills/emerik-onboard/SKILL.md
created .claude/skills/emerik-onboard/checklist.md
created .claude/skills/emerik-onboard/template.md
created .claude/agents/blind-hunter.md
created .claude/agents/edge-case-hunter.md
created .claude/agents/acceptance-auditor.md
created .claude/agents/substrate-auditor.md
created .mcp.json
created .cursor/mcp.json
created AGENTS.md
Prefer just the substrate, no agent wiring? Run emerik init --no-agents.
Works with Cursor. One emerik init wires both harnesses. Cursor 2.4+ loads
the shipped skills through its native .claude/skills/ support (the Agent Skills standard), and
AGENTS.md is first-class in Cursor, so the same substrate context, lexicon and skills reach a
Cursor agent unchanged. Two caveats worth knowing: the emerik-review fan-out uses the host's
native subagents where present, else the documented prompt-file fallback (one prompt per reviewer under
.emerik-local/reviews/, run manually) — that fallback is the guaranteed path on any harness; and
emerik exposes thirty-five MCP tools, comfortably under Cursor's historically undocumented per-server tool
cap. Leases, hooks and harvest are plain git, so a Cursor agent and a Claude Code agent on the same repo
coordinate through the same leases and the same harvest hooks.
The harvest hook resolves emerik robustly: it prefers a global emerik on your
PATH, and otherwise falls back to the absolute CLI path baked in at init time — so a
commit auto-harvests even when emerik is not installed globally. The hook swallows its own output
and always exits 0, so it never blocks a commit or push. If emerik cannot be resolved
at all — no global install and no reachable fallback (a moved clone or a deleted build) — the hook prints one
stderr line (emerik: harvest skipped — emerik not found …) so a missed harvest is visible rather
than silent; it still exits 0 and never blocks the commit.
Harvest Pins
emerik harvest reads a commit, extracts source-bound Pins, and re-derives any Pin whose
source changed. With the post-commit hook installed it runs automatically on every commit;
you can also invoke it directly.
$ emerik harvest
{"level":"info","message":"harvest: committed run","harvested":1,"rederived":0,"invalidated":0}
✓ harvested 1 Pin
Freshness — the honest part
Every Pin is bound to the bytes it came from. On the next harvest:
- Source changed → the Pin is re-derived; the stale predecessor is superseded.
- Source deleted / became binary → the Pin is invalidated with a tombstone.
- Nothing changed → no-op (re-harvest is idempotent).
This is why a Pin never silently drifts out of sync with the code it describes.
Harvest never pins emerik's own scaffolding: the .emerik/ store, the whole
.claude/ tree, and the root-level .mcp.json, AGENTS.md and
CLAUDE.md that init wrote are excluded — so a git add -A right after
init mints Pins only for your real source, never for the wiring outputs. A nested
CLAUDE.md (your own knowledge file) is still harvested.
Anchors & verify
An Anchor is a reviewed contract with a machine-checkable assertion. You choose a subtype, an enforcement level, and a check.
| Flag | Options | Meaning |
|---|---|---|
--subtype | contract · constraint · steward · antibody | What kind of contract this is. |
--enforcement | block warn advisory | What happens when the check fails. |
--check-kind | grep · test · lint · fitness · agent | How the Anchor is checked. |
--check-ref | <pattern | command> | The concrete target — a grep pattern, a test, a shell command. |
--steward | <who> | Who owns this Anchor (required — an honesty label). |
--governs | <path glob> | The code region this Anchor stewards (repeatable; inverted CODEOWNERS, resolved by emerik steward resolve). |
Author a block-level constraint guarded by a grep pattern:
emerik anchor author \
--content "Auth token TTL must remain 3600s to match the gateway." \
--subtype constraint --enforcement block --steward you \
--check-kind grep --check-ref "TOKEN_TTL_SECONDS = 3600"
List what exists, and run the gate:
emerik anchor list # inventory of Anchors + lineage
emerik verify # check every Anchor + convention lint
block fails emerik verify with exit code 1 — a hard CI
gate. warn reports but does not fail. advisory
is informational only.
Promote a Pin into an Anchor
A load-bearing Pin can be promoted through a review gate. Without --approve it is a
no-write preview; with it, a reviewed Anchor is minted carrying promotedFrom lineage.
emerik anchor author --from-pin <pin-ulid> --subtype contract \
--enforcement block --steward you \
--check-kind grep --check-ref "requireAuth" --approve
The honesty loop
This is the whole promise in one worked example: a green check must go red the moment the invariant it guards is broken. Start from the constraint Anchor above.
$ emerik verify # invariant holds
✓ conventions OK — no violations
✓ BLOCK constraint 01KWH64… grep — grep matched: TOKEN_TTL_SECONDS = 3600
1 Anchor(s) checked · 1 ✓ · 0 blocking
exit 0
$ sed -i '' 's/3600/7200/' src/auth.ts # break the invariant
$ emerik verify # drift introduced
⛔ BLOCK constraint 01KWH64… grep — grep found no match for: TOKEN_TTL_SECONDS = 3600
1 Anchor(s) checked · 0 ✓ · 1 blocking
exit 1
A drift detector that can never detect drift is worse than none — it lends false confidence. Always test an Anchor by forcing it to fail, not only by confirming it passes.
Retrieve context
emerik retrieve answers a query with a cost-tiered cascade: it reaches for hot Pins
(semantic search) first, falls through to Anchors, then to raw code — stopping as soon as it has
enough. This is what an AI agent calls to get the right context cheaply.
emerik retrieve "how is the auth token TTL enforced?"
emerik retrieve "auth token TTL" --limit 5 --json
Results carry a heat bar (▁▂▃▅▇) showing how frequently each Pin is referenced.
The local index
Semantic retrieval is powered by a local, ephemeral index built from your committed Pins. It lives
under .emerik-local/ (gitignored) and is rebuilt from the source of truth on demand — the
index is disposable, the Pins are not.
| Command | What it does |
|---|---|
emerik index rebuild | Rebuild the index from scratch, then atomically swap it in. |
emerik index refresh | Incrementally bring the index up to date. |
emerik index status | Report freshness and whether a rebuild is needed. |
The embedder (ONNX all-MiniLM-L6-v2) runs locally. No query or content leaves your machine.
Heat & glacier
Every Pin has heat — a score blending recency-decay with how often it is referenced. Cold Pins can be frozen into a lossless glacier archive to keep the active index lean, and thawed back when they matter again. Freezing is a relocation, never a deletion: the Pin's identity and bytes are preserved.
emerik glacier freeze --threshold <heat> # freeze Pins colder than a threshold
emerik glacier list # what's archived
emerik glacier thaw <pin-ulid> # bring one back
Goals
A Goal is the concurrent work-unit: a team pursues one active Goal against the
shared decision graph while other teams pursue theirs — many Goals run at once with no single-tree bottleneck.
A Goal is a first-class, content-addressed, append-only artifact under .emerik/goals/: starting one
mints an active Goal; advancing supersedes it with a progress entry; completing supersedes it with a completion
stamp. The committed markdown brief under .emerik-goals/ stays the durable narrative — the Goal
links it with --brief; they coexist.
A Goal carries a first-class priority — P0, P1, or P2,
set with goal start --priority (default P1). It is the attention rank the
orchestrator's simple-priority mode orders assignments by (P0 before P1 before P2), and it is carried through
advance unchanged. The field is optional for append-only compatibility: a Goal committed before
priority existed carries none and reads as P1.
| Command | What it does |
|---|---|
emerik goal start "<title>" | Start a team's active Goal (--team, --brief to link the committed brief, --priority P0/P1/P2 — default P1); flags recently-changed depended-on contracts as coordination events. |
emerik goal advance | Record progress (--note) or finish it (--complete). |
emerik goal status | Show a team's active Goal (or one by --id) — no active Goal is not an error. |
emerik goal list | Live Goals — active by default, --all includes completed. |
emerik dependency declare | Declare a contract-dependency on a published Anchor (--anchor, --team, --note) — never live code under lease. |
emerik dependency status | Show the currency of a team's dependencies — current, changed (a coordination event), or retired. |
emerik dependency list | Live dependencies — --team / --anchor (lineage-aware) filters. |
Starting a second Goal for a team while one is active is refused (GOAL_ACTIVE_EXISTS) — advance or
complete the first. This is a local projection check, not distributed locking: two machines racing a start
for one team can both land (the append-only union merges without corruption). The projection then
reconciles the race deterministically — among a team's live active Goals it picks one winner
(earliest createdAt, ties broken by lineage-origin id) as the Goal every team-selector read and write
targets, and flags the rest as raced losers. Both stay visible in goal list; a
loser carries a note naming the winner, its bare goal advance is refused with the same voice, and
goal advance <loser> --complete retires it (the reconciliation move). No compare-and-swap,
no goal ref, no committed-bytes change. Cross-machine write arbitration is still the Lease's job.
When you start a Goal, emerik checks your team's declared contract-dependencies: if
a depended-on Anchor changed since you declared it, goal start flags it after the STARTED line —
“the ‘AuthGuard’ contract changed (2026-07-02…) — coordination event”, with the exact
re-declare sync command. It is a heads-up to sync first, never an alarm: the Goal starts regardless (exit 0,
never “conflict”) — a stale contract does not block starting work; the Lease is the
only write arbiter. The --json payload stays exactly the Goal; agents pull the same report via
dependency_status.
Completing a Goal outputs Pins that feed retrieval — Goals → Pins → RAG — because the Goal's committed
brief and work commits are harvested by the hooks. The Goal surface itself mints no Pin: the
emerik(goal): lifecycle commits are skipped by the harvest recursion guard. Learning is a byproduct
of working.
Contract-dependencies
When your team builds on another team's work, you depend on the published Anchor — the contract — not on their live, leased-out code. It is the microservice-contract version pin: you declare a dependency on the contract you built against, and when the steward supersedes it, that change is an explicit coordination event you are notified about. The surface accepts only an Anchor id — it structurally cannot name live code, a path, or a leased territory, so “never against live code under lease” is a property of the shape, not a convention.
| Command | What it does |
|---|---|
emerik dependency declare --anchor <id> | Declare a contract-dependency (--team, --note) on a live-head Anchor. |
emerik dependency status | Each of a team's dependencies as current, changed, or retired. |
emerik dependency list | Live dependencies — --team / --anchor (lineage-aware “who depends on this contract”). |
Declared against the contract, notified when it changes
A dependency is an append-only artifact naming a published Anchor's id. It is recorded once; when the steward supersedes the contract, you find out four ways:
- The event log. The
emerik(anchor): supersede <id>commit is the coordination event — a git-native, durable record, replayable viaemerik history. - Your read surface.
emerik dependency status(and the dependency_status MCP tool) reports a moved contract as changed and names the new head — the pull-based notification the dependent polls. - The author's render. When the steward runs
emerik anchor author --supersedes, the human output names the dependent teams affected — a courtesy heads-up at change time (the--jsonenvelope is unchanged; the notification surface for agents is the pull-based status). - At Goal start.
emerik goal startrenders the coordination event automatically (human output) for any depended-on contract that moved — sync before building on it. It is a heads-up, not a gate: the Goal starts regardless. Agents on the MCP path pull the same report via dependency_status right after starting.
Sync is a re-declare
There is no separate sync verb: re-declaring on the new head mints a
superseding Dependency (append-only), which dependency status tells you the exact command
for. A same-head re-declare is refused (DEPENDENCY_EXISTS) — it is never a silent no-op. If two
machines race that declare for the same team and anchor and both land, the byte-identical duplicates
dedup at projection (kept: earliest) and count as one dependent — append-only, both files stay on disk.
A changed contract is surfaced in coordination-event voice — a heads-up to reconcile, never an alarm. A live dependent now drives the risk tier: a review that edits an Anchor with dependents is risk-tiered high and routed to a human — see Review at scale.
Exclusive-write leases
When several agents or machines write to the same repo, a lease gives one of them
exclusive rights to a territory for a bounded time. Leases are recorded on a dedicated git
ref (refs/emerik/leases) via compare-and-swap push — your git remote is the serialization
point, so no lock server is needed. A lease expires on crash or stall, so turf never
deadlocks.
Territories collide by path overlap, not just exact string — so mutual exclusion is mechanical,
not a naming convention. A lease on src/auth/ blocks a claim on src/auth/login.ts
(and the reverse), and glob forms like src/auth/** collide the same way. A bare token with no
/ or glob metachar (e.g. payments) is opaque — it collides on exact match only.
The refusal names the overlapping lease so an agent's routing reads it unmistakably.
A trailing slash alone does not make a territory path-aware: billing/ normalizes to the bare
token billing and collides on exact match only — it will not collide with
billing/invoice.ts. Write a multi-segment path (src/billing/) or a glob
(billing/**) when you want collisions detected across an entire module.
| Command | What it does |
|---|---|
emerik lease claim <territory> | Claim exclusive write on a territory (--ttl, --holder, --retries, --wait). |
emerik lease renew <territory> | Extend a lease you hold (--retries, --wait — a transiently-contended renew retries to success). |
emerik lease release <territory> | Give it back early (--retries, --wait). |
emerik lease list | Active leases (add --remote for a fresh cross-machine read). |
Two machines, one territory
Run this on two clones of the same remote at nearly the same instant. Exactly one wins.
This is signed off end-to-end: two clones with distinct git identities, driven over both the CLI and the MCP twins, exercise cross-machine claim, territory overlap, version-skew tolerance, and goal/fork/dedup races against a shared remote — each producing exactly one winner or one surfaced, reconcilable fork. Since the git ref CAS is client-agnostic, the serialization holds identically across physical machines — the executed pass ran against a local bare remote (a hosted remote and a literal second physical machine are a documented follow-up; see the committed sign-off record).
A$ emerik lease claim payments --holder machineA --retries 0
⛔ BLOCK territory 'payments' contended — another machine claimed it first exit 1
B$ emerik lease claim payments --holder machineB --retries 0
▣ LEASED payments · holder machineB · expires in 15m exit 0
--retries 0 fails fast. The default (5) uses jittered back-off so a
transiently-contended claim, renew, or release retries to success instead of failing spuriously —
all three mutating verbs share the one retry discipline.
A lease this client reads may have been written by a newer emerik. The read posture is deliberate and single-winner by construction: a lease whose six core fields still validate (with unknown extra fields stripped on read) is tolerated — honored as held and listed; a blob it cannot fully read is refused as held — its territory stays blocked and its bytes are preserved untouched. A client never grants a territory it cannot prove free, and never rewrites a lease it cannot fully read (renewing a newer-format lease is refused — renew it with that version; releasing one you hold still works). The lease's six fields are a frozen core: any future growth is additive and optional over them, so old and new clients coordinate safely.
A lease's expiresAt is written from the writer's clock; liveness is judged
against each reader's clock. The compare-and-swap that picks the winner of an open
territory never consults a clock — the git ref store serializes it — so
single-winner exclusivity is skew-independent. Skew moves only the expiry edge: a
contender whose clock runs ahead of the holder's by S seconds could claim over an
expired lease S seconds early. With the holder renewing at least M seconds
(its own clock) before expiry and treating a failed renew as loss-of-lease, a live holder stays
exclusive for |S| < M. At the shipped default ttl 900 s and a
renew-at-half cadence, M = 450 s — three to four orders of magnitude above
NTP-disciplined skew (typically < 0.1 s). The operating rule: renew before the
margin, treat a failed renew as lost. (--wait is accumulated back-off, not a
wall clock, so it is unaffected by skew.)
Stigmergic self-deconfliction
Agents coordinate the way ants do — by reading shared state and routing around each other, with
no direct messaging and no orchestrator. Before it chooses where to work, an agent reads the
trail — emerik trail status (MCP trail_status):
- The heat trail. A recently-worked path is hot — a signal that someone is active
there. This is FR-4's heat read as a pheromone: one mechanism, two uses, the same
heat number retrieval ranks by, never a second subsystem. Recency travels through git (a fresh Pin's
committed
createdAt), so "someone just worked here" is visible cross-machine; reference counts add same-machine reinforcement. Heat never blocks — a hot zone is a routing signal, not an error. - The lease markers. The active Leases on
refs/emerik/leasesare the authoritative contested signal — territory · holder · expiry, shown so others route around them.
| Command | What it does |
|---|---|
emerik trail status | Read the trail — hot zones + lease markers — to route around, no messaging (--remote for a fresh cross-machine read, --limit to cap the zones). Pure read; mutates no ref. |
Declaring a territory turns the trail into a guard. When an author declares the territory it holds —
emerik anchor author … --territory <t> (MCP anchor_author
territory) — the write is consulted against the live Leases before it lands
using the same path-overlap check a claim uses: a declared territory that path-overlaps another
holder's live Lease refuses the write (LEASE_HELD, exit 1, no partial
write) in coordination-event voice — never "conflict", never "locked". Your own or an open territory proceeds.
Heat is never consulted at write time; only a live Lease arbitrates.
The routine workload self-deconflicts this way — N agents make N trail reads and zero
pairwise messages, so coordination never grows with the square of the swarm (layer 1, ~95% of the work — an
Epic-8-measured target). The rare genuine collision that cannot be routed around — two agents that truly need
the same territory — is arbitrated by the emerik-orchestrate skill (layer 2, the ~5% path), which
reads this same trail, then applies drafted rules in order: wait out a short-lived Lease,
else let the higher-priority Goal proceed (P0 over P1 over P2), else first-holder
by CAS order, else split at a published Anchor (declare a contract-dependency and build against
it), and finally surface it to the human driver. The orchestrator arbitrates who proceeds — it
grants territory only through Leases and never breaks one.
Onboard a brownfield repo
Adopting emerik on an existing codebase is an agent workflow, not a manual runbook.
Point your coding agent at the emerik-onboard skill and it drives the whole cold start:
ensure the store is initialized, emerik seed --analyze, emerik seed, build the
index, and land a committed onboarding summary grounded in the seeded graph — with each phase's
landing verified from the live surface (via the --json reports and emerik index status)
before the next phase starts. Retrieval works warm from the first query.
A repo with nothing to seed is not forced through the pipeline: when the analyzer reports
codeUnits === 0 (or there is no HEAD), emerik-onboard says so and
routes to the greenfield planning path (emerik-brainstorm +
emerik-guide) instead of seeding nothing — the store is still left initialized with a fresh index.
Deterministic seed content is a code/comment excerpt. When a coding agent drives the cold start it can do
better: after the first analyze pass, it reads the enumerated code units and writes a short synthesized
understanding of each file it can genuinely improve into a gitignored handshake file
.emerik-seed/synthesis.json (keyed by repo-relative path), then re-analyzes with
--synthesis. Those files' Pin content becomes the agent's understanding — provenance-marked
synthesis: agent, a record of how the content was produced, not a trust upgrade — while
the binding stays on the code, so re-derive keeps it fresh from there. No live agent or model is ever loaded
into the engine: the intelligence arrives as data through the same extractor seam the deterministic pass uses.
Documents get the same two-tier treatment. Instead of admitting a whole file as one 2000-character dump,
the analyzer splits each doc on its markdown headings into per-section candidates — the
deterministic fallback, always available. When a coding agent drives the cold start it extracts the actual
decision each section states (an ADR's "use PostgreSQL", not its prose) into a second handshake file
.emerik-seed/doc-decisions.json and re-analyzes with --doc-decisions; those
per-decision candidates replace the section chunks, marked synthesis: agent. Admission is then a
real per-candidate review: bare emerik seed --json returns
docCandidates — each with a stable candidateKey, its source and section, full
content, and a proposed enforcement — and the steward admits them individually through an admissions manifest
(below), each with its own steward and enforcement level. Two documents stating the same decision collapse to
one candidate (and one Anchor). No Anchor is ever written without --approve-docs and a named
steward — the machine still never mints an Anchor on its own.
Two ways in
A skill cannot be its own delivery vehicle — its premise is pre-init — so there are two documented doors:
- Door 1 (recommended). Install emerik globally, run
emerik initas the one manual command (it installs all fourteen skills), then invokeemerik-onboardin your agent. Its first phase detects the store, records init as already landed, and continues with analyze. - Door 2 (fully agent-driven). Point the agent at the packaged
assets/skills/emerik-onboard/SKILL.mdinside the globally-installed package and follow it — the skill drivesemerik inititself as phase 1, self-installing for every future session.
Seeded artifacts are stamped origin: seed — a lower trust tier than reviewed Anchors.
Machine analysis never mints Anchors directly; doc Anchors wait in a review queue, admitted only behind an
explicit steward gate the human names.
The manual runbook advanced
Prefer to drive it yourself (or running in CI / a non-agent harness)? The same pipeline is two review-gated
steps. It requires an initialized store — run emerik init first.
# 1) analyze — walks HEAD + ingests docs into a gitignored .emerik-seed/
emerik seed --analyze
# 2) seed — bulk-write the analysis into the graph (code → Pins)
# bare seed also lists per-candidate doc previews (docCandidates) in --json
emerik seed
# admit ALL doc candidates under one steward (the bulk gate)
emerik seed --approve-docs --steward you
# or review per candidate: admit only the keys in an admissions manifest,
# each with its own steward + enforcement (see below)
emerik seed --approve-docs --admissions .emerik-seed/admissions.json
Seed from generated behavior docs
Already documented the codebase with a tool that emits a coverage.json feature index
(mapping each feature to its docs and the code paths they describe)? Point --analyze
at it and each covered code file's Pin content becomes that feature's behavior contract — bound to the
code, so re-derive keeps it fresh from there.
# enrich covered code Pins with feature contracts (auto-discovers
# the index via doc-agent.config.json when --coverage is omitted)
emerik seed --analyze --coverage docs/features/coverage.json
emerik seed
Seed with agent-synthesized understanding
Have an agent (or a script) that can describe each file? Write its output as a handshake file — a
{ formatVersion, entries } map of repo-relative path to synthesized text — and point
--analyze at it with --synthesis. Covered files are seeded with that
text, stamped synthesis: agent; every uncovered file falls back to the deterministic excerpt. The
named file must exist (a missing --synthesis path is a hard error, unlike auto-discovered
--coverage).
# swap agent understanding in through the extractor seam
emerik seed --analyze --synthesis .emerik-seed/synthesis.json
emerik seed
Extract per-decision doc candidates
Without a handshake, each document is split on its markdown headings into per-section candidates. To capture
the actual decision a section states (rather than its prose), write a
{ formatVersion, decisions } map of doc sourcePath to the decisions it states — an
empty array is the honest "no decisions here" signal — and point --analyze at it
with --doc-decisions. A matched doc's candidates are replaced, stamped
synthesis: agent; a handshake path matching no ingested doc is reported as
unmatchedDocDecisions, never silently dropped. The named file must exist.
# replace section chunks with the agent's per-decision candidates
emerik seed --analyze --doc-decisions .emerik-seed/doc-decisions.json
emerik seed
Admit per candidate
Bare emerik seed --json lists every admittable candidate under docCandidates with a
stable candidateKey. To admit a reviewed subset — each with its own steward and enforcement
level — write an admissions manifest and pass it with --admissions. Listed keys are admitted;
unlisted candidates stay pending; an unknown key is a hard error before anything is written. All
three enforcement levels are accepted (you chose), but an admitted doc Anchor is checkless, so even
block/warn still surfaces as ⚠ WARN in emerik verify —
admission records a decision, it does not add a runnable contract.
# .emerik-seed/admissions.json
# { "formatVersion": 1, "admit": [ { "key": "<candidateKey>", "steward": "data", "enforcement": "advisory" } ] }
emerik seed --approve-docs --admissions .emerik-seed/admissions.json
The docs seed the content; the binding stays on the code. After the first edit to a covered
file, harvest re-derives its Pin back to code-derived content — re-run the seed to refresh. The feature
docs are used as Pin content, not minted as duplicate Anchors (pass --include-feature-docs to keep both).
History & time
Because the substrate is append-only on git, the commit history is the decision log. Three read-and-maintain commands operate over time.
| Command | What it does |
|---|---|
emerik history | Replay the .emerik/ decision log, or one artifact's lineage — the why behind a fact. |
emerik compact | Garbage-collect superseded Pins (keep the latest fact; removed bytes stay replayable in git). Try --dry-run first. |
emerik rollup <domain> | Distil a domain's decision history into a retrievable narrative rollup. |
emerik history <artifact-ulid> # lineage + why
emerik compact --dry-run # preview the GC, mutate nothing
emerik compact # report the before→after .emerik/ byte delta
Use with an AI agent
emerik is built to be an extension of your coding agent. When you run
emerik init, it wires the repo so Claude Code (and other harnesses) can reach the
substrate through a small, safe tool surface — the agent grounds its plan in your team's committed
Pins and Anchors before it writes a line, then works one territory at a time under a Lease.
Start here — the guided path
Inside the coding agent, the front door is emerik-guide. It turns the suite of
skills into a guided method — just say what you need:
- Orient — “orient me”, “where am I?”, “what should I do next?” It derives where the repo stands from live substrate facts (index_status, anchor_list, lease_list, a committed Goal brief) and the installed skills' own declarations, then recommends the next step as a short menu — never a hardcoded script.
- Tour — “give me the emerik tour”, “show me how this works”. A hands-on walkthrough of the whole loop, each leg delegating to the real skill.
- Step-through — “walk me through emerik-goal step by step”. It wraps any suite skill with its gates made explicit, confirms at each gate, and reports what was read from and deposited into the graph.
What emerik init wired
.mcp.json— registers theemerik mcpstdio server as an MCP server named emerik ({ "command": "emerik", "args": ["mcp"] }), merged additively so your other servers are untouched..claude/skills/— the fourteen shipped skills:emerik-brainstorm,emerik-brief,emerik-prd,emerik-architect,emerik-breakdown,emerik-discover,emerik-goal,emerik-plan,emerik-implement,emerik-review,emerik-guide,emerik-retro,emerik-orchestrateandemerik-onboard..claude/agents/— the four reviewer rolesemerik-reviewfans out (blind-hunter,edge-case-hunter,acceptance-auditor,substrate-auditor).- a delimited
<!-- emerik:begin -->…<!-- emerik:end -->block inAGENTS.md(andCLAUDE.mdwhen present) so a non-Claude harness (Codex, Cursor) reads the same context.
An agent touches the graph only through the MCP tools — never
.emerik/ on the filesystem and never the engine directly. The tools are the sanctioned,
audited surface; the store stays append-only and honest.
The thirty-five tools
The MCP surface is exactly thirty-five noun_verb tools, each a thin wrapper over the same engine the CLI calls:
| Tool | What it does |
|---|---|
| graph_retrieve | Resolve a query cost-tiered — hot Pins → Anchors → raw code. |
| anchor_list | List the live Anchors and their checks / enforcement levels. |
| anchor_author | Author a steward Anchor, or promote a reviewed Pin (the --approve review gate is preserved). Optional territory/holder gate the write on the live Leases — a territory held by another refuses it (LEASE_HELD). |
| goal_start | Start a team's active Goal — the concurrent work-unit (links a committed brief with --brief). |
| goal_advance | Record progress on — or --complete — a Goal (append-only supersession). |
| goal_status | Show a team's active Goal (or one by id); null when none, never an error. |
| goal_list | List the live Goals — active by default, --all includes completed. |
| dependency_declare | Declare a contract-dependency on a published Anchor — never live code under lease. |
| dependency_status | Report each dependency as current, changed (a coordination event), or retired. |
| dependency_list | List live dependencies — team / anchorId (lineage-aware) filters. |
| lease_claim | Claim exclusive write on a territory before writing. |
| lease_renew | Extend a Lease you hold. |
| lease_release | Give a territory back. |
| lease_list | Inventory the active Leases. |
| trail_status | Read the stigmergic trail — hot zones (heat) + lease markers — an agent routes around, no messaging (--remote, --limit). |
| review_open | Record + route an agent-quorum review — cleared by quorum, or escalated to a human when it edits a high-dependent Anchor. Declares edited Anchors with anchorIds, verdicts with verdicts, the steward's digest scope with domain. |
| review_list | List the live routing records — routing filters cleared / escalated, domain filters the digest scope. |
| review_bless | Bless an escalated review — clear the change on the steward's authority, through code. Mints a superseding decision Review (optional note, self-asserted by); no status field. |
| review_adjust | Request adjustment on an escalated review — send the change back for rework, through code (a superseding decision Review). |
| steward_resolve | Resolve stewardship for a code path — the Anchor(s) that govern it and the accountable steward, most-specific first (inverted CODEOWNERS). |
| steward_constraints | List a steward-agent's standing constraint set — its constraint-Anchors, in accrual order (the design process is the training signal). |
| antibody_mint | Mint an antibody (a constraint learned from a past failure) attached to a domain steward — a block antibody requires a human bless before it binds. |
| antibody_bless | Bless a pending high-impact antibody so it binds — the human gate before a block antibody can fail CI (OQ-8). |
| antibody_list | List a domain steward's antibodies — the ⊘ constraints minted from past failures, with their bless state. |
| antibody_retract | Retract a false antibody — an append-only Tombstone (no in-place delete); it no longer binds future agents. |
| metrics_rework | Report the drift-driven rework rate — a read-only, drafted SM-1 proxy over the decision log (baseline + an optional ongoing window). |
| metrics_review_time | Report the review funnel per Goal — the agent-quorum auto-clear rate + a drafted human-review-load proxy (reviewer-hours are not instrumented). |
| brief_author | Author a brief — the durable, append-only planning head; supersedes revises the live one (steward defaults to the git identity). |
| prd_author | Author a prd (product requirements document) — same append-only grammar as brief_author. |
| architecture_author | Author an architecture (a solution/architecture design) — same append-only grammar as brief_author. |
| brief_list | List the briefs — live by default, all includes superseded/tombstoned history (returns the live head with supersedes). No brief_status tool — the live head is served here. |
| prd_list | List the prds — live by default, all includes history. |
| architecture_list | List the architectures — live by default, all includes history. |
| index_status | Report whether the local index is built and fresh. |
| index_refresh | Absorb newly-committed Pins into the local index. |
emerik verify stays a shell gate, not a tool — it is the CI / verification step
(exit 1 on any block-level failure), so an agent runs it from the terminal.
The fourteen shipped skills
emerik-brainstorm
Idea → grounded ideation, from anywhere — including a fresh repo with zero Pins (an empty
substrate is a valid entry state, not a special mode). It reads what the substrate already
committed first (index_status → graph_retrieve
→ anchor_list), and when those come back empty it says so and starts
from a blank slate. It facilitates the session, deposits any constraint stated at the moment
of statement as a constraint-Anchor through the review gate (anchor_author,
confirmed with steward_constraints), then lands a committed session doc
under .emerik-planning/ that harvest captures — and hands off to emerik-brief.
emerik-brief
Idea → durable brief. It grounds in the substrate, picks up the brainstorm session doc when one
exists (a brief from scratch is valid too), and writes the brief body from a template. It commits
the body under .emerik-planning/ before minting the artifact, then mints the
brief via brief_author (--doc-path,
--steward) referencing the committed body. A revision supersedes the live head
(append-only, checked with brief_list first), never an in-place edit. The
brief is the input the planning head grows from; it hands off to emerik-prd to grow a
full PRD, or to emerik-goal for a delivery-sized slice.
emerik-prd
Brief → durable PRD. It reads the live brief the PRD grows from (brief_list,
surfacing a fork if more than one live head exists, routing back to emerik-brief if none),
then writes the PRD body from a template — descriptive features stay prose (harvested Pins), while each
binding requirement is offered as a candidate constraint-Anchor at the moment of statement
through the review gate (anchor_author with an owner-consented enforcement
level — BLOCK / WARN / ADVISORY — confirmed with steward_constraints; a
withheld consent is recorded as declined). It commits the body under
.emerik-planning/ — collision-checked, so a revision writes a new file —
before minting the prd via prd_author
(--doc-path, --steward); a revision supersedes the live head
(append-only, checked with prd_list first). It hands off to
emerik-architect to turn the PRD into a solution design, or to
emerik-goal for a delivery-sized slice.
emerik-architect
PRD → durable architecture. It grounds in the substrate — including the standing Anchors
that already govern the code (anchor_list) — reads the live prd the
design answers (prd_list, surfacing a fork if more than one live head
exists, routing back to emerik-prd if none), then writes the architecture body from
a template. Each decision that governs a code region is offered as a candidate governing
Anchor at the moment of statement — anchor_author
--subtype contract --governs '<glob>' (the inverted-CODEOWNERS surface,
confirmed with steward_resolve) — carrying a real
grep/test/lint check where expressible, and an honest
checkless advisory/warn
sticky-note where not (an agent check reports skipped/WARN today; block
only with a runnable check). Non-regional constraints keep the constraint-Anchor leg. The
consent gate is conversational and agent-side — the machine never mints an Anchor unprompted. It
commits the body under .emerik-planning/ — collision-checked, so a revision writes a
new file — before minting the architecture via
architecture_author (--doc-path, --steward);
a revision supersedes the live head (append-only, checked with
architecture_list first). The governing Anchors are the enforcement
surface, so the document and the enforcement surface are the same thing. It hands off to
emerik-breakdown to sequence the prd and architecture into ordered Goal briefs, or
to emerik-goal for a single delivery-sized slice.
emerik-breakdown
PRD + architecture → an ordered sequence of Goal briefs the engineering loop picks up
directly. It grounds in the substrate, reads BOTH live heads (prd_list
and architecture_list, surfacing a fork on either if more than one live
head exists, routing back to emerik-prd or emerik-architect if either is
missing), then writes and commits one initiative brief at
.emerik-planning/initiative-<slug>.md — collision-checked, committed
before any Goal brief cites it. Each Goal brief is the standard Goal-brief shape plus a
leading Sequence block — its initiative citation, a P0/P1/P2
priority (dependency-consistent: a brief never outranks a predecessor it depends on), and explicit
Depends on: lines — written sequence-prefixed under
.emerik-goals/<nn>-<slug>.md so lexical order is the breakdown order, its
guardrails citing real Pin and Anchor ids with their levels. The initiative layer is a documented
convention, not a mechanism: no schema field, no new work-unit type — the Goal stays the sole work
unit. It hands off to emerik-goal per brief (goal_start
--brief --priority, read from the Sequence block) and to
emerik-orchestrate for the whole sequence (the swarm honors each brief's
Depends on: lines; independent briefs fan out in parallel).
emerik-discover
Fuzzy problem → grounded exploration. It reads the substrate first (index_status → graph_retrieve → anchor_list), facilitates the exploration, then lands outcomes honestly: a decision summary you commit (harvest captures the Pins), and candidate constraint-Anchors offered through the review gate.
emerik-goal
Intent → Goal brief with cited guardrails. It reads the substrate first (index_status
→ graph_retrieve → anchor_list), then shapes a Goal and
testable acceptance criteria whose guardrails cite the Pin and Anchor ids they must honor, with their
enforcement levels. The brief is committed under .emerik-goals/ (harvest captures it), then
started as a first-class Goal via goal_start (linking the brief with
--brief) and handed to emerik-plan.
emerik-plan
Goal → committed plan doc. It refreshes the substrate (index_status → index_refresh), runs the retrieval cascade over the Goal and each acceptance criterion, lists the standing constraint-Anchors, and drafts a plan whose every step cites the Pin and Anchor ids it relies on — then lands it as checkbox steps in .emerik-goals/<slug>-plan.md, the Goal brief's sibling, committed and harvested. The plan survives the chat window: a fresh session — or a different model — picks up the next unchecked step cold.
emerik-implement
Plan step → implementation, under coordination discipline and test-first rigor: claim a Lease,
write the failing test first (watch it fail), implement to green (red-green-refactor), let the
harvest hook capture the commit, run emerik verify green, then release the Lease. A step
is done only when the new tests exist and pass and verify exits 0 — never claimed-but-unrun.
Starts cold when it has to: goal_status → the committed plan doc → the first
unchecked box, flipping each finished step's checkbox in the same commit as its code. On fixing a
defect it offers a witness: a checked Anchor through the review gate that would
have caught it, so the regression gate lives in the substrate. And it is extremely critical of its
own TODOs: one it could finish now gets finished, not left; one bigger than the step trips a
circuit breaker back to the driver — "this is bigger than the plan assumed — split it into
its own Goal(s)?" — and only a genuinely blocked one may stand, recorded in
deferred-work.md with its reason and unblock condition.
emerik-review
Change → adversarial review over the substrate. It fans out asymmetric reviewers — Blind Hunter
(diff only), Edge Case Hunter (diff + repo), Acceptance Auditor (diff + Goal brief) — plus the
Substrate Auditor, which audits the diff against the live graph (anchor_list
/ graph_retrieve for block/warn
Anchors, lease_list for Lease discipline, harvest honesty). It triages every finding
into exactly one bucket (decision-needed / patch / defer / dismiss), commits a review record under
.emerik-reviews/ (harvest captures it), and offers recurring failures as antibody-Anchor
candidates through the review gate. It then derives one verdict per reviewer layer and records the
routing decision with review_open — see Review at scale.
It never ends silently on undone work: a defer must be earned (a stated reason plus what would
unblock it — actionable-but-large work is escalated for a split, never deferred), every TODO the
diff introduces goes through triage, and the review closes with a deferral report —
everything that could not be done, each with its reason — offering, with consent, to fold items into
the active Goal, mint a cleanup Goal, or split oversized work into its own Goals.
emerik-guide
The guided front door, in three modes. Orient derives where the repo stands from live substrate facts and the installed skills' declarations, then recommends the next step as a short menu — never a hardcoded script. Tour is a hands-on walkthrough of the whole loop, each leg delegating to the real skill. Step-through wraps any suite skill with its gates made explicit, confirming at each gate and reporting what was read from and deposited into the graph.
emerik-retro
Completed Goal → lessons back in the substrate. It reconstructs what happened from the record first —
emerik history (the decision log), the harvested Pins and the heat
(graph_retrieve), the governing Anchors (anchor_list) —
facilitates the lessons, then lands them: lessons worth keeping as antibody-Anchor candidates through the
review gate (the bless loop lands with Epic 6), action items in a committed summary under
.emerik-retros/ that harvest captures.
emerik-orchestrate
Active Goals → a two-layer swarm. It reads the substrate first (goal_list,
trail_status, dependency_status), carves non-overlapping
assignments, and fans out one host-agent subagent per Goal or plan step — each in its own
git worktree running emerik-implement (sequential in-session where subagents are
unavailable). Layer 1 is hands-off: the heat trail and the Leases deconflict the routine ~95%. Layer 2 is
the rare genuine collision, arbitrated by drafted rules in order — wait out a short Lease, higher-priority
Goal proceeds (P0/P1/P2), first-holder by CAS order, split at a published Anchor, else the human driver. It
grants territory only through Leases — it holds none, claims none on a subagent's behalf, and never
breaks one.
It orders the carved assignments before fanning out. Simple P0/P1/P2 priority is the default on every run. A market-maker mode is an opt-in, evaluable A/B option — never the default: it prices an attention bid (impact × risk) minus a congestion cost on contested territory (overlapping a trail_status hot zone or a live lease marker), nudging non-conflicting Goals first and degrading to simple ordering on any tie. It orders, it never gates — Leases stay the only write arbiter. The mode is announced and recorded so runs are A/B-comparable (OQ-4); the decision criteria — escalation share, time-to-complete, rework rate — are targets Epic-8 instrumentation measures.
emerik-onboard
Brownfield cold start → a warm substrate, as an agent workflow. Its premise is pre-init, so it
requires nothing to start. Phase 0 preflights the git repo and checks for a HEAD; phase 1 is an
idempotent ensure-init (store absent → emerik init, committed; store present → recorded
pre-landed); then emerik seed --analyze and emerik seed mint the
origin: seed Pin/Anchor graph, emerik index rebuild makes retrieval warm, and a
committed summary under .emerik-planning/onboarding-<slug>.md is written from the
seeded graph (real Pin and Anchor ids) — committed before the final emerik index refresh so the
harvested Pins land. Each phase's landing is verified from the live surface (the --json reports,
emerik index status) before the next begins. Doc Anchors are admitted only behind an explicit,
consent-gated emerik seed --approve-docs --steward <id> — the machine never mints an Anchor
unprompted. A repo with nothing to seed (codeUnits === 0, or no HEAD) is detected as
greenfield and routed to emerik-brainstorm + emerik-guide with the store kept
initialized. It hands off to emerik-discover, emerik-goal or emerik-guide
— nothing auto-runs.
The reviewer roles emerik-review fans out over ship under .claude/agents/
(blind-hunter, edge-case-hunter, acceptance-auditor,
substrate-auditor) — each a subagent with only its allowed inputs, so a defect one reviewer
rationalizes away another cannot. Those reviewers are the agent quorum — their verdicts feed the
risk-tiered routing record below; the steward's batched digest of those records ships too —
emerik review [domain] collapses a day of output into a few team-level rows.
Review at scale — agent quorum + risk-tiered routing
When N independent agents adversarially agree a change is safe, it can clear without a
human — but only a change below the risk tier. The Review artifact is the durable,
append-only record of that decision: it names the change, the reviewers' verdicts, the edited Anchors and
their live-dependent counts, and the routing outcome. emerik review open (MCP
review_open) records + routes it; emerik review list projects the
live records. Opening a review orders the outcome — it never gates a write; Leases stay the only
write arbiter, and it exits 0 on both outcomes.
| Command | What it does |
|---|---|
emerik review [domain] | Open the batched digest — the one v1 TUI screen. Boilerplate collapses to a single faint ✓ N cleared row; only escalated changes surface as attention rows; the header reads N cleared · M need you. An optional domain scopes it (exact match). Keyboard-only (j/k move, ↵ drill in, bless, adjust, / filter, q quits); non-TTY / --json print the static digest / model. --web opens the review surface in the browser instead — on-demand, localhost, ephemeral (TTY only; --json/--quiet/non-TTY keep today's behavior). |
emerik review open <subject> | Record + route an agent-quorum review — --anchor (repeatable, the edited Anchors), --verdict <reviewer>:<safe|unsafe> (repeatable), --goal, --domain (the steward's digest scope, declared), --record. --finding <file>:<line>:<severity>:<title> (repeatable, severity ∈ P0/P1/P2) pins structured findings to exact lines; --from-branch (with explicit --base/--head override) stores the reviewed range as commit SHAs so review --web can regenerate the diff live. Exits 0 whether cleared or escalated. |
emerik review list | Live routing records — --routing cleared|escalated and --domain filter. |
emerik review bless <id> | Bless an escalated review — clear the change on the steward's authority, through code (a superseding decision Review). Optional --note, --by (defaults to the git identity). |
emerik review adjust <id> | Request adjustment on an escalated review — send the change back for rework, through code. Optional --note, --by. |
The engine owns these drafted OQ-9 defaults — engine-owned constants, tunable when Epic-8 instrumentation measures the queue reduction (never reported as achieved before then):
- Quorum — a normal-tier change clears only with three distinct reviewers, unanimously safe. Any unsafe, a short quorum, or a missing layer escalates.
- Risk tier — a change that edits an Anchor with at least one live dependent is high tier and is routed to a human regardless of the verdicts. Quorum can never override the tier.
- Spot-audit — one in ten auto-cleared reviews is flagged for a human spot-check, deterministically derived from the review's id (unpredictable before mint, verifiable after).
That last guarantee is structural, not a convention (the SM-C2 guardrail): the Review schema
re-derives the tier and routing from the artifact's own recorded evidence, so a committed review whose bytes
claim cleared on a high-tier change simply fails to parse — and fails emerik verify.
A cleared-high artifact cannot exist. Routing speaks in coordination-event voice — cleared by quorum
or escalated — routed to a human, never "conflict".
The batched digest — emerik review [domain]
Review is owned and scheduled — the steward starts it; it never pushes as an interrupt
stream (no watcher, daemon, or notification surface exists anywhere in emerik). emerik review
[domain] opens the one v1 TUI screen: a single full-height panel — a fixed header
(surface + domain + summary), a scrollable digest body, and a fixed key-hint footer — that reflows legibly
at 80 columns.
- Boilerplate collapses to a single faint
✓ N clearedrow, whether N is 12 or 12,043 — the collapse row is the scale answer. - Only escalated changes surface as their own attention rows: a
▸selection caret + a→state glyph + the change subject (bold) +fan-out:NN(the blast radius — the max declared dependents; omitted when the change names no Anchors). Reviews carry no heat, so there is no heat-bar. - A spot-audited clear earns its own dim
· spot-auditrow (it still counts inN cleared;M need youcounts escalated only). - The header voice is byte-exact:
198 cleared · 2 need you., an all-clear198 cleared · 0 need you., and an empty scopeNo changes since last review. - Domain scopes the digest by exact match — declared at
review open --domain, never inferred; an undomained review appears only in the unscoped digest.
The screen is keyboard-only (j/k or ↑/↓ move the caret,
q quits) and honest under constraint: NO_COLOR reads identically because every
state is glyph + text, and a non-UTF-8 / dumb terminal falls back to ASCII glyphs. A piped / non-TTY run
prints the same digest as static text; --json emits the digest model.
Drill in & resolve — review-as-code
Press ↵ on a flagged row to drill in: an inline accordion (one level deep,
never a second window) shows why it escalated, the change-as-code (the reviewer verdicts + the record
pointer), and the fan-out list — the edited Anchors with their dependent counts. Resolve it
through code, not a heavy approval form: b blesses the change
(clears it on the steward's authority) and a requests adjustment (sends it back
for rework). Each mints a superseding decision Review — the escalated record is retired by
supersession, not flipped by a status field (there is none). / filters the attention
rows; esc collapses. The same resolutions are scriptable off-terminal:
emerik review bless <id> / emerik review adjust <id> (and the MCP
review_bless / review_adjust twins), each carrying an
optional --note.
Read the change in the browser — emerik review --web
For a change too large to read in the terminal, emerik review --web opens the same review as a
browser-grade surface: the diff regenerated live from git with syntax highlighting, each
finding badged on its exact line (severity P0/P1/P2 shown as its
BLOCK/WARN/ADVISORY affordance), boilerplate hunks collapsed with
expand-on-demand, and the Anchor fan-out. It holds emerik's spine: the server is
on-demand, ephemeral, stateless, zero-new-dependency, localhost-only — a
127.0.0.1 HTTP server on an ephemeral port with a one-time capability token in the opened URL,
torn down on exit (Ctrl-C / browser-close / idle timeout). No always-on daemon, no database: git and
.emerik/ stay the only truth and the page is a pure stateless projection over them (the HTTP
sibling of the on-demand emerik mcp server). It is additive — the TUI screen stays, and
--json/--quiet/non-TTY never launch it.
The reviewed change is captured as code, not snapshotted: emerik review open
<subject> --from-branch stores the range as resolved commit SHAs
(base = merge-base(HEAD, upstream/trunk), head = HEAD; --base/--head
override), and the browser regenerates the diff from that range at view time — no committed diff bytes, yet the
exact reviewed change. A review with no stored range degrades to the metadata view (why + verdicts + fan-out),
never a fabricated diff. Bless and request-adjustment from the page call the
same token-gated, review-as-code ops as the terminal — minting superseding Reviews, no new approval model —
and the surface refetches so the resolved row leaves the attention set.
Impact-based routing & authority
When a change edits an Anchor, the dependency graph is the reviewer-routing graph: the
teams that declared a contract-dependency on it are auto-enlisted as the review audience
(the drill-in lists them, per Anchor). Review is team-level — a verdict is one team's
voice (self-asserted; emerik keeps no per-person membership), so any one member clearing counts for the
whole team and hundreds of per-person approvals collapse to a handful of team-level reviews. And the
core owner — the Anchor's steward — can ship a wide-fan-out central change
on their authority (a review bless): dependents are notified and can
review adjust through code, but full consensus is not required. The review is never
skipped — a high-fan-out change escalates by construction and reaches the steward, who ships with
review rather than gating on stop-the-world consensus (pairs with versioned / expand-contract migration,
OQ-10).
A forked Anchor supersession (two Anchors racing to supersede one predecessor) is walked as a
multi-edge lineage: dependentsOf counts the union across every branch, so a
change's blast radius is never under-counted and review tiering can no longer under-tier it. The fork is
surfaced, never silently resolved — dependency status reports the deterministic
primary head and lists all live heads, and emerik history <id> names the divergent
branch. The steward reconciles it by supersede-to-merge: author the merged successor over one
head and tombstone the other, after which every branch's dependents re-point to the single live head. No new
command and no committed-bytes change — the existing surfaces compose.
Stewardship — inverted CODEOWNERS
Decisions own code. An Anchor declares the code region it stewards with
--governs <glob> (repeatable), and emerik steward resolve <path>
(MCP steward_resolve) maps a path back to the Anchor(s) that govern it and the
accountable steward, layered most-specific first — the inverse of a
CODEOWNERS file (the decision owns the code, not a path→owner list). Every Anchor carries a
required steward (human email or agent id — OQ-7 leaves per-domain agent-vs-human assignment
open); a Pin at a governed path inherits the governing Anchor's steward. Stewardship is
permanent and travels with the graph — it sits above transient Leases:
resolving stewardship consults no lease, and a lease never changes who stewards. An ungoverned path resolves
to nothing — a fact, not an error.
A steward-agent carries a standing constraint set — the constraints stated during
PRD / scoping / design, each authored as a permanent subtype:'constraint' Anchor
attached to the steward (emerik anchor author --subtype constraint --steward <who>).
The design process is the steward-agent's training signal: the set evolves append-only as
constraints accrue, retrievable in accrual order via emerik steward constraints <steward>
(MCP steward_constraints). The review skill retrieves and applies a domain's
constraint set when it reviews a change there.
The antibody loop — ⊘
A code-review rejection (or a recurring failure / gotcha) mints an antibody:
a durable subtype:'antibody' constraint-Anchor attached to the domain steward, so every future
agent there inherits the learned constraint — "never make the same mistake twice." An antibody is
learned memory, not a current error — it carries the violet ⊘ marker (never
enforcement red) and its enforcement level. The bless gate (threshold drafted,
OQ-8): a high-impact antibody — one at block enforcement, which could fail CI
for every agent in the domain — is minted pending; it surfaces but does not bind or gate
emerik verify until a human emerik antibody blesses it. A warn /
advisory antibody is low-impact and binds on mint. Future agents retrieve a domain's antibodies
with emerik antibody list <steward> (MCP antibody_list).
A false antibody must not permanently bind every future agent, so there are three append-only retirement
paths (no in-place delete, AR-14). Retract — emerik antibody retract <id>
(MCP antibody_retract) writes a Tombstone; the antibody drops from
the graph and stops binding. Supersede — emerik antibody mint --supersedes <id>
replaces a wrong antibody with a corrected one. Expire — an antibody minted with
--expires-at stops binding once its TTL passes. And an antibody-vs-Anchor conflict
is steward-declared and resolved by retracting (--conflicts-with <anchorId>) or superseding,
leaving one unambiguous live constraint (the rule is drafted, OQ-16). With this, Epic 6 —
governance, stewardship & the antibody loop — is complete.
Instrumentation — the rework rate
emerik's whole thesis is that stale context drives rework — rebuilding work already marked
"done". emerik metrics rework (MCP metrics_rework) measures it, as a
read-only derivation over the .emerik/ decision log — it writes nothing, exactly
like emerik history. It is a drafted proxy for SM-1: the share of decision commits whose
verb rebuilds (supersede) or retracts (tombstone)
an already-recorded fact; every other verb (create, mint, bless, …) is
progression. It reports a baseline (the full log) and, with --since, an
ongoing window, and carries the ~2% directional target and the rough ~5–10% baseline band in
its machine-readable shape so a matched control run is directly comparable. It is honestly a drafted proxy —
the effort-weighted measure is future work — not the final SM-1 number.
emerik metrics review-time (MCP metrics_review_time) measures the
review funnel per Goal (SM-2), read-only over the Review artifacts + live Goals. It reports
two things. (a) The auto-clear rate — the share of changes the agent quorum cleared
without a human (routing = "cleared") — is solid from the bytes; a steep rise is the goal.
(b) Human-review load — humanReviews, the count of changes escalated to a human —
is a drafted proxy: emerik instruments no per-reviewer timer, so actual
reviewer-hours are not available and the report says so verbatim rather than fabricating hours from wall-clock.
Both are reported per Goal (with the Goal's completed state), and the counter-metrics still apply — a high
auto-clear rate must never be gamed past a genuine blast-radius change (SM-C2, structural in the risk-tiered
routing). With this, Epic 8 — instrumentation — is complete.
The decision-log subject grammar (emerik(<type>): <verb> <id>) parses
every shipped artifact family — pin, anchor, lease,
tombstone, goal, dependency, review, and the planning
families brief/prd/architecture. So a Goal, Review, Dependency, or
planning decision now surfaces typed in emerik history and counts in the rework
denominator, rather than landing in unparsed. In particular a steward's
adjustment-requested review resolution (emerik(review): adjust <id>) — a change
literally sent back for rework, the closest real analogue the substrate has — is classified as
rework alongside supersede and tombstone; a bless
stays progression. Because these families now parse, the rate's denominator includes their decisions, and a
planning supersede (a PRD or architecture revision) counts as rework under the same drafted
proxy. Only a genuinely non-grammar .emerik/ commit (e.g. a hand-written chore: note)
is still surfaced honestly as unparsed and never counted.
Those planning heads are first-class substrate: a brief, prd, or
architecture is authored via emerik brief|prd|architecture author (or the MCP
brief_author / prd_author /
architecture_author twins) — a durable, append-only record whose prose lives in the
committed markdown its --doc-path points at. A revision is a new head with
--supersedes (never an in-place edit), so planning history replays, supersedes, and verifies
like every other decision; emerik brief|prd|architecture list and status read the
live head(s).
A worked example — goal to verified change
The loop opens with emerik-goal: you hand it an intent, and it shapes a Goal brief — Goal
statement, testable acceptance criteria, and guardrails that cite the live Pins and Anchors they must honor —
committed under .emerik-goals/, then started as a first-class Goal with
goal_start --brief and handed to the plan skill. (You can also state a Goal and its
acceptance criteria inline to the plan skill; the committed brief, the first-class Goal, or the
pasted-in version all work.) From there the loop runs end-to-end:
# Goal Add rate-limiting to the login route.
# Acceptance criteria
# 1. Repeated failed logins are throttled.
# 2. Credentials are never written to a log.
Plan — grounded in the substrate
emerik-plan calls index_refresh so the plan reflects the latest committed facts, runs graph_retrieve for the Goal and each criterion, and lists live Anchors with anchor_list. A recently-moved Anchor is surfaced as a coordination note (a heads-up, not an alarm). It drafts checkbox steps that cite what they stand on, then commits the plan beside the Goal brief — the durable input to implementation, not chat:
✓ plan grounded in 3 Pins, 2 Anchors
- [ ] Step 1 — Add a retry counter to the login handler. Relies on Pin
01J…A7 (login flow). Must honor constraint-Anchor 01J…D9
"no unbounded retries" (BLOCK). P1.
- [ ] Step 2 — Redact credentials before logging. Relies on Pin 01J…B3
(logger). Must honor antibody-Anchor 01J…E4 "never log
credentials" (BLOCK). P0.
# the engineer commits the plan — the harvest hook fires
✓ .emerik-goals/rate-limit-login-plan.md
A step that cites no Pin or Anchor is grounded in raw code, not the substrate — the skill sends it back to the cascade. The committed doc means the next session (or a cheaper model) resumes from the first unchecked box — no need to keep this window open.
Implement — test-first, lease-guarded, verify-gated
emerik-implement picks up the first unchecked step from the committed plan doc (via
goal_status — a fresh session needs nothing else), claims a Lease on the
territory before writing, then writes the failing test first and
watches it fail — implementing to green (red-green-refactor) only once the test proves the behavior is
missing. It commits with the step's checkbox flipped (the post-commit hook harvests the new Pins for
you — you never hand-craft them), runs
emerik verify, and releases the Lease. The step is done only when the new tests exist
and pass and verify is green — never on claimed-but-unrun tests.
next step: - [ ] Step 1 — Add a retry counter … (.emerik-goals/rate-limit-login-plan.md)
▣ LEASED auth/login · holder plan-agent · expires in 15m
# … edit, flip the box to [x], then commit — the harvest hook fires automatically …
$ emerik verify
✓ 2 Anchor checks passed · no drift exit 0
✓ released auth/login
The skills keep the vocabulary precise — Pin, Anchor, Goal, Lease, Steward, Antibody, Glacier; enforcement block / warn / advisory; priority P0/P1/P2 — and always write the product name emerik lowercase.
Continuous integration
emerik verify is the gate. It exits 1 when any block-level
Anchor check fails, so it drops straight into CI as a required step.
- name: emerik verify
run: |
pnpm install --frozen-lockfile
pnpm build
./bin/run.js verify # fails the job on drift (exit 1)
The pre-push hook installed by emerik init re-derives Pin freshness before a push;
pair it with the CI gate above for defence in depth.
Conventions
- Command grammar is
emerik <noun> <verb>; flags are kebab-case; the global flags--json,--quiet, and--no-colorwork everywhere. - JSON is camelCase everywhere — artifacts,
--jsonoutput, and any future MCP surface. - IDs are ULIDs; the source content hash lives inside the artifact, never in the filename.
- Append-only: a change is a new artifact with
supersedes; a deletion is a tombstone. --quietsuppresses success output only — failures always print.
Command reference
| Command | Purpose |
|---|---|
| init | Initialize emerik in a git repo — store, ignore rules, hooks, and agent wiring (--no-agents to skip). |
| harvest | Harvest source-bound Pins from a commit; re-derive stale ones. |
| anchor author | Author a steward Anchor, or promote a reviewed Pin (--territory/--holder gate the write on the live Leases — a territory held by another refuses it). |
| anchor list | Inventory Anchors with lineage. |
| verify | Run Anchor checks + convention lint — the CI gate. |
| retrieve | Resolve a query cost-tiered: hot Pins → Anchors → raw code. |
| index rebuild · refresh · status | Manage the local semantic index. |
| glacier freeze · thaw · list | Archive cold Pins losslessly; bring them back. |
| goal start · advance · status · list | The concurrent work-unit — one active Goal per team against the shared graph; start takes --priority (P0/P1/P2, default P1) and flags recently-changed depended-on contracts as coordination events. |
| dependency declare · status · list | Contract-dependency on a published Anchor; notified when the contract changes. |
| lease claim · renew · release · list | Exclusive-write territory leases via git-ref CAS. |
| trail status | Read the stigmergic trail — hot zones + lease markers — to route around, no messaging (--remote, --limit). |
| review [domain] | Open the batched review digest (the one v1 TUI screen) — boilerplate collapses to one faint row, only escalated changes surface; an optional domain scopes it. Keyboard-only; non-TTY / --json print the static digest / model. |
| review open · list | Record + route an agent-quorum review — cleared by quorum, or escalated to a human when it edits a high-dependent Anchor (--anchor, --verdict, --domain, --routing filter on list). |
| review bless · adjust | Resolve an escalated review through code — bless clears it on the steward's authority, adjust sends it back; each mints a superseding decision Review (--note, --by). |
| steward resolve | Resolve stewardship for a code path — the governing Anchor(s) + accountable steward, most-specific first (inverted CODEOWNERS, FR-17). |
| steward constraints | List a steward-agent's standing constraint set — its constraint-Anchors in accrual order (FR-18). |
| antibody mint · bless · list · retract | The antibody loop (FR-19) — mint a constraint learned from a failure (--supersedes/--expires-at), bless a pending high-impact one (OQ-8), list a domain's ⊘ antibodies, retract a false one (a Tombstone, AR-14). |
| metrics rework | Report the drift-driven rework rate (FR-26) — a read-only, drafted SM-1 proxy over the decision log; a baseline plus an optional --since ongoing window. |
| metrics review-time | Report the review funnel per Goal (FR-26, SM-2) — the agent-quorum auto-clear rate + a drafted human-review-load proxy (reviewer-hours are not instrumented). Read-only. |
| brief author · list · status | The native planning head for a product brief (FR-33) — author commits a durable, append-only brief (--title, --doc-path the committed markdown body, --steward, --supersedes to revise the live one); list the live briefs (--all includes superseded history); status the live head(s). |
| prd author · list · status | The native planning head for a PRD (FR-33) — same append-only grammar as brief: author (--supersedes revises), list (--all), status. |
| architecture author · list · status | The native planning head for a solution/architecture design (FR-33) — same append-only grammar: author (--supersedes revises), list (--all), status. |
| seed --analyze · seed | Cold-start a brownfield repo into the graph. |
| history | Replay the decision log or an artifact's lineage. |
| compact | GC superseded Pins; keep the latest fact. |
| rollup | Distil a domain's history into a narrative rollup. |
| mcp | Start the stdio MCP server exposing the substrate as agent tools. |
Every command supports --help. Run emerik <command> --help for the full flag list.