Skip to content

Yggdrasil stores verification rules in a graph under .yggdrasil/ in your repository. The graph has three main elements: nodes, aspects, and flows.

The agent creates and maintains these. You tell the agent what matters, and it builds the graph structure.


Directory structure

text
.yggdrasil/
  yg-config.yaml          ← project config (reviewer, quality thresholds)
  yg-architecture.yaml    ← node type definitions
  model/                  ← nodes (your system's components)
    orders/
      order-service/
        yg-node.yaml
    payments/
      payment-service/
        yg-node.yaml
  aspects/                ← cross-cutting rules
    requires-audit/
      yg-aspect.yaml
      content.md
    rate-limiting/
      yg-aspect.yaml
      content.md
  flows/                  ← business processes
    checkout/
      yg-flow.yaml
  schemas/                ← YAML schemas (auto-generated by yg init)

Nodes

A node represents a component in your system: a module, service, library, or any cohesive piece of code. Each node is a directory under .yggdrasil/model/ with a yg-node.yaml file.

yaml
# .yggdrasil/model/orders/order-service/yg-node.yaml
name: OrderService
type: service
description: "Manages order lifecycle: creation, validation, state transitions"

aspects:
  - requires-audit
  - rate-limiting

relations:
  - target: payments/payment-service
    type: calls

mapping:
  - src/orders/
  - src/orders.ts

Key fields:

  • type — must match a type defined in yg-architecture.yaml (e.g. module, service, library)
  • aspects — list of aspect IDs this node must satisfy
  • relations — outgoing dependencies to other nodes (see Relations below)
  • mapping — source files and directories this node owns (paths relative to repo root). Entries accept minimatch glob patterns: * matches within a single path segment, ** matches across segments. For example, src/db/*Repository.cs maps only files matching that pattern in src/db/; src/**/*.ts maps all TypeScript files anywhere under src/. Plain entries (no glob metacharacters) behave as before: an exact file or a directory prefix that covers all files beneath it.

Nodes can be nested. Children inherit parent aspects automatically.

Node size budget

The LLM reviewer sends all of a node's mapped source (plus any aspect reference files) in one prompt, so a node has a character budget: quality.max_node_chars in yg-config.yaml, default 40000. A node that exceeds it produces an oversized-node error in yg check — split it into children or trim its mapping. The budget applies only to nodes an LLM reviewer actually reads — those with at least one non-draft LLM aspect. Nodes reviewed only by deterministic aspects (check.mjs reads files programmatically, with no context window) and aspect-less nodes are never bounded. Binary files (images, fonts, archives, etc.) count 0 toward the budget automatically. For a large unsplittable text artifact — a generated lockfile, for example — opt the node out with sizeExempt: { reason: "..." }.

Relations

Relations declare dependencies between nodes. They serve two purposes: yg impact uses them to calculate blast radius, and yg check validates that targets exist and port contracts are satisfied.

yaml
relations:
  - target: payments/payment-service
    type: calls
  - target: shared/logger
    type: uses

Available types: calls, uses, extends, implements, emits, listens. Relation types are constrained by the architecture — each node type declares which target types it may reach per relation type, and yg check rejects relations that violate those constraints. The event types emits and listens must be paired.

Ports

A node can declare ports — named entry points with required aspects. When another node consumes a port, it must satisfy the port's aspects.

yaml
# payments/payment-service/yg-node.yaml
ports:
  charge:
    description: "Charge a payment method"
    aspects: [correlation-tracking]
yaml
# orders/order-service/yg-node.yaml
relations:
  - target: payments/payment-service
    type: calls
    consumes: [charge]

Now orders/order-service must satisfy the correlation-tracking aspect because it consumes the charge port. This is the "port contracts" channel in the aspect propagation table below.

Minimal nodes (coverage without enforcement)

For parts of the codebase you don't need to enforce rules on, create a node without aspects:

yaml
name: LegacyAuth
type: module
description: "Legacy authentication system — do not modify"
mapping:
  - src/legacy/auth/

Nodes without aspects auto-approve instantly — no hashing, no LLM review. They satisfy the coverage requirement without adding enforcement overhead. When you're ready to enforce rules, add aspects to the node.


Aspects

An aspect is a cross-cutting architectural rule. It's a directory under .yggdrasil/aspects/ with a yg-aspect.yaml and one or more .md content files.

yaml
# .yggdrasil/aspects/requires-audit/yg-aspect.yaml
name: Audit Logging
description: "Every mutation must emit an audit event"
markdown
<!-- .yggdrasil/aspects/requires-audit/content.md -->
Every public mutation endpoint must emit an audit event before returning.

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

The content .md files are the actual rules. The reviewer reads them and checks whether the source code satisfies them. Write them as clearly as you would write a code review comment.

Aspects are verified by reviewers — Yggdrasil ships two reviewer types (LLM and deterministic). See Reviewers for the decision matrix, authoring guides for content.md, check.mjs (deterministic), suppression mechanics, and edge cases.

The implies field

An aspect can declare that it implies other aspects. Implied aspects are included recursively — if A implies B and B implies C, then a node with A automatically gets B and C as well.

yaml
# .yggdrasil/aspects/requires-audit/yg-aspect.yaml
name: Audit Logging
description: "Every mutation must emit an audit event"
implies:
  - requires-logging

Implied aspects must exist in the graph. Cycles are forbidden — yg check detects them. Use implies when one rule logically requires another to be satisfied first. For example, audit logging only makes sense if diagnostic logging is also active.

Reference files

LLM aspects may declare supporting files via references: in yg-aspect.yaml. The reviewer reads them as authoritative context; the agent sees them under read: in yg context.

yaml
references:
  - docs/error-codes.md
  - path: source/lib/codes.ts
    description: "Source of truth for error code constants."

Changes to referenced files cascade like changes to content.md — every node where the aspect is effective is re-approved. Run yg impact --file <ref> before editing a widely-referenced file. Size limits are configured per reviewer tier in yg-config.yaml.

Aspect status

Each aspect carries a status — draft, advisory, or enforced — that controls whether the reviewer runs and how violations surface:

StatusReviewer runs?Refused renders as
draftnon/a (skipped)
advisoryyeswarning
enforcedyeserror (blocks CI)

Status defaults to enforced. Set status: advisory on a new rule to gather signal across the repo without blocking CI; promote to enforced once you trust it. Status can be set on the aspect itself or overridden on any attach site (node, type, flow, port). The effective status on a node is the strictest declared across all channels — bumping up is fine, silent downgrades are rejected.

See Aspect Status for the full lifecycle, status_inherit on implies edges, drift semantics, and migration from 4.x.

How aspects reach nodes

Aspects propagate through seven channels:

ChannelHow it works
OwnListed in aspects: field of yg-node.yaml
AncestorParent node aspects inherited by all children
Own typeDefault aspects defined per node type in architecture file
Ancestor typeArchitecture defaults from parent node types
FlowsAll flow participants inherit flow-level aspects
PortsConsumer must satisfy port-required aspects
ImpliedAspects included via implies chains (recursive)

One aspect can reach dozens of nodes through these channels. When you change an aspect's content, every node that uses it gets flagged for re-approval.


Flows

A flow describes a business process that spans multiple nodes. It's a directory under .yggdrasil/flows/ with a yg-flow.yaml.

yaml
# .yggdrasil/flows/checkout/yg-flow.yaml
name: Checkout
description: "Customer places order, payment is processed, inventory reserved"
nodes:
  - orders/order-service
  - payments/payment-service
  - inventory/inventory-service
aspects:
  - correlation-tracking

Flow-level aspects (like correlation-tracking above) propagate to all participants. This is useful for cross-cutting requirements that apply to an entire business process.


The enforcement loop

  1. Agent reads context before writing code (yg context --file <path>) and sees which aspects apply
  2. Agent writes code
  3. Agent runs yg approve — each aspect's reviewer checks the source code against its rules. LLM aspects call the model with content.md; deterministic aspects run check.mjs locally with no LLM call
  4. Reviewer says pass → approved, new baseline hash recorded
  5. Reviewer says fail → aspect-violation, agent must fix and re-approve

yg check is the CI gate. It compares file hashes — no LLM calls, runs instantly. If a file changed since its last approve, check fails. The agent does the approve (running the relevant reviewer) locally, CI just verifies it happened.


Architecture file

yg init creates .yggdrasil/yg-architecture.yaml with an empty node_types: {} and commented examples — types are defined per project. This file controls what types of nodes are allowed and can set default aspects per type. You define the types your project needs; the agent knows the schema and can modify this file on your behalf.

yaml
node_types:
  module:
    description: "Business logic unit with clear domain responsibility"
    log_required: false
  service:
    description: "Component providing functionality to other nodes"
    aspects: [requires-audit]
    log_required: true
    enforce: strict
    parents: [module]
    relations:
      calls: [service, library]
      uses: [library]
    when:
      path: "src/**/*.service.ts"
  library:
    description: "Shared utility code with no domain knowledge"
    when:
      path: "src/shared/**"

Fields per type:

  • description — Human-readable label shown in yg tree and yg type-suggest output.

  • aspects — Default aspects automatically applied to every node of this type. This is one of the seven aspect distribution channels ("by node type"). Use it for cross-cutting rules that all nodes of a type must satisfy — e.g. every service must have audit logging. Run yg impact --type <id> before adding an aspect here to see how many nodes will be affected.

  • log_required — Whether yg approve requires at least one log entry before running the reviewer. Defaults to true. Set to false for types where business reasoning entries aren't needed (e.g. test suites, config nodes).

  • enforce: strict — When set to the string literal strict, every source file matched by the type's when predicate must belong to exactly one node of this type. Files matched by when but not in any node's mapping: produce a type-strict-orphan error in yg check; files mapped to a node of the wrong type produce a type-strict-misplaced error. Use this to prevent new files from being added to the codebase without graph coverage.

  • parents — List of type IDs that nodes of this type may nest under. If specified, yg check rejects nodes placed under an unlisted parent type. Omit to allow any parent.

  • relations — Map of relation type → allowed target types. Restricts which node types a node of this type may declare relations to. yg check rejects relations that violate these constraints.

  • when — Predicate that classifies files into this type. Can match on path (glob pattern) and/or content (text patterns). Used by yg type-suggest and by enforce: strict backward coverage. See Conditional Aspects for the full predicate grammar — the same grammar applies here. A type with no when is organizational: it can act as a hierarchy parent but its nodes cannot have a non-empty mapping:.


Next

  • Reviewers — LLM and deterministic reviewer types, authoring guides, suppression, drift, edge cases
  • Conditional Aspectswhen predicates for selective aspect application
  • CLI Reference — full command surface