@githolon/dsl 0.114.0 → 0.115.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.
Files changed (67) hide show
  1. package/assets/operation-contract.wasm +0 -0
  2. package/dart/CHANGELOG.md +40 -0
  3. package/dart/lib/src/application_framework_calls.dart +7 -1
  4. package/dart/lib/src/console_enrolment.dart +7 -0
  5. package/dart/lib/src/console_http_io.dart +22 -15
  6. package/dart/lib/src/dev_cloud.dart +62 -0
  7. package/dart/lib/src/operation_receipts.dart +74 -7
  8. package/dart/lib/src/reception/dart_reception.dart +26 -6
  9. package/dart/lib/src/runner/node-entry.mjs +7129 -4424
  10. package/dart/lib/src/runtime_v2_transport.dart +3 -0
  11. package/dart/pubspec.lock +1 -1
  12. package/dart/pubspec.yaml +2 -2
  13. package/dart/test/console_enrolment_test.dart +47 -0
  14. package/dart/test/model_adoption_test.dart +49 -0
  15. package/dart/test/runtime_projections_test.dart +41 -0
  16. package/dart_types/CHANGELOG.md +24 -0
  17. package/dart_types/lib/src/approved_model.dart +78 -1
  18. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pb.dart +528 -2
  19. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbenum.dart +24 -0
  20. package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pb.dart +1897 -112
  21. package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pb.dart +98 -0
  22. package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pb.dart +16 -0
  23. package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pb.dart +43 -2
  24. package/dart_types/pubspec.yaml +1 -1
  25. package/package.json +4 -2
  26. package/src/aggregate.ts +93 -0
  27. package/src/anonymous_value_interning.ts +205 -0
  28. package/src/baseline.ts +27 -0
  29. package/src/baseline_export.ts +432 -0
  30. package/src/baseline_import.ts +312 -0
  31. package/src/baseline_usda.ts +186 -0
  32. package/src/birth_key_rule.ts +472 -0
  33. package/src/births.ts +207 -24
  34. package/src/build_provenance.ts +10 -0
  35. package/src/business_birth.ts +50 -4
  36. package/src/check_package_main.ts +50 -6
  37. package/src/codegen_call_proto.ts +5 -18
  38. package/src/codegen_dart.ts +95 -3
  39. package/src/codegen_dart_client.ts +52 -6
  40. package/src/codegen_dart_facade.ts +57 -35
  41. package/src/codegen_dart_framework_calls.ts +20 -1
  42. package/src/codegen_proof.ts +5 -2
  43. package/src/codegen_result_proto.ts +8 -6
  44. package/src/codegen_result_usda.ts +4 -0
  45. package/src/codegen_ts.ts +109 -10
  46. package/src/compatibility_graph.ts +41 -4
  47. package/src/compatibility_usda.ts +11 -1
  48. package/src/compile_package_main.ts +214 -51
  49. package/src/composed_wire_identity.ts +24 -0
  50. package/src/directive.ts +6 -0
  51. package/src/domain_requirements.ts +75 -0
  52. package/src/engine_javascript.ts +12 -6
  53. package/src/fields.ts +109 -0
  54. package/src/identity_usda.ts +77 -51
  55. package/src/index.ts +25 -1
  56. package/src/intent.ts +5 -0
  57. package/src/law_graph.ts +142 -1
  58. package/src/law_graph_usda.ts +25 -141
  59. package/src/law_layer_prims.ts +199 -0
  60. package/src/law_unit_selection.ts +45 -0
  61. package/src/law_version.ts +107 -0
  62. package/src/package_config.ts +44 -0
  63. package/src/package_dependencies.ts +8 -0
  64. package/src/value_object.ts +6 -3
  65. package/src/wire.ts +2 -0
  66. package/src/wire_paths.ts +17 -0
  67. package/src/workspace_routing.ts +1 -1
@@ -29,6 +29,7 @@ class AdoptApplicationModelRequest extends $pb.GeneratedMessage {
29
29
  $core.String? acceptanceReason,
30
30
  $core.bool? dryRun,
31
31
  $core.String? unitSet,
32
+ $core.Iterable<DomainUnitRequirement>? domainRequirements,
32
33
  }) {
33
34
  final result = create();
34
35
  if (session != null) result.session = session;
@@ -37,6 +38,8 @@ class AdoptApplicationModelRequest extends $pb.GeneratedMessage {
37
38
  if (acceptanceReason != null) result.acceptanceReason = acceptanceReason;
38
39
  if (dryRun != null) result.dryRun = dryRun;
39
40
  if (unitSet != null) result.unitSet = unitSet;
41
+ if (domainRequirements != null)
42
+ result.domainRequirements.addAll(domainRequirements);
40
43
  return result;
41
44
  }
42
45
 
@@ -60,6 +63,8 @@ class AdoptApplicationModelRequest extends $pb.GeneratedMessage {
60
63
  ..aOS(4, _omitFieldNames ? '' : 'acceptanceReason')
61
64
  ..aOB(5, _omitFieldNames ? '' : 'dryRun')
62
65
  ..aOS(6, _omitFieldNames ? '' : 'unitSet')
66
+ ..pPM<DomainUnitRequirement>(7, _omitFieldNames ? '' : 'domainRequirements',
67
+ subBuilder: DomainUnitRequirement.create)
63
68
  ..hasRequiredFields = false;
64
69
 
65
70
  @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
@@ -148,6 +153,99 @@ class AdoptApplicationModelRequest extends $pb.GeneratedMessage {
148
153
  $core.bool hasUnitSet() => $_has(5);
149
154
  @$pb.TagNumber(6)
150
155
  void clearUnitSet() => $_clearField(6);
156
+
157
+ /// THE PER-DOMAIN REQUIREMENTS (CAP-666): for each application domain key, the
158
+ /// exact law unit and the generated consumer requirement this client was
159
+ /// compiled against. A workspace serving that unit for the key satisfies the
160
+ /// requirement for it even when its unit set carries other domains the
161
+ /// application never touches. Absent on clients generated before this field
162
+ /// existed: the unit set alone then decides.
163
+ @$pb.TagNumber(7)
164
+ $pb.PbList<DomainUnitRequirement> get domainRequirements => $_getList(6);
165
+ }
166
+
167
+ /// One application domain's requirement, as the compiler generated it beside the
168
+ /// unit set: the domain's unit hash (`domain:<key>` in `build/<name>.units`) and
169
+ /// the sha256 of the domain's `<name>.compatibility.usda` slice the kernel's
170
+ /// Compatibility Desk judges. Identities only; the office never reads the bytes.
171
+ class DomainUnitRequirement extends $pb.GeneratedMessage {
172
+ factory DomainUnitRequirement({
173
+ $core.String? domainKey,
174
+ $core.String? unitSha256,
175
+ $core.String? requirementSha256,
176
+ }) {
177
+ final result = create();
178
+ if (domainKey != null) result.domainKey = domainKey;
179
+ if (unitSha256 != null) result.unitSha256 = unitSha256;
180
+ if (requirementSha256 != null) result.requirementSha256 = requirementSha256;
181
+ return result;
182
+ }
183
+
184
+ DomainUnitRequirement._();
185
+
186
+ factory DomainUnitRequirement.fromBuffer($core.List<$core.int> data,
187
+ [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
188
+ create()..mergeFromBuffer(data, registry);
189
+ factory DomainUnitRequirement.fromJson($core.String json,
190
+ [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
191
+ create()..mergeFromJson(json, registry);
192
+
193
+ static final $pb.BuilderInfo _i = $pb.BuilderInfo(
194
+ _omitMessageNames ? '' : 'DomainUnitRequirement',
195
+ package:
196
+ const $pb.PackageName(_omitMessageNames ? '' : 'nomos.runtime.v2'),
197
+ createEmptyInstance: create)
198
+ ..aOS(1, _omitFieldNames ? '' : 'domainKey')
199
+ ..aOS(2, _omitFieldNames ? '' : 'unitSha256')
200
+ ..aOS(3, _omitFieldNames ? '' : 'requirementSha256')
201
+ ..hasRequiredFields = false;
202
+
203
+ @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
204
+ DomainUnitRequirement clone() => deepCopy();
205
+ @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
206
+ DomainUnitRequirement copyWith(
207
+ void Function(DomainUnitRequirement) updates) =>
208
+ super.copyWith((message) => updates(message as DomainUnitRequirement))
209
+ as DomainUnitRequirement;
210
+
211
+ @$core.override
212
+ $pb.BuilderInfo get info_ => _i;
213
+
214
+ @$core.pragma('dart2js:noInline')
215
+ static DomainUnitRequirement create() => DomainUnitRequirement._();
216
+ @$core.override
217
+ DomainUnitRequirement createEmptyInstance() => create();
218
+ @$core.pragma('dart2js:noInline')
219
+ static DomainUnitRequirement getDefault() => _defaultInstance ??=
220
+ $pb.GeneratedMessage.$_defaultFor<DomainUnitRequirement>(create);
221
+ static DomainUnitRequirement? _defaultInstance;
222
+
223
+ @$pb.TagNumber(1)
224
+ $core.String get domainKey => $_getSZ(0);
225
+ @$pb.TagNumber(1)
226
+ set domainKey($core.String value) => $_setString(0, value);
227
+ @$pb.TagNumber(1)
228
+ $core.bool hasDomainKey() => $_has(0);
229
+ @$pb.TagNumber(1)
230
+ void clearDomainKey() => $_clearField(1);
231
+
232
+ @$pb.TagNumber(2)
233
+ $core.String get unitSha256 => $_getSZ(1);
234
+ @$pb.TagNumber(2)
235
+ set unitSha256($core.String value) => $_setString(1, value);
236
+ @$pb.TagNumber(2)
237
+ $core.bool hasUnitSha256() => $_has(1);
238
+ @$pb.TagNumber(2)
239
+ void clearUnitSha256() => $_clearField(2);
240
+
241
+ @$pb.TagNumber(3)
242
+ $core.String get requirementSha256 => $_getSZ(2);
243
+ @$pb.TagNumber(3)
244
+ set requirementSha256($core.String value) => $_setString(2, value);
245
+ @$pb.TagNumber(3)
246
+ $core.bool hasRequirementSha256() => $_has(2);
247
+ @$pb.TagNumber(3)
248
+ void clearRequirementSha256() => $_clearField(3);
151
249
  }
152
250
 
153
251
  class ApplicationModelAdopted extends $pb.GeneratedMessage {
@@ -1237,14 +1237,18 @@ class ResolveWorkspaceNameRequest extends $pb.GeneratedMessage {
1237
1237
  }
1238
1238
 
1239
1239
  /// `birth.keyed({ namespace, key })` — `<namespace>-<sha256(namespace + US + key)[:40]>`.
1240
+ /// A business birth's child is named the same way, fixed at birth (CAP-803 withdrew the CAP-788
1241
+ /// parent-scoped naming before release); one parent per node is the kernel's `[birth-child-already-parented]`.
1240
1242
  class KeyedBirthName extends $pb.GeneratedMessage {
1241
1243
  factory KeyedBirthName({
1242
1244
  $core.String? namespace,
1243
1245
  $core.String? key,
1246
+ $core.String? parent,
1244
1247
  }) {
1245
1248
  final result = create();
1246
1249
  if (namespace != null) result.namespace = namespace;
1247
1250
  if (key != null) result.key = key;
1251
+ if (parent != null) result.parent = parent;
1248
1252
  return result;
1249
1253
  }
1250
1254
 
@@ -1264,6 +1268,7 @@ class KeyedBirthName extends $pb.GeneratedMessage {
1264
1268
  createEmptyInstance: create)
1265
1269
  ..aOS(1, _omitFieldNames ? '' : 'namespace')
1266
1270
  ..aOS(2, _omitFieldNames ? '' : 'key')
1271
+ ..aOS(3, _omitFieldNames ? '' : 'parent')
1267
1272
  ..hasRequiredFields = false;
1268
1273
 
1269
1274
  @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
@@ -1302,6 +1307,17 @@ class KeyedBirthName extends $pb.GeneratedMessage {
1302
1307
  $core.bool hasKey() => $_has(1);
1303
1308
  @$pb.TagNumber(2)
1304
1309
  void clearKey() => $_clearField(2);
1310
+
1311
+ /// UNUSED (reserved, CAP-803): the office ignores it and answers the keyed name above. A child is
1312
+ /// addressed by the name it was born with; a parent never re-derives it.
1313
+ @$pb.TagNumber(3)
1314
+ $core.String get parent => $_getSZ(2);
1315
+ @$pb.TagNumber(3)
1316
+ set parent($core.String value) => $_setString(2, value);
1317
+ @$pb.TagNumber(3)
1318
+ $core.bool hasParent() => $_has(2);
1319
+ @$pb.TagNumber(3)
1320
+ void clearParent() => $_clearField(3);
1305
1321
  }
1306
1322
 
1307
1323
  /// The framework's identity anchor — `nomos-rg-<sha256(principal)[:40]>`.
@@ -85,6 +85,7 @@ enum RuntimeRequest_Command {
85
85
  configureProviderOrigins,
86
86
  fetchWorkspace,
87
87
  provideLawProducts,
88
+ serveLiveStream,
88
89
  notSet
89
90
  }
90
91
 
@@ -145,6 +146,7 @@ class RuntimeRequest extends $pb.GeneratedMessage {
145
146
  $7.ConfigureProviderOriginsRequest? configureProviderOrigins,
146
147
  $2.FetchWorkspaceRequest? fetchWorkspace,
147
148
  $2.ProvideLawProductsRequest? provideLawProducts,
149
+ $3.LiveStreamRequest? serveLiveStream,
148
150
  }) {
149
151
  final result = create();
150
152
  if (protocolVersion != null) result.protocolVersion = protocolVersion;
@@ -223,6 +225,7 @@ class RuntimeRequest extends $pb.GeneratedMessage {
223
225
  if (fetchWorkspace != null) result.fetchWorkspace = fetchWorkspace;
224
226
  if (provideLawProducts != null)
225
227
  result.provideLawProducts = provideLawProducts;
228
+ if (serveLiveStream != null) result.serveLiveStream = serveLiveStream;
226
229
  return result;
227
230
  }
228
231
 
@@ -284,6 +287,7 @@ class RuntimeRequest extends $pb.GeneratedMessage {
284
287
  62: RuntimeRequest_Command.configureProviderOrigins,
285
288
  63: RuntimeRequest_Command.fetchWorkspace,
286
289
  64: RuntimeRequest_Command.provideLawProducts,
290
+ 65: RuntimeRequest_Command.serveLiveStream,
287
291
  0: RuntimeRequest_Command.notSet
288
292
  };
289
293
  static final $pb.BuilderInfo _i = $pb.BuilderInfo(
@@ -338,7 +342,8 @@ class RuntimeRequest extends $pb.GeneratedMessage {
338
342
  61,
339
343
  62,
340
344
  63,
341
- 64
345
+ 64,
346
+ 65
342
347
  ])
343
348
  ..aI(1, _omitFieldNames ? '' : 'protocolVersion',
344
349
  fieldType: $pb.PbFieldType.OU3)
@@ -476,6 +481,8 @@ class RuntimeRequest extends $pb.GeneratedMessage {
476
481
  ..aOM<$2.ProvideLawProductsRequest>(
477
482
  64, _omitFieldNames ? '' : 'provideLawProducts',
478
483
  subBuilder: $2.ProvideLawProductsRequest.create)
484
+ ..aOM<$3.LiveStreamRequest>(65, _omitFieldNames ? '' : 'serveLiveStream',
485
+ subBuilder: $3.LiveStreamRequest.create)
479
486
  ..hasRequiredFields = false;
480
487
 
481
488
  @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
@@ -544,6 +551,7 @@ class RuntimeRequest extends $pb.GeneratedMessage {
544
551
  @$pb.TagNumber(62)
545
552
  @$pb.TagNumber(63)
546
553
  @$pb.TagNumber(64)
554
+ @$pb.TagNumber(65)
547
555
  RuntimeRequest_Command whichCommand() =>
548
556
  _RuntimeRequest_CommandByTag[$_whichOneof(0)]!;
549
557
  @$pb.TagNumber(10)
@@ -593,6 +601,7 @@ class RuntimeRequest extends $pb.GeneratedMessage {
593
601
  @$pb.TagNumber(62)
594
602
  @$pb.TagNumber(63)
595
603
  @$pb.TagNumber(64)
604
+ @$pb.TagNumber(65)
596
605
  void clearCommand() => $_clearField($_whichOneof(0));
597
606
 
598
607
  @$pb.TagNumber(1)
@@ -1196,6 +1205,18 @@ class RuntimeRequest extends $pb.GeneratedMessage {
1196
1205
  void clearProvideLawProducts() => $_clearField(64);
1197
1206
  @$pb.TagNumber(64)
1198
1207
  $2.ProvideLawProductsRequest ensureProvideLawProducts() => $_ensure(50);
1208
+
1209
+ /// A coordinator asks the cloud peer that holds this workspace to serve one stream request.
1210
+ @$pb.TagNumber(65)
1211
+ $3.LiveStreamRequest get serveLiveStream => $_getN(51);
1212
+ @$pb.TagNumber(65)
1213
+ set serveLiveStream($3.LiveStreamRequest value) => $_setField(65, value);
1214
+ @$pb.TagNumber(65)
1215
+ $core.bool hasServeLiveStream() => $_has(51);
1216
+ @$pb.TagNumber(65)
1217
+ void clearServeLiveStream() => $_clearField(65);
1218
+ @$pb.TagNumber(65)
1219
+ $3.LiveStreamRequest ensureServeLiveStream() => $_ensure(51);
1199
1220
  }
1200
1221
 
1201
1222
  class InvocationTarget extends $pb.GeneratedMessage {
@@ -1318,6 +1339,7 @@ enum RuntimeResponse_Outcome {
1318
1339
  providerOriginsConfigured,
1319
1340
  workspaceFetch,
1320
1341
  lawProducts,
1342
+ liveStreamServed,
1321
1343
  notSet
1322
1344
  }
1323
1345
 
@@ -1377,6 +1399,7 @@ class RuntimeResponse extends $pb.GeneratedMessage {
1377
1399
  $7.ProviderOriginsConfigured? providerOriginsConfigured,
1378
1400
  $2.WorkspaceFetch? workspaceFetch,
1379
1401
  $18.ExportLawProductsResponse? lawProducts,
1402
+ $3.LiveStreamAnswer? liveStreamServed,
1380
1403
  }) {
1381
1404
  final result = create();
1382
1405
  if (protocolVersion != null) result.protocolVersion = protocolVersion;
@@ -1457,6 +1480,7 @@ class RuntimeResponse extends $pb.GeneratedMessage {
1457
1480
  result.providerOriginsConfigured = providerOriginsConfigured;
1458
1481
  if (workspaceFetch != null) result.workspaceFetch = workspaceFetch;
1459
1482
  if (lawProducts != null) result.lawProducts = lawProducts;
1483
+ if (liveStreamServed != null) result.liveStreamServed = liveStreamServed;
1460
1484
  return result;
1461
1485
  }
1462
1486
 
@@ -1523,6 +1547,7 @@ class RuntimeResponse extends $pb.GeneratedMessage {
1523
1547
  66: RuntimeResponse_Outcome.providerOriginsConfigured,
1524
1548
  67: RuntimeResponse_Outcome.workspaceFetch,
1525
1549
  68: RuntimeResponse_Outcome.lawProducts,
1550
+ 69: RuntimeResponse_Outcome.liveStreamServed,
1526
1551
  0: RuntimeResponse_Outcome.notSet
1527
1552
  };
1528
1553
  static final $pb.BuilderInfo _i = $pb.BuilderInfo(
@@ -1582,7 +1607,8 @@ class RuntimeResponse extends $pb.GeneratedMessage {
1582
1607
  65,
1583
1608
  66,
1584
1609
  67,
1585
- 68
1610
+ 68,
1611
+ 69
1586
1612
  ])
1587
1613
  ..aI(1, _omitFieldNames ? '' : 'protocolVersion',
1588
1614
  fieldType: $pb.PbFieldType.OU3)
@@ -1719,6 +1745,8 @@ class RuntimeResponse extends $pb.GeneratedMessage {
1719
1745
  ..aOM<$18.ExportLawProductsResponse>(
1720
1746
  68, _omitFieldNames ? '' : 'lawProducts',
1721
1747
  subBuilder: $18.ExportLawProductsResponse.create)
1748
+ ..aOM<$3.LiveStreamAnswer>(69, _omitFieldNames ? '' : 'liveStreamServed',
1749
+ subBuilder: $3.LiveStreamAnswer.create)
1722
1750
  ..hasRequiredFields = false;
1723
1751
 
1724
1752
  @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
@@ -1792,6 +1820,7 @@ class RuntimeResponse extends $pb.GeneratedMessage {
1792
1820
  @$pb.TagNumber(66)
1793
1821
  @$pb.TagNumber(67)
1794
1822
  @$pb.TagNumber(68)
1823
+ @$pb.TagNumber(69)
1795
1824
  RuntimeResponse_Outcome whichOutcome() =>
1796
1825
  _RuntimeResponse_OutcomeByTag[$_whichOneof(0)]!;
1797
1826
  @$pb.TagNumber(10)
@@ -1846,6 +1875,7 @@ class RuntimeResponse extends $pb.GeneratedMessage {
1846
1875
  @$pb.TagNumber(66)
1847
1876
  @$pb.TagNumber(67)
1848
1877
  @$pb.TagNumber(68)
1878
+ @$pb.TagNumber(69)
1849
1879
  void clearOutcome() => $_clearField($_whichOneof(0));
1850
1880
 
1851
1881
  @$pb.TagNumber(1)
@@ -2469,6 +2499,17 @@ class RuntimeResponse extends $pb.GeneratedMessage {
2469
2499
  void clearLawProducts() => $_clearField(68);
2470
2500
  @$pb.TagNumber(68)
2471
2501
  $18.ExportLawProductsResponse ensureLawProducts() => $_ensure(53);
2502
+
2503
+ @$pb.TagNumber(69)
2504
+ $3.LiveStreamAnswer get liveStreamServed => $_getN(54);
2505
+ @$pb.TagNumber(69)
2506
+ set liveStreamServed($3.LiveStreamAnswer value) => $_setField(69, value);
2507
+ @$pb.TagNumber(69)
2508
+ $core.bool hasLiveStreamServed() => $_has(54);
2509
+ @$pb.TagNumber(69)
2510
+ void clearLiveStreamServed() => $_clearField(69);
2511
+ @$pb.TagNumber(69)
2512
+ $3.LiveStreamAnswer ensureLiveStreamServed() => $_ensure(54);
2472
2513
  }
2473
2514
 
2474
2515
  enum CustodyOfferBatchResponse_Outcome { admission, failure, blocked, notSet }
@@ -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.13.0
12
+ version: 0.14.0
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.114.0",
3
+ "version": "0.115.0",
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.",
@@ -27,6 +27,8 @@
27
27
  },
28
28
  "./release-publication": "./src/release_publication.ts",
29
29
  "./release-inspection": "./src/release_inspection.ts",
30
+ "./law-version": "./src/law_version.ts",
31
+ "./baseline": "./src/baseline.ts",
30
32
  "./workspace-type": "./src/workspace_type.ts",
31
33
  "./workspace-routing": "./src/workspace_routing.ts",
32
34
  "./workspace-sharding": "./src/workspace_sharding.ts",
@@ -75,7 +77,7 @@
75
77
  "test": "vitest run"
76
78
  },
77
79
  "dependencies": {
78
- "@githolon/runtime": "^0.114.0",
80
+ "@githolon/runtime": "^0.115.0",
79
81
  "@bufbuild/protobuf": "^2.13.0",
80
82
  "@bufbuild/protoc-gen-es": "^2.13.0",
81
83
  "esbuild": "0.24.2",
package/src/aggregate.ts CHANGED
@@ -42,6 +42,89 @@ export type AggregateInvariantFn = (
42
42
  previous?: Readonly<Record<string, unknown>> | null,
43
43
  ) => AggregateInvariantVerdict;
44
44
 
45
+ /**
46
+ * BIRTH KEYS ARE WRITE-ONCE (CAP-804 / CO2-1687). A field-keyed `birth(Aggregate, { key })` names the born
47
+ * workspace `workspaceNameForKey(namespace, <key value>)` — fixed at birth, forever. So the framework, not
48
+ * the tenant, attaches a write-once rule to that field of the born aggregate: it rides the aggregate's
49
+ * composed invariant (the SAME #41 gate machinery as `.fromBirth()` presence and CO2's hand-written
50
+ * `immutableIdentity`), so it is judged at the kernel on author, on edge admission and on verify.
51
+ *
52
+ * - creation (`previous === null`), or a field never set before → ADMITTED (the birth seeds it);
53
+ * - a write that leaves the value unchanged (a genesis seed re-stating the key) → ADMITTED;
54
+ * - a write that CHANGES the value → REFUSED (`{ refuse }`: no strike, no state change) with the typed
55
+ * code `<Aggregate>.<field>:birth-key-write-once` (see {@link BIRTH_KEY_WRITE_ONCE_CODE}).
56
+ *
57
+ * A caller that supplies no transition context (`previous === undefined`, a snapshot-only legacy oracle)
58
+ * cannot show a change, so the rule holds vacuously there — it only ever judges transitions.
59
+ *
60
+ * ERA: the rule lives in the invariant BODY, which ships in the law's own engine bundle; invariant
61
+ * PRESENCE (the hashed manifest bit) is unchanged, because a birth key is always a `.fromBirth()` field
62
+ * and every `.fromBirth()` aggregate already declares the composed presence invariant. Chains authored
63
+ * under earlier law replay under that law byte-identically; only law compiled with this DSL carries it.
64
+ *
65
+ * VALUE-FREE: the refusal names the field, never a value, and it can only tell a writer whether its
66
+ * write would change the key — which the writer, already inside the workspace, can derive from the
67
+ * workspace's own name. It therefore keeps the presence check's `invariantValueFree` posture.
68
+ */
69
+ export const BIRTH_KEY_WRITE_ONCE_CODE = "birth-key-write-once";
70
+
71
+ /** @internal Where `birth()` registers the write-once birth-key fields of an aggregate. Symbol-keyed so it
72
+ * is copied by the handle builders' spreads (every `.public()`/`.readableBy()`/`.owned()` handle shares
73
+ * ONE set) yet invisible to `Object.keys`/`JSON.stringify` (never hashed, never emitted). */
74
+ export const BIRTH_KEY_FIELDS: unique symbol = Symbol.for("nomos.birthKeyWriteOnceFields");
75
+
76
+ /** True for an aggregate-invariant refusal code produced by the birth-key write-once rule. */
77
+ export function isBirthKeyWriteOnceRefusal(code: string): boolean {
78
+ return /^[^\s]+\.[^\s.:]+:birth-key-write-once(?:\s|$)/.test(code);
79
+ }
80
+
81
+ /** @internal Register `field` of `aggregate` as a write-once birth key (called by `birth()`). */
82
+ export function __declareBirthKeyWriteOnce(aggregate: AggregateHandle, field: string): void {
83
+ const registry = (aggregate as unknown as { [BIRTH_KEY_FIELDS]?: Set<string> })[BIRTH_KEY_FIELDS];
84
+ if (registry === undefined) {
85
+ throw new Error(
86
+ `birth(${aggregate.id}): this aggregate handle cannot carry the framework's write-once birth-key rule ` +
87
+ `(it was not built by aggregate())`,
88
+ );
89
+ }
90
+ registry.add(field);
91
+ }
92
+
93
+ /** The write-once birth-key fields `birth()` registered on this aggregate (empty when none). */
94
+ export function birthKeyWriteOnceFields(aggregate: AggregateHandle): readonly string[] {
95
+ const registry = (aggregate as unknown as { [BIRTH_KEY_FIELDS]?: Set<string> })[BIRTH_KEY_FIELDS];
96
+ return registry === undefined ? [] : [...registry].sort();
97
+ }
98
+
99
+ function sameKeyValue(a: unknown, b: unknown): boolean {
100
+ if (a === b) return true;
101
+ if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
102
+ return JSON.stringify(a) === JSON.stringify(b);
103
+ }
104
+
105
+ /** @internal The write-once judgement over one transition (exported for direct tests). */
106
+ export function judgeBirthKeyWriteOnce(
107
+ aggregateId: string,
108
+ fields: Iterable<string>,
109
+ snapshot: Readonly<Record<string, unknown>>,
110
+ previous: Readonly<Record<string, unknown>> | null | undefined,
111
+ ): AggregateInvariantVerdict {
112
+ if (previous === undefined || previous === null) return { accept: true };
113
+ for (const name of fields) {
114
+ const before = previous[name];
115
+ if (before === undefined || before === null) continue;
116
+ if (!sameKeyValue(before, snapshot[name])) {
117
+ return {
118
+ refuse:
119
+ `${aggregateId}.${name}:${BIRTH_KEY_WRITE_ONCE_CODE} — '${name}' is the birth key of ${aggregateId}: the ` +
120
+ `born workspace's name was derived from it at birth and is fixed forever, so it may be seeded with the ` +
121
+ `same value but never changed (architecture/choosing_a_birth_key.md)`,
122
+ };
123
+ }
124
+ }
125
+ return { accept: true };
126
+ }
127
+
45
128
  /**
46
129
  * READ VISIBILITY (the native-relations read gate) — the DECLARED posture of an
47
130
  * aggregate's data on the read side. `public` (the DEFAULT, and the only value
@@ -640,6 +723,10 @@ export function aggregate<const Id extends string, F extends Record<string, Fiel
640
723
  return userInvariant !== undefined ? userInvariant(snapshot, previous) : { accept: true };
641
724
  }
642
725
  : userInvariant;
726
+ // BIRTH-KEY WRITE-ONCE (CAP-804): `birth()` registers its key field here AFTER this aggregate is
727
+ // declared; the composed invariant reads the shared set at evaluation time. A birth key is always a
728
+ // `.fromBirth()` field, so the presence invariant (and its hashed presence bit) already exists.
729
+ const birthKeyFields = new Set<string>();
643
730
  const composedInvariant: AggregateInvariantFn | undefined =
644
731
  fromBirthFields.length > 0
645
732
  ? (snapshot, previous) => {
@@ -649,6 +736,10 @@ export function aggregate<const Id extends string, F extends Record<string, Fiel
649
736
  return { reject: `${id}.${name} is fromBirth() — every ${id} must carry it from creation (the plan left it unset)` };
650
737
  }
651
738
  }
739
+ if (birthKeyFields.size > 0) {
740
+ const writeOnce = judgeBirthKeyWriteOnce(id, birthKeyFields, snapshot, previous);
741
+ if (!("accept" in writeOnce)) return writeOnce;
742
+ }
652
743
  return afterFulfilment !== undefined ? afterFulfilment(snapshot, previous) : { accept: true };
653
744
  }
654
745
  : afterFulfilment;
@@ -660,6 +751,8 @@ export function aggregate<const Id extends string, F extends Record<string, Fiel
660
751
  fields: stored as StoredFields<F>,
661
752
  hasMany: hasMany as HasManyFields<F>,
662
753
  __isAggregateHandle: true as const,
754
+ // Only a `.fromBirth()` aggregate can be born, so only it carries the registry (see BIRTH_KEY_FIELDS).
755
+ ...(fromBirthFields.length > 0 ? { [BIRTH_KEY_FIELDS]: birthKeyFields } : {}),
663
756
  ...(opts?.fulfils !== undefined ? { fulfilment: opts.fulfils } : {}),
664
757
  ...(composedInvariant !== undefined
665
758
  ? { hasInvariant: true as const, invariant: composedInvariant }