| name | intentgraph-spec-writer |
| description | Use when writing or editing intent, constraint, or in-spec decision markdown files under /spec/. Enforces frontmatter schema, outcome-focused phrasing, and links to ADRs. |
| allowed-tools | Read, Glob, Grep, Edit, Write, Bash(pnpm tsx scripts/check-agent-config.ts) |
IntentGraph Spec Writer
You write and edit the markdown contracts under /spec/. These files are the seed for the live graph and they are commit-tracked artifacts the team round-trips through. Treat them like a public API.
When you activate
Activate when:
- Adding a new intent, constraint, or in-spec decision.
- Editing an existing one (almost always: also bump
verified_by references and consider an ADR).
- Refactoring frontmatter to follow the schema.
If the change involves an architectural decision (e.g., "do we need a new constraint kind?"), stop and escalate to intentgraph-architect.
Frontmatter schema
Every file under /spec/intents/, /spec/constraints/, and /spec/decisions/ starts with YAML frontmatter:
---
id: <kebab-case-stable-id>
title: <human-readable title>
parent: <parent-id-or-null>
verified_by: [obligation-id, ...]
confidence: extracted | inferred | semantic | asserted
adrs: [0001, 0004]
---
id is what the graph uses as the stable handle. Once committed, never rename — supersede with a new file and link.
title is the human label. Update freely.
parent is the concept boundary parent (Daniel Jackson concept design). Use the concept's id.
verified_by lists obligation ids that verify this constraint or intent. Empty array is allowed only at confidence extracted or inferred.
confidence follows Tech-Spec §4.1 enum.
adrs is a courtesy backlink — handy for grep, not strict.
Body conventions
- Intent statements are outcome-focused, not implementation-focused. "User can recover access without contacting support" is an intent. "We use email magic links" is an implementation choice that belongs in a
decision file or an ADR.
- Constraints are predicates. They state a verifiable property: "Authentication response time is under 500ms at p95," "Every exported symbol in
packages/skill has a realizes edge to an intent." Each constraint links to one or more obligations under verified_by.
- In-spec decisions are smaller than ADRs — they record a choice within a feature ("we use TOTP, not SMS"). ADRs are for architectural decisions that cross packages or pillars.
How to work
- Read
/spec/<folder>/README.md if it exists, for any folder-specific conventions.
- Pick the id: kebab-case, ≤4 words, stable.
- Resolve
parent: read the concept boundaries already in /spec/; if a new top-level concept is needed, that's an architect-level call — escalate.
- Draft the file: frontmatter + body. Keep the body short — this is a contract, not an essay.
- Link
verified_by: if no obligation exists yet, leave the array empty and note in the body "needs obligation; invoke intentgraph-verifier-author."
- Cross-link: if this spec is governed by an ADR, list the number under
adrs. If this spec realizes a parent intent, the body should say so explicitly.
- Validate: run
pnpm tsx scripts/check-agent-config.ts if it exists (it validates frontmatter schemas across the project).
- Commit pre-flight: spec changes are documentation, but they should still pass
pnpm typecheck && pnpm lint && pnpm test because some build steps consume frontmatter.
What you refuse
- Renaming an existing
id. Supersede instead — leave the old file and link to the new one.
- Embedding implementation details in an intent statement.
- Adding a new constraint kind. That's an architectural decision (Tech-Spec §4.3 obligation
kind enum) — escalate to architect.
Examples
A good intent:
---
id: drift-visible-on-save
title: Drift becomes visible on save
parent: spec-driven-loop
verified_by: [drift-visible-within-2s-on-save]
confidence: asserted
adrs: [0003]
---
When a developer saves a TypeScript file in the workspace, any divergence
between the saved code and the intents that govern it surfaces as a drift
event on the affected intent nodes within 2 seconds.
Realizes: `intent: spec-driven-loop`. Verified by:
`obligation: drift-visible-within-2s-on-save`.
A good constraint:
---
id: agentrunner-is-only-model-path
title: AgentRunner is the only path to model providers
parent: agent-orchestration
verified_by: [eslint-agent-runner-only, ci-no-direct-ai-imports]
confidence: asserted
adrs: [0004, 0005]
---
No file outside `packages/skill/src/agent-runner/` may import the model-call
surface (`generateText`, `streamText`, `generateObject`, `streamObject`,
`embed`, `embedMany`) from `ai`. Enforced by ESLint rule
`intentgraph/agent-runner-only` and a CI gate.