Intent on create, outcome on resolve
The row is created with the facets that route it and resolved with the facets that describe what happened. One row answers both questions.
The situation
Operations wants to know what was asked, what was decided and how long it took. Keeping a separate audit table means two writes and a reconciliation job.
The recipe
const decision = await conditional<{ approved: boolean }>(signalId, { role: 'order-reviewer', metadata: { orderId, channel: 'web', amountBand: 'high' },});await lt.escalations.resolve({ id, resolverPayload: { approved: true }, metadata: { outcome: 'approved', reasonCode: 'verified' },});| Field | Goes where | Indexed |
|---|---|---|
resolverPayload |
Delivered to the waiting workflow, stored as resolver_payload |
No |
metadata |
Merged into the row’s metadata | Yes, GIN |
Every resolve surface takes the metadata patch: the HTTP body, the SDK, and the metadata argument on the resolve_escalation and claim_and_resolve MCP tools.
The guarantee
The patch merges in the same atomic update as the resolve, on the winning resolve only. Intent and outcome are then queryable side by side: facets: { outcome: 'approved' } finds every approval. Elapsed time needs no field, since the row carries created_at and resolved_at. See Conditional.
Related patterns
Resolving from anywhere
The row is the production log
A station resolves its in-flight row with the run's result, so the work queue is also the record of what was produced and how long it took.
The escalation boundary
Know who resolved it
The resolver's identity rides the wake as $resolution and lands on the row as resolved_by, so follow-on work can go to the same person.
Analytics
Dwell and membership
One call answers how long entities spent in each state over a window, or how many are in each state right now.
Resolving from anywhere
Resolve by domain key
A caller that knows only orderId resolves the right row in one atomic statement, with no lookup of the escalation id first.
