The Method: Spec-Driven, Agent-Assisted Development¶
Purpose: the method this course teaches you to run. Read it once early, then come back to it when a phase starts. Your team practises it on a real client project all term; the lectures teach the classic software engineering topics at the moments the method demands them.
The one-sentence version¶
Breadth-complete architecture + requirements → prove with one vertical slice → fan out per use case, looping fixes back into both the requirements and the architecture.
That is "big design up front" with its two classic failure modes filed off: the architecture is complete in breadth but shallow in depth (so you don't over-commit), and it is validated by a working slice before you build on it (so you don't build on a guess). An AI coding agent does the per-use-case design and implementation against a human-authored, version-controlled contract.
Motivation¶
LLM coding agents are fast, but unguided they drift: ad-hoc prompting produces code with no durable contract, no shared vocabulary, and no record of why. "Vibe coding" scales poorly past a toy: there is nothing to review the code against, nothing to keep two features consistent, and nothing to hand to the next contributor (human or agent).
The two traditional answers each fail differently for agent-driven work:
- Big Design Up Front (waterfall-ish). Specifying everything to full depth before building is wasteful and brittle: most of the detail is invalidated on contact with code, and the agent will happily implement a design that reality already contradicts.
- Pure agile / emergent design. Under-specifies. An agent has no stable contract to build toward, so each prompt re-derives intent and the result diverges run to run.
The methodology here is the middle path tuned specifically for a human + coding-agent team: give the agent a stable, reviewable contract (the spec) and a complete-but-shallow map (the architecture), validate the map early with a real slice, then let the agent realize one use case at a time, with an explicit duty to challenge the contract when it's wrong, and a traceability ledger that keeps spec and code honest with each other.
Core principles¶
- The spec is the source of truth, and a durable artifact. Requirements live as version-controlled Markdown in the repo (glossary, vision & scope, use cases, business rules, SRS), not as throwaway prompts. The code implements the spec; when they disagree, one of them is a defect.
- Breadth-complete, depth-shallow architecture: structures decomposed, decisions driven by quality attributes. Architecture is two things at once: the structures (components and cross-cutting subsystems and their relationships) and the significant, hard-to-change decisions that determine the system's quality attributes. The structures come from functional decomposition (the use-case areas map to bounded contexts), and the architecture-of-record names every one (so the map is whole) but stops at responsibilities and relationships (so nothing is over-committed). The significant decisions come from the other direction: the architecturally significant requirements (ASRs) drive them, because functionality can be satisfied by many structures and it is the quality attributes that pick among them. The ASRs are the prioritized quality attributes plus the hard constraints, and they reuse the existing
PER-*/SEC-*/CO-*/ … handles rather than a new ID space. What to commit up front follows a reversibility litmus (the architecture sibling of Principle 8): decide the hard-to-reverse, system-wide, ASR-driven things early; defer the reversible, local ones to per-area design, against real code, just-in-time. - The use case is the unit of work, citation, and test. Each use case is a high-level functional requirement; its steps + associated information are its acceptance criteria; traceability and tests are tagged to it. Use cases are kept small enough that "UC-X passes" is a meaningful statement.
- The spec is authoritative but not infallible: the challenge loop. The agent is not a stenographer. When a step is ambiguous, an assumption breaks against the code, requirements contradict, or the spec suggests something that isn't best practice, the agent surfaces it (asks or pushes back) rather than silently complying or silently inventing. Fixes go back into the spec, then the design is re-derived.
- Bidirectional traceability and co-evolution. Traceability is a two-direction graph, not a one-way chain (see The traceability model): a matrix maps requirements → design → code → tests on a functional and a non-functional axis, checked forward (is every objective built?) and backward (does every artifact justify itself?). It is the connective tissue: changes loop back into the requirements and architecture, not just forward into code.
- Stable identifiers decoupled from position. Requirements carry position-independent IDs (
FEAT-<slug>,UC-<AREA>-<slug>,FR-<AREA>-<slug>,BR-<slug>) so the spec can be reorganized without breaking citations, which is essential when many documents and the code all reference the same handles. - Human-owned levels, agent-built levels. Humans own requirements quality and the high-level architectural decisions; the agent owns turning an approved use case into a design-of-record and then into code + tests, behind explicit approval gates.
- Specify requirements, derive implementation. The spec pins down what is a requirement (business-meaningful, testable, and unsafe to guess) to full depth, but deliberately leaves incidental implementation detail for the agent to derive at build time. The data model is the exemplar: the domain model names entities, fields, coarse types, and relationships, and the business rules and use cases fix the constraints that carry meaning (key formats, status transitions, validation rules); but storage types, column lengths, nullability, indexing, and cross-cutting persistence mechanisms (audit metadata, optimistic-lock version, soft-delete markers) are derived when the code is generated, not enumerated field-by-field in the spec. This is why the SRS keeps no exhaustive data dictionary: a parallel field table would either restate constraints already owned elsewhere or pin implementation choices the method exists to delegate, manufacturing two definitions that drift. The litmus test mirrors the architecture's depth-shallow rule (Principle 2): if guessing it wrong would violate a requirement, the spec pins it; otherwise the agent derives it. A corollary for a fully spec-first build (no code yet at authoring time): the spec must never point down at an implementation as the source of truth for a requirement-bearing fact; the spec is upstream of all code. An existing codebase reverses the order, and Starting from an existing codebase covers it.
The artifacts: a spec→design→trace chain¶
The methodology is realized as a layered set of documents. The shape (as realized in Project Pulse under docs/, one spec set for the whole product):
- Requirements (
requirements/), the what, authored by humans: project-glossary.md: canonical domain vocabulary. Fixes the words used everywhere else (and in code identifiers and UI text). No synonyms.vision-and-scope.md: business objectives, risks, assumptions, major features.use-cases.md: behavioral specs as use cases, grouped by area, each a high-level FR.business-rules.md: cross-cutting policies/constraints (BR-*), cited by use cases and the SRS.software-requirements-specification.md: non-use-case functional requirements (FR-*), domain model, quality attributes, constraints, operating environment.- Design (
design/), the how, generated from the spec, in two levels: - Level 1: architecture-of-record. The breadth-complete, depth-shallow map: a single arc42/C4 architecture-of-record for the whole product (
docs/design/architectural-design.md), holding the platform context/container views and conventions every module inherits, plus each module's component view. Component boundaries for not-yet-designed areas are explicitly provisional. - Level 2: design-of-record, one per use-case area, revised in place. It cites the use cases and FRs it realizes and never restates them. Three sections are never dropped: the API contract (one row per endpoint or scheduled job: caller, request, success, and the error for each extension), the key decisions, each with the alternative it rejected, and the test list (one row per flow, covering every extension). Everything else follows one rule: pin what a wrong guess would break (a requirement, a business rule, a quality attribute, or the contract between two parts of the team), and leave the rest for the agent to derive.
- Traceability (
traceability.md), the spec→code map on two axes (see The traceability model): a functional matrix (one row per use case, carrying FR IDs, design doc, frontend/backend modules, tests, status, and the release it is planned for) and a non-functional matrix (one row per quality attribute → quality scenario → verifying test).
A companion product/guides split keeps shipped default content and build-guidance distinct from the spec itself, and an OPEN-ISSUES backlog (OI-n) tracks gaps still needed to make the spec implementation-ready.
Documentation standards¶
The artifacts above follow recognized industry templates rather than bespoke structures. The choice is deliberate, because a known template is what a newcomer should learn, and it lowers the cost of review (reviewers recognize the structure instead of decoding a custom one).
- Requirements follow Wiegers & Beatty (Software Requirements, 3rd ed.): the SRS, use-case, vision-and-scope, and glossary shapes.
- The architecture-of-record follows arc42 (Starke & Hruschka), using C4 (Brown) for the context and building-block views.
Adopt the canonical sections and ordering; drop or merge sections that genuinely don't apply (arc42 explicitly allows this) rather than padding with filler. Fidelity to the standard beats local optimization here.
Features and use case areas: different views, not different fragments¶
Two listings sit close to each other in the requirements docs and look like they should be the same list, and aren't. Conflating them flattens both, so it's worth naming the distinction once.
- In
vision-and-scope.md, the Major Features are stakeholder-visible capability themes: "what does the product do for the user?" A handful per product (Project Pulse has six), named and written in value terms, no behavioral detail. - In
use-cases.md, use case areas (UC-<AREA>-*) are behavioral groupings: "which actor-task interactions belong together?" Usually aligned with a bounded context in the code, so they double as the spine of the design and the traceability matrix. The area is also the stable namespace baked into everyUC-<AREA>-<slug>ID.
The relationship between them is many-to-many, and a feature is deliberately coarser than an area and never one-to-one with one. A single feature is usually realized by use cases across several areas (Administration & Course Setup spans course-section, team, student, instructor, account, and rubric); a single area can serve more than one feature (the glossary area lives under Requirements Authoring, but its terms also feed Requirements Traceability and Navigation and AI-Assisted Guidance and Feedback). The coarseness is load-bearing in two directions: forcing a 1:1 list either flattens features into a CRUD enumeration (down to substrate like account-setup and section-CRUD) or inflates UC areas into marketing buckets; and because the area is welded into every UC ID, coupling a feature 1:1 to an area would make every vision re-pitch threaten a UC-ID renumber, the exact thing the name-based ID scheme exists to prevent. Keep the two granularities visibly distinct: when a feature would land 1:1 on an area, pull it up to the theme that area belongs to.
Two health checks keep the two lists honest:
- Coverage. Every UC area should be reachable from at least one feature; otherwise there are use cases without a stakeholder-visible reason, and the spec has work the vision doesn't justify. (When we first ran this check on Project Pulse, the glossary and document-authoring areas had no feature pointing at them; both were real gaps, now covered by Requirements Authoring.)
- Right altitude, and never 1:1. A feature should describe a capability theme spanning several areas, not enumerate use cases or shadow a single area; a UC area should reflect the domain, not the marketing pitch. If a feature reads like "create / edit / delete X", it's a UC list mis-cast as a feature. If a feature names exactly one area, it's an area mis-cast as a feature; coarsen it. If an area name reads like a tagline, it's a feature mis-cast as an area.
A deliberate consequence: the Major Features keep no inline UC IDs. Each feature does carry its own position-independent FEAT-<slug> ID (so it is a first-class, citable node and the BO→feature→use-case edges are keyed by handle, not by prose name), but it does not enumerate the use cases under it; the feature → use-case-area map lives in traceability.md, keyed to the FEAT-<slug> ID and verified there. That separation keeps the features at value-altitude and frees the use-case catalog to organize by domain rather than by the marketing brochure.
The traceability model¶
The spine at a glance. A business objective is realized by a feature, which decomposes into use cases and cross-cutting FRs that are built and verified. The objective attaches once, at the feature; the use cases and FRs below inherit it (so the realization matrices carry no per-UC or per-FR objective column). Several layers hang off this spine:
BO ──served by──▶ Feature ──realized by──▶ Use Case ───▶ design → code → test
│ └─▶ non-UC FR ─▶ design → code → test
│
└─ the objective attaches ONCE, here at the feature; UC / FR below inherit it
off-spine overlays, each asking a different question than "is it built?":
Business problem / opportunity ──motivates──▶ BO (why this objective exists)
BO ──measured by──▶ Success Metric ──▶ evaluation route (was it ACHIEVED? post-deployment, not a code test)
Use Case / FR ──honors──▶ Business Rule (what policy bounds it? cited, never restated)
Quality Attribute ──▶ Quality Scenario (QS) ──▶ test (NFRs verified in parallel, never via a use case)
Risk ──threatens / is mitigated by──▶ BO / requirement / decision
Assumption ──conditions──▶ BO / scope / design
The rest of this section is the full picture behind this sketch; the Mermaid diagram below renders the same graph with every edge labeled.
Principle 5 calls traceability "bidirectional" and describes a matrix on two axes. This section makes the model behind it explicit, because the intuitive picture most people carry (a linear chain business objective → feature → use case → design → code) is incomplete in three ways that matter for keeping a fallible spec and generated code honest with each other. The chain is the right spine; it just isn't the whole shape.
1. It is a graph, not a chain. Every edge is many-to-many: one objective spawns several features, one feature decomposes into use cases across several areas (the many-to-many feature ↔ area relation above is one slice of this), one use case touches several code modules, and one cross-cutting module serves many use cases. So an edge reads "realized by one or more," and the structure is a directed acyclic graph, not a line.
2. It runs in two directions, and both are load-bearing. Forward (objective → code) answers coverage: is every objective actually built? Backward (code → objective) answers justification: why does this artifact exist? A requirement nothing traces up to is gold-plating; a feature nothing traces down from is unimplemented promised scope. Bidirectionality is not decoration: ISO/IEC/IEEE 29148 requires it, and /spec-build enforces both. Its forward check flags orphan scope, and its backward check flags unjustified requirements.
3. It has a vertical spine and orthogonal layers. Not every planning artifact sits on the objective→code line. Business problems motivate the objectives; risks and assumptions frame them; business rules and quality attributes cross-cut them; and glossary terms underpin all of it:
flowchart TD
PROB["Business problem / opportunity"] -. motivates .-> BO["Business objective (BO-*)"]
RISK["Risk (RI-*)"] -. threatens / influences .-> BO
RISK -. mitigated by .-> FEAT["Feature (FEAT-*)"]
ASM["Assumption (AS-*)"] -. conditions .-> BO
ASM -. conditions .-> DSGN["Design-of-record"]
BO -. measured by .-> SM["Success metric (SM-*)"]
BO -- realized by --> FEAT
FEAT --> UC["Use case = high-level FR (UC-*)"]
FEAT --> FR["Non-use-case FR (FR-*)"]
UC -- allocated to --> DSGN
FR -- allocated to --> DSGN
DSGN --> CODE["Code"]
CODE --> TEST["Tests"]
TEST -. verifies .-> UC
BR["Business rules (BR-*)"] -. constrains .-> UC
BR -. constrains .-> FR
QA["Quality attributes (PER-*, SEC-*, ROB-*, ...)"] -- operationalized by --> QS["Quality scenario (QS-#lt;slug#gt;)"]
QS -- allocated to --> DSGN
QS -. verified by .-> TEST
GLOSS["Glossary terms"] -. fix vocabulary of .-> UC
Reading the edges: a solid edge is the forward build/realization spine: "X is realized by / allocated to / built into Y" (objective → feature → use case/FR → design → code → tests, plus the parallel NFR chain attribute → scenario → design). A dotted edge is everything off that spine: the backward verification edges that close the loop (test → use case, scenario → test), and the contextual, constraint, and measurement relations from the off-spine nodes (problems motivate, risks threaten / are mitigated by, assumptions condition, business rules constrain, success metrics measure, glossary terms fix vocabulary). Solid answers "is every objective built?"; dotted carries the loop-back, the "why," and the constraints around it.
The nodes, grounded in Project Pulse's identifier spaces:
- Business problems / opportunities: motivate business objectives. They explain the pain, opportunity, or stakeholder value that makes an objective worth pursuing, and they are the primary source of the "why."
- Risks (
RI-*): threaten or influence objectives and project success; they are handled through mitigation requirements, design decisions, process controls, or explicit acceptance. They are not "realized by" code, so they sit off the build spine. - Assumptions (
AS-*): condition objectives, scope, and design choices. They state what must remain true for the plan to hold. If an assumption fails, the objective, scope boundary, or architecture may need to be revisited. - Business objective (
BO-<AREA>-*): the why. Its achievement is made checkable by a success metric (SM-<slug>: a baseline, target, and evaluation route), the outcome → objective backward edge that answers "was it achieved?", not just "was it built?". Most routes are empirical (survey, logs, rubric), validated post-deployment rather than by a code test; the BO measurement matrix records them. - Feature (
FEAT-<slug>): the stakeholder-visible capability, and the single anchor for the business objective. ABO-*attaches to the feature(s) that serve it, and the use cases and FRs below inherit their objective through the feature rather than each re-declaring it. Anchoring theBO → featureedge once (intraceability.md's Feature → use case area map) and composing it with feature → UC/FR is what keeps the spine clean. - Use case = high-level FR (
UC-<AREA>-*): observable behavior. Collapsing the textbook user-requirement / system-requirement split into one node (a use case is its detailed functional requirement: its steps + associated information) deliberately removes a whole traceability hop and its drift; this is the single biggest simplification the method buys. - Non-use-case FR (
FR-<AREA>-*): the cross-cutting subsystems (autosave, validation, AI orchestration, notifications, security) that no single use case owns. They sit beside the use-case layer, not below it. - Design-of-record → code → tests: the realization tail. Requirements are allocated to design, design is built as code, code is verified by tests. The verification edge is what closes the loop: a requirement with no test verifying it is not actually traced.
- Business rules (
BR-*): an orthogonal constraint layer, and a source of requirements rather than a built artifact. A rule ("only a course admin may create a course section") exists independently of any system (software, a manual process, or training could enforce it), so it is not a type of requirement but an origin of them, sitting on the spine's input side alongside business objectives. Two consequences for the model: a rule is cited, never restated as a parallel FR (the same word in the use-case step that enforces it, not a duplicated "shall" to drift), and it is traced transitively, verified through the use case or FR that enforces it, with no realization row of its own. This is the sharp contrast with a non-use-caseFR-*, which is a built node and does get its own design→code→test row: the question "what enforces a givenBR-*?" is a different relation ("constrains") than "what realizes a givenBO-*?". The rule-to-requirement map is many-to-many (one rule constrains many use cases; one use case honors many rules), which is itself why folding rules into FRs loses information. Coverage is still checked both ways: every enforceable rule must be cited by at least one requirement (an orphan rule is either dead policy or a missing requirement), with a carve-out for rules deliberately left to manual process or advisory guidance rather than software (e.g.BR-active-weeks, which says so in its own text). - Quality attributes (
USE-*,PER-*,SEC-*,AVL-*,ROB-*, …): the NFR spine runs in parallel, never through a use case. A quality attribute is operationalized by a quality scenario (QS-<slug>) and verified by a test. This is the spine most projects let float; naming it as its own axis is what keeps it honest. Quality attributes carry a second edge besides this verification one: as architecturally significant requirements they drive the architecture-of-record's key decisions (KD-*), each of which cites the ASR that forced it. That derivation edge makes "quality attributes drive architecture" (Principle 2) checkable rather than merely asserted. - Glossary terms: the consistency axis underneath everything, the same word in objective, use case, code identifier, and UI label.
Three relation kinds, then, not one: requirements derive from higher needs (the spine), are allocated to design, and are verified by tests, plus the orthogonal constrains (rules) and operationalizes (attribute → scenario) edges. Keeping them distinct is what lets the matrix answer "is X built?", "why does X exist?", and "is X verified?" as separate questions.
This model is instantiated, not just described. traceability.md carries it on two axes: a functional matrix (use case → FR/design/code/tests), with a companion register giving each non-use-case FR-* its own realization row, and a non-functional matrix (quality attribute → QS-<slug> → test). The QS-<slug> scenario definitions live in the architecture-of-record and the BR-* constraints in the business rules. /spec-build mechanically checks that every edge resolves and every node is covered both ways. For the QS-<slug> edge, which spans three files, it also checks that the scenario's substance (which attribute it binds and the threshold it asserts) stays consistent across the architecture-of-record, the SRS attribute, and the matrix, not just that the ID resolves. The model is the picture; those documents and checks are its enforcement.
Quality attribute vs. quality scenario: the two-artifact NFR split¶
Non-functional requirements are recorded in two artifacts drawn from two traditions, deliberately kept distinct rather than merged. A quality attribute (PER-*, SEC-*, ROB-*, …) is a Wiegers & Beatty SRS requirement: an atomic "shall" statement naming what must hold and to what threshold (PER-validation-speed: ReqLint results "within 3 seconds for 95% of runs"). A quality scenario (QS-<slug>) is the construct of the Software Engineering Institute and arc42: a six-part operational vignette (source · stimulus · artifact · environment · response · response measure) naming a concrete situation the architecture must withstand and the mechanism by which it does. The attribute lives in the SRS (the requirements spec, Wiegers). The scenario lives in the architecture-of-record (arc42), where the architecture is reasoned about and evaluated, as in the Software Engineering Institute's Architecture Tradeoff Analysis Method. The split is not accidental: it follows the project's two-template structure (Wiegers for specs, arc42/C4 for architecture).
Keeping both is a judgment call, not a law. A pure-Wiegers SRS with no scenarios is perfectly valid, and in Project Pulse most quality attributes carry no scenario at all; they trace directly to a verification route in the non-functional matrix's unpinned tail (SEC-https → deployment config; USE-first-session-success → usability testing). A QS-<slug> is added only when it earns its place by carrying something the attribute cannot:
- operating context the bare requirement omits: "within 3 s" is untestable without "at course scale: ~1,000 artifacts, ≤ 100 concurrent editors";
- an architecture commitment: the scenario's response names the tactic (deny at the
AuthorizationManager; migrate + staging-slot smoke check before swap; add a bounded context in ≤ 2 person-days), content with no home in a requirements "shall"; - bundling: one scenario ties several atomic attributes plus cross-cutting behavior (autosave cadence + edit-loss bound + lock collision) into one testable story, the unit a test actually exercises.
The division of labor is held in place by a single rule: the attribute owns the number; the scenario cites it, never restates it. A QS-<slug> measure references the attribute by ID and must not introduce a requirement-level threshold that no attribute defines. A number found only in a scenario is a requirement hiding in the architecture doc, and is promoted to a real attribute first (the PER-graph-load case). This preserves a single source of truth for every threshold and confines the scenario to its proper job (operational context + architectural response), so the two artifacts complement rather than duplicate. The cost of two homes is drift: the same scenario is described across three files (the threshold in the SRS attribute, the scenario definition in the architecture-of-record, and the attribute → scenario → test binding in the matrix), so the substance can diverge while every ID still resolves. The build checks therefore verify not merely that the QS-<slug> reference resolves, but that the right attribute is bound to the right scenario and that the cited number matches the attribute's "shall" statement, turning the cite-don't-restate rule from a convention into a checked invariant.
The lifecycle¶
Phase A: Author requirements and a breadth-complete architecture (human-led, often parallel)¶
Draft good-quality requirements first; develop the architecture-of-record alongside them, because much of "architecture" (host constraints, operating environment, external integrations) is itself requirements-level, so the two co-evolve. Deriving the architecture runs in two directions at once: the functional structure comes from decomposition (use-case areas become bounded contexts and components), while the significant decisions are driven by the architecturally significant requirements (ASRs). So the derivation step is: identify the quality attributes and hard constraints, prioritize them (importance × difficulty, a utility tree) to find the architecturally significant few, then let those drive each key decision, recording the decision with the ASR that forced it and the alternative it rejects (an architecture decision record). The exit criterion is breadth, not depth: every use-case area, component, cross-cutting subsystem, convention, and external integration is named and placed, and every architecturally significant requirement is bound to a decision; internals are deliberately deferred.
Phase B: Prove the architecture with one vertical slice¶
Before fanning out, pick one proving use case and take it all the way through: design-of-record → implementation → tests. Choose the one that exercises the highest-risk ASRs (the top of the utility tree), not merely a "typical" one, since the slice earns its keep by stressing the decisions most likely to be wrong. This validates the architecture while corrections are still cheap: boundaries that looked clean on the diagram meet real code, and the architecture-of-record is corrected from what was learned. Architecture proven by a working slice beats architecture proven by inspection.
When the slice merges, write the release plan: give every row in traceability.md a Release value, the release its work ships in. Risk usually orders the rest; a client can put value or a dependency first, and the pull request that sets the column says why. A use case not yet designed carries a wide coarse estimate, made by the team in planning poker, written on its issue, and replaced by the refined estimate once its design is approved, so the plan is a forecast that narrows as designs land.
Phase C: Fan out per use case¶
With the architecture validated, realize the remaining use cases one at a time. Design, implementation, and any agent review each start in a fresh agent session that reads only the merged documents, so nothing reaches the build from a conversation the reviewer never saw. A fresh /implement session is also a second questions test: whatever it has to ask, the design should have said. That session fixes the design inside the implementation pull request and names the change under Reviewer focus, so the reviewer reads it as a design change.
/design <UC>: turn the use case into an approved Level-2 design-of-record. A separately-reviewed stage that stops before code, and itself split into two beats, mirroring the RFC problem-then-solution rhythm./designfirst runs the challenge loop against the use case (surfacing ambiguity, contradictions, and assumptions that break against the code, and looping fixes back into the spec) to firm the problem, reaching the🔬 Problem-validatedstate. It then designs the solution and has the developer accept its shape (📐 Designed). Trivial use cases pass through both in one motion. If the area contradicts the provisional Level-1 map, the design revises Level 1 (module architecture) and records it; platform-level changes are confirmed separately. Approval is a merged pull request, reviewed and merged by a human. Its description carries the questions test: a fresh agent session, reading only the use case, its business rules, the architecture-of-record's Crosscutting Concepts, and the design, lists every point it would have to guess. Each guess is either fixed in the design or kept with a one-line reason. No implementation branch opens until the design merges./implement <UC>: build from that approved design (plan → code → tests), extending the existing packages rather than forking the architecture. Commit in steps a reviewer can read, cut along the design: the trunk (code many paths depend on, code that changes behavior existing users rely on, or code that is hard to roll back) first in a commit of its own, then one commit per leaf (new code reached only through the new feature), with any security rule or schema migration alone in its commit. The agent pauses once, after the trunk commit, for the developer to read it before the leaves are built. Code the team accepted without understanding is cognitive debt, and it compounds.- Review the implementation pull request against the contract: the use case with every extension, its business rules, and the design-of-record's API contract and test list. A diff shows what was added, never what was left out, so check each test-list row as covered or not. Set depth by risk and write the triage into the review: read the trunk line by line, and check each leaf through its tests.
- Close the use case after its pull request merges, before the next one starts:
- The branch is merged and deleted, and the traceability row is honest about Build and Verify.
- Spec and architecture fixes the work surfaced are merged; anything still open is logged in
OPEN-ISSUES. - The actual hours are recorded beside the estimate in the calibration table, with the owner's five-minute retrospective: which part missed most, what was not known when estimating, and what changes next time.
- The reviewer asks what the agent had to be corrected on more than once and turns it into a rule in the agent charter (the conventions files the agent reads at the start of every session; see The tooling instance), by a small pull request. Where the new rule applies to code already merged, open an issue for that code rather than widening this pull request.
- Revise the release plan: is the next use case still the right one, and do the remaining estimates, corrected by the calibration ratio, still fit the client's date? Move rows by pull request, saying why. Then
/clear.
Phase D: Continuous co-evolution¶
During design or implementation, the team can always return to adjust both the requirements and the architecture. The challenge loop (Principle 4) plus traceability (Principle 5) make this the normal case, not an exception: the spec, the architecture, and the code are kept in agreement as a standing invariant.
What a change reopens. Phase A's exit criterion is the test. A change that stays inside a named use-case area and adds no architecturally significant requirement is a spec edit: amend the use case or rule, then revise that area's design-of-record through the gate. A change that adds something Phase A must name (a use-case area, a component, an external integration, a cross-cutting subsystem), or that adds or re-ranks an architecturally significant requirement, reopens the architecture-of-record first and places the new element there; a module-level change can ride in the area's design pull request (Phase C step 1), and a platform-level one is confirmed on its own. If it overturns a key decision, record a superseding KD-* with the requirement that forced it before any area designs against it. A scope change starts one level higher, in vision and scope, so its new use cases trace up to a feature, and each enters the release plan with a Release value.
Starting from an existing codebase¶
Phases A to D assume the spec comes first. An existing system usually arrives with code and little documentation, and the method applies once its documents exist. The agent drafts them from the code, and one rule keeps that safe: the code is evidence, never authority. A spec recovered from code describes what the code does, defects included. A missing permission check, read back by an agent, becomes a requirement nobody chose.
- Recover in Phase A's order: glossary, use cases, business rules, then the architecture-of-record as built. The agent reads the code, the UI, the database schema, and whatever notes exist, and marks every statement it drafts as recovered.
- Confirm with whoever owns the intent: the client, or the maintainer of an open-source project, never the team alone. Each recovered statement is confirmed as intended, corrected, or marked a defect. A defect becomes an
OPEN-ISSUESentry or an issue, not a requirement. - Design-of-record only where work lands. An area gets its design when a change first touches it, written as built: the decisions the code already made, with every departure from the confirmed spec flagged. Project Pulse's
/designdoes this for areas coded before the design workflow existed. - Traceability starts honest. A recovered use case is built but not verified until a test exercises it. The first tests on old code pin down confirmed behavior before anyone changes it.
Then Phases B to D run as usual. The proving slice still earns its keep: choose the first change in the part of the old code you trust least.
Scaling the method to the change¶
The full artifact set is sized for a team with real stakeholders and a system meant to outlive the term. A smaller change does not pay the full cost. The litmus is Principle 8 applied to whole documents: keep a document when a wrong guess in it would break something someone cares about.
| Size of the change | What it keeps |
|---|---|
| A bug fix | No /design. If a use case already specifies the behavior, the code is wrong: fix it, add a regression test, and cite the use case. If no use case covers the behavior, the specification is the defect: add the extension first, then fix the code against it. |
| A small change inside one area | Amend the use case and add a row to its design's test list. Revise the design itself only for a decision that would be hard to reverse. |
| A small project (one or two people, a few use cases, short-lived) | A floor of three: the agent charter, opening with a short vision and scope (purpose, users, what is out of scope) and carrying the crosscutting rules; use cases with their extensions, business rules written inline; and a test list per use case. The glossary, the SRS, the architecture-of-record, and the traceability matrix return as stakeholders, team size, or the system's expected lifetime grow. |
| A team, real stakeholders, a long-lived system | The full method, as above. |
The floor keeps behavior, not just intent. Extensions are where an agent guesses most, and the test list is the definition of done; a floor of purpose and technology alone leaves both to the agent.
The human–agent division of labor¶
| Concern | Owner |
|---|---|
| Requirements quality, scope, vocabulary | Human |
| High-level architectural decisions (platform-wide) | Human (agent proposes, human confirms) |
| Turning an approved use case into a design-of-record | Agent, behind a design-review approval gate |
| Implementing an approved design into code + tests | Agent |
| Reviewing the implementation pull request against the contract | Human |
| Module-level architecture revision when an area contradicts the map | Agent, surfaced in the design review and recorded |
| Cross-document consistency checks | Agent (tooling) |
| Judgment calls, ambiguity resolution, "is this a good idea?" | Human, prompted by the agent's challenge loop |
The approval gate between design and code is load-bearing: design is reviewed as a deliberate stage (like requirements), so the shape is corrected while it is cheap, not reverse-engineered from code later.
Where this sits among spec-driven tools¶
Several tools package spec-driven development for coding agents; as of October 2026 the two most visible are GitHub's Spec Kit and Amazon's Kiro. Böckeler (Understanding Spec-Driven-Development: Kiro, spec-kit, and Tessl, 15 October 2025) sorts such tools by what happens to the spec after the work: spec-first (written before a task, then left behind), spec-anchored (kept, and used to evolve and maintain the feature), and spec-as-source (humans edit only the spec, never the code). She finds Spec Kit and Kiro spec-first in practice: each change gets its own set of spec files, and in Spec Kit the spec lives "for the lifetime of a change request, not the lifetime of a feature." Both tools have moved since: Kiro now derives property-based tests from its requirements and keeps "a clear, traceable link between your requirements and the tests that validate them" (Correctness, August 2026), and Spec Kit's own methodology aims past spec-anchored, at code "continuously regenerated" from the spec (spec-driven.md). This method is spec-anchored by design: one living specification for the product, one design-of-record per area revised in place, and traceability that checks the two against the code. The code stays a source file that people review and edit, so the chain runs from requirement to decision to test to code, and is read backward too: code no requirement explains shows up as an orphan.
The pieces correspond, concept first, because tool names change faster than ideas:
| Concept | Spec Kit | Kiro | This method |
|---|---|---|---|
| Standing rules for the agent | constitution | steering files | the agent charter, pointing at the architecture-of-record's Crosscutting Concepts |
| What to build | a spec per feature | requirements.md per feature (EARS) |
use cases, business rules, and the SRS, one set for the whole product |
| How to build it | a plan per feature | design.md per feature |
the design-of-record, one per area |
| The work broken down | tasks | tasks.md |
the design's test list, sub-issues, and trunk-then-leaf commits |
| Cross-document consistency | /speckit-analyze, an optional check across one feature's spec, plan, and tasks |
Analyze Requirements, a check on the requirements before design; property-based tests linked to the requirements they check | /spec-build and traceability, across the whole product |
The cost, stated plainly. Böckeler's sharpest objection, "I'd rather review code than all these markdown files," applies here with more force, because this method keeps more documents than either tool. Its defenses are the ones above: every fact has one home and is cited rather than restated (business rules, quality-attribute thresholds, and Principle 8's refusal of a parallel data dictionary), a design is reviewed for its decisions rather than its prose, and the tooling caps each pull request section at three lines.
The tooling instance (Project Pulse)¶
The methodology is tool-agnostic, but Project Pulse instantiates it with Claude Code and a small set of repository-local slash commands plus a machine-and-human conventions file:
CLAUDE.md(root and per-subtree): the working reminders, the architecture-of-record pointer, and the authoring rules, read by both humans and the agent on every session. The binding code conventions and cross-cutting concepts have one normative home, the architecture-of-record's Crosscutting Concepts section (arc42 §8); each charter restates a rule only as a short imperative that links back to it. The charters are what the agent has in context by default, so every rule it needs while coding appears there at least as a one-liner, while the rationale lives once, in the architecture-of-record, where it cannot drift into two versions. This is how the contract stays enforced rather than aspirational./design: Phase C step 1 (use case → design-of-record; may revise the module architecture)./implement: Phase C step 2 (design → code + tests). It reads Crosscutting Concepts in full before coding and checks the diff against it before running tests, so conventions hold across sessions even where nearby code has drifted from them./spec-build: mechanically verifies and resyncs cross-document consistency (anchors, ID resolution, UC↔traceability coupling, terminology)./sync-checkaudits spec-to-code conformance.
The running case study is the RAM (Requirements Authoring & Management) module, itself a tool for authoring requirements, developed spec-first inside the larger Project Pulse platform.