@githolon/dsl 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,12 +8,12 @@
8
8
  /**
9
9
  * framework/capability.ts — `capability()`: THE MARKETPLACE DECLARATION
10
10
  * (`architecture/capability_marketplace.md` §4, ruling M2 — the framework-hidden
11
- * lane). ONE call declares a typed Order→Receipt capability interface; the dev
12
- * sees a typed order call and a typed task row, and the framework supplies
11
+ * lane). ONE call declares a typed Obligation→Receipt capability interface; the dev
12
+ * sees a typed obligation call and a typed task row, and the framework supplies
13
13
  * everything else through the SHIPPED `impureCapability()` quartet — so the gate
14
- * law (`check_order_receipt`), the kernel recognizer, codegen, and the stable-id
14
+ * law (`check_obligation_receipt`), the kernel recognizer, codegen, and the stable-id
15
15
  * lockfile all see a perfectly ordinary impure capability. Hidden means *not
16
- * hand-rolled*, never *not auditable*: the Order and Receipt facts ride the
16
+ * hand-rolled*, never *not auditable*: the Obligation and Receipt facts ride the
17
17
  * ledger verbatim.
18
18
  *
19
19
  * ```ts
@@ -22,7 +22,7 @@
22
22
  * receipt: { tokensIn: t.int(), tokensOut: t.int() },
23
23
  * });
24
24
  * // derived, deterministically from the name:
25
- * // aggregate LlmCompleteTask order orderLlmComplete
25
+ * // aggregate LlmCompleteTask obligation obligeLlmComplete
26
26
  * // complete completeLlmComplete fail/block/deadLetter likewise
27
27
  * // capability "llm.complete" (the CapabilityBinding the executor must hold)
28
28
  * ```
@@ -84,7 +84,7 @@ export interface CapabilityDecl<
84
84
  > extends ImpureCapability<string, RequestShape<Req>, Req & Rec> {
85
85
  /** The capability name — the `CapabilityBinding` slot the executor must hold. */
86
86
  readonly name: string;
87
- /** The request fields' zod object (what `order<Pascal>`'s params validate as). */
87
+ /** The request fields' zod object (what `oblige<Pascal>`'s params validate as). */
88
88
  readonly requestSchema: z.ZodObject<RequestShape<Req>>;
89
89
  /**
90
90
  * THE EXECUTOR'S SCAN LANE — a declared, indexed query over the task
@@ -127,7 +127,7 @@ export function capability<
127
127
  for (const k of Object.keys(fields)) {
128
128
  if (reserved.has(k)) {
129
129
  throw new Error(
130
- `capability('${name}'): ${side} field '${k}' shadows the reserved Order→Receipt envelope — rename it (the envelope is framework law: ${[...reserved].join(", ")})`,
130
+ `capability('${name}'): ${side} field '${k}' shadows the reserved Obligation→Receipt envelope — rename it (the envelope is framework law: ${[...reserved].join(", ")})`,
131
131
  );
132
132
  }
133
133
  }
@@ -135,7 +135,7 @@ export function capability<
135
135
  for (const k of Object.keys(receipt)) {
136
136
  if (k in spec.request) {
137
137
  throw new Error(
138
- `capability('${name}'): '${k}' is declared on BOTH request and receipt — a field has one writer (the order writes requests, the complete Receipt writes receipts); split or rename it`,
138
+ `capability('${name}'): '${k}' is declared on BOTH request and receipt — a field has one writer (the obligation writes requests, the complete Receipt writes receipts); split or rename it`,
139
139
  );
140
140
  }
141
141
  }
@@ -144,7 +144,7 @@ export function capability<
144
144
  const aggregateId = `${pascal}Task`;
145
145
 
146
146
  // Receipt fields fold only when the complete Receipt lands — auto-`.optional()`
147
- // so a freshly-ordered task decodes on the generated read type (the same
147
+ // so a freshly-obligated task decodes on the generated read type (the same
148
148
  // read-decode discipline the envelope's own optionals follow).
149
149
  const receiptFields = Object.fromEntries(
150
150
  Object.entries(receipt).map(([k, f]) => [k, f.optional()]),
@@ -162,7 +162,7 @@ export function capability<
162
162
 
163
163
  const quartet = impureCapability({
164
164
  aggregateId,
165
- orderDirectiveId: `order${pascal}`,
165
+ obligationDirectiveId: `oblige${pascal}`,
166
166
  completeDirectiveId: `complete${pascal}`,
167
167
  failDirectiveId: `fail${pascal}`,
168
168
  blockDirectiveId: `block${pascal}`,
@@ -6,7 +6,7 @@
6
6
  // If a file isn't this / hosting this / authoring for this / proving this — it's gone.
7
7
 
8
8
  /**
9
- * framework/impure_capability.ts — the FRAMEWORK Order→Receipt mechanism (tenant-agnostic).
9
+ * framework/impure_capability.ts — the FRAMEWORK Obligation→Receipt mechanism (tenant-agnostic).
10
10
  *
11
11
  * ── Why this module exists (the framework↔tenant line) ───────────────────────
12
12
  * A *provider-side intent* in Nomos 1 was an ephemeral job: dispatch → the
@@ -17,12 +17,12 @@
17
17
  *
18
18
  * Nomos 2's law is "**every authored intent folds SOME aggregate**" (contract §1) —
19
19
  * there is no ephemeral job. The faithful shape of an async impure capability is therefore
20
- * an **Order → Receipt** pair of ordinary `WireIntent`s with a first-class lifecycle law:
20
+ * an **Obligation → Receipt** pair of ordinary `WireIntent`s with a first-class lifecycle law:
21
21
  *
22
- * • **Order** — a normal intent whose role is the request: a peer dispatches
23
- * `order…(params, status:'requested')`,
24
- * `.creates` a task aggregate keyed by a KERNEL-MINTED order id
25
- * (`<OrderType>_<uuidv7>`, via the marker-driven front-door; idempotent on
22
+ * • **Obligation** — a normal intent whose role is the request: a peer dispatches
23
+ * `oblige…(params, status:'requested')`,
24
+ * `.creates` a task aggregate keyed by a KERNEL-MINTED obligation id
25
+ * (`<ObligationType>_<uuidv7>`, via the marker-driven front-door; idempotent on
26
26
  * replay by capture).
27
27
  * • **(impure work)** — a capability provider watches `status:'requested'` rows, performs
28
28
  * the side-effect (rendering / upload / multi-target provider work — a host/report
@@ -33,14 +33,14 @@
33
33
  * Some domains also expose explicit blocked/DLQ receipts using the
34
34
  * same envelope fields below.
35
35
  *
36
- * Order and Receipt are roles/facets of `Intent`, not special admission objects, job-table rows,
36
+ * Obligation and Receipt are roles/facets of `Intent`, not special admission objects, job-table rows,
37
37
  * or a privileged mutation channel.
38
38
  *
39
39
  * **Reporting upward = the authoring peer reactively WATCHES the task aggregate.** The receipt
40
40
  * arrives as an ordinary projection update (no polling, no fire-and-forget push, no
41
41
  * response-mutation). This is exactly the proven `ExportJobAggregate` shape
42
42
  * (the tenant domains package `thing_structures`) generalised into a reusable framework pattern so a
43
- * domain dev does NOT hand-roll the {aggregate + order + complete + fail} quartet each
43
+ * domain dev does NOT hand-roll the {aggregate + obligation + complete + fail} quartet each
44
44
  * time — they DECLARE the task's params and get the quartet for free, like `relations.ts`
45
45
  * gives grant/revoke for free.
46
46
  *
@@ -77,7 +77,7 @@ import type { Ports } from "../ctx.js";
77
77
 
78
78
  /**
79
79
  * The impure-capability lifecycle vocabulary — framework-fixed. A task is `requested` when
80
- * the peer orders it; the provider authors the ordinary Receipt intent that flips it
80
+ * the peer obligates it; the provider authors the ordinary Receipt intent that flips it
81
81
  * to `completed` (with a `resultRef`) or `failed` (with a `failureReason`). One-way after a
82
82
  * terminal state: the lifecycle law is terminal-once at the gate/envelope, not a separate
83
83
  * privileged mutation channel.
@@ -92,14 +92,14 @@ export const IMPURE_CAPABILITY_STATUSES = [
92
92
  export type ImpureCapabilityStatus = (typeof IMPURE_CAPABILITY_STATUSES)[number];
93
93
 
94
94
  /**
95
- * The framework envelope fields EVERY impure-capability aggregate carries — the Order→Receipt
95
+ * The framework envelope fields EVERY impure-capability aggregate carries — the Obligation→Receipt
96
96
  * lifecycle, independent of the tenant's request params:
97
97
  *
98
98
  * - requestId Lww — the task's own id (== the aggregate instance id; a
99
- * KERNEL-MINTED `<OrderType>_<uuidv7>`). Immutable-after-create (the
99
+ * KERNEL-MINTED `<ObligationType>_<uuidv7>`). Immutable-after-create (the
100
100
  * create-only gap; modelled Lww).
101
- * - requestedBy Lww — who ordered the task (provenance). Immutable-after-create.
102
- * - requestedAt Int — epoch-ms the order was authored. Immutable-after-create.
101
+ * - requestedBy Lww — who obligated the task (provenance). Immutable-after-create.
102
+ * - requestedAt Int — epoch-ms the obligation was authored. Immutable-after-create.
103
103
  * - status Lww enum — `requested` → the reactor flips to a terminal status.
104
104
  * - resultRef Lww json (OPTIONAL) — the content-addressed RESULT the reactor
105
105
  * records on completion (a blob ref / download url JSON leaf). Absent
@@ -111,10 +111,10 @@ export type ImpureCapabilityStatus = (typeof IMPURE_CAPABILITY_STATUSES)[number]
111
111
  * external condition.
112
112
  * - blockedAt Int (OPTIONAL) — epoch-ms the task was parked as blocked.
113
113
  * - nextAction Lww (OPTIONAL) — human/host-action hint, e.g. refreshAuth.
114
- * - deadLetterReason Lww (OPTIONAL) — why the order was moved to the DLQ.
115
- * - deadLetteredAt Int (OPTIONAL) — epoch-ms the order entered the DLQ.
114
+ * - deadLetterReason Lww (OPTIONAL) — why the obligation was moved to the DLQ.
115
+ * - deadLetteredAt Int (OPTIONAL) — epoch-ms the obligation entered the DLQ.
116
116
  *
117
- * The OPTIONALs are `.optional()` so a freshly-ordered task (which folds none of them)
117
+ * The OPTIONALs are `.optional()` so a freshly-obligated task (which folds none of them)
118
118
  * decodes on the generated read type — the same read-decode discipline the rest of the
119
119
  * framework uses (identity NOTE 1a / gap 1).
120
120
  */
@@ -122,6 +122,8 @@ export const IMPURE_CAPABILITY_ENVELOPE = {
122
122
  requestId: t.string().merge(Lww),
123
123
  requestedBy: t.string().merge(Lww),
124
124
  requestedAt: t.int().merge(Lww),
125
+ scheduledAt: t.int().merge(Lww),
126
+ deadlineAt: t.int().merge(Lww),
125
127
  status: t.enum(IMPURE_CAPABILITY_STATUSES).merge(Lww),
126
128
  resultRef: t.json().merge(Lww).optional(),
127
129
  failureReason: t.string().merge(Lww).optional(),
@@ -131,16 +133,16 @@ export const IMPURE_CAPABILITY_ENVELOPE = {
131
133
  nextAction: t.string().merge(Lww).optional(),
132
134
  deadLetterReason: t.string().merge(Lww).optional(),
133
135
  deadLetteredAt: t.int().merge(Lww).optional(),
134
- // The FRAMEWORK Order↔Receipt CLOSURE link (OPTIONAL): the id of the Order this
136
+ // The FRAMEWORK Obligation↔Receipt CLOSURE link (OPTIONAL): the id of the Obligation this
135
137
  // RECEIPT closes (== the task's own requestId — the reactor mutates the task instance).
136
138
  // GENERIC, written by every impureCapability Receipt (complete OR fail), so the read
137
- // projection answers "is Order O closed, by which Receipt, with what result" in O(1)
138
- // off the `(closesOrder, order_id)` index — never a ledger scan. Absent while
139
- // `requested` (a fresh order has no closing receipt). The kernel supplies its
139
+ // projection answers "is Obligation O closed, by which Receipt, with what result" in O(1)
140
+ // off the `(closesObligation, obligation_id)` index — never a ledger scan. Absent while
141
+ // `requested` (a fresh obligation has no closing receipt). The kernel supplies its
140
142
  // framework-fixed Lww driver (`kernel::manifest_view::IMPURE_CAPABILITY_RECEIPT_FIELDS`)
141
- // and the gate enforces the lifecycle around it (`nomos_executor::check_order_receipt` —
142
- // admit step 5.5: OrphanReceipt / ReceiptOnTerminalOrder, terminal-once as law).
143
- closesOrder: t.string().merge(Lww).optional(),
143
+ // and the gate enforces the lifecycle around it (`nomos_executor::check_obligation_receipt` —
144
+ // admit step 5.5: OrphanReceipt / ReceiptOnTerminalObligation, terminal-once as law).
145
+ closesObligation: t.string().merge(Lww).optional(),
144
146
  } as const;
145
147
 
146
148
  /**
@@ -163,9 +165,9 @@ export function impureCapabilityAggregate<
163
165
  }
164
166
 
165
167
  /**
166
- * The deterministic instance-id rule: a impure capability is keyed by its OWN order id.
167
- * That id is KERNEL-MINTED (`<OrderType>_<uuidv7>`, via the marker-driven front-door),
168
- * captured + committed — so the `order` create is idempotent on replay by capture, and the
168
+ * The deterministic instance-id rule: a impure capability is keyed by its OWN obligation id.
169
+ * That id is KERNEL-MINTED (`<ObligationType>_<uuidv7>`, via the marker-driven front-door),
170
+ * captured + committed — so the `obligation` create is idempotent on replay by capture, and the
169
171
  * `complete`/`fail` receipt addresses the SAME instance by reading the committed minted id.
170
172
  * Trivial today (identity), but centralised here so the keying rule has ONE home (the
171
173
  * symmetry with `relations.ts`'s `relationTupleInstanceId`).
@@ -174,11 +176,13 @@ export function impureCapabilityInstanceId(requestId: string): string {
174
176
  return requestId;
175
177
  }
176
178
 
177
- /** The Zod schema for the framework envelope fields the ORDER payload always carries. */
178
- const orderEnvelopeSchema = z.object({
179
+ /** The Zod schema for the framework envelope fields the Obligation payload always carries. */
180
+ const obligationEnvelopeSchema = z.object({
179
181
  requestId: z.string(),
180
182
  requestedBy: z.string(),
181
183
  requestedAt: z.number().int(),
184
+ scheduledAt: z.number().int(),
185
+ deadlineAt: z.number().int(),
182
186
  });
183
187
 
184
188
  /** The receipt-COMPLETE payload: the request id + the content-addressed result + when. */
@@ -204,7 +208,7 @@ const blockSchema = z.object({
204
208
  nextAction: z.string(),
205
209
  });
206
210
 
207
- /** The receipt-DLQ payload: terminal dead-letter, requiring a fresh order to recover. */
211
+ /** The receipt-DLQ payload: terminal dead-letter, requiring a fresh obligation to recover. */
208
212
  const deadLetterSchema = z.object({
209
213
  requestId: z.string(),
210
214
  deadLetterReason: z.string(),
@@ -232,13 +236,13 @@ function envelopeInstance(aggregateId: string, requestId: string) {
232
236
  }
233
237
 
234
238
  /**
235
- * orderOps — the ORDER half. Seeds the envelope provenance + `status:'requested'`,
239
+ * obligationOps — the Obligation half. Seeds the envelope provenance + timing + `status:'requested'`,
236
240
  * PLUS the tenant's request params (the `paramOps` the caller's `.plan` supplies for
237
241
  * its own fields). The task is bound to its requestId instance (#105).
238
242
  */
239
- export function orderOps(
243
+ export function obligationOps(
240
244
  aggregateId: string,
241
- p: z.infer<typeof orderEnvelopeSchema>,
245
+ p: z.infer<typeof obligationEnvelopeSchema>,
242
246
  paramOps: PlannedOp[] = [],
243
247
  ): PlannedOp[] {
244
248
  const taskInstance = envelopeInstance(aggregateId, p.requestId);
@@ -246,6 +250,8 @@ export function orderOps(
246
250
  set(taskInstance, "requestId", p.requestId),
247
251
  set(taskInstance, "requestedBy", p.requestedBy),
248
252
  set(taskInstance, "requestedAt", p.requestedAt),
253
+ set(taskInstance, "scheduledAt", p.scheduledAt),
254
+ set(taskInstance, "deadlineAt", p.deadlineAt),
249
255
  set(taskInstance, "status", "requested"),
250
256
  ...paramOps,
251
257
  ];
@@ -266,10 +272,10 @@ export function completeOps(
266
272
  set(taskInstance, "status", "completed"),
267
273
  set(taskInstance, "resultRef", p.resultRef),
268
274
  set(taskInstance, "completedAt", p.completedAt),
269
- // The FRAMEWORK Order↔Receipt CLOSURE link — this Receipt closes the Order whose
275
+ // The FRAMEWORK Obligation↔Receipt CLOSURE link — this Receipt closes the Obligation whose
270
276
  // id == requestId (the task instance the reactor mutates). Indexed by the read
271
277
  // projection for the O(1) closure read. Wire-identical to the Rust reactor's op.
272
- set(taskInstance, "closesOrder", impureCapabilityInstanceId(p.requestId)),
278
+ set(taskInstance, "closesObligation", impureCapabilityInstanceId(p.requestId)),
273
279
  // The TENANT RESULT FIELDS the Receipt writes into the task aggregate (e.g.
274
280
  // `transcription = <text>`). The envelope is auto; these are the tenant's own
275
281
  // structured result. Framework writes them verbatim; the tenant owns their meaning.
@@ -293,8 +299,8 @@ export function failOps(
293
299
  set(taskInstance, "failureReason", p.failureReason),
294
300
  set(taskInstance, "completedAt", p.failedAt),
295
301
  // CLOSURE link — a FAILED task is still CLOSED by its receipt (the closure read
296
- // reports it CLOSED with no result). Same `closesOrder == requestId` as `completeOps`.
297
- set(taskInstance, "closesOrder", impureCapabilityInstanceId(p.requestId)),
302
+ // reports it CLOSED with no result). Same `closesObligation == requestId` as `completeOps`.
303
+ set(taskInstance, "closesObligation", impureCapabilityInstanceId(p.requestId)),
298
304
  ];
299
305
  }
300
306
 
@@ -309,11 +315,11 @@ export function blockOps(
309
315
  set(taskInstance, "blockedReason", p.blockedReason),
310
316
  set(taskInstance, "blockedAt", p.blockedAt),
311
317
  set(taskInstance, "nextAction", p.nextAction),
312
- set(taskInstance, "closesOrder", impureCapabilityInstanceId(p.requestId)),
318
+ set(taskInstance, "closesObligation", impureCapabilityInstanceId(p.requestId)),
313
319
  ];
314
320
  }
315
321
 
316
- /** deadLetterOps — terminal DLQ receipt. Recovery is a fresh explicit Order. */
322
+ /** deadLetterOps — terminal DLQ receipt. Recovery is a fresh explicit Obligation. */
317
323
  export function deadLetterOps(
318
324
  aggregateId: string,
319
325
  p: z.infer<typeof deadLetterSchema>,
@@ -323,7 +329,7 @@ export function deadLetterOps(
323
329
  set(taskInstance, "status", "deadLettered"),
324
330
  set(taskInstance, "deadLetterReason", p.deadLetterReason),
325
331
  set(taskInstance, "deadLetteredAt", p.deadLetteredAt),
326
- set(taskInstance, "closesOrder", impureCapabilityInstanceId(p.requestId)),
332
+ set(taskInstance, "closesObligation", impureCapabilityInstanceId(p.requestId)),
327
333
  ];
328
334
  }
329
335
 
@@ -331,11 +337,11 @@ export function deadLetterOps(
331
337
  * The generated directive QUARTET for a impure capability. A domain dev calls
332
338
  * {@link impureCapability} ONCE and gets:
333
339
  * • `aggregate` — the task aggregate handle (envelope ⊕ params),
334
- * • `order` — `.creates` directive (the authored Order),
340
+ * • `obligation` — `.creates` directive (the authored Obligation),
335
341
  * • `complete` — `.mutates` directive (the reactor's success Receipt),
336
342
  * • `fail` — `.mutates` directive (the reactor's failure Receipt),
337
- * plus the deterministic `instanceId` rule. The `order` payload is the framework
338
- * order-envelope MERGED with the tenant's `paramsSchema`; `complete`/`fail` are fixed.
343
+ * plus the deterministic `instanceId` rule. The `obligation` payload is the framework
344
+ * obligation-envelope MERGED with the tenant's `paramsSchema`; `complete`/`fail` are fixed.
339
345
  */
340
346
  export interface ImpureCapability<
341
347
  Id extends string,
@@ -343,7 +349,7 @@ export interface ImpureCapability<
343
349
  F extends Record<string, Field>,
344
350
  > {
345
351
  readonly aggregate: AggregateHandle<Id, typeof IMPURE_CAPABILITY_ENVELOPE & F>;
346
- readonly order: RequirableDirective<OrderPayload<ParamsShape>>;
352
+ readonly obligation: RequirableDirective<ObligationPayload<ParamsShape>>;
347
353
  readonly complete: RequirableDirective<z.infer<typeof completeSchema>>;
348
354
  readonly fail: RequirableDirective<z.infer<typeof failSchema>>;
349
355
  readonly block: RequirableDirective<z.infer<typeof blockSchema>>;
@@ -364,17 +370,17 @@ export interface ImpureCapability<
364
370
  }
365
371
 
366
372
  /**
367
- * The inferred ORDER payload type = the framework envelope schema EXTENDED with the
368
- * tenant's param shape — the EXACT inference `orderEnvelopeSchema.extend(paramsSchema)`
369
- * produces (so the declared `ImpureCapability.order` directive type and the value the factory
373
+ * The inferred Obligation payload type = the framework envelope schema EXTENDED with the
374
+ * tenant's param shape — the EXACT inference `obligationEnvelopeSchema.extend(paramsSchema)`
375
+ * produces (so the declared `ImpureCapability.obligation` directive type and the value the factory
370
376
  * builds are the same type, with no brittle hand-written intersection).
371
377
  */
372
- export type OrderPayload<ParamsShape extends z.ZodRawShape> = z.infer<
373
- ReturnType<typeof orderEnvelopeSchema.extend<ParamsShape>>
378
+ export type ObligationPayload<ParamsShape extends z.ZodRawShape> = z.infer<
379
+ ReturnType<typeof obligationEnvelopeSchema.extend<ParamsShape>>
374
380
  >;
375
381
 
376
382
  /**
377
- * impureCapability — the framework FACTORY. Declares an Order→Receipt task in ONE call:
383
+ * impureCapability — the framework FACTORY. Declares an Obligation→Receipt task in ONE call:
378
384
  *
379
385
  * ```ts
380
386
  * // Build the aggregate handle FIRST so `writeParams` references it without a
@@ -384,7 +390,7 @@ export type OrderPayload<ParamsShape extends z.ZodRawShape> = z.infer<
384
390
  *
385
391
  * export const exportJob = impureCapability({
386
392
  * aggregateId: "ExportJobAggregate",
387
- * orderDirectiveId: "createExportJob",
393
+ * obligationDirectiveId: "createExportJob",
388
394
  * completeDirectiveId: "completeExportJob",
389
395
  * failDirectiveId: "failExportJob",
390
396
  * blockDirectiveId: "blockExportJob",
@@ -400,33 +406,33 @@ export type OrderPayload<ParamsShape extends z.ZodRawShape> = z.infer<
400
406
  *
401
407
  * The tenant declares the wire ids (so they're stable, minify-safe DATA — the same
402
408
  * declared-once rule as `aggregate()`/`directive()`), the param FIELDS (for the
403
- * aggregate shape + codegen) and the param SCHEMA (for the order payload + the
409
+ * aggregate shape + codegen) and the param SCHEMA (for the obligation payload + the
404
410
  * `writeParams` plan fragment). The framework wires the lifecycle: status defaulting,
405
411
  * the requestId keying, and the complete/fail receipt directives.
406
412
  *
407
413
  * `writeParams(taskInstanceId, params)` returns the `PlannedOp[]` for the tenant's OWN
408
- * fields (the framework already writes the envelope) — the param half of the order
414
+ * fields (the framework already writes the envelope) — the param half of the obligation
409
415
  * plan, kept type-checked against the tenant's param fields via the `set()` helper.
410
416
  */
411
417
  export function impureCapability<
412
418
  const Id extends string,
413
- const OrderId extends string,
419
+ const ObligationId extends string,
414
420
  const CompleteId extends string,
415
421
  const FailId extends string,
416
422
  ParamsShape extends z.ZodRawShape,
417
423
  F extends Record<string, Field>,
418
424
  >(spec: {
419
425
  aggregateId: Id;
420
- orderDirectiveId: OrderId;
426
+ obligationDirectiveId: ObligationId;
421
427
  completeDirectiveId: CompleteId;
422
428
  failDirectiveId: FailId;
423
429
  blockDirectiveId: string;
424
430
  deadLetterDirectiveId: string;
425
431
  /** The task's request-param FIELDS (merged onto the envelope for the aggregate shape). */
426
432
  params: F;
427
- /** The task's request-param Zod SHAPE (merged onto the order envelope schema). */
433
+ /** The task's request-param Zod SHAPE (merged onto the obligation envelope schema). */
428
434
  paramsSchema: ParamsShape;
429
- /** Plan fragment writing the tenant's param fields for the order (envelope is auto). */
435
+ /** Plan fragment writing the tenant's param fields for the obligation (envelope is auto). */
430
436
  writeParams: (
431
437
  taskInstanceId: string,
432
438
  params: z.infer<z.ZodObject<ParamsShape>>,
@@ -434,7 +440,7 @@ export function impureCapability<
434
440
  /**
435
441
  * OPTIONAL plan fragment writing the tenant's STRUCTURED RESULT fields onto the task
436
442
  * aggregate when the `complete` Receipt is authored (the lifecycle envelope + the
437
- * `closesOrder` link are auto). The receipt's `resultRef` carries the content-addressed
443
+ * `closesObligation` link is auto). The receipt's `resultRef` carries the content-addressed
438
444
  * Evidence ref (the authoritative artifact); a tenant that ALSO wants the result in a
439
445
  * typed, queryable field (e.g. a voice-note's `transcription`) supplies it here. The
440
446
  * `resultRef` string is passed through so the tenant can derive the field value from the
@@ -451,22 +457,22 @@ export function impureCapability<
451
457
  }): ImpureCapability<Id, ParamsShape, F> {
452
458
  const taskAggregate = impureCapabilityAggregate(spec.aggregateId, spec.params);
453
459
 
454
- // The order payload = the framework provenance envelope ⊕ the tenant's params.
455
- const orderPayload = orderEnvelopeSchema.extend(spec.paramsSchema);
460
+ // The obligation payload = the framework provenance/timing envelope ⊕ the tenant's params.
461
+ const obligationPayload = obligationEnvelopeSchema.extend(spec.paramsSchema);
456
462
 
457
- const order: RequirableDirective<OrderPayload<ParamsShape>> = directive(
458
- spec.orderDirectiveId,
463
+ const obligation: RequirableDirective<ObligationPayload<ParamsShape>> = directive(
464
+ spec.obligationDirectiveId,
459
465
  )
460
466
  .creates(taskAggregate)
461
- .payload(orderPayload)
467
+ .payload(obligationPayload)
462
468
  .plan((p) => {
463
469
  // The validated payload carries BOTH the envelope (required string/int) and the
464
- // tenant params. `orderOps` writes the envelope; the tenant's `writeParams`
470
+ // tenant params. `obligationOps` writes the envelope; the tenant's `writeParams`
465
471
  // writes its own fields. Extract the envelope explicitly (it is required on the
466
472
  // schema, so these are present non-undefined values).
467
- const envelope = orderEnvelopeSchema.parse(p);
473
+ const envelope = obligationEnvelopeSchema.parse(p);
468
474
  const params = p as z.infer<z.ZodObject<ParamsShape>>;
469
- return orderOps(
475
+ return obligationOps(
470
476
  spec.aggregateId,
471
477
  envelope,
472
478
  spec.writeParams(impureCapabilityInstanceId(envelope.requestId), params),
@@ -502,7 +508,7 @@ export function impureCapability<
502
508
 
503
509
  return {
504
510
  aggregate: taskAggregate,
505
- order,
511
+ obligation,
506
512
  complete,
507
513
  fail,
508
514
  block,
@@ -516,7 +522,7 @@ export function impureCapability<
516
522
 
517
523
  // ─────────────────────────── THE CAPABILITY PROVIDER ─────────────────────────
518
524
  //
519
- // The Order half (`order`) is an ordinary Intent authored by the peer through the one write
525
+ // The Obligation half (`obligation`) is an ordinary Intent authored by the peer through the one write
520
526
  // path. The Receipt half (`complete`/`fail`) is an ordinary Intent authored by a capability
521
527
  // PROVIDER — itself a Nomos author (`admission-hosting.md`: hosted/provider-originated
522
528
  // intents author *through* a policy unit too"). The provider is the runtime ROLE; this
@@ -10,7 +10,7 @@
10
10
  * Nomos sync lifecycle.
11
11
  *
12
12
  * Sync is a control-plane impure capability: Nomos records the desired workspace
13
- * reconciliation as an ordinary Order, a host/provider performs transport using
13
+ * reconciliation as an ordinary Obligation, a host/provider performs transport using
14
14
  * explicit capabilities, and the outcome comes back as an ordinary Receipt. The
15
15
  * remote/JWT/network is never ambient domain state; the ledger carries request,
16
16
  * status, cursors, heads, and errors.
@@ -85,7 +85,7 @@ function syncResultOps(taskId: string, resultRef: string): PlannedOp[] {
85
85
 
86
86
  const workspaceSyncTask = impureCapability({
87
87
  aggregateId: "WorkspaceSyncOperation",
88
- orderDirectiveId: "requestWorkspaceSync",
88
+ obligationDirectiveId: "requestWorkspaceSync",
89
89
  completeDirectiveId: "completeWorkspaceSync",
90
90
  failDirectiveId: "failWorkspaceSync",
91
91
  blockDirectiveId: "blockWorkspaceSync",
@@ -113,7 +113,7 @@ const workspaceSyncTask = impureCapability({
113
113
  completeResult: syncResultOps,
114
114
  });
115
115
 
116
- export const requestWorkspaceSync = workspaceSyncTask.order.requires("requestWorkspaceSync");
116
+ export const requestWorkspaceSync = workspaceSyncTask.obligation.requires("requestWorkspaceSync");
117
117
  export const completeWorkspaceSync = workspaceSyncTask.complete.requires("completeWorkspaceSync");
118
118
  export const failWorkspaceSync = workspaceSyncTask.fail.requires("failWorkspaceSync");
119
119
  export const blockWorkspaceSync = workspaceSyncTask.block.requires("blockWorkspaceSync");
package/src/index.ts CHANGED
@@ -278,7 +278,7 @@ export {
278
278
  impureCapabilityAggregate,
279
279
  impureCapabilityInstanceId,
280
280
  impureCapability,
281
- orderOps,
281
+ obligationOps,
282
282
  completeOps,
283
283
  failOps,
284
284
  blockOps,