Skip to content

Family contracts

Yggdrasil, Grain and Horde do not read each other's files. They exchange machine documents — JSON or YAML with a named, versioned shape — and every one of them is listed here. One page, because a contract that each repository describes in its own README is three descriptions that drift.

The table is a register of every machine document the family writes, not only the ones that cross a repository boundary. A document nothing outside its own tool reads still gets a row, with no external consumer in the consumer column: a list that tries to hold only the cross-repository ones goes stale the week somebody adds an internal document, and nobody notices until a consumer appears for it.

A check fails when this page and the code disagree — in either direction, in any of the three repositories. See the guard below.

The register

DocumentSchema idProducerConsumersSinceDescribed where
Run report (yg check --json)yg-check/1YggdrasilHorde — the quality index, land, lawbefore 6.0.0CLI Reference
Context package (yg context --json)yg-context/1YggdrasilHorde — node resolution, landbefore 6.0.0CLI Reference
Blast radius (yg impact --json)yg-impact/1YggdrasilHorde — port consumersbefore 6.0.0CLI Reference
Component (yg node --json)yg-node/1YggdrasilHorde — node existence and structure; Grain — the proposal seambefore 6.0.0CLI Reference
Rule list (yg aspects --json)yg-aspects/1YggdrasilHorde — the quality index, law, landbefore 6.0.0CLI Reference
Attention feed (yg advise --json)yg-advise/1YggdrasilHorde — auditbefore 6.0.0CLI Reference
Rule history (yg aspects log read --json)yg-aspect-log/1YggdrasilHorde — lawbefore 6.0.0CLI Reference
Review package (yg verdict package)yg-review/1Yggdrasila judge outside the configured reviewer — no external consumer inside the familybefore 6.0.0CLI Reference
Recorded verdicts (yg verdict read --json)yg-verdicts/1YggdrasilHorde — retrobefore 6.0.0CLI Reference
Waiver inventory (yg suppressions --json)yg-suppressions/1YggdrasilHorde — land6.0.0CLI Reference
Marketplace manifest (yg-marketplace.yaml)yg-marketplace/1the marketplace authorYggdrasil — yg pack add / update / list, yg marketplace check6.0.0Packages
Package manifest (yg-package.yaml)yg-package/1the package authorYggdrasil — yg pack, yg marketplace check6.0.0Packages
Installed-package lock (.yggdrasil/yg-packages.yaml)yg-packages/1Yggdrasil — yg packYggdrasil — the package-file-modified rail and yg advise; no external consumer6.0.0Packages
Verification event line (.yg-events.jsonl, yg-events.llm.jsonl)yg-events — no schema field; versioned by v: 1Yggdrasil — yg check --approveHorde — cost, over the committed streambefore 6.0.0; the sha field arrived in 6.0.0Configuration
Advice (grain advise --json)grain-advice/1GrainHorde — queue quality; Yggdrasil — yg advise importbefore 6.0.0this page
Proposed graph (proposal.json)grain-proposal/1Grain — proposeYggdrasil — yg adoptbefore 6.0.0this page
Repository exportgrain-export/1Grain — grain exportadopter pipelines and audits; no external consumer inside the familybefore 6.0.0this page
Convention check (grain check --json, grain review --json)grain-check/1Grainno external consumerbefore 6.0.0this page
Obligation (grain obligation --json)grain-obligation/1Grainno external consumerbefore 6.0.0this page
Proposal report (grain propose --json)grain-propose/1Grainno external consumerbefore 6.0.0this page
Oracle record (oracle.json)grain-oracle/1Grain — grain oracle recordGrain's own scoring; no external consumerbefore 6.0.0this page
Adopter correction inside an oracle recordgrain-correction/1Grain — grain oracle recordGrain's own scoring; no external consumerbefore 6.0.0this page
Oracle file list (files.json)grain-oracle-files/1Grain — grain oracle recordGrain's own scoring; no external consumerbefore 6.0.0this page
Oracle scorecard (grain oracle score --json)grain-oracle-score/1Grainno external consumerbefore 6.0.0this page
Family candidates (.family-candidates.json)no schema id — versioned by v: 1Grain — the offline minerYggdrasil — yg advise, which rejects any v it does not namebefore 6.0.0this page
Law diffhorde-law/1Horde — lawthe session, and Ratatoskr6.0.0this page
Mission retrospectivehorde-retro/1Horde — retrothe session; no external consumer6.0.0this page
Mission planhorde-plan/1Horde — queue planHorde itself; no external consumerbefore 6.0.0this page
Drill casehorde-drill-case/1Horde — drillHorde itself; no external consumerbefore 6.0.0this page

before 6.0.0 means the document already existed when the family started releasing together; 6.0.0 is the first joint release, so it is the first version this column can name precisely.

The rule

Adding a field inside /N is free — a consumer that does not know the field ignores it, and every document here may grow that way without warning. Changing the shape of a field that already exists is /N+1, and a new family version with it. A consumer that is handed a document version it does not know refuses, and names the version to install rather than guessing: in Horde that refusal is failStaleCli in skills/horde/scripts/node.mjs, reached when ygJson sees a schema value that is not the one the call site asked for, and it prints the release to upgrade past. (The neighbouring failNoCli is a different refusal — no CLI at all — and says nothing about versions.)

A fourth sentence, because the alternative is a silent break: a change in what an existing field means counts as a shape change, even when the field's type is untouched. A list that used to be empty when a relation named no port, and is never empty now, tells a consumer something different under the same number.

That rule has one recorded exception, taken deliberately in 6.0.0. Normalising every relation onto a named port made the port lists in the component and blast-radius documents never empty, and the decision was to keep both documents on their existing numbers rather than bump them — the change is stated in the changelog and in each document's own reference instead. It is an exception on the record, not a precedent: the next silent meaning change takes a number.

The guard

Two halves, both deterministic, no network and no clock.

In Yggdrasil, source/cli/tests/unit/repo/family-contracts-invariant.test.ts reads every *_JSON_SCHEMA constant out of source/cli/src/formatters/ and requires each one on this page, and requires every yg-…/N id on this page to be either one of those constants or on a named whitelist of documents produced elsewhere. The assertion runs both ways on purpose: one direction alone lets a row for a document that no longer exists sit here forever.

In Grain, the seam job — the only CI that has all three checkouts at once — scans Horde's scripts for the schema ids they read, collects the ids Grain writes, and requires every one of them on this page. Drift in any of the three repositories turns that job red.