[KongchangAI]
· 4 min read· 2,201 words

Living Specs: Making AI Coding Specs That Don't Die on Arrival

Living Specs: Making AI Coding Specs That Don't Die on Arrival

Replace write-once-and-freeze spec archives with one continuously updated, co-located, auto-loaded and auto-folded living spec per capability.

The article addresses the problem in traditional spec-driven development where specs are forgotten once written and pile up into unread archives, and proposes living specs: one continuously updated spec per capability recording only requirements and scenarios; auto-loaded via file glob matching during specify (including parents); folded back at completion as ADDED/MODIFIED/REMOVED/RENAMED so changes enter the same PR diff and get reviewed. Specs can be centralized or co-located with code under a sidebar tree. For bypassed quick fixes, drift detection and living-sync replace hard gates, preserving human edits by syncing rather than regenerating.

Write a spec, hand it to an AI coding agent, ship the feature—and then that spec is never opened again. This is the pitfall almost every team trying spec-driven development falls into. The author of the VS Code extension SpecKit Companion, after writing 238 specs, discovered that his specs folder had become an archive that only grows and never shrinks.

His proposed solution is the living spec: a continuously updated spec corresponding to a current capability (such as billing, auth, or member profiles), automatically loaded at the start of every coding task involving that capability, and automatically written back at the end. This closed-loop mechanism is turning "specs" from a one-time snapshot into a reusable source of context for AI agents.

rss source: Living Specs in SpecKit

Three Structural Flaws of Traditional Specs

Starting from scratch every time

In the conventional spec-kit workflow, the four phases—specify, plan, tasks, implement—each produce a markdown file. When a new spec involves a particular dialog, the agent re-investigates its data model, surrounding components, and existing conventions—then writes down what it learned and files it into a folder that the next run won't read at all.

The same work gets repeated, re-derived from code every time. The author points out that this understanding was real and was recorded; the problem is only that it was archived rather than becoming the starting point for the next run.

Can't find "how this feature actually works right now"

This is the problem the author felt most acutely as a human developer. Taking the thread of "how tasks get marked complete" in his repo as an example, he iterated on it multiple times, each producing an independent spec. As a result, the folder held five related specs, four of which were superseded—but none indicated which one was currently valid.

He cites a view in the article: most teams pay for a spec that "sticks around" but actually get a spec that's forgotten the moment it's written. A spec is fundamentally a snapshot of intent at a moment in time, and snapshots don't update themselves. Stack up 94 of them and you get an archive, not documentation.

Agents only read what's nearby

When you ask Claude or another model to work on a feature without going through the spec workflow, it reads the files near the code you mentioned. The nearby code is real information, but there are two problems: adjacent code isn't necessarily a standard you endorse—the agent will copy conventions no one has vouched for; and the specs folder, the one place that records "expected behavior," is effectively unsearchable because it balloons quickly and makes it impossible to tell what's current—so the agent just works around it.

This leads to the core observation behind the whole design: a markdown file sitting right next to the code it describes will be read regardless of whether you go through the spec workflow, regardless of which vendor's model, regardless of the prompt. Location is itself a feature. The author also cites Chroma's research on context rot, showing that models forget earlier information before a session ends, and that a file which survives across sessions is the way to bring it back.

Context rot refers to the phenomenon where, as the context window gradually fills in a long session, a large language model's attention to and recall of earlier information declines. Chroma's research, using "needle-in-a-haystack" style experiments, found that even when key information remains within the context window, the model's accuracy at retrieving it drops significantly as context grows longer. For coding agents, this means architectural conventions and requirement background loaded early in a session may already be "effectively void" later on. The living spec approach isn't to expand the window but to solidify key conventions into a file on disk—re-read as needed in each new session, effectively resetting context back to a clean state and sidestepping the degradation problem.

Design Trade-offs of Living Specs

Inspired by OpenSpec

The author drew on OpenSpec's approach: maintain one persistent spec per capability, and fold changes back into it when they ship. He also analyzed 788 open-source repos using this pattern and found two key problems.

First, the fold-back step is manual, and skipping it is the beginning of a record's death—code keeps evolving, the spec stays put, and months later someone notices. Second, no one maintains text no one reads; even mature repos leave the purpose line as a placeholder. These two points directly shaped the differentiated design of SpecKit Companion.

Configuration: a single YAML file toggles it on

The feature is off by default until living-specs.yml appears in the project root. Each capability has a name and matching file globs:

# 📃 living-specs.yml
enabled: true
capabilities:
  - name: member-profiles
    match: ['src/features/profile/**', 'src/api/members/**']
  - name: directory-search
    match: ['src/features/directory-search/**']
    spec: src/features/directory-search/directory-search.spec.md # 👈 lives next to the code

match is the most important part—it determines which capability a change belongs to. The spec line determines where the spec file lives: omit it and specs are stored centrally under capabilities/ (OpenSpec style); include it and the spec lives in the folder of the code it describes. You can freely choose per capability, and /speckit.companion.living-move can migrate at any time.

Write only "what to do," not "how it's done"

A living spec contains only Requirements and Scenarios—no component names, endpoint paths, or data models. The author's explanation: code can be refactored beneath a spec without invalidating it, and the parts that go stale easily (how it's implemented) are exactly what the agent can read from the code at any time.


**Requirements and Scenarios** draw on the Gherkin syntax tradition of behavior-driven development (BDD), whose core is expressing a requirement as verifiable concrete behavior using Given/When/Then or When/Then structures. Unlike use cases, scenarios are deliberately kept small and specific; a spec typically consists of several requirements, each with multiple scenarios attached. This structure is especially friendly to AI agents: the requirement layer gives the constraint of "what must hold," the scenario layer gives inputs and outputs that can be checked against, and the agent can map each one during both implementation and acceptance. Because scenarios describe only externally observable behavior and don't bind internal implementation, the spec needs no changes when code is refactored.
# Member Profiles

## Purpose

Every Teamboard member has one profile: a name, a role, a team and a short bio.
This capability owns who may read a profile, who may change it, and what the
rest of the product is allowed to assume about its fields.

## Requirements

### A member edits only their own profile

A profile update MUST be accepted only from the member it belongs to or from a
workspace admin.

#### Scenario: a member opens another member's profile
- **WHEN** the viewer is neither the owner nor an admin
- **THEN** the profile renders read-only and the update endpoint refuses the change

A sidebar tree keeps "co-location" under control

If every spec were scattered across the source tree, navigation would become a disaster. The author's solution is the Living Specs sidebar view: all capabilities shown as a tree, grouped by location, with centrally stored and co-located specs in the same tree. Specs render as pages rather than raw markdown. The author considers this the key to making co-located specs practical—other tools haven't done this part.

The Fold-back Mechanism: Specs Maintained as a By-product of Delivery

Load before starting, rewrite after finishing

At the start of a spec run, the specify phase finds the living specs for the relevant area and loads them before drafting—and not just the leaf node you named, but also its parents in the hierarchy, so the new spec starts from the current facts of the whole area. plan and tasks work from that.

Rewriting happens at the end. The agent appends a short section to the feature's own spec for each capability it touched, explaining what changed. When the feature is marked complete, this section folds back into the corresponding living spec: new requirements are appended, modified requirements replace old text, and removed requirements are deleted. The four operation types are ADDED, MODIFIED, REMOVED, and RENAMED.

Because this section is written in the feature spec, it shows up in the same PR diff as the code—so living spec changes are actually reviewed rather than blindly applied. If a run loaded a living spec but wrote nothing to it, that's called out at completion with a "nothing to fold back" notice, making a skipped fold-back a visible event rather than a silent failure.

The author gives his first real case: a webview rendering change. The ticket said nothing at all about HTML security, but the living spec for that area did—and that rule carried through every phase, eventually becoming a checkable acceptance item. The rule reached the work site because the spec was loaded, not because someone remembered it.

Countering the "specs will freeze" objection

The author directly addresses Kent Beck's criticism of spec-driven development: the problem was never "you write things down," but "after you write them down, they freeze while the code evolves." Folding back at completion is the unfreezing.

He also acknowledges the validity of the opposing view: executable tests are a more durable contract because they stay intact across model upgrades, whereas prose gets reinterpreted. But he argues that living specs carry something tests cannot, and that's the reason this feature exists.

Drift in software engineering originally refers to artifacts gradually falling out of sync—for example, divergence between documentation and code, or between interface definitions and implementations. In the living spec context, drift means code has changed but the corresponding capability's spec hasn't been folded back. Its difference from "outdated specs" lies in detectability: outdated is a vague judgment, whereas drift can be precisely computed by comparing file changes since the last spec commit and attributed per capability. Drift detection thus becomes a lightweight governance tool—it doesn't stop developers from bypassing the process, but makes "what was bypassed" explicitly visible in the sidebar and command output, turning technical debt from implicit to trackable.

Handling the "Bypassing the Process" Reality

Big spikes, exploratory features, Friday quick fixes—the author admits he himself often skips spec-kit. And on a team of dozens, not everyone uses the same AI workflow, which is normal. Code moved, spec didn't—that's exactly how records died in those 788 repos.

Companion handles this with drift detection rather than hard gates. The sidebar shows a drift badge for capabilities whose code changed since the last spec commit:

# 👇 what moved, uncommitted work included
/speckit.companion.living-drift --working

# 👇 update every affected spec
/speckit.companion.living-sync

Drift detection lists changed files per capability and explains how they slipped: tracked files went through the process but changes weren't folded back (missed sync); unspeced files changed entirely outside the process (the first thing to look at). The sync operation groups changes by capability, reads the actual diffs, and updates the affected specs.

The key design is "edit rather than regenerate": every requirement and scenario the change didn't touch is preserved verbatim—sync never flattens someone's hand-written content. Edits are left uncommitted so code and spec get reviewed together.

Adoption Cost and Open Questions

This mechanism requires the team to genuinely adopt it: add domains, organize teams, decide where living specs go, and consciously handle drift. The author candidly admits one unresolved question: the specify and plan phases collect a lot of information—data models, architecture, decisions—and currently the living spec keeps only "how the feature works," nothing else. Whether these artifacts should be persisted, whether they improve understanding of the feature or just add context, and whether they can stay maintainable—the author explicitly says this is an open question, not a roadmap item.

The getting-started path, in the article's order: install the extension, create living-specs.yml and set enabled: true, run /speckit.companion.living-adopt src/<area> on the area you explain most often and read the draft, walk through a full cycle and check the fold-back badge in Overview, and when drift appears run living-sync and commit the spec changes along with the code.

The 90-odd specs no one opened taught the author less than the few that get loaded every session. That line is probably the most distilled conclusion of the whole article.

One-line Summary

Replace write-once-and-freeze spec archives with one continuously updated, co-located, auto-loaded and auto-folded living spec per capability.

Paragraph Summary

The article addresses the problem in traditional spec-driven development where specs are forgotten once written and pile up into unread archives, and proposes living specs: maintain one continuously updated spec per capability (e.g., billing, auth) that records only requirements and scenarios, not implementation details; during the specify phase, match file globs and auto-load relevant specs (including parents); at feature completion, fold changes back into the spec as ADDED/MODIFIED/REMOVED/RENAMED, with the folded content entering the same PR diff as the code so it's actually reviewed. Specs can be stored centrally or co-located next to the code, navigated through a unified sidebar tree. For quick fixes that bypass the process, drift detection and living-sync are used instead of hard gates, and sync preserves human content by editing rather than regenerating.

#living specs#spec-driven development#AI coding agents#SpecKit Companion#context rot#drift detection#BDD
Share:

Related articles