# Agentic Intent Model (AIM) v5.2
Agentic Intent Model (AIM) is a specification language for humans and AI agents. It makes the **intent** of a system durable and computable — from an application's behavior to a complete business process or an organization's commitments. The model remains readable enough for the people who own the intent, while being structured enough for agents to realize it, review it, repair it, and verify reality against it. Software was AIM's first domain and remains its most fully worked example; nothing in the language is specific to it (§18).
**The central proposition.** Every consequential system has two forms: what its owners intend and what actually happens. For an application, reality is code, configuration, data, interfaces, and runtime behavior. For an organization, reality also includes people, approvals, operating procedures, automations, external systems, deadlines, and evidence of work performed. AIM gives both forms a shared structure. It connects purpose and requirements to the contracts, flows, views, events, roles, and resources that fulfill them, then binds those commitments to the places where they are realized.
The result is more than documentation. It is a navigable chain from **why** a system exists, through **what** it promises and **how** those promises are fulfilled, to **where** each part exists in reality. That chain makes drift computable. In an application, drift may be behavior implemented without intent, intent with no implementation, or a stale binding after code moves. Across a company, drift may be a policy no process enforces, an approval people routinely bypass, an automation that no longer fulfills its obligation, a manual step with no accountable performer, or a stated commitment with no capability that satisfies it. The same graph and review loop expose all of these as mismatches between declared intent and observed reality.
AIM v4 was a **breaking change** from v3.1. v3.1 made AIM Markdown-native and self-bootstrapping; it succeeded at making intent *readable*, but it left AIM's highest-value job — the **linking** between the things a system is made of — almost entirely informal. Real applications are not trees; they are **graphs**. A View exposes an Action that invokes a Contract that reads or mutates a Record and emits an Event that a Persona can reach. v3.1 expressed those relations in inconsistent prose ("Invoked by Contract: X", "TRIGGER: Contract.Y", "CALL Z"), demoted the traceability chain to "a useful target, not a requirement," and offered no way to traverse, check, or diff the relation graph.
The three structural shifts of the v4 break — still the spine of the language:
1. **Graph-founded model.** The `.aim` Markdown file is understood as a *projection* of an underlying node-and-edge graph. Every heading is an addressable node; every cross-reference is a typed, directed edge. The graph is *derived* by collecting edges across files — it is never authored as a separate artifact, so `.aim` files remain the sole authority.
2. **Typed edge taxonomy.** A single CommonMark-native edge token replaces the prose. Each edge carries a verb from a closed set and points at a canonical node address. This makes the relation graph traversable and checkable: dangling references, orphan nodes, and impact sets fall out for free, and the traceability chain becomes *computable* rather than aspirational.
3. **Intent↔realization binding layer.** Intent nodes may bind to their realization sites — in code (`file#symbol`, `route:…`, `table:…`), in operations (`system:…`, `agent:…`, `workflow:…`). Drift detection then becomes a **graph-diff** between the declared intent graph and the realized graph recovered from reality, yielding precise, owner-routed findings.
v5 keeps that foundation intact and generalizes it: the unit is named the **intent**, the tree reads parent/children (§2.1), and nothing in the language is specific to software (§13.3, §18).
The design bar for all three is **LLM-parsability** — consistent conventions an LLM follows and traverses reliably, not a rigid grammar requiring a custom parser. AIM remains valid CommonMark that renders on GitHub with no special tooling.
AIM is the authoritative shared artifact between humans and agents. Realization, review, and repair all run against AIM. When reality and intent disagree, the mismatch is resolved explicitly — either by fixing the realization or by revising intent.
**Where AIM is going, and why the drift machinery exists.** AIM's destination is intent as source: the model is what humans maintain, and the realization — code, automations, agent behavior — is synthesized or performed from it, the same inversion every prior abstraction jump made, from assembly to compilers. Today that is not yet safe: synthesis is stochastic, humans still change reality directly, and most systems predate their model (§17). The binding layer, graph-diff, and repair loop (§10, §12) are the trust-building apparatus for exactly this transitional period — every clean report is evidence that intent → reality is reliable for that intent. The success criterion of that machinery is to make itself progressively unnecessary: for an intent that is fully confirmed, Level 3, and repeatedly clean, regeneration replaces repair, and its binding file quietly changes meaning from review tool to build manifest. Readers and agents should treat this as the interpretive key for the rest of the document: the behavioral facets and the edge graph are the destination; the drift machinery is the road.
**Who writes AIM, and when it is worth it.** In practice `.aim` files are authored by an agent (the Architect role, §1.2) from a human's narration — not hand-written token by token. The typed structure therefore costs the author nothing and yields a more precise, checkable artifact. This answers the obvious objection — *if an agent writes the model and an agent produces the realization, why not act directly?* The `.aim` file is the **durable, human-reviewable, machine-checkable contract** between intent and reality: a small model is something a human can read, correct, and diff — and a Reviewer can check reality against — far more cheaply than inspecting the realization itself, and it persists across sessions where a chat prompt does not. The corollary is a boundary worth stating plainly: AIM pays off when reading the model is meaningfully easier than inspecting the realization; for trivially small or throwaway work, acting directly is the right call.
---
## 1. Core Model
An intent is identified by a dotted namespace such as `juice.tasks` or `game.snake`. Child intents extend the namespace: `juice.tasks.create_task` is a child of `juice.tasks`.
Each intent has:
- one required **intent file** (a `.aim` file with `kind: intent`)
- zero or more optional **facets**: `schema`, `flow`, `contract`, `persona`, `view`, `event`
- zero or more optional **child intents** (each is an intent in its own right)
- zero or more optional **mapping files** (`kind: mapping`) — capability-to-provider bindings
- inline **`### Bindings` properties** on nodes — intent-to-realization bindings (a deprecated `kind: binding` sidecar file is still accepted)
The intent file is the canonical entrypoint. All other detail attaches to it directly (embedded), indirectly (sibling facet files), or through child intents.
### 1.1 What Changed From v3.1 (The v4 Break)
| Concern | v3.1 | v4 |
|---|---|---|
| Underlying model | Namespace tree (parent/child) | Node-and-edge graph; the tree is one edge type |
| Cross-references | Prose mentions, inconsistent forms | One typed edge token: `[verb](aim:
)` |
| Traceability chain | "A useful target, not a requirement" | Derived from declared edges; checkable |
| Inverse relations | Authored twice (`### Trigger`, `### Emitted By`) | Declared once at the acting end; inverse derived |
| Code linkage | None | Optional `kind: binding`; drift becomes graph-diff |
| Per-file `version`/`spec` | Contradictory in v3.1 (frontmatter omitted them, but version-inheritance, registry, and diagnostics still required them) | Removed everywhere; version lives only in `AGENTS.md` (and the external catalog) |
### 1.2 Roles And Capabilities
AIM is operated through three **capabilities**, each defined by what it may write — not by a roster of agents a human must choose between. In software they read as architect, developer, reviewer; in operations, as process designer, operators and automations, audit. They are **operating modes, not a menu**: one agent may hold all three, and a mature tool classifies the human's request and enters the right mode itself rather than asking which role to use.
| Capability | Writes intent (`.aim`) | Writes realization | Certifies match |
|---|---|---|---|
| **Architect** | ✓ | — | — |
| **Realizer** (Developer) | — | ✓ | — |
| **Reviewer** | — | — | ✓ (writes nothing) |
- **Architect** — **designs the intent graph**: translates a human's narration into intents, their facets, and the typed edges among them. The `.aim` files are the graph's serialization (§2), not the design itself — an Architect who writes facets without edges has not architected, only documented. Owns the model; authors binding facets when realization is known. The Architect works in two **directions**: *forward*, designing intent from requirements, and *reverse*, recovering intent from a system that already exists (**Encoding**, §17). The reverse direction is the same capability with one difference — its output carries `provenance: inferred` (§17.2) and awaits human confirmation, because it is read off reality rather than authored. Direction and provenance are variations of authoring, not a separate role.
- **Realizer** — makes reality match the model: a development team implementing code and tests, an ops team wiring automations, an agent performing a process. Emits or updates bindings for what it realizes. Fixes the realization when drift is found. (In the software domain this role is conventionally called the **Developer**, and the shipped prompt templates use that name.) A realization is either an **artifact** produced once (code, a configuration) or a **performance** repeated per instance (a process executed by people or agents). In the performed case realization work happens at execution time, so the performer wears this role — an agent executing a process is a Realizer, and the conduct rules below bind it: that is precisely what forbids it from improvising behavior the model does not state.
- **Reviewer** — diffs the declared graph against the realized graph recovered from reality (code, configuration, execution logs — §10.1) and reports drift, writing nothing.
**Verification must be independent.** The Reviewer capability MUST run in a context that cannot write what it judges — a cold evaluation with read-only access to the model and the realization. An agent that both produces a realization and certifies it against intent will pass its own work; drift detection is only meaningful when something with no stake in the output re-derives the graph and diffs it. Independence is a property of the *context and its permissions*, not of the model in use: the same agent re-invoked cold and denied write access is a valid Reviewer; that agent continuing in the session that just wrote the code is not.
Roles are workflow guidance, not language constructs — they do not appear in `.aim` source files. A single agent may hold multiple capabilities and multiple agents may share one; the only separation that is normative is Reviewer independence, above. See [`PROMPT.md`](./PROMPT.md) and [`agents/`](./agents/) for concrete prompt templates for the software domain.
Repair is a verb, not a separate role. When the Reviewer flags drift, either the Realizer fixes the realization or the Architect revises the intent. The decision is explicit, not silent.
Normative behavior across all roles:
- The Realizer must not invent material behavior absent from intent.
- When detail is missing, preserve documented intent and minimize assumptions.
- Assumptions are surfaced for review or converted into explicit intent updates by the Architect.
- When reality and intent disagree, the mismatch is resolved — the Realizer fixes the realization if it is wrong, the Architect revises intent if the model is outdated.
**Architect: validate before present.** Before presenting a proposal to the user or committing an EXTEND or ADD (§16.1), the Architect MUST derive the graph (§2.4) over the *proposed* state and **repair every hard error (§12.1) it can resolve autonomously** — a dangling edge left by a rename, an out-of-sync `## Children` index, a path/header mismatch, an illegal `(verb, from, to)` triple. Only diagnostics that genuinely need a human decision are surfaced: a confirmed-duplicate `merge` (§16.3 invariant 6), an unresolved canonical-home choice (§15.8), or an ambiguous binding (§12.3). A proposal presented with unrepaired hard errors is **non-conforming Architect behavior** — the graph must be well-formed before a human is asked to review it.
### 1.3 Project Authority Model
AIM is Markdown-native by deliberate choice, but that choice creates a risk: AI agents already love to spawn `.md` plans, design notes, decision logs, and PRDs. Without a clear authority boundary, AIM becomes one more `.md` file in the pile instead of the artifact that displaces it. The following rules establish that boundary.
**Authority hierarchy:**
1. **`.aim` files are the sole behavioral authority.** Every requirement, contract, schema, flow, persona, view, event, and **edge** that defines the system's behavior must live in a `.aim` file. Tools, agents, and reviewers treat `.aim` as the only source of truth for what the system is supposed to do. The derived graph is a *view* of these files, never a competing artifact.
2. **Other `.md` files are explanatory, not authoritative.** `README.md`, `CONTRIBUTING.md`, ADRs, and similar documents may describe, link to, or summarize intent — but they must not define new behavioral requirements. If a behavioral requirement appears only in an `.md` file and not in a `.aim` file, it is **drift**. The Reviewer reports it. The fix is to move the requirement into a `.aim` file.
3. **Anything outside `/aim/` is invisible to authority.** Behavioral content found in `docs/`, top-level `.md` files, code comments treated as spec, or chat history transcripts is not part of the project's behavioral authority. If it matters, it gets moved into `.aim`. If it doesn't, it isn't authoritative. The lone exception is `AGENTS.md` at the project root (see §3.3) — which carries project bootstrap metadata for agents but does not define behavior itself.
**Behavior vs. realization.** A binding (§10) records *where* behavior is realized. Realization is not behavior. Bindings are authoritative for the intent↔realization mapping but never define what the system should do — that always lives in the behavioral facets. A binding carries its own `provenance`, so it can go stale (code moved) without the intent being wrong — realization stays a separately-trusted layer even when it sits inline on the node (§10.2).
**The system vs. the work on the system.** AIM describes the *system under specification* — its commitments, actors, data, and events. Facts about *producing and maintaining that description* — who authored a file, what review state it is in, when and by whom it changed — are **meta-level** facts. The test for any proposed field, block, or marker: *would the fact survive replacing the entire team and toolchain while the specified system stays identical?* If it would not, it is meta-level. Several existing rules are instances of this one rule: roles are workflow guidance, not language constructs (§1.2); implementation status is banned from behavioral content (§3.8 — intent, not status); work artifacts are point-in-time and non-authoritative (§16.4, §17.4); enforcement mechanics and report formats are tooling concerns this spec does not define (§10.1). Evolution history and change governance belong to the project's storage and collaboration environment — a version-control system, a product database, a reviewed file share — and AIM requires nothing of that environment: the spec defines what the files *mean*, never how they are versioned, reviewed, or approved. Meta facts that must travel *with* the files — authority and accountability — use the spec-defined frontmatter fields (`provenance`, `owner`, `status`, `approved_by`, §3.2); further project-specific keys are permitted and carry no spec semantics. Meta facts never appear in the **body**: no status markers in requirements, no changelogs, no review annotations in behavioral content. The boundary excludes only the meta level: actors *of the specified system* are object-level and fully in-model — a `Persona` is a commitment about who the system serves, never a statement about who maintains its spec. A business process specified in AIM therefore legitimately models its workers, approvals, and execution evidence as Personas, Contracts, and Events, while the question of who edits those `.aim` files stays outside the language.
**Diagnostics:**
- **Hard error** — none for the prose/authority boundary. The Authority Model is enforced socially and by review, not by the parser. Tools cannot reliably distinguish "describes intent" from "defines intent" in arbitrary prose.
- **Informational diagnostic** — reviewers and validators may flag `.md` files that appear to contain behavioral requirements not present in `.aim` files. The recommended remediation is always to move the requirement into a `.aim` file.
**Why this matters:**
The whole point of AIM is to replace `.md` sprawl with a structured behavioral artifact agents can read once and build from. Without the Authority Model, the same agents that spawned 100 `.md` files will spawn 100 `.md` files plus a few `.aim` files. With the Authority Model, every behavioral fact has exactly one home — and drift between sources is impossible because there is only one source.
---
## 2. Graph Model
v4 reinterpreted the v3.1 file surface as the projection of a graph. No new file format and no new parser tier: every node already has a heading, and every edge is just a typed cross-reference. This section defines what a node is and how it is addressed; §8 defines edges.
**Why a graph, and what kind.** An application's intent is not a document; it is a set of commitments and the relations among them. A facet is a **unit of intent**; a typed edge is **relational intent** — `[exposes](aim:#Contract:CreateTodo)` is itself a normative statement ("this surface shall offer this operation to users"), carrying the same authority as any requirement bullet. The graph is therefore **normative, not descriptive**. A code-analysis graph is derived from source and cannot disagree with it — it has no concept of *wrong*, only of structure. The intent graph is *authored*, so it can be wrong, unrealized, or drifted-from — and that capacity for disagreement is the point: drift (§10, §12) is only a meaningful concept because the declared graph is authoritative over what the system *should* do, independent of what the code *does*.
**Why prose could not hold it.** Prose and headings are trees; application behavior is a graph. Serializing a graph into a tree forces every relation that crosses the hierarchy to be stated twice — once at each end ("Invoked by Contract: X" on the flow, "CALL X" in the steps) — and duplicated statements inevitably drift apart. v3.1's "three inconsistent expressions" problem (§8.3) was the structural symptom, not sloppy authoring. v4 re-normalized this: every relation is declared once, at the acting end, and inverse views are derived, never authored. The resulting division of labor is exact — **prose carries semantics** (what a commitment means, and why), **the graph carries structure** (how commitments compose into a system). Each holds precisely what the other cannot.
**Who reads which projection.** The model carries two structures for two audiences, superimposed in the same files. The **tree** — namespaces, `## Children`, headings — is the *human* projection: hierarchy gives progressive disclosure (a handful of children per level, drill down), and the parent–child edge carries explanation — descending answers *how*, ascending answers *why*. Humans navigate, review, and understand the model through the tree. The **graph** — the typed edges — is the *machine* substrate: complete, queryable, and never meant to be read whole. Humans consume the graph only as **answers**: an impact set, a satisfies-coverage report, a calendar of every Trigger, one role's operations. Tools SHOULD surface the graph as such views and queries, and SHOULD NOT require a human to comprehend the global graph — no human can, and the design never asks them to. A `.aim` file is both projections at once: its headings are the tree, its edge tokens are the graph, and each reader takes their half (§18). And the two structures are independent by design: the tree may nest intents to whatever depth they earn (§5.5 — every level re-earns the shape rules), while the typed edges form **one global graph** that crosses tree levels and intent boundaries freely (§8.1) — tree position never limits what an edge may connect; it only decides who owns the node.
The split rests on a hard asymmetry: a human can hold a tree in mind; no one holds a graph. That makes the tree not a convenience view but the model's **entire human interface** — and its *shape* a first-class quality property. Every intent SHOULD read as a short table of contents for the level below; an intent that degenerates into a flat bag of technical nodes has failed its audience even while the graph beneath it validates perfectly. The noun-cluster diagnostic (§12.2) and the `promote` transform (§16.2) exist to restore the ladder.
### 2.1 Nodes
A node is any **addressable heading** in the resolved source. There are three ranks, all of which already exist as headings:
| Rank | Markdown | Node-type | Example |
|---|---|---|---|
| Intent | `aim:` frontmatter / H1 | `intent` | `nemicko.demo.todo` |
| Facet | `## : ` | `schema` `view` `contract` `flow` `persona` `event` `trigger` (+ `capability` — the `## Capability:` surface, §9.2; **not** a `## Requirements` list item) | `## Contract: CreateTodo` |
| Facet sub-block | `### ` and its list items | `block` (addressable, not separately typed) | `### Ensures` item `[2]` |
Top-level prose sections (`## Summary`, `## Requirements`, `## Tests`, `## Dependencies`) are nodes of type `section`. They are valid anchor targets for drift reports but are **edge-inert** as sections — a section heading is never itself the endpoint of a typed edge. Only `intent` and the facet node-types participate in the edge graph, with two defined sub-block exceptions: a **schema attribute** participates solely as an endpoint of a `refs` edge (§3.7, §8.2), and a **`## Requirements` list item** participates solely as the target of a `satisfies` edge (§8.2). Both are addressable sub-blocks, not separately typed nodes; no other sub-block is ever an edge endpoint, and `## Requirements` as a *section* stays edge-inert (only its individual items are `satisfies` targets). The node-type is read directly off the facet-heading keyword; there is no inference.
Intents form a **tree**: hierarchy is a *property*, not a kind. There is exactly one intent node-type — an intent with a `parent:` (§3.2, §5) is the same kind of node as the root, and a node's tree position never changes what it is: the tree is a projection (§2.4). The relational vocabulary this spec uses for tree position is **parent**, **child** (plural **children**), **root intent**, **leaf**, and **siblings**.
### 2.2 Node Addresses
The canonical address of a node is:
```
#:[ → ### []]
```
- `` — the dotted namespace from the file's `aim:` field. Present in any stored or derived address (fully qualified). **Elidable** at an inline reference site when the target resolves within the same intent, yielding the unqualified form `#:`.
- `#:` — the facet heading, verbatim. `FacetType` is capitalized exactly as in the heading (`Contract`, `View`, `Record`, …). Because facet names are constrained to `[A-Za-z][A-Za-z0-9_]*` (§3.6), the address is always a valid CommonMark link destination with no percent-encoding.
- `→ ### []` — optional finer pointer into a sub-block list item, 1-based, matching the Reviewer's drift-report convention.
This is the address scheme drift reports already use (`## Contract: CreateTask → ### Ensures [2]`), promoted from a review artifact to the language's identity scheme.
**Sub-block addresses are point-in-time, not durable.** A `→ ### []` (or `→ ## Requirements []`) pointer is 1-based and positional: inserting or reordering a list item silently re-points every later index, so such an address is valid only relative to a *specific revision* of the file. Authored `.aim` source therefore MUST NOT use a sub-block address as an edge target — typed edges (§8) target facet-level nodes only (`#:`). Sub-block addresses appear only in point-in-time work artifacts (drift reports and change records under `/aim/work/`), which SHOULD additionally record the item's text as a **content anchor** so the reference re-resolves after the list shifts.
The one sub-block target permitted in authored source is a `## Requirements` list item referenced by a `satisfies` edge (§8.2), and it comes in two forms with different durability. A **positional** reference (`#Requirements[3]`) inherits the point-in-time fragility above: inserting or reordering requirement bullets is a `rename`-class transform (§16.2), and every inbound positional `satisfies` edge MUST be re-pointed under §16.3 invariant 1 — an edit made without tooling silently re-points them, which is why positional references are the casual form, not the durable one. A **labeled** reference (`#Requirements[NET14]`, §8.2) targets a requirement by its stable label and survives any reordering — once `satisfies` edges exist, labels SHOULD be used.
### 2.3 Address Examples
```
nemicko.demo.todo # intent node
nemicko.demo.todo#Record:TodoItem # facet node
nemicko.demo.todo#Record:TodoItem → ### Schema [3] # the `title` attribute
nemicko.demo.todo#Contract:CreateTodo
nemicko.demo.todo#Contract:CreateTodo → ### Ensures [2] # "Emits a TodoCreated event"
nemicko.demo.todo#Flow:ExecuteCreateTodo → ### Steps [3]
nemicko.demo.todo#Persona:StandardUser
nemicko.demo.todo#View:TodoDashboard → ### Actions [1]
nemicko.demo.todo#Event:TodoCreated
```
Within a single-file intent, every node shares the one intent prefix, so inline references drop it entirely (`#Contract:CreateTodo`).
### 2.4 The Graph Is Derived
There is no graph file. A tool or agent builds the project graph by:
1. **Collecting nodes** — every intent/facet/sub-block heading in the resolved source becomes a node keyed by its fully-qualified address.
2. **Collecting edges** — scanning inline edge tokens (§8) and structured `ref(Type.field)` attributes; each yields one directed, typed edge `(from = enclosing facet node, verb, to = resolved address)`.
3. **Validating** each edge against the from→to schema (§8.2) and resolving each target via §11.1.
4. **Adding derived inverse edges** (§8.4) and reconciling them against any authored inverse blocks.
The result lives only in tool/LLM memory or a build artifact. The `.aim` files remain the sole authority (§1.3).
---
## 3. File Format
### 3.1 Extension
All AIM source files use the `.aim` extension. The extension is a brand and discipline marker: a file named `*.aim` is an authoritative AIM artifact, not a generic note. (Legacy v2.2 sources used `.intent`. Every version since v3.1 uses `.aim`; neither the v3.1→v4 nor the v4→v5 break changed the extension.)
Files are valid CommonMark Markdown with YAML frontmatter. Any Markdown renderer will display them correctly.
### 3.2 Header (YAML Frontmatter)
Every `.aim` file begins with a small YAML frontmatter block:
```yaml
---
aim: juice.tasks.create_task
kind: intent
parent: juice.tasks
---
```
Required fields:
- `aim` — the intent namespace (lowercase, dot-separated)
- `kind` — the file role: one of `intent | schema | flow | contract | persona | view | event | trigger | mapping | binding`
Optional fields:
- `parent` — the parent intent namespace, present on child intents
- `display` — a human-readable display name (overrides the H1 heading for tooling)
- `tags` — array of free-form tags for discovery
- `nature` — the entity kind of an intent that is *itself a thing* (§7.1, §7.4): one of the facet natures (`record | persona | flow | view | event | trigger`) or `capability` (an operation-grouping intent — also the default reading when the field is absent). **Projection-only**: tools use it for tree badges and grouped views; it never affects resolution, edge legality, or synthesis. When absent, tools MAY derive it from content — a top-level `## Schema` with no acting edges reads as `record`; intent-level `## Role`/`## Access` or acting edges read as `persona`. A declared nature that contradicts content is an informational diagnostic (§12.2), never an error: content is the authority, the badge is the hint.
- `provenance` — `inferred` on a file produced by re-encoding existing code and not yet human-accepted (§17). Absent (the default) or `confirmed` means authored/accepted intent. This is the only field that qualifies a file's authority.
- `owner` — the identity accountable for this file's intent (free-form — a name or an email). Tools use it as the routing target for findings (§12.3). Accountability metadata only; it never affects the file's authority.
- `status` — the authoring lifecycle of the **specification**: `draft | review | approved | deprecated`. This is the state of the intent, never of the implementation — implementation status remains banned from `.aim` files (§3.8) and lives in the drift report. Absent means unspecified.
- `approved_by` — the identity that ratified the current content, optionally paired with `approved_at` (a date). This records the *who* of a §17.2 acceptance — or of ordinary review — in any environment, including after export to bare files.
**Frontmatter is an open set with reserved keys.** Keys this spec does not define are permitted: conforming tools MUST ignore keys they do not recognize (an informational diagnostic at most, §12.2), and project-specific keys MUST NOT redefine the spec-defined keys. Two consistency rules bind the defined keys: `provenance: inferred` combined with `status: approved` or a present `approved_by` is a contradiction (informational diagnostic, §12.2) — accepting an inferred file flips `provenance` and records `approved_by` in the same act — and `provenance` remains the only field that qualifies a file's *authority* (§17.3); `owner`, `status`, and `approved_by` are accountability and lifecycle metadata and never change how tools treat the file's content. The boundary all these fields sit on — what belongs to the system versus what belongs to the work on the system — is defined in §1.3.
The frontmatter carries **no** per-file `version:` or `spec:` field. The project-wide AIM version and spec URL live in **`AGENTS.md` at the project root** (see §3.3) — a single source of truth that eliminates redundancy and drift between files. There is no per-file version anywhere in the language.
### 3.3 `AGENTS.md` — Project Bootstrap
Every AIM project carries an `AGENTS.md` file at its root. This is the universal entry point any agent (Claude, Cursor, Aider, Gemini, etc.) reads first when entering the project — the convention originated with coding agents, predates AIM, and is now the de facto standard across the agent ecosystem.
**Required structure:**
```markdown
---
aim_version: 5.2
aim_root: ./aim/
spec: https://intentmodel.dev/spec.md
---
# Agents
This project uses the **Agentic Intent Model (AIM) v5** for behavioral specification.
[...prose explaining roles, conventions, project specifics...]
```
The frontmatter on `AGENTS.md` carries:
- `aim_version` — the AIM language version this project targets (e.g. `5`)
- `aim_root` — where `.aim` files live (default `./aim/`)
- `spec` — the canonical specification URL for the declared version
The prose body explains AIM to a cold-start agent in natural language: what the roles are, where `.aim` files live, what conventions apply, that `.aim` files are a projection of a node-and-edge graph, and that mapping and binding facets live alongside their intent. Anything an agent needs to know about working in this project — both AIM and non-AIM — belongs here.
**Why this works:**
1. **Cold-start universally solved.** Any agent that follows the `AGENTS.md` convention finds AIM automatically. No AIM-aware tooling required for the first read.
2. **One source of truth for version.** Bumping AIM versions is a one-line edit, not a project-wide search-and-replace. Because no `.aim` file carries a version, there is nothing else to update.
3. **No per-file boilerplate.** `.aim` files carry only what's unique to them (namespace + facet); shared facts live once in `AGENTS.md`.
4. **Tool interop for free.** Cursor, Copilot, Anthropic tooling, and others that already read `AGENTS.md` pick up AIM context without integration work.
### 3.4 Local Spec Cache
Many agents operate without network access (sandboxed environments, CI runners, offline editing, restricted enterprise networks). To support them, AIM tooling installs a local copy of the spec under `/aim/specs/`.
**Layout:**
```
/aim/
specs/
spec.md # the AIM specification (mirrored from spec: URL)
/ # one directory per intent (mapping/binding facets live inside it)
```
**Required installer behavior:**
1. On first project setup (performed by an agent or tooling), fetch the spec from the URL declared in `AGENTS.md` and write it to `/aim/specs/spec.md`.
2. When the project adopts a new AIM version, the installer overwrites the local `spec.md` with the new version.
3. The local spec file is a verbatim mirror of the URL content. Tools must not modify it.
**Agent spec-resolution order:**
1. **`AGENTS.md`** — read the project's frontmatter to determine the `spec` URL (the `aim_version` provides language context but the URL is the source of truth). If `AGENTS.md` declares no `aim_version`/`spec`, refuse to proceed — operating against an unknown specification is unsafe.
2. **Local cache** — read `/aim/specs/spec.md` if present. Always works, even offline.
3. **URL fallback** — fetch the `spec` URL declared in `AGENTS.md`.
4. **Hard error** — if none of these resolve, refuse to proceed.
**Reserved names under `/aim/`:**
The `aim/specs/` directory name is reserved for cached specifications (`.md` files) and must not be used as an intent namespace. Mapping and binding facets are **not** separate top-level directories — they are `kind: mapping` / `kind: binding` files that live inside their intent's own directory (§4.2, §9.3, §10.2).
Any other directory under `/aim/` that contains a `.aim` file is an intent.
### 3.5 Body (Markdown)
The body of the file is Markdown. Structure is conveyed by heading levels:
- **H1** — the intent's display name (exactly one per file)
- **H2** — top-level sections (`## Summary`, `## Requirements`, `## Tests`, `## Schema`, `## Role`, `## Access`, `## Children`, `## Dependencies`) and facet blocks (`## Contract: CreateTask`, `## Flow: AssignTask`, etc.)
- **H3** — facet sub-blocks (`### Schema`, `### Input`, `### Ensures`, `### Steps`, etc.)
- **Bulleted lists** — for requirements, tests, steps, attributes, and any enumeration
- **Fenced code blocks** — for attribute definitions, type expressions, and code samples
### 3.6 Heading Conventions
Facet headings use the form `## : `:
```markdown
## Contract: CreateTask
## Flow: AssignTask
## Persona: TaskOwner
## View: TaskDashboard
## Event: TaskCreated
## Trigger: NightlySweep
```
Realization bindings attach to a node as an inline `### Bindings` property (§10.2). The deprecated `kind: binding` sidecar addresses each node with a `## Bind: :` heading — the payload after `Bind:` is the bound node's in-intent address minus the leading `#`:
```markdown
## Bind: Contract:CreateTask
```
Top-level section headings use the bare form:
```markdown
## Summary
## Requirements
## Tests
## Children
## Dependencies
```
Tooling MUST accept `## Subintents` and `## Subcomponents` — the headings earlier versions wrote — as deprecated aliases for `## Children`.
The facet heading text is the node's address within the file (§2.2). Every facet heading MUST be immediately followed by an explicit `### Summary` sub-block, with the single exception of a `Persona` acting only as a role/access declaration (§7.8). This keeps node boundaries deterministic.
**Facet name grammar.** A facet `` MUST match `[A-Za-z][A-Za-z0-9_]*` (PascalCase recommended) — no spaces, no punctuation. A heading whose name violates the pattern (`## Contract: Create Task`) is a hard error (§12.1). This constraint is what lets a name sit verbatim inside an `aim:` link destination and a `## Bind:` heading as a valid CommonMark link target without percent-encoding (§2.2).
### 3.7 Attribute Syntax
A node's data — its **schema property** — uses a fenced code block with a simple line format:
````markdown
### Schema
```schema
title: string required min(1) max(200)
description: string optional
ownerId: string required ref(User.id)
status: enum(open, completed, archived) required
createdAt: datetime required
```
````
Format per line: `: *`
Modifiers: `required`, `optional`, `min(n)`, `max(n)`, `ref(.)`, `ref(#.)`, `enum(a, b, c)`, `default()`, `list()`.
`list()` declares a repeated value: `` is a scalar (`list(string)`) or a Record name — `items: list(ExpenseItem)` for a log that owns its line items as parts. A Record-typed list derives a `refs` edge to that Record (§8.2), exactly as `ref()` does.
`ref(.)` is a **typed graph edge** (`refs`, §8.2): it links a schema attribute to another schema's attribute and is collected into the graph alongside inline edge tokens.
**Resolving `ref()`.** The `` resolves as an unqualified facet name of type `Record` through the canonical resolution algorithm (§11.1); if it resolves in more than one intent along the resolution path, first-match-wins per §11.1 (a shadow diagnostic fires on the losing sources). To pin a specific provider, use the qualified form `ref(#.)`. It is a hard error (§12.1) if `` does not resolve to a Record, or if `` does not exist as a field on the resolved Record's schema.
### 3.8 Allowed Markdown Features
`.aim` files use CommonMark Markdown, but the spec constrains which features are allowed where. The rule:
- **Structured spec blocks** — facet sub-blocks like `### Requirements`, `### Tests`, `### Steps`, `### Schema`, `### Input`, `### Ensures`, `### Returns`, `### Actions`, `### Access` — use only the patterns the spec defines (bullets, fenced `schema` blocks, and the inline edge token `[verb](aim:…)`). These blocks have parseable semantics; alternative forms create parsing ambiguity.
- **Free-form prose sections** — `## Summary`, descriptive paragraphs between facet blocks, the body of `AGENTS.md` — follow standard CommonMark without restriction.
| Feature | In structured blocks | In prose |
|---|---|---|
| Bulleted lists | ✓ (required form) | ✓ |
| Edge token `[verb](aim:…)` | ✓ (typed cross-reference) | ✓ |
| Tables | ✗ | ✓ |
| Blockquotes | ✗ | ✓ |
| Task lists (`- [ ]`) | ✗ | ✓ |
| Footnotes | ✗ | ✓ |
| Bold, italic, inline links | ✓ (inside list items) | ✓ |
| Inline code (backticks) | ✓ | ✓ |
| Fenced code blocks | ✓ (only `schema` in `### Schema` / payload sub-blocks; arbitrary elsewhere) | ✓ |
| Raw HTML | ✗ | ✗ |
The edge token is a standard CommonMark inline link whose destination uses the `aim:` URI scheme (§8.1). It renders as a clickable link on GitHub and is therefore allowed inside structured blocks, where it carries the cross-reference semantics.
**Task lists deserve specific mention.** Markdown's `- [ ]` syntax is forbidden in `## Requirements`, `## Tests`, and other structured blocks even though it looks like a bullet list. The `.aim` file is *intent*, not *status*. Implementation and verification status live in a drift report under `/aim/work/` produced by the Reviewer — see §1.3 (Authority Model) and the Reviewer's drift-report convention in [`agents/aim-reviewer.md`](agents/aim-reviewer.md). Putting status into intent makes the spec lie when code changes and the checkbox doesn't. The frontmatter `status:` field (§3.2) is unaffected by this rule: it tracks the *specification's* own authoring lifecycle, never the implementation's.
**Raw HTML is banned everywhere** because it breaks parsers and circumvents the Markdown-native discipline.
---
## 4. Project Layout
### 4.1 Decomposition-First Default
AIM decomposes real applications into focused child intents. Each child is a real intent with its own intent file, its own namespace, and its own facets. The parent intent serves as an index plus a home for cross-cutting requirements and shared facets.
Reasons this is the default:
- LLMs reason better over small focused files than large ones
- Multiple agents can work on different children in parallel without merge conflicts
- Diffs are meaningful when each file has a single concern
- Synthesis maps cleanly to small focused code modules
The namespace hierarchy is the `extends` edge of the graph (parent ← child); it is one relation among many, not the model's organizing principle.
### 4.2 Canonical Layout
```
/aim/
juice.tasks/
juice.tasks.aim # parent: index + shared
juice.tasks.record.aim # shared Records (Task, User refs; `kind: record` — `kind: schema` is the deprecated spelling)
juice.tasks.mapping.aim # capability mappings (kind: mapping)
juice.tasks.binding.aim # code bindings (kind: binding)
create_task/
juice.tasks.create_task.aim
juice.tasks.create_task.contract.aim
assign_task/
juice.tasks.assign_task.aim
complete_task/
juice.tasks.complete_task.aim
```
Rules:
- Each intent lives in a directory named after its namespace.
- The intent file filename matches `.aim`.
- Sidecar filenames match `..aim` — facet files (schema, contract, …) and the mapping/binding files alike, all co-located with their intent.
- Child intents live in nested directories under the parent.
- Mapping and binding facets belong to the child they realize: a facet for `juice.tasks.create_task` lives in the `create_task/` directory, not the parent's.
- Generic filenames (`aim.aim`, `schema.aim`, `binding.aim`) are invalid.
### 4.3 When To Collapse Into A Single File
An intent should stay in a single `.aim` file (no children, facets embedded inline) only when **all** of the following hold:
- Total content fits comfortably in a single screen of reading.
- There is one clear behavior, not a set of distinct features.
- No facet needs independent ownership or review.
Otherwise, split. Split is the default; single-file is the exception.
### 4.4 Path Identity
The header `aim:` field is authoritative for identity. The directory and filename must agree with the header — tools treat path/header mismatch as a hard error. This lets paths function as a fast sanity check without competing with the header as the source of truth.
### 4.5 One Version Per Project
A working project is **single-version**: its `/aim/` tree conforms wholly to one AIM language version, because graph derivation and conformance require one consistent model. Bringing in intents authored against an older version means migrating them first (§13) — a project never mixes versions.
---
## 5. The Intent Tree
### 5.1 Definition
A child intent is an intent whose namespace extends its parent's namespace by exactly one segment:
- Parent: `juice.tasks`
- Child: `juice.tasks.create_task`
The child declares the parent in its frontmatter:
```yaml
---
aim: juice.tasks.create_task
kind: intent
parent: juice.tasks
---
```
A child is a real intent: it has its own intent file, its own facets, and is independently addressable. The `parent:` relation is the graph's `extends` edge.
**The constraint is on the name, never the count.** "Exactly one segment" defines the shape of a single parent→child *edge* — it does not limit fan-out or depth. A parent has as many children as its capabilities demand (3–9 per level is the healthy band, §5.5), and a child nests further the moment its own behaviors earn children of their own (§15.4). One segment per edge; any number of edges.
Why one segment: it keeps the namespace an honest tree coordinate. The parent chain and the namespace walk are the same walk (§5.4, §11.1); no level can exist without its own intent file and Summary; and discovery (§5.3) stays mechanical. A grouping that wants to skip a level is either a real intermediate intent or a compound leaf name (`admin_reports`) — never an unnamed level.
### 5.2 Parent As Index
The parent intent's intent file serves two purposes:
1. **Index of children** — either auto-discovered from sibling directories or explicitly listed.
2. **Home for cross-cutting concerns** — shared requirements, shared schemas, shared personas, shared events that apply across all children.
Example parent intent:
````markdown
---
aim: juice.tasks
kind: intent
---
# Tasks
A task management subsystem. Users create, assign, and complete tasks tied to projects.
## Summary
The tasks subsystem owns the full task lifecycle: creation, assignment, state transitions, and archival. All child intents share the `Task` schema and emit events on the `tasks.*` channel.
## Requirements
- Every task belongs to exactly one owner.
- State transitions are auditable.
- Soft-delete is preferred over hard-delete.
## Children
- [create_task](./create_task/juice.tasks.create_task.aim) — create a new task
- [assign_task](./assign_task/juice.tasks.assign_task.aim) — assign a task to a user
- [complete_task](./complete_task/juice.tasks.complete_task.aim) — mark a task completed
## Record: Task
### Summary
The shared task record used by all children.
### Schema
```schema
id: string required
title: string required min(1) max(200)
description: string optional
ownerId: string required ref(User.id)
status: enum(open, in_progress, completed, archived) required
createdAt: datetime required
updatedAt: datetime required
```
````
### 5.3 Child Discovery
By default, children are **auto-discovered**: any sibling directory containing a `.aim` file with a matching `parent:` field is treated as a child of the parent.
The parent may override discovery with an explicit `## Children` block. When the explicit list is present:
- listed children must exist on disk
- discovered children not in the list emit a hard error (ambiguous authority)
- the explicit list is authoritative for the order in which children are presented to agents and tooling
### 5.4 Upward Facet Resolution
A child intent may reference facets defined in the parent without qualification. The complete precedence rules are defined once in §11.1 — children add one detail: the **parent chain** step walks the namespace upward (parent → grandparent → root) until a match is found or the chain ends.
Tools emit an informational diagnostic when a child defines a facet that shadows one already defined in a parent. This is usually a sign that either the shared definition should move up, or the child's name should be more specific.
### 5.5 Tree Shape
**Breadth:** 3–9 children per level is the healthy band. A parent's children should read as a short table of contents for the level below — that is the tree's job as the human projection (§2). One child is a smell (§15.2 — no single-child parents); a dozen or more means an intermediate level wants to exist or siblings want merging.
**Depth:** depth is **earned level by level, never capped**. Every level must itself hold the breadth band and read as a table of contents for the level below; depth follows from scale alone — a focused tool may need one level, a whole-organization model (§18) many. The stopping rule is content, never a count: decomposition stops where an intent holds **one clear behavior** (§4.3) — while an intent's Summary still says "and", it needs children. Level count is never a reason to stop, and never a reason to split. What tools flag is not absolute depth but *accidental* depth: a chain of single-child levels (§15.2), or a level outside the breadth band. A dotted address grown unreadable is usually breadth debt surfacing as depth, not a depth problem itself.
There is no version inheritance: `.aim` files carry no version, so a child is automatically consistent with its parent. (v4 removed the v3.1 "version inheritance" rule entirely.)
---
## 6. Intent Envelope
### 6.1 Minimum Valid Intent File
```markdown
---
aim: demo.todo
kind: intent
---
# Todo
## Summary
A simple personal todo tracker.
## Requirements
- User can add, complete, and delete todos.
```
Hard minimum for validity:
- Valid frontmatter with required fields (`aim`, `kind`).
- Exactly one H1 heading.
- A `## Summary` section with at least one paragraph **or** an H1 followed by a paragraph that serves as the summary.
- A `## Requirements` section with at least one bullet.
Recommended:
- `## Tests` section with observable behavior bullets.
- One or more facets when the intent has stable interfaces.
### 6.2 Extended Intent Example (Child Intent)
````markdown
---
aim: juice.tasks.create_task
kind: intent
parent: juice.tasks
---
# CreateTask
## Summary
Create a new task on behalf of the authenticated user. The task starts in the `open` state and is owned by the creator.
## Requirements
- A task must have a non-empty title (1–200 characters).
- A description is optional.
- The creating user becomes the owner.
- The task is persisted with status `open`.
- A `tasks.created` event is emitted on successful creation.
## Tests
- Creating a task with an empty title fails with a validation error.
- A newly created task is visible in the owner's task list.
- A `tasks.created` event is emitted with the new task's id.
## Contract: CreateTask
### Summary
Create a task on behalf of the current user.
### Input
```schema
title: string required min(1) max(200)
description: string optional max(2000)
```
### Authz
- Caller must be authenticated.
### Ensures
- A new Task record is persisted with status="open" — [mutates](aim:#Record:Task).
- ownerId is set to the current user's id.
- A `tasks.created` event is emitted — [emits](aim:juice.tasks#Event:TaskCreated).
### Returns
- The newly created Task record.
````
Note: `Task` and `User` are not defined in this file — they resolve upward to the parent intent `juice.tasks` (§11.1).
---
## 7. Precision Facets
The six behavioral facets are unchanged in meaning from v3.1. What changes is how their cross-references are written: prose mentions become typed edge tokens (§8), and the inverse blocks `### Trigger` and `### Emitted By` are removed because they are derivable. v4 added the seventh facet — **Trigger** (§7.7) — for non-actor entry points such as schedules and webhooks.
### 7.1 Record And The Schema Property
The old `## Schema:` facet conflated two things this section now separates:
- A **Record** is a passive data-thing — stored, read, and processed, and does not act (`TravelLog`, `ExpenseItem`, `Invoice`). It is a facet, `## Record: `, and — when it earns its own file — an intent. It replaces the `## Schema:` facet.
- A **schema** is a Record's (or any node's) **data property**: its fields, constraints, and immutability. It is not a node — it lives *inside* whatever has the data.
A Record *has* a schema — thing and shape. This is the fix for the old category error: `## Schema:` named the *shape* and pretended to be the *thing*.
A Record facet carries its schema in a `### Schema` sub-block:
````markdown
## Record: ExpenseItem
### Schema
```schema
amount: number required
category: string required
incurredOn: datetime required
```
### Constraints
- An approved item's amount is fixed.
### Immutable
- `id`
````
Any other facet that carries data uses the same `schema` fence under its role sub-block — a Contract's `### Input`, an Event's `### Payload` (§7.2, §7.6). An intent that is *itself* a Record declares its data in a top-level `## Schema` section.
**Deprecated, still accepted (5.1).** The `## Schema: ` *facet* is read as a `## Record: `, and the `aim-attrs` fence is an alias for `schema`. Both SHOULD be migrated but require no forced rewrite (§13).
### 7.2 Contract
Externally observable guarantees and obligations.
````markdown
## Contract: CreateTask
### Summary
Create a task on behalf of the current user.
### Input
```schema
title: string required
description: string optional
```
### Authz
- Caller must be authenticated.
### Expects
- Title is non-empty after trimming.
### Ensures
- A new Task record is persisted with status="open" — [mutates](aim:#Record:Task).
- A `tasks.created` event is emitted — [emits](aim:#Event:TaskCreated).
### Returns
- The newly created Task record.
````
### 7.3 Flow
Operational sequencing, branching, retries, and error handling.
```markdown
## Flow: CreateTask
### Summary
Persists a new task and emits the creation event.
### Steps
1. Validate input against the contract.
2. Persist the task — [mutates](aim:#Record:Task).
3. Emit the creation event — [emits](aim:#Event:TaskCreated).
4. Return the persisted task.
### On Error
- Storage failures: surface as `PersistenceError` and emit no event.
- Event bus failures: persisted task is retained; emission is retried via outbox.
```
The flow's trigger (which contract invokes it) is **not** authored here — it is derived from the `invokes` edge declared at the contract or view that calls the flow (§8.4).
When a guarantee cannot be cheaply and completely verified from its outcome, a Flow's `### Steps` take on a second job: they are the normative **proxy verifier** for that guarantee (§15.10).
### 7.4 Persona
Actor identity, role semantics, and view access.
```markdown
## Persona: TaskOwner
### Role
- Authenticated user who owns one or more tasks.
### Access
- [accesses](aim:#View:TaskDashboard)
- Create and complete tasks — [invokes](aim:#Contract:CreateTask), [invokes](aim:#Contract:CompleteTask)
```
**The promoted form — an entity-intent (5.2).** A Persona with substance of its own — canon attributes, and eventually its own operations — outgrows facet-hood and becomes its **own intent**. The intent *is* the actor; no self-named facet sits inside it. Its acting face moves to **top-level sections**: `## Role` and `## Access` carry exactly what the facet's `### Role`/`### Access` carried, with the actor's edges declared from the intent node itself (`accesses`/`invokes` from an intent, §8.2 — the same intent-level form `subscribes` has always had). Its data is a top-level `## Schema` (§7.1); its operations co-locate as facets under it; `nature: persona` is the badge (§3.2).
````markdown
---
aim: app.employee
kind: intent
nature: persona
parent: app
---
# Employee
## Role
- Submits expense logs; the subject of the monthly reimbursement cycle.
## Access
- [accesses](aim:app.expenses#View:SubmitForm)
## Schema
```schema
id: string required
homeAddress: string required
```
## Contract: UpdateHomeAddress
### Summary
Change the employee's verified home address — [mutates](aim:app.employee).
````
The cue for the promotion is the moment an entity grows an **operation of its own** (`UpdateHomeAddress`): entity-specific behavior belongs under the entity, not scattered on the parent (§16.5). A Record promotes the same way — top-level `## Schema`, `nature: record` — when its data and lifecycle earn a file of their own.
### 7.5 View
Shared interface surfaces and user-visible actions.
```markdown
## View: TaskDashboard
### Summary
The owner's primary task list view.
### Display
- A list of [reads](aim:#Record:Task) records grouped by due date.
- Completed tasks collapsed by default.
### Actions
- Create task — opens the creation form and [exposes](aim:#Contract:CreateTask).
- Complete task — [exposes](aim:#Contract:CompleteTask).
- Archive task — [exposes](aim:#Contract:ArchiveTask).
```
### 7.6 Event
Asynchronous payloads, emissions, and routing.
````markdown
## Event: TaskCreated
### Summary
Emitted when a new task is persisted.
### Payload
```schema
taskId: string required
ownerId: string required
createdAt: datetime required
```
### Routing
- Channel: `tasks.created`
- Durable: true
````
The event's emitters (`### Emitted By`) are **not** authored here — they are derived from the `emits` edges declared at the contracts/flows that emit the event (§8.4).
### 7.7 Trigger
Non-actor entry points: schedules, webhooks, and external origins that initiate behavior without a Persona or View. A Trigger is the source of a `triggers` edge into a Flow or Contract — which gives a cron job or inbound webhook a place in the graph, and gives the flow it starts a legitimate inbound edge (so it is not flagged as an orphan).
```markdown
## Trigger: NightlySweep
### Summary
Runs the stale-ticket sweep every night.
### Kind
- schedule
### Schedule
- cron: `0 2 * * *`
### Fires
- [triggers](aim:#Flow:EscalateStale)
```
A webhook or external origin sets `### Kind` to `webhook` / `external` and describes the source in prose instead of `### Schedule`. **Externally-originated events** need no special construct: model the origin as a Trigger that `triggers` an ingest Flow, and let that Flow `emits` the internal Event — the event then has a real emitter. Note: the `## Trigger:` *facet* defined here (an entry-point node) is distinct from v3.1's removed `### Trigger` *inverse block* (§8.3).
**Deadlines and calendar rhythms.** Time commitments — SLAs, escalation windows, expiries, recurring obligations — are all this one construct: a Trigger with `### Kind` `deadline` (event-anchored) or `schedule` (calendar-recurring). Its `### Schedule` lines take one of two forms, each with an optional informal *unless-clause* naming what disarms the timer:
- **Event-anchored:** ` after [Event:]` with units `minutes | hours | days | business-days | weeks` — e.g. `5 business-days after [Event:ContractSigned] — unless [Event:KickoffScheduled]`.
- **Calendar-recurring:** `monthly on the th` / `weekly on ` / `yearly on `, alongside the machine-precise `cron:` form — e.g. `monthly on the 8th — unless [Event:ReportReceived] for the period`.
**The unless-clause is informal prose and therefore not conformance-checkable** — a tool arms and disarms timers from the realization; nothing evaluates the clause text. When disarm verification matters, the disarming condition SHOULD be an **Event** emitted by the fulfilling operation, and the clause SHOULD name it (`— unless [Event:KickoffScheduled]`): the disarm condition then references a node the graph can check — the Event exists and has an emitter (§12.2) — while a clause naming anything else remains documentation.
Which operating calendar `business-days` names is realization, bound to whatever arms the timer — the duration and its anchor are the intent. Recurring patterns compose from this construct alone: an SLA is a deadline firing an escalation, an escalation ladder is chained deadline Triggers, an expiry is a deadline firing a revocation.
### 7.8 Summary Rule
Every facet block must carry an explicit `### Summary` sub-block immediately following the facet heading. `Persona` may omit the summary when it acts only as a role/access declaration.
---
## 8. Graph Edges
This section defines the typed edges that connect nodes. An edge is declared once, inline, at the node that *acts* — and the graph (§2.4) is derived by collecting edges across the project.
### 8.1 The Edge Token
A typed cross-reference is a standard CommonMark inline link whose link text is an edge **verb** and whose destination is an `aim:` URI carrying a node address:
```
[verb](aim:)
```
- It renders on GitHub as a clickable link reading the verb (e.g. "invokes").
- It is LLM- and regex-parsable: `\[(\w+)\]\(aim:([^)]+)\)`.
- It is valid CommonMark with no raw HTML, honoring §3.8.
An **authoring shorthand** is permitted for same-intent references: the verb followed by a backticked address, `invokes` `` `#Contract:CreateTask` ``. Tools normalize the shorthand to the canonical link form when deriving the graph. The shorthand is the smallest possible delta from what v3.1 authors already typed (`` `Contract: CreateTask` ``): prepend the verb, switch to the address form.
**Normalization is in-memory only.** Normalizing the shorthand to the canonical link form happens *during graph derivation*; a tool MUST NOT rewrite authored source as a side effect of deriving the graph (consistent with the §1.3 authority model — the author owns the file). Both the shorthand and the canonical link form are valid on disk indefinitely, and every conforming parser MUST accept both. A formatter or linter MAY offer canonicalization to the link form as an explicit, user-invoked operation, never as an automatic rewrite.
**Prose convention.** The recommended style for an edge token inside a list item is *human label — [verb](aim:…)*: a plain-language label, an em-dash, then the token. The prose SHOULD NOT restate the verb the link text already carries (write `Create task — [exposes](aim:#Contract:CreateTask)`, not `Create task exposes [exposes](aim:…)`). A bare token with no label (`- [accesses](aim:#View:TaskDashboard)`) is equally valid where no label adds meaning.
The **`from` node** of an edge is the nearest enclosing facet node of the line the token sits on (the Contract / Flow / View / Persona whose block contains it). The **`to` node** is the resolved address.
For a cross-intent reference, the address is fully qualified:
```
- [invokes](aim:company.storage#Contract:PersistTask)
```
This subsumes the *use site* of a v3.1 `## Dependencies → Imports` alias: the import still declares the alias (§9), but the call site now points at a real node address, so a dangling import becomes checkable.
### 8.2 Closed Verb Taxonomy
There are eleven **declared** verbs and three **derived** inverses. Each declared verb has a fixed from→to node-type schema. A verb used between disallowed node-types is a **hard error**.
| Verb | from | to | Meaning | Kind |
|---|---|---|---|---|
| `exposes` | view | contract | a View action surfaces a Contract to users | declared |
| `invokes` | flow, view, contract, persona, intent | contract, flow | a call into another behavioral unit — from a Persona (or a persona-natured intent, §7.4): the actor performs the operation directly | declared |
| `reads` | contract, flow, view | record | reads a persisted Record | declared |
| `mutates` | contract, flow | record | creates / updates / deletes a Record | declared |
| `emits` | flow, contract | event | produces an event | declared |
| `subscribes` | flow, contract, intent | event | consumes an event | declared |
| `accesses` | persona, intent | view, intent | a persona — as a facet or a promoted persona-intent (§7.4) — may reach a view, or a whole screen/route intent | declared |
| `navigates` | view | view | UI navigation between surfaces | declared |
| `triggers` | trigger | contract, flow | a schedule, webhook, or external origin initiates a behavioral unit | declared |
| `refs` | record field | record field | data-level foreign reference (the `ref()` modifier) | declared |
| `satisfies` | contract, flow, view, trigger | requirement item | a behavioral unit (or a deadline Trigger enforcing a time policy) realizes a `## Requirements` item | declared |
| `triggered-by` | flow, contract | contract / view / trigger / persona / intent | inverse of `invokes`/`exposes`/`triggers` — a persona (or promoted persona-intent) appears here when it invokes the unit directly (§7.4) | derived |
| `emitted-by` | event | flow / contract | inverse of `emits` | derived |
| `satisfied-by` | requirement item | contract / flow / view / trigger | inverse of `satisfies` | derived |
`requires` is **not** a graph verb — it stays as `## Dependencies → Requires` (a capability alias resolved by a mapping, §9). `extends` is **not** a graph verb — it is the `parent:` frontmatter relation (§5.1). **Render/layout composition** — a screen displaying another view inline (a dashboard laying out widget-panels) — is **not** a graph verb either: a UI piece has fluid granularity (`### Display` prose in its host view when simple, a promoted child intent owning its own facets once it earns them, §15.9), and the inline arrangement is realization expressed in code and bindings (§1.3), not an intent edge.
An `accesses` edge may target a **View** (access to one surface) **or** a **intent** (route/screen-level access — the persona may reach that whole feature). Use the intent form for role-gated screens that aggregate several views; `[accesses](aim:app.profile)` is valid and means "this persona may reach the profile screen."
**`exposes` vs `invokes` from a View.** Both are legal view→contract edges, and the choice carries meaning. Use **`exposes`** when the contract is reached through a *user-initiated action* on the view — a button, a form submit, a gesture. Use **`invokes`** when the view calls the contract *programmatically*, with no user initiation — an on-load data fetch, a poll, a prefetch. Either inbound edge counts as the contract being "reached" for the orphan check (§12.2); the distinction records whether the behavior is user-driven, which matters for reading intent and for realization.
**`subscribes` from a `intent`.** Almost every `subscribes` edge is declared at the consuming Flow or Contract. A `intent`-level `subscribes` is a deliberate early-stage exception: it declares that an intent consumes an event *without yet naming the internal handler* — `[subscribes](aim:#Event:TicketResolved)` written at the intent root. It is valid at Level 1/2; at Level 3 it SHOULD be refined to a flow- or contract-level edge once the handler exists (an informational diagnostic flags an intent-level `subscribes` that survives into a Level-3 intent, §12.2).
**`accesses`/`invokes` from an `intent` are different in kind:** they are not an early-stage placeholder but the **permanent form of a promoted Persona** (§7.4) — the entity-intent acting directly. They are declared in the intent's top-level `## Access` section and never need refinement.
**`satisfies` and its target.** `satisfies` is the one edge that reaches a sub-block target: its `to` is a `## Requirements` list item, not a facet node. It is declared at the acting unit — the Contract, Flow, or View that realizes the requirement, or the deadline Trigger that enforces a time policy (§7.7). The token URI form is `aim:[]#Requirements[]`: the reserved section address carries **no** `FacetType:` colon, which is exactly what distinguishes it from the facet form `#:` and keeps it a valid CommonMark link destination (§2.2, §3.6). The `` takes two forms, discriminated by grammar:
- **Positional** — a 1-based integer: `[satisfies](aim:#Requirements[2])`. Valid everywhere, but fragile: reordering bullets re-points it (§2.2, §16.3 invariant 1). The casual form for small models.
- **Labeled** — a name matching the facet-name grammar (§3.6): `[satisfies](aim:#Requirements[NET14])`. A requirement declares its label by opening the bullet with a bolded name and an em-dash:
```markdown
## Requirements
- **NET14** — The invoice is issued within one business day of signature, net-14.
- **COUNTERSIGN** — No work is scheduled before the contract is countersigned.
- Requesters see only their own tickets. (unlabeled — positional only)
```
A labeled reference survives any insertion or reordering; the label is part of the requirement's identity, so *renaming a label* is the `rename`-class transform instead. Labels MUST be unique within their intent's `## Requirements` and SHOULD be used once `satisfies` edges exist; labeled and unlabeled bullets may coexist, and an unlabeled bullet remains addressable by position.
Cross-intent targets are fully qualified either way: `aim:app.tasks#Requirements[NET14]`. This makes "which behavior satisfies this requirement?" a graph query, and the derived inverse `satisfied-by` (§8.4) makes the reverse computable.
`satisfies` targets **only** a `## Requirements` list item. It never targets a `## Capability:` surface (§9.2) — that is a facet node resolved by a mapping, not a requirement statement; `[satisfies](aim:#Capability:AssigneeUsers)` is a hard error (type mismatch, §12.1). *(v4 named this surface `## Requirement:`; v5 renamed it to end the collision with `## Requirements` items — §13.3.)*
### 8.3 Declared vs Derived
- **Declared once, at the acting end.** The node that performs the verb owns the edge: a View declares `exposes`/`navigates`/`reads`, a Flow declares `invokes`/`emits`/`reads`/`mutates`, a Persona declares `accesses`/`invokes`, a Contract declares `emits`/`mutates`/`invokes`, a Trigger declares `triggers`, a Record's schema field declares `refs`.
- **Inverse views are derived, never authored.** The v3.1 blocks `### Trigger` ("Invoked by Contract: X") on a Flow and `### Emitted By` on an Event are inverse projections of `invokes`/`emits` edges. v4 removed them from authored source. A tool may render them as read-only views.
This is the structural fix for v3.1's "three inconsistent expressions" problem: there is now exactly one authoritative direction per relation, so the forward and backward statements can never fall out of sync.
### 8.4 Inverse Derivation
For every declared `invokes`/`exposes` edge `A → B`, the graph contains a derived `triggered-by` edge `B → A`. For every declared `emits` edge `A → E`, the graph contains a derived `emitted-by` edge `E → A`. For every declared `satisfies` edge `A → R` (R a `## Requirements` item), the graph contains a derived `satisfied-by` edge `R → A`. Derived edges are computed during graph derivation (§2.4 step 4) and are available to tooling and reviewers exactly like declared edges, but they never appear in source.
If an author writes a `### Trigger` or `### Emitted By` block anyway (e.g. migrated content not yet cleaned up), tools reconcile it against the derived set and emit an informational "redundant inverse, possibly stale" diagnostic on mismatch.
### 8.5 Before / After
The `View: TodoDashboard` facet from the canonical example, v3.1 prose vs the graph form (v4 onward):
**v3.1:**
```markdown
### Actions
- Submitting the "New Task" form → invokes `Contract: CreateTodo`.
- Tapping the checkbox on a PENDING task → invokes `Contract: CompleteTodo`.
```
**Graph form:**
```markdown
### Actions
- Submitting the "New Task" form — [exposes](aim:#Contract:CreateTodo)
- Tapping the checkbox on a PENDING task — [exposes](aim:#Contract:CompleteTodo)
```
Both forms render on GitHub. The graph form additionally yields two first-class edges `View:TodoDashboard → exposes → Contract:CreateTodo|CompleteTodo`, so renaming a contract dangles the edge (hard error), an orphan check confirms every contract is exposed, and the impact set of either contract now formally includes the view. The free prose ("Submitting the New Task form") survives as the human label; only the edge is now machine-recognizable.
### 8.6 Extending The Verb Set
The taxonomy is closed on purpose: every verb an author must learn taxes every author, and a verb that means "relates to" is prose with extra syntax. Pressure to add verbs is permanent; this policy is the gate. A verb is admitted into a future version only when **all** of the following hold:
1. **It is a relational commitment** between existing node types — not an attribute (belongs in `schema`), not sequencing (belongs in a Flow's `### Steps`), not composition or layout (realization, §15.9), not accountability metadata (prose in `### Role`/`### Authz`).
2. **It is not expressible today** — no existing verb plus prose carries the meaning without losing checkability. (`satisfies` passed this test: requirement linkage was uncheckable prose; `performs` fails it: it is `invokes` from a persona.)
3. **It pays rent** — at least one question becomes computable, or one diagnostic becomes possible, *only* with the new verb. A verb that no validator, query, or reviewer check ever consumes is decoration.
4. **It has demonstrated need** — real models worked around its absence (in prose, in convention) before it is canonized. Verbs are admitted from evidence, never from anticipation.
5. **It is fully specified on arrival** — a fixed `(verb, from, to)` schema, its derived inverse (or the explicit statement that none is derived), and its diagnostics.
Two standing rules bound the process: **verbs are forever** — removing or renaming one is a breaking change to every model in existence, so the default answer is no; and **one verb, one meaning** — no aliases, no domain-specific synonyms (a domain may *read* a verb in its own vocabulary, never spell it differently).
---
## 9. Dependencies, Requirements, And Mappings
### 9.1 Dependencies Block
External providers and required capabilities are declared in a `## Dependencies` block:
```markdown
## Dependencies
### Imports
- `company.storage.Contract` as Storage
- `company.events.EventBus` as EventBus
### Requires
- Identity as AssigneeUsers
```
- **Imports** reference concrete provider surfaces from other intents. An import declares an alias; the *use site* is a cross-intent edge token (§8.1) whose address names the real node.
- **Requires** declares required capabilities by alias (the `requires` relation). The alias is resolved via a mapping file.
### 9.2 Capability Surfaces
Required capabilities may be documented inline with a `## Capability: ` block:
```markdown
## Capability: AssigneeUsers
### Summary
Capability required to resolve user identities.
### Operations
- `ResolveUser(id) -> UserRecord`
```
### 9.3 Mapping Files
Mappings bind required aliases to concrete providers. A mapping is a `kind: mapping` file that co-locates with its intent, exactly like any other facet (§4.2).
```markdown
---
aim: juice.tasks
kind: mapping
---
# Tasks Mappings
## Map: AssigneeUsers
### Target
- `company.identity`
### Operation Map
- `AssigneeUsers.ResolveUser` → `company.identity.ResolveUser`
```
Unresolved `Requires` aliases are hard errors at validation time.
**Mappings vs bindings.** A mapping is an **intent→intent** capability binding: it resolves a required-capability alias to a concrete provider *intent*. A binding (§10) is an **intent→realization** binding: it links a node to the code/system that implements it. They are distinct — a mapping is a `kind: mapping` facet; a binding is an inline `### Bindings` property (§10.2) — and must not be confused with each other.
---
## 10. Binding Layer
A binding records *where* an intent node is realized — in code, in a system configuration, in an automation, in an agent — so the Reviewer can diff the declared intent graph against the realized graph (§12). Bindings are optional: an intent with no bindings is a valid Level 1/2 intent (§11.2). Bindings raise fidelity, exactly as facets do.
### 10.1 Drift As Graph-Diff
Two graphs:
- **Declared graph (D)** — nodes are AIM intents/facets; edges are the typed relations of §8. Authored by the Architect; behavioral authority.
- **Realized graph (R)** — nodes are realization sites (functions, routes, tables, SaaS workflows, agent skills); edges are relations recovered from the realization — code, system configuration, or execution logs (this handler writes that table; this automation fires on that event; these log entries show the step performed).
A **binding** connects a node in D to a node in R. Drift detection projects D and R into a common space through the bindings and diffs them.
**Building R is bounded, not global.** A tool does **not** statically analyze the whole codebase to reconstruct R. Bindings localize the work: for each *declared* edge, the Reviewer opens the *bound site* and checks that one claim — "does `src/todos/create.ts#createTodo` actually mutate `Ticket` and emit `TicketCreated`?" That is read-the-bound-file, not map-the-system, and it is **polyglot by default** (an agent reads any language, where a static analyzer needs one parser per language; dynamic code that defeats static analysis can still be reasoned about and flagged). Because R is *inferred* this way, every graph-diff finding carries a **confidence** (§12.3). Tooling that *does* have static analysis MAY supply a precomputed **realized-graph manifest** for deterministic diffing — this spec does not define that manifest's format; it is an ecosystem concern.
**What graph-diff verifies — and what it cannot.** Graph-diff verifies *structure*: that each declared edge has a realized counterpart at its bound site. The *semantic* content of a guarantee — what an `### Ensures` bullet claims — is verified by reading the bound code, and the finding carries a confidence (§12.3). A third class of guarantee is not cheaply and completely verifiable from output or code at all — judgment-shaped claims ("the summary is accurate," "the review was performed diligently"). For those, the declared *procedure* stands in as the verifier: see §15.10 (proxy verification).
### 10.2 Binding Notation And Location
A binding target is a portable **locator URI**, written as inline code. The scheme set is **open**: the generic form is `:`, this specification registers the code family, and other domains register theirs (a process model binds to `system:`, `workflow:`, `form:`, `agent:`, `queue:`, `calendar:` locators). Unknown schemes are valid; a tool that cannot check a scheme reports the binding as unverifiable, never as an error. The code family:
```
src/todos/create.ts#createTodo # file # symbol (functions, classes, methods)
src/todos/create.ts # file only (a whole module realizes a node)
src/todos/create.ts#L40-L72 # file # line span (last resort; lines drift fastest)
route:POST /api/todos # HTTP endpoint
topic:todos.created # message / event channel
table:todo_items # database table / model
```
A binding line is a normal Markdown bullet:
```markdown
- binds: `src/todos/create.ts#createTodo` — as: handler
```
`as` (`handler | model | component | route | topic | test`) is optional and advisory. One node may declare multiple bindings (a Contract realized by both a route and a handler). Everything after the backticked locator is prose a tool may use but the parser may ignore.
Bindings attach to the node they realize as an inline **`### Bindings` property** — a sub-block on the node itself (an intent, or a facet such as a `## Record:` or `## Contract:`). The `binds:` bullets list that node's locators, and an optional `provenance:` line carries the binding's trust:
````markdown
## Record: ExpenseItem
### Schema
```schema
amount: number required
```
### Bindings
- binds: `table:expense_items` — as: table
- binds: `src/expenses/model.ts#ExpenseItem` — as: model
- provenance: inferred
````
**Per-node provenance.** A binding is *read off reality* while the intent around it is *authored*, so an inline binding carries its own `provenance` — `inferred` until a human confirms it (§17.2). A node's authored meaning and its guessed realization are trusted separately: the volatile, lower-trust locator sits beside the behavior without contaminating it. This is what makes the inline form safe — it is the isolation the old separate-file rule was really buying. What the binding exists *for* is the reconciliation join (§10.1): *change → follow the binding → adjust*, which needs the binding **addressable on the node**.
**Deprecated, still accepted (5.1): the `kind: binding` sidecar file.** Earlier versions kept every binding in a dedicated `kind: binding` file co-located with its intent, each node addressed by a `## Bind: :` heading — the node's in-intent address minus the leading `#`:
```markdown
---
aim: nemicko.demo.todo
kind: binding
---
# TaskManager Bindings
## Bind: Contract:CreateTodo
- binds: `src/todos/create.ts#createTodo` — as: handler
- binds: `route:POST /api/todos` — as: route
## Bind: Record:TodoItem
- binds: `src/models/todo.ts#TodoItem` — as: model
- binds: `table:todo_items` — as: table
## Bind: Event:TodoCreated
- binds: `topic:todos.created` — as: topic
```
A conforming tool MUST still read that sidecar form, but the inline `### Bindings` property is now canonical. The file-per-realization rule existed to keep volatile locators out of behavioral diffs; per-node `provenance` achieves that same isolation on the node itself, so a separate file is no longer required — only its provenance stamp was ever load-bearing (§1.3).
### 10.3 Optional-Capability Invariant
- An intent with no binding facet is fully valid (Level 1/2). Binding coverage is reported as informational, never a hard error.
- Bindings become load-bearing only at Level 3 graph-diff — which the author opted into by writing the bindings. You are never punished for a binding you did not write.
---
## 11. Resolution And Synthesis
### 11.1 Canonical Resolution Algorithm
This algorithm is **authoritative**. All other sections that describe resolution (notably §5.4 for child intents and §9.3 for mappings) defer to this order. It resolves both unqualified facet names and full node addresses (§2.2).
For any reference within an intent:
1. **Intent part.** If the address carries an intent prefix, resolve to that exact namespace (which must exist). If absent, the intent is the current one.
2. **Facet name**, resolved within the chosen intent in this precedence order:
1. **Embedded** — a facet block in the same intent file.
2. **Sibling facet file** — `..aim` next to the intent file.
3. **Explicit Imports** — entries under `## Dependencies → Imports` in the current file. Explicit author intent beats implicit parent inheritance.
4. **Parent chain** — facets defined in the parent intent, then the grandparent, and so on up the namespace until a match is found or the chain ends.
5. **Required alias via mapping** — names declared under `## Dependencies → Requires`, resolved through a mapping file (§9.3).
6. **Absent** — the name does not resolve. If it was required by another facet or edge, this is a hard error.
3. **Type agreement.** If the reference is an address with a `FacetType` (e.g. `#Contract:X`), the resolved node's type must match. A `#Contract:X` that resolves to a `## Record: X` is a hard error.
4. **Sub-block part.** If the address carries `→ ### Sub [n]`, resolve within the facet node by heading text and 1-based list index. The reserved requirement-item form `#Requirements[]` (§8.2) resolves against the resolved intent's top-level `## Requirements` section: a numeric `` by 1-based list index, a named `` by matching a bullet's declared **label** (`- **