Match and refuse before any write
A wrong pairing is refused with copy that names the right container, and nothing is written.
The situation
An associate holding a bag scans the wrong bin. The station must catch it before anything changes, and tell the person where the bag actually goes, in words they can act on.
The recipe
match states what the pairing must satisfy. refuse states what the station says when it does not.
{ "query": { "roles": ["binning"], "status": "pending" }, "verb": "accumulate", "requireActingIdentity": true, "subject": { "schemes": [11] }, "match": { "target": ["{subject.containerCode}"] }, "refuse": { "markdown": "That's {container.locationName}'s container. This one goes in **{subject.containerCode}**.", "conflict": "That container was just taken. Scan the item again.", "missing": "That container is closed. Scan the item again." }, "params": { "itemKey": "{subject.itemId}", "accumulate": { "from": "subject" } } }match.target lists templates the scanned target must equal. match.facets lists facets the located container must share with the held row. A match.target entry that is a lone token naming a list facet (such as {subject.offeredContainers}) accepts any entry in that list.
The guarantee
A miss answers refused with the rendered markdown, and nothing is written. When the write itself loses a race (the container closed, another bench took the free slot), the answer is refused with the refuse.conflict copy, and the subject clears so the next scan reads fresh state. A step payload the role’s form schema rejects also answers refused with the reason, never a raw 422. See Scan codes.
Related patterns
The physical world
Item, badge, container
Three scans place an item in a container: the item says which, the badge says who, and the container scan writes both rows at once.
The physical world
Hold a subject
The item scan writes nothing: it holds the item on the station so the next scan knows what it is acting on.
Resolving from anywhere
Validate before claiming
An agent's claim-and-resolve validates the payload first, so a rejected answer never leaves a row locked to the agent.
