What the graph models

A traditional architecture model says what contains what. An Architecture Graph says what the parts mean to each other: who owns a responsibility, which part is authoritative for a shared meaning, which contracts hold at a boundary, and what must not be silently changed.

The examples on this page are taken from the task-manager example and use the real authored format, so you can copy them into a graph that ag validate accepts.

Meaning, not structure

Folder layouts, package diagrams and call graphs already describe structure, and a coding agent can read them from the code. What the code does not say is intent: that the task service is the only part allowed to change a task, that reminders depend on an event announced only after a change is saved, or that how repeating tasks are stored is still undecided. Those are the facts that keep a change from quietly breaking something elsewhere, and they are what the graph records.

The node kinds

Each node has a kind, a name, a description, a status and references to the files that support it. The kinds fall into six roles.

Who takes part
actorPeople, teams or outside systems that use or act on the software.
Where behavior lives
componentsurfaceadaptertransformParts of the software that run: services and modules, the surfaces people use, adapters to other systems, and steps that turn one thing into another.
What the system does
capabilityworkflowResponsibilities and the sequences of work that deliver them.
What crosses boundaries
contractartifactdata_objectObligations between parts, such as an event or API response, and the files and data shapes they exchange.
The rules
boundarypolicyconstraintWhat must stay true: who may change what, and the limits every change has to respect.
The record of design thinking
decisionevidence_surfaceChoices made and questions still open, and where evidence about the design is recorded. An evidence surface names a concept; it does not run or certify anything.

Component and capability

A component is structure: where behavior lives. A capability is a responsibility: what the system does. Keeping them apart lets the graph say which component owns a responsibility, and lets that answer change when code moves without rewriting what the system does.

nodes:
  - id: component:task-service
    kind: component
    name: Task service
    description: The only writer of task state. Validates and saves changes, then announces each saved
      change as a task-changed event.
    status: proposed
    references:
      - kind: design
        path: requirements.md
    module: module:tasks
  - id: capability:manage-tasks
    kind: capability
    name: Manage tasks
    description: Create, read, update, complete, reopen and delete tasks and lists.
    status: proposed
    references:
      - kind: design
        path: requirements.md
    module: module:tasks
relationships:
  - id: relationship:task-service-owns
    kind: owns
    from: component:task-service
    to: capability:manage-tasks
    description: The task service owns task management.
    status: proposed
    references:
      - kind: design
        path: requirements.md

Relationships that carry meaning

Every relationship has a kind and a description that says why the connection matters. These carry the most meaning:

owns
An actor, component or capability is responsible for an idea. Each idea has at most one declared owner, and ownership has no cycles.
authority_for
Decides what a capability, contract or artifact means. Others preserve that meaning instead of keeping their own copy. At most one authority per idea.
provides
A component, surface or transform supplies a capability to the rest of the system.
consumes
An idea relies on another, typically a contract. The description says what it does with it.
preserves
An idea keeps a contract or artifact intact: the guarantee a change must not break.
constrained_by
An idea must respect a boundary, contract or policy.

Ownership and authority are checked for conflicts because they answer the question a change most needs answered: which part decides. Two declared owners, or two authorities for the same contract, means nobody can tell which one a change should follow. Competing proposals can still be recorded as unresolved alternatives until one is chosen.

relationships:
  - id: relationship:task-changed-authority
    kind: authority_for
    from: component:task-service
    to: contract:task-changed
    description: Defines the event announced after each saved change.
    status: proposed
    references:
      - kind: design
        path: requirements.md
  - id: relationship:task-changed-after-commit
    kind: preserves
    from: component:task-service
    to: contract:task-changed
    description: Publishes only after saving, with a unique event ID per change.
    status: proposed
    references:
      - kind: design
        path: requirements.md
  - id: relationship:scheduler-task-changes
    kind: consumes
    from: component:reminder-scheduler
    to: contract:task-changed
    description: Schedules, moves or cancels reminders when due times change or tasks are completed.
    status: proposed
    references:
      - kind: design
        path: requirements.md
  - id: relationship:scheduler-task-authority
    kind: constrained_by
    from: component:reminder-scheduler
    to: boundary:task-state-authority
    description: Never writes task state directly, even when snoozing.
    status: proposed
    references:
      - kind: design
        path: requirements.md

Other kinds such as depends_on, produces, governed_by and affects are described in the reference.

Status as honesty

Every node and relationship says how sure it is:

An open question stays visible instead of being guessed. When an agent reaches an unresolved decision, the right move is to leave it alone or ask, not to settle it as a side effect of another change.

nodes:
  - id: decision:events-after-commit
    kind: decision
    name: Announce changes only after saving
    description: "Chosen: task-changed events are published after the change is durably saved, so no
      consumer reacts to a change that was rolled back."
    status: proposed
    references:
      - kind: design
        path: requirements.md
    module: module:tasks
  - id: decision:recurrence-model
    kind: decision
    name: How are repeating tasks stored?
    description: "Open: one record whose due time moves forward on completion, or one record per
      occurrence. This changes the task shape, history and how reminders are scheduled."
    status: unresolved
    references:
      - kind: design
        path: requirements.md
    module: module:tasks

Modules and contracts

Modules are logical boundaries inside one graph. Each lists the nodes it exports to other modules and the nodes it imports from them; a relationship that crosses modules must match those lists, so every dependency between modules is visible and reviewable. Modules do not have to match packages or folders.

Contract sidecars show the data crossing a boundary: a contract can reference a JSON Schema and example payloads, and validation checks that the examples match the schema. That shows the files agree with each other, not that the code sends that data. Sidecars arrive in the next release.

nodes:
  - id: contract:task-changed
    kind: contract
    name: Task changed
    description: Announced after a change is saved, never before. Carries the task after the change. May
      be delivered more than once, so consumers ignore events they have already seen.
    status: proposed
    references:
      - kind: design
        path: requirements.md
      - kind: schema
        path: contracts/task-changed.schema.json
      - kind: example
        path: contracts/examples/task-changed.json
    module: module:tasks
modules:
  - id: module:notifications
    name: Notifications
    description: Delivers push and email notifications.
    exports: []
    imports:
      - contract:reminder-due
      - contract:task-changed

Why this helps agents

A coding agent starts each session without the history behind the code. Without a blueprint, it re-derives intent from whatever files it happens to read, and an assumption that looks reasonable locally can break a guarantee another part relies on. The graph gives it a compact, authoritative starting point: ag context returns the owners, consumers, rules and open questions around the code it is about to change, within a fixed size limit. After the change, the agent updates the graph, so the next session starts from the current design instead of rediscovering it.

Get started