# The spec template

Companion handout for the CISOSE 2026 tutorial
**Building and Verifying AI Systems with Agentic AI** — Chia-Kai Chang, National Central University.

A spec an agent can implement is a spec you can test. That is the whole idea. The five
parts below are not documentation: they are the acceptance criteria, written before the
code exists, in the only window where you are still thinking clearly about what "done"
means.

---

## 1. The five parts

| Part | One line | Why an agent needs it |
|---|---|---|
| **Goal** | One sentence of intent. | Without it the agent optimises for the last thing you said. |
| **Constraints** | Performance, security, style, and **what NOT to touch**. | The "do not touch" line is what stops scope creep. |
| **Interfaces** | Inputs, outputs, contracts. | Names the seams the tests will attach to. |
| **Examples** | Concrete in → out pairs. | One example resolves more ambiguity than a paragraph. |
| **Acceptance tests** | How we know it is done. | Turns the spec into an oracle instead of a wish. |

### A complete small one

```
# spec: forgot-password rate limit

goal       : cap reset requests per account
constraint : 3 / hour; do not touch login
interface  : POST /reset → 429 on excess
example    : 4th request in 1h → 429
accept     : - unit: counter resets after 1h
             - e2e: 4th call blocked, logged
```

Six lines. It is implementable, and every line of it is checkable. That combination is
the target — not length.

> The `3 / hour` above is an illustration for this handout, not the value used in any
> production system. Pick your own and put it in the spec, because a threshold that
> lives only in code is a threshold nobody can review.

---

## 2. When the feature is real: twelve sections

Small features fit in five parts. A feature with users, money, or research data attached
needs more, and the extra sections are where the design actually happens. The structure
below is the one used for a real production feature (a blind model-comparison arena); the
redacted spec itself ships alongside this handout.

```
01  goal & teaching claim        what is this FOR, in one paragraph
02  user flow, step by step      the whole path, numbered, no branches skipped
03  model / service roster       what it depends on and what happens when that is down
04  integrity rules              the properties that must hold even under failure
05  policy                       the judgement calls, stated as policy not code
06  the maths                    any scoring, ranking, or money, written out
07  data model                   tables, keys, retention
08  API endpoints                method, path, request, response, error
09  front-end conventions        so the agent does not invent a new design language
10  research / measurement       what this produces that anyone will analyse later
11  ethics / approval            consent, governance, and who signed off
12  phasing                      what ships first, and what deliberately does not
```

Sections 04, 05, and 11 are the ones people skip. They are also the ones that cannot be
recovered later, because they encode intent rather than behaviour.

---

## 3. The test that tells you the spec is doing work

After writing, find **three clauses an agent could not have inferred from the goal.**

If you cannot find three, you have written a prompt, not a spec — and a prompt produces
code that looks right and is unreviewable, because there is nothing to review it against.

Real examples of such clauses, from the spec that ships with this handout:

1. **A pattern that is correct elsewhere in the system is wrong here.**
   Ordinary chat may fall back to a second provider when the first is unavailable. This
   feature must never fall back, because a silent substitution would corrupt the exact
   comparison the feature exists to make. No agent infers this from "build a comparison
   page"; it comes from knowing what the numbers will be used for.

2. **A behaviour that has to be suppressed, discovered by observation.**
   Some models announce their own name unprompted, which destroys a blind comparison.
   The spec has to say so, and say it twice — once as an instruction to the model, once
   as an output filter — because the instruction alone is not enforcement.

3. **A failure that must be recorded rather than scored.**
   "Both answers are bad" is a real verdict, and it must not move the ranking. It is a
   failure signal, not a relative-preference signal. Fold it into the score and the
   ranking quietly stops meaning what its label says.

Each one is a judgement about what the thing is *for*. That is the part of the work that
did not get cheaper.

---

## 4. Writing it takes longer than building it

For the twelve-section spec, writing took roughly a working afternoon and the
implementation took less. That ratio is not a cost overrun — it is where the design
moved to. Anyone who calls this waterfall should be told the rest: the document was
revised the next day, and again the following week. **Heavy document, light process.**

---

## 5. Blank template

Copy this. Fill it in before you open the agent.

```markdown
# spec: <feature name>

## 01 Goal
<one paragraph — what this is FOR, not what it does>

## 02 Flow
1.
2.
3.

## 03 Dependencies
- service / model:
- when it is unavailable:

## 04 Integrity rules
- must hold even under failure:

## 05 Policy
- judgement call:
- stated at which layer (platform / feature / model):

## 06 Maths
<any scoring, ranking, cost, or money — written out, with the edge cases>

## 07 Data model
<tables, keys, retention, deletion>

## 08 API
| Method + path | Request | Response | Errors |
|---|---|---|---|

## 09 Front-end conventions
<reuse existing patterns explicitly, so the agent does not invent new ones>

## 10 Research / measurement
<what will be analysed later, and which field it comes from>

## 11 Ethics / approval
<consent, governance, approval reference, and the opt-out path>

## 12 Phasing
- Phase 1 (ships):
- Phase 2 (deliberately not yet):

## Constraints
- do NOT touch:

## Acceptance
- [ ] unit:
- [ ] e2e:
- [ ] cannot be unit-tested, verified by:
```

That last acceptance line is not a weakness in the template. Some clauses genuinely
cannot be asserted in a unit test — they need a real disconnection, or a human in review.
Writing down *which* ones, and *how* they are checked instead, is more useful than
pretending everything is automatable.

---

*Released with the tutorial materials. Reuse freely; attribution welcome.*
