@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.
@@ -13,7 +13,7 @@
13
13
  * hashed (the deployed law's content hash baked in), carrying a `<Domain>Client` over a connected
14
14
  * [NomosBridge] (the WebView runner) with:
15
15
  *
16
- * * a typed DISPATCH method per directive — takes the generated `…Payload`, serialises via its
16
+ * * a typed OFFER method per directive — takes the generated `…Payload`, serialises via its
17
17
  * `toPayloadJson()`, auto-stamps omitted `…At` timestamps (the same caller-clock sugar the TS
18
18
  * client applies), returns the new local head;
19
19
  * * a typed declared-QUERY accessor per `query(...)` — one-shot `Future<List<…ReadModel>>` + a
@@ -81,7 +81,7 @@ function rowToModel(model: string, rowVar: string): string {
81
81
  }
82
82
 
83
83
  export interface DartBridgeClientOptions {
84
- /** sha256 of the built `.package.usda` — the law this client dispatches under. */
84
+ /** sha256 of the built `.package.usda` — the law this client offers under. */
85
85
  readonly domainHash: string;
86
86
  }
87
87
 
@@ -95,7 +95,7 @@ export function generateDartBridgeClient(mod: DomainModule, opts: DartBridgeClie
95
95
 
96
96
  const methods: string[] = [];
97
97
 
98
- // ── directives → typed dispatch ──
98
+ // ── directives → typed offer ──
99
99
  const dirs = (mod.directives as AnyDirective[]).filter(
100
100
  (d) => d && typeof d.id === "string" && typeof (d as { payloadSchema?: unknown }).payloadSchema === "object",
101
101
  );
@@ -106,12 +106,12 @@ export function generateDartBridgeClient(mod: DomainModule, opts: DartBridgeClie
106
106
  if (stamps.length > 0) {
107
107
  methods.push(
108
108
  ` /// Author \`${domain}/${d.id}\` locally under the installed law; returns the new local head.`,
109
- ` /// Omitted ${stamps.map((s) => `\`${s}\``).join(" / ")} auto-stamp(s) ISO now() at dispatch.`,
109
+ ` /// Omitted ${stamps.map((s) => `\`${s}\``).join(" / ")} auto-stamp(s) ISO now() at offer.`,
110
110
  ` Future<String> ${method}(${payloadClass} payload) {`,
111
111
  ` final data = payload.toPayloadJson();`,
112
112
  ` final now = DateTime.now().toUtc().toIso8601String();`,
113
113
  ...stamps.map((s) => ` data.putIfAbsent(${dartStr(s)}, () => now);`),
114
- ` return bridge.dispatch(domain: ${dartStr(domain)}, directiveId: ${dartStr(d.id)}, payload: data, domainHash: domainHash);`,
114
+ ` return bridge.offer(intentBytes: _encodeIntentOffer(${dartStr(domain)}, ${dartStr(d.id)}, data, domainHash));`,
115
115
  ` }`,
116
116
  ``,
117
117
  );
@@ -119,7 +119,7 @@ export function generateDartBridgeClient(mod: DomainModule, opts: DartBridgeClie
119
119
  methods.push(
120
120
  ` /// Author \`${domain}/${d.id}\` locally under the installed law; returns the new local head.`,
121
121
  ` Future<String> ${method}(${payloadClass} payload) =>`,
122
- ` bridge.dispatch(domain: ${dartStr(domain)}, directiveId: ${dartStr(d.id)}, payload: payload.toPayloadJson(), domainHash: domainHash);`,
122
+ ` bridge.offer(intentBytes: _encodeIntentOffer(${dartStr(domain)}, ${dartStr(d.id)}, payload.toPayloadJson(), domainHash));`,
123
123
  ``,
124
124
  );
125
125
  }
@@ -196,6 +196,8 @@ export function generateDartBridgeClient(mod: DomainModule, opts: DartBridgeClie
196
196
  `// final app = ${className}(bridge);`,
197
197
  `// await app.${dirs[0] ? safeMethod(dirs[0].id) : "someDirective"}(/* typed payload */);`,
198
198
  ``,
199
+ `import 'dart:convert';`,
200
+ ``,
199
201
  `import 'nomos_dsl.dart';`,
200
202
  `import '${mod.name}.dart';`,
201
203
  `// Re-export the sibling types (read models + payloads + the bridge runtime) so ONE import of this`,
@@ -203,9 +205,20 @@ export function generateDartBridgeClient(mod: DomainModule, opts: DartBridgeClie
203
205
  `export '${mod.name}.dart';`,
204
206
  `export 'nomos_dsl.dart' show NomosBridge, BridgeTransport, NomosBridgeError, kWebviewBridgeVersion;`,
205
207
  ``,
206
- `/// sha256 of the deployed \`.package.usda\` — the installed law this client dispatches under.`,
208
+ `/// sha256 of the deployed \`.package.usda\` — the installed law this client offers under.`,
207
209
  `const String ${hashConst} = ${dartStr(opts.domainHash)};`,
208
210
  ``,
211
+ `String _encodeIntentOffer(String domain, String directiveId, Object? payload, String domainHash) =>`,
212
+ ` base64Encode(utf8.encode(jsonEncode({`,
213
+ ` 'v': 2,`,
214
+ ` 'intent': {`,
215
+ ` 'domain': domain,`,
216
+ ` 'directiveId': directiveId,`,
217
+ ` 'payload': payload,`,
218
+ ` 'domainHash': domainHash,`,
219
+ ` },`,
220
+ ` })));`,
221
+ ``,
209
222
  `/// Typed async client for the \`${domain}\` domain, bound to a connected [NomosBridge].`,
210
223
  `class ${className} {`,
211
224
  ` final NomosBridge bridge;`,
@@ -9,32 +9,32 @@
9
9
  * Provider SDK codegen — the SYMMETRIC TWIN of the typed peer write codegen
10
10
  * (`codegen_dart.ts`).
11
11
  *
12
- * Order and Receipt are ordinary intent roles. This codegen gives a capability provider typed
12
+ * Obligation and Receipt are ordinary intent roles. This codegen gives a capability provider typed
13
13
  * decode/encode helpers; it does not create a separate admission job protocol or a privileged
14
14
  * mutation path.
15
15
  *
16
16
  * ── Why this exists (Jack 2026-06-04) ────────────────────────────────────────
17
17
  * Code is ~free; INTEGRATION POINTS are expensive + fuckup-guaranteed. A capability
18
18
  * PROVIDER (Whisper, a renderer, a multi-target PR transporter) sits at
19
- * the far end of the framework `impureCapability` Order→Receipt pair. Today a dev would
20
- * hand-parse the incoming Order intent JSON and hand-build the Receipt — exactly the
19
+ * the far end of the framework `impureCapability` Obligation→Receipt pair. Today a dev would
20
+ * hand-parse the incoming Obligation intent JSON and hand-build the Receipt — exactly the
21
21
  * brittle integration the peer write codegen eliminated on the WRITE side. This generator
22
22
  * eliminates it on the PROVIDE side: a dev writes ONE typed method
23
- * `handler(Order) -> Result | Failure`, and the generated dispatcher DECODES the Order
23
+ * `handler(Obligation) -> Result | Failure`, and the generated dispatcher DECODES the Obligation
24
24
  * and ENCODES the ordinary Receipt intent — proven in ISOLATION (a Dart unit test), with NO deploy /
25
25
  * daemon / e2e.
26
26
  *
27
27
  * ── What it emits, per `impureCapability` capability (the STANDARD shape, never invented) ──
28
- * 1. a typed `XOrder` — the Order params (envelope provenance `requestId` /
28
+ * 1. a typed `XObligation` — the Obligation params (envelope provenance `requestId` /
29
29
  * `requestedBy` / `requestedAt` ⊕ the tenant params from the DSL), with a
30
- * `fromJson` decoding the incoming Order intent's payload JSON;
30
+ * `fromJson` decoding the incoming Obligation intent's payload JSON;
31
31
  * 2. a typed `XResult` — the success payload the handler returns (`resultRef`, the
32
32
  * content-addressed Evidence handle — the framework's `completeSchema.resultRef`),
33
33
  * implementing `TaskResult.toResultRef()`;
34
34
  * 3. typed failure/blocked/DLQ outcomes;
35
- * 4. a handler typedef `XHandler = TaskOutcome<XResult> Function(XOrder)`;
35
+ * 4. a handler typedef `XHandler = TaskOutcome<XResult> Function(XObligation)`;
36
36
  * 5. a generated `XProvider` dispatcher (`implements TaskProvider`): given the
37
- * registered typed handler, `handleOrder(orderJson, now)` DECODES → calls the
37
+ * registered typed handler, `handleObligation(obligationJson, now)` DECODES → calls the
38
38
  * handler → ENCODES the typed outcome into the ordinary RECEIPT intent — REUSING the peer write
39
39
  * codegen's `CompleteXPayload` / `FailXPayload` / `BlockXPayload` /
40
40
  * `DeadLetterXPayload`. The Receipt is an
@@ -61,7 +61,7 @@ import {
61
61
  /**
62
62
  * The minimal shape of a `impureCapability` declaration the provider codegen needs. A
63
63
  * `ImpureCapability` (framework `impure_capability.ts`) carries EXACTLY this — its task `aggregate`
64
- * handle (whose `.id` is the wire type) and its `.order` / receipt directives
64
+ * handle (whose `.id` is the wire type) and its `.obligation` / receipt directives
65
65
  * directives — so a `impureCapability(...)` return value satisfies `ImpureCapabilityDecl`
66
66
  * STRUCTURALLY. The emit script passes the values straight through: no re-derivation, no
67
67
  * fragile structural inference over the flattened directive list.
@@ -69,8 +69,8 @@ import {
69
69
  export interface ImpureCapabilityDecl {
70
70
  /** The task aggregate handle; `.id` is the wire type (e.g. `TranscribeVoiceNoteAggregate`). */
71
71
  readonly aggregate: { readonly id: string };
72
- /** The ORDER directive: `.creates`, payload = envelope ⊕ tenant params. */
73
- readonly order: { readonly id: string; readonly payloadSchema: z.ZodTypeAny };
72
+ /** The Obligation directive: `.creates`, payload = envelope ⊕ tenant params. */
73
+ readonly obligation: { readonly id: string; readonly payloadSchema: z.ZodTypeAny };
74
74
  /** The COMPLETE (success) Receipt directive: payload = {requestId, resultRef, completedAt}. */
75
75
  readonly complete: { readonly id: string };
76
76
  /** The FAIL (failure) Receipt directive: payload = {requestId, failureReason, failedAt}. */
@@ -81,17 +81,23 @@ export interface ImpureCapabilityDecl {
81
81
  readonly deadLetter: { readonly id: string };
82
82
  }
83
83
 
84
- /** The framework ORDER-ENVELOPE field names — provenance, NOT tenant params. The Order
85
- * type carries them (the provider sees the whole Order intent) but the codegen labels
84
+ /** The framework Obligation envelope field names — provenance/timing, NOT tenant params. The Obligation
85
+ * type carries them (the provider sees the whole Obligation intent) but the codegen labels
86
86
  * them so the doc separates provenance from the tenant's request params. */
87
- const ORDER_ENVELOPE_FIELDS = new Set(["requestId", "requestedBy", "requestedAt"]);
87
+ const OBLIGATION_ENVELOPE_FIELDS = new Set([
88
+ "requestId",
89
+ "requestedBy",
90
+ "requestedAt",
91
+ "scheduledAt",
92
+ "deadlineAt",
93
+ ]);
88
94
 
89
- /** A stable PascalCase base for a task's generated class family, derived from its ORDER
90
- * directive id with a leading `create`/`order` verb stripped (so `createTranscribeVoiceNote`
95
+ /** A stable PascalCase base for a task's generated class family, derived from its Obligation
96
+ * directive id with a leading `create`/`oblige` verb stripped (so `createTranscribeVoiceNote`
91
97
  * → `TranscribeVoiceNote`). Pure + deterministic. */
92
- export function taskBaseName(orderDirectiveId: string): string {
93
- const stripped = orderDirectiveId.replace(/^(create|order)/, "");
94
- const base = stripped === "" ? orderDirectiveId : stripped;
98
+ export function taskBaseName(obligationDirectiveId: string): string {
99
+ const stripped = obligationDirectiveId.replace(/^(create|oblige)/, "");
100
+ const base = stripped === "" ? obligationDirectiveId : stripped;
95
101
  return cap(base);
96
102
  }
97
103
 
@@ -109,21 +115,21 @@ function deadLetterPayloadClass(deadLetterDirectiveId: string): string {
109
115
  return `${cap(deadLetterDirectiveId)}Payload`;
110
116
  }
111
117
 
112
- /** Dart field DECL for one Order field (optional → nullable). */
113
- function orderFieldDecl(s: PayloadFieldSpec): string {
114
- const provenance = ORDER_ENVELOPE_FIELDS.has(s.name)
118
+ /** Dart field DECL for one Obligation field (optional → nullable). */
119
+ function obligationFieldDecl(s: PayloadFieldSpec): string {
120
+ const provenance = OBLIGATION_ENVELOPE_FIELDS.has(s.name)
115
121
  ? ` // envelope provenance (framework)`
116
122
  : ``;
117
123
  return ` final ${s.dartType}${s.optional ? "?" : ""} ${s.name};${provenance}`;
118
124
  }
119
125
 
120
- /** Dart ctor PARAM for one Order field. */
121
- function orderCtorParam(s: PayloadFieldSpec): string {
126
+ /** Dart ctor PARAM for one Obligation field. */
127
+ function obligationCtorParam(s: PayloadFieldSpec): string {
122
128
  return ` ${s.optional ? "" : "required "}this.${s.name},`;
123
129
  }
124
130
 
125
131
  /**
126
- * Expression decoding ONE Order field FROM the already-`jsonDecode`d payload map `j`.
132
+ * Expression decoding ONE Obligation field FROM the already-`jsonDecode`d payload map `j`.
127
133
  * The REVERSE of the peer codegen's `toJsonExpr` — same field-kind matrix:
128
134
  * • scalar — read directly, cast to the Dart scalar type.
129
135
  * • enum — `XEnum.fromWire(j['f'] as String)`.
@@ -132,7 +138,7 @@ function orderCtorParam(s: PayloadFieldSpec): string {
132
138
  * Optional fields tolerate absence (return null); required fields cast strictly (a
133
139
  * missing required field throws on the cast — the strict-decode contract).
134
140
  */
135
- function orderFromJsonExpr(s: PayloadFieldSpec): string {
141
+ function obligationFromJsonExpr(s: PayloadFieldSpec): string {
136
142
  const raw = `j['${s.name}']`;
137
143
  switch (s.toJson) {
138
144
  case "scalar": {
@@ -168,22 +174,22 @@ function orderFromJsonExpr(s: PayloadFieldSpec): string {
168
174
  }
169
175
  }
170
176
 
171
- /** Emit the typed `XOrder` class — the DECODED incoming Order. */
172
- function emitOrderClass(
177
+ /** Emit the typed `XObligation` class — the DECODED incoming Obligation. */
178
+ function emitObligationClass(
173
179
  base: string,
174
- orderDirectiveId: string,
180
+ obligationDirectiveId: string,
175
181
  specs: PayloadFieldSpec[],
176
182
  ): string {
177
- const cls = `${base}Order`;
178
- const fieldDecls = specs.map(orderFieldDecl).join("\n");
179
- const ctorParams = specs.map(orderCtorParam).join("\n");
183
+ const cls = `${base}Obligation`;
184
+ const fieldDecls = specs.map(obligationFieldDecl).join("\n");
185
+ const ctorParams = specs.map(obligationCtorParam).join("\n");
180
186
  const fromJsonAssigns = specs
181
- .map((s) => ` ${s.name}: ${orderFromJsonExpr(s)},`)
187
+ .map((s) => ` ${s.name}: ${obligationFromJsonExpr(s)},`)
182
188
  .join("\n");
183
189
  return [
184
- `/// The typed ORDER for the \`${orderDirectiveId}\` capability — what the provider`,
190
+ `/// The typed Obligation for the \`${obligationDirectiveId}\` capability — what the provider`,
185
191
  `/// RECEIVES. Carries the framework envelope provenance (requestId / requestedBy /`,
186
- `/// requestedAt) ⊕ the tenant request params. Decoded from the incoming Order intent's`,
192
+ `/// requestedAt / scheduledAt / deadlineAt) ⊕ the tenant request params. Decoded from the incoming Obligation intent's`,
187
193
  `/// payload JSON via [${cls}.fromJson]; the handler sees ONLY this typed value.`,
188
194
  `class ${cls} {`,
189
195
  fieldDecls,
@@ -192,8 +198,8 @@ function emitOrderClass(
192
198
  ctorParams,
193
199
  ` });`,
194
200
  ``,
195
- ` /// Decode an Order from the already-\`jsonDecode\`d payload map (the EXACT shape the`,
196
- ` /// peer's \`${orderDirectiveId}\` \`toPayloadJson()\` produced). Reverse of the peer`,
201
+ ` /// Decode an Obligation from the already-\`jsonDecode\`d payload map (the EXACT shape the`,
202
+ ` /// peer's \`${obligationDirectiveId}\` \`toPayloadJson()\` produced). Reverse of the peer`,
197
203
  ` /// encode — types stop here, at the JSON serde.`,
198
204
  ` factory ${cls}.fromJson(Map<String, Object?> j) => ${cls}(`,
199
205
  fromJsonAssigns,
@@ -224,7 +230,7 @@ function emitResultClasses(base: string, completeDirectiveId: string): string {
224
230
  ``,
225
231
  `/// The typed FAILURE the \`${base}\` handler returns — the recorded reason the side-effect`,
226
232
  `/// failed. Maps to the framework \`fail…\` Receipt (status:failed + failureReason +`,
227
- `/// closesOrder); a recorded failure is terminal, NOT a retry storm.`,
233
+ `/// closesObligation); a recorded failure is terminal, NOT a retry storm.`,
228
234
  `class ${failureCls} {`,
229
235
  ` final String failureReason;`,
230
236
  ` const ${failureCls}({required this.failureReason});`,
@@ -237,7 +243,7 @@ function emitProviderClass(
237
243
  task: ImpureCapabilityDecl,
238
244
  base: string,
239
245
  ): string {
240
- const orderCls = `${base}Order`;
246
+ const obligationCls = `${base}Obligation`;
241
247
  const resultCls = `${base}Result`;
242
248
  const handlerTypedef = `${base}Handler`;
243
249
  const providerCls = `${base}Provider`;
@@ -247,25 +253,25 @@ function emitProviderClass(
247
253
  const deadLetterCls = deadLetterPayloadClass(task.deadLetter.id);
248
254
  return [
249
255
  `/// The typed HANDLER the dev writes for the \`${base}\` capability: given the typed`,
250
- `/// [${orderCls}], perform the side-effect and return a typed [TaskOutcome] — a`,
256
+ `/// [${obligationCls}], perform the side-effect and return a typed [TaskOutcome] — a`,
251
257
  `/// [TaskSuccess]<[${resultCls}]>, [TaskFailure], [TaskBlocked], or [TaskDeadLetter]. NO raw JSON, NO directive id, NO`,
252
258
  `/// Receipt construction: the dev writes pure typed business logic; the [${providerCls}]`,
253
259
  `/// does the decode/encode round-trip.`,
254
- `typedef ${handlerTypedef} = TaskOutcome<${resultCls}> Function(${orderCls} order);`,
260
+ `typedef ${handlerTypedef} = TaskOutcome<${resultCls}> Function(${obligationCls} obligation);`,
255
261
  ``,
256
262
  `/// The GENERATED dispatcher for the \`${base}\` capability — the symmetric twin of the`,
257
- `/// peer's typed dispatch. Register a typed [${handlerTypedef}]; [handleOrder] DECODES an`,
258
- `/// incoming Order intent JSON into [${orderCls}], runs the handler, and ENCODES the typed`,
263
+ `/// peer's typed dispatch. Register a typed [${handlerTypedef}]; [handleObligation] DECODES an`,
264
+ `/// incoming Obligation intent JSON into [${obligationCls}], runs the handler, and ENCODES the typed`,
259
265
  `/// outcome into the ordinary RECEIPT intent: [${completeCls}], [${failCls}],`,
260
266
  `/// [${blockCls}], or [${deadLetterCls}]. The returned [NomosIntent] ships through the SAME`,
261
267
  `/// \`dispatch\` gate as any authored write (ONE-WRITE-PATH).`,
262
- `class ${providerCls} implements TaskProvider<${orderCls}, ${resultCls}> {`,
268
+ `class ${providerCls} implements TaskProvider<${obligationCls}, ${resultCls}> {`,
263
269
  ` /// The dev's typed handler.`,
264
270
  ` final ${handlerTypedef} handler;`,
265
271
  ` const ${providerCls}(this.handler);`,
266
272
  ``,
267
273
  ` @override`,
268
- ` String get orderDirectiveId => '${task.order.id}';`,
274
+ ` String get obligationDirectiveId => '${task.obligation.id}';`,
269
275
  ` @override`,
270
276
  ` String get completeDirectiveId => '${task.complete.id}';`,
271
277
  ` @override`,
@@ -276,39 +282,39 @@ function emitProviderClass(
276
282
  ` String get deadLetterDirectiveId => '${task.deadLetter.id}';`,
277
283
  ``,
278
284
  ` @override`,
279
- ` NomosIntent handleOrder(String orderPayloadJson, {required HostClock now}) =>`,
280
- ` handleOrderMap(decodeOrderPayload(orderPayloadJson), now: now);`,
285
+ ` NomosIntent handleObligation(String obligationPayloadJson, {required HostClock now}) =>`,
286
+ ` handleObligationMap(decodeObligationPayload(obligationPayloadJson), now: now);`,
281
287
  ``,
282
288
  ` @override`,
283
- ` NomosIntent handleOrderMap(`,
284
- ` Map<String, Object?> orderPayload, {`,
289
+ ` NomosIntent handleObligationMap(`,
290
+ ` Map<String, Object?> obligationPayload, {`,
285
291
  ` required HostClock now,`,
286
292
  ` }) {`,
287
- ` final order = ${orderCls}.fromJson(orderPayload);`,
288
- ` final outcome = handler(order);`,
293
+ ` final obligation = ${obligationCls}.fromJson(obligationPayload);`,
294
+ ` final outcome = handler(obligation);`,
289
295
  ` // ENCODE the outcome into the ordinary RECEIPT intent (reusing the peer-codegen Receipt`,
290
296
  ` // payload classes — same generated file). The Receipt addresses the SAME instance`,
291
- ` // the Order created (keyed by requestId), so closesOrder == requestId by`,
297
+ ` // the Obligation created (keyed by requestId), so closesObligation == requestId by`,
292
298
  ` // construction in the engine's \`.plan()\` — terminal-once is the kernel's gate.`,
293
299
  ` return switch (outcome) {`,
294
300
  ` TaskSuccess<${resultCls}>(:final result) => ${completeCls}(`,
295
- ` requestId: order.requestId,`,
301
+ ` requestId: obligation.requestId,`,
296
302
  ` resultRef: result.toResultRef(),`,
297
303
  ` completedAt: now(),`,
298
304
  ` ),`,
299
305
  ` TaskFailure<${resultCls}>(:final failureReason) => ${failCls}(`,
300
- ` requestId: order.requestId,`,
306
+ ` requestId: obligation.requestId,`,
301
307
  ` failureReason: failureReason,`,
302
308
  ` failedAt: now(),`,
303
309
  ` ),`,
304
310
  ` TaskBlocked<${resultCls}>(:final blockedReason, :final nextAction) => ${blockCls}(`,
305
- ` requestId: order.requestId,`,
311
+ ` requestId: obligation.requestId,`,
306
312
  ` blockedReason: blockedReason,`,
307
313
  ` blockedAt: now(),`,
308
314
  ` nextAction: nextAction,`,
309
315
  ` ),`,
310
316
  ` TaskDeadLetter<${resultCls}>(:final deadLetterReason) => ${deadLetterCls}(`,
311
- ` requestId: order.requestId,`,
317
+ ` requestId: obligation.requestId,`,
312
318
  ` deadLetterReason: deadLetterReason,`,
313
319
  ` deadLetteredAt: now(),`,
314
320
  ` ),`,
@@ -319,20 +325,20 @@ function emitProviderClass(
319
325
  }
320
326
 
321
327
  /**
322
- * Emit the full provider SDK for ONE `impureCapability`: the typed Order/Result/Failure
328
+ * Emit the full provider SDK for ONE `impureCapability`: the typed Obligation/Result/Failure
323
329
  * classes + the handler typedef + the dispatcher. `enumDartTypes` is the SAME map the
324
- * domain's `collectEnums` produced (so an enum-typed order param references the
330
+ * domain's `collectEnums` produced (so an enum-typed obligation param references the
325
331
  * already-emitted domain enum — NO duplicate enum decl).
326
332
  */
327
333
  export function emitProviderTask(
328
334
  task: ImpureCapabilityDecl,
329
335
  enumDartTypes: Map<string, string>,
330
336
  ): string {
331
- const base = taskBaseName(task.order.id);
332
- const specs = introspectPayload(task.order.payloadSchema, enumDartTypes);
337
+ const base = taskBaseName(task.obligation.id);
338
+ const specs = introspectPayload(task.obligation.payloadSchema, enumDartTypes);
333
339
  return [
334
340
  `// ── provider SDK: ${base} (impureCapability '${task.aggregate.id}') ──`,
335
- emitOrderClass(base, task.order.id, specs),
341
+ emitObligationClass(base, task.obligation.id, specs),
336
342
  ``,
337
343
  emitResultClasses(base, task.complete.id),
338
344
  ``,
package/src/codegen_ts.ts CHANGED
@@ -10,7 +10,7 @@
10
10
  *
11
11
  * Dart apps get generated payload classes / read models / typed query accessors;
12
12
  * until this file, a web app on `@githolon/client` hand-typed directive ids and
13
- * payload shapes against the raw `dispatch()` surface. `generateTsClient(modules,
13
+ * payload shapes against the raw `offer()` surface. `generateTsClient(modules,
14
14
  * opts)` closes that: ONE generated `.ts` file per package, emitted by
15
15
  * `nomos-compile` AFTER the package is hashed (the deployed law's content hash is
16
16
  * baked in as a constant), carrying:
@@ -20,13 +20,13 @@
20
20
  * becomes `?`, `.int()` stays `number`), so a payload the type system accepts is
21
21
  * a payload the engine accepts; required string fields named `…At` are
22
22
  * CALLER-STAMPED TIMESTAMPS — optional in the type, auto-filled with ISO now()
23
- * at dispatch (see {@link autoStampFields});
23
+ * at offer (see {@link autoStampFields});
24
24
  * * a READ-MODEL interface per aggregate — derived from the DSL `Field` kinds the
25
25
  * projection decodes by (set→`T[]`, map→`Record<string, V>` via `mapValueKind`,
26
26
  * `t.jsonObject()`→`Record<string, unknown>`, plain `t.json()`→`unknown`), with
27
27
  * derived/combined read fields merged in (typed off their declared zod returns).
28
28
  * EVERY field is `?`: a projection row carries only what has folded so far;
29
- * * a CLIENT FACTORY per domain — typed `dispatch` wrappers bound to the baked
29
+ * * a CLIENT FACTORY per domain — typed `offer` wrappers bound to the baked
30
30
  * domainHash, typed declared-query accessors, and by-id read/watch helpers —
31
31
  * over a STRUCTURAL `NomosHolon` interface, so the file has ZERO imports and
32
32
  * binds to `@githolon/client`'s `connect()` result (browser or node) as-is.
@@ -173,7 +173,7 @@ function tsTypeOfZod(zt: z.ZodTypeAny, indent: string): string {
173
173
  /**
174
174
  * Top-level REQUIRED string payload fields named `…At` (`signedAt`, `updatedAt`,
175
175
  * `stampedAt`, …) are CALLER-STAMPED TIMESTAMPS by convention: the generated client
176
- * fills an omitted one with `new Date().toISOString()` at dispatch. The stamp rides
176
+ * fills an omitted one with `new Date().toISOString()` at offer. The stamp rides
177
177
  * IN the payload — the plan stays a pure function of `(payload, ports)` and replays
178
178
  * byte-identical on every peer — so the domain dev stops thinking about clocks.
179
179
  * Passing the field explicitly always wins (scheduling-shaped fields like
@@ -210,11 +210,11 @@ function tsTypeOfPayload(
210
210
  f = zodDef(f).innerType!;
211
211
  }
212
212
  const note = stamped.has(name)
213
- ? " // auto-stamped with ISO now() at dispatch when omitted"
213
+ ? " // auto-stamped with ISO now() at offer when omitted"
214
214
  : mintedSet.has(name)
215
- ? " // kernel-minted at dispatch when omitted (the front-door mint; returned on the result)"
215
+ ? " // kernel-minted at offer when omitted (the front-door mint; returned on the result)"
216
216
  : placedSet.has(name)
217
- ? " // picked LEAST-LOADED over the coordinator's law-state at dispatch when omitted (sharding §3)"
217
+ ? " // picked LEAST-LOADED over the coordinator's law-state at offer when omitted (sharding §3)"
218
218
  : "";
219
219
  return ` ${name}${optional ? "?" : ""}: ${tsTypeOfZod(f, " ")};${note}`;
220
220
  });
@@ -287,7 +287,7 @@ function aggregatesOf(mod: Record<string, unknown>): AggregateHandle[] {
287
287
  export interface TsClientOptions {
288
288
  /** The package name (`nomos.package.mjs` `name`) — used for the hash constant. */
289
289
  readonly packageName: string;
290
- /** sha256 of the built `.package.usda` — the law this client dispatches under. */
290
+ /** sha256 of the built `.package.usda` — the law this client offers under. */
291
291
  readonly domainHash: string;
292
292
  }
293
293
 
@@ -319,7 +319,7 @@ export function generateTsClient(modules: readonly DomainModule[], opts: TsClien
319
319
  `// const holon = await connect({ cloud, workspace, clientId });`,
320
320
  `// const app = ${tsClientFactoryName(modules[0]?.domain ?? "domain")}(holon);`,
321
321
  ``,
322
- `/** sha256 of the deployed \`.package.usda\` — the installed law this client dispatches under. */`,
322
+ `/** sha256 of the deployed \`.package.usda\` — the installed law this client offers under. */`,
323
323
  `export const ${hashConst} = ${JSON.stringify(opts.domainHash)};`,
324
324
  ``,
325
325
  `/** One projection row: the aggregate's wire type, its id, and the folded fields. */`,
@@ -327,7 +327,7 @@ export function generateTsClient(modules: readonly DomainModule[], opts: TsClien
327
327
  ``,
328
328
  `/** The holon surface this client binds to (structurally satisfied by @githolon/client). */`,
329
329
  `export interface NomosHolon {`,
330
- ` dispatch(domain: string, directiveId: string, payload: unknown, opts: { domainHash: string }): Promise<string>;`,
330
+ ` offer(intentBytes: string | Uint8Array): Promise<string>;`,
331
331
  ` query(queryId: string, params?: Record<string, unknown>): Promise<unknown>;`,
332
332
  ` queryById(aggregateId: string): Promise<unknown>;`,
333
333
  ` watchById(aggregateId: string, cb: (rows: unknown) => void, opts?: { intervalMs?: number }): () => void;`,
@@ -347,6 +347,9 @@ export function generateTsClient(modules: readonly DomainModule[], opts: TsClien
347
347
  ` discardDeadLetter(id: string): Promise<{ ok: boolean; removed: number }>;`,
348
348
  `}`,
349
349
  ``,
350
+ `const encodeIntentOffer = (domain: string, directiveId: string, payload: unknown, domainHash: string): Uint8Array =>`,
351
+ ` new TextEncoder().encode(JSON.stringify({ v: 2, intent: { domain, directiveId, payload, domainHash } }));`,
352
+ ``,
350
353
  );
351
354
 
352
355
  const anyAutoStamp = modules.some((mod) =>
@@ -553,7 +556,7 @@ export function generateTsClient(modules: readonly DomainModule[], opts: TsClien
553
556
 
554
557
  const mintPlan = mintPlans.get(mod);
555
558
  // PLACEMENT directives (packed taxonomy births): their `shard` is AUTO-PICKED
556
- // least-loaded by the routed holon at dispatch — optional on the typed surface
559
+ // least-loaded by the routed holon at offer — optional on the typed surface
557
560
  // (the §7 transparency contract: a dev never names a shard).
558
561
  const placementDirs = new Set<string>();
559
562
  if (mod.workspaceTypes !== undefined && mod.workspaceTypes.length > 0) {
@@ -573,7 +576,7 @@ export function generateTsClient(modules: readonly DomainModule[], opts: TsClien
573
576
  const mint = mintPlan?.get(d.id);
574
577
  const placed = placementDirs.has(d.id) ? ["shard"] : [];
575
578
  out.push(
576
- `/** \`${domain}/${d.id}\` payload (mirrors the engine's zod schema).${stamps.length ? ` Omitted ${stamps.map((s) => `\`${s}\``).join("/")} auto-stamp(s) ISO now() at dispatch.` : ""}${mint ? ` Omitted \`${mint.field}\` is KERNEL-MINTED at dispatch (returned on the result).` : ""}${placed.length ? " Omitted `shard` is picked LEAST-LOADED at dispatch (the routed holon)." : ""} */`,
579
+ `/** \`${domain}/${d.id}\` payload (mirrors the engine's zod schema).${stamps.length ? ` Omitted ${stamps.map((s) => `\`${s}\``).join("/")} auto-stamp(s) ISO now() at offer.` : ""}${mint ? ` Omitted \`${mint.field}\` is KERNEL-MINTED at offer (returned on the result).` : ""}${placed.length ? " Omitted `shard` is picked LEAST-LOADED at offer (the routed holon)." : ""} */`,
577
580
  `export type ${payloadName} = ${tsTypeOfPayload(schema, stamps, mint ? [mint.field] : [], placed)};`,
578
581
  ``,
579
582
  );
@@ -592,7 +595,7 @@ export function generateTsClient(modules: readonly DomainModule[], opts: TsClien
592
595
  ` * — ids stay internal plumbing — and returns it with the new local head. */`,
593
596
  ` ${d.id}: async (payload: ${payloadName}): Promise<{ head: string; ${mint.field}: string }> => {`,
594
597
  ` const ${mint.field} = payload.${mint.field} ?? (await (holon as NomosRoutedHolon).mint(${JSON.stringify(mint.mintType)}${mintOpts}));`,
595
- ` const head = await holon.dispatch(${JSON.stringify(domain)}, ${JSON.stringify(d.id)}, ${body}, { domainHash });`,
598
+ ` const head = await holon.offer(encodeIntentOffer(${JSON.stringify(domain)}, ${JSON.stringify(d.id)}, ${body}, domainHash));`,
596
599
  ` return { head, ${mint.field} };`,
597
600
  ` },`,
598
601
  );
@@ -601,8 +604,8 @@ export function generateTsClient(modules: readonly DomainModule[], opts: TsClien
601
604
  methods.push(
602
605
  ` /** Author \`${domain}/${d.id}\` locally under the installed law; returns the new local head. */`,
603
606
  stamps.length
604
- ? ` ${d.id}: (payload: ${payloadName}) => holon.dispatch(${JSON.stringify(domain)}, ${JSON.stringify(d.id)}, stampNow(payload, ${JSON.stringify(stamps)}), { domainHash }),`
605
- : ` ${d.id}: (payload: ${payloadName}) => holon.dispatch(${JSON.stringify(domain)}, ${JSON.stringify(d.id)}, payload, { domainHash }),`,
607
+ ? ` ${d.id}: (payload: ${payloadName}) => holon.offer(encodeIntentOffer(${JSON.stringify(domain)}, ${JSON.stringify(d.id)}, stampNow(payload, ${JSON.stringify(stamps)}), domainHash)),`
608
+ : ` ${d.id}: (payload: ${payloadName}) => holon.offer(encodeIntentOffer(${JSON.stringify(domain)}, ${JSON.stringify(d.id)}, payload, domainHash)),`,
606
609
  );
607
610
  }
608
611
 
@@ -30,13 +30,10 @@
30
30
  * It emits into `outDir` (default `<config dir>/build`):
31
31
  * * `<name>.package.usda` — the deployable package (engine bundle + USD IR);
32
32
  * its sha256 IS the deploy `domainHash` (`policy:{hash}`).
33
- * * `domain_manifests.json` — the READ manifest (field kinds + query routes).
34
- * * `domain_identity_manifests.json` + `domain_identity_hashes.json` (+ excluded
35
- * sidecar) — the per-domain IDENTITY artifacts (admission gates).
36
- * * `<name>.deploy.json` — `{packageUsda, readManifest, identityManifests,
37
- * identityHashes}`: the EXACT body `POST /v1/workspaces/:ws/domains` accepts as
38
- * `application/json` on Nomos Cloud (package + per-workspace manifest overlay in
39
- * one deploy).
33
+ * * `<name>.deploy.json` — `{packageUsda}`: the EXACT body
34
+ * `POST /v1/workspaces/:ws/domains` accepts as `application/json` on Nomos Cloud.
35
+ * Admission/projection manifests are embedded in the package USDA and are not host
36
+ * sidecars.
40
37
  * * `<name>.proof.mts` — the GENERATED PROOF (codegen_proof.ts): a
41
38
  * runnable, domain-shaped live e2e synthesized from the law's own directives /
42
39
  * queries / counts. Run it with `githolon proof`. Never fatal: an unsampleable
@@ -406,7 +403,7 @@ const DART_RELATIVE_IMPORT = `import 'nomos_dsl.dart';`;
406
403
  const DART_SUPPORT_SRC = [
407
404
  "wire.dart",
408
405
  "driver.dart",
409
- "dispatch.dart",
406
+ "offer.dart",
410
407
  "subscriptions.dart",
411
408
  "provider.dart",
412
409
  // The async WebView bridge runtime (NomosBridge / BridgeTransport) the generated
@@ -519,7 +516,7 @@ async function main(): Promise<void> {
519
516
  capability: string;
520
517
  domain: string;
521
518
  aggregateId: string;
522
- order: string;
519
+ obligation: string;
523
520
  complete: string;
524
521
  fail: string;
525
522
  block: string;
@@ -750,7 +747,7 @@ async function main(): Promise<void> {
750
747
  capability: cap,
751
748
  domain: d.key,
752
749
  aggregateId: c.aggregate.id,
753
- order: c.order.id,
750
+ obligation: c.obligation.id,
754
751
  complete: c.complete.id,
755
752
  fail: c.fail.id,
756
753
  block: c.block.id,
@@ -1252,7 +1249,8 @@ async function main(): Promise<void> {
1252
1249
  relPaths.push(`layers/${base}.usda`);
1253
1250
  }
1254
1251
  }
1255
- const flatJson = JSON.stringify(flattenLayeredUsd(allLayers));
1252
+ const flat = flattenLayeredUsd(allLayers);
1253
+ const flatJson = JSON.stringify(flat);
1256
1254
  if (flatJson !== usdJson) {
1257
1255
  fail(
1258
1256
  `layered emission does not flatten to the canonical composed IR — the module fold and ` +
@@ -1264,11 +1262,13 @@ async function main(): Promise<void> {
1264
1262
  }
1265
1263
  for (const w of pendingWrites) writeFileSync(w.path, w.text, "utf8");
1266
1264
  layeredRootPath = path.join(outDir, `${cfg.name}.layered.usda`);
1267
- writeFileSync(layeredRootPath, layeredRootUsda(relPaths), "utf8");
1265
+ writeFileSync(layeredRootPath, layeredRootUsda(relPaths, flat), "utf8");
1268
1266
  layeredFileCount = relPaths.length;
1269
1267
  }
1270
1268
 
1271
1269
  // ── 3. the READ manifest + IDENTITY artifacts ──
1270
+ // These are generated from the same source functions embedded into USDA by emitUsd().
1271
+ // They remain local diagnostics for now; the deploy/cloud/runtime contract ignores them.
1272
1272
  const readManifest = buildReadManifest(domainModules);
1273
1273
  const readPath = path.join(outDir, "domain_manifests.json");
1274
1274
  writeFileSync(readPath, JSON.stringify(readManifest, null, 2) + "\n", "utf8");
@@ -1276,7 +1276,7 @@ async function main(): Promise<void> {
1276
1276
  const identity = buildIdentity(domainModules);
1277
1277
  const { manifestsPath } = writeIdentity(identity, outDir);
1278
1278
 
1279
- // ── 4. the one-call deploy body (package + per-workspace manifest overlay) ──
1279
+ // ── 4. the one-call deploy body (package only) ──
1280
1280
  // SELF-DESCRIBING: `summary` states what the (otherwise hex-opaque) package
1281
1281
  // contains — purely derived from the inputs (no timestamps), ignored by the
1282
1282
  // deploy endpoint, and mirrored as build/<name>.summary.txt for a human or
@@ -1297,9 +1297,6 @@ async function main(): Promise<void> {
1297
1297
  const deployBody = {
1298
1298
  summary,
1299
1299
  packageUsda: text,
1300
- readManifest,
1301
- identityManifests: identity.manifests,
1302
- identityHashes: identity.hashes,
1303
1300
  // The capability lowering (unhashed sidecar; omit-when-empty — see the
1304
1301
  // collector's doc above). The worker stages it on the manifest overlay:
1305
1302
  // the deploy-time binding warning + the executor's scan map read it there.