Walkthrough: Commitments That Cannot Be Silently Edited
This walkthrough traces FLOW-0003 — Commitments That Cannot Be Silently Edited — an order’s free draft composition, the real four-check submission floor, the hard freeze at Submit, and closure only by deliberate acts — to the Orders module calls that implement it, with Business Affiliate providing the vendor pin and Demand supplying the readiness check Submit relies on. This is the best-covered flow in the catalog: 26 of its 27 narrative steps have a direct, tested API counterpart, and the one exception is the out-of-band step of the order physically reaching the supplier.
See the walkthroughs index for the shared call shape and the built-vs-designed legend used throughout.
Draft — free composition
Section titled “Draft — free composition”| # | Business step | API call | What changes | Refusals you can hit | Evidence |
|---|---|---|---|---|---|
| 1 | Create the order | POST /v1/procurement/order/order/create | Orders: new order, status = DRAFT, priority = UNDEFINED, internalNotes = []. Under mutation-mode: PROPAGATE, even a name-only supplier is linked at rest | RRetiredRef (403) at genesis. RForeignRef (403) | order.functional.test.ts:22-35; RRetiredRef :159-161 |
| 2 | Add lines freely — three arms | POST /v1/procurement/order/order-line/add-line (FROM_DEMAND / FROM_ITEM / FREE_FORM) | Orders / order-line: new line, lineStatus = L_OPEN, quantity set, received keyed zero, demand set (FROM_DEMAND only). Item pin is server-derived | RForeignRef, RDemandIneligible, RDemandHeld | order-line.functional.test.ts:28-54, 56-76, 78-92 |
| 3 | Edit a line’s quantity | POST /v1/procurement/order/order-line/update-line | Orders / order-line: quantity set; item pin untouched | RFrozen (post-submit), RLineClosed | order-line.functional.test.ts:111-133 |
| 4 | Remove a line | POST /v1/procurement/order/order-line/remove-line | Orders / order-line: line tombstones — GET by id 404s, drops from by-order | RFrozen, RLineClosed | order-line.functional.test.ts:284-303 |
| 5 | Change the supplier freely while Draft | POST /v1/procurement/order/order/update-supplier | Orders: the whole supplier binding is replaced | RForeignRef (403), RRetiredRef (403), ROrderClosed | order.functional.test.ts:38-50; RForeignRef :52-63; RRetiredRef on an existing order :156-158 |
| 6 | Drop per-order supplier overrides, keep the reference | POST /v1/procurement/order/order/reset-to-supplier | Orders: supplier.overrides cleared; supplier.reference preserved | RFrozen, ROrderClosed | order.functional.test.ts:66-81 |
| 7 | Set the header details (priority, assignee, notes) | POST /v1/procurement/order/order/update-details | Orders: priority / assignee / notes cluster — SET semantics, an absent field clears | RFrozen post-submit, ROrderClosed | order.functional.test.ts:110-133 |
| 8 | Keep internal notes | POST /v1/procurement/order/order/annotate | Orders: internalNotes replaced wholesale (SET, not append); one ANNOTATE occurrence per act | ROrderClosed on a terminal order | order.functional.test.ts:84-107 |
The submission floor
Section titled “The submission floor”The narrative states four checks. The API has four distinct refusals, each with its own stable code, and each leaves the draft’s content intact.
| # | Business step | API call | What changes | Refusals you can hit | Evidence |
|---|---|---|---|---|---|
| 9 | Submit with a name-only supplier → refused; a linked vendor is required | POST /v1/procurement/order/order/submit | Nothing — the draft keeps its content | See Fidelity notes — this refusal is not reachable as the narrative frames it | order-submit-settle.functional.test.ts:59-74 (comment at :61-65); link-at-rest behavior order.functional.test.ts:29-33 |
| 10 | Submit an order with no lines → refused | POST .../order/submit | Nothing | RNoLines | order-submit-settle.functional.test.ts:48-57 |
| 11 | Submit with a vendor linked but retired since → refused; re-checked at the commitment | POST .../order/submit | Nothing — the draft keeps its content | RRetiredRef (403) | order-submit-settle.functional.test.ts:102-116 |
| 12 | Submit with a serviced demand item not ready to start → refused | POST .../order/submit | Nothing | RDemandIneligible | order-submit-settle.functional.test.ts:36-46 |
| 13 | Submit — floor satisfied | POST .../order/submit | Orders: status DRAFT → SUBMITTED; structure, supplier, header details, and assignee freeze on their pins. Demand: every serviced demand item RELEASED → IN_PROCESS, each minting its own holding pool | — | order-submit-settle.functional.test.ts:24-34; arc shared/arcs.ts:116-126 |
| 14 | The committed order goes to Vera | — (physical step) | Nothing in the system | — | No transport exists in this build |
What freezes — and what stays open
Section titled “What freezes — and what stays open”| # | Business step | API call | What changes | Refusals you can hit | Evidence |
|---|---|---|---|---|---|
| 15 | Change a line’s quantity after submit → refused; the structure froze | POST /v1/procurement/order/order-line/update-line (and add-line / remove-line) on a SUBMITTED parent | Nothing | RFrozen | order-line.functional.test.ts:306-315 |
| 16 | Edit the header details after submit → refused | POST /v1/procurement/order/order/update-details | Nothing | RFrozen (the headerDetailFrozen law) | order-submit-settle.functional.test.ts:91-99 |
| 17 | Change the supplier after submit → refused | POST /v1/procurement/order/order/update-supplier | Nothing | RFrozen (the supplierBindingFrozen law) | order-submit-settle.functional.test.ts:77-88 |
| 18 | Record the supplier’s answer on a line → recorded; answers accrete against the frozen promise | POST /v1/procurement/order/order-line/record-acknowledgment | Orders / order-line: confirmation:{disposition, confirmedQuantity?} recorded; the note rides the occurrence envelope | RBadState — acknowledgment on a line whose parent is still DRAFT. RLineClosed | order-submit-settle.functional.test.ts:148-176 |
| 19 | Receipts accrue against the lines | POST /v1/procurement/order/order-line/record-receipt (manual) — the C/NOTIF listener drives the same kind internally | Orders / order-line: received accrues pairwise; reversal via reverse-receipt decrements | RNoDemand — free-form line has no received tracking. RLineClosed | order-receipt.functional.test.ts:26-43, 46-59, 62-82 |
| 20 | Notes accrete | POST /v1/procurement/order/order-line/annotate-line | Orders / order-line: note lands on the ANNOTATE_LINE occurrence envelope; no payload field changes | RLineClosed | order-line.functional.test.ts:284-303 (annotate leg at :293-297) |
| 21 | Read the order → Confirmed / Receiving are computed views, not stored states | GET /v1/procurement/order/order/{eId}/readings?asOf= | Nothing (read). Returns {confirmed, receiving, reading}; payload.status never leaves SUBMITTED | 404 on a missing order | order-submit-settle.functional.test.ts:179-195; also order-lifecycle.acceptance.test.ts:40-43 |
Endings are deliberate
Section titled “Endings are deliberate”| # | Business step | API call | What changes | Refusals you can hit | Evidence |
|---|---|---|---|---|---|
| 22 | Close the order → refused; a line is still open | POST /v1/procurement/order/order/close | Nothing | RLinesOpen | order-submit-settle.functional.test.ts:119-131 |
| 23 | Close the outstanding line short — an explicit act, never automatic | POST /v1/procurement/order/order-line/close-line | Orders / order-line: lineStatus L_OPEN → L_CLOSED. The shortfall is simply left recorded — nothing reconciles it | RLineClosed — second close | order-submit-settle.functional.test.ts:134-145 |
| 24 | Close the order → Closed, final | POST /v1/procurement/order/order/close | Orders: status SUBMITTED → CLOSED (terminal) | — (succeeds once every line is closed) | order-submit-settle.functional.test.ts:126-130; full arc order-lifecycle.acceptance.test.ts:24-51 |
| 25 | Cancel exists only while the order is a Draft | POST /v1/procurement/order/order/cancel | Orders: DRAFT → CANCELED (terminal). Every attached demand item returns to the queue untouched, still RELEASED | ROrderClosed — see Fidelity notes (gap: only witnessed as a re-cancel) | order.functional.test.ts:165-176; demand-release arc order-lifecycle.acceptance.test.ts:93-124 |
| 26 | A terminal order is deleted | POST /v1/procurement/order/order/delete | Orders: order tombstones — GET .../{eId} 404s; every further mutation refuses | RNotTerminal — still live. ROrderClosed afterwards | order.functional.test.ts:179-187, 189-200 |
| 27 | The record shows what was refused, not just what happened | GET /v1/procurement/order/order/{eId}/log?page=&size= (line equivalent: .../order-line/{eId}/log) | Nothing (read). Returns occurrences most recent first, carrying both COMMITTED and REFUSED rows | — | order.functional.test.ts:203-222 |
Fidelity notes
Section titled “Fidelity notes”What the tests witness today versus what the narrative claims:
- The “name-only supplier” refusal (step 9) is not reachable as drawn. Under
mutation-mode: PROPAGATE, a name-only supplier is linked at rest, at create time, to a found-or-created VENDOR role — so it carries a realeId/affiliateEIdand submits without complaint. The reachableRNoSuppliercase is an order with no binding at all. The narrative’s claim (“a name alone can compose a draft but never a commitment”) is true of the design intent, not of the current API surface. - The assignee (step 7, frozen at step 13) doesn’t freeze on a pin, because there is no pin.
assigneeis a plain string with no Staff-module resolution. It does freeze —update-detailsrefusesRFrozenpost-submit — but as text, not as a version pin the way the vendor and item references are. - The RECEIVING reading (step 21) is reachable, contrary to a stale comment in the acceptance suite claiming the
record-receiptroute isn’t on the T0 surface. The route is present and covered elsewhere, but no test yet drivesreadingsall the way to RECEIVING — the one reading in the narrative’s “computed views” claim with no direct witness. - Refusal HTTP status is disposition-driven, not per-reason.
PRECONDITIONreasons render 409;POLICYreasons render 403 — which is whyRRetiredRefandRForeignRefare asserted with an explicit403in the tests whileRFrozen,RNoLines, andRLinesOpentake the default. - Step 14 is the only step with no API counterpart in this flow. Every other narrative step maps to a real, tested call — the reason this is the best-covered of the four walkthroughs.
- Cancel-on-SUBMITTED (step 25’s actual claim) is not directly witnessed. The suite’s only cancel refusal is a re-cancel of an already-CANCELED order — nothing asserts that canceling a SUBMITTED order refuses.
Read next
Section titled “Read next”- FLOW-0003 — Commitments That Cannot Be Silently Edited — the narrative this walkthrough traces.
- Orders, Business Affiliate, Demand — the module pages that specify the freeze, the vendor pin, and demand readiness.
Copyright: © Arda Systems 2025-2026, All rights reserved