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.
The situation
A plant manager asks two questions every morning: where did the fleet’s time go yesterday, and how many machines are in each state now. Both should be one query, sliceable by model.
The recipe
const dwell = await lt.escalations.aggregateByFacets({ query: { entity: 'serialNumber' }, groupBy: { state: true, facets: ['model'] }, measure: { kind: 'dwell', window: { from: '2026-10-22T00:00:00Z', to: '2026-10-23T00:00:00Z' } },});// groups: [{ state: 'running', facets: { model: 'mx-200' }, dwellSeconds, sampleCount }, ...]
const now = await lt.escalations.aggregateByFacets({ query: { entity: 'serialNumber' }, groupBy: { state: true }, measure: { kind: 'membership' }, distinctBy: 'serialNumber',});// one group per state, count = machines, not rowsltc esc aggregate-facets --entity serialNumber --group-state --group-facets model \ --window '{"from":"2026-10-22T00:00:00Z","to":"2026-10-23T00:00:00Z"}'Dwell sums open-seconds per group, clipped to the half-open window. Membership counts rows open at an instant, or distinct entities with distinctBy.
The guarantee
A row missing a grouped facet forms a real null group rather than vanishing. Fields that would silently change the meaning (status, available, query-level paging) are rejected with a 400. Results over the group cap set overflow: true. See Analytics.
Related patterns
Analytics
The entity is a shared facet
Roles that declare the same entity key form that entity's system. One dial per role, and every analytics query can scope to the whole journey.
Analytics
Reconstruct any moment
Pass a past instant and membership rebuilds the live set as it stood then, from the same rows, with no snapshot table.
Analytics
Park every state
Each state an item passes through is one escalation row. The queue becomes a complete time series of the process with no extra instrumentation.
The queue as a market
The list and the badge agree
The at-risk count on the Pace Board and the list it links to run the same SQL predicate, so the list always holds exactly that many rows.
