Skip to content

You lay the track. Your agent drives. The reviewer keeps it on the rails.

The point of laying track rather than repeating directions is that it is still there tomorrow. A rule you write once governs every session after it, including the ones you have not started yet.

The track is a set of rules and structure that live next to your code, in a .yggdrasil/ directory in your repo. The rules say what your code must do: "every payment handler emits an audit event", "UI code never imports the database client". The agent writes code as usual. Before that code is done, the reviewer checks it against the rules and won't let the agent move on until it complies. When it drifts, the reviewer catches it and sends it back to fix course, in the same session, while the context is still fresh.

Right now, you are the feedback loop ​

There are three ways to work with an agent, and most people are stuck in the middle.

  • Autocomplete. It suggests the next line, you write the rest. One small use case.
  • You are the loop. It generates whole changes, and you check each one, send it back, check again. This is where the day goes.
  • A full feedback loop. It runs a real check, reads the failure, and fixes itself before you look.

Yggdrasil gives the agent a loop of its own.

The check runs inside the agent's loop, not in your review afterward. The agent reaches you already green, instead of you policing every change by hand.

The three players ​

You say what matters, in plain language. "Services that touch money must log an audit trail." "Background jobs can't call the HTTP layer directly." You don't write YAML or wire up a graph by hand. You state the rule and the intent.

Your agent turns that into structure. It keeps the graph under .yggdrasil/ in step with your code: it writes the rules down, says where each one applies, and records which source files belong to which component. You work with the agent to build and change this. It knows the schema and the commands; you provide the judgment.

The reviewer checks the code. It is a separate step: the reviewer is the model you configure, which reads a rule written in prose and your source and decides whether the code satisfies it. Rules that can be checked mechanically (an import ban, a naming convention) are script rules instead: a free local script decides them, with no reviewer involved. Between them, they are the thing that actually says yes or no.

The loop ​

From the agent's point of view, every change runs this cycle:

  1. Before editing, the agent runs yg context --file <path>. It gets only the rules in force on that one file, not the whole rulebook, so it writes code that fits them instead of guessing and getting bounced.
  2. After editing, it runs yg check --approve. The free script rules run first, then the reviewer rules go to the reviewer.
  3. On a pass, the verdict is recorded in the lock. The reviewer's verdicts live in a committed file; the free script verdicts live in a gitignored local cache that any checkout rebuilds for free.
  4. On a failure, the agent gets specific feedback (which rule, which file, what is wrong), fixes it in the same session, and re-runs. It loops here until green.
  5. In CI, a free, keyless step rebuilds the local-script cache (yg check --approve --only-deterministic), then yg check --no-approve confirms the recorded verdicts still hold for the current code.

CI is free and keyless — and a green build can't lie

CI does not call the reviewer and needs no API keys. Because the free script verdicts live in a gitignored cache, a fresh checkout first rebuilds that cache with yg check --approve --only-deterministic (free and keyless), then yg check --no-approve confirms every recorded verdict still holds — and re-checks, live and for free, that the code's real dependencies match its declared relations. Each verdict is tied by hash to the exact code it checked, so a file that changed but was never re-checked turns the build red — a stale or unverified change can't ride through as green. The reviewer's check happens locally while the agent works; CI just re-proves it was done.

That guarantee rides on the explicit flags, which is why the CI recipe spells them out: they always override the auto_approve setting, so a CI script is unaffected by it. As a second line of defence, when the CI environment variable is set, a bare yg check ignores a committed auto_approve: full and stays read-only (it says so on stderr). A bare yg check run anywhere else is free, keyless, and read-only only while auto_approve is unset or false in yg-config.yaml. Set auto_approve: full and an unqualified yg check behaves exactly like yg check --approve — it runs a fill: it calls the reviewer, needs API keys, and writes fresh verdicts into the committed lock instead of merely re-confirming the ones already there, so a change that was never re-checked gets its verdict on the spot rather than turning the build red. The mechanics, and the full auto_approve story, live in The lock.

Nobody restates the rules ​

The loop above runs the same way whether it is the first session on a file or the fortieth. The rules are attached to the code, not to a conversation, so a new session inherits them without being told. That is the difference between a rules file, which is re-read and half-applied each time, and a rule that is verified against the code it governs.

There is a worked example of a refused change and the fix on the home page.

You never trace the rules by hand ​

A file can pick up rules through several channels at once, seven in all (see Aspects). You never work that out yourself by reading the graph. You ask the tool:

bash
yg context --file src/payments/charge.ts

It prints every rule in force on that file, plus the path to each rule's text. The graph computes it, you read the answer. yg context --node <path> gives the same picture from a component's side. Add --json to either form and each rule also carries every channel it arrived through, each with a machine-readable origin token: own, ancestor, type, ancestor-type, flow, port:<name>@<node> or implies.

What lives next to your code ​

Everything Yggdrasil needs sits in .yggdrasil/, committed alongside your source. Three things matter day to day:

  • Aspects are your rules. Each one is a plain-Markdown statement of what the code must do, checked by the reviewer. See Aspects.
  • Nodes are your components. Each maps a set of source files and lists the rules that apply to them. See Nodes.
  • Flows are your processes. A flow ties components together as one business process so a shared rule applies across all of them. See Relations, flows, and ports.

A rule is just a few lines of Markdown:

markdown
# aspects/requires-audit/content.md
Every public mutation must emit an audit event before returning.

Use the shared auditLog.emit() utility, not custom audit logic.
The event must include: user ID, action, timestamp, resource ID.

The reviewer reads that and checks your code against it. Write rules the way you would write a clear code-review comment.

No lock-in

Delete .yggdrasil/ and your project builds and runs exactly as before — no build dependencies, no runtime hooks. The few artifacts that live outside it are the agent-rules files yg init wrote (AGENTS.md's summary block, the import line it added to CLAUDE.md, and .clinerules/yggdrasil.md — whichever of them this project has enabled under rules_artifacts), the eight lines it maintains in the repo-root .gitattributes, and, in each clone, the two merge drivers it configures in .git/config and the post-merge hook it installs when the repository has none; delete those too if you want no trace left.

What it costs before it pays ​

On an existing codebase the first step is usually not writing rules, it is finding out that the code does not match the architecture you thought you had. That work is real and it comes first. Phase 0 is the honest account of it, along with a per-feature earn-rate: which parts of the schema paid for themselves in practice and which only start to matter once a project grows.

Where next ​