@krzysztofdudek/yggdrasil
AAgent Instructions — Yggdrasil Repository
Install
agr install @krzysztofdudek/yggdrasil --target claudeWrites 1 file into .claude/skills/, pinned to git-a9efb8b8.
- .claude/skills/yggdrasil/AGENTS.md
Document
Agent Instructions — Yggdrasil Repository
You work on the Yggdrasil repository: an open-source CLI that makes a rule written once hold in every later session. A rule is attached to the code it governs, the agent editing a file gets only the rules that touch it, and a change has to satisfy them before it moves on. Some rules are local scripts that run for free; some are prose a separate model judges. Every verdict is tied by hash to the code it checked, so CI re-proves the set without a key.
This repo both implements that tool and runs it on itself, so the graph under .yggdrasil/ is a live example as well as the thing being enforced on your work here.
Context — Where Things Live
| Path | Role |
|---|---|
source/cli/ | Implementation — CLI code. |
.yggdrasil/model/cli/ | Graph — describes intended CLI architecture. Aspects enforce rules on source code. |
docs/ | User docs — for adopters. |
scripts/ | The quality gate (repo-check.sh) plus dogfood measurement instruments — deliberately NOT yg commands. See CONTRIBUTING before assuming one belongs in the CLI. |
tools/ | Repo tooling outside the shipped CLI (the demo GIF renderer). Has its own README. |
.plans/ | Agent working dir — design docs and implementation plans. Ignore skill paths (e.g. docs/plans/) — always use <root>/.plans/YYYY-MM-DD-<topic>-design.md and .plans/YYYY-MM-DD-<topic>-plan.md. Gitignored; not committed. |
Product Scope
rules.ts (printed by yg prime) and the committed AGENTS.md digest are consumed by agents in ANY repository that adopts Yggdrasil — not just this one. When editing rules content, examples, or guidance: use domain-neutral examples (no Yggdrasil-specific types or commands). Think "what would help an agent working on an e-commerce app or a mobile game?" not "what would help an agent working on this CLI."
Constraints
- Never hand-edit the marker-delimited Yggdrasil digest block in
AGENTS.md(or.clinerules/yggdrasil.md) — it is generated. To change rules content: editsource/cli/src/templates/rules.ts(full manual) orsource/cli/src/templates/digest.ts(committed digest), then rebuild and regenerate:node source/cli/dist/bin.js init --upgradefrom repo root (repo-check's digest assertion fails the commit otherwise — it tells you the exact command). - Ignore the generated digest block for understanding; the source of truth is
templates/rules.ts+templates/digest.ts. - Always reflect changes in corresponding documentation. When modifying code behavior, algorithms, or data structures, identify and update all documentation that describes the changed behavior —
docs/(user docs) and.yggdrasil/(graph metadata). Changes to behavior are not complete until every document describing that behavior is consistent. - NEVER run
yg initfrom a subdirectory. Always run from the repository root. Running fromsource/cli/or any subdirectory creates a new.yggdrasil/there or corrupts the project config. Usenode source/cli/dist/bin.jsfor local builds, notnpx yg(which may use a cached global version).
Yggdrasil-derived local state lives under .yggdrasil/
All Yggdrasil-derived local/rebuildable state (caches, indexes, scratch state) MUST live under the .yggdrasil/ directory — never at the repo root or elsewhere. Gitignore it within .yggdrasil/ (it is rebuildable and must not be committed). Examples: the relation pass's content-addressed AST fact cache lives under .yggdrasil/.ast-cache/, and the deterministic-verdict cache at .yggdrasil/.yg-lock.deterministic.json (rebuilt for free by yg check --approve --only-deterministic) — both dot-prefixed and gitignored. (.symbols-cache/ is a retired predecessor: nothing writes it any more, and it stays in the installed gitignore list only so an old checkout's leftovers keep being ignored. Do not treat it as the live cache.) Do not scatter Yggdrasil state outside .yggdrasil/ (no root-level .yg-cache/ etc.). The committed graph (model/aspects/flows/lock) also lives here; keep derived state in dot-prefixed, gitignored subdirectories so it never mixes with the committed graph.
Adding Support for a New Agent
Universal install covers every agent that reads AGENTS.md natively; Claude Code via the CLAUDE.md import; Cline via .clinerules/. A new agent needing a bespoke file is a design decision — open it with the maintainer before adding an installer.
Version Bump & Changelog
- Changelog is always updated. Every code or behavior change gets an entry under
## [Unreleased]inCHANGELOG.md. This happens as part of normal work — do not wait for a release. - A changelog entry records what changed between versions — it is not a work log. Write for someone reading release notes: what was wrong, what is now true, why it matters to them. Leave out how you found it, what you ran to verify it, and the order you did things in. "Verified by reproducing each case and comparing output" is your method, not a change. One entry per change, not one per step; group related fixes rather than listing each file you touched.
- Version bumps only on explicit user request. Never bump the version in
source/cli/package.jsonunless the user explicitly asks for a release. When they do:- Bump version (patch/minor/major per semver).
- Run
npm installinsource/cli/to updatepackage-lock.json. - Move current version entries to a release section in
CHANGELOG.md.
- Two distinct version notions — do not conflate them. The
package.jsonversion is the release/marketing version and moves on every release. The graph schema version is separate: it lives inCLI_SUPPORTED_SCHEMA(core/graph-loader.ts) and theversion:field oftemplates/default-config.ts, and it advances ONLY when the graph format/migrations change — never for a code-only patch.yg initcompares a project's graph version againstCLI_SUPPORTED_SCHEMA(not the package version) to decide whether an upgrade is needed. Bumpingpackage.jsondoes NOT require bumping the schema version; bump the schema version (in both places, plus a migration) only when the graph format actually changes.
CLI Message Design Principle
Every diagnostic message the CLI outputs to an agent must follow the what / why / next structure:
- WHAT happened — facts, one line or short block
- WHY it's a problem — context the agent needs to understand the situation
- NEXT — concrete command or instruction to resolve
Use buildIssueMessage({ what, why, next }) from source/cli/src/formatters/message-builder.ts for all error/warning messages in validator, check, approve, and build-context. The builder enforces the structure; the caller handles presentation (indentation, error code prefix).
This applies to CLI output only. Rules.ts (system prompt) provides the map — workflow, vocabulary, categories. CLI provides the GPS — specific errors, next commands. They share vocabulary but never duplicate information.
Quality Gate
ALWAYS run scripts/repo-check.sh from repo root before ANY commit and ensure it passes cleanly. Do not commit with failing checks. This is non-negotiable — every commit must leave the repo in a green state. Do not run these individually before committing — repo-check.sh covers everything. The pre-commit hook also runs repo-check.sh, so there is no need to run it manually before committing either.
The gate is 16 fail-fast steps, in order: CLI typecheck; portal-e2e typecheck; lint; build; a built-binary guard (so the E2E suites cannot silently skip); a pack smoke; the deterministic cache as a test prerequisite; tests with coverage; a coverage >= 90% threshold; the AST-cache false-green audit; a Chromium-present guard; the portal E2E driving real Chromium through Playwright; docs build; markdown lint; digest freshness; and finally the graph check, which runs yg check --approve --only-deterministic (free and keyless — it rebuilds the cache and reports in one step).
Two of those bite in ways the category list would not warn you about:
- The portal E2E needs Chromium installed for Playwright. Without it the guard fails the gate by design rather than letting the suite skip. Install it once:
(cd source/cli && npx playwright install --with-deps chromium). - The digest gate is not repo-root-only. It checks both installed artifacts —
AGENTS.mdand.clinerules/yggdrasil.md— at the repo root and in everyexamples/*/directory that carries its own.yggdrasil/. So after editingtemplates/digest.ts, oneinit --upgradeat the root is not enough: each such example needs its own, run from that directory against this repo's built binary. A newly added example with a graph but no agent-rules install fails this step too.
Only seven of the sixteen steps are themselves protected against being quietly dropped, by the advisory repo-check-gate-steps rule (typecheck, lint, build, test/coverage, docs build, markdownlint, graph check). The other nine rest on this list alone — if you add or remove a step, update it here.
Dogfood Issue Tracking
While working in this repo, if you encounter a problem with the CLI itself or with the rules/knowledge content (contradictions, missing warnings, misleading examples), append an entry to .temp/dogfood-report.md. Format:
## <date> — <short title>
**WHAT:** <what happened>
**WHERE:** <file:line or command>
**WHY:** <why it matters>
**REPRO:** <steps to reproduce>
Mark entries RESOLVED (with commit SHA) once fixed, or DEFERRED (with reason) when punted.
Memory
Do NOT use the auto memory system. All persistent knowledge goes into CLAUDE.md or AGENTS.md — nowhere else.
Working Preferences (maintainer-set; apply by default in this repo)
- Quality over cost. Iterate as much as needed; do not skimp on rigor to save effort or tokens.
- High bar, out-of-the-box. Aim for genuinely excellent — not "good enough" — and think past the obvious framing.
- Never hardcode assumptions; derive and verify. Establish facts from the real config/code and check them, rather than asserting "probably X" when X depends on configuration. (E.g. whether a reviewer costs money or needs an API key depends on the configured provider — a hosted API does; a local/CLI provider like
claude-codedoes not — read the config, don't guess.) - Don't expose internals in user-facing surfaces. A person sees what is happening in plain terms — not the names of commands, flags, or internal mechanisms.
- Use multi-agent processes for substantive work — opinion panels, adversarial review, research workflows — rather than a single pass. When external research is wanted, offer a ready-to-run research prompt in a code block so the maintainer can run it with their own agent.
- Subagents run on Sonnet or Opus only — set the model explicitly on every spawn. Never launch a subagent that silently inherits the session model. Default to Sonnet for research/mechanical work, Opus for hard synthesis or judging. Instruct subagents not to spawn their own agents (nested spawns would bypass the model choice).
- Ground yourself before designing. Read all of
yg knowledgeandyg schemasso you understand the engine completely before proposing a design. - Graph before code, hierarchically; lock the design in. Design the target architecture + aspects up front, then calibrate as work proceeds. Concrete ("betonuj") the intended rules, relations, and architecture in Yggdrasil — a hierarchical model + aspects — so a future session cannot build anything inconsistent with the design (
yg checkrefuses the drift). Where a preference here is mechanically checkable, prefer encoding it as an aspect, not just prose. - No artificial mocking. Tests run against real on-disk fixture projects (a real
.yggdrasil/graph + real source), never fabricated data. E2E tests use only the public CLI surface (spawn the builtbin.js), drive the real output in Playwright + Chromium — every path, properly, not a token smoke test — and assert consistency withyg check. This matches the repo's existingsource/cli/tests/fixtures/**+source/cli/tests/e2e/convention.
Task-dependent — ask, don't assume. A few preferences vary by task; at the start of a relevant task, ask the maintainer rather than defaulting:
- Working mode — run fully autonomously with minimal reporting, or collaborate with check-ins along the way.
- Visual deliverables — whether to route the visual layer through Claude Design (rendered previews on claude.ai) for review.
When Evaluating yg check or scripts/repo-check.sh
Consider both:
- Product — Is the command correct and useful for adopters?
- Dogfood — Is this repo's graph coverage correct and complete?
Yggdrasil
This repository is managed by Yggdrasil — continuous architecture enforcement.
An architecture graph in .yggdrasil/ defines the rules; a reviewer verifies
source code against them, and yg check blocks CI whenever an enforced rule
is violated or unverified.
Required first step: run yg prime and follow the protocol it prints
before making any change. The full, current operating manual comes from the
installed CLI — this block is only the standing summary. If yg prime is not
a recognized command, the installed Yggdrasil CLI predates this integration:
update the @chrisdudek/yg package before proceeding.
Non-negotiable invariants (they hold even before reading the manual):
- Never write a
yg-suppressmarker without the user's explicit confirmation. The reviewer honors suppressions unconditionally — an unauthorized suppress silently disables a rule. - Never change a rule's
review_by:date; renewing or retiring a rule is the user's decision. - Treat
yg adviseitems and incidents as proposals: dismissing, deferring, or recording one requires the user's approval. Never fabricate an incident. - Changes to
.yggdrasil/yg-architecture.yamlrequire the user's confirmation. - Log entries (
yg log add) carry WHY in self-contained prose — no references to plans, file paths, steps, or conversation state. - Never hand-edit
.yggdrasil/lock files. - If the user explicitly requests a code-only change without graph updates,
comply but warn: the affected rules stay unverified and CI stays red. Do
not run
yg check --approve— leave the rules unverified.
Start every session with yg check; re-print the manual any time with
yg prime.
Repository README
Describes krzysztofdudek/Yggdrasil as a whole, which may contain artifacts other than this one. Where this artifact had no useful description of its own, its summary was taken from here.
Yggdrasil
Say it once.
Write a rule and it holds in every session after that, without you repeating yourself. Before the agent edits a file it gets only the rules that touch that file, not all two hundred. After the edit they are checked, and a violation comes back as an error the agent has to fix before it moves on. The same checks re-run in CI for free, with no API key.
You probably don't feel this problem, and that is the interesting part
If you have ever been the only thing standing between an agent and production, solo, after hours, shipping something fast to find out whether it was worth building, then you know the wall. Code arrives faster than you can keep quality up with it. From there it goes one of two ways. You slow to a crawl because you now have to watch everything yourself, or you lose the thread and end up with bugs you can no longer trace back to a decision.
If you work somewhere that pays for quality, you have probably never hit that wall. Review, QA and the rhythm of a sprint sit between you and it. Those same things mean you have never seen your own unconstrained speed either.
Nobody measures this in either direction. The best study available put experienced developers 19% slower on real tasks with AI assistance, while they believed they had been 20% faster (METR, 2025). Neither the people who feel fast nor the people who feel careful have an instrument.
Scaffolding is not there to stop you falling. It is there so that the brake does not have to be a person.
Five minutes to your first enforced rule
Requires Node.js 22+. You can start without an API key: yg init offers "None for now" as a real answer, and script rules, dependency control and the CI gate all work from there with no key and no model calls.
npm install -g @chrisdudek/yg
cd your-project
yg init
yg check
That first check is green, and honest about why:
yg check: PASS (1 warning) 0 nodes · 0/50 files (0%) · 0 aspects · 0 flows
uncovered (50) Not under a coverage.required root. Visible, non-blocking.
Nothing is enforced yet, because you have not said what matters yet. Nothing is pretending otherwise. That list is your to-do, not a finding.
So say one thing to your agent:
"Every service that handles payments must emit audit events. Create a rule for it and apply it to the payments module."
It writes the rule and maps the module. yg check now fails, because that rule has never been verified against your code. yg check --approve verifies it. From that point the rule holds, and any change that breaks it comes back to the agent as an error before it reaches you.
That is the whole loop, and it is the shortest honest path to seeing it.
Prefer to be taught instead? Tell your agent "onboard me into Yggdrasil". In an adopted repo the agent knows the tutor playbook and will teach you on your own code, in your own language.
What it does
The rule: every charge records an audit event. The agent writes a refund that skips it.
async function refund(req) {
await payments.refund(req.body.chargeId)
return { ok: true }
}
yg check refuses it: refund changes a charge with no audit event. The agent adds the call, re-runs, passes.
async function refund(req) {
await payments.refund(req.body.chargeId)
await audit('refund', req.body.chargeId) // added
return { ok: true }
}
You reviewed nothing. That is the loop: the agent writes, the check runs, the agent fixes its own work before you look at it.
You attach a rule once and the tool works out everywhere it lands. You never paste it onto each file, and you never hand the agent the whole rulebook.
Two kinds of rule
Script rules ship a check.mjs that runs locally, every time, at zero cost. Deterministic, and there is no talking past it. This is the layer to lean on, and it is exactly the kind of rule an agent quietly drops when it is only a line in a rules file.
Judgment rules are plain Markdown, read by a separate model, for the calls a script genuinely cannot make.
# Audit every payment mutation
Any function that creates, updates, or refunds a charge must
call `auditLog.emit()` before it returns. A mutation with no
audit event is a refusal.
Judgment rules are the higher variance layer, so keep those components small and run new rules as advisory before you enforce them. A rule is one kind or the other, never both.
The rest of the vocabulary, components, flows, ports, statuses and the predicate language, is in the docs. You do not need any of it to get the first finding.
The part that is genuinely not available elsewhere
Every verdict, from a script or from a model, is recorded against a content hash of everything that produced it. CI does not re-run your model review. It recomputes the hashes and re-proves the existing verdicts, for free, with no API key.
In practice you pay a reviewer once per piece of code instead of once per pull request. Every metered AI review product bills you again for code that did not change, and none of them can stop without breaking their own pricing.
If the code changes, the hash changes, the verdict is void and the check goes red. A green build cannot quietly mean "we skipped that one".
Why it is built the way it is
Everything in this tool is here because at some point I needed it and did not have it. Nothing was added because it sounded good on a feature list. If a mechanism looks oddly specific, that is usually why, and the commit history says when.
I built it while shipping things alone, fast, which is where the wall above comes from. That is one person's experience, not a study. Take it as such.
Two limits, before you install
It enforces structure, not runtime behaviour. It can require that you call the audit utility. It cannot prove the audit fired in production.
A green check is only as good as the rule behind it. A shallow rule passes shallow code. The enforcement is real. Deciding what is worth enforcing stays yours.
See the whole graph
yg portal renders everything as a read only map in the browser: every component, every rule, and whether each one is verified against the code as it stands right now. Nothing is rounded up to green. yg portal --static writes a single self contained file you can hand to someone who has no checkout.
In CI
- run: npx @chrisdudek/yg check --approve --only-deterministic
- run: npx @chrisdudek/yg check
The first line rebuilds the free local cache that a fresh checkout never has. The second is the gate: it recomputes the input hash of every rule against its recorded verdict, and fails if anything changed without being verified. No keys, no model calls.
Works with
Any agent that reads AGENTS.md: Claude Code, Cursor, Copilot, Codex, Cline, OpenCode, Amp, Zed and others. yg init writes one universal rule set, so there is no platform to pick.
Reviewer providers: Anthropic, OpenAI, Google, OpenAI compatible, Ollama locally, or delegation to an installed agent CLI with no API key at all.
FAQ
How is this different from a rules file? A rules file is flat text dumped into every prompt, with no scoping and no verification. Here the agent gets only the rules that touch the file it is editing, and the output is checked against them.
How is this different from a pre-commit or agent hook?
A hook is a real gate and you should use one. Point it at yg check and you have wired this in. What a bare hook has no notion of is which rule applies to which file, rules that need judgment rather than a script, and a lock that lets CI re-prove a model verdict for free.
How is this different from an AI review bot? Review bots hunt for bugs against their own idea of good code, and they re-run and re-bill on every pull request. This checks your specific rules, the ones only your team knows, and records a durable proof of each verdict.
What if I want to stop?
Delete .yggdrasil/ and the rules file. No runtime dependencies, no build hooks, nothing left behind.
Examples and docs
examples/ has six runnable projects, four of them keyless. This repository uses Yggdrasil on itself, so .yggdrasil/ is a live graph you can read. Full docs at krzysztofdudek.github.io/Yggdrasil.
License
MIT
Trustgrade A
- passBody integrity
Whether the stored document is plausibly the kind of file the artifact declares, rather than something fetched by mistake.
- passType matchnot applicable to this artifact type
Whether the artifact is really the kind of thing its metadata claims it is.
- passFreshness
How long since the source repository was last pushed to.
- passPrompt injection
Scans the artifact's own text for instructions aimed at your agent rather than at you.
- passLicense
Whether the source repository declares an SPDX license permissive enough to redistribute.
How the grade is calculated
Each check contributes 0 points when it passes, 1 when it warns, and 2 when it fails. The total maps to a letter:
- Aevery check passed
- Bone warning
- Ctwo warnings
- Dprompt injection or body integrity failed, or three warnings
- Fone of those failed, and something else is wrong
These are automated hygiene checks, not a security audit, and not a dependency or vulnerability scan. A grade of A means nothing was flagged — not that the artifact is safe.
Versions
git-a9efb8b8dc5e2026-08-04