Step 4 Specification: Write Down What Correct Means
An assistant can turn a sentence into a working program in a few minutes, and a program that works is not the same as a program that is right. The difference is a statement, written before the code, of what right means: which inputs the tool will meet, what it must produce for each, what it must do when the input is wrong, and what it must never do under any input. That statement is a specification, and this chapter teaches the builder to write one. It concerns Step 4 Specification, the fourth of the twelve steps defined in Chapter 4, and the developer function behind it, D1 Frame: understanding the problem and fixing intent in a form that people and tools can check.
The chapter makes one argument in five parts. A specification is the source of intent for both the builder and the assistant, and when code is cheap it is the most valuable text in the repository. Examples beat prose, because an example can be run and a paragraph can only be agreed with. Failure cases and never-do rules carry as much weight as the success cases. The test cases that judge the finished tool must be written by someone, and owned by someone, who is not the assistant that wrote the code. And the assistant helps with the drafting and does not decide what correct means. The practical guide ends in a specification record with a worked example for each of the two builds: SPC-NR-2031-0001 for Hanna Wikström's service-point-check and SPC-HM-2031-0001 for Katarzyna Nowak's claims-doc-check.

Figure 8.1: From intent to executable examples. The builder decides the rules and examples; the oracle is owned and written outside the generator, and a failing case changes the code and never the case.
The chapter builds on the earlier volumes. AI Developer, Chapter 5, treated framing for developers: the requirement that can be checked, the acceptance criterion, the specification record kept in the repository and the traceability from need to test (86, Nordqvist, 2026). AI Developer, Chapter 9, set the independence of the oracle, which this chapter applies to a builder. Architecture as Code (Chapter 4) set the habit of short, numbered records kept beside the code and changed by reviewed change (74, Nordqvist, 2025). The specification record is that habit applied to intent. The chapter assumes the workshop of Chapter 7: a repository, a branch for the assistant, AGENTS.md and a mandate.
What a developer does at this step
A developer who is about to build something does not begin with the program. The developer writes down what the program has to do in a form that a colleague can read in five minutes and a test can check in a second. The specification is not long. For a small tool it is a page or two. What makes it a specification and not a wish is that every statement in it can be answered with yes or no by looking at the tool's behaviour.
What a developer writes before the code
The specification has six parts, and each answers a question that the builder would otherwise leave to the assistant. The need says why the tool exists, with the evidence that the need is real. The rules say what the tool must do, each in one sentence with an identifier (R1, R2) so that it can be referred to. The examples show each rule at work with concrete values. The failure cases say what the tool does when its input is wrong. The never-do list says what it must not do whatever the input. The out-of-scope list says what it deliberately does not do. Open questions, assumptions and the use of AI complete the record, and they matter because they are where a specification is honest about what is not yet known.
The reason to write it before the code is the same for a professional as for a builder. A specification written afterwards describes what the program does, and what the program does is the thing in question. Written beforehand, it is the only account of intent that is independent of the code. AI Developer, Chapter 5, called the specification the artefact that becomes the most valuable text in the repository when code is cheap to generate and read less carefully than code a person typed (86, Nordqvist, 2026).
Acceptance criteria as examples
A rule such as "opening hours are equal in the portal and the phone script" is easy to agree with and hard to test, because it leaves unsaid what equal means, which weekdays count, and what the tool reports when they differ. An acceptance example removes that room. It is a small scene with concrete values that says what a correct tool does. The usual form has three parts, called given, when and then: the situation at the start, the thing that happens, and the result that must follow. Martin Fowler's description of the form says that it represents a test as a scenario in which the given sets up the preconditions, the when is the behaviour being specified and the then describes the expected results; it was developed by Daniel Terhorst-North and Chris Matts as part of behaviour-driven development, and it is a way of specifying behaviour by example (120, Fowler, 2013). The builder does not need the movement behind it. The form is useful because it forces the writer to say what is in, what happens and what must come out, in words that a person who is not a developer can check.
Three habits make an example good. It uses values that could occur and are specific: "SP-07 has Friday hours 09:00-16:00 in the portal and 09:00-15:00 in the phone script" and not "the hours differ". It states one thing, so that when it fails the failure points at one rule. And it covers a rule, which is recorded in the example's covers field: the rule says what must hold, the example shows it. The working standard of the book is one example per rule at least, and one per variant where a rule covers several kinds of case that the tool may treat differently. A rule without an example is a promise that nobody checks. An example without a rule is either a gap in the specification or an example that can be deleted.
The values should come from the world and not from the builder's imagination. Hanna took hers from entries that the Region actually publishes. Katarzyna's are synthetic, because her tool handles claims data, but they follow the real document types and claim types.
Failure and never-do cases
Success examples are the easy half. Real input arrives with missing fields, doubled rows, a date in the wrong format and a file that is not what its name says. A specification that lists only success cases leaves the failures to the assistant, and an assistant fills a gap with the most plausible behaviour. A 2025 preprint on underspecification in prompts reports that models infer unspecified requirements by default in 41.1 per cent of cases and that underspecified prompts are twice as likely to regress across model or prompt changes (154, Yang et al., 2025). The study concerns requirements inside prompts to language models, not software products, and the cited figures are the abstract's. Its relevance is the mechanism: what the specification leaves out is not left blank, it is decided silently.
A failure case therefore states an awkward input and the behaviour that is wanted. The wanted behaviour is often neither "crash" nor "carry on", and the choice is the builder's. If one service point's hours are written in a form that cannot be read, should the tool stop, skip the service point or report it? The specification of Nordvik Region's tool says that it reports the problem as a finding of its own kind and continues with the rest, because a weekly report that fails entirely on one bad cell is worse than a report with one extra line.
A never-do rule states something that must not happen whatever the input. It is the specification's counterpart of the mandate of Chapter 7, and the two should agree: the mandate bounds the assistant while it works, and the never-do rules bound the tool when it runs. For service-point-check the main one is that the tool opens its three sources for reading only and never writes to them. For claims-doc-check the list is longer: the tool never decides a claim, never ranks handlers or claimants, never sends a message, never writes to the claims core, never reads a free-text field and never logs anything but claim ids and counts. Each of these is a statement that a test can check or a reviewer can look for.
The oracle and its owner
A test oracle is whatever says, for a given input, what the correct output is. For a hand-written test the oracle is the person who wrote the expected value. The research literature calls the general problem of deciding what the correct behaviour is for a given input the oracle problem, and has surveyed it for decades (89, Barr et al., 2015). The builder's version is practical: when the tests pass, the question is who decided what the tests expect.
If the assistant writes the code and the assistant also writes the expected values, the check closes on itself. The tests show that the code does what the code does. This is the failure that AI Developer, Chapter 5, called circular verification, and its control, set in Chapter 4 as the independent oracle, is that some of the cases that judge a change are written by a person other than the one who wrote the change and not by the tool that generated it (86, Nordqvist, 2026). Applied to the builder, the rule has three parts. The oracle owner is a named person who is neither the assistant nor, ideally, the builder. The cases they write live in a place the assistant may not edit, in these builds tests/oracle/cases.yaml, which the mandate lists among the files never to change. And the owner draws the cases from the world (real notices, real claim files), so that they test the tool against reality and not against the builder's description of reality.
The specification record names the oracle owner and states the arrangement. The examples in the specification are the builder's, written to say what the builder wants; the oracle cases are the owner's, written to test whether the tool delivers it. The two sets overlap and should not be identical, and the difference is the point. At Hansa Mutual the rule R2 covers two claim types in one sentence and the specification gives one example for it, while Marek Zieliński's oracle cases cover every claim type. Chapter 11 shows what that difference caught.
What vibe coding does instead
The default is to describe the tool to the assistant in a few sentences and judge the result by looking at it. The description is a prompt, and the prompt is not kept: it scrolls out of the conversation, is changed by the next request and is not in the repository. The assistant produces a tool, the builder tries it on a few inputs, and when the output looks right the work is finished. Correctness is whatever the builder happened to try.
The gap is the one that the underspecification result describes. The prompt left things out, the assistant filled them in, and nobody recorded what it chose. The builder cannot say what the tool does with a malformed input, because that was never asked. Nor can the builder say what it will do after the next change, because the only specification is a conversation that has moved on. Later chapters pay for this. A reviewer cannot judge a change against intent that was never written, a receiving developer cannot take the tool over without it, and the takeover test of Chapter 4 includes the question of what the tool is supposed to do. A short written specification is the cheapest way to make the later steps possible.
How AI helps and where it misleads
The assistant is useful in the specification, in a particular role. It can draft rule wording from a rough description, propose failure cases that the builder had not thought of, check a draft for gaps against a checklist (what happens on an empty file, on a second run, on a date in the past) and turn a decided rule into a given-when-then example once the builder has said what the values are. It can read the draft back as a newcomer would and ask where it is unclear. In the delegation of the two builds, the work class specify is Level 1 Assisted: the assistant offers drafts and questions, and the builder adopts or rejects each line.
Spec-driven development is the name given to working from the specification first. In the description of an open-source toolkit that GitHub published in September 2025, the specification is a contract for how the code should behave and the source of truth that tools and agents use to generate, test and validate code, and the workflow runs through four phases: specify, plan, tasks and implement, with the developer verifying at each phase (53, Delimarsky, 2025). The source is a vendor's description of its own toolkit and not an evaluation of results, and the toolkit is an example of the pattern, not a recommendation. The pattern, that the written specification is read by the generating tool and the tests are derived from it, is the one this chapter follows. The difference in the book is who holds which phase: the assistant may draft the specification, and the builder decides it.
Four misleading behaviours recur, and the first three are failures of drafting that look like helpfulness.
The plausible requirement. A fluent draft contains requirements that read like requirements. "Applications are kept for 24 months" sounds right whether or not any rule says so. The control is provenance: each rule has a source, which is a person, a document or an observed fact, and a rule without one is marked as an assumption in the record.
Agreement. Language models fine-tuned on human feedback have been shown to tend to match the user's stated beliefs. A 2023 study found that five state-of-the-art assistants consistently showed sycophancy across four free-form text-generation tasks (43, Sharma et al., 2023). A builder who asks an assistant "isn't this specification complete?" is likely to hear that it is. The control is to ask for what is missing and not for approval: "list ten inputs this specification does not say what to do with", and to answer them from knowledge of the work.
Scope inflation. Extending a draft costs nothing, so each round tends to add a rule for a case somebody could imagine. No study of this has been verified for this book, so it is a working hypothesis and not a finding. The control is the out-of-scope list and a limit: a small tool with more than about eight rules is usually two tools.
Circular drafting. The assistant writes the rules, then the examples from the rules, then the code from both, then the tests from the code. Every step is consistent with the one before and none of them has touched the need. The control is the oracle of the previous section and the builder's own decisions at the two points that matter: which rules, and which examples.
Working with the assistant on the specification
The assistant needs the specification in a form it can read, and the form is the file in the repository. The specification lives at docs/specification.yaml, and AGENTS.md (Chapter 7) points to it and says what to do when it and the code disagree: stop and report. A short exchange shows the working pattern. The builder pastes the need and the rules and asks for questions, not answers:
Here is the need and my three rules for a tool that compares opening hours.
Do not write any code. List the inputs and situations my rules do not say
what to do with. For each, say which rule it touches. Do not propose answers.
The assistant returns a list. The builder picks the ones that are real, decides the behaviour for each, and writes the decision into the specification as a failure case or an assumption. The assistant is then asked to turn each decided rule into an example in the given-when-then form with concrete values, and the builder reads each one against the rule it covers. The step of reading a drafted specification is a review, and it has its own checks: does every rule have an example, does every example point at a rule, is each failure case decided by the builder and not by the draft, and does any text appear that nobody asked for. The script in the practical guide automates the first two.
A change to the specification is a change to what counts as correct. The assistant may propose it and may not adopt it: the Level 4 prohibition in Chapter 4 on changing the assistant's own mandate, permissions, credentials or test oracles applies, because a specification edit made in the same change as the code it is meant to check removes the check. The builder changes the specification, in a commit of its own, before the code that depends on it.
Proportion: what each tier needs
The tiers are those of AI Developer, Chapter 19, restated in Chapter 4 (86, Nordqvist, 2026). The table is the book's own proportion rule.
| Element | Tier A personal | Tier B team | Tier C business | Tier D project |
|---|---|---|---|---|
| Specification | A paragraph of intent and three examples in the README | A specification record SPC-* with rules, examples, failure cases and never-do rules |
As Tier B, with sources for each rule and a named reviewer who is not the builder | Not a builder's decision; developers write it |
| Examples | One for the main use | One per rule; real values where they exist | One per rule and per variant; real values from the work | |
| Failure and never-do | The one thing it must not do | Written for the inputs the sources can produce | As Tier B, and agreed with the data protection officer where personal data is involved | |
| Oracle | The builder's own check | A named oracle owner who is not the assistant; cases in a protected folder | An owner independent of the builder and the assistant; a sample drawn from real cases | |
| Assistant's role | Drafts; the builder decides | Level 1 Assisted: questions and drafts only | Level 1 Assisted; no assistant writes oracle cases | |
| Gate evidence | Examples exist and pass | SPC-* approved by the builder's manager, dated |
As Tier B; approval by the owner and a developer; valid until the rules change |
A change to a rule at any tier is a change to the specification and starts from it. At Tier A the three examples in a README are enough, and they are still written before the code is accepted.
Practical guide: Writing the specification
Practical guide: Writing a specification record with acceptance examples and failure cases
Purpose. Leave the builder with a short, approved specification in the repository that states the need, the rules, concrete examples for each rule, the failure cases, the never-do rules and the oracle arrangement, so that the assistant builds against it, the oracle owner tests against reality, and a developer can later tell what the tool is supposed to do.
Roles. The builder writes and decides. The assistant offers questions and drafts at Level 1 Assisted. The oracle owner (Saga Edvardsson at Nordvik Region, Marek Zieliński at Hansa Mutual) reads the draft and agrees to write the independent cases. The builder's manager or the person named in the build record approves. A developer, such as the receiving developer, is asked one question at the end: could someone build the tool from this page.
Inputs. The intent from Step 1 and the tier from Step 2; the workshop of Step 3 with docs/ in the repository; samples of the real inputs, or published entries, to take values from; the list of what the tool must never do, which usually comes from the tier decision.
Steps.
- Write the need in two or three sentences and the evidence that it is real: a count, a sample, a complaint log. If there is no evidence, say so in the record; the gap is information.
- Write the rules, one sentence each, numbered. Start from the verbs of the tool (compares, lists, drafts) and put every statement in a form that can be answered yes or no.
- Ask the assistant for the situations the rules do not cover, using the prompt above. Decide each one yourself and write it as a failure case, an assumption or an out-of-scope item.
- Write the never-do rules from the tier decision and from the mandate: what the tool must not read, write, send, decide or log.
- Write one example per rule in the given-when-then form with concrete values taken from real or realistic input, and add an example per variant where one rule covers several kinds of case. The assistant may draft; read each against the rule it covers.
- Name the oracle owner and agree where the cases will live. The owner writes
tests/oracle/cases.yamlfrom real material, in a folder the mandate forbids the assistant to change. The owner does not share the cases with the assistant. - Run the completeness check below, fix what it reports, and commit the specification on its own, before any code that depends on it.
- Have the assistant read
docs/specification.yamland summarise in its own words what the tool does and does not do. Compare the summary with your intent; each difference is a gap in the specification and not a fault of the assistant. - Ask the manager to approve, and record the date and the approver in the record. Mark each example
pendingundertestsuntil a test checks it.
Exit gate. The gate is met by showing the approved SPC-* record in the repository with a date and an approver; the output of the completeness check, which reports that every rule has an example and every example names an existing rule; the named oracle owner and the location of the cases; and the commit that holds the specification, earlier in the history than the code. The specification is valid until a rule changes, and is reviewed at the date in the record (90 days, illustrative).
Artefacts. The specification record, docs/specification.yaml (SPC-*), and the oracle cases of the owner in tests/oracle/cases.yaml. Each acceptance example becomes a test in Step 6 and Step 7.
The completeness check is a short script that a builder can run and that an assistant can help to write. It reads the specification and reports what is missing. It was run against both specifications of this chapter, and it reported a problem when a rule was removed and its example left behind.
"""Check that a specification record is complete: every rule has an example,
every example names a rule that exists, and every example has a test status."""
import sys
import yaml
spec = yaml.safe_load(open(sys.argv[1], encoding="utf-8"))
rules = {r["id"] for r in spec["rules"]}
examples = {a["id"]: a for a in spec["acceptance_examples"]}
problems = []
for rule in sorted(rules - {a["covers"] for a in examples.values()}):
problems.append(f"{rule} has no acceptance example")
for a in examples.values():
if a["covers"] not in rules:
problems.append(f'{a["id"]} covers {a["covers"]}, which is not a rule')
for part in ("given", "when", "then"):
if not str(a.get(part, "")).strip():
problems.append(f'{a["id"]} has no {part}')
for ident in sorted(set(examples) - set(spec.get("tests", {}))):
problems.append(f"{ident} has no entry under tests")
print("\n".join(problems) if problems else "specification is complete")
sys.exit(1 if problems else 0)
Run as python check_spec.py docs/specification.yaml; it prints specification is complete and exits with 0, or lists the problems and exits with 1. The same check can run in the pipeline from Chapter 13 so that a specification cannot silently fall out of step with itself.
A specification record
The record below is Nordvik Region's SPC-NR-2031-0001, in the form of AI Developer, Chapter 5 (86, Nordqvist, 2026), shown in two parts. The first part holds the need, the rules and the acceptance examples.
id: SPC-NR-2031-0001
title: service-point-check, weekly comparison of opening hours and notices
status: approved
date: 2031-10-15
build: BLD-NR-2031-0001
owner: Hanna Wikström # the builder; decides scope
oracle_owner: Saga Edvardsson # quality engineer; writes tests/oracle/cases.yaml
delegation: MND-NR-BLD-2031-01
need:
statement: >
The Region publishes the opening hours of its 24 service points in three
places that are updated by different people. Residents and phone-service
staff meet different answers.
evidence:
- source: phone-service count of calls categorised "wrong opening hours"
result: 41 in August 2031
read_by: Hanna Wikström
rules:
- id: R1
text: For each service point and weekday, the opening hours are equal in the portal export and the phone script.
- id: R2
text: A notice whose date range covers today appears in both the portal export and the phone script.
- id: R3
text: Every service point SP-01 to SP-24 is present in all three sources.
acceptance_examples:
- id: AC1
covers: R1
given: SP-07 has Monday hours "09:00-16:00" in the portal and "09:00-16:00" in the phone script
when: the comparison runs
then: there is no mismatch for SP-07 on Monday
- id: AC2
covers: R1
given: SP-07 has Friday hours "09:00-16:00" in the portal and "09:00-15:00" in the phone script
when: the comparison runs
then: there is exactly one mismatch, kind hours, service point SP-07, field fri, with both values shown
- id: AC3
covers: R2
given: >
notices.csv has a notice for SP-12 from 2031-12-22 to 2032-01-06, today is 2031-12-29,
the phone script shows the notice and the portal export does not
when: the comparison runs
then: there is exactly one mismatch, kind notice, service point SP-12, naming the portal as the source that lacks it
- id: AC4
covers: R3
given: SP-19 is present in the phone script and in notices.csv but not in the portal export
when: the comparison runs
then: there is exactly one mismatch, kind missing_service_point, service point SP-19, naming the portal
The second part holds the failure cases, the never-do rules and the remainder of the record.
failure_cases:
- id: F1
given: a phone-script cell for SP-03 on Tuesday reads "9-16"
then: a mismatch of kind malformed_hours is reported for SP-03 and the comparison continues with the other service points
- id: F2
given: portal.json is not valid JSON
then: the run stops with a message that names the file and no report is written
- id: F3
given: SP-05 appears twice in phone.csv
then: a mismatch of kind duplicate_id is reported and the first row is used for the comparison
never_do:
- id: N1
text: Write to the portal, the phone script or the notices spreadsheet; the three sources are opened for reading only.
- id: N2
text: Report a service point as correct because one source is missing; a missing source is a mismatch.
out_of_scope:
- correcting the sources
- checking the hours themselves against the service points' doors
open_questions:
- question: Which notice text counts as equal when the portal and the phone script word it differently?
owner: Hanna Wikström
decision_needed_by: 2031-10-22
assumptions:
- Hours are written "HH:MM-HH:MM" or left empty for a closed day.
ai_involvement:
- tool: approved assistant of the builder workspace (Mode 2)
use: drafted the rule wording and proposed seven failure cases; Hanna kept three
reviewed_by: Hanna Wikström
tests:
AC1: pending
AC2: pending
AC3: pending
AC4: pending
review_due: 2032-01-15
Each acceptance example becomes a test in Step 6, one for one. The test for AC2 is shown as it would look against the interface that the tool's compare function has in the build (compare(portal, phone, notices, today) returning a list of Mismatch records). It was run in a scratch directory against a stub of that function and passed; the real function and its tests come in Chapter 10.
from datetime import date
from spcheck.compare import Mismatch, compare
TODAY = date(2031, 12, 29)
def test_ac2_friday_hours_differ_between_portal_and_phone_script():
portal = {"SP-07": {"hours": {"fri": "09:00-16:00"}}}
phone = {"SP-07": {"fri": "09:00-15:00"}}
result = compare(portal, phone, notices={}, today=TODAY)
assert result == [
Mismatch("hours", "SP-07", "fri", {"portal": "09:00-16:00", "phone": "09:00-15:00"})
]
Hansa Mutual's record has the same form and different content. It is shown in an abridged form that keeps the rules, the examples and the scope, and leaves out the parts that repeat the structure.
id: SPC-HM-2031-0001
title: claims-doc-check, missing documents in motor property-damage claim files
status: approved
date: 2031-10-30
build: BLD-HM-2031-0001
owner: Katarzyna Nowak
oracle_owner: Marek Zieliński # senior claims handler; did not write the checker
quality_sample: office of the head of claims operations, drawn without the assistant
delegation: MND-HM-BLD-2031-01
scope:
claim_types: [motor_collision, motor_parking_damage, motor_glass]
out_of_scope_types: [motor_injury] # free text may hold health data; recorded in AIDR-1251
rules:
- id: R1
text: A motor_collision claim needs photos, accident_report and repair_estimate.
- id: R2
text: A motor_parking_damage claim and a motor_glass claim each need photos and repair_estimate.
- id: R3
text: A claim of type motor_injury is written as OUT_OF_SCOPE and is not processed.
- id: R4
text: For each claim with missing documents a draft request is written in the claim's language (pl, de or en) for the handler to review and send.
acceptance_examples:
- id: AC1
covers: R1
given: claim SYN-0001, type motor_collision, documents received "photos;repair_estimate"
when: the check runs
then: status MISSING, missing "accident_report"
- id: AC2
covers: R2
given: claim SYN-0002, type motor_parking_damage, documents received "photos;repair_estimate"
when: the check runs
then: status COMPLETE and no draft is written
- id: AC3
covers: R3
given: claim SYN-0003, type motor_injury, documents received "photos"
when: the check runs
then: status OUT_OF_SCOPE, no documents are listed and no draft is written
- id: AC4
covers: R4
given: claim SYN-0004, type motor_collision, language pl, documents received "photos"
when: the check runs
then: a draft in Polish asks for exactly "accident_report" and "repair_estimate" and mentions no other document
never_do:
- Decide a claim, rank handlers or claimants, or say that a claim is valid or invalid.
- Send any message; the handler reviews and sends from the claims core.
- Write to the claims core or read a free-text field.
- Log anything but claim ids and counts.
Three things in the two records deserve a second look. In each, the source of every rule is the work and not the assistant: Hanna's rules come from what the e-services team and the phone service said they needed, and Katarzyna's from the group's document rules, which rules.yaml will carry. The never-do lists mirror the mandates of Chapter 7, line for line, so that the assistant is bounded while it works and the tool is bounded when it runs. And each record carries an open question that its owner has to close, which is the honest state of a specification at approval and not a defect.
The two builds at this step
Nordvik Region: service-point-check. Hanna Wikström wrote the first draft of the rules on her own, from three conversations with the phone-service team and the person who maintains the notices spreadsheet, and asked the assistant, at Level 1 Assisted, for the situations her three rules did not cover. It proposed seven failure cases. She kept three, because the others (a service point renamed in one source, a closed day written as the word "closed" and a daylight-saving clock change) either could not occur in the exports or were outside the tool's purpose, and she moved them to the out-of-scope list. Her acceptance examples come from opening-hours entries that the Region publishes: the hours in the examples follow real published entries, with identifiers adapted for the record. The oracle owner is Saga Edvardsson, a quality engineer in the Region who is not part of the e-services team; she wrote the 12 oracle cases from real notices, which contain no personal data, and placed them in tests/oracle/. The specification SPC-NR-2031-0001 was approved on 2031-10-15 by Gunilla Roos. Gate status recorded in BLD-NR-2031-0001: Step 4 Specification passed on 2031-10-15, with one open question (what counts as equal notice text), due 2031-10-22.
Hansa Mutual: claims-doc-check. Katarzyna Nowak wrote the rules from the group's document requirements for property-damage claims, and her examples are synthetic claims with identifiers from SYN-0001, because the tool handles claims data and the repository holds none. The scope, shaped by the group data protection officer's screening, excludes injury claims, whose free-text fields may hold health data, and that exclusion is rule R3, with its own example, so that the decision is tested and not only noted. The oracle owner is Marek Zieliński, her deputy and a senior claims handler, who did not write the checker; he wrote the 25 oracle cases, and the office of the head of claims operations draws a quality sample from real files without any assistant taking part. The specification SPC-HM-2031-0001 was approved on 2031-10-30 by Joanna Kaczmarek. Gate status recorded in BLD-HM-2031-0001: Step 4 Specification passed on 2031-10-30. The specification gives one example for rule R2, which covers parking damage and glass claims together, and Marek's oracle cases cover each claim type. Chapter 11 returns to what that difference caught.
Across the degrees of delegation
The table sets out what the builder's assistant may do at this step, with the usage mode of the builder workspace or lane (Mode 2 in the editor, Mode 3 for delegated tasks inside a sandbox).
| Work at this step | Ceiling | Usage mode | Who decides, and how it is checked |
|---|---|---|---|
| Ask which situations the rules do not cover | Level 1 Assisted | Mode 2 | Builder picks the real ones and decides each behaviour |
| Draft rule wording and examples | Level 1 Assisted | Mode 2 | Builder reads each against the rule it covers; completeness check |
| Summarise the specification back | Level 2 Supervised | Mode 2 | Builder compares the summary with intent |
| Decide the scope, the rules and the never-do list | Not delegated | Builder; manager approves | |
| Write or change the oracle cases | Level 4 prohibited | Oracle owner only; never the assistant | |
| Change the specification in the same change as the code | Level 4 prohibited | Builder, in a commit of its own, before the code | |
| Run the completeness check and tests | Level 2 Supervised | Mode 3 | Mandate and sandbox; builder reads the result |
Level 4 Autonomous is not used for any of these. The prohibitions that bear on the step are the one on changing the assistant's own mandate, permissions, credentials, test oracles or pipeline gates, and the one on a decision about a named person's employment, pay, performance rating or access to work, which is why a specification for a tool that ranks or scores people is outside what a builder writes (Tier D).
Nordvik Region and Hansa Mutual
Both builders wrote their specifications in the same form, and the two records show how little the form constrains the content. The Region's tool reads public data and the work of the specification was to settle what equal means across three sources written by different people. The group's tool reads claims data, and the work of the specification was to settle the boundary: which claim types, which fields, what is logged, and what the tool never does. In neither case did the specification take more than a few days, and in both the oracle owner is a person with knowledge of the real material who is not the builder and not the assistant. The European setting shows in two places: the Region writes for residents who rely on the published hours of public services, and the group's tool drafts requests in Polish, German and English for claimants in more than one country, with a scope narrowed because of the General Data Protection Regulation's rules on health data (4, European Union, 2016). Chapter 17 describes what the two organisations provide around the builder, and Chapter 23 tells both builds in full.
When to call a developer
The specification is mostly a builder's work, because it is about the work and the builder knows the work. The stop signs are about what the rules touch.
- A rule would rank, score, flag or decide something about a named person: an employee, a claimant, a resident. That is a Tier D signal; the specification stops and a developer and the data protection officer are asked.
- The tool would need to read free text, health data or any special category of personal data to meet a rule. Take it to the data protection officer before it is written down as a rule.
- A rule depends on a law, a regulation or a contract that the builder cannot read with confidence. Ask the owner of the process and, where needed, legal counsel; do not let the assistant paraphrase the instrument into a rule.
- The specification keeps growing: more than about eight rules, or a second user group appearing. It is probably two tools, and the second is a developer's.
- No one independent of the builder and the assistant can be found to own the oracle. Without one the tool cannot pass the gate of Step 7, and a developer or a quality function must be asked to name an owner.
- The assistant's summary of the specification differs from the builder's intent in ways that the builder cannot resolve by editing the text.
Observable, expected, speculation
That assistants draft and clarify specifications is observable in tools and vendor descriptions such as the toolkit cited above (53, Delimarsky, 2025), and that models fill what a prompt leaves out is reported in the preprint cited (154, Yang et al., 2025); the cited figures concern prompts to language models and not software products. That specification-first work improves what assistants produce is plausible and partly observed in those descriptions, but this book cites no controlled study of builders, and so it labels the claim expected. It is expected that more tools will generate tests from a specification and check the specification for completeness. That specifications will in future be written by assistants without a person deciding the rules is speculation, and the book's rule that a named person decides what correct means does not depend on it.
Summary
Step 4 Specification writes down what correct means before the code exists. A specification states the need with its evidence, the rules in numbered sentences, a concrete given-when-then example for each rule, the failure cases, the never-do rules and what is out of scope, with open questions and assumptions kept in view. Examples beat prose because they can be run. Underspecified instructions are filled in silently by the model, so the failure and never-do cases are not extras. The oracle that judges the finished tool is owned and written by a named person who is neither the assistant nor, ideally, the builder, and sits in a folder the assistant may not change; a change to the specification is made by the builder in a commit of its own and never in the same change as the code it checks. The assistant drafts and asks, and the builder decides. The step concerns D1 Frame, and the gate is met by showing the approved SPC-* record with a date and an approver, the completeness check, the named oracle owner with the location of the cases, and a commit history in which the specification comes before the code. Hanna Wikström passed the gate on 2031-10-15 with Saga Edvardsson as oracle owner, and Katarzyna Nowak on 2031-10-30 with Marek Zieliński. The conclusions are the book's own: that a specification a builder can write in a few days is enough to make the later steps checkable, and that the oracle owner is the most important name in it. Chapter 9 takes the next step, designing with boring technology and recording the decisions.
Sources
- European Parliament and Council of the European Union. Regulation (EU) 2016/679 (General Data Protection Regulation). Official Journal of the European Union, 4 May 2016. (4, European Union, 2016) https://eur-lex.europa.eu/eli/reg/2016/679/oj
- Delimarsky, D. (2025). Spec-driven development with AI: Get started with a new open source toolkit. GitHub Blog, 2 September 2025. (53, Delimarsky, 2025) https://github.blog/ai-and-ml/generative-ai/spec-driven-development-with-ai-get-started-with-a-new-open-source-toolkit/
- Nordqvist, G. (2025). Architecture as Code. (74, Nordqvist, 2025) https://aac.geon.se/
- Barr, E. T., Harman, M., McMinn, P., Shahbaz, M. & Yoo, S. (2015). The Oracle Problem in Software Testing: A Survey. IEEE Transactions on Software Engineering 41(5), 507-525. (89, Barr et al., 2015) https://doi.org/10.1109/TSE.2014.2372785
- Nordqvist, G. (2026). AI Developer: How the Developer Role Changes When AI Is Used Broadly in European Enterprises and Public Authorities. (86, Nordqvist, 2026) https://developer.geon.se/
- Yang, C., Shi, Y., Ma, Q., Liu, M. X., Kästner, C. & Wu, T. (2025). What Prompts Don't Say: Understanding and Managing Underspecification in LLM Prompts. arXiv:2505.13360. (154, Yang et al., 2025) https://arxiv.org/abs/2505.13360
- Sharma, M., Tong, M., Korbak, T., Duvenaud, D., Askell, A., Bowman, S. R. et al. (2023). Towards Understanding Sycophancy in Language Models. arXiv:2310.13548. (43, Sharma et al., 2023) https://arxiv.org/abs/2310.13548
- Fowler, M. (2013). Given When Then. martinfowler.com, 21 August 2013. (120, Fowler, 2013) https://martinfowler.com/bliki/GivenWhenThen.html