Keeping Specs, Tests, And Code In Sync In AI Development AI coding agents cause specs, tests, and code to drift apart, creating false confidence and circular test coverage. A traceability model using six identifiers—requirement ID, design decision ID, task ID, test ID, commit/PR link, and change file—enforces links between requirements, tests, and code, with CI checks catching drift before merge. The approach, outlined by developer Glukhov, addresses specification rot in AI-assisted development by making traceability queryable data rather than shared understanding. Keeping Specs, Tests, And Code In Sync In AI Development Stop AI agents drifting from specs, tests, and code. AI coding agents ship features fast, but specs, tests, and code quietly drift apart. This guide covers a traceability model, spec-to-test and spec-to-code mapping, and the CI checks that catch drift before a merge. A spec that nobody re-checks against the running system is worse than no spec at all, because it creates false confidence. Reviewers trust the document instead of the diff, and an AI agent asked to “follow the existing pattern” will happily follow whatever the code actually does, even when that contradicts the requirement it was supposed to satisfy. The fix is not more documentation. It is a small, enforceable link between four things that already exist in most repositories: the requirement, the design decision behind it, the tests that prove it, and the commits or pull requests that changed it. Once that link exists as data rather than as a shared understanding, you can query it. You can ask which requirements have no test coverage, which tests no longer map to any requirement, and which files changed in a pull request without a matching requirement ID. That query is the actual deliverable of this article, and the rest of the post walks through how to build it with tools you likely already run. The Drift Problem: Why Specs, Tests, And Code Fall Out Of Sync Drift shows up in four recognisable shapes, and AI-assisted teams tend to hit all four faster than teams that write every line by hand. Spec changes, code does not. A requirement gets clarified in a follow-up conversation or a comment thread, but nobody regenerates or edits the implementation to match. Code changes, spec does not. An agent or a developer fixes a bug or refactors a module, and the spec keeps describing the old behavior as if it were still current. Tests cover implementation, not intent. Unit tests assert what the code currently does, which is circular: they pass by construction even when the code satisfies the wrong requirement. Pull requests do not reference requirements. Reviewers approve a diff on the strength of “looks reasonable” because there is no explicit claim to check it against. Recent process research on AI development frameworks identifies specification drift as a recurring risk precisely because agents regenerate code quickly and repeatedly, and each regeneration is a fresh opportunity for spec and implementation to diverge a little further. The Spec-Driven Development vs Vibe Coding https://www.glukhov.org/ai-devtools/vibe-coding/spec-driven-development-vs-vibe-coding/ debate is really an argument about this same failure mode: a spec that nobody enforces degenerates into the same drift you get without one, just with extra ceremony. Modern spec-kit-style workflows increasingly frame this as specification rot : the spec keeps looking authoritative while quietly losing its connection to what the system actually does. The core definition of spec-driven development https://www.glukhov.org/app-architecture/documentation/what-is-spec-driven-development/ treats the spec as the source of truth, but a source of truth only stays true if something keeps checking it against reality. A Traceability Model For AI-Assisted Development A workable traceability model needs six identifiers that connect a business requirement all the way down to the lines of code and the pull request that implemented it. Most teams already have three or four of these; the missing ones are usually the design decision ID and the explicit link back from tests and commits. | Identifier | Lives in | Example | |---|---|---| | Requirement ID | requirements.md or spec tool | REQ-014 | | Design decision ID | ADR / decision record | ADR-0032 | | Task ID | task breakdown or issue tracker | TASK-014-3 | | Test ID | test file or test name | test req 014 password reset | | Commit / PR link | Git history | PR 482 | | Changed files | Git diff | auth/reset.go , auth/reset test.go | The relationships between these identifiers form a graph rather than a straight line, because one requirement can spawn several tasks, and one pull request can touch several requirements at once. REQ-014" -- ADR "Design Decision ADR-0032" ADR -- TASK "Task TASK-014-3" TASK -- CODE "Code Change auth/reset.go" TASK -- TEST "Test test req 014 password reset" CODE -- PR "Pull Request 482" TEST -- PR PR -- COMMIT "Commit history" Storing this graph as structured data, not prose, is what lets you query it later. GitHub’s Spec Kit ecosystem has moved in exactly this direction: extensions like spec-kit-trace scan REQ-XXX tokens embedded in spec files and test files and generate a deterministic matrix from that literal text match, deliberately avoiding fuzzy name-based guessing that produces silent false positives. Spec-To-Test Mapping: Turning Acceptance Criteria Into Test Cases Every acceptance criterion in a spec is, by construction, a behavioral assertion: given this state, when the actor does this, then the system should respond that way. That is already the shape of a test case, which is why the strongest SDD workflows generate tests from the same acceptance criteria that generate the code, instead of asking the code-generating agent to also invent its own tests after the fact. A widely used format for writing these criteria is EARS Easy Approach to Requirements Syntax , which forces each requirement into an unambiguous, testable pattern such as “When