@githolon/dsl 0.117.1 → 0.119.9

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.
Files changed (35) hide show
  1. package/assets/operation-contract.wasm +0 -0
  2. package/compile_proof.mjs +16 -0
  3. package/dart/CHANGELOG.md +41 -0
  4. package/dart/lib/src/application_status.dart +58 -2
  5. package/dart/lib/src/peer_application_streams.dart +7 -1
  6. package/dart/lib/src/reception/dart_reception.dart +92 -12
  7. package/dart/lib/src/reception/reception_session.dart +10 -2
  8. package/dart/lib/src/replica_readiness.dart +200 -50
  9. package/dart/lib/src/runner/node-entry.mjs +2480 -1021
  10. package/dart/lib/src/sync_status_projection.dart +346 -4
  11. package/dart/lib/src/sync_status_reducer.dart +44 -0
  12. package/dart/lib/src/test_harness.dart +1 -0
  13. package/dart/lib/src/typed_watch_registry.dart +67 -7
  14. package/dart/pubspec.lock +1 -1
  15. package/dart/pubspec.yaml +2 -2
  16. package/dart/test/copy_repair_status_test.dart +238 -0
  17. package/dart/test/reads_never_wait_test.dart +387 -0
  18. package/dart/test/replica_readiness_test.dart +223 -109
  19. package/dart/test/sync_status_reducer_test.dart +39 -0
  20. package/dart/test/typed_watch_registry_test.dart +69 -0
  21. package/dart/test/unshared_work_status_test.dart +269 -0
  22. package/dart_types/CHANGELOG.md +18 -0
  23. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pb.dart +320 -85
  24. package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pb.dart +57 -1
  25. package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pb.dart +14 -0
  26. package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pbenum.dart +25 -1
  27. package/dart_types/lib/src/runtime_outcome.dart +17 -0
  28. package/dart_types/pubspec.yaml +1 -1
  29. package/package.json +5 -2
  30. package/src/codegen_ts.ts +7 -8
  31. package/src/compile_package_main.ts +11 -4
  32. package/src/reception_unit_artifact.ts +105 -0
  33. package/src/reception_unit_artifact_contract.ts +220 -0
  34. package/src/reception_unit_context.ts +47 -0
  35. package/src/reception_unit_source.ts +205 -0
@@ -8334,6 +8334,7 @@ class LiveFrontier extends $pb.GeneratedMessage {
8334
8334
  $core.String? outcomesDigest,
8335
8335
  $core.Iterable<RetainedRef>? outcomes,
8336
8336
  $core.String? sealed,
8337
+ $core.Iterable<$core.String>? heldTrees,
8337
8338
  }) {
8338
8339
  final result = create();
8339
8340
  if (head != null) result.head = head;
@@ -8344,6 +8345,7 @@ class LiveFrontier extends $pb.GeneratedMessage {
8344
8345
  if (outcomesDigest != null) result.outcomesDigest = outcomesDigest;
8345
8346
  if (outcomes != null) result.outcomes.addAll(outcomes);
8346
8347
  if (sealed != null) result.sealed = sealed;
8348
+ if (heldTrees != null) result.heldTrees.addAll(heldTrees);
8347
8349
  return result;
8348
8350
  }
8349
8351
 
@@ -8370,6 +8372,7 @@ class LiveFrontier extends $pb.GeneratedMessage {
8370
8372
  ..pPM<RetainedRef>(6, _omitFieldNames ? '' : 'outcomes',
8371
8373
  subBuilder: RetainedRef.create)
8372
8374
  ..aOS(7, _omitFieldNames ? '' : 'sealed')
8375
+ ..pPS(8, _omitFieldNames ? '' : 'heldTrees')
8373
8376
  ..hasRequiredFields = false;
8374
8377
 
8375
8378
  @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
@@ -8448,6 +8451,13 @@ class LiveFrontier extends $pb.GeneratedMessage {
8448
8451
  $core.bool hasSealed() => $_has(6);
8449
8452
  @$pb.TagNumber(7)
8450
8453
  void clearSealed() => $_clearField(7);
8454
+
8455
+ /// Law-closure trees this copy already holds in some workspace on its own peer (CAP-901), at most
8456
+ /// 16. The peer may leave every object reachable from a named tree out of a checkpoint it offers
8457
+ /// (LiveCheckpointOffer.omitted_trees); the copy supplies them from its own custody before it verifies
8458
+ /// the checkpoint, or asks again naming none. A copy that names none gets the whole checkpoint.
8459
+ @$pb.TagNumber(8)
8460
+ $pb.PbList<$core.String> get heldTrees => $_getList(7);
8451
8461
  }
8452
8462
 
8453
8463
  /// Start or resume a checkpoint. A resume names the checkpoint (`head`, `pack_id`) and the bytes it
@@ -8460,12 +8470,14 @@ class LiveCheckpointRequest extends $pb.GeneratedMessage {
8460
8470
  $core.String? packId,
8461
8471
  $fixnum.Int64? offset,
8462
8472
  $core.int? windowChunks,
8473
+ $core.Iterable<$core.String>? heldTrees,
8463
8474
  }) {
8464
8475
  final result = create();
8465
8476
  if (head != null) result.head = head;
8466
8477
  if (packId != null) result.packId = packId;
8467
8478
  if (offset != null) result.offset = offset;
8468
8479
  if (windowChunks != null) result.windowChunks = windowChunks;
8480
+ if (heldTrees != null) result.heldTrees.addAll(heldTrees);
8469
8481
  return result;
8470
8482
  }
8471
8483
 
@@ -8489,6 +8501,7 @@ class LiveCheckpointRequest extends $pb.GeneratedMessage {
8489
8501
  defaultOrMaker: $fixnum.Int64.ZERO)
8490
8502
  ..aI(4, _omitFieldNames ? '' : 'windowChunks',
8491
8503
  fieldType: $pb.PbFieldType.OU3)
8504
+ ..pPS(5, _omitFieldNames ? '' : 'heldTrees')
8492
8505
  ..hasRequiredFields = false;
8493
8506
 
8494
8507
  @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
@@ -8546,6 +8559,11 @@ class LiveCheckpointRequest extends $pb.GeneratedMessage {
8546
8559
  $core.bool hasWindowChunks() => $_has(3);
8547
8560
  @$pb.TagNumber(4)
8548
8561
  void clearWindowChunks() => $_clearField(4);
8562
+
8563
+ /// As in LiveFrontier: trees the copy holds and the checkpoint may leave out. A resume names the same
8564
+ /// trees it named when the checkpoint was offered (they are part of the checkpoint's identity).
8565
+ @$pb.TagNumber(5)
8566
+ $pb.PbList<$core.String> get heldTrees => $_getList(4);
8549
8567
  }
8550
8568
 
8551
8569
  /// The checkpoint the peer will send: the frontier state at H (H's commit and state tree, and the
@@ -8564,6 +8582,8 @@ class LiveCheckpointOffer extends $pb.GeneratedMessage {
8564
8582
  $core.String? requestId,
8565
8583
  $core.String? sealed,
8566
8584
  $core.String? main,
8585
+ $core.Iterable<$core.String>? omittedTrees,
8586
+ $core.Iterable<$core.String>? sharedTrees,
8567
8587
  }) {
8568
8588
  final result = create();
8569
8589
  if (head != null) result.head = head;
@@ -8577,6 +8597,8 @@ class LiveCheckpointOffer extends $pb.GeneratedMessage {
8577
8597
  if (requestId != null) result.requestId = requestId;
8578
8598
  if (sealed != null) result.sealed = sealed;
8579
8599
  if (main != null) result.main = main;
8600
+ if (omittedTrees != null) result.omittedTrees.addAll(omittedTrees);
8601
+ if (sharedTrees != null) result.sharedTrees.addAll(sharedTrees);
8580
8602
  return result;
8581
8603
  }
8582
8604
 
@@ -8609,6 +8631,8 @@ class LiveCheckpointOffer extends $pb.GeneratedMessage {
8609
8631
  ..aOS(10, _omitFieldNames ? '' : 'requestId')
8610
8632
  ..aOS(11, _omitFieldNames ? '' : 'sealed')
8611
8633
  ..aOS(12, _omitFieldNames ? '' : 'main')
8634
+ ..pPS(13, _omitFieldNames ? '' : 'omittedTrees')
8635
+ ..pPS(14, _omitFieldNames ? '' : 'sharedTrees')
8612
8636
  ..hasRequiredFields = false;
8613
8637
 
8614
8638
  @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
@@ -8725,6 +8749,16 @@ class LiveCheckpointOffer extends $pb.GeneratedMessage {
8725
8749
  $core.bool hasMain() => $_has(10);
8726
8750
  @$pb.TagNumber(12)
8727
8751
  void clearMain() => $_clearField(12);
8752
+
8753
+ /// The held trees (named by the copy's ask) whose reachable objects this pack leaves out (CAP-901):
8754
+ /// the copy supplies them from its own custody before it verifies the checkpoint.
8755
+ @$pb.TagNumber(13)
8756
+ $pb.PbList<$core.String> get omittedTrees => $_getList(11);
8757
+
8758
+ /// The law-closure trees this checkpoint carries (its `state/law-units` tree): a copy that holds one
8759
+ /// in another workspace may name it in its next ask and receive a checkpoint without it.
8760
+ @$pb.TagNumber(14)
8761
+ $pb.PbList<$core.String> get sharedTrees => $_getList(12);
8728
8762
  }
8729
8763
 
8730
8764
  /// One chunk of a checkpoint, or (with `request_id`) of a history slice.
@@ -9646,6 +9680,7 @@ class PeerStreamReceipt extends $pb.GeneratedMessage {
9646
9680
  $core.String? sealed,
9647
9681
  $core.String? stagedPackFile,
9648
9682
  $core.String? main,
9683
+ $core.Iterable<$core.String>? omittedTrees,
9649
9684
  }) {
9650
9685
  final result = create();
9651
9686
  if (pack != null) result.pack = pack;
@@ -9656,6 +9691,7 @@ class PeerStreamReceipt extends $pb.GeneratedMessage {
9656
9691
  if (sealed != null) result.sealed = sealed;
9657
9692
  if (stagedPackFile != null) result.stagedPackFile = stagedPackFile;
9658
9693
  if (main != null) result.main = main;
9694
+ if (omittedTrees != null) result.omittedTrees.addAll(omittedTrees);
9659
9695
  return result;
9660
9696
  }
9661
9697
 
@@ -9694,6 +9730,7 @@ class PeerStreamReceipt extends $pb.GeneratedMessage {
9694
9730
  ..aOS(6, _omitFieldNames ? '' : 'sealed')
9695
9731
  ..aOS(7, _omitFieldNames ? '' : 'stagedPackFile')
9696
9732
  ..aOS(8, _omitFieldNames ? '' : 'main')
9733
+ ..pPS(9, _omitFieldNames ? '' : 'omittedTrees')
9697
9734
  ..hasRequiredFields = false;
9698
9735
 
9699
9736
  @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
@@ -9801,6 +9838,11 @@ class PeerStreamReceipt extends $pb.GeneratedMessage {
9801
9838
  $core.bool hasMain() => $_has(7);
9802
9839
  @$pb.TagNumber(8)
9803
9840
  void clearMain() => $_clearField(8);
9841
+
9842
+ /// With a checkpoint: the trees its pack leaves out (LiveCheckpointOffer.omitted_trees), which the
9843
+ /// copy supplies from its own custody before the import verifies it.
9844
+ @$pb.TagNumber(9)
9845
+ $pb.PbList<$core.String> get omittedTrees => $_getList(8);
9804
9846
  }
9805
9847
 
9806
9848
  enum PeerSocketEffect_Operation { openUrl, send, close, notSet }
@@ -10348,8 +10390,18 @@ class LiveLinkClosure extends $pb.GeneratedMessage {
10348
10390
  void clearUnansweredBatches() => $_clearField(12);
10349
10391
  }
10350
10392
 
10393
+ /// The peer observed a socket receipt. `rung_workspaces` names each workspace whose upstream rang
10394
+ /// for a main this copy does not hold while the receipt was handled: its follow round is now due, and
10395
+ /// it is news a person is waiting for. A native shell starts that round at once rather than after the
10396
+ /// foreground's quiet window, still behind interactive requests already queued.
10351
10397
  class PeerSocketObserved extends $pb.GeneratedMessage {
10352
- factory PeerSocketObserved() => create();
10398
+ factory PeerSocketObserved({
10399
+ $core.Iterable<$core.String>? rungWorkspaces,
10400
+ }) {
10401
+ final result = create();
10402
+ if (rungWorkspaces != null) result.rungWorkspaces.addAll(rungWorkspaces);
10403
+ return result;
10404
+ }
10353
10405
 
10354
10406
  PeerSocketObserved._();
10355
10407
 
@@ -10365,6 +10417,7 @@ class PeerSocketObserved extends $pb.GeneratedMessage {
10365
10417
  package:
10366
10418
  const $pb.PackageName(_omitMessageNames ? '' : 'nomos.runtime.v2'),
10367
10419
  createEmptyInstance: create)
10420
+ ..pPS(1, _omitFieldNames ? '' : 'rungWorkspaces')
10368
10421
  ..hasRequiredFields = false;
10369
10422
 
10370
10423
  @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
@@ -10385,6 +10438,9 @@ class PeerSocketObserved extends $pb.GeneratedMessage {
10385
10438
  static PeerSocketObserved getDefault() => _defaultInstance ??=
10386
10439
  $pb.GeneratedMessage.$_defaultFor<PeerSocketObserved>(create);
10387
10440
  static PeerSocketObserved? _defaultInstance;
10441
+
10442
+ @$pb.TagNumber(1)
10443
+ $pb.PbList<$core.String> get rungWorkspaces => $_getList(0);
10388
10444
  }
10389
10445
 
10390
10446
  class ExportLiveOffersRequest extends $pb.GeneratedMessage {
@@ -127,10 +127,12 @@ class LocalReadPending extends $pb.GeneratedMessage {
127
127
  factory LocalReadPending({
128
128
  $core.String? workId,
129
129
  $core.int? resumeAfterMillis,
130
+ $core.String? heldReason,
130
131
  }) {
131
132
  final result = create();
132
133
  if (workId != null) result.workId = workId;
133
134
  if (resumeAfterMillis != null) result.resumeAfterMillis = resumeAfterMillis;
135
+ if (heldReason != null) result.heldReason = heldReason;
134
136
  return result;
135
137
  }
136
138
 
@@ -151,6 +153,7 @@ class LocalReadPending extends $pb.GeneratedMessage {
151
153
  ..aOS(1, _omitFieldNames ? '' : 'workId')
152
154
  ..aI(2, _omitFieldNames ? '' : 'resumeAfterMillis',
153
155
  fieldType: $pb.PbFieldType.OU3)
156
+ ..aOS(3, _omitFieldNames ? '' : 'heldReason')
154
157
  ..hasRequiredFields = false;
155
158
 
156
159
  @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
@@ -189,6 +192,17 @@ class LocalReadPending extends $pb.GeneratedMessage {
189
192
  $core.bool hasResumeAfterMillis() => $_has(1);
190
193
  @$pb.TagNumber(2)
191
194
  void clearResumeAfterMillis() => $_clearField(2);
195
+
196
+ /// CAP-900: why the kernel holds this read (ReadIndexPending.last_ready_absent), so the shell's
197
+ /// log names it. Diagnostic text only: shells never branch on it.
198
+ @$pb.TagNumber(3)
199
+ $core.String get heldReason => $_getSZ(2);
200
+ @$pb.TagNumber(3)
201
+ set heldReason($core.String value) => $_setString(2, value);
202
+ @$pb.TagNumber(3)
203
+ $core.bool hasHeldReason() => $_has(2);
204
+ @$pb.TagNumber(3)
205
+ void clearHeldReason() => $_clearField(3);
192
206
  }
193
207
 
194
208
  /// An exact custody frontier advanced through a write entrance which is not
@@ -100,6 +100,28 @@ class RuntimeUnavailableReason extends $pb.ProtobufEnum {
100
100
  RuntimeUnavailableReason._(
101
101
  7, _omitEnumNames ? '' : 'RUNTIME_UNAVAILABLE_REASON_PEER_BUSY');
102
102
 
103
+ /// CAP-891: this peer's only ready read index for the workspace was built under the law its head
104
+ /// has since replaced. The application's decoders belong to the new law, so no rows are served
105
+ /// from it; read maintenance is building the index under the new law. Retryable: render
106
+ /// "updating", never an empty result and never a decode failure (CO2-APP-209).
107
+ static const RuntimeUnavailableReason
108
+ RUNTIME_UNAVAILABLE_REASON_READ_INDEX_REFRESHING_SUCCESSOR_LAW =
109
+ RuntimeUnavailableReason._(
110
+ 8,
111
+ _omitEnumNames
112
+ ? ''
113
+ : 'RUNTIME_UNAVAILABLE_REASON_READ_INDEX_REFRESHING_SUCCESSOR_LAW');
114
+
115
+ /// This workspace has never been on this device: its placement keeps a local copy here and that
116
+ /// copy's first download (Replica Follow) is under way. Answered at once, to a read and to a
117
+ /// write alike; nothing is admitted against an absent base. Retryable, and ONE-TIME per workspace
118
+ /// per device: once the first download lands the copy persists across kills, updates and reboots,
119
+ /// and writes go against it as normal (Jack, 11 Oct 2026). Distinct from LOCAL_REPLICA_NOT_READY,
120
+ /// which also names a copy that is here but unreadable (being refetched or awaiting a drop).
121
+ static const RuntimeUnavailableReason
122
+ RUNTIME_UNAVAILABLE_REASON_FIRST_DOWNLOAD = RuntimeUnavailableReason._(
123
+ 9, _omitEnumNames ? '' : 'RUNTIME_UNAVAILABLE_REASON_FIRST_DOWNLOAD');
124
+
103
125
  static const $core.List<RuntimeUnavailableReason> values =
104
126
  <RuntimeUnavailableReason>[
105
127
  RUNTIME_UNAVAILABLE_REASON_UNSPECIFIED,
@@ -110,10 +132,12 @@ class RuntimeUnavailableReason extends $pb.ProtobufEnum {
110
132
  RUNTIME_UNAVAILABLE_REASON_CREDENTIAL_UNAVAILABLE,
111
133
  RUNTIME_UNAVAILABLE_REASON_WORKSPACE_OPENING,
112
134
  RUNTIME_UNAVAILABLE_REASON_PEER_BUSY,
135
+ RUNTIME_UNAVAILABLE_REASON_READ_INDEX_REFRESHING_SUCCESSOR_LAW,
136
+ RUNTIME_UNAVAILABLE_REASON_FIRST_DOWNLOAD,
113
137
  ];
114
138
 
115
139
  static final $core.List<RuntimeUnavailableReason?> _byValue =
116
- $pb.ProtobufEnum.$_initByValueList(values, 7);
140
+ $pb.ProtobufEnum.$_initByValueList(values, 9);
117
141
  static RuntimeUnavailableReason? valueOf($core.int value) =>
118
142
  value < 0 || value >= _byValue.length ? null : _byValue[value];
119
143
 
@@ -17,6 +17,18 @@ enum NomosUnavailableReason {
17
17
  localReplicaNotReady,
18
18
  credentialUnavailable,
19
19
  workspaceOpening,
20
+
21
+ /// The workspace's only ready read index was built under a law its head has since replaced,
22
+ /// and it is being rebuilt under the new law (CAP-891). This application decodes the head's
23
+ /// law only (no reception for the predecessor closure, CAP-884), so no rows are served from
24
+ /// it. A wait like [localReplicaNotReady], never a result and never a failure (CAP-900).
25
+ readIndexRefreshing,
26
+
27
+ /// This workspace has never been on this device and its first download is under way. A read
28
+ /// answers empty and a write is refused, at once; nothing is admitted against an absent base.
29
+ /// ONE-TIME per workspace per device: the landed copy persists (CAP-907). Distinct from
30
+ /// [localReplicaNotReady], which also names a copy here that is unreadable (Jack, 11 Oct 2026).
31
+ firstDownload,
20
32
  unknown,
21
33
  }
22
34
 
@@ -57,6 +69,11 @@ final class NomosUnavailable implements Exception {
57
69
  NomosUnavailableReason.credentialUnavailable,
58
70
  RuntimeUnavailableReason.RUNTIME_UNAVAILABLE_REASON_WORKSPACE_OPENING =>
59
71
  NomosUnavailableReason.workspaceOpening,
72
+ RuntimeUnavailableReason
73
+ .RUNTIME_UNAVAILABLE_REASON_READ_INDEX_REFRESHING_SUCCESSOR_LAW =>
74
+ NomosUnavailableReason.readIndexRefreshing,
75
+ RuntimeUnavailableReason.RUNTIME_UNAVAILABLE_REASON_FIRST_DOWNLOAD =>
76
+ NomosUnavailableReason.firstDownload,
60
77
  _ => NomosUnavailableReason.unknown,
61
78
  },
62
79
  workspace: unavailable.workspace,
@@ -9,7 +9,7 @@ description: >-
9
9
  Pure kernel wire/data types for Nomos Dart frontends — no transport, no bridge,
10
10
  no runtime. Typed protobuf and USDA boundary values plus the offer codec,
11
11
  reactive read engine, schema validation, and data-only error/trace types.
12
- version: 0.15.0
12
+ version: 0.15.2
13
13
 
14
14
  environment:
15
15
  sdk: ^3.5.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@githolon/dsl",
3
- "version": "0.117.1",
3
+ "version": "0.119.9",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "Nomos 2 domain-authoring DSL: aggregates + directives in TS, executed and encoded to the Rust kernel's wire shapes.",
@@ -34,6 +34,9 @@
34
34
  "./workspace-sharding": "./src/workspace_sharding.ts",
35
35
  "./reception-artifact": "./src/reception_artifact.ts",
36
36
  "./reception-artifact-contract": "./src/reception_artifact_contract.ts",
37
+ "./reception-unit-artifact": "./src/reception_unit_artifact.ts",
38
+ "./reception-unit-artifact-contract": "./src/reception_unit_artifact_contract.ts",
39
+ "./reception-unit-source": "./src/reception_unit_source.ts",
37
40
  "./build-package": "./src/build_package.ts",
38
41
  "./codegen-dart": "./src/codegen_dart.ts",
39
42
  "./codegen-dot": "./src/codegen_dot.ts",
@@ -79,7 +82,7 @@
79
82
  "test": "vitest run"
80
83
  },
81
84
  "dependencies": {
82
- "@githolon/runtime": "^0.117.1",
85
+ "@githolon/runtime": "^0.119.9",
83
86
  "@bufbuild/protobuf": "^2.13.0",
84
87
  "@bufbuild/protoc-gen-es": "^2.13.0",
85
88
  "esbuild": "0.24.2",
package/src/codegen_ts.ts CHANGED
@@ -4765,6 +4765,7 @@ function generateTsClientBody(
4765
4765
  d as unknown as {
4766
4766
  businessBirth?: {
4767
4767
  aggregateId: string;
4768
+ domain: string;
4768
4769
  publicPayloadSchema: z.ZodTypeAny;
4769
4770
  rootId: string;
4770
4771
  namespace: string;
@@ -4789,17 +4790,15 @@ function generateTsClientBody(
4789
4790
  businessBirth === undefined
4790
4791
  ? undefined
4791
4792
  : (() => {
4792
- const owners = [
4793
- ...new Set(
4794
- aggregateOwners.get(businessBirth.aggregateId) ?? []
4795
- ),
4796
- ];
4793
+ if (typeof businessBirth.domain !== "string" || businessBirth.domain.length === 0) {
4794
+ throw new Error(`codegen: business birth '${domain}/${d.id}' has no declared owning domain`);
4795
+ }
4796
+ const owners = (aggregateOwners.get(businessBirth.aggregateId) ?? [])
4797
+ .filter(owner => owner === businessBirth.domain);
4797
4798
  if (owners.length !== 1) {
4798
4799
  throw new Error(
4799
4800
  `codegen: business birth '${domain}/${d.id}' must target one generated domain owning ` +
4800
- `'${businessBirth.aggregateId}', found ${
4801
- owners.length === 0 ? "none" : owners.join(", ")
4802
- }`
4801
+ `'${businessBirth.domain}/${businessBirth.aggregateId}', found ${owners.length}`
4803
4802
  );
4804
4803
  }
4805
4804
  return owners[0]!;
@@ -265,6 +265,7 @@ import {
265
265
  import { proofLegsIndex } from "./codegen_proof.js";
266
266
  import { usdzPack } from "./usdz.js";
267
267
  import { compilerReceptionOperations, bundleCompilerReceptionFactory, compressCompilerReceptionTemplate } from "./reception_template.js";
268
+ import { ReceptionUnitContextIndex } from "./reception_unit_context.js";
268
269
  import { embedReceptionTemplate, appendReceptionDomainOpinion, RECEPTION_CLOSURE_TOKEN } from "./reception_artifact_contract.js";
269
270
  import {
270
271
  LAW_UNIT_MANIFEST,
@@ -2586,12 +2587,12 @@ const dartTypesImportFor = (generatedSource: string): string => {
2586
2587
  * just the floor generated code needs — a lagging pin forces tenants into dependency_overrides. */
2587
2588
  // 0.3.0: SurfacedValue<T>/SurfacedObservation/PendingConflictRow — generated Surfaced-field typing +
2588
2589
  // the pendingConflicts read rows.
2589
- const NOMOS_TYPES_DEP = "^0.15.0";
2590
+ const NOMOS_TYPES_DEP = "^0.15.2";
2590
2591
  // 0.48.x: session-bound clients (XClient.bind/NomosClientInterface/DomainClientSessionMismatch/
2591
2592
  // DomainNotInstalledForSession, engine.follow, pendingConflicts lanes) — the generated client
2592
2593
  // REQUIRES this surface. 0.48.1 fixes NomosSession.watchPendingConflicts delegation.
2593
- const NOMOS_CLIENT_DEP = "^0.70.4";
2594
- const NOMOS_FLUTTER_DEP = "^0.68.6";
2594
+ const NOMOS_CLIENT_DEP = "^0.70.10";
2595
+ const NOMOS_FLUTTER_DEP = "^0.69.0";
2595
2596
  // DRIFT TRIPWIRE: pubspec_pins.test.ts asserts these equal the CURRENT versions in the Dart and Flutter
2596
2597
  // package pubspecs — a release without this bump fails the suite, so tenants never
2597
2598
  // land back in dependency_overrides (the exact co2 report this closes, third occurrence).
@@ -7893,6 +7894,11 @@ async function main(): Promise<void> {
7893
7894
  // Complete compiler-produced Reception metadata belongs to the domain unit.
7894
7895
  // The realised closure is not known until the target kernel composes its installed
7895
7896
  // set; its token is bound only by the server generator, outside law identity.
7897
+ const receptionUnitContexts = new ReceptionUnitContextIndex(domainModules.map(module => {
7898
+ const graph = typedLawGraphs.get(module.name);
7899
+ if (!graph) fail(`Reception unit context has no graph for '${module.name}'`);
7900
+ return { module, graph };
7901
+ }), lawDependencies, v3LawPathByNode);
7896
7902
  for (const module of presentationDomainModules) {
7897
7903
  const graph = typedLawGraphs.get(module.name);
7898
7904
  const paths = typedLawPathsByName.get(module.name);
@@ -7912,7 +7918,8 @@ async function main(): Promise<void> {
7912
7918
  const binding = readFileSync(path.join(scratch, "operation_payloads_pb.ts"), "utf8");
7913
7919
  const source = generateTsClient([module], {
7914
7920
  packageName: module.name, domainHash: RECEPTION_CLOSURE_TOKEN,
7915
- interface: { [domain]: packageInterface[domain]! }, kernelContracts,
7921
+ interface: { [domain]: packageInterface[domain]! },
7922
+ kernelContracts: receptionUnitContexts.forGraph(graph).kernelContracts,
7916
7923
  homeDomain: domain, exportHolonFactory: true, callUsda: [{ graph, paths }],
7917
7924
  factoryOnly: { composition: presentationDomainModules },
7918
7925
  });
@@ -0,0 +1,105 @@
1
+ /** Pure additive prototype; no production activation, source transformation or law evaluation.
2
+ * Unit associations must be authenticated upstream. A cached module receives closure context
3
+ * only through a fresh host-verified binding; the Office still judges the requested operation. */
4
+ import { RECEPTION_CLOSURE_TOKEN, RECEPTION_SOURCE_BYTES_LIMIT } from "./reception_artifact_contract.js";
5
+ import {
6
+ assertUnitReceptionRecord, snapshotUnitReceptionValue, canonicalReceptionOperations, closureReceptionOperations, closureReceptionManifestIdentity,
7
+ receptionSha256, unitReceptionArtifactIdentity, unitReceptionUnavailable, validateUnitReceptionIdentity,
8
+ verifyClosureReceptionManifest, verifyUnitReceptionArtifact, verifyUnitReceptionManifest,
9
+ UNIT_RECEPTION_ARTIFACT_FORMAT, CLOSURE_RECEPTION_MANIFEST_FORMAT,
10
+ type UnitReceptionInput, type UnitReceptionArtifact, type UnitReceptionManifest,
11
+ type ClosureReceptionManifestInput, type ClosureReceptionManifest,
12
+ } from "./reception_unit_artifact_contract.js";
13
+ export * from "./reception_unit_artifact_contract.js";
14
+
15
+ export async function generateUnitReceptionArtifact(input: UnitReceptionInput): Promise<UnitReceptionArtifact> {
16
+ input = snapshotUnitReceptionValue(input);
17
+ assertUnitReceptionRecord(input, ["unitKey", "unitHash", "domain", "generator", "runtimeContract", "template"]);
18
+ validateUnitReceptionIdentity(input);
19
+ const template = input.template;
20
+ if (!template || template.domain !== input.domain || !/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(template.factory)) unitReceptionUnavailable("unit Reception template domain/factory mismatch");
21
+ let bytes = 0;
22
+ for (const [source, hash] of [[template.factorySource, template.factorySha256], [template.clientSource, template.sourceSha256],
23
+ [template.operationBinding, template.bindingSha256], [template.operationProto, template.protoSha256]]) {
24
+ if (typeof source !== "string" || !source) unitReceptionUnavailable("incomplete unit Reception template");
25
+ if (source.length > RECEPTION_SOURCE_BYTES_LIMIT) unitReceptionUnavailable("unit Reception source budget exceeded");
26
+ bytes += new TextEncoder().encode(source).byteLength;
27
+ if (bytes > RECEPTION_SOURCE_BYTES_LIMIT) unitReceptionUnavailable("unit Reception source budget exceeded");
28
+ if (typeof hash !== "string" || !/^[0-9a-f]{64}$/.test(hash) || await receptionSha256(source) !== hash) unitReceptionUnavailable("unit Reception template digest mismatch");
29
+ }
30
+ const literal = JSON.stringify(RECEPTION_CLOSURE_TOKEN);
31
+ if (!template.factorySource.includes(literal) || !template.clientSource.includes(literal)) unitReceptionUnavailable("unit Reception template has no closure binding");
32
+ const operations = canonicalReceptionOperations(template.operations);
33
+ const description = { unitKey: input.unitKey, unitHash: input.unitHash, domain: input.domain,
34
+ generator: input.generator, runtimeContract: input.runtimeContract, factory: template.factory, operations };
35
+ // This is a lexical expression substituted once into compiler-produced code, not an eval or
36
+ // per-closure module rewrite. Each call gets a separate factory and closure variable.
37
+ const source = [
38
+ 'import { WorkerEntrypoint } from "cloudflare:workers";',
39
+ `const description = ${JSON.stringify(description)};`,
40
+ `description.operations = Object.freeze(description.operations.map(operation => Object.freeze(operation))); Object.freeze(description);`,
41
+ `const operations = Object.freeze(Object.fromEntries(description.operations.map(operation => [operation.member, Object.freeze(operation)])));`,
42
+ `function bindFactory(__nomosBoundClosure) { ${template.factorySource.replaceAll(literal, "__nomosBoundClosure")} return __NOMOS_RECEPTION_FACET__.${template.factory}; }`,
43
+ `function serialiseResult(value, depth = 0, seen = new Set()) {`,
44
+ ` const unavailable = () => { const error = new Error("Generated result cannot cross the hosted JSON contract"); error.code = "nomos.reception.result.unavailable"; throw error; };`,
45
+ ` if (depth > 64) return unavailable();`,
46
+ ` if (value === undefined) return { $nomos: "absent" };`,
47
+ ` if (value === null || typeof value === "string" || typeof value === "boolean") return value;`,
48
+ ` if (typeof value === "number") { if (!Number.isFinite(value)) return unavailable(); return value; }`,
49
+ ` if (typeof value === "bigint") return { $nomos: "integer", decimal: value.toString() };`,
50
+ ` if (value instanceof Uint8Array) { let binary = ""; for (const byte of value) binary += String.fromCharCode(byte); return { $nomos: "bytes", base64: btoa(binary) }; }`,
51
+ ` if (value instanceof Date) { if (!Number.isFinite(value.getTime())) return unavailable(); return { $nomos: "timestamp", iso: value.toISOString() }; }`,
52
+ ` if (typeof value !== "object" || seen.has(value)) return unavailable();`,
53
+ ` seen.add(value); try {`,
54
+ ` if (Array.isArray(value)) return value.map(item => serialiseResult(item, depth + 1, seen));`,
55
+ ` const prototype = Object.getPrototypeOf(value); if (prototype !== Object.prototype && prototype !== null) return unavailable();`,
56
+ ` const output = {}; for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(value))) { if (descriptor.get || descriptor.set) return unavailable(); Object.defineProperty(output, key, { value: serialiseResult(descriptor.value, depth + 1, seen), enumerable: true }); } return output;`,
57
+ ` } finally { seen.delete(value); }`,
58
+ `}`,
59
+ `export default class UnitReception extends WorkerEntrypoint {`,
60
+ ` describe() { return description; }`,
61
+ ` async invoke(call, office, binding) {`,
62
+ ` if (!call || !binding || call.domain !== description.domain || binding.domain !== description.domain || binding.unitKey !== description.unitKey || binding.unitHash !== description.unitHash || call.member !== binding.member || call.closure !== binding.closure || !/^[0-9a-f]{64}$/.test(binding.closure)) throw new Error("unit Reception invocation binding mismatch");`,
63
+ ` if (typeof call.workspace !== "string" || !call.workspace || typeof call.principal !== "string" || !call.principal || !Array.isArray(call.args)) throw new Error("invalid unit Reception invocation");`,
64
+ ` if (!Object.hasOwn(operations, call.member)) throw new Error("unit Reception member is absent");`,
65
+ ` const selected = operations[call.member]; if (binding.kind !== selected.kind || binding.selector !== selected.selector) throw new Error("unit Reception operation binding mismatch");`,
66
+ ` const scoped = { workspace: call.workspace, principal: call.principal, offerCall: (...args) => office.offerCall(...args),`,
67
+ ` __nomosQuery: (...args) => office.__nomosQuery(...args), __nomosQueryUsda: (...args) => office.__nomosQueryUsda(...args),`,
68
+ ` __nomosQueryByIdUsda: (...args) => office.__nomosQueryByIdUsda(...args), __nomosQueryByTypeUsda: (...args) => office.__nomosQueryByTypeUsda(...args),`,
69
+ ` __nomosQueryPage: (...args) => office.__nomosQueryPage(...args), __nomosQueryPageUsda: (...args) => office.__nomosQueryPageUsda(...args),`,
70
+ ` prepareDomain: (domain, closure) => { if (domain !== description.domain || closure !== binding.closure) throw new Error("unit Reception closure binding escaped its call"); } };`,
71
+ ` const client = bindFactory(binding.closure)(scoped);`,
72
+ ` const path = call.member.split("."); let parent = client;`,
73
+ ` for (const part of path.slice(0, -1)) { if (!part || part.startsWith("__") || ["constructor", "prototype"].includes(part) || !Object.hasOwn(parent, part)) throw new Error("unit Reception business member is absent"); parent = parent[part]; if (!parent || typeof parent !== "object") throw new Error("unit Reception business member is absent"); }`,
74
+ ` const member = path.at(-1); if (!member || member.startsWith("watch") || member.startsWith("__") || ["constructor", "prototype"].includes(member) || !Object.hasOwn(parent, member) || typeof parent[member] !== "function") throw new Error("unit Reception business member is absent");`,
75
+ ` const args = [...call.args];`,
76
+ ` if (selected.kind === "byId" && typeof args[0] === "string" && typeof parent.fromAddress === "function") args[0] = parent.fromAddress(args[0]);`,
77
+ ` return serialiseResult(await parent[member](...args));`,
78
+ ` }`,
79
+ `}`, "",
80
+ ].join("\n");
81
+ const content: Omit<UnitReceptionManifest, "artifactId"> = { format: UNIT_RECEPTION_ARTIFACT_FORMAT,
82
+ unitKey: input.unitKey, unitHash: input.unitHash, domain: input.domain, generator: input.generator,
83
+ runtimeContract: input.runtimeContract, entrypoint: "reception-unit.mjs", moduleSha256: await receptionSha256(source),
84
+ factory: template.factory, factorySha256: template.factorySha256, sourceSha256: template.sourceSha256,
85
+ bindingSha256: template.bindingSha256, protoSha256: template.protoSha256, operations };
86
+ const artifact: UnitReceptionArtifact = { manifest: { ...content, artifactId: await unitReceptionArtifactIdentity(content) }, moduleSource: source };
87
+ await verifyUnitReceptionArtifact(artifact, input);
88
+ return snapshotUnitReceptionValue(artifact);
89
+ }
90
+
91
+ export async function generateClosureReceptionManifest(input: ClosureReceptionManifestInput): Promise<ClosureReceptionManifest> {
92
+ input = snapshotUnitReceptionValue(input);
93
+ assertUnitReceptionRecord(input, ["closure", "generator", "runtimeContract", "units", "domains"]);
94
+ if (!Array.isArray(input.domains) || input.domains.length === 0 || new Set(input.domains).size !== input.domains.length) unitReceptionUnavailable("invalid authoritative Reception domain coverage");
95
+ if (!Array.isArray(input.units) || input.units.length === 0) unitReceptionUnavailable("invalid closure Reception units");
96
+ for (const unit of input.units) await verifyUnitReceptionManifest(unit);
97
+ const units = input.units.map((unit: UnitReceptionManifest) => ({ ...unit, operations: unit.operations.map(operation => ({ ...operation })) }))
98
+ .sort((a, b) => a.unitKey < b.unitKey ? -1 : a.unitKey > b.unitKey ? 1 : 0);
99
+ if (input.domains.length !== units.length || units.some(unit => !input.domains.includes(unit.domain))) unitReceptionUnavailable("closure Reception domain membership is incomplete");
100
+ const content = { format: CLOSURE_RECEPTION_MANIFEST_FORMAT, closure: input.closure,
101
+ generator: input.generator, runtimeContract: input.runtimeContract, units, operations: closureReceptionOperations(units) } as const;
102
+ const manifest = { ...content, manifestId: await closureReceptionManifestIdentity(content) };
103
+ await verifyClosureReceptionManifest(manifest, input);
104
+ return snapshotUnitReceptionValue(manifest);
105
+ }