Draft proposal · Version 0.1

Spec of Record (SoR)

Spec of Record is a development philosophy and toolchain for AI-built software, where one or many agents build from a single spec, aimed at mid-sized to large projects. It turns the spec into a centralized design document that always stays in sync with the code, so team members with different areas of expertise can understand, review and change the software together. You write what your software should do in plain language, an AI agent turns it into code, and each piece of code stays linked to the statements of the spec and to its test results.

Version 0.1 · October 2026
Install in

Run in a terminal

claude plugin marketplace add sebastianrath/specofrecord && claude plugin install specofrecord@specofrecord
Other ways

Already inside Claude Code? Paste one line at a time

/plugin marketplace add sebastianrath/specofrecord
/plugin install specofrecord@specofrecord

No claude command? Install Claude Code first (macOS and Linux, other systems)

curl -fsSL https://claude.ai/install.sh | bash
New project Recommended

Starting from scratch? In an empty folder, tell Claude Code what you want to build. For example:

I want to build a game engine. Write the first specs following Spec of Record for a game engine that...
Existing project

Already have code? Name the one thing you are about to change. Only that part goes under a spec, and the rest stays as it is. For example:

I am about to change the checkout. Put just that under Spec of Record first, then make my change.
Editor extension Optional

Why use SoR?

The reason a code line exists usually lives in a verbose commit message, a Jira ticket or a Slack message, not in the code itself. AI can write code quickly and loves to rewrite larger parts of the source code because it feels like it. AI agents mostly find context with a keyword search like grep, so when someone makes a change, they either miss things they should address or change too much.

Over time, the team loses track of how their software actually works. In a review, you face hundreds of new lines that nobody can meaningfully trace, and you send them back to the AI with "fix this" or wave them through with LGTM (sry that's me, I do that). The AI needs help too. Round after round of fixes, it keeps changing code until it has moved so far away from what it is meant to address that no one understands it anymore. It is a quality and liability risk.

Here is what Spec of Record offers, for people and AI:

Where it fits in spec-driven development

Spec-driven development means writing down what the software should do and letting an AI write the code. The term is overloaded, so it helps to be clear about its meaning. The difference is what happens to the spec after the code exists. Birgitta Böckeler of Unblocked describes three levels:1

LevelWhat happens to the spec
Spec-firstWritten to get one task done, then left behind.
Spec-anchoredKept after the task and updated as the feature changes.
Spec-as-sourceThe only thing people edit. Nobody touches the code.
Spec-of-recordThis proposal.

Where spec-driven development goes wrong

A design document is only useful when it's up to date. Spec drift is what happens when the spec says one thing, the code does another, and nobody can tell which is right. It creeps in one small change at a time.

Most spec-driven development is spec-first, which makes it a good place to look at the potential traps. The spec is written for one task and then put away. The code keeps changing, and the spec stays as it was.

Take a game. Its spec says the player needs 3 keys to open the exit, and the designer asks for 5. The AI changes the code, and nobody updates the spec, because nobody asked or the AI forgot. Now the spec says 3, the game asks for 5, and no one knows what should be true.

Spec-first has a second trap, and it catches the teams that avoid the first one. To keep the spec up to date, they write a follow-up spec for every change, so you end up with SPEC.md, then SPEC-2026-01-08.md, then another one. To learn what the software does today, you read them all in the right order. They become incredibly verbose and eventually no one reads them anymore.

In spec-as-source, people edit only the spec and the code is generated from it. You can't fix the code by hand, so every fix goes through the spec, and because an AI does not generate the same code twice,1 the result can come back different each time.

Spec of Record extends spec-as-source and allows hand edits on purpose. You can change either side, the spec or the code, and the other one is updated to match. Finished code is kept, so a change to a clause rewrites only that clause's code, and the clause's tests check the result. The bundled tools enforce this: they check that spec and code agree, block AI edits that skip the clause, and flag any mismatch until it is fixed.

How it works

The spec is split into parts, one Markdown file for each area of the software, such as a game's logic or its multiplayer networking. Each part says why it exists and lists its rules, its approach, what it needs from other parts, what it gives to them, and its tests.

The unit is a clause, which is one rule, one number or one table row. Every clause comes with a marker, a short comment above its code that names the clause. Below is a clause and the code built from it, so you can see each side point at the other.

sor/run.md

...

turn – Each key press moves the player, picks up keys and moves the monsters.

...

// @clause run/turn
export function takeTurn(game, dx, dy) {
  move(game.player, dx, dy);
  keepInRoom(game.player);
  pickUpKeys(game.player, game.keys);
  ...
}
// @clause end

With that association in place, a change stays local. Say you want the player to move only on every second key press. You change that one sentence, and the AI updates just the code blocks marked with that clause.

If you know how a compiler works, you can apply that concept to Spec of Record, with the AI playing the compiler.

Legacy developmentSpec of Record
You write source codeYou write the spec
A compiler turns it into a programThe AI turns it into code
Only changed files are rebuiltOnly the affected code is rebuilt
Tests check the programEach clause can have its own test

A real compiler gives the same output every time, so it can throw that output away and start over. An AI cannot, so Spec of Record keeps finished code.

Several agents can work on one project at once. A sub-agent first claims a spec clause and its code blocks. It changes the clause, then the code, locks the result and releases the claim. A clause that someone else holds is refused.

sor/
src/
player/movementrefused

One key press moves the player 20 px, and the player cannot leave the 500 px room.

// @clause player/movement
const STEP = 20;
export function move(player, dx, dy) {
  ...
}
// @clause end
player/livesrefused

A new player has 3 lives.

// @clause player/lives
const START_LIVES = 3;
export function withLives(player) {
  ...
}
// @clause end
// @clause player/movement
const ROOM = 500;
export function keepInRoom(player) {
  ...
}
// @clause end
monsters/chaserefused

Every turn a monster steps 20 px toward the player.

// @clause monsters/chase player/movement
const MONSTER_STEP = 20;
export function chase(monster, player) {
  ...
}
// @clause end

An agent claims a clause with sor claim before it touches anything.

Tools can follow the marker links too. An editor extension jumps from a spec to its code and back, and the sor command line interface (CLI) gives an AI or a script the same answer as text or JSON. A clause can also reach further back, to a source outside the code such as a Figma design.

In your editor

The VS Code extension shows these links on a sample project. You do not need it to use Spec of Record, but it is a handy way to see them.

VS Code with a spec file on the left and the code on the right. The fit clause is selected in the spec and its code region is highlighted in the code.

What you get

Beyond the editor extension, Spec of Record comes with a CLI, sor, and an edit guard for your AI agent. Here is what they do:

The build record

If you manage a team or answer to auditors, this is the section for you.

The build record is a checkpoint. Each time a piece of work is finished and the specs, the code and the tests agree, the tool saves one, together with the test results. Later it compares the project with that checkpoint to see what changed, and you can look up what was tested. It is a plain file in your repository, so version control keeps every earlier checkpoint.

QuestionAnswer
Why does this line of code exist?Its marker names the clause it serves.
What code does this clause make?The clause points to all of it.
What did this change affect?The change list shows it.
Who changed it, and when?Version control shows it. Code changed without its clause is flagged, with author and date if you use git.
Was it tested?The build record stores the results, and version control keeps the history.
Does every result come from an automated test?Yes, except for checks a person signed off by hand. An AI cannot sign off.

That helps with reviews, handovers and compliance checks, anywhere a change has to be explained later.

Common questions

Is this more documentation to maintain?

No, it replaces the documents you would otherwise write. The spec is what you edit. A change edits a clause, and a new feature adds clauses, or a new part, to the same set of files. There is no second copy buried in Confluence or Slack.

Do I have to convert my existing project?

No. Start with the one thing you are about to change, as in the install steps at the top. The rest of the code stays as it is.

Where do the specs live?

In a sor/ folder next to your code, as plain Markdown files you commit with it. There is no service to run and no account to create.

Will building my entire project with SoR create a dependency?

No, there is zero dependency. The code is plain source with marker comments in it, so you can delete the specs and carry on from the source, or switch to another engineering approach.

Which languages and tools work?

It is agnostic to language, framework and editor. Today the workflow ships as a Claude Code plugin, and other agents and editors are open for contributions.

What if I change the code myself?

Go ahead. Your change stays. The tool flags it as code changed without its clause (with author and date if you use git), and the AI rewrites the clause to match what you did.

What if the AI builds something wrong?

The clause's tests fail, and the code is fixed until they pass. Clauses with no passing test are listed, so a gap is visible instead of hidden.

Can several people or agents work at once?

Yes. Agents claim clauses, as shown above, and people on different machines coordinate through version control, the way they do now.

Related projects

Spec-driven tooling is a crowded field. The projects below cover different parts of the workflow. Some have gone quiet or are paused, and some are tied to one editor, agent or service. Spec of Record borrows ideas from several of them and adds its own.

ProjectWhat it does
Spec KitSpec-first or anchoredCommands and templates that take a feature from spec to plan, tasks and code. From GitHub.
OpenSpecSpec-anchoredA proposal for each change. When the change is done, it is merged into the specs.
KiroSpec-anchoredAn editor with requirements, design and task files for each feature, plus hooks that run on events.
BMAD-METHODSpec-first or anchoredA method with agent roles for product, architecture, UX, development and testing.
PurlinSpec-anchoredRules and proofs in specs. Ties tests to rules and marks results out of date when the spec, code or tests change. A Claude Code plugin.
CodeSpeakSpec-as-sourceSpecs that a tool turns into code. In alpha.
TesslSpec-as-sourceA registry of skills and context for agents. Its spec-as-source Framework is paused. Its spec-driven-development tile is open source.
nimaSpec-anchoredA hosted service that turns signals from tools like Slack, Linear and Sentry into specs, and checks pull requests against them. In private beta.
DriftSpec-anchoredBinds Markdown docs to code files or symbols. Flags a doc as stale when its code changes.
CoherenceSpec-anchoredChecks that code, docs, decision records and tests still point at each other.
SpecLockSpec-anchoredStores rules and warns or blocks when an AI action would break one.

About

Spec of Record and its tools are written by Sebastian Rath. The source is on GitHub, and contributions are welcome.

License

This document is licensed under Creative Commons Attribution 4.0 (CC BY 4.0). You can share and adapt it, even commercially, as long as you credit the author, link the license and say what you changed.

The tools are published under the MIT License.

Example Game
  • sor/
    • SPECOFRECORD.md
    • run.md
    • player/
      • movement.md
      • lives.md
    • monsters/
      • monsters.md
    • level/
      • exit.md← this file
sor/level/exit.md

# Exit

Why The way out: find enough keys and the door opens.

## Rules

layout 4 keys lie around the room, and the exit sits in the top right corner.

door The player must collect at least 3 keys to open the exit, and is told how many are still missing.

pickup Touching a key picks it up, and a key can only be picked up once.

win Walking into the open exit wins the run.

## Approach

How it looks. The exit is grey until it opens, then green. The corner shows how many keys are still missing.

Out of scope. Coloured keys and locked doors elsewhere.

## Contract

Needs player/movement/position where the player is, from the player part.

## Tests

door With 2 keys the exit stays shut and 1 key is missing. With 3 it opens.

do: pick up 2 keys

expect: the exit is shut, 1 key is missing

do: pick up a 3rd key

expect: the exit is open

pickup Touching the same key twice counts once.

do: touch the same key twice

expect: the player holds 1 key

src/level/exit.js
// @clause level/exit/needs player/movement/positionimport { near } from "../player/movement.js";// @clause end// @clause level/exit/layoutexport const keys = [  { x: 100, y: 100 },  { x: 380, y: 200 },  { x: 200, y: 400 },  { x: 440, y: 300 },];export const exit = {  x: 440,  y: 20,};export function placeKeys(room) {  for (const key of keys) {    room.add(key);  }  room.add(exit);}// @clause end// @clause level/exit/pickupexport function pickUpKeys(player, keys) {  for (const key of keys) {    if (!key.taken && near(player, key)) {      key.taken = true;      player.keys += 1;    }  }}// @clause end// @clause level/exit/doorconst KEYS_NEEDED = 3;export function canOpenExit(player) {  return player.keys >= KEYS_NEEDED;}export function keysMissing(player) {  return Math.max(0, KEYS_NEEDED - player.keys);}// @clause end// @clause level/exit/winexport function reachedExit(player, exit) {  return canOpenExit(player) && near(player, exit);}// @clause end// @clause level/exit/lookexport function exitColor(player) {  return canOpenExit(player) ? "limegreen" : "gray";}export function keysLabel(player) {  return `Keys missing: ${keysMissing(player)}`;}// @clause end// @clause test level/exit/doorconsole.assert(!canOpenExit({ keys: 2 }), "2 keys: the exit stays shut");console.assert(keysMissing({ keys: 2 }) === 1, "2 keys: one is missing");console.assert(canOpenExit({ keys: 3 }), "3 keys: the exit opens");// @clause end// @clause test level/exit/pickupconst p = { x: 0, y: 0, keys: 0 };const one = [{ x: 0, y: 0 }];pickUpKeys(p, one);pickUpKeys(p, one);console.assert(p.keys === 1, "touching the same key twice counts once");// @clause end