Primitive spec
Trantor
The physical-world primitive. Trantor owns resources that have identity and location — rooms, courts, lanes, tables, carts, stylists, kitchen stations, POS terminals — and the atomic allocation of those resources to other primitives that need to hold them.
What it owns
Trantor models physical things. It does not own people (that is Terminus) — a stylist Resource in Trantor carries a terminusPersonId pointer and only the physical-state delta (current location, current status, calendar) that Terminus does not need to know about. One Person row in Terminus, one Resource row in Trantor that references it. No identity duplication.
Trantor also owns the act of allocation: the all-or-nothing hold that Seldon needs when it confirms a Booking and the inventory consume that Hober performs when it closes an Order. Allocation is pessimistic; conflict surfaces as a structured 409.
Concepts
- ResourceType
- A category of interchangeable physical things —
tee_time_slot,standard_king_room,cart,stylist,pos_terminal,kitchen_station,table. Allocation works against the type, not the instance. - Resource
- A concrete instance of a ResourceType. Carries optional
terminusPersonIdfor human resources, location, state (open / closed / out-of-service), and a calendar reference for availability windows. - ResourceHold
- An atomic lock on N instances of a ResourceType for a specific consumer (typically a Seldon Booking). All-or-nothing: a request for 4 carts at a moment that only has 3 free returns a structured conflict rather than holding 3. Expires if not consumed; an expired-hold sweeper releases them.
- TrantorAllocator
- The service Seldon and Hober inject directly today. Three methods:
queryAvailableTypes(what is free in this window),holdResources(atomic hold, throwsAllocationConflictExceptionwith structured shortfall on failure),releaseHolds(cancel a hold without consuming). - Discrete resource state
- Some Resources have a long-lived state that is not a Hold. A dine-in table is held "until the tab closes" rather than "for a 90-minute window" — modelled as a state flag on the Resource (with the Order id in
linkRef), not a ResourceHold row. Different physics, different storage. - Calendar-aware allocation
- Resources can carry an
availabilityTimeSchemeIdpointing at a Seldon TimeScheme. The allocator respects open hours, maintenance windows, and per-resource calendars. Time-window-aware allocation against arbitrary calendars is a follow-up; today's allocator works on instantaneous counts.
API surface
All endpoints are versioned under /trantor/v1/. Seldon and Hober typically call TrantorAllocator directly via dependency injection rather than through HTTP; the endpoints below are for admin tooling, external integrations, and the AI-driven authoring surface.
| Method | Path | Purpose |
|---|---|---|
| POST / GET | /trantor/v1/resource-types | Create or list ResourceTypes for the tenant. |
| GET / PUT / DELETE | /trantor/v1/resource-types/{id} | Fetch, replace, or soft-delete a ResourceType. |
| POST / GET | /trantor/v1/resources | Create or list concrete Resources of a given type. |
| GET / PATCH / DELETE | /trantor/v1/resources/{id} | Fetch, modify (state, location, calendar), or soft-delete a Resource. |
| POST | /trantor/v1/holds | Place an atomic ResourceHold. Body specifies the type, count, and window. 409 on shortfall with structured detail. |
| DELETE | /trantor/v1/holds/{id} | Release a ResourceHold without consuming. |
| GET | /trantor/v1/availability | Query free counts per type for a window. |
Example: hold two carts
Seldon's Booking flow calls this internally; the equivalent HTTP shape:
POST /trantor/v1/holds
Content-Type: application/json
Authorization: Bearer <token>
Idempotency-Key: 7a4b8c1f-…
{
"resourceTypeId": "rt_cart",
"count": 2,
"window": {
"startsAt": "2026-06-12T09:00:00Z",
"endsAt": "2026-06-12T13:00:00Z"
},
"linkRef": {
"kind": "seldon_booking",
"id": "bk_01JAZB…"
}
}
If only one cart is free, the response is a structured 409:
{
"type": "https://errors.foundation.dev/allocation-conflict",
"title": "Allocation conflict",
"status": 409,
"detail": "Insufficient resources of type rt_cart in window",
"resourceTypeId": "rt_cart",
"requested": 2,
"available": 1,
"conflictingHoldIds": ["rh_01JAZC…"]
}
How it fits with the rest
flowchart LR
S[Seldon Booking] -- hold --> T(Trantor)
Ho[Hober close] -- consume --> T
T -. identity for humans .-> Te[Terminus]
Pa[Payments POS terminals] -. device .-> T
Seldon calls TrantorAllocator during Booking confirm to hold the resources the Offer requires. Hober calls it on Order close to consume Hardin recipe ingredients out of Trantor inventory. Terminus is referenced for the identity behind any human Resource. Payments POS Terminals are Trantor Resources of type pos_terminal, not a Payments-owned entity. Kitchen stations, tables, and printers all live here too — one physical-asset choke point.