Versioned lookups
lookupsx-lt-optionsenvelope.lookupslist_knowledge_versionsGET /api/escalations/:id/lookupsOption lists live once in the knowledge store as immutable editions; rows carry a three-field ref and forms read the edition through it.
The situation
Ten thousand open rows offer the same list of reject reasons. The list changes monthly. Copying it onto every row wastes space and freezes stale options; reading it live makes old records lie about what the person saw.
The recipe
Reference the list on the wait:
const result = await conditional<{ reasonId: string }>(`inspect-${orderId}`, { role: 'inspection', metadata: { orderId }, lookups: [ { domain: 'catalog', key: 'reasons' }, // follows the newest edition { domain: 'catalog', key: 'materials', version: 2 }, // pinned ],});Address it from the role’s form:
"reasonId": { "type": "string", "title": "Reason", "x-lt-options": "lookup.reasons.items" }Every write that changes an entry’s data mints a new immutable edition. A current ref on a pending row shows the newest edition on its next render; a resolved row reads the edition that was current when it closed. Inspect lineage with ltc kb versions catalog reasons or list_knowledge_versions. Paths may interpolate an answer, lookup.geo.regions.{{resolver.country}}, for cascading selects.
The guarantee
The refs on the row are the grant: anyone who may read the row may fetch exactly the editions it names, through GET /api/escalations/:id/lookups. A value picked from a current list is validated against the edition in force at submit, so an option removed while the form was open is refused with the canonical 422. Refs live on the unindexed envelope, never on the metadata facets. See Lookups.
Related patterns
Forms and configuration
The form is data
A role's form is a versioned JSON Schema; each row pins the edition it renders, so editing the form never breaks work in flight.
Forms and configuration
Conditional fields
A field appears only when a condition holds, live as the person types, and a hidden required field is skipped by validation on both sides.
Resolving from anywhere
One contract, one 422
Every resolve surface rejects a bad payload with the same 422 body naming each field, so every client handles validation one way.
Forms and configuration
Start a workflow from a form
A control on a work form starts another workflow with values mapped from the record, under the same per-caller list and input gate.
