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.
| 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. |
# 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 / hourabove 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.
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.
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:
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.
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.
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.
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.
Copy this. Fill it in before you open the agent.
# 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.