Skip to content
All insights
Brian Humphrey

Building Kalendar, Part 2: The Docs Are the Contract, the Code Is the Fulfillment

In most codebases the code is the source of truth and the docs rot. Kalendar inverts it: the spec is the only normative artifact, and the code is annotated as fulfilling it. How a behavioral contract traces to the exact code and tests that satisfy it, and the time that traceability caught what I would have missed.

Agentic SDLCAI-native development

In most codebases, the code is the truth and the documentation is a rumor about it. The docs describe what the code did at some point in the past, they drift, and eventually nobody trusts them. Kalendar runs the other way around. The specification is the only normative artifact. The code is downstream of it, annotated as fulfilling a contract it did not get to define.

This is Part 2 of a series on building Kalendar, a real product, mostly with an AI coding agent. Part 1 was about the discipline in general. This one is about the single idea the whole thing rests on: the docs are the contract, and the code is just the fulfillment.

The repository is split exactly that way. One tree, docs/, is the contract. The application code is the fulfillment. One rule governs both: one owner per field, no fact authored in two places. If you want to know what the system is supposed to do, there is exactly one place to look, and it is not the code.

A spec is a contract, not a wiki page

The word “spec” is doing heavy lifting, so let me show you a real one. Here is the heart of the login-and-logout spec:

WHEN a user presents a valid, unexpired, unused magic-link credential
THE SYSTEM SHALL establish an authenticated session for the associated account.

That is not prose. It is a formal, testable statement of observable behavior, written in a requirements syntax that forces precision. Every spec is built from statements like it, grouped into primary behavior, secondary behavior, constraints, and unwanted behaviors. The unwanted ones carry as much weight as the wanted ones:

IF a user presents an expired magic-link credential
THEN THE SYSTEM SHALL deny the login.

A spec describes what must be true from outside the boundary, and nothing about how. It never names a database or a function. That is deliberate. The contract has to survive the implementation changing underneath it, which, as Part 1 covered, it did when I swapped the entire platform and the specs did not move.

This is also where a lesson from an earlier post shows up again. Vague in, vague out. A loose requirement produces a loose result, whether the thing reading it is a junior engineer or an agent. The formal syntax exists to make the requirement impossible to skim past.

The contract is wired to the code, in both directions

Here is where it stops being a nice document and starts being load-bearing. Every spec carries front-matter that ties it to its fulfillment:

status: ratified
satisfied_by: [packages/auth/src/use-cases/login.ts, apps/web/src/app/login/page.tsx, ...]
verified_by: [TP-AUTH-001, CompleteLogin.test.ts, logout.e2e.ts]

satisfied_by is the list of code files that fulfill the contract. verified_by is the test plan and the tests that prove it. Read that direction carefully. The code does not own the truth and then get described by a doc. The code declares which contract it satisfies. The spec is the source, the code is the fulfillment, and the link between them is explicit and machine-readable.

Traceability you cannot fake

All of those links roll up into one artifact: a requirements traceability matrix, a table with a row for every spec, its status, the code that satisfies it, and the tests that verify it. A linter generates it, and it carries a blunt comment at the top: do not hand-edit. A human cannot fudge it. If a spec claims to be satisfied by code that does not exist, or sits ratified without the verification it should have, the build fails.

The payoff is simple and rare. You can point at any behavior in the product and trace it, mechanically, to the code that implements it and the test that proves it. On every commit. One unbroken thread, from a sentence in a spec, to login.ts, to CompleteLogin.test.ts. Nothing floats.

The time it caught what I would have missed

Here is the moment this stopped being theory for me.

Early in the project, the authentication work had an acceptance criterion about session timeout. At that phase, honestly, I was not worried about authentication timeouts. It was not where my attention was. But the criterion was in the spec, and the test plan had a verification for it, so a test existed whether I was thinking about it or not. The initial implementation did not satisfy that criterion, and the test said so, out loud, before I would ever have thought to check.

That is the entire argument for this machinery, in one story. The contract remembered a requirement I had let slip out of my own head. It is the same idea from Part 1, that most of this documentation is memory for the machine, except here the memory reached back and caught a real gap. The discipline was not slowing me down in that moment. It was doing the worrying I had stopped doing.

Why the agent needs this

For an agent, the contract is the difference between implementing intent and guessing at it. An agent does not have to reverse-engineer what login is supposed to do from the code that currently does it. It reads the spec for the required behavior, the front-matter for where that behavior lives, and the matrix for whether it is actually covered. The contract is how intent survives the trip from my head, through a spec, into a machine’s hands, without me standing over its shoulder narrating.

What stays human

The part that does not delegate is sharper than “writing the spec.” It is the vision. Holding the picture of what this product is for, and sharing that view, is the product owner’s job, and it stays human. The agent cannot want anything for a parish. I can. Judgment calls like refusing to disclose whether an account even exists on a failed login are product decisions, not implementation details, and they live in the spec because that is where intent lives.

Crafting the spec from that vision, and refining it, is not a solo human act. It is exactly where an agent should help, and where the structure earns its keep a second time. Because the spec is formal and traceable, the agent can do something I cannot reliably do in my own head: check that my requirements do not contradict each other. I bring the intent. The agent helps turn it into a contract that is internally consistent, and tells me when two things I have asked for cannot both be true.

So it is not human-writes, machine-builds. I own the vision and the final say. The agent helps shape that vision into a coherent spec, fulfills the spec in code, and holds me to it. The judgment about what the product should be stays mine. The work of making that judgment precise, and keeping it consistent, is shared.

Next in the series: the contract does not write or fulfill itself. A set of agents does, each with a defined role, a product owner, a tech lead, a spec author, a task generator, and a reviewer. That org chart is next.


Have a project in mind?

Let's talk through the path from idea to production.

Book a consultation