ADR-009 — The agent contract
Entry points, error codes and a published schema — accepted 2026-09-07.
Date: 2026-09-07 · Status: accepted · Scope: CLI contract, init, agent interface
Context
kadence promised that init "writes a guide the AI agent finds on its own".
Research on 2026-09-07 found the promise did not hold, and that two related
claims were thinner than they read:
initwrote onlyAGENTS.md. That is the cross-tool convention — 30+ agents read it, 60,000+ repositories carry it — but Claude Code loadsCLAUDE.mdand does not readAGENTS.md. Verified directly: in the session that produced the research,CLAUDE.mdwas in context andAGENTS.mdwas not. For the largest agent audience,initcreated no entry point that loads on its own.- A failed
--jsoncall returned one English sentence. An agent could tell that something failed and nothing else. The most likely agent error is a wrong status — and statuses are configured per project, so no amount of reading the documentation tells an agent the valid set. schema: "kadence/v1"was a label. Nothing published what the fields are, and nothing failed if one was renamed. It was called "a contract an agent can rely on"; it was a version string that a test asserted the presence of.
Options considered
Entry points
| Option | Pros | Cons |
|---|---|---|
AGENTS.md only (status quo) | one file, one convention | invisible to Claude Code — the largest audience, and the agent we build with |
CLAUDE.md containing @AGENTS.md | the vendor-documented bridge; one source of truth | the import pulls someone else's entire instruction file into every session |
| The same short section in both, regenerated on every init | works everywhere; the upsert prevents drift; ambient cost stays at a few lines | two files carry the same paragraph |
A file per agent (.cursor/rules, copilot-instructions, GEMINI.md, …) | maximum reach | four more files written into someone's repository to advertise ourselves; those tools read AGENTS.md already |
Error detail
| Option | Pros | Cons |
|---|---|---|
| Message only (status quo) | nothing to maintain | agent cannot branch, cannot self-correct |
code + received + allowed + hint | agent fixes its own input in one step; allowed is the only way to learn configured statuses | the code list becomes part of the contract |
Adding retryable / recoverable | matches the pattern the literature recommends | there is no network and no lock here: the same input always fails identically, so the field would be a constant presented as information |
Publishing the schema
| Option | Pros | Cons |
|---|---|---|
Document the fields in .kadence/README.md | no code | prose drifts from behaviour, silently |
| Ship a static JSON Schema file | standard format, existing validators | a second artefact to keep in step; a dependency to validate it |
kadence schema --json, checked against real output in CI | the tool is the source of truth; zero dependencies; the check fails the build on a rename | our own shape, not JSON Schema — a consumer wanting formal validation must convert |
Decision
Write the same section into both AGENTS.md and CLAUDE.md. No @AGENTS.md
import: it would drag an arbitrary amount of someone else's context into every
session. No per-vendor files.
Give every failure a code, from a closed list, with received, allowed and
hint where each is knowable. No retryable — and the absence is asserted by
a test, so it is not added absent-mindedly later.
Publish the contract as kadence schema --json. It works outside a
repository. Three tests keep it honest: the published error codes must equal the
ones the code emits, every required field must appear in real command output, and
every command named must actually run.
The contract is deliberately narrower than the output. Fields we emit but do
not list may still change; what is listed is a promise. Within kadence/v1,
fields and codes may be added, never renamed or removed.
Consequences
- Positive: the promise is now true for Claude Code users. An agent that sends a wrong status is told the configured set and fixes itself in one step. Renaming a task field fails the build instead of breaking consumers quietly.
- Cost:
initnow writes a second file into the user's repository root — the most intrusive thing kadence does, which is why the section is short and the upsert leaves human text untouched. The error-code list and the schema are both public surface: additive changes only, forever. - Measured: bundle 29 KB (was 30 KB),
schema --jsonunder 4 KB of output, performance budgets unchanged. - Revisit if: Claude Code adds native
AGENTS.mdsupport — then the second file becomes redundant and should be dropped rather than kept out of habit. Or if consumers ask for formal JSON Schema, at which point the contract we already publish is the thing to generate it from.
This record was written in English in the product repository; the canonical copy is 009-the-agent-contract.md.