@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
@@ -27,6 +27,7 @@ import type {
27
27
  LawWorkspaceTypeNode,
28
28
  LawWorkspaceRelationNode,
29
29
  } from "./law_graph.js";
30
+ import { MemberAuthenticationKind, type AuthenticationProviderRef } from "./member_authentication.js";
30
31
 
31
32
  const FRAMEWORK_SCALAR: Readonly<Record<string, string>> = {
32
33
  string: "/Nomos/Framework/Types/t_0000000000000001",
@@ -35,10 +36,16 @@ const FRAMEWORK_SCALAR: Readonly<Record<string, string>> = {
35
36
  };
36
37
 
37
38
  const FRAMEWORK_RELATIONS = [
38
- { key: "workspace#admin", id: "r_0000000000000001", object: "o_0000000000000001", subjects: ["user", "key"] },
39
+ // admin and installDomain also admit the framework's console and custodian principals: token-free
40
+ // genesis (D22) seats a `console:`/`custodian:` founder as admin and as model authority. These two
41
+ // scopes are framework vocabulary (`member_authentication.ts`); no tenant scope is ever named here.
42
+ // This layer is the framework unit every law composes with: changing it moves the unit, so it lands
43
+ // only together with a stage-zero law refreshed through `npm run prepare:kernel` (the candidate's
44
+ // companion and nomos_flutter/assets/nomos_kernel/bootstrap.law.usdz), never alone.
45
+ { key: "workspace#admin", id: "r_0000000000000001", object: "o_0000000000000001", subjects: ["user", "key", "console", "custodian"] },
39
46
  { key: "workspace#creator", id: "r_0000000000000002", object: "o_0000000000000001", subjects: ["user", "key"] },
40
47
  { key: "workspace#platformCreator", id: "r_0000000000000003", object: "o_0000000000000001", subjects: ["user", "key"] },
41
- { key: "workspace#installDomain", id: "r_0000000000000004", object: "o_0000000000000001", subjects: ["user", "key"] },
48
+ { key: "workspace#installDomain", id: "r_0000000000000004", object: "o_0000000000000001", subjects: ["user", "key", "console", "custodian"] },
42
49
  { key: "identity#signer", id: "r_0000000000000005", object: "o_0000000000000002", subjects: ["key"] },
43
50
  ] as const;
44
51
 
@@ -146,6 +153,22 @@ export function emitFrameworkLawUsda(): string {
146
153
  }
147
154
  type ResolveDependency = (dependency: LawDependencyRef<any>) => string;
148
155
 
156
+ /**
157
+ * A member's declared provider (identity target model D7, D22). An enrolled or sovereign provider is
158
+ * its kind and scope and nothing else: no issuer, key or token is law. The legacy digest form keeps
159
+ * today's scope/identity pair, which the kernel still requires paired.
160
+ */
161
+ function authenticationProviderOpinions(provider: AuthenticationProviderRef, pad: string): string[] {
162
+ const scope = `${pad}custom uniform string nomos:authenticationProviderScope = ${quote(provider.scope)}`;
163
+ switch (provider.kind) {
164
+ case "legacySelected":
165
+ return [scope, `${pad}custom uniform string nomos:authenticationProviderIdentity = ${quote(provider.identitySha256)}`];
166
+ case "enrolled":
167
+ case "sovereign":
168
+ return [`${pad}custom uniform token nomos:authenticationProviderKind = ${quote(provider.kind)}`, scope];
169
+ }
170
+ }
171
+
149
172
  function entityLabelOpinions(
150
173
  entity: { readonly label: string; readonly priorLabels: readonly string[] },
151
174
  indent: number,
@@ -165,6 +188,8 @@ function entityLabelOpinions(
165
188
  export interface CompiledSparseLawGraphUsda {
166
189
  readonly text: string;
167
190
  readonly paths: ReadonlyMap<string, string>;
191
+ /** The exact same nominal export resolution used while emitting relationship targets. */
192
+ readonly resolveDependency: ResolveDependency;
168
193
  }
169
194
 
170
195
  /** Render one sparse law layer and retain its exact identity-path projection for client codegen. */
@@ -304,10 +329,10 @@ export function compileSparseLawGraphUsda(
304
329
  candidates = dependencyPaths.get(`*:${dependency.entityKind}:${dependency.label}`) ?? [];
305
330
  }
306
331
  if (candidates.length === 0) {
307
- fail(`no closure path resolves dependency '${dependency.domain ?? "?"}:${dependency.entityKind}:${dependency.label}'`);
332
+ fail(`domain '${graph.domain.entity.label}' has no closure path resolving dependency '${dependency.domain ?? "?"}:${dependency.entityKind}:${dependency.label}'`);
308
333
  }
309
334
  if (candidates.length > 1) {
310
- fail(`dependency '${dependency.domain ?? "?"}:${dependency.entityKind}:${dependency.label}' is ambiguous in the closure`);
335
+ fail(`domain '${graph.domain.entity.label}' dependency '${dependency.domain ?? "?"}:${dependency.entityKind}:${dependency.label}' is ambiguous in the closure`);
311
336
  }
312
337
  return candidates[0]!;
313
338
  };
@@ -437,8 +462,12 @@ export function compileSparseLawGraphUsda(
437
462
  }
438
463
  case "nullable":
439
464
  case "default": {
440
- // These wrappers already resolve to their carried type rather than owning a USDA prim.
441
- key = childKey(node.expression.value.id);
465
+ // Wrappers do not own prims, but their meaning must survive structural
466
+ // interning. A nullable field is not the same contract as a non-null field.
467
+ const child = childKey(node.expression.value.id);
468
+ if (child !== undefined) key = JSON.stringify(node.expression.kind === "nullable"
469
+ ? ["nullable", child]
470
+ : ["default", child, node.expression.defaultValue]);
442
471
  break;
443
472
  }
444
473
  case "record": {
@@ -450,19 +479,25 @@ export function compileSparseLawGraphUsda(
450
479
  }
451
480
  if (members.length === (anonymousFieldsByOwner.get(id)?.length ?? 0)) {
452
481
  members.sort(([left], [right]) => left.localeCompare(right));
453
- key = JSON.stringify(["record", members]);
482
+ key = JSON.stringify(["record", members,
483
+ node.expression.semantic?.kind === "value-object" ? node.expression.semantic.geometry : undefined]);
454
484
  }
455
485
  break;
456
486
  }
457
487
  case "sum": {
458
- const members: string[] = [];
488
+ const members: Array<readonly [string, string]> = [];
459
489
  for (const variant of anonymousVariantsByOwner.get(id) ?? []) {
460
490
  const child = childKey(variant.value.id);
461
491
  if (child === undefined) break;
462
- members.push(child);
492
+ // A union arm's discriminator is part of its wire contract. Two sums
493
+ // with the same carried value shapes but different arm labels cannot
494
+ // share one USDA prim: authoring reconstructs the nominal oneof from
495
+ // these labels before it decodes the generated Protobuf call.
496
+ members.push([variant.label ?? variant.entity.label, child]);
463
497
  }
464
498
  if (members.length === (anonymousVariantsByOwner.get(id)?.length ?? 0)) {
465
- members.sort();
499
+ members.sort(([leftLabel, leftChild], [rightLabel, rightChild]) =>
500
+ leftLabel.localeCompare(rightLabel) || leftChild.localeCompare(rightChild));
466
501
  key = JSON.stringify(["sum", members]);
467
502
  }
468
503
  break;
@@ -547,14 +582,16 @@ export function compileSparseLawGraphUsda(
547
582
  for (const variant of anonymousVariantsByOwner.get(canonicalId) ?? []) {
548
583
  const child = semanticTypeKey(variant.value.id);
549
584
  if (child === undefined) continue;
550
- const matches = canonicalVariants.get(child) ?? [];
585
+ const key = JSON.stringify([variant.label ?? variant.entity.label, child]);
586
+ const matches = canonicalVariants.get(key) ?? [];
551
587
  matches.push(variant);
552
- canonicalVariants.set(child, matches);
588
+ canonicalVariants.set(key, matches);
553
589
  }
554
590
  for (const variant of anonymousVariantsByOwner.get(duplicateId) ?? []) {
555
591
  const child = semanticTypeKey(variant.value.id);
556
592
  if (child === undefined) continue;
557
- const canonical = canonicalVariants.get(child)?.shift();
593
+ const key = JSON.stringify([variant.label ?? variant.entity.label, child]);
594
+ const canonical = canonicalVariants.get(key)?.shift();
558
595
  if (canonical !== undefined) {
559
596
  pathById.set(variant.entity.id, requiredPath(canonical.entity.id, pathById));
560
597
  }
@@ -631,14 +668,17 @@ export function compileSparseLawGraphUsda(
631
668
  `${" ".repeat(indent)}custom uniform token nomos:objectNamespace = ${quote(objectType.namespace)}`,
632
669
  ], undefined, () => "NomosObjectType");
633
670
  }
634
- if (contracts.length > 0 || payloadContracts.length > 0) {
671
+ if (contracts.length > 0 || payloadContracts.length > 0 || readContracts.length > 0) {
635
672
  lines.push("");
636
- const emittedContracts: Array<LawContractNode | LawPayloadContractNode> = [...contracts, ...payloadContracts]
637
- .sort((a, b) => a.entity.id.localeCompare(b.entity.id));
673
+ const emittedContracts: Array<LawContractNode | LawPayloadContractNode | LawReadContractNode> =
674
+ [...contracts, ...payloadContracts, ...readContracts]
675
+ .sort((a, b) => a.entity.id.localeCompare(b.entity.id));
638
676
  emitContainer(lines, "Contracts", emittedContracts, 12, (contract, indent) =>
639
677
  contract.kind === "contract"
640
678
  ? emitContract(contract, fields, pathById, resolveDependency, indent)
641
- : emitPayloadContract(contract, anonymousFields, anonymousTypeById, pathById, indent));
679
+ : contract.kind === "payload-contract"
680
+ ? emitPayloadContract(contract, anonymousFields, anonymousTypeById, pathById, indent)
681
+ : emitReadContract(contract, pathById, indent));
642
682
  }
643
683
  if (relations.length > 0) {
644
684
  lines.push("");
@@ -654,7 +694,7 @@ export function compileSparseLawGraphUsda(
654
694
  if (reads.length > 0) {
655
695
  lines.push("");
656
696
  emitContainer(lines, "Reads", reads, 12, (read, indent) =>
657
- emitRead(read, readContracts, readPredicates, pathById, indent));
697
+ emitRead(read, readContracts, readPredicates, anonymousTypeById, pathById, indent));
658
698
  }
659
699
  if (policies.length > 0) {
660
700
  lines.push("");
@@ -683,7 +723,7 @@ export function compileSparseLawGraphUsda(
683
723
  (flow) => `w_${flow.entity.sid}`);
684
724
  }
685
725
  lines.push(" }", " }", "}", "");
686
- return { text: lines.join("\n"), paths: new Map(pathById) };
726
+ return { text: lines.join("\n"), paths: new Map(pathById), resolveDependency };
687
727
  }
688
728
 
689
729
  /** First W4.4 vertical slice: render domain, aggregate, stored-field and reusable-contract opinions. */
@@ -769,7 +809,7 @@ function emitPolicy(
769
809
  if (policy.keyHashes.length > 0) {
770
810
  body.push(`${pad}custom uniform string[] nomos:keyHashes = ${stringList(policy.keyHashes)}`);
771
811
  }
772
- } else {
812
+ } else if (policy.policyKind === "source-set") {
773
813
  body.push(
774
814
  `${pad}custom rel nomos:allowList = <${requiredPath(policy.allowList.id, paths)}>`,
775
815
  `${pad}custom rel nomos:genesisField = <${requiredPath(policy.genesisField.id, paths)}>`,
@@ -793,6 +833,9 @@ function emitInvariant(
793
833
  ? [`${pad}custom uniform token nomos:evaluator = ${quote(invariant.evaluator)}`]
794
834
  : []),
795
835
  ];
836
+ if (invariant.invariantKind === "workspace" && invariant.refusalType !== undefined) {
837
+ body.push(`${pad}custom rel nomos:refusalType = <${requiredPath(invariant.refusalType.id, paths)}>`);
838
+ }
796
839
  if (invariant.invariantKind === "workspace" && invariant.reads !== undefined) {
797
840
  body.push(`${pad}custom uniform token nomos:readEvaluator = "point-v1"`);
798
841
  for (const [index, read] of invariant.reads.entries()) {
@@ -916,6 +959,12 @@ function emitCapability(
916
959
  ...(capability.endpoint === undefined
917
960
  ? []
918
961
  : [`${pad}custom uniform asset nomos:endpoint = @${capability.endpoint.replaceAll("@", "@@")}@`]),
962
+ ...(capability.executionPlacement === undefined
963
+ ? []
964
+ : [`${pad}custom uniform token nomos:executionPlacement = ${quote(capability.executionPlacement)}`]),
965
+ ...(capability.executionCardinality === undefined
966
+ ? []
967
+ : [`${pad}custom uniform token nomos:executionCardinality = ${quote(capability.executionCardinality)}`]),
919
968
  `${pad}custom rel nomos:aggregate = <${requiredPath(capability.task.id, paths)}>`,
920
969
  `${pad}custom rel nomos:obligation = <${requiredPath(capability.obligation.id, paths)}>`,
921
970
  `${pad}custom rel nomos:complete = <${requiredPath(capability.complete.id, paths)}>`,
@@ -989,18 +1038,38 @@ function emitIntent(
989
1038
  ...(intent.acceptsVerifiedBirthParentRecovery
990
1039
  ? [`${pad}custom uniform token nomos:workspaceEffectAdmission = "VerifiedBirthParentRecovery"`]
991
1040
  : []),
992
- ...(intent.offerAuthenticationProof === "enrolledDevice"
1041
+ ...(intent.acceptsVerifiedWorkspaceClaim
1042
+ ? [`${pad}custom uniform token nomos:workspaceEffectAdmission = "VerifiedWorkspaceClaim"`]
1043
+ : []),
1044
+ ...(intent.authentication?.kind === MemberAuthenticationKind.EnrolledDevice
993
1045
  ? [`${pad}custom uniform token nomos:offerAuthenticationProof = "EnrolledDevice"`]
994
1046
  : []),
1047
+ ...(intent.authentication?.kind === MemberAuthenticationKind.SelectedProvider
1048
+ ? authenticationProviderOpinions(intent.authentication.provider, pad)
1049
+ : []),
1050
+ // D22 sovereign self-certification: the kernel admits this intent's founding signer tuple iff the
1051
+ // signing key's sha256 is the captured principal's subject (and the payload's keyHash).
1052
+ ...(intent.selfCertifyingPrincipal === true
1053
+ ? [`${pad}custom uniform token nomos:selfCertifyingPrincipal = "true"`]
1054
+ : []),
995
1055
  `${pad}custom rel nomos:target = ${targets([intent.target], paths, resolveDependency)}`,
996
1056
  `${pad}custom rel nomos:accepts = <${requiredPath(intent.accepts.id, paths)}>`,
997
1057
  ...(intent.publicAccepts === undefined
998
1058
  ? []
999
1059
  : [`${pad}custom rel nomos:publicAccepts = <${requiredPath(intent.publicAccepts.id, paths)}>`]),
1000
1060
  ];
1061
+ if ((intent.domainRefusals?.length ?? 0) > 0) {
1062
+ body.push(`${pad}custom rel nomos:refusalTypes = [${intent.domainRefusals!.map(type => `<${requiredPath(type.id, paths)}>`).join(", ")}]`);
1063
+ }
1001
1064
  if (intent.reads.length > 0) {
1002
1065
  body.push(`${pad}custom rel nomos:reads = ${targets(intent.reads, paths, resolveDependency)}`);
1003
1066
  }
1067
+ // A composed business intent executes its steps, so it depends on their whole law. The edge
1068
+ // is declared here rather than rediscovered by any consumer of the emitted law.
1069
+ if (intent.steps.length > 0) {
1070
+ body.push(`${pad}custom rel nomos:steps = ${relList(intent.steps
1071
+ .map((step) => `<${requiredPath(step.id, paths)}>`))}`);
1072
+ }
1004
1073
  if ((intent.directsWorkspaceEffects ?? []).length > 0) {
1005
1074
  body.push(
1006
1075
  `${pad}custom rel nomos:directsWorkspaceEffect = ${targets(intent.directsWorkspaceEffects, paths, resolveDependency)}`,
@@ -1017,6 +1086,11 @@ function emitIntent(
1017
1086
  `${pad}custom rel nomos:requiresObjectIdField = <${requiredPath(intent.authorization.objectFrom.idField.id, paths)}>`,
1018
1087
  );
1019
1088
  }
1089
+ if (intent.authorization.subjectFromAttestedRead !== undefined) {
1090
+ body.push(
1091
+ `${pad}custom uniform token nomos:requiresSubjectFromAttestedRead = ${quote(intent.authorization.subjectFromAttestedRead)}`,
1092
+ );
1093
+ }
1020
1094
  }
1021
1095
  if (intent.requiresHostCapabilities.length > 0) {
1022
1096
  body.push(
@@ -1033,6 +1107,12 @@ function emitIntent(
1033
1107
  `${pad}custom uniform string nomos:birthPrefix = ${quote(intent.birthPrefix ?? "")}`,
1034
1108
  );
1035
1109
  }
1110
+ if (intent.birthDelegations !== undefined) {
1111
+ body.push(
1112
+ `${pad}custom rel nomos:birthDelegations = <${requiredPath(intent.birthDelegations.id, paths)}>`,
1113
+ `${pad}custom uniform string nomos:birthDelegationPrefix = ${quote(intent.birthDelegationPrefix ?? "")}`,
1114
+ );
1115
+ }
1036
1116
  if (intent.birthPolicySeed !== undefined) {
1037
1117
  body.push(
1038
1118
  `${pad}custom rel nomos:birthPolicySeed = <${requiredPath(intent.birthPolicySeed.id, paths)}>`,
@@ -1166,6 +1246,7 @@ function emitRead(
1166
1246
  read: LawReadNode,
1167
1247
  contracts: readonly LawReadContractNode[],
1168
1248
  predicates: readonly LawPredicateNode[],
1249
+ types: ReadonlyMap<string, LawAnonymousValueTypeNode>,
1169
1250
  paths: ReadonlyMap<string, string>,
1170
1251
  indent: number,
1171
1252
  ): string[] {
@@ -1181,13 +1262,25 @@ function emitRead(
1181
1262
  `${pad}custom uniform bool nomos:public = ${read.public ?? false}`,
1182
1263
  `${pad}custom rel nomos:of = <${requiredPath(read.of.id, paths)}>`,
1183
1264
  `${pad}custom rel nomos:returns = <${requiredPath(returns, paths)}>`,
1265
+ `${pad}custom rel nomos:contract = <${requiredPath(contract.entity.id, paths)}>`,
1266
+ ...(read.authentication?.kind === MemberAuthenticationKind.SelectedProvider
1267
+ ? authenticationProviderOpinions(read.authentication.provider, pad)
1268
+ : []),
1184
1269
  ];
1270
+ if (contract.returns.kind === "scalar") {
1271
+ let type = types.get(returns);
1272
+ while (type?.expression.kind === "default") type = types.get(type.expression.value.id);
1273
+ if (type?.expression.kind === "nullable") body.push(`${pad}custom uniform bool nomos:nullable = true`);
1274
+ }
1185
1275
  const fieldTarget = (field: { readonly field: { readonly id: string }; readonly path: readonly { readonly id: string }[] }): string => {
1186
1276
  const terminal = field.path[field.path.length - 1] ?? field.field;
1187
1277
  return `<${requiredPath(terminal.id, paths)}>`;
1188
1278
  };
1189
1279
  switch (read.readKind) {
1190
1280
  case "query":
1281
+ // Interpretation is part of law identity. Never silently change historical captured
1282
+ // witnesses when the kernel gains rename-aware domain reads.
1283
+ body.push(`${pad}custom uniform token nomos:capturedReadSemantics = "current-labels-v1"`);
1191
1284
  body.push(`${pad}custom rel nomos:key = ${relList(read.key.map(fieldTarget))}`);
1192
1285
  body.push(`${pad}custom uniform string[] nomos:keyPath = ${stringList(read.key.map((field) => field.authoredPath))}`);
1193
1286
  break;
@@ -1435,6 +1528,48 @@ function emitPayloadContract(
1435
1528
  return body;
1436
1529
  }
1437
1530
 
1531
+ /** A read's signature: its parameters and its result. The read names it with `nomos:contract`.
1532
+ * Parameter value types (a spatial read's bounds union, for example) are declared nowhere else,
1533
+ * so without this prim nothing in the emitted law would say the read depends on them. */
1534
+ function emitReadContract(
1535
+ contract: LawReadContractNode,
1536
+ paths: ReadonlyMap<string, string>,
1537
+ indent: number,
1538
+ ): string[] {
1539
+ const pad = " ".repeat(indent);
1540
+ const returns = contract.returns.kind === "rows"
1541
+ ? contract.returns.aggregate.id
1542
+ : contract.returns.value.id;
1543
+ const body = [
1544
+ ...entityLabelOpinions(contract.entity, indent),
1545
+ `${pad}custom rel nomos:returns = <${requiredPath(returns, paths)}>`,
1546
+ ];
1547
+ if (contract.parameters.length > 0) {
1548
+ body.push("", `${pad}def Scope "Parameters"`, `${pad}{`);
1549
+ for (const [index, parameter] of contract.parameters.entries()) {
1550
+ if (index > 0) body.push("");
1551
+ body.push(
1552
+ `${pad} def Scope "Parameter_${String(index).padStart(4, "0")}"`,
1553
+ `${pad} {`,
1554
+ `${pad} custom uniform string nomos:name = ${quote(parameter.name)}`,
1555
+ );
1556
+ if (parameter.field !== undefined) {
1557
+ const terminal = parameter.field.path[parameter.field.path.length - 1] ?? parameter.field.field;
1558
+ body.push(
1559
+ `${pad} custom rel nomos:field = <${requiredPath(terminal.id, paths)}>`,
1560
+ `${pad} custom uniform string nomos:fieldPath = ${quote(parameter.field.authoredPath)}`,
1561
+ );
1562
+ }
1563
+ if (parameter.value !== undefined) {
1564
+ body.push(`${pad} custom rel nomos:type = <${requiredPath(parameter.value.id, paths)}>`);
1565
+ }
1566
+ body.push(`${pad} }`);
1567
+ }
1568
+ body.push(`${pad}}`);
1569
+ }
1570
+ return body;
1571
+ }
1572
+
1438
1573
  function emitType(
1439
1574
  type: LawTypeNode,
1440
1575
  fields: readonly LawFieldNode[],
@@ -1619,6 +1754,7 @@ function emitAnonymousType(
1619
1754
  body.push(
1620
1755
  `${pad} def Scope "v_${variant.entity.sid}"`,
1621
1756
  `${pad} {`,
1757
+ `${pad} custom uniform string nomos:label = ${quote(variant.label ?? variant.entity.label)}`,
1622
1758
  `${pad} custom rel nomos:type = <${requiredPath(variant.value.id, paths)}>`,
1623
1759
  `${pad} }`,
1624
1760
  );
@@ -1652,6 +1788,11 @@ function emitAnonymousValueSemantic(
1652
1788
  case "unit":
1653
1789
  case "value-object":
1654
1790
  body.push(`${pad}custom uniform string nomos:semanticName = ${quote(semantic.name)}`);
1791
+ if (semantic.kind === "value-object" && semantic.geometry !== undefined) {
1792
+ // Geometry remains an ordinary typed record in custody. Its explicit
1793
+ // RFC 7946 meaning permits the operation boundary to pack coordinates.
1794
+ body.push(`${pad}custom uniform token nomos:geometry = ${quote(semantic.geometry)}`);
1795
+ }
1655
1796
  break;
1656
1797
  case "recipient":
1657
1798
  body.push(`${pad}custom uniform token nomos:subject = ${quote(semantic.subject)}`);
@@ -1687,6 +1828,11 @@ function emitAnonymousField(
1687
1828
  `${pad}def Scope "f_${field.entity.sid}"`,
1688
1829
  `${pad}{`,
1689
1830
  `${pad} custom uniform string nomos:label = ${quote(field.name)}`,
1831
+ `${pad} custom uniform string nomos:wireKey = ${quote(field.entity.wireKey ?? field.entity.sid)}`,
1832
+ ...(field.entity.wireAliases === undefined || field.entity.wireAliases.length === 0
1833
+ ? []
1834
+ : [`${pad} custom uniform string[] nomos:wireAliases = ${stringList(field.entity.wireAliases)}`]),
1835
+ `${pad} custom uniform string nomos:schemaIdentity = ${quote(field.entity.sid)}`,
1690
1836
  `${pad} custom uniform bool nomos:required = ${field.required}`,
1691
1837
  `${pad} custom rel nomos:type = <${requiredPath(field.value.id, paths)}>`,
1692
1838
  ];
@@ -1847,9 +1993,9 @@ function emitField(
1847
1993
  if (field.nullable) body.push(`${pad} custom uniform bool nomos:nullable = true`);
1848
1994
  if (field.encrypted) body.push(`${pad} custom uniform bool nomos:encrypted = true`);
1849
1995
  if (field.blindIndexed) body.push(`${pad} custom uniform bool nomos:blindIndexed = true`);
1850
- if (field.refTarget === undefined) {
1851
- body.push(`${pad} custom rel nomos:type = <${requiredPath(field.value.id, paths)}>`);
1852
- }
1996
+ // Shape and reference target are independent. Atomic workspace/type/id addresses and
1997
+ // versioned references retain their structural type just like every other value object.
1998
+ body.push(`${pad} custom rel nomos:type = <${requiredPath(field.value.id, paths)}>`);
1853
1999
  if (field.refTarget !== undefined) {
1854
2000
  body.push(`${pad} custom rel nomos:refTarget = ${targets([field.refTarget], paths, resolveDependency)}`);
1855
2001
  body.push(`${pad} custom uniform token nomos:referenceMode = ${quote(field.referenceMode ?? "current")}`);
@@ -18,6 +18,7 @@ export const LAW_ENTITY_KINDS = [
18
18
  "flow",
19
19
  "workspace-type",
20
20
  "contract",
21
+ "operation-field",
21
22
  "type",
22
23
  "object-type",
23
24
  ] as const;
@@ -25,6 +26,19 @@ export const LAW_ENTITY_KINDS = [
25
26
  export type LawEntityKind = (typeof LAW_ENTITY_KINDS)[number];
26
27
  export type LawIdentityStatus = "active" | "retired";
27
28
 
29
+ /**
30
+ * Durable transport allocation for one named operation field.
31
+ *
32
+ * These are wire keys, not identities. `canonical` is used by newly generated
33
+ * clients; aliases remain decodable during an explicitly managed overlap window.
34
+ */
35
+ export interface LawOperationFieldWire {
36
+ readonly protobuf: {
37
+ readonly canonical: string;
38
+ readonly aliases?: readonly string[];
39
+ };
40
+ }
41
+
28
42
  export interface LawIdentityEntity {
29
43
  readonly kind: LawEntityKind;
30
44
  readonly sid: string;
@@ -34,6 +48,8 @@ export interface LawIdentityEntity {
34
48
  readonly priorLabels?: readonly string[];
35
49
  readonly generation: number;
36
50
  readonly status: LawIdentityStatus;
51
+ /** Present only on an operation-field ledger row. The row's label + lineage carry meaning. */
52
+ readonly operationFieldWire?: LawOperationFieldWire;
37
53
  }
38
54
 
39
55
  export interface LawIdentityRegistry {
@@ -54,6 +70,8 @@ export interface ReconcileLawIdentityScopeInput {
54
70
  readonly carries?: readonly LawIdentityCarry[];
55
71
  /** Used only while importing an existing compiler-minted identity byte-for-byte. */
56
72
  readonly preferredSids?: Readonly<Record<string, string>>;
73
+ /** First-appearance transport allocations for named operation fields. */
74
+ readonly preferredOperationFieldWire?: Readonly<Record<string, LawOperationFieldWire>>;
57
75
  /**
58
76
  * Defaults to true. Set false only when this compile sees a non-exhaustive slice of
59
77
  * a shared scope (currently project-root domains and framework declarations).
@@ -95,6 +113,7 @@ const KIND_PREFIX: Readonly<Record<LawEntityKind, string>> = {
95
113
  flow: "w",
96
114
  "workspace-type": "x",
97
115
  contract: "c",
116
+ "operation-field": "u",
98
117
  type: "t",
99
118
  "object-type": "o",
100
119
  };
@@ -191,97 +210,105 @@ export function createLawIdentityReconciler(
191
210
  const carriedTo = new Set<string>();
192
211
 
193
212
  for (const carry of [...(input.carries ?? [])].sort((a, b) => compareText(a.to, b.to))) {
194
- if (carry.from === carry.to) {
195
- throw new Error(`NMS-ID-004 identity carry '${carry.from}' -> '${carry.to}' is not a rename`);
196
- }
197
- if (carriedFrom.has(carry.from) || carriedTo.has(carry.to)) {
198
- throw new Error(`NMS-ID-004 duplicate identity carry involving '${carry.from}' or '${carry.to}'`);
199
- }
200
- const key = activeByLabel.get(carry.from);
201
- if (key === undefined) {
202
- throw new Error(
203
- `NMS-ID-004 cannot carry missing active ${input.kind} '${carry.from}' in ${scopeName(input.parent)}`,
204
- );
205
- }
206
- if (!requested.has(carry.to)) {
207
- throw new Error(`NMS-ID-004 carry target '${carry.to}' is not a current ${input.kind} declaration`);
208
- }
209
- const occupied = activeByLabel.get(carry.to);
210
- if (occupied !== undefined && occupied !== key) {
211
- throw new Error(`NMS-ID-003 active ${input.kind} label '${carry.to}' already owns another SID`);
212
- }
213
- const entity = entities[key]!;
214
- entities[key] = {
215
- ...entity,
216
- label: carry.to,
217
- priorLabels: appendUnique(entity.priorLabels, carry.from),
218
- };
219
- activeByLabel.delete(carry.from);
220
- activeByLabel.set(carry.to, key);
221
- carriedFrom.add(carry.from);
222
- carriedTo.add(carry.to);
223
- carried.push({ ...carry, key });
213
+ if (carry.from === carry.to) {
214
+ throw new Error(`NMS-ID-004 identity carry '${carry.from}' -> '${carry.to}' is not a rename`);
215
+ }
216
+ if (carriedFrom.has(carry.from) || carriedTo.has(carry.to)) {
217
+ throw new Error(`NMS-ID-004 duplicate identity carry involving '${carry.from}' or '${carry.to}'`);
218
+ }
219
+ const key = activeByLabel.get(carry.from);
220
+ if (key === undefined) {
221
+ throw new Error(
222
+ `NMS-ID-004 cannot carry missing active ${input.kind} '${carry.from}' in ${scopeName(input.parent)}`,
223
+ );
224
+ }
225
+ if (!requested.has(carry.to)) {
226
+ throw new Error(`NMS-ID-004 carry target '${carry.to}' is not a current ${input.kind} declaration`);
227
+ }
228
+ const occupied = activeByLabel.get(carry.to);
229
+ if (occupied !== undefined && occupied !== key) {
230
+ throw new Error(`NMS-ID-003 active ${input.kind} label '${carry.to}' already owns another SID`);
231
+ }
232
+ const entity = entities[key]!;
233
+ entities[key] = {
234
+ ...entity,
235
+ label: carry.to,
236
+ priorLabels: appendUnique(entity.priorLabels, carry.from),
237
+ };
238
+ activeByLabel.delete(carry.from);
239
+ activeByLabel.set(carry.to, key);
240
+ carriedFrom.add(carry.from);
241
+ carriedTo.add(carry.to);
242
+ carried.push({ ...carry, key });
224
243
  }
225
244
 
226
245
  const resolved: Record<string, string> = {};
227
246
  const allocated: { label: string; key: string; generation: number }[] = [];
228
247
  for (const label of labels) {
229
- const existing = activeByLabel.get(label);
230
- if (existing !== undefined) {
231
- resolved[label] = existing;
232
- continue;
233
- }
234
-
235
- const historic = Object.values(entities).filter(
236
- (entity) =>
237
- entity.kind === input.kind &&
238
- entity.parent === input.parent &&
239
- (entity.label === label || entity.priorLabels?.includes(label) === true),
240
- );
241
- const generation = historic.reduce((max, entity) => Math.max(max, entity.generation), -1) + 1;
242
- const preferredSid = generation === 0 ? input.preferredSids?.[label] : undefined;
243
- // A compatibility proposal may repeat a deterministic legacy SID whose former
244
- // owner is now a tombstone (most visibly a field beneath a recreated aggregate).
245
- // That is evidence to allocate afresh, never authority to reuse the old identity.
246
- const preferredKey = preferredSid === undefined ? undefined : lawEntityKey(input.kind, preferredSid);
247
- const preferredAvailable =
248
- preferredSid !== undefined && preferredKey !== undefined && entities[preferredKey] === undefined;
249
- const sid =
250
- preferredAvailable
251
- ? preferredSid
252
- : mintStableId(
253
- `law-entity:${input.kind}:${input.parent ?? "@root"}:${label}:generation:${generation}`,
248
+ const existing = activeByLabel.get(label);
249
+ if (existing !== undefined) {
250
+ const preferredWire = input.preferredOperationFieldWire?.[label];
251
+ if (preferredWire !== undefined && entities[existing]!.operationFieldWire === undefined) {
252
+ entities[existing] = { ...entities[existing]!, operationFieldWire: preferredWire };
253
+ }
254
+ resolved[label] = existing;
255
+ continue;
256
+ }
257
+
258
+ const historic = Object.values(entities).filter(
259
+ (entity) =>
260
+ entity.kind === input.kind &&
261
+ entity.parent === input.parent &&
262
+ (entity.label === label || entity.priorLabels?.includes(label) === true),
263
+ );
264
+ const generation = historic.reduce((max, entity) => Math.max(max, entity.generation), -1) + 1;
265
+ const preferredSid = generation === 0 ? input.preferredSids?.[label] : undefined;
266
+ // A compatibility proposal may repeat a deterministic legacy SID whose former
267
+ // owner is now a tombstone (most visibly a field beneath a recreated aggregate).
268
+ // That is evidence to allocate afresh, never authority to reuse the old identity.
269
+ const preferredKey = preferredSid === undefined
270
+ ? undefined
271
+ : lawEntityKey(input.kind, preferredSid);
272
+ const preferredAvailable =
273
+ preferredSid !== undefined && preferredKey !== undefined && entities[preferredKey] === undefined;
274
+ const sid = preferredAvailable
275
+ ? preferredSid
276
+ : mintStableId(
277
+ `law-entity:${input.kind}:${input.parent ?? "@root"}:${label}:generation:${generation}`,
278
+ );
279
+ const key = lawEntityKey(input.kind, sid);
280
+ if (entities[key] !== undefined) {
281
+ throw new Error(
282
+ `NMS-ID-002 SID collision: ${key} is already ${describeEntity(entities[key]!)}`,
254
283
  );
255
- const key = lawEntityKey(input.kind, sid);
256
- if (entities[key] !== undefined) {
257
- throw new Error(
258
- `NMS-ID-002 SID collision: ${key} is already ${describeEntity(entities[key]!)}`,
259
- );
260
- }
261
- entities[key] = {
262
- kind: input.kind,
263
- sid,
264
- ...(input.parent === undefined ? {} : { parent: input.parent }),
265
- label,
266
- generation,
267
- status: "active",
268
- };
269
- activeByLabel.set(label, key);
270
- resolved[label] = key;
271
- allocated.push({ label, key, generation });
284
+ }
285
+ entities[key] = {
286
+ kind: input.kind,
287
+ sid,
288
+ ...(input.parent === undefined ? {} : { parent: input.parent }),
289
+ label,
290
+ generation,
291
+ status: "active",
292
+ ...(input.preferredOperationFieldWire?.[label] === undefined
293
+ ? {}
294
+ : { operationFieldWire: input.preferredOperationFieldWire[label] }),
295
+ };
296
+ activeByLabel.set(label, key);
297
+ resolved[label] = key;
298
+ allocated.push({ label, key, generation });
272
299
  }
273
300
 
274
301
  const retired: string[] = [];
275
302
  if (input.retireAbsent !== false) {
276
- for (const [key, entity] of scopeEntries) {
277
- if (carriedFrom.has(entity.label)) continue;
278
- if (requested.has(entity.label)) continue;
279
- if (entities[key]?.status === "active") {
280
- entities[key] = { ...entities[key]!, status: "retired" };
281
- retired.push(key);
282
- retireDescendants(entities, key, retired);
283
- }
284
- }
303
+ for (const [key, entity] of scopeEntries) {
304
+ if (carriedFrom.has(entity.label)) continue;
305
+ if (requested.has(entity.label)) continue;
306
+ if (entities[key]?.status === "active") {
307
+ entities[key] = { ...entities[key]!, status: "retired" };
308
+ retired.push(key);
309
+ retireDescendants(entities, key, retired);
310
+ }
311
+ }
285
312
  }
286
313
 
287
314
  return {
@@ -320,6 +347,17 @@ export function validateLawIdentityRegistry(registry: LawIdentityRegistry): void
320
347
  if (entity.status !== "active" && entity.status !== "retired") {
321
348
  throw new Error(`NMS-ID-001 ${key} has invalid status`);
322
349
  }
350
+ if (entity.operationFieldWire !== undefined) {
351
+ if (entity.kind !== "operation-field") {
352
+ throw new Error(`NMS-ID-001 ${key} carries operation-field wire custody but is not an operation field`);
353
+ }
354
+ const keys = [
355
+ entity.operationFieldWire.protobuf.canonical,
356
+ ...(entity.operationFieldWire.protobuf.aliases ?? []),
357
+ ];
358
+ assertUniqueNonEmpty(keys, `${key} protobuf wire keys`);
359
+ for (const wireKey of keys) assertSid(wireKey, `${key} protobuf wire key`);
360
+ }
323
361
  if (entity.priorLabels !== undefined) {
324
362
  assertUniqueNonEmpty([...entity.priorLabels], `${key} prior labels`);
325
363
  if (entity.priorLabels.includes(entity.label)) {