Chapter 1 — a customer places an order and knows it was taken
As a customer I want to place an order and be told it was accepted, so that I know I will be charged for something that is actually coming.
An order that is silently dropped and an order that is quietly duplicated look identical to whoever placed it: nothing arrives, or two things do, and in both cases the shop knew and the customer did not. This flow is the moment between those two.
mermaid source for this flow
flowchart TD
START([<b>START</b> · somebody with a basket<br/>and a card]):::terminal
TAKE[<b>ch1-1</b> · take the order —<br/>what was asked for, and by whom]:::process
STOCK{<b>ch1-2</b><br/>is every line<br/>actually available?}:::decision
SHORT[/<b>REFUSED</b> · said before the card is charged,<br/>naming the line that is short/]:::bad
CHARGE[<b>ch1-3</b> · charge once, and only<br/>after the order is committed]:::process
TELL[<b>ch1-4</b> · tell the customer,<br/>with a reference they can quote]:::process
DONE([<b>END</b> · an order the shop can act on and<br/>the customer can ask about]):::good
START --> TAKE --> STOCK
STOCK -->|no| SHORT
STOCK -->|yes| CHARGE --> TELL --> DONE
U1>"<b>ch1-U1</b> what happens to a basket<br/>abandoned mid-charge"]:::unknown
P1>"<b>ch1-P1</b> how long a reservation<br/>is held"]:::parameter
CHARGE -.exposes.-> U1
STOCK -.tuned by.-> P1
subgraph LEGEND["legend — shape carries the role"]
direction LR
LT([START / END — named in caps, so it reads in greyscale]):::terminal
LA[action]:::process
LS[[sub-flow — drills down]]:::subroutine
LD{decision}:::decision
LG([reached]):::good
LB[/REFUSED — a different shape, so it reads in greyscale/]:::bad
LP>parameter — a value to discover]:::parameter
LU>open unknown — a decision to take]:::unknown
end
classDef terminal fill:#eef0ee,stroke:#52514e,color:#0b0b0b;
classDef process fill:#e9eef5,stroke:#2a78d6,color:#0b0b0b;
classDef decision fill:#e9eef5,stroke:#2a78d6,color:#0b0b0b;
classDef subroutine fill:#e9eef5,stroke:#2a78d6,color:#0b0b0b;
classDef good fill:#e2f2e2,stroke:#0ca30c,color:#0b0b0b;
classDef bad fill:#f7e1e1,stroke:#d03b3b,color:#0b0b0b;
classDef parameter fill:#f4f2ee,stroke:#8a6212,color:#0b0b0b,stroke-dasharray:2 2;
classDef unknown fill:#f4f4f2,stroke:#898781,color:#0b0b0b,stroke-dasharray:2 2;Elements
Defined here. The element's name is its id, and it is unique across the corpus.
| Element | Type | Card. | Domain | Enforced | Provenance |
|---|---|---|---|---|---|
basket |
list<line> | 1 | at least one line | yes | authored |
customer |
record | 1 | — | — | authored |
line |
record | 1..n per basket | — | — | authored |
line.quantity |
integer | 1 per line | 1 or more | yes | authored |
order |
record | 0..1 | — | — | derived |
order.reference |
string | 1 per order | unique across all orders | yes | generated |
reservation |
record | 0..1 per order | — | — | derived |
charge |
record | 0..1 per order | — | — | observed |
refusal |
record | 0..1 | names one short line | no | derived |
confirmation |
record | 0..1 per order | — | — | derived |
There is no Imported. table here, and that is a fact about this example rather than an omission. This corpus is one chapter, so every element it moves is one it owns. An import names another part of this corpus — a chapter beside it, or a sibling spec the project declared — and one naming a corpus the gate cannot see resolves to nothing, which is ch15-4 with a row in front of it. goldspec/spec/'s own chapters carry real ones.
Nodes
ch1-1 · take the order
As a customer I want the shop to record what I actually chose, so that nothing is reconstructed from stock levels later and quietly changed.
| In | basket, line, line.quantity, customer |
| Held | — |
| Out | order, order.reference |
A basket is a customer's intent at one moment. A shop that rebuilds it at dispatch time is answering a different question from the one the customer asked.
ch1-2 · is every line actually available?
As a customer I want to be told a line is short before my card is touched, so that I am not refunding money I should never have paid.
| In | order, line, line.quantity |
| Held | reservation |
| Out | order, or refusal |
The cost of getting this backwards is not symmetric. Refusing an order that could have been filled loses a sale; charging for one that cannot be filled takes money for nothing and spends the refund, the apology and the trust to put it back.
Parameter ch1-P1 — how long a reservation is held. Known to exist; the value is not. Too short and a slow card release stock somebody has already been promised; too long and a browsing customer holds stock nobody can sell. Model: the distribution of time from reservation to charge, measured over real orders, bounds it from below; the rate of stock held-and-abandoned bounds it from above.
ch1-3 · charge the card once
As a customer I want to be charged exactly once for one order, so that a retry somewhere in the shop's plumbing is not a second payment out of my account.
| In | order, order.reference, customer |
| Held | — |
| Out | charge |
The network between a shop and a payment processor drops responses as readily as requests, so the charge succeeded and the charge is unknown arrive as the same evidence. Idempotency is what makes the retry safe, and a retry always happens eventually.
NFR ch1-N1 — the processor's reconciliation window closes at 02:00. External, known, and restricting: a charge submitted near it may settle on either side of the boundary, so nothing here may assume same-day settlement. It is not this shop's to negotiate.
ch1-4 · tell the customer
As a customer I want a reference I can quote back, so that when I need to ask about my order there is something to ask about.
| In | order.reference, customer |
| Held | — |
| Out | confirmation |
A confirmation carrying no handle is a courtesy rather than a record: it cannot be searched for, cited in a complaint, or used by the shop to find the order it refers to.
Open unknowns
- ch1-U1 — what happens to a basket abandoned mid-charge. The card was submitted and no response came back. The order is neither placed nor refused, and the customer has closed the tab. Settling it needs the processor's reconciliation behaviour, which nobody here has read yet. A decision to take rather than a value to discover, which is why it is
Uand notP.
Glossary
| Term | Meaning |
|---|---|
| Basket | What a customer chose, before it is an order. It has no reference and the shop owes nothing on it |
| Order | A basket the shop has accepted responsibility for. It has a reference from the moment it exists |
| Short | A line the shop cannot fill in the quantity asked for. Not the same as out of stock, which is a property of the item rather than of this order |