@githolon/dsl 0.108.7 → 0.110.1

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 (308) hide show
  1. package/assets/operation-contract.wasm +0 -0
  2. package/check_package.mjs +12 -15
  3. package/compile_package.mjs +17 -18
  4. package/compile_proof.mjs +16 -1
  5. package/dart/CHANGELOG.md +80 -1
  6. package/dart/README.md +25 -2
  7. package/dart/lib/src/admission_decision.dart +41 -0
  8. package/dart/lib/src/application_framework_calls.dart +52 -2
  9. package/dart/lib/src/application_model_adoption.dart +187 -0
  10. package/dart/lib/src/application_status.dart +73 -0
  11. package/dart/lib/src/console_enrolment.dart +271 -0
  12. package/dart/lib/src/console_http_io.dart +26 -0
  13. package/dart/lib/src/console_http_stub.dart +7 -0
  14. package/dart/lib/src/dev_cloud.dart +69 -22
  15. package/dart/lib/src/generated_call.dart +83 -23
  16. package/dart/lib/src/kernel_primitives.dart +119 -0
  17. package/dart/lib/src/nms_conformance_runtime.dart +1 -1
  18. package/dart/lib/src/nms_generated_runtime.dart +32 -10
  19. package/dart/lib/src/nms_test_environment.dart +1 -1
  20. package/dart/lib/src/node_runner_transport.dart +41 -46
  21. package/dart/lib/src/observed_session_directory.dart +70 -0
  22. package/dart/lib/src/operation_receipts.dart +112 -0
  23. package/dart/lib/src/peer_application_streams.dart +103 -0
  24. package/dart/lib/src/reception/dart_reception.dart +4280 -0
  25. package/dart/lib/src/reception/event_desk.dart +214 -0
  26. package/dart/lib/src/reception/extremum_read.dart +46 -0
  27. package/dart/lib/src/reception/local_copies.dart +371 -0
  28. package/dart/lib/src/reception/peer_office_connection.dart +11 -0
  29. package/dart/lib/src/reception/progress_desk.dart +268 -0
  30. package/dart/lib/src/reception/reception_contract.dart +11 -0
  31. package/dart/lib/src/reception/reception_session.dart +284 -0
  32. package/dart/lib/src/reception/sync_diagnostics.dart +26 -0
  33. package/dart/lib/src/reception/workspace_preparation_report.dart +126 -0
  34. package/dart/lib/src/replica_readiness.dart +123 -0
  35. package/dart/lib/src/runner/node-entry.mjs +23950 -15776
  36. package/dart/lib/src/runtime_v2_gateway.dart +56 -3
  37. package/dart/lib/src/runtime_v2_transport.dart +544 -3
  38. package/dart/lib/src/session_bound.dart +200 -311
  39. package/dart/lib/src/signing_identity_kernel.dart +84 -0
  40. package/dart/lib/src/sync_status_projection.dart +550 -0
  41. package/dart/lib/src/sync_status_reducer.dart +170 -0
  42. package/dart/lib/src/test_harness.dart +809 -1153
  43. package/dart/lib/src/testing/test_custody.dart +66 -0
  44. package/dart/lib/src/testing/test_peer.dart +328 -0
  45. package/dart/lib/src/typed_watch_registry.dart +169 -0
  46. package/dart/lib/src/workspace_open_progress.dart +27 -0
  47. package/dart/pubspec.lock +24 -1
  48. package/dart/pubspec.yaml +7 -2
  49. package/dart/pubspec_overrides.yaml +2 -0
  50. package/dart/test/admission_decision_test.dart +48 -0
  51. package/dart/test/background_model_adoption_test.dart +228 -0
  52. package/dart/test/bundle_version_pairing_test.dart +4 -4
  53. package/dart/test/child_authentication_repair_test.dart +359 -0
  54. package/dart/test/cloud_bound_local_shell_test.dart +119 -0
  55. package/dart/test/console_enrolment_refusal_test.dart +52 -0
  56. package/dart/test/console_enrolment_test.dart +227 -0
  57. package/dart/test/errors_test.dart +5 -5
  58. package/dart/test/event_plane_seam_test.dart +50 -164
  59. package/dart/test/extremum_read_test.dart +58 -0
  60. package/dart/test/fixtures/wedge/wedge-entry.mjs +3 -21
  61. package/dart/test/generated_query_test.dart +32 -0
  62. package/dart/test/harness_phase_timeout_test.dart +18 -23
  63. package/dart/test/kernel_author_after_read_test.dart +242 -0
  64. package/dart/test/kernel_fence_test.dart +137 -0
  65. package/dart/test/local_copies_test.dart +222 -0
  66. package/dart/test/model_adoption_test.dart +198 -0
  67. package/dart/test/node_peer_local_custody_test.dart +153 -0
  68. package/dart/test/offer_authentication_proof_test.dart +352 -52
  69. package/dart/test/operation_compatibility_test.dart +151 -0
  70. package/dart/test/platform_credential_effect_test.dart +386 -0
  71. package/dart/test/receiving_group_open_test.dart +213 -0
  72. package/dart/test/reception/workspace_preparation_report_test.dart +103 -0
  73. package/dart/test/reception_event_desk_close_test.dart +16 -0
  74. package/dart/test/reception_span_trace_test.dart +23 -0
  75. package/dart/test/replica_readiness_test.dart +156 -0
  76. package/dart/test/runtime_projections_test.dart +92 -0
  77. package/dart/test/runtime_v2_gateway_test.dart +131 -0
  78. package/dart/test/signing_identity_kernel_test.dart +75 -0
  79. package/dart/test/sync_status_preparation_report_test.dart +29 -0
  80. package/dart/test/sync_status_reducer_test.dart +419 -0
  81. package/dart/test/test_harness_test.dart +71 -95
  82. package/dart/test/typed_query_watch_test.dart +170 -0
  83. package/dart/test/typed_watch_registry_test.dart +249 -0
  84. package/dart_lints/README.md +18 -0
  85. package/dart_lints/lib/nomos_lints.dart +70 -0
  86. package/dart_lints/pubspec.lock +580 -0
  87. package/dart_lints/pubspec.yaml +12 -0
  88. package/dart_lints/test/reference_identity_test.dart +75 -0
  89. package/dart_types/CHANGELOG.md +18 -0
  90. package/dart_types/lib/nomos_kernel_protocol.dart +21 -1
  91. package/dart_types/lib/nomos_types.dart +9 -1
  92. package/dart_types/lib/src/admission_decision.dart +37 -0
  93. package/dart_types/lib/src/aggregate_meta.dart +1 -1
  94. package/dart_types/lib/src/approved_model.dart +65 -0
  95. package/dart_types/lib/src/custody.dart +10 -18
  96. package/dart_types/lib/src/error_info.dart +4 -4
  97. package/dart_types/lib/src/errors.dart +29 -29
  98. package/dart_types/lib/src/foreign_ref.dart +62 -33
  99. package/dart_types/lib/src/local_work_rejected.dart +3 -3
  100. package/dart_types/lib/src/nomos_ref.dart +3 -1
  101. package/dart_types/lib/src/principal.dart +2 -2
  102. package/dart_types/lib/src/proto/google/protobuf/any.pb.dart +163 -0
  103. package/dart_types/lib/src/proto/google/protobuf/any.pbenum.dart +11 -0
  104. package/dart_types/lib/src/proto/nomos/discovery/v1/workspace_placement.pb.dart +137 -0
  105. package/dart_types/lib/src/proto/nomos/discovery/v1/workspace_placement.pbenum.dart +11 -0
  106. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pb.dart +20721 -10433
  107. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbenum.dart +555 -27
  108. package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pb.dart +387 -0
  109. package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pbenum.dart +46 -0
  110. package/dart_types/lib/src/proto/nomos/runtime/v2/application_readiness_desk.pb.dart +295 -0
  111. package/dart_types/lib/src/proto/nomos/runtime/v2/application_readiness_desk.pbenum.dart +139 -0
  112. package/dart_types/lib/src/proto/nomos/runtime/v2/authentication_desk.pb.dart +269 -0
  113. package/dart_types/lib/src/proto/nomos/runtime/v2/authentication_desk.pbenum.dart +103 -0
  114. package/dart_types/lib/src/proto/nomos/runtime/v2/compatibility_desk.pb.dart +417 -0
  115. package/dart_types/lib/src/proto/nomos/runtime/v2/compatibility_desk.pbenum.dart +62 -0
  116. package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pb.dart +9968 -0
  117. package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pbenum.dart +133 -0
  118. package/dart_types/lib/src/proto/nomos/runtime/v2/credentials_desk.pb.dart +204 -0
  119. package/dart_types/lib/src/proto/nomos/runtime/v2/credentials_desk.pbenum.dart +54 -0
  120. package/dart_types/lib/src/proto/nomos/runtime/v2/custody_desk.pb.dart +1550 -0
  121. package/dart_types/lib/src/proto/nomos/runtime/v2/custody_desk.pbenum.dart +11 -0
  122. package/dart_types/lib/src/proto/nomos/runtime/v2/framework.pb.dart +710 -0
  123. package/dart_types/lib/src/proto/nomos/runtime/v2/framework.pbenum.dart +11 -0
  124. package/dart_types/lib/src/proto/nomos/runtime/v2/identity_desk.pb.dart +845 -0
  125. package/dart_types/lib/src/proto/nomos/runtime/v2/identity_desk.pbenum.dart +11 -0
  126. package/dart_types/lib/src/proto/nomos/runtime/v2/interaction_desk.pb.dart +198 -0
  127. package/dart_types/lib/src/proto/nomos/runtime/v2/interaction_desk.pbenum.dart +11 -0
  128. package/dart_types/lib/src/proto/nomos/runtime/v2/law_desk.pb.dart +369 -0
  129. package/dart_types/lib/src/proto/nomos/runtime/v2/law_desk.pbenum.dart +11 -0
  130. package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pb.dart +609 -0
  131. package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pbenum.dart +65 -0
  132. package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pb.dart +358 -0
  133. package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pbenum.dart +100 -0
  134. package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pb.dart +2231 -0
  135. package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pbenum.dart +271 -0
  136. package/dart_types/lib/src/proto/nomos/runtime/v2/placement_desk.pb.dart +1286 -0
  137. package/dart_types/lib/src/proto/nomos/runtime/v2/placement_desk.pbenum.dart +11 -0
  138. package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pb.dart +636 -0
  139. package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pbenum.dart +116 -0
  140. package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pb.dart +1637 -0
  141. package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pbenum.dart +175 -0
  142. package/dart_types/lib/src/proto/nomos/runtime/v2/recovery_desk.pb.dart +1269 -0
  143. package/dart_types/lib/src/proto/nomos/runtime/v2/recovery_desk.pbenum.dart +129 -0
  144. package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pb.dart +2782 -0
  145. package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pbenum.dart +97 -0
  146. package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pb.dart +2232 -8627
  147. package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pbenum.dart +0 -358
  148. package/dart_types/lib/src/proto/nomos/runtime/v2/sealing_desk.pb.dart +1639 -0
  149. package/dart_types/lib/src/proto/nomos/runtime/v2/sealing_desk.pbenum.dart +46 -0
  150. package/dart_types/lib/src/runtime_outcome.dart +133 -0
  151. package/dart_types/lib/src/subscriptions.dart +116 -23
  152. package/dart_types/pubspec.yaml +1 -1
  153. package/dart_types/test/extremum_subscriptions_test.dart +40 -0
  154. package/dart_types/test/foreign_ref_resolution_test.dart +75 -0
  155. package/dart_types/test/nomos_ref_address_test.dart +25 -0
  156. package/dart_types/test/ordered_subscriptions_test.dart +34 -0
  157. package/operation_contract_check.mjs +24 -0
  158. package/package.json +11 -6
  159. package/src/aggregate.ts +70 -10
  160. package/src/application_topology.ts +18 -11
  161. package/src/attested.ts +3 -0
  162. package/src/attested_core.ts +46 -3
  163. package/src/birth_meter_ensure.ts +19 -7
  164. package/src/births.ts +29 -4
  165. package/src/build_package.ts +140 -13
  166. package/src/business_birth.ts +181 -4
  167. package/src/codegen_call_proto.ts +1276 -0
  168. package/src/codegen_call_usda.ts +49 -6
  169. package/src/codegen_dart.ts +83 -44
  170. package/src/codegen_dart_client.ts +458 -800
  171. package/src/{codegen_dart_application.ts → codegen_dart_facade.ts} +331 -582
  172. package/src/codegen_dart_framework_calls.ts +320 -37
  173. package/src/codegen_dart_indexed_variant.ts +159 -0
  174. package/src/codegen_dart_scenario.ts +42 -13
  175. package/src/codegen_proof.ts +3 -3
  176. package/src/codegen_provider_dart.ts +5 -0
  177. package/src/codegen_query_proto.ts +158 -0
  178. package/src/codegen_refusal.ts +189 -0
  179. package/src/codegen_result_proto.ts +931 -0
  180. package/src/codegen_result_usda.ts +4 -0
  181. package/src/codegen_ts.ts +464 -616
  182. package/src/codegen_workspace_access.ts +105 -0
  183. package/src/compatibility_graph.ts +759 -0
  184. package/src/compatibility_usda.ts +264 -0
  185. package/src/compile_package_main.ts +2017 -1195
  186. package/src/custody_placement.ts +156 -0
  187. package/src/custody_placement_usda.ts +91 -0
  188. package/src/decl_shapes.ts +21 -2
  189. package/src/describe_model.ts +1 -0
  190. package/src/directive.ts +59 -2
  191. package/src/drivers.ts +51 -0
  192. package/src/engine_entry.ts +52 -8
  193. package/src/evolve_preview.ts +11 -4
  194. package/src/exists.ts +2 -0
  195. package/src/fields.ts +75 -11
  196. package/src/flow_gate.ts +2 -1
  197. package/src/framework/application_home.ts +170 -18
  198. package/src/framework/attested_receipts.ts +8 -3
  199. package/src/framework/auth_provider_law.ts +169 -9
  200. package/src/framework/birth_certificate_law.ts +4 -0
  201. package/src/framework/capability.ts +34 -3
  202. package/src/framework/child_authentication.ts +346 -0
  203. package/src/framework/console_governance.ts +295 -0
  204. package/src/framework/delegated_roles.ts +301 -0
  205. package/src/framework/domain_lifecycle.ts +26 -13
  206. package/src/framework/founding_genesis.ts +193 -0
  207. package/src/framework/framework_aggregates.ts +1 -1
  208. package/src/framework/genesis.ts +6 -1
  209. package/src/framework/governance_relations.ts +19 -0
  210. package/src/framework/home.ts +5 -734
  211. package/src/framework/legacy/child_auth_provider_epoch.ts +73 -0
  212. package/src/framework/model_authority_repair.ts +116 -0
  213. package/src/framework/model_authority_seed.ts +185 -0
  214. package/src/framework/model_transition.ts +211 -0
  215. package/src/framework/owned_custody.ts +1 -1
  216. package/src/framework/principal_enrollment.ts +302 -0
  217. package/src/framework/receiving_group.ts +857 -0
  218. package/src/framework/receiving_group_recovery.ts +314 -0
  219. package/src/framework/relations.ts +4 -8
  220. package/src/framework/signing_identity.ts +242 -95
  221. package/src/framework/source_roster.ts +11 -5
  222. package/src/framework/workspace_access.ts +149 -0
  223. package/src/framework/workspace_invariant.ts +73 -52
  224. package/src/framework/workspaces.ts +407 -40
  225. package/src/framework_call_definitions.ts +32 -3
  226. package/src/generated_client_bundle_boundary.ts +97 -0
  227. package/src/generated_client_names.ts +1 -0
  228. package/src/identity_usda.ts +27 -0
  229. package/src/index.ts +183 -12
  230. package/src/indexed_variant.ts +83 -0
  231. package/src/intent.ts +173 -45
  232. package/src/law_check.ts +8 -5
  233. package/src/law_dependency_index.ts +231 -0
  234. package/src/law_graph.ts +508 -71
  235. package/src/law_graph_usda.ts +170 -24
  236. package/src/law_identity.ts +119 -81
  237. package/src/law_source_map.ts +4 -1
  238. package/src/law_units.ts +498 -0
  239. package/src/law_value_type.ts +7 -3
  240. package/src/member_authentication.ts +302 -0
  241. package/src/null_meta.ts +1 -1
  242. package/src/openusd_compiled_layer.ts +132 -0
  243. package/src/operation_contract_check.ts +31 -0
  244. package/src/ordered_read.ts +2 -1
  245. package/src/package_config.ts +33 -12
  246. package/src/package_dependencies.ts +1 -0
  247. package/src/plan_source_slice.ts +30 -1
  248. package/src/protobuf_field_number.ts +36 -0
  249. package/src/provenance_wire.ts +6 -0
  250. package/src/query.ts +2 -1
  251. package/src/read.ts +50 -0
  252. package/src/relation.ts +4 -1
  253. package/src/relation_schema.ts +15 -2
  254. package/src/report.ts +2 -2
  255. package/src/sqlite_computation_bundle.ts +78 -0
  256. package/src/sqlite_computed.ts +102 -0
  257. package/src/sqlite_counts.ts +154 -0
  258. package/src/sqlite_layout.ts +823 -0
  259. package/src/sqlite_reads.ts +275 -0
  260. package/src/sqlite_samples.ts +185 -0
  261. package/src/stable_id_identity_bridge.ts +1 -1
  262. package/src/stable_ids_for_module.ts +16 -4
  263. package/src/test_call_usda.ts +6 -6
  264. package/src/usda_relationship_integrity.ts +20 -3
  265. package/src/usda_text.ts +21 -1
  266. package/src/value_object.ts +81 -11
  267. package/src/wire.ts +22 -0
  268. package/src/wire_encode.ts +227 -20
  269. package/src/workspace_sharding.ts +15 -6
  270. package/src/workspace_sharing.ts +51 -49
  271. package/src/zod_introspection.ts +2 -0
  272. package/compat_package.mjs +0 -46
  273. package/dart/lib/src/home.dart +0 -15
  274. package/dart/lib/src/runner/offline-install.mjs +0 -15367
  275. package/dart/lib/src/source_trust.dart +0 -51
  276. package/dart/lib/src/webview_bridge.dart +0 -6540
  277. package/dart/lib/src/workspace_name.dart +0 -30
  278. package/dart/test/acquire_session_credentials_test.dart +0 -150
  279. package/dart/test/acquire_session_test.dart +0 -534
  280. package/dart/test/adv_realm_interference_test.dart +0 -600
  281. package/dart/test/cloud_bound_topology_test.dart +0 -168
  282. package/dart/test/cluster_births_test.dart +0 -202
  283. package/dart/test/cluster_device_keyed_acceptance_test.dart +0 -105
  284. package/dart/test/cluster_local_autosync_test.dart +0 -123
  285. package/dart/test/cluster_scale_test.dart +0 -122
  286. package/dart/test/compat_test.dart +0 -61
  287. package/dart/test/dlq_session_surface_test.dart +0 -222
  288. package/dart/test/first_app_gauntlet_test.dart +0 -386
  289. package/dart/test/fixtures/wedge/fake-install.mjs +0 -11
  290. package/dart/test/four_layer_nested_topology_test.dart +0 -290
  291. package/dart/test/home_parity_test.dart +0 -13
  292. package/dart/test/local_sync_inert_test.dart +0 -107
  293. package/dart/test/node_runner_transport_test.dart +0 -78
  294. package/dart/test/parent_signer_recovery_test.dart +0 -155
  295. package/dart/test/residency_refusal_test.dart +0 -49
  296. package/dart/test/sealing_client_test.dart +0 -181
  297. package/dart/test/session_bound_test.dart +0 -285
  298. package/dart/test/stale_bundle_test.dart +0 -80
  299. package/dart/test/vo_read_typing_test.dart +0 -131
  300. package/dart/test/watch_after_offer_stall_test.dart +0 -100
  301. package/dart/test/webview_bridge_test.dart +0 -1930
  302. package/dart/test/workspace_name_test.dart +0 -24
  303. package/runtime/generated-application-host.js +0 -22860
  304. package/src/codegen_query_usda.ts +0 -214
  305. package/src/compat_manifest.ts +0 -26
  306. package/src/compat_package_main.ts +0 -224
  307. package/src/shape_compat.ts +0 -352
  308. package/src/usd_state.ts +0 -505
package/src/law_graph.ts CHANGED
@@ -4,6 +4,7 @@
4
4
  import type { DomainModule } from "./codegen_dart.js";
5
5
  import type { z } from "zod";
6
6
  import { finishCount } from "./count.js";
7
+ import { SIGNER_DELEGATION_PREFIX } from "./framework/signing_identity.js";
7
8
  import { finishExists } from "./exists.js";
8
9
  import { finishExtremum } from "./extremum.js";
9
10
  import { finishSum } from "./sum.js";
@@ -15,9 +16,12 @@ import {
15
16
  mintStructuralLawSid,
16
17
  type LawEntityKind,
17
18
  type LawIdentityCarry,
19
+ type LawIdentityEntity,
18
20
  type LawIdentityRegistry,
21
+ type LawOperationFieldWire,
19
22
  } from "./law_identity.js";
20
- import type { ReferentialMarker } from "./directive.js";
23
+ import type { Directive, ReferentialMarker } from "./directive.js";
24
+ import type { MemberAuthentication } from "./member_authentication.js";
21
25
  import type { CanonicalPred } from "./predicate.js";
22
26
  import type { PointGuardPredicate } from "./point_guard.js";
23
27
  import {
@@ -27,7 +31,7 @@ import {
27
31
  type Rewrite,
28
32
  } from "./relation_schema.js";
29
33
  import type { StableFieldEvolution, StableIds } from "./stable_ids_types.js";
30
- import { isBusinessIntent } from "./intent.js";
34
+ import { isBusinessIntent, publicOfferRoute } from "./intent.js";
31
35
  import {
32
36
  lowerLawValueType,
33
37
  type LawConstant,
@@ -38,7 +42,13 @@ import {
38
42
  type LawValueType,
39
43
  } from "./law_value_type.js";
40
44
  import { harvestPrincipalFlows } from "./wire_encode.js";
41
- import { admitsNull, type Field, type FieldKind, type ValueObjectFieldDescriptor } from "./fields.js";
45
+ import {
46
+ admitsNull,
47
+ type Field,
48
+ type FieldKind,
49
+ type ValueObjectDescriptor,
50
+ type ValueObjectFieldDescriptor,
51
+ } from "./fields.js";
42
52
  import type { GeometryKind } from "./value_object.js";
43
53
  import type { Driver } from "./drivers.js";
44
54
  import {
@@ -302,6 +312,10 @@ export interface LawStructuralEntity<K extends LawStructuralKind> {
302
312
  readonly sid: string;
303
313
  readonly label: string;
304
314
  readonly identityClass: "structural";
315
+ /** Retained transport allocation. Meaning remains the scoped field name + its disclosed lineage. */
316
+ readonly wireKey?: string;
317
+ /** Previously shipped transport allocations accepted during a managed overlap. */
318
+ readonly wireAliases?: readonly string[];
305
319
  }
306
320
 
307
321
  export interface LawStructuralRef<K extends LawStructuralKind> {
@@ -469,7 +483,12 @@ export interface LawTrustSourceSetNode extends LawPolicyBase<"source-set"> {
469
483
  readonly keysField: LawRef<"field">;
470
484
  }
471
485
 
472
- export type LawPolicyNode = LawPinnedTrustSourceNode | LawTrustSourceSetNode;
486
+ export interface LawAuthorizedCustodySourceNode extends LawPolicyBase<"authorized-custody-source"> {}
487
+
488
+ export type LawPolicyNode =
489
+ | LawPinnedTrustSourceNode
490
+ | LawTrustSourceSetNode
491
+ | LawAuthorizedCustodySourceNode;
473
492
 
474
493
  export interface LawForeignReadNode {
475
494
  readonly kind: "structural";
@@ -508,6 +527,7 @@ export interface LawWorkspaceInvariantNode {
508
527
  readonly entity: LawEntity<"invariant">;
509
528
  readonly domain: LawRef<"domain">;
510
529
  readonly invariantKind: "workspace";
530
+ readonly refusalType?: LawRef<"type">;
511
531
  readonly on: LawRef<"intent">;
512
532
  readonly reads?: readonly {
513
533
  readonly binding: string;
@@ -516,7 +536,7 @@ export interface LawWorkspaceInvariantNode {
516
536
  readonly eachPayloadPath?: readonly string[];
517
537
  readonly id: InvariantReadBinding["id"];
518
538
  }[];
519
- readonly evaluator?: "birth-quota-v1" | "founder-signer-write-once-v1" | "point-guard-v1";
539
+ readonly evaluator?: "birth-quota-v1" | "founder-signer-write-once-v1" | "founder-signer-write-once-v2" | "point-guard-v1";
520
540
  readonly pointGuard?: {
521
541
  readonly aggregate: LawRef<"aggregate">;
522
542
  readonly binding: string;
@@ -549,6 +569,8 @@ export interface LawCapabilityNode {
549
569
  readonly deadLetter: LawRef<"intent">;
550
570
  readonly capability: string;
551
571
  readonly endpoint?: string;
572
+ readonly executionPlacement?: "custodian";
573
+ readonly executionCardinality?: "singleton";
552
574
  readonly tasks?: LawRef<"read">;
553
575
  }
554
576
 
@@ -572,6 +594,7 @@ export interface LawIntentAuthorization {
572
594
  readonly relation: LawTargetRef<"relation">;
573
595
  readonly object: string;
574
596
  readonly objectFrom?: LawIntentAuthorizationObjectFrom;
597
+ readonly subjectFromAttestedRead?: string;
575
598
  }
576
599
 
577
600
  export interface LawIntentNode {
@@ -597,9 +620,13 @@ export interface LawIntentNode {
597
620
  readonly acceptsWorkspaceEffect: boolean;
598
621
  /** Recovery-only admission: the verified source workspace must be the target's exact birth parent. */
599
622
  readonly acceptsVerifiedBirthParentRecovery: boolean;
600
- /** Client proof selected by law for this exact public offer. Absence retains the ordinary
601
- * session-credential posture. */
602
- readonly offerAuthenticationProof?: "enrolledDevice";
623
+ readonly acceptsVerifiedWorkspaceClaim?: boolean;
624
+ /** Authentication selected by law for this exact member. Absence retains the historical
625
+ * default-provider interpretation for existing law. */
626
+ readonly authentication?: MemberAuthentication;
627
+ /** D22 sovereign self-certification: the founding signer tuple this intent writes is admitted iff
628
+ * the signing key's sha256 is the captured principal's subject. */
629
+ readonly selfCertifyingPrincipal?: boolean;
603
630
  /** Exact nominal target directives this source intent may author into another workspace. */
604
631
  readonly directsWorkspaceEffects: readonly LawTargetRef<"intent">[];
605
632
  /** Zanzibar relation required to author this intent. */
@@ -609,6 +636,8 @@ export interface LawIntentNode {
609
636
  readonly births: boolean;
610
637
  readonly birthRecord?: LawRef<"aggregate">;
611
638
  readonly birthPrefix?: string;
639
+ readonly birthDelegations?: LawRef<"field">;
640
+ readonly birthDelegationPrefix?: string;
612
641
  /** The law-declared genesis verb that establishes this workspace's birth policy. This is a
613
642
  * relationship to executable law, never a compiler/runtime allowance value: the kernel uses it
614
643
  * only to require an explicit matching step in child genesis before custody can be created. */
@@ -644,12 +673,7 @@ export interface LawIntentNode {
644
673
  readonly effects: readonly LawStructuralRef<"effect">[];
645
674
  readonly steps: readonly LawRef<"intent">[];
646
675
  readonly refuses: readonly string[];
647
- }
648
-
649
- /** The public directive vocabulary carried into typed USDA and generated-client compatibility. */
650
- export function publicIntentRoute(intent: DomainModule["directives"][number]): string {
651
- const businessBirth = (intent as { businessBirth?: unknown }).businessBirth;
652
- return businessBirth === undefined || isBusinessIntent(intent) ? intent.id : "birth";
676
+ readonly domainRefusals?: readonly LawRef<"type">[];
653
677
  }
654
678
 
655
679
  /**
@@ -680,6 +704,7 @@ interface LawReadBase<K extends string> {
680
704
  readonly of: LawRef<"aggregate">;
681
705
  readonly contract: LawRef<"contract">;
682
706
  readonly public: boolean;
707
+ readonly authentication?: MemberAuthentication;
683
708
  readonly predicate?: LawStructuralRef<"predicate">;
684
709
  }
685
710
 
@@ -711,6 +736,9 @@ export interface LawOrderedReadNode extends LawReadBase<"ordered"> {
711
736
  readonly filtered: boolean;
712
737
  }
713
738
 
739
+ /** Declared aggregate-row reads share typed transport, not selection semantics. */
740
+ export type LawRowReadNode = LawQueryReadNode | LawOrderedReadNode;
741
+
714
742
  export interface LawSpatialReadNode extends LawReadBase<"spatial"> {
715
743
  readonly geometry: LawFieldPathRef;
716
744
  }
@@ -776,6 +804,8 @@ export interface LawRetirement {
776
804
  }
777
805
 
778
806
  export interface DiscoverLawGraphOptions {
807
+ /** The package's exact declaration/export identity, not a label search. */
808
+ readonly workspaceEffectDomain?: (target: Directive<unknown>) => string;
779
809
  readonly intentCarries?: readonly LawIdentityCarry[];
780
810
  readonly readCarries?: readonly LawIdentityCarry[];
781
811
  readonly source?: (
@@ -786,6 +816,7 @@ export interface DiscoverLawGraphOptions {
786
816
  | "intent"
787
817
  | "read"
788
818
  | "contract"
819
+ | "operation-field"
789
820
  | "type"
790
821
  | "object-type"
791
822
  | "relation"
@@ -992,6 +1023,7 @@ export function discoverLawGraph(
992
1023
  }
993
1024
 
994
1025
  const types = [...(module.valueObjects ?? [])].sort((a, b) => compareText(a.name, b.name));
1026
+ const typeDescriptorByName = new Map(types.map((type) => [type.name, type] as const));
995
1027
  reconciled = identities.reconcile({
996
1028
  kind: "type",
997
1029
  parent: domainKey,
@@ -1008,7 +1040,7 @@ export function discoverLawGraph(
1008
1040
  ),
1009
1041
  ] as const));
1010
1042
  const linkValueObjectField = (node: LawFieldNode, field: Field, owner: string): LawFieldNode => {
1011
- const valueObject = field.valueObject;
1043
+ const valueObject = field.valueObject ?? field.mapValueVo;
1012
1044
  if (valueObject === undefined) return node;
1013
1045
  const target = typeEntitiesByLabel.get(valueObject.name);
1014
1046
  if (target === undefined) fail(`field '${owner}.${node.entity.label}' targets unknown type '${valueObject.name}'`);
@@ -1017,6 +1049,27 @@ export function discoverLawGraph(
1017
1049
  anonymousValueNodes.delete(id);
1018
1050
  }
1019
1051
  }
1052
+ if (field.mapValueVo !== undefined) {
1053
+ // A map of named values is not an anonymous JSON/map shape. Lower its
1054
+ // canonical descriptor into the graph so every downstream projection
1055
+ // (Protobuf, Dart, TypeScript and USDA) sees the same nested nominal
1056
+ // enums, units, references and objects.
1057
+ const lowered = lowerAnonymousValueGraph(
1058
+ {
1059
+ kind: "map",
1060
+ value: descriptorTypeValue(
1061
+ field.mapValueVo.descriptor,
1062
+ typeDescriptorByName,
1063
+ new Set(),
1064
+ ),
1065
+ },
1066
+ node.entity,
1067
+ );
1068
+ for (const structural of lowered.nodes) {
1069
+ anonymousValueNodes.set(structural.entity.id, structural);
1070
+ }
1071
+ return { ...node, value: lowered.root };
1072
+ }
1020
1073
  return { ...node, value: ref(target) };
1021
1074
  };
1022
1075
  for (const aggregate of module.aggregates) {
@@ -1064,7 +1117,10 @@ export function discoverLawGraph(
1064
1117
  if (target === undefined) fail(`type '${type.name}' field '${fieldName}' targets unknown type '${field.ref}'`);
1065
1118
  value = ref(target);
1066
1119
  } else {
1067
- const lowered = lowerAnonymousValueGraph(descriptorValue(field), fieldEntity);
1120
+ const lowered = lowerAnonymousValueGraph(
1121
+ descriptorValue(field, typeDescriptorByName, new Set(), type.name, fieldName),
1122
+ fieldEntity,
1123
+ );
1068
1124
  for (const structural of lowered.nodes) anonymousValueNodes.set(structural.entity.id, structural);
1069
1125
  value = lowered.root;
1070
1126
  }
@@ -1076,7 +1132,7 @@ export function discoverLawGraph(
1076
1132
  evolutions: [],
1077
1133
  mergeDrivers: [],
1078
1134
  required: field.optional !== true,
1079
- nullable: false,
1135
+ nullable: field.nullable === true,
1080
1136
  encrypted: false,
1081
1137
  blindIndexed: false,
1082
1138
  };
@@ -1090,12 +1146,13 @@ export function discoverLawGraph(
1090
1146
  if (referenced === undefined) fail(`type '${type.name}' variant '${variantName}' targets unknown type '${variant.ref}'`);
1091
1147
  target = ref(referenced);
1092
1148
  } else {
1149
+ const armOwner = `${type.name}${variantName[0]!.toUpperCase()}${variantName.slice(1)}`;
1093
1150
  const value: LawValueType = {
1094
1151
  kind: "record",
1095
1152
  fields: Object.entries(variant.fields).sort(([a], [b]) => compareText(a, b)).map(([name, field]) => ({
1096
1153
  name,
1097
1154
  required: field.optional !== true,
1098
- value: descriptorValue(field),
1155
+ value: descriptorValue(field, typeDescriptorByName, new Set(), armOwner, name),
1099
1156
  })),
1100
1157
  };
1101
1158
  const lowered = lowerAnonymousValueGraph(value, typeEntity);
@@ -1195,7 +1252,16 @@ export function discoverLawGraph(
1195
1252
  // A workspace-effect-only intent has no local aggregate consequence. Its nominal target
1196
1253
  // aggregate belongs to the destination package; retain that as a dependency export instead
1197
1254
  // of fabricating a local audit row merely to satisfy the older directive-shaped IR.
1198
- return { kind: "dependency-export", entityKind: "aggregate", label: aggregateId };
1255
+ const effect = intent.workspaceEffects.find(
1256
+ (candidate) => candidate.target.aggregateId === aggregateId,
1257
+ )!;
1258
+ const owner = options.workspaceEffectDomain?.(effect.target);
1259
+ return {
1260
+ kind: "dependency-export",
1261
+ entityKind: "aggregate",
1262
+ label: aggregateId,
1263
+ ...(owner === undefined ? {} : { domain: owner }),
1264
+ };
1199
1265
  }
1200
1266
  const birth = (intent as { businessBirth?: { aggregateId?: unknown; domain?: unknown } }).businessBirth;
1201
1267
  if (birth?.aggregateId === aggregateId && typeof birth.domain === "string") {
@@ -1205,6 +1271,55 @@ export function discoverLawGraph(
1205
1271
  };
1206
1272
  const intentNodes: LawIntentNode[] = intents.map((intent) => {
1207
1273
  const intentEntity = intentEntities.get(intent.id)!;
1274
+ const liveOperationFieldNames = new Set<string>();
1275
+ const lowerPayloadContract = (
1276
+ value: LawValueType,
1277
+ contract: LawEntity<"contract">,
1278
+ scope: "accepts" | "publicAccepts",
1279
+ ): ReturnType<typeof lowerAnonymousContractFields> => {
1280
+ // A field's scoped name is its semantic declaration. The identity ledger keeps
1281
+ // its prior names/tombstone; this additional binding retains only the Protobuf
1282
+ // allocation already spoken by clients. Schema fingerprints remain structural.
1283
+ const provisional = lowerAnonymousContractFields(value, contract);
1284
+ const provisionalById = new Map(provisional.nodes.map((node) => [node.entity.id, node]));
1285
+ const fields = provisional.fields.map((fieldRef): LawAnonymousValueFieldNode => {
1286
+ const field = provisionalById.get(fieldRef.id);
1287
+ if (field?.kind !== "structural" || field.structuralKind !== "value-field") {
1288
+ fail(`directive '${intent.id}' has an invalid top-level ${scope} field`);
1289
+ }
1290
+ return field;
1291
+ });
1292
+ const labels = fields.map((field) => field.name);
1293
+ for (const label of labels) liveOperationFieldNames.add(label);
1294
+ const preferredOperationFieldWire = Object.fromEntries(
1295
+ fields.map((field) => [
1296
+ field.name,
1297
+ { protobuf: { canonical: field.entity.sid } },
1298
+ ]),
1299
+ );
1300
+ const fieldLedger = identities.reconcile({
1301
+ kind: "operation-field",
1302
+ parent: intentEntity.id,
1303
+ labels,
1304
+ preferredOperationFieldWire,
1305
+ // `accepts` and the optional public projection are lowered separately but
1306
+ // share one operation namespace. Retire only after their union is known.
1307
+ retireAbsent: false,
1308
+ });
1309
+ registry = fieldLedger.registry;
1310
+ return lowerAnonymousContractFields(value, contract, (name) => {
1311
+ const key = fieldLedger.resolved[name];
1312
+ const field = key === undefined ? undefined : registry.entities[key];
1313
+ if (
1314
+ field?.kind !== "operation-field" ||
1315
+ field.status !== "active" ||
1316
+ field.operationFieldWire === undefined
1317
+ ) {
1318
+ fail(`directive '${intent.id}' has no active wire allocation for ${scope} field '${name}'`);
1319
+ }
1320
+ return field.operationFieldWire.protobuf;
1321
+ });
1322
+ };
1208
1323
  const businessBirth = (intent as {
1209
1324
  businessBirth?: { publicPayloadSchema?: z.ZodTypeAny; owned?: boolean };
1210
1325
  }).businessBirth;
@@ -1263,9 +1378,10 @@ export function discoverLawGraph(
1263
1378
  ) {
1264
1379
  frameworkCapabilities.add("owned-authoring");
1265
1380
  }
1266
- const payloadShape = lowerAnonymousContractFields(
1381
+ const payloadShape = lowerPayloadContract(
1267
1382
  internalPayloadContract(tenantPayloadContract, frameworkCapabilities),
1268
1383
  payloadContractEntity,
1384
+ "accepts",
1269
1385
  );
1270
1386
  for (const node of payloadShape.nodes) anonymousValueNodes.set(node.entity.id, node);
1271
1387
  payloadContractNodes.push({
@@ -1284,9 +1400,10 @@ export function discoverLawGraph(
1284
1400
  `${intent.id}#publicAccepts`,
1285
1401
  options.source?.("intent", intent.id, module.name),
1286
1402
  );
1287
- const publicPayloadShape = lowerAnonymousContractFields(
1403
+ const publicPayloadShape = lowerPayloadContract(
1288
1404
  lowerLawValueType(businessBirth.publicPayloadSchema),
1289
1405
  publicPayloadContractEntity,
1406
+ "publicAccepts",
1290
1407
  );
1291
1408
  for (const node of publicPayloadShape.nodes) anonymousValueNodes.set(node.entity.id, node);
1292
1409
  payloadContractNodes.push({
@@ -1296,6 +1413,12 @@ export function discoverLawGraph(
1296
1413
  fields: publicPayloadShape.fields,
1297
1414
  });
1298
1415
  }
1416
+ const finalizedOperationFields = identities.reconcile({
1417
+ kind: "operation-field",
1418
+ parent: intentEntity.id,
1419
+ labels: [...liveOperationFieldNames],
1420
+ });
1421
+ registry = finalizedOperationFields.registry;
1299
1422
  const payloadFields = new Map<string, LawStructuralRef<"value-field">>();
1300
1423
  const indexPayloadValue = (
1301
1424
  valueRef: LawValueTypeRef,
@@ -1477,7 +1600,7 @@ export function discoverLawGraph(
1477
1600
  // compiler-lowered business-birth directive publishes the generic framework `birth` route.
1478
1601
  // Keep this identical to domainInterface/codegen so the typed USDA is sufficient to
1479
1602
  // reconstruct the generated-client compatibility surface.
1480
- publicRoute: publicIntentRoute(intent),
1603
+ publicRoute: publicOfferRoute(intent),
1481
1604
  accepts: ref(payloadContractEntity),
1482
1605
  ...(publicPayloadContractEntity === undefined ? {} : {
1483
1606
  publicAccepts: ref(publicPayloadContractEntity),
@@ -1488,16 +1611,25 @@ export function discoverLawGraph(
1488
1611
  acceptsWorkspaceEffect: intent.acceptsWorkspaceEffectGate === true,
1489
1612
  acceptsVerifiedBirthParentRecovery:
1490
1613
  intent.acceptsVerifiedBirthParentRecoveryGate === true,
1491
- ...(isBusinessIntent(intent) && intent.offerAuthenticationProof !== undefined
1492
- ? { offerAuthenticationProof: intent.offerAuthenticationProof }
1614
+ ...(intent.acceptsVerifiedWorkspaceClaimGate === true
1615
+ ? { acceptsVerifiedWorkspaceClaim: true }
1493
1616
  : {}),
1494
- directsWorkspaceEffects: (isBusinessIntent(intent) ? intent.workspaceEffects : []).map((effect) => {
1495
- const local = intentEntities.get(effect.target.id);
1617
+ ...(intent.authentication !== undefined
1618
+ ? { authentication: intent.authentication }
1619
+ : {}),
1620
+ ...(intent.selfCertifyingPrincipalGate === true
1621
+ ? { selfCertifyingPrincipal: true }
1622
+ : {}),
1623
+ directsWorkspaceEffects: (intent.workspaceEffects ?? []).map((effect) => {
1624
+ const owner = options.workspaceEffectDomain?.(effect.target);
1625
+ const local = owner === undefined || owner === (module.domain ?? module.name)
1626
+ ? intentEntities.get(effect.target.id) : undefined;
1496
1627
  return local === undefined
1497
1628
  ? {
1498
1629
  kind: "dependency-export" as const,
1499
1630
  entityKind: "intent" as const,
1500
1631
  label: effect.target.id,
1632
+ ...(owner === undefined ? {} : { domain: owner }),
1501
1633
  }
1502
1634
  : ref(local);
1503
1635
  }),
@@ -1508,6 +1640,10 @@ export function discoverLawGraph(
1508
1640
  birthRecord: ref(aggregateByLabel.get("NomosBirthRecord")!.node.entity),
1509
1641
  birthPrefix: "nomos-birth-record:",
1510
1642
  }),
1643
+ ...(intent.birthsChild !== true || aggregateByLabel.get("SignerDelegation")?.fields.get("certificates") === undefined ? {} : {
1644
+ birthDelegations: ref(aggregateByLabel.get("SignerDelegation")!.fields.get("certificates")!.entity),
1645
+ birthDelegationPrefix: SIGNER_DELEGATION_PREFIX,
1646
+ }),
1511
1647
  ...(intent.birthsChild !== true || intentEntities.get("seedBirthPolicy") === undefined ? {} : {
1512
1648
  birthPolicySeed: ref(intentEntities.get("seedBirthPolicy")!),
1513
1649
  }),
@@ -1709,10 +1845,15 @@ export function discoverLawGraph(
1709
1845
  });
1710
1846
  for (const [index, intentNode] of intentNodes.entries()) {
1711
1847
  const declaration = intents.find((intent) => intent.id === intentNode.entity.label)!;
1848
+ if (declaration.acceptsVerifiedWorkspaceClaimGate === true &&
1849
+ declaration.acceptsVerifiedBirthParentRecoveryGate === true) {
1850
+ throw new Error(`directive '${declaration.id}' declares competing workspace admission policies`);
1851
+ }
1712
1852
  const gate = declaration.requiresRelationGate;
1713
1853
  if (
1714
1854
  declaration.acceptsWorkspaceEffectGate === true &&
1715
- declaration.acceptsVerifiedBirthParentRecoveryGate !== true
1855
+ declaration.acceptsVerifiedBirthParentRecoveryGate !== true &&
1856
+ declaration.acceptsVerifiedWorkspaceClaimGate !== true
1716
1857
  ) {
1717
1858
  if (gate === undefined || gate.object !== "workspace:self" || gate.objectFrom !== undefined) {
1718
1859
  fail(
@@ -1722,6 +1863,17 @@ export function discoverLawGraph(
1722
1863
  }
1723
1864
  }
1724
1865
  if (gate === undefined) continue;
1866
+ if (
1867
+ gate.subjectFromAttestedRead !== undefined &&
1868
+ !(declaration.declaredForeignReads ?? []).some(
1869
+ (edge) => edge.queryId === gate.subjectFromAttestedRead,
1870
+ )
1871
+ ) {
1872
+ fail(
1873
+ `intent '${declaration.id}' authorization subject read ` +
1874
+ `'${gate.subjectFromAttestedRead}' is not declared with .readsFrom(...)`,
1875
+ );
1876
+ }
1725
1877
  const objectType = gate.objectFrom?.recordType ?? gate.object.split(":", 1)[0]!;
1726
1878
  const exactLabel = gate.relation.includes("#") ? gate.relation : `${objectType}#${gate.relation}`;
1727
1879
  const candidates = relationNodes.filter((relation) =>
@@ -1770,6 +1922,9 @@ export function discoverLawGraph(
1770
1922
  : ref(candidates[0].entity),
1771
1923
  object: authorizationObject,
1772
1924
  ...(objectFrom === undefined ? {} : { objectFrom }),
1925
+ ...(gate.subjectFromAttestedRead === undefined
1926
+ ? {}
1927
+ : { subjectFromAttestedRead: gate.subjectFromAttestedRead }),
1773
1928
  },
1774
1929
  };
1775
1930
  }
@@ -1800,10 +1955,10 @@ export function discoverLawGraph(
1800
1955
  }
1801
1956
 
1802
1957
  const pinnedSources = [...(module.trustSources ?? [])].sort((a, b) => compareText(a.workspace, b.workspace));
1803
- const sourceSets = [...(module.trustPolicies ?? [])].sort((a, b) => compareText(a.policyId, b.policyId));
1958
+ const trustPolicies = [...(module.trustPolicies ?? [])].sort((a, b) => compareText(a.policyId, b.policyId));
1804
1959
  const policyLabels = [
1805
1960
  ...pinnedSources.map((source) => `source:${source.workspace}`),
1806
- ...sourceSets.map((policy) => policy.policyId),
1961
+ ...trustPolicies.map((policy) => policy.policyId),
1807
1962
  ];
1808
1963
  reconciled = identities.reconcile({
1809
1964
  kind: "policy",
@@ -1829,7 +1984,24 @@ export function discoverLawGraph(
1829
1984
  onDishonor: source.onDishonor ?? "refuse",
1830
1985
  };
1831
1986
  }),
1832
- ...sourceSets.map((policy): LawTrustSourceSetNode => {
1987
+ ...trustPolicies.map((policy): LawTrustSourceSetNode | LawAuthorizedCustodySourceNode => {
1988
+ if (policy.authorizationOnly === true) {
1989
+ return {
1990
+ kind: "policy",
1991
+ entity: entity(registry, reconciled.resolved[policy.policyId]!, "policy", options.source?.("policy", policy.policyId, module.name)),
1992
+ domain: domainRef,
1993
+ policyKind: "authorized-custody-source",
1994
+ monotone: false,
1995
+ onDishonor: "refuse",
1996
+ };
1997
+ }
1998
+ if (
1999
+ policy.allowListType === undefined ||
2000
+ policy.genesisField === undefined ||
2001
+ policy.keysField === undefined
2002
+ ) {
2003
+ fail(`policy '${policy.policyId}' has no source-set fields`);
2004
+ }
1833
2005
  const allowList = aggregateByLabel.get(policy.allowListType);
1834
2006
  if (allowList === undefined) fail(`policy '${policy.policyId}' targets unknown aggregate '${policy.allowListType}'`);
1835
2007
  const genesisField = allowList.fields.get(policy.genesisField);
@@ -1856,8 +2028,9 @@ export function discoverLawGraph(
1856
2028
  policyNodes.filter((node): node is LawPinnedTrustSourceNode => node.policyKind === "pinned-source")
1857
2029
  .map((node) => [node.workspace, node] as const),
1858
2030
  );
1859
- const sourceSetByLabel = new Map(
1860
- policyNodes.filter((node): node is LawTrustSourceSetNode => node.policyKind === "source-set")
2031
+ const dynamicPolicyByLabel = new Map(
2032
+ policyNodes.filter((node): node is LawTrustSourceSetNode | LawAuthorizedCustodySourceNode =>
2033
+ node.policyKind === "source-set" || node.policyKind === "authorized-custody-source")
1861
2034
  .map((node) => [node.entity.label, node] as const),
1862
2035
  );
1863
2036
  const foreignReadNodes: LawForeignReadNode[] = [];
@@ -1867,13 +2040,34 @@ export function discoverLawGraph(
1867
2040
  for (const edge of declaration.declaredForeignReads ?? []) {
1868
2041
  const source = "from" in edge
1869
2042
  ? pinnedPolicyByWorkspace.get(edge.from)
1870
- : sourceSetByLabel.get(edge.policy);
2043
+ : dynamicPolicyByLabel.get(edge.policy);
1871
2044
  if (source === undefined) {
1872
2045
  fail(
1873
2046
  `intent '${declaration.id}' foreign read '${edge.queryId}' targets undeclared ` +
1874
2047
  `${"from" in edge ? `source '${edge.from}'` : `policy '${edge.policy}'`}`,
1875
2048
  );
1876
2049
  }
2050
+ if (
2051
+ source.policyKind === "authorized-custody-source" &&
2052
+ declaration.requiresRelationGate?.subjectFromAttestedRead !== edge.queryId
2053
+ ) {
2054
+ const subjectQuery = declaration.requiresRelationGate?.subjectFromAttestedRead;
2055
+ const subjectEdge = declaration.declaredForeignReads?.find((candidate) =>
2056
+ candidate.queryId === subjectQuery
2057
+ && "policy" in candidate
2058
+ && "policy" in edge
2059
+ && candidate.policy === edge.policy
2060
+ && "fromPayload" in candidate
2061
+ && "fromPayload" in edge
2062
+ && candidate.fromPayload === edge.fromPayload
2063
+ );
2064
+ if (subjectEdge === undefined) {
2065
+ fail(
2066
+ `intent '${declaration.id}' authorized custody read '${edge.queryId}' must derive its ` +
2067
+ `Zanzibar subject from an attested read bound to the same source`,
2068
+ );
2069
+ }
2070
+ }
1877
2071
  const fromField = "fromPayload" in edge
1878
2072
  ? payloadFieldsByIntent.get(declaration.id)?.get(edge.fromPayload)
1879
2073
  : undefined;
@@ -1912,6 +2106,10 @@ export function discoverLawGraph(
1912
2106
  });
1913
2107
  registry = reconciled.registry;
1914
2108
  const invariantNodes: LawInvariantNode[] = invariants.map((invariant) => {
2109
+ const refusalType = invariant.refusal === undefined ? undefined : typeEntitiesByLabel.get(invariant.refusal.name);
2110
+ if (invariant.refusal !== undefined && refusalType === undefined) {
2111
+ fail(`invariant '${invariant.id}' refusal targets unknown nominal type '${invariant.refusal.name}'`);
2112
+ }
1915
2113
  const point = invariant.__nomosPointGuard;
1916
2114
  const pointAggregate = point === undefined ? undefined : aggregateByLabel.get(point.aggregateId)?.node;
1917
2115
  if (point !== undefined && pointAggregate === undefined) {
@@ -1940,6 +2138,7 @@ export function discoverLawGraph(
1940
2138
  ),
1941
2139
  domain: domainRef,
1942
2140
  invariantKind: "workspace",
2141
+ ...(refusalType === undefined ? {} : { refusalType: ref(refusalType) }),
1943
2142
  on: intentRef(intentByLabel, invariant.on, `invariant '${invariant.id}'`),
1944
2143
  ...(nativeReads === undefined ? {} : { reads: nativeReads }),
1945
2144
  ...(invariant.__nomosEvaluator === undefined
@@ -1978,6 +2177,22 @@ export function discoverLawGraph(
1978
2177
  });
1979
2178
  }
1980
2179
 
2180
+ // Derive the output family closure from rules and composed steps. A tenant
2181
+ // cannot omit a reachable refusal by supplying a handwritten command list.
2182
+ const refusalTypesFor = (id: string, active = new Set<string>()): LawRef<"type">[] => {
2183
+ if (active.has(id)) fail(`cyclic refusal contract composition at '${id}'`);
2184
+ const next = new Set(active).add(id);
2185
+ const direct = invariantNodes.flatMap((rule) => rule.invariantKind === "workspace"
2186
+ && rule.on.id === id && rule.refusalType !== undefined ? [rule.refusalType] : []);
2187
+ const steps = intentNodes.find(node => node.entity.id === id)?.steps ?? [];
2188
+ return [...new Map([...direct, ...steps.flatMap(step => refusalTypesFor(step.id, next))]
2189
+ .map(type => [type.id, type])).values()].sort((a, b) => compareText(a.id, b.id));
2190
+ };
2191
+ for (const [index, node] of intentNodes.entries()) {
2192
+ const domainRefusals = refusalTypesFor(node.entity.id);
2193
+ if (domainRefusals.length > 0) intentNodes[index] = { ...node, domainRefusals };
2194
+ }
2195
+
1981
2196
  const capabilities = [...(module.impureCapabilities ?? [])].sort((a, b) =>
1982
2197
  compareText(a.aggregate.id, b.aggregate.id),
1983
2198
  );
@@ -1992,7 +2207,12 @@ export function discoverLawGraph(
1992
2207
  readonly name?: unknown;
1993
2208
  readonly capability?: unknown;
1994
2209
  readonly endpoint?: unknown;
2210
+ readonly execution?: unknown;
1995
2211
  };
2212
+ const execution = metadata.execution as {
2213
+ readonly placement?: unknown;
2214
+ readonly cardinality?: unknown;
2215
+ } | undefined;
1996
2216
  return {
1997
2217
  kind: "capability",
1998
2218
  entity: entity(
@@ -2014,6 +2234,12 @@ export function discoverLawGraph(
2014
2234
  ? metadata.capability
2015
2235
  : capability.aggregate.id,
2016
2236
  ...(typeof metadata.endpoint === "string" ? { endpoint: metadata.endpoint } : {}),
2237
+ ...(execution?.placement === "custodian"
2238
+ ? { executionPlacement: execution.placement }
2239
+ : {}),
2240
+ ...(execution?.cardinality === "singleton"
2241
+ ? { executionCardinality: execution.cardinality }
2242
+ : {}),
2017
2243
  };
2018
2244
  });
2019
2245
 
@@ -2207,6 +2433,9 @@ export function discoverLawGraph(
2207
2433
  of: readAggregate,
2208
2434
  contract: ref(readContractEntity),
2209
2435
  public: read.public,
2436
+ ...(read.authentication === undefined
2437
+ ? {}
2438
+ : { authentication: read.authentication }),
2210
2439
  ...(predicate === undefined ? {} : { predicate }),
2211
2440
  };
2212
2441
  switch (read.readKind) {
@@ -2238,6 +2467,8 @@ export function discoverLawGraph(
2238
2467
  filtered: read.filtered,
2239
2468
  };
2240
2469
  case "ordered":
2470
+ if (!Number.isInteger(read.limit) || read.limit < 1 || read.limit > 0xffff_ffff)
2471
+ throw new Error(`ordered read '${read.id}' needs an integer limit from 1 through 4294967295`);
2241
2472
  return {
2242
2473
  ...base,
2243
2474
  readKind: "ordered",
@@ -2353,13 +2584,23 @@ export function discoverLawGraph(
2353
2584
  parent: string,
2354
2585
  label: string,
2355
2586
  status?: "active" | "retired",
2356
- ) => {
2587
+ allowAbsent = false,
2588
+ ): [string, LawIdentityEntity] | undefined => {
2357
2589
  const matches = Object.entries(registry.entities).filter(([, candidate]) =>
2358
2590
  candidate.kind === kind &&
2359
2591
  candidate.parent === parent &&
2360
2592
  (status === undefined || candidate.status === status) &&
2361
2593
  (candidate.label === label || candidate.priorLabels?.includes(label) === true)
2362
2594
  );
2595
+ if (allowAbsent && matches.length === 0) return undefined;
2596
+ // Resolve the latest unambiguous declaration when a label has been reused.
2597
+ // Field tombstones below additionally retain its older retired generations
2598
+ // so one candidate can migrate frontiers from before that reuse.
2599
+ if (matches.length > 1) {
2600
+ const latest = Math.max(...matches.map(([, candidate]) => candidate.generation));
2601
+ const latestMatches = matches.filter(([, candidate]) => candidate.generation === latest);
2602
+ if (latestMatches.length === 1) return latestMatches[0]!;
2603
+ }
2363
2604
  if (matches.length !== 1) {
2364
2605
  fail(
2365
2606
  `${kind} retirement '${label}' resolves to ${matches.length} prior identities under '${parent}'; ` +
@@ -2368,42 +2609,61 @@ export function discoverLawGraph(
2368
2609
  }
2369
2610
  return matches[0]!;
2370
2611
  };
2371
- const retirements = (module.retirements ?? []).map((declaration): LawRetirement => {
2372
- const retirementParent = (module.aggregates ?? []).some(
2612
+ const retirements = (module.retirements ?? []).flatMap((declaration): LawRetirement[] => {
2613
+ const frameworkRetirement = (module.aggregates ?? []).some(
2373
2614
  (aggregate) => aggregate.id === declaration.aggregate && aggregate.framework === true,
2374
- )
2375
- ? LAW_FRAMEWORK_PARENT
2376
- : domainKey;
2377
- const [aggregateKey, aggregate] = identityEntity(
2615
+ );
2616
+ const retirementParent = frameworkRetirement ? LAW_FRAMEWORK_PARENT : domainKey;
2617
+ const aggregateIdentity = identityEntity(
2378
2618
  "aggregate",
2379
2619
  retirementParent,
2380
2620
  declaration.aggregate,
2381
2621
  declaration.kind === "aggregate" ? "retired" : undefined,
2622
+ frameworkRetirement,
2382
2623
  );
2624
+ // Framework evolution is composed into both established and greenfield
2625
+ // domains. A tombstone exists only when this identity layer actually has
2626
+ // the historical declaration; greenfield law never invents a past. Tenant
2627
+ // retirements remain strict so a typo cannot silently erase intent.
2628
+ if (aggregateIdentity === undefined) return [];
2629
+ const [aggregateKey, aggregate] = aggregateIdentity;
2383
2630
  if (declaration.kind === "aggregate") {
2384
- return {
2631
+ return [{
2385
2632
  kind: "aggregate",
2386
2633
  sid: aggregate.sid,
2387
2634
  label: aggregate.label,
2388
- };
2635
+ }];
2389
2636
  }
2390
- const [, field] = identityEntity(
2637
+ const fieldIdentity = identityEntity(
2391
2638
  "field",
2392
2639
  aggregateKey,
2393
2640
  declaration.field ?? "",
2394
2641
  "retired",
2642
+ frameworkRetirement,
2395
2643
  );
2396
- return {
2644
+ if (fieldIdentity === undefined) return [];
2645
+ // A candidate may replace any older frontier, not just the last compiler
2646
+ // generation. Keep every known tombstone for this explicitly retired
2647
+ // field; otherwise an older installed SID has no retirement authority.
2648
+ const retiredFields = Object.values(registry.entities).filter((field) =>
2649
+ field.kind === "field" && field.parent === aggregateKey && field.status === "retired" &&
2650
+ (field.label === declaration.field || field.priorLabels?.includes(declaration.field ?? "") === true)
2651
+ );
2652
+ return retiredFields.map((field) => ({
2397
2653
  kind: "field",
2398
2654
  sid: field.sid,
2399
2655
  label: field.label,
2400
2656
  parentSid: aggregate.sid,
2401
- };
2657
+ }));
2402
2658
  }).sort((left, right) => compareText(`${left.kind}:${left.sid}`, `${right.kind}:${right.sid}`));
2403
2659
  return { registry, graph: { v: 1, domain: domainNode, retirements, nodes } };
2404
2660
  }
2405
2661
 
2406
- type ReadInputBase = { id: string; public: boolean };
2662
+ type ReadInputBase = {
2663
+ id: string;
2664
+ public: boolean;
2665
+ authentication?: MemberAuthentication;
2666
+ };
2407
2667
  type ReadInput =
2408
2668
  | (ReadInputBase & { readKind: "query"; of: string; key: readonly string[] })
2409
2669
  | (ReadInputBase & { readKind: "count" | "exists"; of: string; by?: string; where?: CanonicalPred; filtered: boolean })
@@ -2442,34 +2702,41 @@ function readInputs(module: DomainModule): ReadInput[] {
2442
2702
  const isPublic = (value: unknown): boolean =>
2443
2703
  !(value && typeof value === "object" &&
2444
2704
  (value as { businessBirthInternal?: unknown }).businessBirthInternal === true);
2705
+ const authenticationOf = (value: unknown): MemberAuthentication | undefined =>
2706
+ (value as { authentication?: MemberAuthentication }).authentication;
2707
+ const member = (value: unknown) => {
2708
+ const authentication = authenticationOf(value);
2709
+ return authentication === undefined ? {} : { authentication };
2710
+ };
2445
2711
  const reads: ReadInput[] = [
2446
- ...(module.queries ?? []).map((read) => ({ id: read.id, public: isPublic(read), readKind: "query" as const, of: read.returns, key: read.key })),
2712
+ ...(module.queries ?? []).map((read) => ({ id: read.id, public: isPublic(read), ...member(read), readKind: "query" as const, of: read.returns, key: read.key })),
2447
2713
  ...(module.counts ?? []).map((value) => {
2448
2714
  const read = finishCount(value);
2449
- return { id: read.id, public: isPublic(value), readKind: "count" as const, of: read.of, ...(read.by === undefined ? {} : { by: read.by }), ...(read.where === undefined ? {} : { where: read.where }), filtered: read.where !== undefined };
2715
+ return { id: read.id, public: isPublic(value), ...member(value), readKind: "count" as const, of: read.of, ...(read.by === undefined ? {} : { by: read.by }), ...(read.where === undefined ? {} : { where: read.where }), filtered: read.where !== undefined };
2450
2716
  }),
2451
2717
  ...(module.exists_ ?? []).map((value) => {
2452
2718
  const read = finishExists(value);
2453
- return { id: read.id, public: isPublic(value), readKind: "exists" as const, of: read.of, ...(read.by === undefined ? {} : { by: read.by }), ...(read.where === undefined ? {} : { where: read.where }), filtered: read.where !== undefined };
2719
+ return { id: read.id, public: isPublic(value), ...member(value), readKind: "exists" as const, of: read.of, ...(read.by === undefined ? {} : { by: read.by }), ...(read.where === undefined ? {} : { where: read.where }), filtered: read.where !== undefined };
2454
2720
  }),
2455
2721
  ...(module.sums ?? []).map((value) => {
2456
2722
  const read = finishSum(value);
2457
- return { id: read.id, public: isPublic(value), readKind: "sum" as const, of: read.of, value: read.sumField, ...(read.by === undefined ? {} : { by: read.by }), ...(read.where === undefined ? {} : { where: read.where }), filtered: read.where !== undefined };
2723
+ return { id: read.id, public: isPublic(value), ...member(value), readKind: "sum" as const, of: read.of, value: read.sumField, ...(read.by === undefined ? {} : { by: read.by }), ...(read.where === undefined ? {} : { where: read.where }), filtered: read.where !== undefined };
2458
2724
  }),
2459
2725
  ...(module.mins ?? []).map((value) => {
2460
2726
  const read = finishExtremum(value);
2461
- return { id: read.id, public: isPublic(value), readKind: "min" as const, of: read.of, value: read.valueField, ...(read.by === undefined ? {} : { by: read.by }), ...(read.where === undefined ? {} : { where: read.where }), filtered: read.where !== undefined };
2727
+ return { id: read.id, public: isPublic(value), ...member(value), readKind: "min" as const, of: read.of, value: read.valueField, ...(read.by === undefined ? {} : { by: read.by }), ...(read.where === undefined ? {} : { where: read.where }), filtered: read.where !== undefined };
2462
2728
  }),
2463
2729
  ...(module.maxes ?? []).map((value) => {
2464
2730
  const read = finishExtremum(value);
2465
- return { id: read.id, public: isPublic(value), readKind: "max" as const, of: read.of, value: read.valueField, ...(read.by === undefined ? {} : { by: read.by }), ...(read.where === undefined ? {} : { where: read.where }), filtered: read.where !== undefined };
2731
+ return { id: read.id, public: isPublic(value), ...member(value), readKind: "max" as const, of: read.of, value: read.valueField, ...(read.by === undefined ? {} : { by: read.by }), ...(read.where === undefined ? {} : { where: read.where }), filtered: read.where !== undefined };
2466
2732
  }),
2467
- ...(module.orderedReads ?? []).map((read) => ({ id: read.id, public: isPublic(read), readKind: "ordered" as const, of: read.of, orderBy: read.orderKey, descending: read.orderDesc, limit: read.limit, ...(read.where === undefined ? {} : { where: read.where }), filtered: read.where !== undefined })),
2468
- ...(module.spatials ?? []).map((read) => ({ id: read.id, public: isPublic(read), readKind: "spatial" as const, of: read.of, geometry: read.on })),
2733
+ ...(module.orderedReads ?? []).map((read) => ({ id: read.id, public: isPublic(read), ...member(read), readKind: "ordered" as const, of: read.of, orderBy: read.orderKey, descending: read.orderDesc, limit: read.limit, ...(read.where === undefined ? {} : { where: read.where }), filtered: read.where !== undefined })),
2734
+ ...(module.spatials ?? []).map((read) => ({ id: read.id, public: isPublic(read), ...member(read), readKind: "spatial" as const, of: read.of, geometry: read.on })),
2469
2735
  ...(module.deriveds ?? []).map((read) => "kind" in read && read.kind === "temporalEnum"
2470
2736
  ? {
2471
2737
  id: read.id,
2472
2738
  public: isPublic(read),
2739
+ ...member(read),
2473
2740
  readKind: "temporalEnum" as const,
2474
2741
  of: read.of,
2475
2742
  sourceField: read.sourceField,
@@ -2478,8 +2745,8 @@ function readInputs(module: DomainModule): ReadInput[] {
2478
2745
  transitions: read.transitions,
2479
2746
  returns: read.returns,
2480
2747
  }
2481
- : { id: read.id, public: isPublic(read), readKind: "derived" as const, of: read.of, returns: read.returns }),
2482
- ...(module.combineds ?? []).map((read) => ({ id: read.id, public: isPublic(read), readKind: "combined" as const, of: read.of, via: read.refField, reads: read.reads, returns: read.returns })),
2748
+ : { id: read.id, public: isPublic(read), ...member(read), readKind: "derived" as const, of: read.of, returns: read.returns }),
2749
+ ...(module.combineds ?? []).map((read) => ({ id: read.id, public: isPublic(read), ...member(read), readKind: "combined" as const, of: read.of, via: read.refField, reads: read.reads, returns: read.returns })),
2483
2750
  ];
2484
2751
  return reads.sort((a, b) => compareText(a.id, b.id));
2485
2752
  }
@@ -2556,7 +2823,20 @@ function readResult(read: ReadInput, aggregate: LawRef<"aggregate">): RawLawRead
2556
2823
 
2557
2824
  function storedFieldValue(field: Field): LawValueType {
2558
2825
  if (field.kind === "json") return { kind: "unknown" };
2559
- if (field.kind === "ref" || field.kind === "position") {
2826
+ if (field.kind === "ref") {
2827
+ // A same-workspace or sibling-workspace reference is stored as its stable
2828
+ // id string. Atomic and versioned Nomos references are different lawful
2829
+ // values: their workspace/type/id address (or pinned/series pair) is one
2830
+ // indivisible value and must remain structured in the law graph. Lowering
2831
+ // every ref to string made the generated transport disagree with the
2832
+ // generated public type and deferred that contradiction until tenant
2833
+ // application compilation.
2834
+ if (field.nomosRef !== undefined || field.versionedForeignRef !== undefined) {
2835
+ return lowerLawValueType(field.zod);
2836
+ }
2837
+ return { kind: "string", constraints: [] };
2838
+ }
2839
+ if (field.kind === "position") {
2560
2840
  return { kind: "string", constraints: [] };
2561
2841
  }
2562
2842
  if (field.kind === "counter") return { kind: "integer", constraints: [] };
@@ -2638,30 +2918,167 @@ function linkStoredField(
2638
2918
  };
2639
2919
  }
2640
2920
 
2641
- function descriptorValue(field: ValueObjectFieldDescriptor): LawValueType {
2642
- switch (field.type) {
2643
- case "string":
2644
- return { kind: "string", constraints: [] };
2921
+ function descriptorTypeValue(
2922
+ descriptor: ValueObjectDescriptor,
2923
+ byName: ReadonlyMap<string, ValueObjectDescriptor>,
2924
+ active: ReadonlySet<string>,
2925
+ ): LawValueType {
2926
+ if (active.has(descriptor.name)) {
2927
+ fail(`value object '${descriptor.name}' recursively contains itself without a declared recursive type`);
2928
+ }
2929
+ const next = new Set(active).add(descriptor.name);
2930
+ const semantic: LawValueSemantic = {
2931
+ kind: "value-object", name: descriptor.name,
2932
+ ...(descriptor.geometry === undefined ? {} : { geometry: descriptor.geometry }),
2933
+ };
2934
+ if (descriptor.geometry !== undefined) {
2935
+ const scalar: LawValueType = { kind: "number", constraints: [] };
2936
+ const position: LawValueType = { kind: "list", element: scalar };
2937
+ const coordinates: LawValueType = descriptor.geometry === "Point"
2938
+ ? position
2939
+ : descriptor.geometry === "Polygon"
2940
+ ? { kind: "list", element: { kind: "list", element: position } }
2941
+ : { kind: "list", element: position };
2942
+ return {
2943
+ kind: "record",
2944
+ semantic,
2945
+ fields: [
2946
+ {
2947
+ name: "type",
2948
+ required: true,
2949
+ value: { kind: "literal", value: descriptor.geometry },
2950
+ },
2951
+ { name: "coordinates", required: true, value: coordinates },
2952
+ ],
2953
+ };
2954
+ }
2955
+ if (descriptor.union !== undefined) {
2956
+ return {
2957
+ kind: "sum",
2958
+ semantic,
2959
+ options: Object.entries(descriptor.union)
2960
+ .sort(([a], [b]) => compareText(a, b))
2961
+ .map(([discriminator, arm]) => {
2962
+ const fields: LawValueMember[] = [{
2963
+ name: "type",
2964
+ required: true,
2965
+ value: { kind: "literal", value: discriminator },
2966
+ }];
2967
+ if ("ref" in arm) {
2968
+ const target = byName.get(arm.ref);
2969
+ if (target === undefined) {
2970
+ fail(`value object '${descriptor.name}' references unknown type '${arm.ref}'`);
2971
+ }
2972
+ // Named union arms are deliberately nested under `value`; this is
2973
+ // the authored value-object wire shape, not a flattened record.
2974
+ fields.push({
2975
+ name: "value",
2976
+ required: true,
2977
+ value: descriptorTypeValue(target, byName, next),
2978
+ });
2979
+ } else {
2980
+ fields.push(...Object.entries(arm.fields)
2981
+ .sort(([a], [b]) => compareText(a, b))
2982
+ .map(([name, field]) => ({
2983
+ name,
2984
+ required: field.optional !== true,
2985
+ value: descriptorValue(
2986
+ field,
2987
+ byName,
2988
+ next,
2989
+ `${descriptor.name}${discriminator[0]!.toUpperCase()}${discriminator.slice(1)}`,
2990
+ name,
2991
+ ),
2992
+ })));
2993
+ }
2994
+ return {
2995
+ kind: "record" as const,
2996
+ fields,
2997
+ };
2998
+ }),
2999
+ };
3000
+ }
3001
+ return {
3002
+ kind: "record",
3003
+ semantic,
3004
+ fields: Object.entries(descriptor.fields ?? {})
3005
+ .sort(([a], [b]) => compareText(a, b))
3006
+ .map(([name, field]) => ({
3007
+ name,
3008
+ required: field.optional !== true,
3009
+ value: descriptorValue(field, byName, next, descriptor.name, name),
3010
+ })),
3011
+ };
3012
+ }
3013
+
3014
+ function descriptorValue(
3015
+ field: ValueObjectFieldDescriptor,
3016
+ byName: ReadonlyMap<string, ValueObjectDescriptor>,
3017
+ active: ReadonlySet<string> = new Set(),
3018
+ ownerName?: string,
3019
+ fieldName?: string,
3020
+ ): LawValueType {
3021
+ const value: LawValueType = (() => {
3022
+ switch (field.type) {
3023
+ case "string": {
3024
+ const semantic: LawValueSemantic | undefined = field.digest === "sha256"
3025
+ ? { kind: "sha256-digest" }
3026
+ : field.idOf !== undefined
3027
+ ? {
3028
+ kind: "id-reference",
3029
+ target: field.idOf,
3030
+ targetKind: field.idOfContract === true ? "contract" : "aggregate",
3031
+ }
3032
+ : field.brand !== undefined
3033
+ ? { kind: "branded-id", name: field.brand }
3034
+ : undefined;
3035
+ return {
3036
+ kind: "string",
3037
+ constraints: [],
3038
+ ...(semantic === undefined ? {} : { semantic }),
3039
+ };
3040
+ }
2645
3041
  case "number":
2646
- return { kind: field.int === true ? "integer" : "number", constraints: [] };
3042
+ return {
3043
+ kind: field.int === true ? "integer" : "number",
3044
+ constraints: [],
3045
+ ...(field.unit === undefined
3046
+ ? {}
3047
+ : { semantic: { kind: "unit", name: field.unit } as const }),
3048
+ };
2647
3049
  case "boolean":
2648
3050
  return { kind: "boolean" };
2649
3051
  case "enum":
2650
3052
  return {
2651
3053
  kind: "enum",
2652
3054
  values: [...field.values].sort(compareText),
2653
- ...(field.name === undefined ? {} : { name: field.name }),
3055
+ ...(field.name !== undefined
3056
+ ? { name: field.name }
3057
+ : ownerName !== undefined && fieldName !== undefined
3058
+ ? { name: `${ownerName}${fieldName[0]!.toUpperCase()}${fieldName.slice(1)}` }
3059
+ : {}),
2654
3060
  };
2655
3061
  case "literal":
2656
3062
  return { kind: "literal", value: field.value };
2657
3063
  case "array":
2658
- return { kind: "list", element: descriptorValue(field.items) };
3064
+ return {
3065
+ kind: "list",
3066
+ element: descriptorValue(field.items, byName, active, ownerName, fieldName),
3067
+ };
2659
3068
  case "map":
2660
- return { kind: "map", value: descriptorValue(field.values) };
3069
+ return {
3070
+ kind: "map",
3071
+ value: descriptorValue(field.values, byName, active, ownerName, fieldName),
3072
+ };
2661
3073
  case "object":
2662
- case "geometry":
2663
- return { kind: "unknown" };
2664
- }
3074
+ case "geometry": {
3075
+ const target = byName.get(field.ref);
3076
+ if (target === undefined) fail(`value object field references unknown type '${field.ref}'`);
3077
+ return descriptorTypeValue(target, byName, active);
3078
+ }
3079
+ }
3080
+ })();
3081
+ return field.nullable === true ? { kind: "nullable", value } : value;
2665
3082
  }
2666
3083
 
2667
3084
  function mergeDrivers(driver: Driver): LawMergeDriver[] {
@@ -2823,12 +3240,22 @@ export function lowerAnonymousValueGraph<K extends LawEntityKind>(
2823
3240
  }
2824
3241
  case "sum": {
2825
3242
  const variants = value.options.map((option) => {
3243
+ const discriminator = option.kind === "record"
3244
+ ? option.fields.find((field) =>
3245
+ field.name === "type" &&
3246
+ field.required &&
3247
+ field.value.kind === "literal" &&
3248
+ typeof field.value.value === "string")?.value
3249
+ : undefined;
3250
+ const label = discriminator?.kind === "literal" && typeof discriminator.value === "string"
3251
+ ? discriminator.value
3252
+ : undefined;
2826
3253
  const child = lower(option);
2827
3254
  const variantEntity = structuralEntity(
2828
3255
  owner,
2829
3256
  "value-variant",
2830
- { parent: entity.id, value: child.id },
2831
- `${owner.label}#${child.id}`,
3257
+ { parent: entity.id, ...(label === undefined ? {} : { label }), value: child.id },
3258
+ `${owner.label}#${label ?? child.id}`,
2832
3259
  );
2833
3260
  if (!nodes.has(variantEntity.id)) {
2834
3261
  nodes.set(variantEntity.id, {
@@ -2836,6 +3263,7 @@ export function lowerAnonymousValueGraph<K extends LawEntityKind>(
2836
3263
  structuralKind: "value-variant",
2837
3264
  entity: variantEntity,
2838
3265
  owner: structuralRef(entity),
3266
+ ...(label === undefined ? {} : { label }),
2839
3267
  value: child,
2840
3268
  });
2841
3269
  }
@@ -2866,6 +3294,10 @@ export function lowerAnonymousValueGraph<K extends LawEntityKind>(
2866
3294
  export function lowerAnonymousContractFields(
2867
3295
  source: LawValueType,
2868
3296
  owner: LawEntity<"contract">,
3297
+ wireForField: (
3298
+ name: string,
3299
+ structuralSid: string,
3300
+ ) => LawOperationFieldWire["protobuf"] = (_name, structuralSid) => ({ canonical: structuralSid }),
2869
3301
  ): { readonly fields: readonly LawStructuralRef<"value-field">[]; readonly nodes: readonly LawAnonymousValueNode[] } {
2870
3302
  const value = canonicalValueType(source);
2871
3303
  if (value.kind !== "record") {
@@ -2881,10 +3313,15 @@ export function lowerAnonymousContractFields(
2881
3313
  { name: member.name, required: member.required, value: lowered.root.id },
2882
3314
  `${owner.label}#${member.name}`,
2883
3315
  );
3316
+ const wire = wireForField(member.name, entity.sid);
2884
3317
  nodes.set(entity.id, {
2885
3318
  kind: "structural",
2886
3319
  structuralKind: "value-field",
2887
- entity,
3320
+ entity: {
3321
+ ...entity,
3322
+ wireKey: wire.canonical,
3323
+ ...(wire.aliases === undefined ? {} : { wireAliases: wire.aliases }),
3324
+ },
2888
3325
  owner: ref(owner),
2889
3326
  name: member.name,
2890
3327
  required: member.required,