# Spec Graph — Complete Documentation
> A declarative specification model for predictable agent-driven system manifestation.
> Generated: 2026-03-04
---
# Spec Graph
The **Spec Graph** is a formal specification framework for predictable, automated, agent-driven software development. It extends behavior-only specifications into a multi-dimensional graph that captures everything required to predictably manifest a system: behavior, architecture, design, technology choices, domain concepts, and constraints.
## Authoring Model: AI-Agent First
Spec Graph can be edited manually, and that is useful for learning and inspection.
However, the intended operating model is:
- AI agents write and refactor spec graph nodes
- AI agents consume spec graphs to manifest systems
- humans supervise, review, and set direction
In practice, this means using agent tooling (typically via MCP) as the primary interface to the graph, while keeping the JSON structure transparent and auditable.
## The Core Idea
A Spec Graph is a directed, typed graph where:
- **Nodes** are atomic specification entries — each one captures a single decision, behavior, concept, or constraint
- **Edges** express typed relationships between nodes — dependency, constraint, implementation, containment
- **Grouping nodes** (`feature`, `layer`) are namespaces for vertical slices and shared horizontal capabilities
```mermaid
graph TD
F[AUTH Feature] --> B1[AUTH-01: Login Form]
F --> B2[AUTH-02: Email Validation]
F --> D1[DEC-AUTH-01: Auth Provider Interface]
F --> DOM1[DOM-USER-01: User Account]
F --> P1[POL-PERF-01: Page Load Budget]
B1 -->|implements| DOM1
B1 -->|depends_on| D1
D1 -->|constrains| B1
P1 -->|constrains| B1
```
## The Defining Property
> **The Spec Graph is the minimal structure that, when processed by a capable implementing agent, will always produce logically equivalent manifestations of the designer's intended system.**
Two agents given the same spec graph should produce systems that are indistinguishable across every dimension the designer specified — same behaviors, same architecture, same visual presentation, same domain semantics.
## What This Framework Produces
The Spec Graph framework is a **design specification**, not running code. It produces:
1. **Formal theory** — completeness, minimality, predictable manifestation
2. **Data model** — node types, edge types, and their schemas
3. **JSON Schemas** — for validating spec graph files
4. **Manifestation process** — how agents traverse and implement the graph
5. **Worked examples** — realistic spec graphs demonstrating the framework
These artifacts are sufficient for an engineering team to build a Spec Graph-based agentic development platform without further design decisions about the specification format.
## Quick Start
If you want to jump straight in:
1. Read [Motivation](/docs/introduction/motivation) to understand the problem
2. Use the [MCP Server guide](/docs/guides/mcp-server) for the recommended agent-driven workflow
3. Explore the [Graph Structure](/docs/graph/structure) to understand the data model
4. Check [Getting Started](/docs/guides/getting-started) to learn the manual file mechanics
## Background
Behavior-only specs are a strong starting point, but by themselves they usually leave key decisions unspecified (architecture, domain boundaries, non-functional constraints, and technology contracts). Spec Graph closes that gap by modeling those dimensions directly in the graph so manifestations are repeatable across implementing agents.
---
# Motivation
## The Problem with Behavior-Only Specs
Declarative behavioral specifications — where each entry describes WHAT the system does from the user's perspective — are powerful. They are atomic, testable, and reviewable. But they leave critical information stranded in implicit side-channels:
| What's Missing | Where It Lives Today | Why It Matters |
|---|---|---|
| Architectural patterns | Agent instructions, tech stack profiles | Two agents given the same behavioral spec may produce architecturally incompatible implementations |
| Design system | Scattered docs, component libraries, agent prompts | Visual consistency requires shared design knowledge that behaviors don't capture |
| Technology choices | Tech stack profiles, CLAUDE.md, package.json | "Build a login form" manifests very differently in Next.js vs. Phoenix LiveView |
| Domain model | Implicit in code, sometimes in docs | Business concepts and their relationships constrain valid implementations |
| Non-functional requirements | Constraint nodes in agent instructions | Performance and security budgets shape every implementation decision |
## The Consequence
When you re-manifest a system from its behavioral spec alone, you get a system that **does the right things** but may do them in incompatible, inconsistent, or suboptimal ways.
Consider two agents implementing the same behavioral spec for user authentication:
- **Agent A** wraps Clerk behind an abstract `AuthProvider` interface, uses optimistic session caching, and enforces route protection via middleware
- **Agent B** calls Clerk directly throughout the codebase, checks sessions synchronously on every render, and scatters auth guards across individual components
Both produce correct behavior. But the systems are architecturally different, inconsistently structured, and impossible to maintain as a single codebase. The behavioral spec was _necessary_ but not _sufficient_ for predictable manifestation.
## The Shadow Spec
In practice, teams compensate by creating a **shadow spec** — a mishmash of documents, agent instructions, tech stack profiles, PR review comments, and senior engineer guidance that fills the gaps the behavioral spec leaves. This shadow spec:
- Is **scattered** across multiple locations and formats
- Is **mutable** and often unversioned
- Is **implicit** — different team members carry different pieces
- **Drifts** from the actual system over time
- Cannot be **validated** or **verified** programmatically
The shadow spec is the gap between what the behavioral spec says and what you actually need to know to build the system right.
## The Goal
The Spec Graph eliminates the shadow spec by making every load-bearing decision explicit, reviewable, and verifiable:
> **Define a specification framework where the spec determines all decisions the designer cares about, across every dimension — not just behavior.**
This is the Spec Graph: a multi-dimensional specification that captures behavior alongside the architecture, design, technology, domain, and constraints that together determine the intended system.
---
# Design Principles
The Spec Graph framework is guided by six principles. These are not aspirational — they are load-bearing constraints that shape every design decision in the framework.
## 1. Atomicity Everywhere
A strong atomicity rule is essential in graph authoring. In Spec Graph:
> **Every node expresses one decision, one contract, or one constraint.**
If a node contains "and" across multiple decisions, split it. This applies to all node types, not just behaviors. A decision node captures one architectural decision. A domain node defines one business concept. A policy node specifies one measurable requirement.
Atomicity creates **review pressure** (each node is small enough to reason about), **composability** (nodes combine without hidden coupling), and **testability** (each node has a clear verification criterion).
## 2. Every Normative Node Must Be Verifiable
If it cannot be verified, it is not a complete spec.
Every node that makes a [normative](/docs/reference/normative) claim — a behavior, a policy, a decision — must include verification criteria that produce a pass/fail result. Verification can be:
- **Executable**: test commands, linters, policy checks
- **Static**: type checking, AST rules, dependency constraints
- **Observable**: manual inspection with unambiguous criteria
Unverifiable nodes are informational at best, misleading at worst.
## 3. The Graph Should Be Minimal
> **Only include what reduces manifestation ambiguity.**
Redundant or derived information belongs in generated artifacts, not the graph. The **minimality test** for any proposed node: "If I removed this, could a competent implementing agent make a choice I wouldn't want?" If yes, keep it. If no, remove it.
This is analogous to a basis in linear algebra — the minimal set of vectors that spans the space. The Spec Graph is the minimal set of specifications that spans the designer's intent space.
## 4. Progressive Adoption
A behavior-only graph is valid. You don't need to start with all node types.
Additional node types are added when the minimality test demands it — when manifestation ambiguity on a dimension the designer cares about requires explicit specification. A typical project grows its spec graph over time:
1. Start with behavior nodes (captures what the system does)
2. Add decision nodes (locks in architecture and tech stack)
3. Add domain nodes (establishes shared vocabulary)
4. Add policy nodes (sets non-functional boundaries)
## 5. Declarative Truth, Not Narrative Docs
Every decision that matters becomes a verifiable node in the graph, not prose in a document. The graph is the **constitution** — the single source of truth that agents and humans reference.
Narrative documentation is valuable for explanation, but it is not authoritative. If the docs disagree with the graph, the graph wins.
## 6. Normative vs. Informative Content
The framework strictly distinguishes:
- **[Normative](/docs/reference/normative)**: `expectation`, `statement`, `constraints`, `verification` — MUST be true, MUST be implemented, MUST pass
- **[Informative](/docs/reference/normative#informative-content)**: `metadata.rationale`, `metadata.notes` — non-executable context; specific node types may require certain metadata fields
This prevents "helpful notes" from becoming implicit requirements that break predictable manifestation.
---
# Completeness
Completeness is the central formal property of the Spec Graph. It answers the question: **does this specification contain enough information for predictable manifestation?**
## Definition
A Spec Graph G is **complete** with respect to implementing agent A if, for any two manifestations M₁ and M₂ that A could produce from G:
```
∀ M₁, M₂ ∈ Manifest(G, A): M₁ ≡ M₂
```
Intuitively: no matter how many times you manifest from the same spec, you get the "same" system. The agent has no ambiguous decisions left to make on dimensions the designer cares about.
## What "Equivalent" Means
Equivalence is **parameterized by the node types present in the graph**:
| Nodes Present | Equivalence Includes |
|---|---|
| behavior only | Same observable behavior |
| + decision | Same architectural structure and technology choices |
| + domain | Same data model and business rules |
| + policy | Same non-functional characteristics |
| All core types | Approaches total equivalence |
A graph with only behavior nodes guarantees behavioral equivalence — two manifestations do the same things, but may be structured completely differently. Adding decision nodes narrows the architecture. Adding domain nodes aligns the data model. Each additional dimension tightens the equivalence relation.
## Practical Implications
Perfect completeness requires that the spec determines **every** decision. In practice, this is neither achievable nor desirable — some decisions (variable names, exact file structure, import ordering) don't affect the designer's intent.
The practical goal is:
> **The remaining unspecified decisions do not violate the designer's intent.**
A spec graph is "complete enough" when the implementing agent's remaining choices are all in the category of irrelevant implementation details — things the designer genuinely doesn't care about.
## Completeness and the Agent
Completeness is defined **relative to an implementing agent**. A less capable agent may need more specification to produce predictable results. A more capable agent (one with better judgment about conventions, patterns, and best practices) may need less.
The Spec Graph aims to be complete enough for a **competent implementing agent** — one that understands the target language, frameworks, and common conventions, but has no special knowledge of this particular system's design intent.
## Relationship to Manifestation
Completeness is the precondition for predictable manifestation. In the ideal case, it yields full determinism:
```
Complete(G) ∧ Capable(A) → Deterministic(Manifest(G, A))
```
Perfect determinism — where every manifestation is identical — is an idealistic goal. In practice, the Spec Graph targets **predictability**: manifestations are equivalent across all dimensions the designer specified, even if they differ in incidental details. Determinism is the asymptote; predictability is the practical achievement.
Where `Capable(A)` means the agent can parse the graph, resolve dependencies, apply guidance, respect constraints, and verify outcomes. See [Manifestation](/docs/manifestation/overview) for how agents process a complete graph.
## Model-Theoretic Roots
The notion of completeness in the Spec Graph is inspired by two related concepts from mathematical logic. In model theory, a [complete theory](https://en.wikipedia.org/wiki/Complete_theory) is one that decides every sentence in its language — for any statement φ, the theory either proves φ or proves ¬φ. The consequence is that all models of a complete theory are *elementarily equivalent*: they satisfy exactly the same sentences. A stronger property, [categoricity](https://en.wikipedia.org/wiki/Categorical_theory), requires that a theory has essentially one model up to isomorphism — all realizations are structurally identical, not merely equivalent in what they satisfy.
The Spec Graph draws on both notions. Like a complete theory, a complete spec graph leaves no *relevant* question undecided — every decision the designer cares about is determined. Like a categorical theory, the goal is that all manifestations are the "same system" across specified dimensions, not merely that they agree on observable properties. The parameterized equivalence relation — where adding node types progressively tightens equivalence from behavioral to structural to total — mirrors the relationship between these concepts: completeness guarantees agreement on sentences, while categoricity guarantees structural identity.
We use the term "completeness" rather than "categoricity" for two reasons: it is far more intuitive for a non-mathematical audience, and the correspondence is deliberately loose. The Spec Graph departs from both model-theoretic notions in important ways — completeness is relative to the implementing agent rather than absolute, equivalence is parameterized by dimension rather than fixed, and an acceptable residual gap of benign decisions is by design rather than a failure of the theory. These are engineering adaptations, not formal instantiations of the mathematical concepts.
---
# Minimality
Minimality is the counterpart to completeness. Where completeness asks "is there enough?", minimality asks "is there too much?"
## Definition
A Spec Graph G is **minimal** if removing any node would break completeness — that is, would allow the implementing agent to make a choice the designer wouldn't want:
```
∀ node n ∈ G:
∃ M₁, M₂ ∈ Manifest(G \ {n}, A):
M₁ ≢ M₂
```
In plain English: every node in the graph is **load-bearing**. If you can remove a node and still get predictable manifestation, that node was redundant and shouldn't be there.
## The Basis Analogy
Minimality is analogous to a **basis** in linear algebra — the minimal set of vectors that spans the space. The Spec Graph is the minimal set of specifications that spans the designer's "intent space."
Just as a redundant vector in a basis can be expressed as a combination of the others, a redundant node in the graph can be derived from the remaining nodes (or simply doesn't affect manifestation).
## The Minimality Test
For any proposed spec node, ask:
> "If I removed this, could a competent implementing agent make a choice I wouldn't want?"
- **If yes** → the node is load-bearing, keep it
- **If no** → the node is redundant, remove it
This test should be applied regularly during spec authoring to keep the graph lean.
## Why Minimality Matters
### Cognitive Load
Every node in the graph is something an implementing agent (or human reviewer) must read, understand, and respect. Redundant nodes waste attention and create opportunities for contradiction.
### Contradiction Risk
Redundant nodes can drift out of sync with the nodes they duplicate. When two nodes say the same thing in different words, they will eventually disagree — and the agent must decide which one to follow.
### Signal-to-Noise Ratio
A minimal graph has maximum signal. Every node you read changes your understanding of the system. In a non-minimal graph, many nodes are noise — they don't tell you anything you couldn't derive from other nodes.
## What Minimality Is Not
Minimality does **not** mean brevity. A 200-node graph can be minimal if every node is load-bearing. A 5-node graph can be non-minimal if one node is redundant.
Minimality also does not mean "omit things the agent can figure out." It means "omit things where the agent's guess would always match the designer's intent." If there's any risk of divergence, the node is load-bearing.
## Relationship to Progressive Adoption
Minimality supports [progressive adoption](/docs/guides/progressive-adoption). You start with a small graph (behavior nodes only) and add nodes only when the minimality test demands it. This means the graph grows organically in response to actual manifestation ambiguity, not speculatively.
---
# The Completeness Gap
In practice, no spec graph is perfectly complete. There will always be some ambient knowledge the agent brings — language semantics, framework conventions, common sense. The **completeness gap** measures what's left unspecified.
## Definition
```
Completeness Gap = {decisions the agent must make} − {decisions the spec determines}
```
A behavior-only spec has a **large** completeness gap: the agent must decide architecture, technology, design, domain model, and constraints entirely on its own. Each additional node type in the graph **shrinks** the gap.
## Visualizing the Gap
```mermaid
graph LR
subgraph "Behavior-Only Spec"
B1[Behavior] --> GAP1[Large Gap:
Architecture, Design,
Tech, Domain, Constraints]
end
subgraph "Full Spec Graph"
B2[Behavior] --> GAP2[Small Gap:
Variable names,
file structure]
D[Decisions] --> GAP2
DOM[Domain] --> GAP2
C[Constraints] --> GAP2
end
```
## What Lives in the Gap
The completeness gap contains every decision the agent makes that isn't determined by the spec. These fall into categories:
### Benign Decisions (Acceptable Gap)
Decisions where any reasonable choice produces an equivalent system:
- Variable and function naming conventions
- File and directory organization (within a framework's conventions)
- Import ordering
- Comment placement
- Test organization details
These are part of the "acceptable residual" — the gap that remains even in a complete-enough graph.
### Dangerous Decisions (Unacceptable Gap)
Decisions where different choices produce materially different systems:
- Whether to use an abstraction layer or call a service directly
- Which state management pattern to use
- How to handle error recovery
- What the color palette is
- Whether a business rule is enforced in the UI, API, or database
If these decisions are in the gap, the spec is not complete enough.
## Shrinking the Gap
The practical question is always:
> **Is the remaining gap small enough that the agent's choices won't violate the designer's intent?**
Each node type addresses a specific category of decisions:
| Node Type | Gap It Closes |
|---|---|
| `behavior` | What the system does |
| `decision` | How it's structured and what it's built with |
| `domain` | What business concepts mean |
| `policy` | What non-functional boundaries must hold |
| `feature` | How specs are organized and grouped |
Extension types (`design_token`, `api_contract`, etc.) close additional specialized gaps as needed.
## The Gap Is Relative
The completeness gap depends on the **implementing agent's capabilities**. A more capable agent (with better defaults, stronger conventions, deeper framework knowledge) has a smaller effective gap — it fills in reasonable choices where a less capable agent might diverge.
The Spec Graph targets a **competent but uninformed** agent: one that knows how to code well but has no special knowledge of this system's design intent. Everything the designer cares about must be in the graph.
---
# Equivalence
Equivalence defines what it means for two manifestations to be "the same system." This is not a simple question — it depends on what dimensions the designer cares about.
## Parameterized Equivalence
Two manifestations M₁ and M₂ are **equivalent** (M₁ ≡ M₂) if they are indistinguishable across every dimension specified in the graph. The equivalence relation is parameterized by the node types present:
- **Behavioral equivalence**: Same observable behavior from the user's perspective
- **Structural equivalence**: Same architectural patterns and module boundaries
- **Technological equivalence**: Same technology stack and usage patterns
- **Domain equivalence**: Same data model, business rules, and vocabulary
- **Constraint equivalence**: Same non-functional characteristics (performance, security, accessibility)
A graph with all core node types approaches **total equivalence** — two manifestations would be indistinguishable in every dimension the designer specified.
## Semantic, Not Byte-Level
The Spec Graph targets **semantic predictability**, not byte-level determinism:
- **Byte-level determinism**: identical source code (rarely achievable, rarely necessary)
- **Semantic predictability**: logically equivalent systems under a declared equivalence contract
Two manifestations can have different variable names, different file structures, and different whitespace — and still be equivalent, because those differences don't affect any dimension the designer specified.
## The Equivalence Contract
For formal use, a Spec Graph can include an `equivalence_contract` extension node that explicitly declares what "same system" means:
- Which tests must pass
- Which invariants must hold
- Which performance budgets must be met
- Which structural properties must be preserved
This makes equivalence **testable** — you can verify whether two manifestations satisfy the contract.
## Levels of Equivalence
In practice, teams operate at different levels of equivalence strictness:
### Level 1: Behavioral Equivalence
The weakest useful level. Two systems do the same things from the user's perspective. Architecture, technology, and design may differ completely.
*Achieved by: behavior nodes only.*
### Level 2: Structural Equivalence
Systems share the same architectural patterns and boundaries. You could swap one implementation for the other without changing the system's module graph.
*Achieved by: behavior + decision nodes.*
### Level 3: Full Dimensional Equivalence
Systems are indistinguishable across all specified dimensions. Same behavior, same structure, same domain model, same constraints satisfied.
*Achieved by: all core node types.*
## Equivalence and Verification
Equivalence is verified through the **verification criteria** on each node. If every node's verification passes for both M₁ and M₂, the systems are equivalent under the graph's implied equivalence relation.
This means verification is not just about testing — it's the mechanism by which equivalence is established. See [Verification](/docs/manifestation/verification) for details.
---
# Graph Structure
The Spec Graph is a **directed, typed graph** where nodes are specification entries and edges are relationships between them.
## Components
A Spec Graph consists of three elements:
1. **Nodes** — individual specification entries, each with a type that determines its schema
2. **Edges** — typed, directed relationships between nodes (stored inside nodes as outbound links)
3. **Grouping nodes** — `feature` and `layer` nodes that organize related nodes across types
## Storage Model
The graph is stored as a directory of JSON files:
```
specgraph/
graph.json # Index: version, node references
nodes/
features/AUTH.json # Feature grouping node (vertical)
layers/PLATFORM.json # Layer grouping node (horizontal)
behaviors/AUTH-01.json # Behavior node
decisions/DEC-AUTH-01.json # Decision node
domains/DOM-USER-01.json # Domain node
policies/POL-PERF-01.json # Policy node
```
### The Index File
`graph.json` is the entry point. It lists every node in the graph with its file path:
```json
{
"$schema": "https://oco-adam.github.io/specgraph/schemas/graph.schema.json",
"specgraphVersion": "1.0.0",
"nodes": [
{ "id": "AUTH", "path": "nodes/features/AUTH.json" },
{ "id": "AUTH-01", "path": "nodes/behaviors/AUTH-01.json" },
{ "id": "DEC-AUTH-01", "path": "nodes/decisions/DEC-AUTH-01.json" },
{ "id": "DOM-USER-01", "path": "nodes/domains/DOM-USER-01.json" },
{ "id": "POL-PERF-01", "path": "nodes/policies/POL-PERF-01.json" }
]
}
```
### Node Files
Each node is a self-contained JSON file. Edges are expressed as outbound links inside the node:
```json
{
"id": "AUTH-01",
"type": "behavior",
"title": "Login Form Display",
"expectation": "Login page renders email and password input fields with a submit button",
"constraints": ["Password field must mask input characters"],
"verification": "pytest tests/auth/test_login_form.py -k AUTH_01",
"links": {
"implements": ["DOM-USER-01"],
"depends_on": ["DEC-AUTH-01"]
}
}
```
## Node-Local Edges
Edges live inside the node that owns them. This is a deliberate design choice:
- **Atomic PRs**: changing a node and its relationships happens in one file
- **Local reasoning**: when reviewing a node, you see exactly what it depends on and constrains
- **No duplication**: outbound edges are canonical; reverse edges are computed by tooling
Only **forward edges** are stored. If `AUTH-01` depends on `DEC-AUTH-01`, the `depends_on` link is stored in `AUTH-01.json`. The inverse ("`DEC-AUTH-01` is depended on by `AUTH-01`") is computed at query time.
`Forward` here is a storage convention (declare the relationship once on the source node), not a runtime traversal limitation.
## The Graph as a Whole
```mermaid
graph TD
AUTH[AUTH
feature] -->|contains| AUTH01[AUTH-01
behavior]
AUTH -->|contains| AUTH02[AUTH-02
behavior]
AUTH -->|contains| DEC[DEC-AUTH-01
decision]
AUTH -->|contains| DOM[DOM-USER-01
domain]
AUTH -->|contains| POL[POL-PERF-01
policy]
AUTH01 -->|implements| DOM
AUTH01 -->|depends_on| DEC
AUTH02 -->|depends_on| AUTH01
DEC -->|constrains| AUTH01
POL -->|constrains| AUTH01
```
At manifestation time, tooling loads all node files and constructs the full in-memory graph. The file-per-node layout is a storage concern — at runtime, it's one unified graph.
Only `depends_on` is required to be acyclic. Other edge types may form cycles when modelling legitimate mutual relationships.
## Validation
The graph is validated at two levels:
| Level | What's Checked | Tool |
|---|---|---|
| **Schema** | Each node file matches `node.schema.json`; `graph.json` matches `graph.schema.json` | JSON Schema validator (e.g., `ajv`) |
| **Graph integrity** | Referential integrity (all edge targets exist), no self-references, `depends_on` is acyclic | Graph validator tooling |
Schema validation and graph integrity validation are independent — you can validate node content without checking structural integrity, and vice versa.
### Commands
From the repo root:
```bash
# Schema validation (examples)
node validate.js
# Graph integrity validation (examples)
node graph_check.js
```
---
# Node Types Overview
Spec Graph uses a **tiered type system**: seven core types plus optional extension types.
Core taxonomy in v2:
- 2 grouping types: `feature`, `layer`
- 5 [normative](/docs/reference/normative) core types: `foundation`, `behavior`, `decision`, `domain`, `policy`
## Core Types
| Type | Purpose | Key Question |
|---|---|---|
| **`feature`** | Vertical grouping and namespace | Which product slice does this spec belong to? |
| **`layer`** | Horizontal shared grouping | Which shared platform capability does this belong to? |
| **`foundation`** | Concrete repository scaffolding | What physical structure must exist before application logic? |
| **`behavior`** | Observable system behavior | What does the user see and do? |
| **`decision`** | Architectural, technical, or stack decision | How should it be built, and with what? |
| **`domain`** | Business concept, term, or rule | What do domain terms mean? |
| **`policy`** | Non-functional requirement | What limits must be met? |
### Node Shapes
**Grouping nodes** (`feature`, `layer`) use a simple shape with `title`, `description`, and optional `links`.
**Behavior nodes** carry `expectation` and optional `constraints`.
**Contract nodes** (foundation, decision, domain, policy, extensions) share a uniform shape with `statement` + `verification`.
## Extension Types
For finer-grained modeling, extension types are available:
| Extension Type | Purpose |
|---|---|
| `design_token` | Visual tokens: colors, typography, spacing |
| `ui_contract` | UI component props, states, variants |
| `api_contract` | REST, GraphQL, or event contracts |
| `data_model` | Database schemas, migrations, invariants |
| `artifact` | Content-addressed external artifact |
| `equivalence_contract` | Formal definition of "same system" |
| `pipeline` | Manifestation pipeline steps and gates |
## Decision Categories
Decision nodes use `category` to capture where the decision sits:
| Category | What It Captures | Example |
|---|---|---|
| `architecture` | Structural patterns, module boundaries | "All auth goes through an AuthProvider interface" |
| `stack` | Technology choices and usage constraints | "Use Clerk for authentication" |
| `pattern` | Implementation patterns | "Use optimistic updates for drag-and-drop" |
| `interface` | Public API contracts between modules | "Auth module exports authenticate() and revokeSession()" |
For detailed documentation on each type, see the [Node Types](/docs/node-types/behavior) section.
---
# Edge Types
Edges encode the relationships between nodes. They are the connective tissue that makes the graph more than a collection of lists.
## The Seven Edge Types
The Spec Graph defines seven typed edge kinds. Each relationship is authored once as a forward declaration in the source node:
| Edge Type | Meaning | Example |
|---|---|---|
| `contains` | Grouping/namespace relationship | Feature or layer contains its child nodes |
| `depends_on` | Must be satisfied before this node | A behavior depends on a decision being in place |
| `constrains` | Narrows implementation choices for the target | A policy limits how a behavior can be implemented |
| `implements` | This node realizes part of the target | A behavior implements a domain concept |
| `derived_from` | Generated or imported from target (pinned hash) | A design token derived from an artifact |
| `verified_by` | Points to verification checks | A behavior verified by a test strategy node |
| `supersedes` | Replaces the target node | A new decision supersedes an old one |
## Edge Direction
Edges are **forward-only** and stored in the source node's `links` field:
```json
{
"id": "AUTH-01",
"type": "behavior",
"links": {
"implements": ["DOM-USER-01"],
"depends_on": ["DEC-AUTH-01"]
}
}
```
This means: "AUTH-01 implements DOM-USER-01" and "AUTH-01 depends on DEC-AUTH-01."
**Inverse edges are computed by tooling, never stored.** If `AUTH-01` depends on `DEC-AUTH-01`, tooling derives that `DEC-AUTH-01` is depended on by `AUTH-01`. This keeps the graph DRY — each relationship is declared once, on the node where it's most natural to author.
`forward-only` describes storage, not runtime traversal. Tooling materializes both outbound and inbound views in memory.
## Edge Semantics
### `contains`
Used by grouping nodes (`feature`, `layer`) to group their children. This is the primary organizational edge.
```json
// In AUTH.json (feature)
"links": {
"contains": ["AUTH-01", "AUTH-02", "DEC-AUTH-01", "DOM-USER-01"]
}
```
### `depends_on`
Establishes ordering requirements. If A depends on B, then B must be manifested (or at least understood) before A.
**Critical rule: `depends_on` must be acyclic.** Cycles in dependency edges are a validation error.
Layer-specific safety rule:
- `layer -> depends_on -> feature` is invalid
- `feature -> depends_on -> layer` is valid
- `layer -> depends_on -> layer` is valid
This acyclicity requirement is specific to `depends_on` because manifestation order must be topologically sortable. Other edge types can be cyclic when that matches the domain model.
### `constrains`
Narrows the implementation space of the target. A policy node might constrain a behavior node, meaning the behavior must be implemented within the policy's boundaries.
```json
// In POL-PERF-01.json (policy)
"links": {
"constrains": ["AUTH-01", "AUTH-04"]
}
```
#### Constraint propagation through `contains`
To avoid edge explosion, `constrains` is interpreted with transitive propagation:
1. If `A constrains B` and `B contains C`, then `A` implicitly constrains `C` (downward cascade).
2. Propagation does not flow upward from child to parent.
3. Direct `constrains` propagation does not flow over `depends_on`; dependency-based propagation is a separate layer-specific rule.
4. If a node has multiple `contains` ancestors, inherited constraints are unioned.
### `implements`
Links a concrete node to the abstract concept it realizes. Typically used by behavior nodes pointing to domain nodes:
```json
// In AUTH-01.json (behavior)
"links": {
"implements": ["DOM-USER-01"]
}
```
### `derived_from`
Indicates that this node was generated from or imported from another node, typically an `artifact`.
To make derivations reproducible, derived nodes should include `pins` entries that record the expected source hash at derivation time. Tooling can then detect when a derived node is stale (source hash changed) and trigger re-derivation.
### `verified_by`
Points from a node to the node(s) that verify it. Useful when verification logic is complex enough to warrant its own node (e.g., an `equivalence_contract` or `pipeline` node).
### `supersedes`
Marks a node as replacing another. The existence of this edge signals that the target node is deprecated. This supports evolution without losing history.
## Validation Rules
1. **Referential integrity**: every edge target must exist as a node in the graph
2. **No self-references**: a node cannot have an edge to itself
3. **Acyclicity for `depends_on`**: dependency chains must not form cycles
4. **Non-decorative**: edges must affect planning, implementation, or verification — purely decorative edges are discouraged
## Common Edge Patterns
### Valid Non-`depends_on` Cycle
Non-dependency cycles are allowed by graph integrity checks. For example, this structural cycle is valid:
```mermaid
graph LR
API[API-AUTH-01
api_contract] -->|implements| DOM[DOM-USER-01
domain]
DOM -->|constrains| API
```
This can model a bidirectional relationship between a contract and domain rules. It is valid because neither edge is `depends_on`.
### Invalid `depends_on` Cycle
Dependency cycles are rejected by graph integrity validation:
```mermaid
graph LR
A[AUTH-01
behavior] -->|depends_on| B[DEC-AUTH-01
decision]
B -->|depends_on| A
```
This is invalid because `depends_on` must be acyclic to preserve a deterministic manifestation order.
### Behavior → Decision → Stack
```mermaid
graph LR
B[AUTH-01
behavior] -->|depends_on| D[DEC-AUTH-01
decision/architecture]
D -->|depends_on| S[DEC-AUTH-02
decision/stack]
```
A behavior depends on an architectural decision, which in turn depends on a technology choice.
### Policy → Multiple Behaviors
```mermaid
graph TD
P[POL-PERF-01
policy] -->|constrains| B1[AUTH-01]
P -->|constrains| B2[AUTH-04]
P -->|constrains| B3[DASH-01]
```
A performance policy applies to multiple behaviors across features.
### Grouping Node → Children
```mermaid
graph TD
F[AUTH
feature] -->|contains| B1[AUTH-01]
L[PLATFORM
layer] -->|contains| D[DEC-PLAT-01]
L -->|contains| P[POL-PLAT-SEC-01]
```
Features and layers both group related nodes regardless of type.
---
# Features & Namespaces
Spec Graph has two [non-normative](/docs/reference/normative#non-normative-content) grouping node types:
- `feature`: vertical product slices
- `layer`: horizontal shared infrastructure
## Feature Nodes (Vertical)
A feature organizes behavior, decisions, domains, and policies for one product area.
```json
{
"id": "AUTH",
"type": "feature",
"title": "User Authentication",
"description": "Login, session management, and logout flows",
"links": {
"contains": ["AUTH-01", "AUTH-02", "DOM-USER-01"],
"depends_on": ["PLATFORM"]
}
}
```
## Layer Nodes (Horizontal)
A layer groups cross-feature platform capability.
```json
{
"id": "PLATFORM",
"type": "layer",
"title": "Core Platform",
"description": "Shared infrastructure and security/performance controls",
"links": {
"contains": ["DEC-PLAT-01", "POL-PLAT-SEC-01"]
}
}
```
## Directional Rule
Layers are foundational. They cannot depend on product features.
- Allowed: `feature -> depends_on -> layer`
- Allowed: `layer -> depends_on -> layer`
- Forbidden: `layer -> depends_on -> feature`
## Cross-Feature Reuse
Use layers when multiple features share the same infrastructure context.
```mermaid
graph TD
AUTH[AUTH feature] -->|depends_on| PLATFORM[PLATFORM layer]
BILLING[BILLING feature] -->|depends_on| AUTH
PLATFORM -->|contains| DEC[DEC-PLAT-01]
PLATFORM -->|contains| POL[POL-PLAT-SEC-01]
```
In this pattern, billing transitively inherits platform guidance through `BILLING -> AUTH -> PLATFORM`.
## Containment Is Membership
`contains` defines grouping membership. File location is a convention, not a semantic rule.
---
# Behavior Nodes
Behavior nodes are the foundation of the Spec Graph. They capture **observable system behavior** from the user's perspective — what the system does, not how it does it.
## Schema
```json
{
"id": "AUTH-01",
"type": "behavior",
"title": "Login Form Display",
"expectation": "Login page renders email and password input fields with a submit button",
"constraints": ["Password field must mask input characters"],
"verification": "pytest tests/auth/test_login_form.py -k AUTH_01",
"links": {
"implements": ["DOM-USER-01"],
"depends_on": ["DEC-AUTH-01"]
}
}
```
## Fields
| Field | Required | Description |
|---|---|---|
| `id` | Yes | Unique identifier (e.g., `AUTH-01`, `TASKBOARD-03`) |
| `type` | Yes | Must be `"behavior"` |
| `title` | Yes | Short human-readable name (3–100 chars) |
| `expectation` | Yes | WHAT the system does (min 10 chars) |
| `constraints` | No | [Normative](/docs/reference/normative) conditions that must hold for this behavior. Authoring convention: include explicitly; use `[]` when none. |
| `verification` | Yes | Single pass/fail check (min 5 chars) |
| `links` | No | Outbound edges to other nodes |
| `metadata` | No | [Non-normative](/docs/reference/normative#non-normative-content) context (rationale, notes, tags) |
## The ONE Rule
Behavior nodes follow the ONE behavior rule:
> **ONE trigger → ONE behavior → ONE outcome**
A behavior node describes exactly one observable thing the system does. If the expectation contains "and" connecting two distinct behaviors, split it into two nodes.
**Good:**
```
"expectation": "Login page renders email and password input fields with a submit button"
```
This is one observable state — the login form.
**Bad:**
```
"expectation": "Login page renders a form and validates email on submit and redirects on success"
```
This is three behaviors. Split into: form display, email validation, and redirect.
## Expectation vs. Constraints
- **Expectation**: describes WHAT happens — the observable behavior
- **Constraints**: conditions that must hold for this behavior — guard rails, not additional behaviors
The constraints are not second behaviors — they are side-conditions on the expectation.
"Password field must mask input characters" constrains how the login form is displayed,
not what happens when you submit it.
Schema compatibility: a behavior with no constraints may omit the field.
Authoring convention for agent interoperability: include `constraints` explicitly and use an empty array (`[]`) when none apply.
There is no need for a `"None"` sentinel.
### Temporal specificity
Constraints are [normative](/docs/reference/normative) — an implementor must respect them. But not all conditions
hold at all times. The condition's own language carries its temporal semantics:
| Condition | Temporal profile |
|-----------|--------------------|
| "Form fields remain disabled during submission" | **Immediate** — must hold at all times during the operation |
| "Email must be eventually unique" | **Eventual** — converges after the operation; brief violations during convergence are acceptable |
| "Confirmation email is sent within 30 seconds" | **Bounded** — must complete within a time window |
All three are [normative](/docs/reference/normative) and testable. The enforcement model is encoded in the
condition text, not in the field name or type.
#### Example: immediate and eventual constraints on the same behavior
```json
{
"type": "behavior",
"id": "REG-01",
"title": "User Registration",
"expectation": "When user submits valid registration form, an account is created and confirmation email is sent",
"constraints": [
"Form fields remain disabled during submission",
"Email must be eventually unique",
"Confirmation email is sent within 30 seconds"
]
}
```
Three constraints with three different temporal profiles — all [normative](/docs/reference/normative), all testable,
none contradicted by the field name.
If the choice of enforcement model (strong vs. eventual consistency, database vs.
application-level) is itself a dangerous completeness gap, capture it as a
**decision node** — that's what decisions are for. See the
[Conditions, Enforcement, and Strictness](/docs/authoring/writing-nodes#conditions-enforcement-and-strictness)
section for the full pattern.
:::info Disambiguation: `constraints` field vs policy nodes vs `constrains` edges
The spec graph uses related but distinct concepts:
| Concept | What it is | Scope |
|---------|-----------|-------|
| `constraints` field | Array of [normative](/docs/reference/normative) conditions on a single node | Narrows THIS node's `expectation` or `statement` |
| Policy node (`type: "policy"`) | A standalone node for cross-cutting NFRs | Affects OTHER nodes via `constrains` edges; has `severity` (hard/soft) |
| `constrains` edge | A graph relationship | Declares that the source node narrows implementation choices for the target |
**Decision rule:**
- Condition specific to one node → `constraints` field entry on that node
- Cross-cutting requirement affecting multiple nodes → policy node with `constrains` edges
- Expressing that one node limits another → `constrains` edge
:::
## Verification
Each behavior has a single verification string — typically an executable test command:
```json
"verification": "pytest tests/auth/test_login_form.py -k AUTH_01"
```
For behaviors that are harder to test programmatically, the verification can be a description of a manual check:
```json
"verification": "Visual inspection: login form matches wireframe layout"
```
Prefer executable verification wherever possible.
## Common Edge Patterns
| Edge | Direction | Meaning |
|---|---|---|
| `implements` | behavior → domain | This behavior realizes a domain concept |
| `depends_on` | behavior → behavior | Must be implemented after the dependency |
| `depends_on` | behavior → decision | Requires this architectural decision |
Behaviors are typically the **most connected** nodes in the graph — they reference the decisions that guide their implementation, the domain concepts they realize, and the constraints that limit them.
## ID Conventions
Behavior IDs follow the pattern `FEATURE-##`:
| Feature | Behavior IDs |
|---|---|
| AUTH | `AUTH-01`, `AUTH-02`, `AUTH-03` |
| TASKBOARD | `TASKBOARD-01`, `TASKBOARD-02` |
| BILLING | `BILLING-01`, `BILLING-02` |
---
# Decision Nodes
Decision nodes capture **any architectural, technical, or stack decision that narrows the solution space**. They represent the "how" and "with what" that a senior engineer would communicate to a team.
## Schema
```json
{
"id": "DEC-AUTH-01",
"type": "decision",
"category": "architecture",
"title": "Abstract Auth Provider Interface",
"statement": "All authentication operations must go through an abstract AuthProvider interface. Concrete implementations include a production provider and a DeterministicProvider for testing.",
"constraints": [
"Interface must define: authenticate(), validateSession(), revokeSession()",
"No authentication call may bypass the AuthProvider interface"
],
"verification": [
"cargo test auth::provider::contract -- --exact",
"go test ./internal/auth/provider -run TestAuthProviderContract"
],
"links": {
"constrains": ["AUTH-01", "AUTH-04", "AUTH-05"]
},
"metadata": {
"rationale": "Enables deterministic testing of auth flows without hitting external services.",
"rejected_alternatives": [
{
"title": "Direct Clerk SDK calls in UI components",
"reason": "Creates provider lock-in and makes deterministic integration testing significantly harder."
}
]
}
}
```
## Fields
| Field | Required | Description |
|---|---|---|
| `id` | Yes | Unique identifier (e.g., `DEC-AUTH-01`) |
| `type` | Yes | Must be `"decision"` |
| `category` | Yes | One of: `architecture`, `stack`, `pattern`, `interface` |
| `title` | Yes | Short name (3–140 chars) |
| `statement` | Yes | The declarative truth that must hold |
| `constraints` | No | [Normative](/docs/reference/normative) conditions that further narrow this decision's statement |
| `verification` | Yes | Array of pass/fail checks (min 1) |
| `links` | No | Outbound edges |
| `metadata` | Yes | Context object for decision history; must include `metadata.rationale` (min 10 chars) |
## Categories
The `category` field distinguishes sub-types of decisions:
### `architecture`
Structural patterns, module boundaries, abstraction layers.
```json
{
"category": "architecture",
"statement": "All auth operations go through an AuthProvider interface."
}
```
**When to use:** The decision affects the system's structure or module boundaries.
### `stack`
Technology choices, frameworks, libraries, and their usage constraints.
```json
{
"category": "stack",
"statement": "Use Clerk as the production authentication provider.",
"constraints": ["Use Clerk's middleware for route protection"]
}
```
**When to use:** The decision is about which technology to use and how.
### `pattern`
Implementation patterns that guide how behaviors are built.
```json
{
"category": "pattern",
"statement": "Task status changes use optimistic updates with revert on failure."
}
```
**When to use:** The decision prescribes a specific coding pattern.
### `interface`
Public API contracts between modules or services.
```json
{
"category": "interface",
"statement": "The auth module exports authenticate() and revokeSession() functions."
}
```
**When to use:** The decision defines a boundary contract.
## Why One Type with Categories?
Earlier research separated "technical" and "stack" into distinct node types. In practice, the boundary was blurry — "Use Next.js App Router" is both a technology choice and an architectural decision. The single `decision` type with a `category` field reflects this reality.
## The Statement Field
The `statement` is the **declarative truth** that must hold. It should be:
- **Specific**: "Use Clerk for authentication" not "Use a managed auth provider"
- **Actionable**: an implementing agent can determine exactly what to do
- **Verifiable**: there's a way to check whether it's been followed
## Decision Rationale (Micro-ADR)
Every decision node must include `metadata.rationale`. This keeps the top-level contract clean while preserving the historical "why" that agents need to avoid regressions.
Use `metadata.rejected_alternatives` when meaningful trade-offs were evaluated. Each entry has:
- `title`: rejected approach name
- `reason`: why it was rejected
## When to Create Decision Nodes
Apply the [minimality test](/docs/theory/minimality):
> "If I removed this, could a competent implementing agent make a choice I wouldn't want?"
Common triggers:
| Scenario | Decision Category |
|---|---|
| Two implementations could use incompatible architectures | `architecture` |
| Technology choice affects how behaviors are implemented | `stack` |
| A specific coding pattern is required for quality/testability | `pattern` |
| Modules need a stable interface contract | `interface` |
## ID Conventions
Decision IDs follow the pattern `DEC-FEATURE-##`:
- `DEC-AUTH-01` — first decision in the AUTH feature
- `DEC-TB-03` — third decision in the TASKBOARD feature
- `DEC-PLATFORM-01` — platform-wide decision
---
# Domain Nodes
Domain nodes capture **business concepts, terms, and rules** — the ubiquitous language of the system. They establish shared vocabulary so that when a behavioral spec says "user," both the spec author and the implementing agent agree on exactly what "user" means.
## Schema
```json
{
"id": "DOM-USER-01",
"type": "domain",
"title": "User Account",
"statement": "A registered entity that can authenticate, own resources, and have role-based permissions within a project.",
"constraints": [
"A user must have exactly one role per project (owner, admin, or member)",
"Email uniqueness is enforced at the database level",
"Every user must have a verified email before accessing any resource"
],
"verification": [
"Database schema enforces email uniqueness",
"go test ./internal/domain/... -run TestUserAccountRules",
"pytest tests/domain/test_user_account.py -k DOM_USER_01"
],
"metadata": {
"rationale": "Establishes shared vocabulary for 'user' across the system."
}
}
```
## Fields
| Field | Required | Description |
|---|---|---|
| `id` | Yes | Unique identifier (e.g., `DOM-USER-01`) |
| `type` | Yes | Must be `"domain"` |
| `title` | Yes | The concept name (3–140 chars) |
| `statement` | Yes | What this concept IS — its definition |
| `constraints` | No | Business rules governing this concept |
| `verification` | Yes | How to verify the domain model is correct (min 1) |
| `links` | No | Outbound edges |
| `metadata` | No | [Non-normative](/docs/reference/normative#non-normative-content) context |
## The Statement Field
For domain nodes, the `statement` is a **definition**: what the concept IS in the business domain.
**Good:**
```
"statement": "A registered entity that can authenticate, own resources, and have role-based permissions within a project."
```
**Bad:**
```
"statement": "Users can log in and manage their projects."
```
The bad example describes behaviors, not the concept itself. Behaviors belong in behavior nodes.
## Constraints as Business Rules
The `constraints` array captures the business rules governing this concept:
```json
"constraints": [
"A user must have exactly one role per project",
"Email uniqueness is enforced at the database level",
"Tasks are created in 'backlog' status by default",
"Status transitions follow: backlog → in-progress → review → done"
]
```
Each entry is a [normative](/docs/reference/normative) condition that implementations must respect.
## When to Create Domain Nodes
Apply the minimality test:
> "If I removed this definition, could the implementing agent misunderstand what this business term means?"
Common triggers:
| Scenario | Example |
|---|---|
| A business term is ambiguous | "User" could mean account, profile, or person |
| A concept has non-obvious rules | Tasks have a specific status transition order |
| Multiple behaviors reference the same concept | Several behaviors deal with "projects" |
| Domain rules constrain implementation | "Email must be unique" affects database design |
## Relationship to Behaviors
Domain nodes are often **implemented by** behavior nodes:
```mermaid
graph LR
B1[AUTH-01: Login Form] -->|implements| DOM[DOM-USER-01: User Account]
B2[AUTH-04: Login Redirect] -->|implements| DOM
B3[AUTH-05: Session Persistence] -->|implements| DOM
```
This means these behaviors collectively realize the "User Account" concept.
## ID Conventions
Domain IDs follow the pattern `DOM-CONCEPT-##`:
- `DOM-USER-01` — User Account
- `DOM-TASK-01` — Task (in a task management system)
- `DOM-PROJECT-01` — Project
- `DOM-TENANT-01` — Tenant (in a multi-tenant system)
---
# Policy Nodes
Policy nodes capture **cross-cutting non-functional requirements** — performance budgets, security policies, accessibility standards, cost limits, reliability targets.
## Schema
```json
{
"id": "POL-PERF-01",
"type": "policy",
"severity": "hard",
"title": "Page Load Performance Budget",
"statement": "All pages must reach First Contentful Paint within 1.5 seconds on a 4G connection.",
"constraints": [
"FCP must be measured under simulated 4G conditions",
"Budget applies to all routes including the login page"
],
"verification": [
{
"kind": "command",
"command": "npx lighthouse --preset=perf --assert-fcp=1500 http://localhost:3000"
}
],
"links": {
"constrains": ["AUTH-01", "AUTH-04"]
},
"metadata": {
"rationale": "Core Web Vitals compliance for SEO and user experience."
}
}
```
## Fields
| Field | Required | Description |
|---|---|---|
| `id` | Yes | Unique identifier (e.g., `POL-PERF-01`) |
| `type` | Yes | Must be `"policy"` |
| `severity` | Yes | `"hard"` (blocks manifestation) or `"soft"` (quality target) |
| `title` | Yes | Short name (3–140 chars) |
| `statement` | Yes | What must be true, with measurable criteria |
| `constraints` | No | [Normative](/docs/reference/normative) sub-conditions that further narrow this node's statement |
| `verification` | Yes | How to measure compliance (min 1) |
| `links` | No | Outbound edges (typically `constrains`) |
| `metadata` | No | [Non-normative](/docs/reference/normative#non-normative-content) context |
## Severity
The `severity` field distinguishes between requirements that block manifestation and those that are quality targets:
### `hard`
A hard policy **must** be satisfied. If verification fails, manifestation is blocked.
```json
{
"severity": "hard",
"statement": "All pages must reach FCP within 1.5s on 4G."
}
```
### `soft`
A soft policy is a **quality target**. Failing it produces a warning, not a blocker.
```json
{
"severity": "soft",
"statement": "API response times should be under 200ms at p50."
}
```
## Policy Categories
Policy nodes cover a wide range of non-functional requirements:
| Category | Examples |
|---|---|
| **Performance** | Latency budgets, throughput minimums, bundle size limits |
| **Security** | Authentication requirements, data encryption, input validation |
| **Accessibility** | WCAG compliance level, keyboard navigation, screen reader support |
| **Reliability** | Uptime targets, error rate limits, recovery time |
| **Cost** | Cloud spend ceilings, API call budgets |
## The `constrains` Edge
Policy nodes typically use the `constrains` edge to declare which nodes they apply to:
```mermaid
graph TD
P[POL-PERF-01
Performance Budget] -->|constrains| B1[AUTH-01
Login Form]
P -->|constrains| B2[AUTH-04
Login Redirect]
P -->|constrains| B3[DASH-01
Dashboard Load]
```
A single policy can apply to many behaviors across multiple features.
Policies can also be attached at higher-level feature/layer/domain nodes and inherited by contained descendants via transitive propagation through `contains` edges. Prefer the highest applicable attachment point to reduce graph clutter.
## When to Create Policy Nodes
Apply the minimality test:
> "If I removed this, could the implementing agent produce a system that violates a non-functional requirement I care about?"
Common triggers:
| Scenario | Policy Type |
|---|---|
| Pages must load within a time budget | Performance |
| All user data must be encrypted at rest | Security |
| The app must meet WCAG 2.1 AA | Accessibility |
| Monthly cloud costs must stay under $500 | Cost |
| Board must render 200 cards at 60fps | Performance |
## ID Conventions
Policy IDs follow the pattern `POL-CATEGORY-##` or `POL-FEATURE-CATEGORY-##`:
- `POL-PERF-01` — global performance policy
- `POL-SEC-01` — global security policy
- `POL-TB-PERF-01` — taskboard-specific performance policy
---
# Feature Nodes
Feature nodes are **[non-normative](/docs/reference/normative#non-normative-content) grouping nodes** that organize the graph into vertical product slices. They are the simplest node type — they have no `statement`, `verification`, or `constraints`.
Use [Layer Nodes](/docs/node-types/layer) for shared horizontal infrastructure.
## Schema
```json
{
"id": "AUTH",
"type": "feature",
"title": "User Authentication",
"description": "Login, session management, and logout flows",
"links": {
"contains": ["AUTH-01", "AUTH-02", "AUTH-03", "DEC-AUTH-01", "DOM-USER-01", "POL-PERF-01"]
}
}
```
## Fields
| Field | Required | Description |
|---|---|---|
| `id` | Yes | Short uppercase identifier (e.g., `AUTH`, `TASKBOARD`) |
| `type` | Yes | Must be `"feature"` |
| `title` | Yes | Human-readable name (3–100 chars) |
| `description` | Yes | What this feature area covers |
| `links` | No | Outbound edges (typically `contains`) |
| `metadata` | No | [Non-normative](/docs/reference/normative#non-normative-content) context |
## Non-Normative
Feature nodes make **no claims about the system**. They don't have expectations, statements, or verification criteria. They exist purely to organize.
This means:
- Removing a feature node doesn't change what gets implemented
- Feature nodes don't participate in the minimality test
- They are organizational sugar, not load-bearing specification
## The `contains` Edge
Features use the `contains` edge to declare their children:
```json
"links": {
"contains": ["AUTH-01", "AUTH-02", "DEC-AUTH-01", "DOM-USER-01"]
}
```
A feature can contain nodes of **any type**: behaviors, decisions, domains, policies, and extensions.
## ID Conventions
Feature IDs are short uppercase identifiers:
| Feature | ID |
|---|---|
| User Authentication | `AUTH` |
| Task Board | `TASKBOARD` |
| Billing & Payments | `BILLING` |
| Design System | `DESIGNSYSTEM` |
| Notifications | `NOTIFICATIONS` |
Feature IDs should be recognizable and concise — they serve as the namespace prefix for child node IDs.
For shared infrastructure names like `PLATFORM` or `DATA`, prefer `type: "layer"` instead of `type: "feature"`.
---
# Layer Nodes
Layer nodes are **[non-normative](/docs/reference/normative#non-normative-content) grouping nodes** for shared horizontal infrastructure. They complement feature nodes.
- `feature`: groups vertical product slices
- `layer`: groups shared platform/infrastructure capability
## Schema
```json
{
"id": "PLATFORM",
"type": "layer",
"title": "Core Platform",
"description": "Shared infrastructure contracts and platform policies",
"links": {
"contains": ["DEC-PLAT-01", "POL-PLAT-SEC-01"]
}
}
```
## Fields
| Field | Required | Description |
|---|---|---|
| `id` | Yes | Short uppercase identifier (e.g., `PLATFORM`, `DATA`) |
| `type` | Yes | Must be `"layer"` |
| `title` | Yes | Human-readable name (3–100 chars) |
| `description` | Yes | What shared capability this layer covers |
| `links` | No | Outbound edges (typically `contains`, optionally `depends_on`) |
| `metadata` | No | [Non-normative](/docs/reference/normative#non-normative-content) context |
## Dependency Rule
Layer dependencies are directional:
- Allowed: `feature -> depends_on -> layer`
- Allowed: `layer -> depends_on -> layer`
- Forbidden: `layer -> depends_on -> feature`
Tooling rejects the forbidden pattern as an inversion error.
## Propagation Semantics
If a node transitively depends on a layer, it receives layer-originated effective guidance:
- decisions in the layer `contains` closure
- applicable constraining nodes (for example policies) that apply to the layer root or descendants under normal `constrains` + `contains` semantics
If a grouping node (feature or layer) depends on a layer, its contained descendants inherit that same propagated layer guidance.
## When to Use a Layer
Use a layer when a capability is shared across multiple features and is architecturally meaningful.
Good candidates:
- `PLATFORM` (shared runtime and gateway decisions)
- `DATA` (shared persistence and consistency rules)
- `DESIGN` (design-system policies and tokens)
- `OBSERVABILITY` (logging/metrics/tracing baseline)
If a decision set is only used by one feature, keep it in that feature instead of creating a layer.
---
# Foundation Nodes
Foundation nodes describe the persistent, foundational structural realities of a repository. They declare that mandatory scaffolding, package configuration, and baseline file structure are manifested and available before application logic is implemented.
## Schema
```json
{
"$schema": "https://oco-adam.github.io/specgraph/schemas/node.schema.json",
"id": "FND-GO-MOD",
"type": "foundation",
"title": "Go Module Manifestation",
"statement": "The repository contains a valid Go module identified as 'github.com/oco-adam/todoist-clone', satisfying the Go runtime requirements of the platform.",
"verification": [
"test -f go.mod",
"grep -q 'module github.com/oco-adam/todoist-clone' go.mod"
],
"links": {
"depends_on": ["DEC-PLAT-01"]
},
"metadata": {
"rationale": "Provides the Go module anchor that all subsequent Go source files and packages depend on."
}
}
```
## Fields
| Field | Required | Description |
|---|---|---|
| `id` | Yes | Unique identifier (pattern: `FND-*`) |
| `type` | Yes | Must be `"foundation"` |
| `title` | Yes | Short name (3-140 chars) |
| `statement` | Yes | Declarative truth about manifested filesystem state. Must not use imperative verbs. |
| `verification` | Yes | Array of self-contained shell commands (min 1). No project test runners. |
| `constraints` | No | Additional structural conditions |
| `links` | No | Outbound edges. `depends_on` targets must be `decision`, `policy`, or `foundation` only. |
| `metadata` | No | Non-executable context |
## The Cold Start Problem
In a greenfield repository, abstract decisions alone do not provide physical implementation anchors. A decision such as "the backend is written in Go" can still lead an agent to produce incorrect scaffolding if no concrete repository baseline exists.
Foundation nodes close that gap. They declare a concrete baseline state, such as module manifests, workspace configuration, and required directories, so downstream manifestation starts from explicit physical reality instead of inference.
## Verification Command Rules
Foundation verification commands must be self-contained shell checks against repository state.
- Use file/directory/content checks such as `test -f`, `test -d`, and `grep -q`
- Do not use project test runners (`npm test`, `go test`, `pytest`) for foundation verification
- For structured verification entries, use `"kind": "command"` exclusively
## Agent Implementation Expectations
Foundation manifestation should be idempotent and convergent:
- The declared filesystem state exists after processing
- Existing compliant state is preserved
- Conflicting state is converged to match the declaration
- Application behavior logic and test suites are not created as part of foundation work
### Orchestrator Dispatch Rules
Resolve foundation nodes before dispatching dependent `layer`, `feature`, or `behavior` nodes.
Recommended ordering:
1. `decision` / `policy` nodes
2. `foundation` nodes
3. `layer` / `feature` / `behavior` nodes
## Dependency Rule
- Allowed: `foundation -> depends_on -> decision`
- Allowed: `foundation -> depends_on -> policy`
- Allowed: `foundation -> depends_on -> foundation`
- Forbidden: `foundation -> depends_on -> layer`
- Forbidden: `foundation -> depends_on -> feature`
- Forbidden: `foundation -> depends_on -> behavior`
Tooling rejects forbidden dependencies.
## Foundation vs. Decision
| Characteristic | `decision` Node | `foundation` Node |
|---|---|---|
| Semantic role | Abstract architectural constraint or rule | Concrete, manifested physical repository state |
| Example title | "Go Backend" | "Go Module Manifestation" |
| Example statement | "The backend application must be written in Go." | "The repository contains a valid go.mod file." |
| Verification style | Constraint checks, linting, architecture checks | Structural shell checks (`test -f`, `grep -q`) |
| Agent action | Constrains downstream implementation choices | Creates and converges baseline scaffolding |
## When to Use a Foundation Node
Create a foundation node when:
- The repository is empty or greenfield and needs physical bootstrap state
- A new language ecosystem must be initialized before behavior implementation
- A monorepo workspace boundary must be declared and verified
- Mandatory directory or manifest structure must exist before application logic
## ID Conventions
Use `FND-*` or `FND-SCOPE-*` identifiers.
Examples:
- `FND-GO-MOD`
- `FND-GO-DIRS`
- `FND-NPM-WORKSPACE`
## Anti-Patterns
- Imperative statements ("Initialize the Go module") instead of declarative state
- Using project test runners as foundation verification
- Mixing domain/application logic into foundation statements
- Depending on `layer`, `feature`, or `behavior` nodes
- Stating "build success" instead of concrete manifested structure
## Examples
### Example 1: Go Module Initialization
```json
{
"id": "FND-GO-MOD",
"type": "foundation",
"title": "Go Module Manifestation",
"statement": "The repository contains a valid Go module identified as 'github.com/oco-adam/todoist-clone', satisfying the Go runtime requirements of the platform.",
"verification": [
"test -f go.mod",
"grep -q 'module github.com/oco-adam/todoist-clone' go.mod"
],
"links": {
"depends_on": ["DEC-PLAT-01"]
},
"metadata": {
"rationale": "Provides the Go module anchor that all subsequent Go source files and packages depend on."
}
}
```
### Example 2: Standard Go Directory Layout
```json
{
"id": "FND-GO-DIRS",
"type": "foundation",
"title": "Standard Go Directory Layout",
"statement": "The repository adheres to the standard Go project layout, providing distinct 'cmd/', 'internal/', and 'pkg/' directories for structural separation of concerns.",
"verification": [
"test -d cmd",
"test -d internal",
"test -d pkg"
],
"links": {
"depends_on": ["FND-GO-MOD", "DEC-ARCH-01"]
}
}
```
### Example 3: NPM Workspace Configuration
```json
{
"id": "FND-NPM-WORKSPACE",
"type": "foundation",
"title": "NPM Workspace Initialization",
"statement": "The project is structured as an NPM workspace containing a 'packages' directory and a root package.json defining the workspace boundary.",
"verification": [
"test -f package.json",
"test -d packages",
"node -e \"const pkg = require('./package.json'); if (!pkg.workspaces || !pkg.workspaces.includes('packages/*')) process.exit(1);\""
],
"links": {
"depends_on": ["DEC-MONOREPO-01"]
}
}
```
### Example 4: Python Project Configuration
```json
{
"id": "FND-PY-PROJECT",
"type": "foundation",
"title": "Python Project Manifestation",
"statement": "The repository contains a valid pyproject.toml with the project name 'my-service' and a src/ layout for package sources.",
"verification": [
"test -f pyproject.toml",
"grep -q 'name = \"my-service\"' pyproject.toml",
"test -d src/my_service"
],
"links": {
"depends_on": ["DEC-PLAT-01"]
}
}
```
---
# Extension Types
Extension types provide finer-grained modelling for graphs that need it. They use the same **contract shape** as foundation, decision, domain, and policy nodes — the `type` value is what distinguishes them.
## Available Extensions
| Type | Purpose | When to Use |
|---|---|---|
| `design_token` | Visual tokens: colors, typography, spacing, shadows | When visual consistency matters and design decisions are load-bearing |
| `ui_contract` | UI component contracts: props, states, variants | When component interfaces need to be specified |
| `api_contract` | REST, GraphQL, or event contracts | When API boundaries need formal specification |
| `data_model` | Database schemas, migrations, invariants | When data structure decisions are load-bearing |
| `artifact` | Content-addressed external artifact | When external assets (Figma tokens, config files) must be pinned |
| `equivalence_contract` | Formal definition of "same system" | When you need an explicit equivalence definition |
| `pipeline` | Manifestation pipeline steps and gates | When the build/deploy process must be specified |
## Schema
All extension types use the same contract shape:
```json
{
"id": "DT-TASKCARD-01",
"type": "design_token",
"title": "Task Card Design Token",
"statement": "Visual specification for task cards on the board.",
"constraints": [
"Card: white background, 1px border-muted, 8px radius, 12px padding",
"Title: 14px semibold",
"Priority badge: top-right, colored dot (critical=red, high=orange, medium=blue, low=gray)"
],
"verification": [
{
"kind": "observation",
"description": "Visual inspection: task cards match spec in all four columns"
}
],
"links": {
"constrains": ["TASKBOARD-02"]
}
}
```
## Design Tokens
Design tokens capture the visual language of the system — colors, typography, spacing, shadows, radii.
```json
{
"type": "design_token",
"title": "Primary Color Palette",
"statement": "Primary interactive color is oklch(59.59% 0.24 275.75).",
"constraints": [
"All interactive elements use the primary palette",
"Primary-hover is oklch(52.85% 0.24 275.75)"
],
"verification": ["CSS audit: no hardcoded colors outside the token system"]
}
```
**Use when:** visual consistency matters and the design system is a load-bearing part of the spec.
## API Contracts
API contracts specify the interface between services or modules.
```json
{
"type": "api_contract",
"title": "Auth API Contract",
"statement": "POST /api/auth/login accepts {email, password} and returns {token, user}.",
"constraints": [
"Returns 401 on invalid credentials",
"Returns 429 after 5 failed attempts in 15 minutes"
],
"verification": [
{
"kind": "http",
"method": "POST",
"url": "http://localhost:3000/api/auth/login",
"expectStatus": 200
}
]
}
```
## Artifacts
Artifact nodes pin external inputs (Figma exports, config files, etc.) by **content hash** so that manifestation can be reproduced later.
```json
{
"id": "ART-DS-01",
"type": "artifact",
"title": "Design Tokens Export (Pinned)",
"statement": "Pinned design token export used as an input to derived nodes.",
"artifact": {
"sha256": "0000000000000000000000000000000000000000000000000000000000000000",
"source": "figma://file/abc123?node-id=...",
"format": "tokens-studio-json"
},
"verification": ["specgraph verify-artifacts --id ART-DS-01"]
}
```
## Pins for `derived_from`
When a node is derived from an artifact, use `links.derived_from` plus `pins` so tooling can detect staleness:
```json
{
"id": "DT-TASKCARD-01",
"type": "design_token",
"title": "Task Card Design Token",
"statement": "Visual specification for task cards on the board.",
"verification": [{ "kind": "observation", "description": "Visual inspection: matches spec" }],
"pins": [{ "id": "ART-DS-01", "sha256": "0000000000000000000000000000000000000000000000000000000000000000" }],
"links": { "derived_from": ["ART-DS-01"] }
}
```
## Equivalence Contracts
An equivalence contract formally declares what "same system" means for this graph.
```json
{
"type": "equivalence_contract",
"title": "System Equivalence Definition",
"statement": "Two manifestations are equivalent if all unit tests pass, all integration tests pass, Lighthouse performance scores exceed thresholds, and all policy nodes are satisfied.",
"constraints": [
"Unit test suite must achieve 100% pass rate",
"Integration tests must cover all behavior nodes",
"Lighthouse FCP < 1.5s, LCP < 2.5s"
],
"verification": [
{ "kind": "command", "command": "go test ./... -run TestSystemEquivalence" },
{ "kind": "command", "command": "python -m pytest tests/integration/test_equivalence.py" },
{ "kind": "command", "command": "pnpm exec lighthouse --preset=perf http://localhost:3000" }
]
}
```
## When to Use Extensions
Extensions follow the same minimality test as core types:
> "If I removed this, could a competent implementing agent make a choice I wouldn't want?"
Start with core types. Add extensions when you discover that core types don't provide enough precision for a specific dimension. For most projects, the seven core types are sufficient.
### Progressive Addition
A typical extension adoption path:
1. **Start** with core types only (`feature`, `layer`, `foundation`, `behavior`, `decision`, `domain`, `policy`)
2. **Add `design_token`** when visual inconsistency between features becomes a problem
3. **Add `api_contract`** when service boundaries need formal specification
4. **Add `data_model`** when database schema decisions are load-bearing
5. **Add `equivalence_contract`** when you need reproducible re-manifestation
---
# Writing Nodes
This guide covers the practical mechanics of writing good spec graph nodes.
## The Universal Structure
All [normative](/docs/reference/normative) nodes (everything except grouping nodes: `feature` and `layer`) follow a pattern:
1. **Declare** what must be true (`statement` or `expectation`)
2. **Constrain** what limits apply (`constraints`)
3. **Verify** how to check it (`verification`)
This maps to the question: "What is true? What must hold? How do we know?"
## Writing Good Statements
The `statement` field (or `expectation` for behaviors) is the core of every node. It should be:
### Specific
```
✅ "Use Clerk as the production authentication provider for OAuth and session management."
❌ "Use a managed authentication service."
```
### Actionable
An implementing agent should be able to determine exactly what to do.
```
✅ "All auth operations go through an AuthProvider interface with authenticate(), validateSession(), revokeSession()."
❌ "Auth should be abstracted properly."
```
### Declarative
State what must be true, not the steps to get there.
```
✅ "Task status transitions follow: backlog → in-progress → review → done."
❌ "First create a status enum, then add a transition validator..."
```
## Writing Good Constraints
Each `constraints` entry is an independent, [normative](/docs/reference/normative) condition that must hold.
The condition's own language carries its temporal semantics — "must hold at all
times" vs "must eventually hold" vs "must hold within N seconds."
### Each Constraint is Independent
```json
"constraints": [
"Interface must define authenticate()",
"Interface must define validateSession()",
"No auth call may bypass the interface"
]
```
Not:
```json
"constraints": [
"Interface must define authenticate() and validateSession() and no call may bypass it"
]
```
### Constraints Are Testable
Each constraint should map to a verifiable condition:
```json
"constraints": ["Email uniqueness is enforced at the database level"]
// → Verifiable by checking the schema has a unique constraint on email
```
## Conditions, Enforcement, and Strictness
The framework separates three orthogonal concerns:
### 1. Condition — what must hold (`constraints` on any node)
The condition text states what is required. It should be specific enough that an
implementor knows exactly what to check. The condition's own language carries its
temporal semantics:
```
"Email must be unique" — strong consistency implied
"Email must be eventually unique" — eventual consistency acceptable
"Email must be unique within 5s of creation" — bounded eventual consistency
```
All of these are [normative](/docs/reference/normative) — they are not suggestions. An implementor must respect
the condition, including its temporal qualifier.
### 2. Enforcement — how it's guaranteed (decision node)
If the enforcement model is a dangerous completeness gap — meaning two implementors
could choose incompatible approaches — capture it as a decision node:
```json
{
"type": "decision",
"id": "DEC-DATA-01",
"category": "architecture",
"title": "Eventual Consistency for User Records",
"statement": "User records use eventual consistency with a convergence window under 5 seconds",
"constraints": [
"A background uniqueness checker runs every 2 seconds",
"Duplicate accounts detected after convergence are merged, not deleted"
],
"verification": [
"Integration test: two concurrent registrations with same email both succeed initially, one is merged within 5s"
],
"links": {
"constrains": ["REG-01"]
},
"metadata": {
"rationale": "Eventual consistency is required to keep write latency bounded under peak registration traffic."
}
}
```
Apply the minimality test: if removing the enforcement choice would let an agent
pick the wrong consistency model, add a decision node.
### 3. Strictness — whether violation blocks manifestation (policy node severity)
For cross-cutting non-functional requirements, policy nodes have `severity`:
- `hard` — violation blocks manifestation
- `soft` — quality target, produces a warning
```json
{
"type": "policy",
"id": "POL-DATA-01",
"severity": "hard",
"title": "Convergence Window Budget",
"statement": "All eventually-consistent operations must converge within 5 seconds under normal load",
"constraints": ["Measured at p99 under 100 concurrent writes per second"],
"verification": [
{ "kind": "command", "command": "python -m pytest tests/consistency/test_convergence.py --maxfail=1" }
],
"links": { "constrains": ["REG-01"] }
}
```
Severity applies to the policy node as a whole, not to individual `constraints`
field entries. Field-level `constraints` entries are always [normative](/docs/reference/normative).
:::info Disambiguation: `constraints` field vs policy nodes vs `constrains` edges
The spec graph uses related but distinct concepts:
| Concept | What it is | Scope |
|---------|-----------|-------|
| `constraints` field | Array of [normative](/docs/reference/normative) conditions on a single node | Narrows THIS node's `expectation` or `statement` |
| Policy node (`type: "policy"`) | A standalone node for cross-cutting NFRs | Affects OTHER nodes via `constrains` edges; has `severity` (hard/soft) |
| `constrains` edge | A graph relationship | Declares that the source node narrows implementation choices for the target |
**Decision rule:**
- Condition specific to one node → `constraints` field entry on that node
- Cross-cutting requirement affecting multiple nodes → policy node with `constrains` edges
- Expressing that one node limits another → `constrains` edge
:::
## Writing Good Verification
Verification criteria produce **pass/fail** results. Prefer executable checks:
### Executable (Best)
```json
"verification": ["pytest tests/auth/test_login_form.py -k AUTH_01"]
```
```json
"verification": [
{ "kind": "command", "command": "cargo test auth::provider::contract -- --exact" },
{ "kind": "command", "command": "go test ./internal/auth/provider -run TestAuthProviderContract" }
]
```
### Observable (Acceptable)
```json
"verification": [
{
"kind": "observation",
"description": "Visual inspection: task cards match spec in all four columns"
}
]
```
### Manual (Last Resort)
```json
"verification": [
{
"kind": "manual",
"steps": [
"Open the login page in Chrome",
"Submit invalid email format",
"Verify error appears below email field"
],
"expected": "Inline error message visible without page reload"
}
]
```
## Using Metadata
The `metadata` field is for **non-executable context** — information that helps humans and agents understand a node's background:
```json
"metadata": {
"rationale": "Enables deterministic testing of auth flows.",
"notes": "Consider adding a MockProvider for unit tests.",
"owner": "auth-team",
"tags": ["auth", "architecture"]
}
```
In most node types, metadata is optional. Some type-specific rules may require metadata fields (for example, `decision` nodes require `metadata.rationale`).
**Key rule:** if something in metadata should be enforced as behavior, it belongs in `statement` or `constraints`, not metadata.
## Status Is Workflow-Derived
**Status is not stored in nodes.** A node's lifecycle state is determined by git context:
- **Draft** — the node exists on a feature branch that hasn't been reviewed yet
- **Proposed** — the node's branch has an open pull request
- **Approved** — the PR merged to the main branch
- **Deprecated** — a `supersedes` edge from another node points to it
- **Rejected** — the PR was closed without merging
This keeps nodes purely declarative and avoids the **mutation-after-approval paradox** — where marking a node as "approved" requires editing a file that was already reviewed, creating a tautological commit. By deriving status from git workflow, the spec graph describes *what the system is*, not *where each node is in a review process*.
---
# Atomicity Rules
Every node in the Spec Graph must be **atomic** — expressing exactly one decision, one behavior, one concept, or one constraint.
## The General Rule
> **Each node MUST express ONE decision / ONE contract / ONE constraint, with ONE verification intent.**
If it contains "and" across multiple decisions, split it.
## Type-Specific Atomicity
### Behavior Nodes
The ONE behavior rule:
> ONE trigger → ONE behavior → ONE outcome
```
✅ "Login page renders email and password input fields with a submit button"
(One observable state)
❌ "Login page renders a form and validates input and redirects on success"
(Three behaviors — split into form display, validation, redirect)
```
### Decision Nodes
**ONE decision per node:**
```
✅ "Use Clerk for authentication" (one technology choice)
✅ "All auth goes through an AuthProvider interface" (one pattern)
❌ "Use Clerk for auth, Next.js for frontend, and Convex for backend"
(Three decisions — split into three nodes)
```
### Domain Nodes
**ONE concept per node:**
```
✅ "User Account: A registered entity that can authenticate and own resources"
✅ "Task: A unit of work that moves through a Kanban workflow"
❌ "Users and Projects: Users belong to projects and have roles"
(Two concepts — split into User and Project nodes)
```
### Constraint Nodes
**ONE measurable requirement per node:**
```
✅ "All pages must reach FCP within 1.5s on 4G"
✅ "All user data must be encrypted at rest with AES-256"
❌ "Pages must load fast and data must be encrypted"
(Two requirements — split into performance and security nodes)
```
## Size Guidelines
Atomicity is enforced partly through size limits. These are guidelines, not hard limits:
| Field | Target Maximum |
|---|---|
| `statement` / `expectation` | 240 characters |
| Each `constraint` entry | 140 characters |
| Each `verification` entry (string) | 180 characters |
| `metadata.rationale` | No limit (but keep concise) |
If you're hitting these limits, the node is probably trying to say too much. Split it.
## The Split Test
When in doubt about whether to split:
1. **Can you write one verification for it?** If you need two unrelated checks, split.
2. **Could you change one half without changing the other?** If yes, they're independent — split.
3. **Does the title need "and"?** If the title is "X and Y," it's probably two nodes.
## Why Atomicity Matters
### Reviewability
Small nodes are easy to review. A reviewer can assess a single decision in seconds. A compound node requires understanding multiple decisions and their interactions.
### Composability
Atomic nodes combine without hidden coupling. If "Use Clerk" and "Abstract auth provider" are separate nodes, you can change the Clerk decision without touching the abstraction pattern.
### Traceability
When a verification fails, an atomic node points to exactly one thing that's wrong. A compound node requires investigation to determine which part failed.
### Edge Precision
Edges between atomic nodes are precise. "`AUTH-01` depends on `DEC-AUTH-01`" is clear. "`AUTH-01` depends on `DEC-EVERYTHING-01`" (which contains five decisions) is ambiguous about which decision matters.
---
# When to Add Nodes
Not every system needs all node types. The Spec Graph grows organically in response to manifestation ambiguity — you add nodes when the [minimality test](/docs/theory/minimality) demands it.
## The Minimality Test
For any proposed node:
> "If I removed this, could a competent implementing agent make a choice I wouldn't want?"
- **If yes** → the node is load-bearing, add it
- **If no** → the node is redundant, don't add it
## Triggers by Node Type
### Add Behavior Nodes When...
- There's an observable user-facing action to specify
- A feature needs testable acceptance criteria
- The system must respond to a specific trigger
**Always start here.** Behaviors are the foundation of every spec graph.
### Add Decision Nodes When...
| Scenario | Category |
|---|---|
| Two implementations could use incompatible architectures | `architecture` |
| A technology choice affects how behaviors are implemented | `stack` |
| A specific coding pattern is required for testability or quality | `pattern` |
| Modules need a stable interface contract | `interface` |
**Key signal:** you find yourself writing the same guidance in multiple PR reviews, agent instructions, or CLAUDE.md files. If you're repeating it, it should be a node.
### Add Layer Nodes When...
- Two or more features depend on the same infrastructure capability
- Shared architectural guidance is being duplicated across feature namespaces
- Cross-feature platform concerns (security baseline, data access layer, observability) need explicit grouping
**Key signal:** the same infrastructure decisions/policies are repeated across multiple vertical slices. Promote them into a `layer` and have features depend on it.
### Add Foundation Nodes When...
- The repository is empty or greenfield and agents need a physical bootstrapping anchor
- Module manifests, workspace configurations, or mandatory directory structures must exist before any application code
- Agents have previously hallucinated scaffolding in the wrong language or ecosystem
- Multiple decision nodes reference physical structure that doesn't exist yet
**Key signal:** you dispatched a decision node to an empty repo and the agent built the wrong ecosystem scaffolding. The physical baseline needs to be declared, not inferred.
### Add Domain Nodes When...
- A business term is ambiguous without explicit definition
- Multiple behaviors reference the same concept differently
- Domain rules constrain valid implementations
- The implementing agent might confuse two similar concepts
**Key signal:** you say "by 'user' I mean..." more than once.
### Add Policy Nodes When...
- Performance, security, or accessibility must be measured
- A non-functional requirement cuts across multiple features
- Cost budgets must be enforced
- Reliability targets need explicit verification
**Key signal:** the quality attribute matters enough that you'd reject an implementation that ignores it. Express it as a policy node with appropriate severity.
### Add Extension Types When...
Core types don't provide enough precision:
| Extension | Trigger |
|---|---|
| `design_token` | Visual inconsistency between features is unacceptable |
| `api_contract` | Service boundaries need formal specification |
| `data_model` | Database schema decisions are load-bearing |
| `equivalence_contract` | You need reproducible re-manifestation |
| `pipeline` | Build/deploy process must be explicitly specified |
## The Growth Pattern
A typical project's spec graph grows in this order:
```
1. Early stage: behavior nodes only
2. Tech decisions: + decision nodes (stack category)
3. Bootstrapping: + foundation nodes (when greenfield)
4. Architecture: + decision nodes (architecture/pattern categories)
5. Domain model: + domain nodes
6. Quality gates: + policy nodes
7. Refinement: + extension types as needed
```
## Don't Pre-Populate
A common mistake is trying to fill in every node type from the start. This leads to non-minimal graphs with speculative nodes that don't reduce actual ambiguity.
Instead:
1. Start with behaviors
2. Try to manifest (or mentally simulate manifestation)
3. When the agent would make a wrong choice → add the node that prevents it
4. Repeat
This keeps the graph minimal and every node justified.
## Removing Nodes
Nodes should also be removed when they stop being load-bearing. Common reasons:
- The decision became so standard it's a framework convention (no ambiguity)
- The constraint was temporary (e.g., a cost limit during a promotional period)
- A domain concept was consolidated with another
- A node was superseded by a new node
Add a `supersedes` edge from the replacement node if applicable. The old node's deprecated state is inferred from the existence of this edge.
---
# Directory Layout
The Spec Graph uses a file-per-node storage model. Each node is a self-contained JSON file, organized by type.
## Recommended Layout
```
specgraph/
graph.json # Index: version, node list
nodes/
features/
AUTH.json
TASKBOARD.json
layers/
PLATFORM.json
DATA.json
behaviors/
AUTH-01.json
AUTH-02.json
TASKBOARD-01.json
decisions/
DEC-AUTH-01.json
DEC-AUTH-02.json
DEC-TB-01.json
domains/
DOM-USER-01.json
DOM-TASK-01.json
policies/
POL-PERF-01.json
POL-TB-PERF-01.json
```
## Conventions
### File Naming
Node files are named after their ID:
| Node ID | File Path |
|---|---|
| `AUTH` | `nodes/features/AUTH.json` |
| `PLATFORM` | `nodes/layers/PLATFORM.json` |
| `AUTH-01` | `nodes/behaviors/AUTH-01.json` |
| `DEC-AUTH-01` | `nodes/decisions/DEC-AUTH-01.json` |
| `DOM-USER-01` | `nodes/domains/DOM-USER-01.json` |
| `POL-PERF-01` | `nodes/policies/POL-PERF-01.json` |
### Extension Type Directories
Extension types get their own directories:
```
nodes/
design_tokens/
DT-TASKCARD-01.json
api_contracts/
API-AUTH-01.json
data_models/
DM-USER-01.json
```
### The Index File
`graph.json` must reference every node and its path:
```json
{
"specgraphVersion": "1.0.0",
"nodes": [
{ "id": "AUTH", "path": "nodes/features/AUTH.json" },
{ "id": "PLATFORM", "path": "nodes/layers/PLATFORM.json" },
{ "id": "AUTH-01", "path": "nodes/behaviors/AUTH-01.json" },
{ "id": "DEC-AUTH-01", "path": "nodes/decisions/DEC-AUTH-01.json" }
]
}
```
Paths are relative to `graph.json`. The path in the index is the **canonical** location — tooling uses this to find node files.
## Why File-Per-Node?
### Atomic PRs
Changing a node and its relationships happens in one file. A PR that adds `DEC-AUTH-01.json` is self-contained and easy to review.
### Reduced Merge Conflicts
In a single-file spec, two people editing different nodes create conflicts. With file-per-node, changes to different nodes never conflict.
### Local Reasoning
When reviewing a node file, you see everything about that node: its statement, constraints, verification, and edges. No need to cross-reference a separate file.
### Tooling Simplicity
Tooling can load, validate, and process individual nodes without parsing the entire graph.
## Optional Files
### `graph.index.json`
A generated flattened edge list for fast traversal. This is a **derived artifact** — the source of truth is always the `links` field in each node file.
```json
{
"specgraphVersion": "1.0.0",
"generatedFrom": { "graphPath": "graph.json" },
"edges": [
{ "from": "AUTH-01", "type": "implements", "to": "DOM-USER-01" },
{ "from": "AUTH-01", "type": "depends_on", "to": "DEC-AUTH-01" }
]
}
```
### `manifest.lock.json`
A lockfile capturing the exact graph, artifacts, and toolchain used for a manifestation. Enables reproducible re-manifestation.
## Scaling
For large graphs (100+ nodes), consider organizing by feature:
```
specgraph/
graph.json
nodes/
auth/
features/AUTH.json
behaviors/AUTH-01.json
decisions/DEC-AUTH-01.json
taskboard/
features/TASKBOARD.json
behaviors/TASKBOARD-01.json
```
The index file still lists all nodes — the directory structure is a convention, not a constraint.
---
# Manifestation
**Manifestation** is the process of going from specification to running system. It encompasses all intermediate steps: planning, designing, coding, building, testing, and deploying.
In the intended workflow, the same AI-agent ecosystem both:
- maintains the spec graph (through graph-aware tools such as MCP), and
- manifests the system from that graph.
Humans keep strategic control; agents perform structured execution.
```
Spec Graph → [Manifest] → Running System
G A M
```
Where:
- **G** = the spec graph
- **A** = the implementing agent (or team of agents)
- **M** = the manifested system
## The Manifestation Property
When a spec graph is [complete](/docs/theory/completeness) and the implementing agent is capable:
```
Complete(G) ∧ Capable(A) → Predictable(Manifest(G, A))
```
This means: the same graph, processed by the same (or equivalent) agent, always produces logically equivalent systems. Perfect determinism — identical output every time — is the idealistic limit; predictability across all specified dimensions is the practical achievement.
## What "Capable" Means
A capable implementing agent can:
1. **Parse** the spec graph — load `graph.json`, resolve all node references
2. **Traverse** the graph — follow edges, resolve dependencies
3. **Apply guidance** — implement according to decision nodes
4. **Respect constraints** — verify non-functional requirements
5. **Verify outcomes** — run each node's verification criteria
## Conceptual Phases
The manifestation process has three conceptual phases:
| Phase | Input | Output | Activity |
|---|---|---|---|
| **Orient** | All nodes | System understanding | Read and comprehend the full graph |
| **Scaffold** | Decision + domain nodes | Infrastructure | Create architecture, abstractions, shared code |
| **Implement** | Behavior nodes + context | Working features | Build each behavior with full graph context |
These phases are described in detail in [Orient, Scaffold, Implement](/docs/manifestation/orient-scaffold-implement).
## Not a Rigid Pipeline
The three phases are conceptual, not prescriptive steps. A capable agent might:
- Process phases iteratively rather than sequentially
- Interleave scaffold and implement work
- Revisit earlier phases when new information emerges
What matters is the **outcome**: the manifested system satisfies all nodes in the graph. The phases describe the natural ordering of information, not a mandatory workflow.
## Manifestation vs. Task Derivation
The spec graph is a specification, not a task list. The mapping from graph to tasks is determined by the agent's planning phase:
- Each approved behavior node typically yields one implementation task
- Decision nodes may yield infrastructure/setup tasks
- Constraint nodes yield verification tasks
- Domain nodes inform all tasks but may not generate their own
The specific task derivation strategy is an agent concern, not a spec graph concern. Different agents may derive different task orderings while producing equivalent results.
---
# Orient, Scaffold, Implement
The manifestation process follows three conceptual phases, each consuming a different slice of the graph.
## Phase 1: Orient
**Purpose:** Build a complete understanding of the system before writing any code.
**Reads:** All node types — domain, decision, foundation, policy, behavior, feature, layer
**Produces:** Mental model of the system
During orientation, the implementing agent:
1. Reads all **domain** nodes to understand the business vocabulary
2. Reads all **decision** nodes to understand architectural and technology choices
3. Reads all **foundation** nodes to understand what physical scaffolding exists
4. Reads all **policy** nodes to understand non-functional boundaries
5. Reads all **behavior** nodes to understand what the system does
6. Traverses edges to understand how everything relates
This phase is **read-only** — no code is written. The goal is to understand the full system before making any implementation decisions.
### Why Orient First?
Without orientation, the agent makes early implementation choices that may conflict with nodes it hasn't read yet. For example:
- Building a component before knowing the design token it should use
- Choosing a data structure before reading the domain model
- Implementing a feature without knowing the performance policy
Orientation prevents these "premature implementation" errors.
## Phase 2: Scaffold
**Purpose:** Create the architectural infrastructure that behaviors depend on.
**Reads:** Foundation nodes, decision nodes (architecture, stack, pattern, interface), domain nodes
**Produces:** Abstractions, interfaces, shared infrastructure, configuration
During scaffolding, the agent:
1. Processes decision nodes in dependency order (topological sort on `depends_on` edges)
2. Creates abstract interfaces from `architecture` decisions
3. Configures technology choices from `stack` decisions
4. Sets up implementation patterns from `pattern` decisions
5. Creates shared types and models from domain nodes
```mermaid
graph TD
D1[DEC-AUTH-02
Clerk Provider
stack] --> D2[DEC-AUTH-01
Auth Provider Interface
architecture]
D2 --> S[Scaffold:
AuthProvider interface,
ClerkProvider,
DeterministicProvider]
```
### Dependency Order
Scaffolding respects the `depends_on` graph:
1. Technology choices first (they determine what's available)
2. Architectural patterns next (they define structure)
3. Implementation patterns last (they guide coding style)
If `DEC-AUTH-01` (AuthProvider interface) depends on `DEC-AUTH-02` (Clerk), then Clerk must be configured before the interface is created.
## Phase 3: Implement
**Purpose:** Build each behavior using the full context assembled from the graph.
**Reads:** Behavior nodes + all related nodes via edges
**Produces:** Working, verified features
For each behavior node:
1. **Resolve edges** — what decisions guide it, what constraints limit it, what domain concepts it implements
2. **Assemble context** — gather all related nodes into a unified context
3. **Implement** — build the behavior following all guidance
4. **Verify** — run the behavior's verification criteria
5. **Check policies** — verify related policy nodes are satisfied
### Processing Order
Behaviors are processed in dependency order:
```mermaid
graph LR
B1[AUTH-01
Login Form] --> B2[AUTH-02
Email Validation]
B1 --> B3[AUTH-03
Password Validation]
B1 --> B4[AUTH-04
Login Redirect]
B4 --> B5[AUTH-05
Session Persistence]
```
`AUTH-01` is implemented first because `AUTH-02`, `AUTH-03`, and `AUTH-04` depend on it.
## The Full Picture
```
Phase 1: ORIENT
Read all nodes → Build system understanding
(No code written)
Phase 2: SCAFFOLD
Process decision + domain nodes → Create infrastructure
(Abstractions, interfaces, config, shared code)
Phase 3: IMPLEMENT
For each behavior (in dependency order):
1. Assemble context from edges
2. Implement with full guidance
3. Verify behavior
4. Check policies
```
---
# Context Assembly
When manifesting a single behavior, the agent assembles its full **context** from the graph. Context assembly is the mechanism that makes the spec graph more than a collection of independent nodes.
## How It Works
Starting from a behavior node, the agent follows all outbound edges to gather related nodes:
```
Behavior: AUTH-01 "Login Form Display"
│
├─ depends_on: DEC-AUTH-01 "Abstract Auth Provider Interface"
│ → "All auth goes through AuthProvider interface..."
│ └─ depends_on: DEC-AUTH-02 "Clerk Authentication Provider"
│ → "Use Clerk for OAuth and session management..."
│
├─ implements: DOM-USER-01 "User Account"
│ → "A registered entity that can authenticate..."
│ → constraints: "Email unique, verified before access"
│
└─ (via inverse) POL-PERF-01 "Page Load Budget"
→ "FCP within 1.5s on 4G..."
```
The assembled context gives the agent **everything it needs** to implement this behavior correctly — without searching through external documents, agent instructions, or tech stack profiles.
## Context Depth
Context assembly follows edges to a configurable depth:
- **Depth 1**: Direct edges from the behavior node
- **Depth 2**: Edges from depth-1 nodes (e.g., the tech stack that an architectural decision depends on)
- **Full**: Transitive closure of all reachable nodes
In practice, depth 2 is usually sufficient. Going deeper can include nodes that are contextually relevant but not directly actionable for this behavior.
## Inverse Edges
Some context comes from **inverse edges** — nodes that point TO this behavior:
- A policy node with `"constrains": ["AUTH-01"]` is relevant context for AUTH-01, even though AUTH-01 doesn't link to it
- A grouping node (`feature` or `layer`) with `"contains": ["AUTH-01"]` provides namespace context
- A policy or decision that constrains an ancestor feature/layer/domain of AUTH-01 is inherited by AUTH-01 via `contains` propagation
- Layer-originated guidance can also be inherited when AUTH-01 (or its parent grouping node) transitively depends on a layer
Tooling computes inverse edges from the stored forward edges and includes them in context assembly.
## Decision Metadata in Context
For decision nodes, `metadata.rationale` is required and must be included in assembled context.
`metadata.rejected_alternatives` is optional but should be included whenever present so agents can avoid previously rejected approaches.
## Context as a Document
The assembled context for a behavior can be rendered as a structured document:
```markdown
# Context for AUTH-01: Login Form Display
## Behavior
Login page renders email and password input fields with a submit button.
Constraints: Password field must mask input characters.
## Architectural Decisions
- DEC-AUTH-01: All auth operations go through an AuthProvider interface.
Constraints: Interface must define authenticate(), validateSession(), revokeSession().
## Technology Decisions
- DEC-AUTH-02: Use Clerk as the production authentication provider.
Constraints: Use Clerk's middleware for route protection.
## Domain Concepts
- DOM-USER-01: User Account — a registered entity that can authenticate,
own resources, and have role-based permissions.
## Policies
- POL-PERF-01: All pages must reach FCP within 1.5s on 4G. (severity: hard)
## Verification
pytest tests/auth/test_login_form.py -k AUTH_01
```
This document is what the implementing agent "sees" when building AUTH-01. It includes direct edges, inverse edges, and effective propagated guidance.
## Why Context Assembly Matters
Without context assembly, the agent must independently discover that:
- Auth should go through an abstract interface (from a decision node)
- Clerk is the auth provider (from another decision node)
- "User" means a registered entity with specific properties (from a domain node)
- The page must load in under 1.5s (from a policy node)
If any of these are missed, the implementation may diverge from the designer's intent. Context assembly ensures completeness at the per-behavior level.
---
# Verification
Verification is the **spine of predictability**. Every [normative](/docs/reference/normative) node must be verifiable, and verification is what makes [equivalence](/docs/theory/equivalence) testable.
## Verification at Every Level
Verification happens at three levels during manifestation:
### Node-Level Verification
Each node has its own verification criteria. After implementing a behavior or scaffolding a decision, the agent runs the node's verification:
```json
// Behavior node
"verification": "pnpm test -- --grep AUTH-01"
// Decision node
"verification": [
"cargo test auth::provider::contract -- --exact",
"go test ./internal/auth/provider -run TestAuthProviderContract"
]
```
### Policy-Level Verification
After implementing behaviors, policy nodes that `constrain` those behaviors are verified:
```json
// Policy node
"verification": [
{
"kind": "command",
"command": "python -m pytest tests/policies/test_perf_budget.py -k POL_PERF_01"
}
]
```
### Graph-Level Verification
After all nodes are manifested, the full graph is verified:
1. All behavior node verifications pass
2. All policy node verifications pass
3. All decision node verifications pass
4. If an `equivalence_contract` exists, its criteria are met
## Preflight and Quality Gates
Verification answers: "Does the manifested system satisfy the spec graph?"
In practice, manifestation pipelines often include two additional operational stages that are adjacent to verification but conceptually distinct:
### Preflight Checks (Environment Readiness)
Preflight checks answer: "Is this workspace/toolchain healthy enough to start work?"
Examples:
- `pnpm install --frozen-lockfile`
- `cargo check --workspace`
- `go test ./... -run TestSmoke`
- `python -m pip check`
These are **agent/tooling concerns**, not properties of the manifested system, so they typically live outside the spec graph.
### Quality Gates (Project Quality Bar)
Quality gates answer: "Does the code meet the project's quality standard before we ship/review it?"
In Spec Graph terms, the **[normative](/docs/reference/normative)** quality bar should be expressed as:
- `policy` nodes (with `severity: hard|soft` and explicit `verification` commands), and/or
- an `equivalence_contract` node that aggregates the definition of "done".
The **execution strategy** (fast vs slow ordering, fail-fast behavior, retries, timeouts, parallelism) belongs to the agent/tooling layer.
### Typical Flow
1. Run preflight checks (tooling)
2. Implement changes
3. Run fast quality gates (lint/typecheck/format)
4. Run node + policy verification
5. Run slow gates / equivalence contract (tests/build/perf/security)
## Verification Types
The schema supports several verification formats:
### Command
The most common type — a shell command that returns exit code 0 on success:
```json
{
"kind": "command",
"command": "./tools/verify-auth-contract",
"timeoutSeconds": 60
}
```
### HTTP
For API-level verification:
```json
{
"kind": "http",
"method": "POST",
"url": "http://localhost:3000/api/auth/login",
"expectStatus": 200
}
```
### Manual
When automated verification isn't practical:
```json
{
"kind": "manual",
"steps": [
"Open Chrome DevTools Performance tab",
"Record while scrolling through all columns",
"Verify no frames exceed 16ms"
],
"expected": "All frames render within 16ms budget"
}
```
### Observation
A simpler manual check — visual inspection or qualitative assessment:
```json
{
"kind": "observation",
"description": "Visual inspection: task cards match spec in all four columns"
}
```
### Policy
Reference to an external policy or rule:
```json
{
"kind": "policy",
"ruleId": "WCAG-2.1-AA",
"description": "All interactive elements are keyboard accessible"
}
```
## Verification and Determinism
Verification is the mechanism by which equivalence is established. If every node's verification passes for both manifestation M₁ and M₂, the systems are equivalent under the graph's equivalence relation.
This means good verification criteria are crucial:
- **Specific**: test exactly what the node specifies
- **Deterministic**: produce the same result for the same system state
- **Independent**: don't depend on execution order or external services (where possible)
- **Fast**: encourage frequent re-verification
## The Verification Chain
Verification flows through the graph:
```mermaid
graph TD
B[AUTH-01
pnpm test -- --grep AUTH-01] --> N[Node-level: ✓]
P[POL-PERF-01
python -m pytest ...] --> PL[Policy-level: ✓]
D[DEC-AUTH-01
go test ./internal/auth/provider ...] --> DL[Decision-level: ✓]
N --> G[Graph-level: All pass]
PL --> G
DL --> G
```
When all three levels pass, the manifestation is verified.
---
# MCP Server (`@specgraph/mcp`)
Spec Graph is designed to be **written and consumed primarily by AI agents**.
Humans should still understand the file format and graph mechanics, but day-to-day authoring is best done through agent tooling.
The recommended interface is the Spec Graph MCP server: [`@specgraph/mcp`](https://www.npmjs.com/package/@specgraph/mcp).
## Why MCP?
With MCP, your coding agent can:
- initialize a new spec graph
- validate graph + nodes
- query nodes/edges/subgraphs
- perform safe graph writes (node/edge add/update/remove)
This gives you structured, graph-aware edits instead of ad-hoc JSON file manipulation.
## Run the Server
```bash
npx @specgraph/mcp --repo-dir /path/to/repo
```
`--repo-dir` should be the repository root that contains your `specgraph/` directory.
## Configure in an MCP Client
Example MCP config:
```json
{
"mcpServers": {
"specgraph": {
"command": "npx",
"args": ["@specgraph/mcp", "--repo-dir", "."]
}
}
}
```
## LLM Context Files
For agent bootstrapping and retrieval workflows, the site publishes both summary and full-context text files:
- [llms.txt](https://oco-adam.github.io/specgraph/llms.txt) — concise overview
- [llms-full.txt](https://oco-adam.github.io/specgraph/llms-full.txt) — complete docs bundle
See [LLM Context Files](/docs/reference/llm-context-files) for usage details.
## Tool Surface (`@specgraph/mcp@0.5.x`)
### Shared Definitions
Most tools use these common field definitions:
- `directory` (optional string): spec graph directory relative to `--repo-dir` (defaults to `specgraph`)
- `node_id`, `feature_id`, `group_id`, `source`, `target`: node IDs with pattern `^[A-Z][A-Z0-9-]{0,79}$`
- `edge_type`: one of `contains`, `depends_on`, `constrains`, `implements`, `derived_from`, `verified_by`, `supersedes`
`add_node` and `update_node` accept `node` as a typed union:
- `feature` and `layer` require `id`, `type`, `title`, `description`
- `behavior` requires `id`, `type`, `title`, `expectation`, `verification`
- behavior authoring convention: include `constraints` explicitly; use `[]` if none apply (schema still allows omission)
- contract types (`foundation`, `decision`, `domain`, `policy`, `design_token`, `ui_contract`, `api_contract`, `data_model`, `artifact`, `equivalence_contract`, `pipeline`) require `id`, `type`, `title`, `statement`, `verification`
- `decision` additionally requires `category` and `metadata.rationale`
- `policy` additionally requires `severity`
Optional node fields include `links`, `metadata`, and type-specific fields (for example `category`, `severity`, `pins`, `artifact`, `constraints`).
### Initialization and Validation
#### `init_specgraph`
Creates `graph.json` and optionally a root feature node.
Input:
```json
{
"directory": "specgraph",
"specgraph_version": "1.0.0",
"root_feature": {
"id": "ROOT",
"title": "Root Feature",
"description": "Top-level feature for this spec graph."
}
}
```
#### `validate_specgraph`
Validates graph + nodes against schemas and structural rules.
Input:
```json
{
"directory": "specgraph"
}
```
Checks include:
- missing targets
- self-references
- `depends_on` cycles
- dependency inversion guard (`layer -> depends_on -> feature` is invalid)
- foundation dependency guard (`foundation -> depends_on -> layer/feature/behavior` is invalid)
- propagated layer decision ambiguity detection
### Query Tools
#### `list_nodes`
Lists node summaries (`id`, `type`, `title`, `status`) plus count.
Input:
```json
{
"directory": "specgraph"
}
```
#### `get_node`
Returns the full JSON object for one node.
Input:
```json
{
"directory": "specgraph",
"node_id": "AUTH-01"
}
```
#### `get_upstream_context` (recommended)
Returns all upstream nodes that influence a target node, with reason labels.
Input:
```json
{
"directory": "specgraph",
"node_id": "AUTH-01"
}
```
Includes:
- transitive `depends_on` nodes
- direct and inherited constraining nodes (via `contains` propagation)
- `contains` ancestors
- direct `implements`/`verified_by`/`derived_from` references
- inbound `supersedes` references
Compatibility alias: `get_affecting_nodes`.
#### `list_dependencies`
Lists direct `depends_on` dependencies for a node.
Input:
```json
{
"directory": "specgraph",
"node_id": "AUTH-01"
}
```
#### `list_dependencies_full`
Returns transitive `depends_on` closure with explicit [normative](/docs/reference/normative) layer-propagated guidance and informational-only non-layer dependency context.
Input:
```json
{
"directory": "specgraph",
"node_id": "AUTH-01"
}
```
#### `get_effective_constraints`
Computes effective [normative](/docs/reference/normative) guidance for a target node:
- direct/inherited `constrains` via `contains` ancestry
- transitive layer-originated propagation over `depends_on`
- propagated decision ambiguity reporting
Input:
```json
{
"directory": "specgraph",
"node_id": "AUTH-01"
}
```
#### `get_group_subgraph`
Returns a grouping node (`feature` or `layer`) plus all reachable `contains` descendants.
Input:
```json
{
"directory": "specgraph",
"group_id": "PLATFORM"
}
```
#### `get_feature_subgraph`
Backward-compatible feature-only alias for group subgraph retrieval.
Input:
```json
{
"directory": "specgraph",
"feature_id": "AUTH"
}
```
#### `list_edges`
Lists all graph edges as `{source, target, edge_type}`.
Input:
```json
{
"directory": "specgraph"
}
```
#### `search_nodes`
Fuzzy-searches on `id`, `title`, core text fields, and metadata context such as rationale.
Input:
```json
{
"directory": "specgraph",
"query": "token refresh"
}
```
### Write Tools
All writes are validated against canonical Spec Graph JSON Schemas before persistence.
#### `add_node`
Adds a new node. Fails if ID already exists.
Input example:
```json
{
"directory": "specgraph",
"node": {
"id": "AUTH-01",
"type": "behavior",
"title": "Reject invalid credentials",
"expectation": "When credentials are invalid, login is rejected with a generic error.",
"constraints": ["Response does not reveal whether username exists"],
"verification": "pytest tests/auth/test_reject_invalid_credentials.py -k AUTH_01"
}
}
```
#### `update_node`
Replaces an existing node.
Important: this is full replacement, not partial patch. Send the complete node object, including all required fields for that node type.
Input example:
```json
{
"directory": "specgraph",
"node": {
"id": "AUTH-01",
"type": "behavior",
"title": "Reject invalid credentials",
"expectation": "When credentials are invalid, login is rejected with a generic error and no user detail leakage.",
"verification": "pytest tests/auth/test_reject_invalid_credentials.py -k AUTH_01",
"constraints": ["Response does not reveal whether username exists"]
}
}
```
#### `remove_node`
Removes a node and scrubs inbound references from other node link arrays.
Input:
```json
{
"directory": "specgraph",
"node_id": "AUTH-01"
}
```
#### `add_edge`
Adds one typed edge from `source` to `target`.
Input:
```json
{
"directory": "specgraph",
"source": "AUTH",
"target": "AUTH-01",
"edge_type": "contains"
}
```
#### `remove_edge`
Removes one typed edge from `source` to `target`.
Input:
```json
{
"directory": "specgraph",
"source": "AUTH",
"target": "AUTH-01",
"edge_type": "contains"
}
```
### Backward Compatibility Note
As of `0.3.x`, the server exposes explicit tools listed above.
- removed: `query_specgraph` (operation-dispatch wrapper)
- removed: `write_specgraph` (operation-dispatch wrapper)
## Recommended Workflow
1. Ask your agent to initialize or update the graph through MCP tools.
2. Ask your agent to run `validate_specgraph` after each graph change.
3. Keep manual JSON edits for debugging/learning, not primary authoring.
## Schema Version and Overrides
By default, the MCP package uses bundled canonical schemas.
For advanced cases, it supports environment overrides:
- `SPECGRAPH_MCP_SCHEMA_DIR`
- `SPECGRAPH_MCP_SCHEMA_BASE_URL`
- `SPECGRAPH_MCP_GRAPH_SCHEMA`
- `SPECGRAPH_MCP_NODE_SCHEMA`
---
# Getting Started
This guide walks you through creating your first spec graph from scratch.
:::important AI-Agent-First Workflow
Spec Graphs are intended to be authored and consumed by AI agents. Manual editing is still valuable for learning the mechanics and debugging, but the recommended operational path is to use an agent via the [MCP server guide](/docs/guides/mcp-server).
:::
## Recommended Path (Production)
Use an AI agent with the Spec Graph MCP server:
1. Configure your MCP client to run `npx @specgraph/mcp --repo-dir .`
2. Ask the agent to call `init_specgraph`
3. Ask the agent to add/update nodes with `add_node` and `update_node`
4. Ask the agent to run `validate_specgraph` after each change
Then use the manual steps below to understand exactly what the agent is generating.
## Prerequisites
- A directory for your project's spec graph
- A text editor with JSON support
- (Optional) `ajv` or another JSON Schema validator
## Step 1: Create the Directory Structure
```bash
mkdir -p specgraph/nodes/features specgraph/nodes/layers specgraph/nodes/foundations specgraph/nodes/behaviors
```
## Step 2: Create Your First Feature
Create `specgraph/nodes/features/TODO.json`:
```json
{
"$schema": "https://oco-adam.github.io/specgraph/schemas/node.schema.json",
"id": "TODO",
"type": "feature",
"title": "Todo List",
"description": "A simple todo list with add, complete, and delete operations",
"links": {
"contains": ["TODO-01", "TODO-02", "TODO-03"]
}
}
```
## Step 3: Add Behavior Nodes
Create `specgraph/nodes/behaviors/TODO-01.json`:
```json
{
"$schema": "https://oco-adam.github.io/specgraph/schemas/node.schema.json",
"id": "TODO-01",
"type": "behavior",
"title": "Add Todo Item",
"expectation": "When user types text into the input field and presses Enter, a new todo item appears in the list",
"constraints": ["Input field is cleared after adding"],
"verification": "npm test -- --grep TODO-01"
}
```
Create `specgraph/nodes/behaviors/TODO-02.json`:
```json
{
"$schema": "https://oco-adam.github.io/specgraph/schemas/node.schema.json",
"id": "TODO-02",
"type": "behavior",
"title": "Complete Todo Item",
"expectation": "When user clicks the checkbox next to a todo item, it is marked as complete with a strikethrough style",
"constraints": ["Completed items remain in the list"],
"verification": "npm test -- --grep TODO-02",
"links": {
"depends_on": ["TODO-01"]
}
}
```
Create `specgraph/nodes/behaviors/TODO-03.json`:
```json
{
"$schema": "https://oco-adam.github.io/specgraph/schemas/node.schema.json",
"id": "TODO-03",
"type": "behavior",
"title": "Delete Todo Item",
"expectation": "When user clicks the delete button on a todo item, it is removed from the list",
"constraints": ["Deletion is immediate with no confirmation dialog"],
"verification": "npm test -- --grep TODO-03",
"links": {
"depends_on": ["TODO-01"]
}
}
```
## Step 4: Create the Graph Index
Create `specgraph/graph.json`:
```json
{
"$schema": "https://oco-adam.github.io/specgraph/schemas/graph.schema.json",
"specgraphVersion": "1.0.0",
"nodes": [
{ "id": "TODO", "path": "nodes/features/TODO.json" },
{ "id": "TODO-01", "path": "nodes/behaviors/TODO-01.json" },
{ "id": "TODO-02", "path": "nodes/behaviors/TODO-02.json" },
{ "id": "TODO-03", "path": "nodes/behaviors/TODO-03.json" }
]
}
```
## Step 5: Validate
If you have the Spec Graph validator:
```bash
node validate.js
```
Or validate manually with `ajv`:
```bash
npx ajv validate -s graph.schema.json -d specgraph/graph.json
npx ajv validate -s node.schema.json -d specgraph/nodes/behaviors/TODO-01.json
```
## Your Spec Graph
You now have a minimal spec graph:
```mermaid
graph TD
TODO[TODO
feature] -->|contains| T1[TODO-01
Add Item]
TODO -->|contains| T2[TODO-02
Complete Item]
TODO -->|contains| T3[TODO-03
Delete Item]
T2 -->|depends_on| T1
T3 -->|depends_on| T1
```
This is a **behavior-only** graph — the simplest valid spec graph. It specifies what the system does but leaves architecture, technology, and design to the implementing agent.
## What's Next?
- **Add decision nodes** when you want to constrain how the system is built. See [Decision Nodes](/docs/node-types/decision).
- **Add domain nodes** when business terms need explicit definition. See [Domain Nodes](/docs/node-types/domain).
- **Add policy nodes** when non-functional requirements must be measured. See [Policy Nodes](/docs/node-types/policy).
- **Add layer nodes** when shared infrastructure should be explicit across features. See [Layer Nodes](/docs/node-types/layer).
- **Add foundation nodes** when the repository needs concrete scaffolding before application logic. See [Foundation Nodes](/docs/node-types/foundation).
- Read [When to Add Nodes](/docs/authoring/when-to-add-nodes) for guidance on growing your graph.
- Read the [MCP Server guide](/docs/guides/mcp-server) for the recommended agent-driven workflow.
- See the [auth example](/docs/reference/examples) for a full-featured spec graph.
---
# Progressive Adoption
The Spec Graph is designed for incremental adoption. You don't need every node type from day one — start with behaviors and add dimensions as the [minimality test](/docs/theory/minimality) demands.
Use this guide to decide **what** to add to the graph.
For **how** to operate on the graph in day-to-day work, use the agent-driven [MCP Server workflow](/docs/guides/mcp-server).
## The Adoption Path
### Level 1: Behavior-Only
Start here. A behavior-only spec graph is valid and useful. It captures what the system does and provides testable acceptance criteria.
```
specgraph/
graph.json
nodes/
features/AUTH.json
layers/PLATFORM.json # optional, when shared infra exists
foundations/FND-01.json # optional, for cold-start bootstrapping
behaviors/AUTH-01.json
behaviors/AUTH-02.json
```
**What you get:** behavioral equivalence. Two agents produce systems that do the same things.
**What you don't get:** architectural, technological, or visual consistency.
### Level 2: + Decision Nodes
When you discover that different implementations make incompatible architecture or technology choices, add decision nodes.
```
specgraph/
graph.json
nodes/
features/AUTH.json
behaviors/AUTH-01.json
behaviors/AUTH-02.json
decisions/DEC-AUTH-01.json ← NEW
decisions/DEC-AUTH-02.json ← NEW
```
**Trigger:** "The last two manifestations used different auth libraries" or "Agent keeps putting auth logic in the wrong place."
**What you get:** structural and technological equivalence added to behavioral equivalence.
### Level 2.5: + Foundation Nodes
When the repository must be bootstrapped from scratch and agents need a concrete physical anchor before implementing decisions, add foundation nodes.
```
nodes/
...
foundations/FND-01.json ← NEW
```
**Trigger:** "The agent hallucinated scaffolding in the wrong language/framework" or "No physical codebase existed for the agent to anchor its decisions to."
**What you get:** a concrete, verified physical baseline. The repository is bootstrapped with the right module system, directory layout, and package manifests.
### Level 3: + Layer Nodes
When multiple features share foundational infrastructure guidance, add a layer node.
```
nodes/
...
layers/PLATFORM.json ← NEW
```
**Trigger:** "Multiple feature slices depend on the same platform/security/data decisions."
**What you get:** explicit horizontal architecture and reusable shared guidance.
### Level 4: + Domain Nodes
When business terms are ambiguous or domain rules are being violated, add domain nodes.
```
nodes/
...
domains/DOM-USER-01.json ← NEW
```
**Trigger:** "The agent interpreted 'user' as a profile, but we mean an account" or "Status transitions are wrong."
**What you get:** domain equivalence — same data model and business rules.
### Level 5: + Policy Nodes
When non-functional requirements need measurement, add policy nodes.
```
nodes/
...
policies/POL-PERF-01.json ← NEW
```
**Trigger:** "The page is too slow" or "We need WCAG compliance."
**What you get:** policy equivalence — same non-functional characteristics.
### Level 6: + Extension Types
When core types don't provide enough precision for a specific dimension:
```
nodes/
...
design_tokens/DT-COLOR-01.json ← NEW
api_contracts/API-AUTH-01.json ← NEW
```
**Trigger:** "Visual inconsistency between features" or "API contract keeps changing."
## When to Move to the Next Level
Each level is triggered by a **manifestation failure** — a case where the current graph doesn't prevent an undesirable outcome:
| Failure | Add |
|---|---|
| Two implementations have incompatible architectures | Decision nodes (architecture) |
| Agent hallucinated scaffolding or wrong ecosystem | Foundation nodes |
| Wrong technology or library was used | Decision nodes (stack) |
| Multiple features require the same infrastructure context | Layer nodes |
| Agent misunderstood a business term | Domain nodes |
| Performance or security requirement was violated | Policy nodes |
| Visual design is inconsistent | Design token extensions |
## You Don't Need Every Level
Many projects will never reach Level 6. A behavior + decision graph (Level 2) may be sufficient for most internal tools. The key insight is:
> **Only add nodes that prevent actual manifestation problems.**
Speculative nodes — added "just in case" — violate minimality and add noise.
## Migrating from No Spec
If your project has no formal spec at all:
1. **Audit existing behavior** — what does the system currently do?
2. **Write behavior nodes** — one per observable action
3. **Group vertical slices** — create feature nodes
4. **Extract shared horizontal capabilities** — create layer nodes only when multiple features depend on them
5. **Bootstrap the physical baseline** — if the repo is greenfield, add foundation nodes declaring required scaffolding (module manifests, directory layout)
6. **Build the index** — create `graph.json`
7. **Validate** — run the schema validator
8. **Iterate** — add decision/layer/foundation/domain/policy nodes as needed
The initial behavior audit is the most time-consuming step. Once you have behaviors, the graph grows incrementally.
---
# Migrating from Behavior-Only Specs
If you already maintain a behavior-only specification (for example, a `SPEC.json` file), this guide shows how to migrate to Spec Graph.
## What Changes
| Aspect | Behavior-Only Spec | Spec Graph |
|---|---|---|
| Node types | `behavior` only | core + extension node types |
| Structure | Flat array, often nested under features | Graph with typed edges |
| Storage | Single file | File-per-node directory |
| Relationships | Implicit via grouping | Explicit typed edges |
| Tech guidance | External docs or agent instructions | Decision nodes |
| Domain model | Implicit | Domain nodes |
| Constraints | Implicit or prose | Policy nodes + `constraints` fields |
| Edge storage | N/A | Node-local (`links`) |
## What Stays the Same
- **Atomicity**: one behavior node should still capture one observable outcome
- **Declarative**: specs still describe truth, not implementation steps
- **Verifiable**: each node still needs pass/fail verification criteria
- **Repo-first**: the graph still lives in version control as source of truth
## Migration Steps
### Step 1: Extract Behaviors
Convert each behavior into a standalone node file:
**Before (behavior-only `SPEC.json`):**
```json
{
"features": [{
"id": "AUTH",
"name": "Authentication",
"behaviors": [{
"id": "AUTH-01",
"name": "Login Form Display",
"expectation": "Login page renders email and password fields",
"invariant": "Password field must mask input",
"verification": "pytest tests/auth/test_login.py -k AUTH_01"
}]
}]
}
```
**After (`nodes/behaviors/AUTH-01.json`):**
```json
{
"id": "AUTH-01",
"type": "behavior",
"title": "Login Form Display",
"expectation": "Login page renders email and password fields",
"constraints": ["Password field must mask input"],
"verification": "pytest tests/auth/test_login.py -k AUTH_01"
}
```
Changes:
- `name` -> `title`
- Added `type: "behavior"`
- `invariant` (string) -> `constraints` (array of strings)
- Each behavior becomes its own file
### Step 2: Create Grouping Nodes
Convert feature-level groupings into explicit `feature` nodes:
```json
{
"id": "AUTH",
"type": "feature",
"title": "User Authentication",
"description": "Login, session management, and logout flows",
"links": {
"contains": ["AUTH-01", "AUTH-02", "AUTH-03"]
}
}
```
Optional horizontal extraction after baseline migration:
- Keep product slices as `feature` nodes
- Move shared infrastructure groupings into `layer` nodes
- Add `feature -> depends_on -> layer` edges where needed
### Step 3: Create the Graph Index
```json
{
"specgraphVersion": "1.0.0",
"nodes": [
{ "id": "AUTH", "path": "nodes/features/AUTH.json" },
{ "id": "AUTH-01", "path": "nodes/behaviors/AUTH-01.json" },
{ "id": "AUTH-02", "path": "nodes/behaviors/AUTH-02.json" }
]
}
```
### Step 4: Extract Design Decisions
What previously lived in implementation notes or agent instructions should become decision nodes:
```text
"all auth goes through AuthProvider" -> DEC-AUTH-01
"use PostgreSQL for primary state" -> DEC-DATA-01
"use gRPC between services" -> DEC-API-01
```
### Step 5: Add Edges
Connect behaviors to the nodes that guide them:
```json
// In AUTH-01.json
"links": {
"depends_on": ["DEC-AUTH-01"],
"implements": ["DOM-USER-01"]
}
```
### Step 6: Add Domain and Policy Nodes
Use the [minimality test](/docs/theory/minimality): add domain/policy nodes where leaving them out would create manifestation ambiguity.
## Coexistence During Migration
During migration, your original behavior-only spec can coexist with the new graph directory. You can project behavior nodes back into a compatibility file when legacy tooling still needs that format.
This lets teams migrate incrementally without breaking existing workflows.
## Key Differences to Watch
### Edges Replace Structural Nesting
In behavior-only formats, belonging is often implied by nesting. In Spec Graph, membership is explicit through `contains` edges.
### Verification Is Richer
Behavior nodes keep a single verification string for simplicity. Contract nodes (foundation, decision, domain, policy, and extensions) support verification arrays with structured entries (`command`, `http`, `manual`, `observation`, `policy`).
---
# JSON Schemas
All Spec Graph files are validated against JSON Schemas using [JSON Schema Draft 2020-12](https://json-schema.org/specification).
## Schema Files
| Schema | Validates | Required? |
|---|---|---|
| [`graph.schema.json`](pathname:///specgraph/schemas/graph.schema.json) | `graph.json` index file | Yes |
| [`node.schema.json`](pathname:///specgraph/schemas/node.schema.json) | Individual node files | Yes |
| [`graph-index.schema.json`](pathname:///specgraph/schemas/graph-index.schema.json) | Generated edge index | Optional |
| [`manifest-lock.schema.json`](pathname:///specgraph/schemas/manifest-lock.schema.json) | Manifestation lockfile | Optional |
Schema `$id` base URL: `https://oco-adam.github.io/specgraph/schemas/`
## graph.schema.json
Validates the `graph.json` entry point file. Required fields:
- `specgraphVersion` — semver string (e.g., `"1.0.0"`)
- `nodes` — array of node references with `id` and `path`
Optional fields:
- `root` — root node ID
- `nodeSearchPaths` — additional directories to scan
- `defaults` — default values for tooling
### Node References
Each entry in `nodes` has:
```json
{
"id": "AUTH-01",
"path": "nodes/behaviors/AUTH-01.json",
"sha256": "...", // optional: content hash
"expectedType": "behavior" // optional: quick type check
}
```
## node.schema.json
Validates individual node files. Uses `oneOf` to distinguish four shapes:
### Feature Nodes
Required: `id`, `type` (= `"feature"`), `title`, `description`
### Layer Nodes
Required: `id`, `type` (= `"layer"`), `title`, `description`
### Behavior Nodes
Required: `id`, `type` (= `"behavior"`), `title`, `expectation`, `verification`
Optional: `constraints` (array)
Authoring convention: include `constraints` explicitly on behavior nodes and use `[]` when none apply. Omission remains schema-valid for backward compatibility.
### Contract Nodes
All other types (foundation, decision, domain, policy, and extensions).
Required: `id`, `type`, `title`, `statement`, `verification` (array)
Type-specific requirements:
- `decision` nodes require `category` and `metadata.rationale` (min length 10)
- `policy` nodes require `severity`
- `artifact` nodes require an `artifact` object with a [normative](/docs/reference/normative) `sha256` hash (and optional `source`/`format`)
Other optional fields: `constraints` (array), `links`, `metadata`, `pins` (for derived nodes)
Metadata supports optional `rejected_alternatives` entries (`title` + `reason`) for decision history context.
### The `links` Field
Available on all node types. Supports seven edge types:
```json
"links": {
"contains": ["..."],
"depends_on": ["..."],
"constrains": ["..."],
"implements": ["..."],
"derived_from": ["..."],
"verified_by": ["..."],
"supersedes": ["..."]
}
```
### Verification Entries
Contract nodes use an array of verification entries. Each entry is either a string or a structured object:
```json
"verification": [
"pytest tests/architecture/test_auth_provider.py -k DEC_AUTH_01",
{
"kind": "command",
"command": "cargo test auth::provider::contract -- --exact",
"timeoutSeconds": 120
},
{
"kind": "http",
"method": "GET",
"url": "http://localhost:3000/api/health",
"expectStatus": 200
}
]
```
Supported `kind` values: `command`, `http`, `manual`, `observation`, `policy`.
### Node ID Format
Node IDs must match: `^[A-Z][A-Z0-9-]{0,79}$`
Feature IDs must match: `^[A-Z][A-Z0-9-]{0,19}$`
### Node Types
Core: `feature`, `layer`, `foundation`, `behavior`, `decision`, `domain`, `policy`
Extensions: `design_token`, `ui_contract`, `api_contract`, `data_model`, `artifact`, `equivalence_contract`, `pipeline`
## graph-index.schema.json
Validates the optional generated edge index. Required fields:
- `specgraphVersion`
- `generatedFrom` — path (and optional hash) of the source `graph.json`
- `edges` — array of `{ from, type, to }` triples
This file is a **derived artifact**. It should be regenerated from node files and never hand-edited.
## manifest-lock.schema.json
Validates the optional manifestation lockfile. Required fields:
- `specgraphVersion`
- `graph` — path and SHA-256 hash of the manifested graph
- `manifestedAt` — ISO 8601 timestamp
Optional fields:
- `equivalenceContractId` — node ID of the equivalence contract used
- `toolchain` — pinned tool versions
- `artifacts` — resolved artifact hashes
- `checks` — verification results from manifestation
## Validation
Use `ajv` or any JSON Schema Draft 2020-12 compatible validator:
```bash
# Validate a graph index
npx ajv validate -s schemas/graph.schema.json -d specgraph/graph.json
# Validate a node file
npx ajv validate -s schemas/node.schema.json -d specgraph/nodes/behaviors/AUTH-01.json
```
Or use the provided validation script:
```bash
node validate.js
```
For graph-level integrity checks (edges, missing targets, `depends_on` cycles, pins), run:
```bash
node graph_check.js
```
---
# Examples
Four worked examples are provided, each emphasizing a different part of the model.
## Minimal: Todo List
**Purpose:** Smallest valid graph (behavior-first adoption).
**Node types used:** `feature`, `behavior`
**Source:** [`examples/minimal/`](pathname:///specgraph/examples/minimal/graph.json)
## Auth: User Authentication
**Purpose:** Canonical core example with behavior, decisions, domain, and policy nodes.
**Node types used:** `feature`, `behavior`, `decision`, `domain`, `policy`
**Source:** [`examples/auth/`](pathname:///specgraph/examples/auth/graph.json)
## Layered: Shared Platform Layer
**Purpose:** Demonstrates feature/layer coexistence, transitive dependency (`B -> A -> L`), and layer-originated guidance.
**Node types used:** `feature`, `layer`, `behavior`, `decision`, `policy`
**Source:** [`examples/layered/`](pathname:///specgraph/examples/layered/graph.json)
```mermaid
graph TD
AUTH[AUTH feature] -->|depends_on| PLATFORM[PLATFORM layer]
BILLING[BILLING feature] -->|depends_on| AUTH
PLATFORM -->|contains| DEC[DEC-PLAT-01]
PLATFORM -->|contains| POL[POL-PLAT-SEC-01]
AUTH -->|contains| A1[AUTH-01]
BILLING -->|contains| B1[BILLING-01]
```
## Taskboard: Kanban Task Board
**Purpose:** Richer example with extension types.
**Node types used:** `feature`, `behavior`, `decision`, `domain`, `policy`, `design_token`, `artifact`
**Source:** [`examples/taskboard/`](pathname:///specgraph/examples/taskboard/graph.json)
---
# Normative
In Spec Graph, **normative** means: a statement that is required for conformance.
If content is normative, an implementing agent or team must treat it as binding:
- it must be implemented
- it must be respected during manifestation
- it must pass its verification criteria
## Normative Content
In practice, these fields are normative:
- `expectation` (behavior nodes)
- `statement` (contract-style nodes)
- `constraints`
- `verification`
If two implementations differ on normative content, they are not equivalent under the same spec graph.
## Informative Content
Informative content explains intent and context, but does not impose implementation requirements.
Typical informative fields include:
- `metadata.rationale`
- `metadata.notes`
- optional contextual metadata (tags, ownership, references)
Informative content can guide humans and agents, but it is not itself a conformance target.
## Non-Normative Content
Grouping nodes are non-normative organizational structure:
- `feature`
- `layer`
These nodes shape navigation and scope, not runtime behavior.
## Quick Test
Use this test when deciding if content is normative:
1. If this statement is violated, is the implementation non-conformant?
2. If yes, it is normative.
3. If no, it is informative/non-normative.
## Why This Distinction Matters
Clear normative boundaries prevent ambiguity during manifestation. They keep the graph executable as a contract, while still allowing supporting context for collaboration and review.
---
# Glossary
| Term | Definition |
|---|---|
| **Spec Graph** | A directed, typed graph of specification nodes that completely describes a software system across multiple dimensions. |
| **Node** | A single specification entry with a type, ID, and content. The atomic unit of the spec graph. |
| **Edge** | A typed, directed relationship between two nodes. Stored as outbound links inside the source node. |
| **Feature** | A [non-normative](/docs/reference/normative#non-normative-content) grouping node for vertical product slices. |
| **Foundation Node** | A [normative](/docs/reference/normative) node declaring the manifested physical state of repository scaffolding (files, directories, package manifests). Uses self-contained shell commands for verification. Cannot depend on layer, feature, or behavior nodes. |
| **Layer** | A [non-normative](/docs/reference/normative#non-normative-content) grouping node for shared horizontal infrastructure capability. |
| **Manifestation** | The process of producing a running system from a spec graph. Encompasses planning, coding, building, testing, and deploying. |
| **Completeness** | The property that manifestation is predictable across all specified dimensions — any two manifestations from the same graph are equivalent. |
| **Minimality** | The property that no node can be removed without breaking completeness. Every node is load-bearing. |
| **Completeness Gap** | The set of decisions left to the implementing agent — decisions the spec graph does not determine. |
| **Load-bearing** | A node whose removal would create manifestation ambiguity. |
| **Equivalence** | The relation between two manifestations that makes them "the same system" across all dimensions specified in the graph. |
| **Equivalence Contract** | An optional extension node that formally declares what "same system" means for this graph. |
| **Implementing Agent** | The agent (human or AI) that processes the spec graph to produce a running system. |
| **Capable Agent** | An implementing agent that can parse, traverse, apply, respect, and verify the spec graph. |
| **Contract Node** | The unified shape shared by all non-grouping, non-behavior node types (foundation, decision, domain, policy, extensions). |
| **Decision Node** | A node capturing an architectural, technical, or stack decision that narrows the solution space. |
| **Domain Node** | A node defining a business concept, term, or rule — the ubiquitous language of the system. |
| **Policy Node** | A node specifying a cross-cutting non-functional requirement (performance, security, accessibility, cost, reliability). Uses `severity` (hard/soft) and `constrains` edges. Not to be confused with the `constraints` field. |
| **Constraints (field)** | Normative conditions on a node that narrow its primary field (`expectation` or `statement`). Each entry is independent and testable. Present on both behavior and contract node shapes. Not to be confused with policy nodes (a node type for cross-cutting NFRs). |
| **Extension Type** | An optional node type for finer-grained modelling (design_token, api_contract, data_model, etc.). |
| **Verification** | Pass/fail criteria attached to a node. The mechanism by which equivalence is established. |
| **Normative** | Content that MUST be true, MUST be implemented, MUST pass verification (expectation, statement, constraints, verification). |
| **Informative** | Content that provides non-executable context (metadata.rationale, metadata.notes). Some node types may require specific metadata fields. |
| **Orient** | The first manifestation phase: read all nodes to build system understanding. |
| **Scaffold** | The second manifestation phase: create architectural infrastructure from decision and domain nodes. |
| **Implement** | The third manifestation phase: build each behavior with full graph context. |
| **Context Assembly** | The process of gathering all related nodes for a behavior by following edges. |
| **Forward Edge** | An edge stored in the source node's links field. The canonical storage representation (not a traversal restriction). |
| **Inverse Edge** | An edge computed by tooling from forward edges. Never stored. |
| **Progressive Adoption** | The practice of starting with behavior nodes and adding other types as needed. |
| **The ONE Rule** | Atomicity rule for behaviors: ONE trigger, ONE behavior, ONE outcome. |
| **Minimality Test** | "If I removed this node, could a competent agent make a choice I wouldn't want?" |
| **Shadow Spec** | The scattered, mutable, implicit collection of documents and knowledge that fills gaps left by a behavior-only spec. |
| **Severity** | For policy nodes: `hard` (blocks manifestation) or `soft` (quality target). |
---
# LLM Context Files
Spec Graph publishes two text files specifically for AI agent context loading.
## Available Files
- [llms.txt](https://oco-adam.github.io/specgraph/llms.txt) — concise summary of the framework, key concepts, and canonical links
- [llms-full.txt](https://oco-adam.github.io/specgraph/llms-full.txt) — full documentation bundle concatenated into one file
## When to Use Which
- Use `llms.txt` for lightweight orientation and quick context bootstrap
- Use `llms-full.txt` when an agent needs complete project documentation in a single artifact
## Source of Truth
- `llms.txt` is maintained at `website/static/llms.txt`
- `llms-full.txt` is generated from docs via `node generate_llms_full.js`
On deploy, `llms-full.txt` is copied into `website/static/llms-full.txt`, which makes it available at `/llms-full.txt` on GitHub Pages.
---
# Comparison with Adjacent Concepts
The Spec Graph shares ideas with several existing approaches. This page clarifies the similarities and differences.
## vs. Product Requirements Documents (PRDs)
| Aspect | PRD | Spec Graph |
|---|---|---|
| Format | Prose document | Structured, typed graph |
| Actionability | Requires interpretation | Machine-parseable |
| Verification | Manual review | Every node has pass/fail criteria |
| Granularity | Section-level | Atomic nodes |
| Relationships | Implicit in text | Explicit typed edges |
**Similarity:** Both describe what to build.
**Difference:** PRDs are narrative documents that require human interpretation. The Spec Graph is a structured, machine-actionable specification where every claim is verifiable.
## vs. Architecture Decision Records (ADRs)
| Aspect | ADRs | Spec Graph Decision Nodes |
|---|---|---|
| Purpose | Historical narrative of decisions | Prescriptive, living specification |
| Status | Usually immutable once accepted | Can evolve via `supersedes` edges |
| Verification | None | Required for every decision |
| Relationships | Cross-references in text | Typed edges (depends_on, constrains) |
**Similarity:** Both capture architectural and technical decisions.
**Difference:** ADRs are retrospective records ("We decided X because Y"). Decision nodes are prescriptive specifications ("X must be true, verified by Z").
## vs. Design Tokens
| Aspect | Design Token Systems | Spec Graph Design Token Nodes |
|---|---|---|
| Content | Values (colors, spacing, fonts) | Values + usage rules + relationships |
| Verification | Build-time token validation | Node-level verification criteria |
| Context | Standalone token system | Edges connect tokens to behaviors and constraints |
**Similarity:** Both specify visual properties.
**Difference:** Standard design tokens are values in isolation. Spec Graph design token nodes include usage rules, verification, and edges to the behaviors they constrain.
## vs. Domain-Driven Design (DDD)
| Aspect | DDD | Spec Graph Domain Nodes |
|---|---|---|
| Format | Books, workshops, modeling sessions | Declarative JSON specifications |
| Scope | Methodology and philosophy | Specific data model |
| Output | Mental models, code patterns | Verifiable, machine-readable nodes |
**Similarity:** Both model business concepts and establish ubiquitous language.
**Difference:** DDD is a methodology — a way of thinking about software. Domain nodes are concrete, verifiable specifications that capture the outcomes of domain modeling.
## vs. Infrastructure as Code (IaC)
| Aspect | IaC (Terraform, Pulumi) | Spec Graph |
|---|---|---|
| Scope | Infrastructure resources | Entire system intent |
| Output | Running infrastructure | Running system (via manifestation) |
| Abstraction | Resource-level | Intent-level |
**Similarity:** Both aim for predictable creation from declarative specifications.
**Difference:** IaC specifies infrastructure resources at a concrete level. The Spec Graph specifies system intent at a higher level — including behavior, architecture, domain, and constraints — leaving the agent to determine the concrete implementation.
## vs. UML / System Modeling
| Aspect | UML | Spec Graph |
|---|---|---|
| Purpose | System documentation and visualization | System specification for manifestation |
| Diagrams | Class, sequence, state, etc. | Node graph with typed edges |
| Verification | None (descriptive) | Every node verifiable |
| Actionability | Reference for humans | Input for implementing agents |
**Similarity:** Both model system structure and relationships.
**Difference:** UML diagrams describe systems for human understanding. Spec Graph nodes prescribe system properties for agent implementation, with mandatory verification.
## vs. OpenAPI / API Specifications
| Aspect | OpenAPI | Spec Graph API Contract Nodes |
|---|---|---|
| Scope | HTTP API surface | Full system specification |
| Detail | Endpoint-level (paths, schemas) | Intent-level (what the API must do) |
| Generation | Code from spec or spec from code | System manifestation from graph |
**Similarity:** Both formally specify API contracts.
**Difference:** OpenAPI is a complete API surface description. Spec Graph API contract nodes capture intent and constraints, leaving implementation details to the agent. The two can complement each other — an API contract node might reference an OpenAPI spec as an artifact.
## vs. BDD / Gherkin
| Aspect | BDD (Given/When/Then) | Spec Graph Behavior Nodes |
|---|---|---|
| Format | Natural language scenarios | Structured JSON with expectation + constraints |
| Scope | User-facing behavior | Multi-dimensional (behavior + architecture + ...) |
| Verification | Scenario runner (Cucumber) | Flexible (commands, HTTP, manual) |
**Similarity:** Both capture observable system behavior.
**Difference:** BDD scenarios focus on user stories. Spec Graph behaviors are part of a larger graph that also captures the non-behavioral dimensions needed for predictable manifestation.
---