Skip to content

Roster: check off who arrived

Forms and configurationDashboardScanHTTPx-lt-widget: rosterx-lt-rosterroster.<field>.outstandingPOST /api/escalations/:id/roster-action

A roster lists every member a container expects, marks each waiting, in or flagged, and holds the rest of the form until all are accounted for.

The situation

A box should hold five bags. The packer scans bags in as they arrive, marks one missing when it is not in the bin, and weighs the box only once every bag is either in or accounted for.

The recipe

form_schema
"orders": {
"type": "string",
"readOnly": true,
"title": "Orders in this box",
"x-lt-widget": "roster",
"x-lt-roster": {
"expected": { "query": { "roles": ["packing"], "facets": { "packingBox": "{{escalation.id}}" } } },
"key": "{{metadata.orderId}}",
"in": ["items", "resolver.bags"],
"flagged": [{ "when": "resolver.outcome=missing", "label": "Missing" }]
},
"x-lt-columns": [{ "label": "Order", "value": "{{metadata.orderId}}" }],
"x-lt-actions": [
{ "label": "Missing", "when": "waiting", "resolverPayload": { "outcome": "missing" },
"confirm": "Mark {{metadata.orderId}} missing?" }
]
},
"weight": { "type": "number", "title": "Box weight", "x-lt-showIf": "roster.orders.outstanding=0" }

Add "x-lt-submit-guard": { "roster": "orders", "message": "{{count}} orders are still unaccounted for" } at the schema root to hold the submit. The expected set is a query, a list on the row, or the row’s own batch keys.

The guarantee

Roster actions run on the server: the dashboard sends only which action and which entry, and the server re-reads the action from the row’s pinned form schema, confirms the entry is on the roster it computes, and runs the act under the person’s RBAC. For enforce_schema roles, a resolve with an outstanding entry is refused with the guard’s message as a 422 on every surface. The roster refreshes on the same escalation events as every list. See Forms.