| name | program-design |
| description | Designing a program by the discipline of rational development. Use when an FRD (from the `requirements-intake` skill) and a frozen API contract (OpenAPI/AsyncAPI) exist and a design package is needed for later implementation (vertical slices, module contracts, antecedents/consequents, unit tests by formula, component scenarios). Do NOT use if the FRD, the API contract, or the README failure-mode map is missing — run `requirements-intake` first. Tier-agnostic: stepwise steps, router tables, checklists and STOP rules carry the essence without bloat — the optimum, not the minimum. |
program-design.skill — design a program by the discipline of rational development
Purpose
The planner's skill. In: a functional-requirements document (FRD) produced by the
requirements-intake skill (problem statement, Cockburn use cases, interfaces, draft
contract + failure-mode map). Out: a design package the implementer uses to build the
program.
Roles are work modes, not necessarily different models. One model can play both
roles in sequence; what matters is the mode switch and the artifact handoff (the
design package), not a model switch.
How you reason — the frame the steps derive from
Rational design = correctness by construction, not by exhaustive test:
- Stepwise refinement (Wirth) — decompose top-down, one decision at a time, until each module is
trivially implementable.
- Information hiding (Parnas) — a module is a hidden design decision: the one design decision it hides behind a contract,
so it changes without rippling.
- Design by Contract (Hoare/Meyer) — every module carries an antecedent (what must hold in) and a
consequent (what it guarantees out); that pair is its correctness.
- Compositional correctness — the whole is correct because the contracts compose (each antecedent met
by its caller's consequent), established by the call-graph reconciliation (Step 9), not by testing every
path. Unit tests confirm the formula; they don't establish correctness.
- Vertical slice — organize every artifact by behaviour (one external input → outcome), never by
technical layer.
The 12 steps below are this frame made procedural.
Scope
DO:
- Design the module schema and contracts.
- Iteratively discuss decision points with the operator.
- Prepare the ticket backlog for the implementer.
DON'T:
- Write implementation code (that's the implementer's job).
- Make architecture decisions without operator approval.
- Add dependencies or technologies without explicit justification.
Steps (procedure map + progressive disclosure)
Hold the whole process by this table. Keep only the current step in context, not the whole
skill — find the step number, open its detail file (reference/step-NN-*.md); each file has its own
In/Out. If you get lost, return here.
| Step | Does | Main out | Detail file (reference/) |
|---|
| 0 | Check mandatory input artifacts (FRD, contract, failure table, Gherkin) | decision "design / stop" | step-00-input.md |
| 1 | Confirm the FRD from requirements-intake (problem statement + use cases + interfaces) | verified requirements/<slug>.md | step-01-requirements.md |
| 2 | List the external inputs of the slices | slice table | step-02-inputs.md |
| 3 | Design each slice's module tree (hard rules, C4, head-pipe pseudocode) | tree + head-pipe pseudocode | step-03-module-tree.md |
| 4 | Describe the message catalog + error model | messages.md (types, Result<T, Error>) | step-04-messages.md |
| 5 | Describe module contracts by template (Dependencies: checklist) | contracts (Input/Deps/antecedent/consequent) | step-05-contracts.md |
| 6 | Isolate I/O into autonomous objects | I/O objects (Store/Client/Publisher) | step-06-isolate-io.md |
| 7 | Describe the app infrastructure module | infrastructure.md | step-07-infrastructure.md |
| 8 | Count unit tests by formula + reconcile design with Gherkin | unit-test table + Gherkin-mapping | step-08-tests-gherkin.md |
| 9 | Reconcile contract consistency via the call graph | contracts-graph.md | step-09-contracts-graph.md |
| 10 | Assemble the design package | folder .agent/planner/design/<slug>/ | step-10-package.md |
| 11 | Build the ticket backlog (one per slice; + ) |
Hard rules and STOP (consolidated — this is the skill's conformance-gate)
plan-reviewer checks the design against this list as the reference standard (asymmetry:
do not trust Step 12's self-fill). Any violation = STOP, return to the named step,
handoff forbidden. Anti-gaming: you MUST NOT tick [x] without an existing artifact.
The full text of each rule is in its step's file.
- Correctness by composition, not coverage (P1, foundational). A program is correct because each
module's consequent ⊆ the next's antecedent (Step 9) — not because tests pass ("testing never proves
the absence of errors; program better, don't test more"). Module bodies are structured: nested blocks,
one entry / one exit, only sequence/selection/iteration, no goto.
- Input gate (Step 0). No contract (
OpenAPI/AsyncAPI), no "failure-mode map" in
the README, or no Gherkin scenarios for the slice endpoints → design does not start.
- Single
Request (Step 3, lesson D1). 1 slice = 1 external input = exactly one
Request; flag/option = a Request field; no side-injections past Request; no
test-only I/O method; a branch on a field = logic (unit), not a component scenario.
- One data input per node (Step 3). A pipe node takes exactly one data entity — the one that
step owns. Environment (ports, clock, clients, config scalars already validated at boot) is bound
before the pipe, in the composition root, and enters the step as a ready collaborator with a
one-argument method (
BuildSpecLoader(provider, timeout, client) -> SpecLoader; in the pipe
loader.Load()), never as a second parameter. A real join of two flows is materialized as a
separate named input type with its own constructor node NewT(...) — the only node taking 2+.
- No shared
Context/State (Step 3, counter-rule to the above). One input ≠ one common
object threaded through the whole pipe: a carrier gives every node access to everything, the
antecedent stops being narrow, the step cannot be tested in isolation, and the
antecedent→consequent table in contracts.md becomes fiction. Review gate, not scriptable
(a port is indistinguishable from data by signature) — @mills reads it at Gate #1.
- Invariant — subtype, not guard (Step 3). Checking an invariant over a domain struct =
a subtype constructor, not a guard function
-> ().
- I/O isolation (Step 5/6). No raw
*sql.DB/*http.Client/broker-conn in
Dependencies: or in the head's Deps — only autonomous
Store////.
Definition of Done of the skill
- All 13 steps (0–12) passed.
- Folder
.agent/planner/design/<slug>/ (working package) created and filled.
- Durable docs published to
docs/design/<slice>/: module-tree.md, contracts.md (this skill),
c4.md (c4), use-case.md (cockburn-use-case) — committed, reviewed at Gate #1 (Step 10 "Two locations").
backlog.md contains one ticket per slice.
- The handoff checklist in
backlog.md is fully [x] (including the last line with the handle and the PR creation date).
- The design PR is open; operator review awaited. Merging the PR = approval = the implementer may start.