@githolon/dsl 0.108.7 → 0.110.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (307) 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 +76 -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 +211 -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 +267 -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 +23905 -15774
  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 +548 -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_span_trace_test.dart +23 -0
  74. package/dart/test/replica_readiness_test.dart +156 -0
  75. package/dart/test/runtime_projections_test.dart +92 -0
  76. package/dart/test/runtime_v2_gateway_test.dart +131 -0
  77. package/dart/test/signing_identity_kernel_test.dart +75 -0
  78. package/dart/test/sync_status_preparation_report_test.dart +29 -0
  79. package/dart/test/sync_status_reducer_test.dart +419 -0
  80. package/dart/test/test_harness_test.dart +71 -95
  81. package/dart/test/typed_query_watch_test.dart +170 -0
  82. package/dart/test/typed_watch_registry_test.dart +249 -0
  83. package/dart_lints/README.md +18 -0
  84. package/dart_lints/lib/nomos_lints.dart +70 -0
  85. package/dart_lints/pubspec.lock +580 -0
  86. package/dart_lints/pubspec.yaml +12 -0
  87. package/dart_lints/test/reference_identity_test.dart +75 -0
  88. package/dart_types/CHANGELOG.md +18 -0
  89. package/dart_types/lib/nomos_kernel_protocol.dart +21 -1
  90. package/dart_types/lib/nomos_types.dart +9 -1
  91. package/dart_types/lib/src/admission_decision.dart +37 -0
  92. package/dart_types/lib/src/aggregate_meta.dart +1 -1
  93. package/dart_types/lib/src/approved_model.dart +65 -0
  94. package/dart_types/lib/src/custody.dart +10 -18
  95. package/dart_types/lib/src/error_info.dart +4 -4
  96. package/dart_types/lib/src/errors.dart +29 -29
  97. package/dart_types/lib/src/foreign_ref.dart +62 -33
  98. package/dart_types/lib/src/local_work_rejected.dart +3 -3
  99. package/dart_types/lib/src/nomos_ref.dart +3 -1
  100. package/dart_types/lib/src/principal.dart +2 -2
  101. package/dart_types/lib/src/proto/google/protobuf/any.pb.dart +163 -0
  102. package/dart_types/lib/src/proto/google/protobuf/any.pbenum.dart +11 -0
  103. package/dart_types/lib/src/proto/nomos/discovery/v1/workspace_placement.pb.dart +137 -0
  104. package/dart_types/lib/src/proto/nomos/discovery/v1/workspace_placement.pbenum.dart +11 -0
  105. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pb.dart +20721 -10433
  106. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbenum.dart +555 -27
  107. package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pb.dart +387 -0
  108. package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pbenum.dart +46 -0
  109. package/dart_types/lib/src/proto/nomos/runtime/v2/application_readiness_desk.pb.dart +295 -0
  110. package/dart_types/lib/src/proto/nomos/runtime/v2/application_readiness_desk.pbenum.dart +139 -0
  111. package/dart_types/lib/src/proto/nomos/runtime/v2/authentication_desk.pb.dart +269 -0
  112. package/dart_types/lib/src/proto/nomos/runtime/v2/authentication_desk.pbenum.dart +103 -0
  113. package/dart_types/lib/src/proto/nomos/runtime/v2/compatibility_desk.pb.dart +417 -0
  114. package/dart_types/lib/src/proto/nomos/runtime/v2/compatibility_desk.pbenum.dart +62 -0
  115. package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pb.dart +9968 -0
  116. package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pbenum.dart +133 -0
  117. package/dart_types/lib/src/proto/nomos/runtime/v2/credentials_desk.pb.dart +204 -0
  118. package/dart_types/lib/src/proto/nomos/runtime/v2/credentials_desk.pbenum.dart +54 -0
  119. package/dart_types/lib/src/proto/nomos/runtime/v2/custody_desk.pb.dart +1550 -0
  120. package/dart_types/lib/src/proto/nomos/runtime/v2/custody_desk.pbenum.dart +11 -0
  121. package/dart_types/lib/src/proto/nomos/runtime/v2/framework.pb.dart +710 -0
  122. package/dart_types/lib/src/proto/nomos/runtime/v2/framework.pbenum.dart +11 -0
  123. package/dart_types/lib/src/proto/nomos/runtime/v2/identity_desk.pb.dart +845 -0
  124. package/dart_types/lib/src/proto/nomos/runtime/v2/identity_desk.pbenum.dart +11 -0
  125. package/dart_types/lib/src/proto/nomos/runtime/v2/interaction_desk.pb.dart +198 -0
  126. package/dart_types/lib/src/proto/nomos/runtime/v2/interaction_desk.pbenum.dart +11 -0
  127. package/dart_types/lib/src/proto/nomos/runtime/v2/law_desk.pb.dart +369 -0
  128. package/dart_types/lib/src/proto/nomos/runtime/v2/law_desk.pbenum.dart +11 -0
  129. package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pb.dart +609 -0
  130. package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pbenum.dart +65 -0
  131. package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pb.dart +358 -0
  132. package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pbenum.dart +100 -0
  133. package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pb.dart +2231 -0
  134. package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pbenum.dart +271 -0
  135. package/dart_types/lib/src/proto/nomos/runtime/v2/placement_desk.pb.dart +1286 -0
  136. package/dart_types/lib/src/proto/nomos/runtime/v2/placement_desk.pbenum.dart +11 -0
  137. package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pb.dart +636 -0
  138. package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pbenum.dart +116 -0
  139. package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pb.dart +1637 -0
  140. package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pbenum.dart +175 -0
  141. package/dart_types/lib/src/proto/nomos/runtime/v2/recovery_desk.pb.dart +1269 -0
  142. package/dart_types/lib/src/proto/nomos/runtime/v2/recovery_desk.pbenum.dart +129 -0
  143. package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pb.dart +2782 -0
  144. package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pbenum.dart +97 -0
  145. package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pb.dart +2232 -8627
  146. package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pbenum.dart +0 -358
  147. package/dart_types/lib/src/proto/nomos/runtime/v2/sealing_desk.pb.dart +1639 -0
  148. package/dart_types/lib/src/proto/nomos/runtime/v2/sealing_desk.pbenum.dart +46 -0
  149. package/dart_types/lib/src/runtime_outcome.dart +133 -0
  150. package/dart_types/lib/src/subscriptions.dart +116 -23
  151. package/dart_types/pubspec.yaml +1 -1
  152. package/dart_types/test/extremum_subscriptions_test.dart +40 -0
  153. package/dart_types/test/foreign_ref_resolution_test.dart +75 -0
  154. package/dart_types/test/nomos_ref_address_test.dart +25 -0
  155. package/dart_types/test/ordered_subscriptions_test.dart +34 -0
  156. package/operation_contract_check.mjs +24 -0
  157. package/package.json +11 -6
  158. package/src/aggregate.ts +70 -10
  159. package/src/application_topology.ts +18 -11
  160. package/src/attested.ts +3 -0
  161. package/src/attested_core.ts +46 -3
  162. package/src/birth_meter_ensure.ts +19 -7
  163. package/src/births.ts +29 -4
  164. package/src/build_package.ts +140 -13
  165. package/src/business_birth.ts +181 -4
  166. package/src/codegen_call_proto.ts +1274 -0
  167. package/src/codegen_call_usda.ts +49 -6
  168. package/src/codegen_dart.ts +79 -44
  169. package/src/codegen_dart_client.ts +458 -800
  170. package/src/{codegen_dart_application.ts → codegen_dart_facade.ts} +331 -582
  171. package/src/codegen_dart_framework_calls.ts +320 -37
  172. package/src/codegen_dart_indexed_variant.ts +159 -0
  173. package/src/codegen_dart_scenario.ts +42 -13
  174. package/src/codegen_proof.ts +3 -3
  175. package/src/codegen_provider_dart.ts +5 -0
  176. package/src/codegen_query_proto.ts +158 -0
  177. package/src/codegen_refusal.ts +189 -0
  178. package/src/codegen_result_proto.ts +931 -0
  179. package/src/codegen_result_usda.ts +4 -0
  180. package/src/codegen_ts.ts +464 -616
  181. package/src/codegen_workspace_access.ts +105 -0
  182. package/src/compatibility_graph.ts +759 -0
  183. package/src/compatibility_usda.ts +264 -0
  184. package/src/compile_package_main.ts +1994 -1189
  185. package/src/custody_placement.ts +156 -0
  186. package/src/custody_placement_usda.ts +91 -0
  187. package/src/decl_shapes.ts +21 -2
  188. package/src/describe_model.ts +1 -0
  189. package/src/directive.ts +59 -2
  190. package/src/drivers.ts +51 -0
  191. package/src/engine_entry.ts +52 -8
  192. package/src/evolve_preview.ts +11 -4
  193. package/src/exists.ts +2 -0
  194. package/src/fields.ts +75 -11
  195. package/src/flow_gate.ts +2 -1
  196. package/src/framework/application_home.ts +170 -18
  197. package/src/framework/attested_receipts.ts +8 -3
  198. package/src/framework/auth_provider_law.ts +169 -9
  199. package/src/framework/birth_certificate_law.ts +4 -0
  200. package/src/framework/capability.ts +34 -3
  201. package/src/framework/child_authentication.ts +346 -0
  202. package/src/framework/console_governance.ts +295 -0
  203. package/src/framework/delegated_roles.ts +301 -0
  204. package/src/framework/domain_lifecycle.ts +26 -13
  205. package/src/framework/founding_genesis.ts +193 -0
  206. package/src/framework/framework_aggregates.ts +1 -1
  207. package/src/framework/genesis.ts +6 -1
  208. package/src/framework/governance_relations.ts +19 -0
  209. package/src/framework/home.ts +5 -734
  210. package/src/framework/legacy/child_auth_provider_epoch.ts +73 -0
  211. package/src/framework/model_authority_repair.ts +116 -0
  212. package/src/framework/model_authority_seed.ts +185 -0
  213. package/src/framework/model_transition.ts +211 -0
  214. package/src/framework/owned_custody.ts +1 -1
  215. package/src/framework/principal_enrollment.ts +302 -0
  216. package/src/framework/receiving_group.ts +857 -0
  217. package/src/framework/receiving_group_recovery.ts +314 -0
  218. package/src/framework/relations.ts +4 -8
  219. package/src/framework/signing_identity.ts +242 -95
  220. package/src/framework/source_roster.ts +11 -5
  221. package/src/framework/workspace_access.ts +149 -0
  222. package/src/framework/workspace_invariant.ts +73 -52
  223. package/src/framework/workspaces.ts +407 -40
  224. package/src/framework_call_definitions.ts +32 -3
  225. package/src/generated_client_bundle_boundary.ts +97 -0
  226. package/src/generated_client_names.ts +1 -0
  227. package/src/identity_usda.ts +27 -0
  228. package/src/index.ts +183 -12
  229. package/src/indexed_variant.ts +83 -0
  230. package/src/intent.ts +173 -45
  231. package/src/law_check.ts +8 -5
  232. package/src/law_dependency_index.ts +231 -0
  233. package/src/law_graph.ts +508 -71
  234. package/src/law_graph_usda.ts +170 -24
  235. package/src/law_identity.ts +119 -81
  236. package/src/law_source_map.ts +4 -1
  237. package/src/law_units.ts +498 -0
  238. package/src/law_value_type.ts +7 -3
  239. package/src/member_authentication.ts +302 -0
  240. package/src/null_meta.ts +1 -1
  241. package/src/openusd_compiled_layer.ts +132 -0
  242. package/src/operation_contract_check.ts +31 -0
  243. package/src/ordered_read.ts +2 -1
  244. package/src/package_config.ts +33 -12
  245. package/src/package_dependencies.ts +1 -0
  246. package/src/plan_source_slice.ts +30 -1
  247. package/src/protobuf_field_number.ts +36 -0
  248. package/src/provenance_wire.ts +6 -0
  249. package/src/query.ts +2 -1
  250. package/src/read.ts +50 -0
  251. package/src/relation.ts +4 -1
  252. package/src/relation_schema.ts +15 -2
  253. package/src/report.ts +2 -2
  254. package/src/sqlite_computation_bundle.ts +78 -0
  255. package/src/sqlite_computed.ts +102 -0
  256. package/src/sqlite_counts.ts +154 -0
  257. package/src/sqlite_layout.ts +823 -0
  258. package/src/sqlite_reads.ts +275 -0
  259. package/src/sqlite_samples.ts +185 -0
  260. package/src/stable_id_identity_bridge.ts +1 -1
  261. package/src/stable_ids_for_module.ts +16 -4
  262. package/src/test_call_usda.ts +6 -6
  263. package/src/usda_relationship_integrity.ts +20 -3
  264. package/src/usda_text.ts +21 -1
  265. package/src/value_object.ts +81 -11
  266. package/src/wire.ts +22 -0
  267. package/src/wire_encode.ts +227 -20
  268. package/src/workspace_sharding.ts +15 -6
  269. package/src/workspace_sharing.ts +51 -49
  270. package/src/zod_introspection.ts +2 -0
  271. package/compat_package.mjs +0 -46
  272. package/dart/lib/src/home.dart +0 -15
  273. package/dart/lib/src/runner/offline-install.mjs +0 -15367
  274. package/dart/lib/src/source_trust.dart +0 -51
  275. package/dart/lib/src/webview_bridge.dart +0 -6540
  276. package/dart/lib/src/workspace_name.dart +0 -30
  277. package/dart/test/acquire_session_credentials_test.dart +0 -150
  278. package/dart/test/acquire_session_test.dart +0 -534
  279. package/dart/test/adv_realm_interference_test.dart +0 -600
  280. package/dart/test/cloud_bound_topology_test.dart +0 -168
  281. package/dart/test/cluster_births_test.dart +0 -202
  282. package/dart/test/cluster_device_keyed_acceptance_test.dart +0 -105
  283. package/dart/test/cluster_local_autosync_test.dart +0 -123
  284. package/dart/test/cluster_scale_test.dart +0 -122
  285. package/dart/test/compat_test.dart +0 -61
  286. package/dart/test/dlq_session_surface_test.dart +0 -222
  287. package/dart/test/first_app_gauntlet_test.dart +0 -386
  288. package/dart/test/fixtures/wedge/fake-install.mjs +0 -11
  289. package/dart/test/four_layer_nested_topology_test.dart +0 -290
  290. package/dart/test/home_parity_test.dart +0 -13
  291. package/dart/test/local_sync_inert_test.dart +0 -107
  292. package/dart/test/node_runner_transport_test.dart +0 -78
  293. package/dart/test/parent_signer_recovery_test.dart +0 -155
  294. package/dart/test/residency_refusal_test.dart +0 -49
  295. package/dart/test/sealing_client_test.dart +0 -181
  296. package/dart/test/session_bound_test.dart +0 -285
  297. package/dart/test/stale_bundle_test.dart +0 -80
  298. package/dart/test/vo_read_typing_test.dart +0 -131
  299. package/dart/test/watch_after_offer_stall_test.dart +0 -100
  300. package/dart/test/webview_bridge_test.dart +0 -1930
  301. package/dart/test/workspace_name_test.dart +0 -24
  302. package/runtime/generated-application-host.js +0 -22860
  303. package/src/codegen_query_usda.ts +0 -214
  304. package/src/compat_manifest.ts +0 -26
  305. package/src/compat_package_main.ts +0 -224
  306. package/src/shape_compat.ts +0 -352
  307. package/src/usd_state.ts +0 -505
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@githolon/dsl",
3
- "version": "0.108.7",
3
+ "version": "0.110.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "Nomos 2 domain-authoring DSL: aggregates + directives in TS, executed and encoded to the Rust kernel's wire shapes.",
@@ -20,7 +20,6 @@
20
20
  "./stable-ids": "./src/stable_ids.ts",
21
21
  "./compose": "./src/compose.ts",
22
22
  "./application-composition": "./src/application_composition.ts",
23
- "./usd-state": "./src/usd_state.ts",
24
23
  "./engine-entry": "./src/engine_entry.ts",
25
24
  "./compile-proof": {
26
25
  "types": "./compile_proof.d.mts",
@@ -43,6 +42,8 @@
43
42
  "./package.json": "./package.json"
44
43
  },
45
44
  "files": [
45
+ "assets/operation-contract.wasm",
46
+ "operation_contract_check.mjs",
46
47
  "src",
47
48
  "!src/**/*.test.ts",
48
49
  "!src/framework/identity.ts",
@@ -50,26 +51,30 @@
50
51
  "!dart/.dart_tool",
51
52
  "!dart/build",
52
53
  "dart_types",
54
+ "dart_lints",
55
+ "!dart_lints/.dart_tool",
56
+ "!dart_lints/build",
53
57
  "!dart_types/.dart_tool",
54
58
  "!dart_types/build",
55
- "runtime",
56
59
  "compile_proof.mjs",
57
60
  "compile_proof.d.mts",
58
61
  "compile_package.mjs",
59
62
  "check_package.mjs",
60
- "compat_package.mjs",
61
63
  "lsp_server.mjs"
62
64
  ],
63
65
  "publishConfig": {
64
66
  "access": "public"
65
67
  },
66
68
  "scripts": {
67
- "build:application-runtime": "node scripts/build_application_runtime.mjs",
68
- "prepack": "npm run build:application-runtime",
69
+ "build:contracts": "node ../scripts/build_operation_contract_checker.mjs",
70
+ "prepack": "npm run build:contracts",
69
71
  "typecheck": "tsc --noEmit",
70
72
  "test": "vitest run"
71
73
  },
72
74
  "dependencies": {
75
+ "@githolon/runtime": "^0.110.0",
76
+ "@bufbuild/protobuf": "^2.13.0",
77
+ "@bufbuild/protoc-gen-es": "^2.13.0",
73
78
  "esbuild": "0.24.2",
74
79
  "tsx": "^4.19.2",
75
80
  "typescript": "^5.6.3",
package/src/aggregate.ts CHANGED
@@ -26,8 +26,21 @@ import { Lww } from "./drivers.js";
26
26
  * PRESENCE ONLY is hashed in the manifest (like a directive `plan`); the body ships
27
27
  * in the engine bundle, never in the ledger.
28
28
  */
29
- export type AggregateInvariantVerdict = { accept: true } | { reject: string };
30
- export type AggregateInvariantFn = (snapshot: Record<string, unknown>) => AggregateInvariantVerdict;
29
+ export type AggregateInvariantVerdict =
30
+ | { accept: true }
31
+ | { reject: string }
32
+ /** Refuse admission without committing a strike or changing business state. */
33
+ | { refuse: string };
34
+ /**
35
+ * `previous` is the snapshot immediately before this event; null means creation.
36
+ * Undefined means a legacy caller did not supply transition context. A predicate
37
+ * requiring that context must fail closed, never interpret undefined as creation.
38
+ * Only touched aggregate types declaring an invariant reach this callback.
39
+ */
40
+ export type AggregateInvariantFn = (
41
+ snapshot: Readonly<Record<string, unknown>>,
42
+ previous?: Readonly<Record<string, unknown>> | null,
43
+ ) => AggregateInvariantVerdict;
31
44
 
32
45
  /**
33
46
  * READ VISIBILITY (the native-relations read gate) — the DECLARED posture of an
@@ -361,6 +374,13 @@ type AggregateOptions = {
361
374
  visibility?: ReadVisibility;
362
375
  global?: boolean;
363
376
  attributed?: boolean;
377
+ /**
378
+ * `"candidates"` decides every first-writer field of this aggregate (and the entries of a
379
+ * first-writer map) from the SET of changes instead of their arrival order - see
380
+ * `FirstWriterCandidates`. Fields with any other merge policy are untouched. This changes the
381
+ * aggregate's law, so it ships through a domain rollout, never as a silent edit.
382
+ */
383
+ firstWriter?: "candidates";
364
384
  };
365
385
 
366
386
  export function aggregate<const Id extends string, F extends Record<string, Field>>(
@@ -389,6 +409,22 @@ export function aggregate<const Id extends string, F extends Record<string, Fiel
389
409
  );
390
410
  }
391
411
  const { stored, hasMany } = partitionFields(fields);
412
+ for (const [name, field] of Object.entries(stored)) {
413
+ const opaqueJsonLeaf = field.kind === "json";
414
+ const opaqueJsonMap = field.kind === "map" && field.mapValueKind === "json";
415
+ if (!opaqueJsonLeaf && !opaqueJsonMap) continue;
416
+ const declaredShape = field.valueObject?.name ?? field.mapValueVo?.name;
417
+ // Named value objects lower to the kernel's recursive ValueObject algebra and nested USDA prims.
418
+ // The historical FieldKind remains `json` only as a source/API compatibility label; it is never
419
+ // permitted to select Value::Json when a nominal descriptor is present.
420
+ if (declaredShape !== undefined) continue;
421
+ throw new Error(
422
+ `aggregate '${id}': field '${name}'${declaredShape === undefined ? "" : ` (${declaredShape})`} ` +
423
+ `would persist as opaque JSON, which cannot be authoritative Nomos state. ` +
424
+ `Declare a named valueObject so its members lower structurally into USDA, use independently composed ` +
425
+ `aggregate fields/records, or retain an exact foreign document through t.evidence() when source fidelity is required.`,
426
+ );
427
+ }
392
428
  // THE TIER FIELDS (`fulfilment.ts`). Completeness is law-state, so it is a real declared field
393
429
  // — hashed, projected, queryable, evolvable — not something a reader re-derives by guessing
394
430
  // which columns happen to be null. The blocked sibling records a TERMINAL reason, so "no
@@ -501,7 +537,30 @@ export function aggregate<const Id extends string, F extends Record<string, Fiel
501
537
  // can be attributed"). Each attributed field's wire driver is wrapped
502
538
  // `{"Attributed": <inner>}` — the law-era mechanism (old kernels refuse it cleanly;
503
539
  // unattributed fields/domains stay byte-identical).
504
- const LWW_CLASS_KINDS = new Set(["FirstWriterWins", "Lww", "LwwCausal", "LwwBy"]);
540
+ // `{ firstWriter: "candidates" }`: swap the policy BEFORE attribution wraps it, so an
541
+ // attributed first-writer field becomes Attributed(FirstWriterCandidates).
542
+ if (opts?.firstWriter === "candidates") {
543
+ const candidates = (driver: { kind: string; inner?: unknown }): unknown =>
544
+ driver.kind === "FirstWriterWins"
545
+ ? { kind: "FirstWriterCandidates" }
546
+ : driver.kind === "MapOf" && (driver.inner as { kind?: string } | undefined)?.kind === "FirstWriterWins"
547
+ ? { kind: "MapOf", inner: { kind: "FirstWriterCandidates" } }
548
+ : undefined;
549
+ for (const [name, f] of Object.entries(stored)) {
550
+ const swapped = candidates((f as Field).driver as { kind: string; inner?: unknown });
551
+ if (swapped === undefined) continue;
552
+ // Clone by descriptors: a lazy `t.ref(() => Agg)` getter must not be forced here.
553
+ const clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(f)) as Field;
554
+ Object.defineProperty(clone, "driver", {
555
+ value: swapped,
556
+ enumerable: true,
557
+ configurable: true,
558
+ writable: false,
559
+ });
560
+ (stored as Record<string, Field>)[name] = clone;
561
+ }
562
+ }
563
+ const LWW_CLASS_KINDS = new Set(["FirstWriterWins", "FirstWriterCandidates", "Lww", "LwwCausal", "LwwBy"]);
505
564
  const attributableDriver = (f: Field) => LWW_CLASS_KINDS.has((f.driver as { kind?: string }).kind ?? "");
506
565
  const attributableKind = (f: Field) =>
507
566
  f.kind !== "set" && f.kind !== "map" && f.kind !== "counter" && f.kind !== "position";
@@ -574,22 +633,23 @@ export function aggregate<const Id extends string, F extends Record<string, Fiel
574
633
  const fulfils = opts?.fulfils;
575
634
  const afterFulfilment: AggregateInvariantFn | undefined =
576
635
  fulfils !== undefined
577
- ? (snapshot) => {
636
+ ? (snapshot, previous) => {
578
637
  const verdict = fulfils.invariant(snapshot);
579
638
  if ("reject" in verdict) return { reject: `${id}: ${verdict.reject}` };
580
- return userInvariant !== undefined ? userInvariant(snapshot) : { accept: true };
639
+ if ("refuse" in verdict) return verdict;
640
+ return userInvariant !== undefined ? userInvariant(snapshot, previous) : { accept: true };
581
641
  }
582
642
  : userInvariant;
583
643
  const composedInvariant: AggregateInvariantFn | undefined =
584
644
  fromBirthFields.length > 0
585
- ? (snapshot) => {
645
+ ? (snapshot, previous) => {
586
646
  for (const name of fromBirthFields) {
587
647
  const v = snapshot[name];
588
648
  if (v === undefined || v === null) {
589
649
  return { reject: `${id}.${name} is fromBirth() — every ${id} must carry it from creation (the plan left it unset)` };
590
650
  }
591
651
  }
592
- return afterFulfilment !== undefined ? afterFulfilment(snapshot) : { accept: true };
652
+ return afterFulfilment !== undefined ? afterFulfilment(snapshot, previous) : { accept: true };
593
653
  }
594
654
  : afterFulfilment;
595
655
 
@@ -661,12 +721,12 @@ export function aggregate<const Id extends string, F extends Record<string, Fiel
661
721
  // `.fromBirth()` plus the composed invariant means even a hand-authored create cannot omit it.
662
722
  ownedFields[OWNER_FIELD] = attributionField(t.string().fromBirth());
663
723
  ownedEncrypted.sort();
664
- ownedInvariant = (snapshot) => {
724
+ ownedInvariant = (snapshot, previous) => {
665
725
  const owner = snapshot[OWNER_FIELD];
666
726
  if (typeof owner !== "string" || owner.length === 0) {
667
727
  return { reject: `${id} is .owned() — Nomos must stamp its verified creator` };
668
728
  }
669
- return composedInvariant !== undefined ? composedInvariant(snapshot) : { accept: true };
729
+ return composedInvariant !== undefined ? composedInvariant(snapshot, previous) : { accept: true };
670
730
  };
671
731
  }
672
732
 
@@ -737,7 +797,7 @@ export function aggregate<const Id extends string, F extends Record<string, Fiel
737
797
 
738
798
  /** Lww-class driver: a wholesale-replace merge (`Lww`, or `MapOf` wrapping an Lww-class inner). */
739
799
  function isLwwClass(driver: { kind: string; inner?: { kind: string } }): boolean {
740
- if (driver.kind === "FirstWriterWins" || driver.kind === "Lww") return true;
800
+ if (driver.kind === "FirstWriterWins" || driver.kind === "FirstWriterCandidates" || driver.kind === "Lww") return true;
741
801
  if (driver.kind === "MapOf" && driver.inner) return isLwwClass(driver.inner as { kind: string });
742
802
  return false;
743
803
  }
@@ -17,8 +17,8 @@ type TopologyDirective = DomainModule["directives"][number] & {
17
17
  export interface ApplicationTopology {
18
18
  /** Co-resident business domains bound to the application workspace. */
19
19
  readonly rootDomains: readonly string[];
20
- /** Verified ancestor domains that are addressed remotely and never opened on the device plane. */
21
- readonly remoteDomains: readonly string[];
20
+ /** Domains whose law lives in a verified parent workspace. This is lineage, never placement. */
21
+ readonly parentWorkspaceDomains: readonly string[];
22
22
  /** Deterministic primary facet for generated narration/scenarios; not a storage-topology choice. */
23
23
  readonly homeDomain: string;
24
24
  readonly homeModule: DomainModule;
@@ -39,8 +39,9 @@ const publicLanguageOf = (module: DomainModule): readonly TopologyDirective[] =>
39
39
  *
40
40
  * Domains are vocabulary, not storage partitions. They are co-resident by default. A declared
41
41
  * cross-domain business birth is the evidence that its target belongs in a child workspace; a
42
- * verified-parent effect is the equally explicit evidence that its target belongs in an ancestor
43
- * workspace and must not be bound into the participant's receiving group.
42
+ * verified-parent effect is evidence only that its target belongs in a different workspace and
43
+ * must not be bound into the participant's receiving group. Neither relationship says where either
44
+ * workspace is placed, replicated, or executed.
44
45
  *
45
46
  * The primary domain is only a deterministic generated-surface/narration choice: an explicit receiving-group
46
47
  * language wins, then a literal `home`, then declaration order. It does not move the other root domains.
@@ -68,7 +69,7 @@ export function resolveApplicationTopology(
68
69
  const homeDomain = domainOf(homeModule);
69
70
  return {
70
71
  rootDomains: [homeDomain],
71
- remoteDomains: [],
72
+ parentWorkspaceDomains: [],
72
73
  homeDomain,
73
74
  homeModule,
74
75
  };
@@ -108,7 +109,7 @@ export function resolveApplicationTopology(
108
109
  }
109
110
 
110
111
  const targetedByAnotherDomain = new Set<string>();
111
- const remoteAncestorDomains = new Set<string>();
112
+ const parentWorkspaceDomains = new Set<string>();
112
113
  const parentsByDomain = new Map<string, Set<string>>();
113
114
  const childrenByDomain = new Map<string, Set<string>>();
114
115
  const birthSources = new Set<string>();
@@ -149,7 +150,7 @@ export function resolveApplicationTopology(
149
150
  for (const parent of parentsByDomain.get(source) ?? []) {
150
151
  if (seen.has(parent)) continue;
151
152
  seen.add(parent);
152
- remoteAncestorDomains.add(parent);
153
+ parentWorkspaceDomains.add(parent);
153
154
  pending.push(parent);
154
155
  }
155
156
  }
@@ -157,7 +158,7 @@ export function resolveApplicationTopology(
157
158
  // Packages without an explicit receiving group retain the conservative interpretation: a
158
159
  // verified-parent target cannot be silently opened in the same workspace as its requester.
159
160
  for (const parents of parentsByDomain.values()) {
160
- for (const parent of parents) remoteAncestorDomains.add(parent);
161
+ for (const parent of parents) parentWorkspaceDomains.add(parent);
161
162
  }
162
163
  }
163
164
 
@@ -176,7 +177,13 @@ export function resolveApplicationTopology(
176
177
  const target = [...owners][0]!;
177
178
  birthSources.add(source);
178
179
  if (target !== source) {
179
- if (!remoteAncestorDomains.has(source)) {
180
+ // A verified ancestor may birth both the participant receiving group
181
+ // and other sibling workspaces. Only the participant itself is rooted
182
+ // in this generated application; the ancestor's other children remain
183
+ // separate lives and are opened through typed references.
184
+ const isParticipantBirthFromAncestor =
185
+ parentWorkspaceDomains.has(source) && target === participantDomain;
186
+ if (!isParticipantBirthFromAncestor) {
180
187
  targetedByAnotherDomain.add(target);
181
188
  }
182
189
  const children = childrenByDomain.get(source) ?? new Set<string>();
@@ -209,7 +216,7 @@ export function resolveApplicationTopology(
209
216
  const rootModules = modules.filter((module) => {
210
217
  const domain = domainOf(module);
211
218
  return (
212
- !targetedByAnotherDomain.has(domain) && !remoteAncestorDomains.has(domain)
219
+ !targetedByAnotherDomain.has(domain) && !parentWorkspaceDomains.has(domain)
213
220
  );
214
221
  });
215
222
  const rootDomains = rootModules.map(domainOf);
@@ -236,7 +243,7 @@ export function resolveApplicationTopology(
236
243
  rootModules[0]!;
237
244
  return {
238
245
  rootDomains,
239
- remoteDomains: [...remoteAncestorDomains],
246
+ parentWorkspaceDomains: [...parentWorkspaceDomains],
240
247
  homeDomain: domainOf(homeModule),
241
248
  homeModule,
242
249
  };
package/src/attested.ts CHANGED
@@ -29,8 +29,10 @@ import { NomosSourceFrontier } from "./attested_core.js";
29
29
  export {
30
30
  trustSource,
31
31
  trustParent,
32
+ trustAuthorizedSource,
32
33
  PARENT_TRUST_SENTINEL,
33
34
  foreignQuery,
35
+ attestedReadSubject,
34
36
  isTrustSourceDecl,
35
37
  trustPolicy,
36
38
  isTrustPolicyDecl,
@@ -48,6 +50,7 @@ export {
48
50
  type FromPayloadRef,
49
51
  type ForeignReadEdge,
50
52
  type ForeignQueryRef,
53
+ type AttestedReadSubjectRef,
51
54
  } from "./attested_core.js";
52
55
 
53
56
  /**
@@ -24,6 +24,30 @@ export interface ForeignQueryRef {
24
24
  readonly __nomosForeignQuery: true;
25
25
  }
26
26
 
27
+ /**
28
+ * Select the sovereign source workspace proven by one declared attested read as
29
+ * the subject of a directive's ordinary Zanzibar `.requires(...)` check.
30
+ *
31
+ * This is not a second authentication policy. It only chooses which already-
32
+ * verified subject the existing relation gate checks: the session principal by
33
+ * default, or the proven `workspace:<source address>` for the named foreign
34
+ * read. The attested-read gate separately binds that address to its trusted
35
+ * genesis before the relation check can run.
36
+ */
37
+ export interface AttestedReadSubjectRef {
38
+ readonly queryId: string;
39
+ readonly __nomosAttestedReadSubject: true;
40
+ }
41
+
42
+ export function attestedReadSubject(
43
+ query: ForeignQueryRef | { readonly id: string },
44
+ ): AttestedReadSubjectRef {
45
+ if (query.id.trim().length === 0) {
46
+ throw new Error("attestedReadSubject(): query id must not be empty");
47
+ }
48
+ return { queryId: query.id, __nomosAttestedReadSubject: true };
49
+ }
50
+
27
51
  /** Name a query of the SOURCE workspace's law by id (see {@link ForeignQueryRef}). */
28
52
  export function foreignQuery(id: string): ForeignQueryRef {
29
53
  return { id, __nomosForeignQuery: true };
@@ -186,12 +210,14 @@ export interface TrustPolicyOptions {
186
210
  * Lowered into the `nomosAttestedReadGate` era key's `policies` map. */
187
211
  export interface TrustPolicyDecl {
188
212
  readonly policyId: string;
213
+ /** A signed source attribution whose authority is decided by the directive's Zanzibar gate. */
214
+ readonly authorizationOnly?: boolean;
189
215
  /** The resolved allow-list aggregate TYPE id. */
190
- readonly allowListType: string;
216
+ readonly allowListType?: string;
191
217
  /** The resolved genesis field LABEL. */
192
- readonly genesisField: string;
218
+ readonly genesisField?: string;
193
219
  /** The resolved keyHashes field LABEL. */
194
- readonly keysField: string;
220
+ readonly keysField?: string;
195
221
  readonly maxAgeMs?: number;
196
222
  readonly minSourceEpoch?: "birth" | number;
197
223
  readonly monotone?: boolean;
@@ -199,6 +225,23 @@ export interface TrustPolicyDecl {
199
225
  readonly __nomosTrustPolicy: true;
200
226
  }
201
227
 
228
+ /**
229
+ * Declare a payload-routed, signed source attribution whose authority is decided by the
230
+ * directive's ordinary Zanzibar requirement. Co-located custody verifies the exact read now;
231
+ * otherwise the signed source/frontier/result remains replayable evidence for later reconciliation.
232
+ * The compiler refuses this policy unless the directive derives its relation subject from the
233
+ * same attested read.
234
+ */
235
+ export function trustAuthorizedSource(policyId: string): TrustPolicyDecl {
236
+ const decl: TrustPolicyDecl = {
237
+ policyId,
238
+ authorizationOnly: true,
239
+ __nomosTrustPolicy: true,
240
+ };
241
+ policyRegistry.set(policyId, decl);
242
+ return decl;
243
+ }
244
+
202
245
  // The runtime policy registry — the dynamic read lane resolves a policy's monotone posture through
203
246
  // the directive's readsFrom (which registers the queryId); this map lets the compile discovery + the
204
247
  // validators resolve a decl by id. Keyed by policyId; later declaration wins.
@@ -53,14 +53,17 @@ import {
53
53
  BirthAttestationLink,
54
54
  birthAttestation,
55
55
  seedBirthAttestation,
56
- } from "./framework/home.js";
56
+ } from "./framework/receiving_group.js";
57
57
  import { requireParentAttestedBirth } from "./births.js";
58
58
  import {
59
59
  SignerDelegation,
60
60
  enrollDelegation,
61
+ enrollFirstDevice,
61
62
  enrollSigner,
62
63
  enrollSignerSelf,
63
64
  enrollSignerSelfRequiresVerified,
65
+ enrollDelegationRetainsEvidence,
66
+ enrollFirstDeviceRetainsEvidence,
64
67
  identityRelations,
65
68
  signerDelegationByUser,
66
69
  signerObject,
@@ -75,8 +78,10 @@ import {
75
78
  import { refAs, workspaceInvariant } from "./framework/workspace_invariant.js";
76
79
  import { isRelationTypeDecl, type RelationTypeDecl } from "./relation_schema.js";
77
80
 
78
- /** Lightweight copy of the framework's founding-signer guard, kept here to avoid importing the whole
79
- * governance module (whose unrelated captured-read laws would otherwise survive into every child lump). */
81
+ /** Child genesis may already inherit the founder's active device roster through model authority.
82
+ * Re-establishing that same active key must compose with those other devices. This door remains
83
+ * genesis-only and cannot add a different key to a nonempty roster or reactivate a revoked key.
84
+ * The v1 evaluator remains in the kernel for replay of previously installed law. */
80
85
  const bornFounderSignerWriteOnce = workspaceInvariant("FounderSignerWriteOnce")
81
86
  .on("enrollSigner")
82
87
  .reads((ctx) => [
@@ -85,11 +90,15 @@ const bornFounderSignerWriteOnce = workspaceInvariant("FounderSignerWriteOnce")
85
90
  "RelationTuple",
86
91
  "pre:" + relationTupleInstanceId(signerObject(String(ctx.principal ?? "")), "signer"),
87
92
  ),
93
+ refAs("warrant", "GenesisWarrant", "pre:" + GENESIS_WARRANT_ID),
88
94
  ])
89
- .assert(({ tuple }, ctx) => {
90
- const subjects = (tuple as { subjects?: Record<string, unknown> })?.subjects ?? {};
95
+ .assert(({ tuple, warrant }, ctx) => {
96
+ if ((warrant as { principal?: string } | undefined)?.principal) {
97
+ return { reject: "founding-signer-genesis-only" };
98
+ }
99
+ const subjects = (tuple as { subjects?: Record<string, { status?: string }> })?.subjects ?? {};
91
100
  const want = "key:" + String((ctx?.intent ?? {}).keyHash ?? "");
92
- return Object.keys(subjects).every((key) => key === want)
101
+ return Object.keys(subjects).length === 0 || subjects[want]?.status === "active"
93
102
  ? { accept: true }
94
103
  : { reject: "founding-signer-already-enrolled" };
95
104
  });
@@ -99,7 +108,7 @@ const bornFounderSignerWriteOnce = workspaceInvariant("FounderSignerWriteOnce")
99
108
  // folded tuple instead of booting a tenant report sandbox during every child genesis.
100
109
  const nativeBornFounderSignerWriteOnce = {
101
110
  ...bornFounderSignerWriteOnce,
102
- __nomosEvaluator: "founder-signer-write-once-v1" as const,
111
+ __nomosEvaluator: "founder-signer-write-once-v2" as const,
103
112
  };
104
113
 
105
114
  /**
@@ -303,6 +312,9 @@ export function withBirthMeterExports<M extends Record<string, unknown>>(mod: M)
303
312
  // but never appear in TS/Dart/OpenAPI tenant surfaces.
304
313
  internalize("__nomos_SignerDelegation", SignerDelegation);
305
314
  internalize("__nomos_enrollDelegation", enrollDelegation);
315
+ internalize("__nomos_enrollFirstDevice", enrollFirstDevice);
316
+ internalize("__nomos_enrollDelegationRetainsEvidence", enrollDelegationRetainsEvidence);
317
+ internalize("__nomos_enrollFirstDeviceRetainsEvidence", enrollFirstDeviceRetainsEvidence);
306
318
  internalize("__nomos_signerDelegationByUser", signerDelegationByUser);
307
319
  reserve("__nomos_parentAttestedBirth", internal({
308
320
  ...requireParentAttestedBirth,
package/src/births.ts CHANGED
@@ -55,6 +55,20 @@ export function workspaceRole<const Name extends string>(name: Name): Name & Wor
55
55
  return name as Name & WorkspaceRole;
56
56
  }
57
57
 
58
+ /**
59
+ * THE FRAMEWORK'S OWN PARENT-WORKSPACE ROLES. These are constants of the receiving-group lifecycle,
60
+ * not tenant vocabulary: every born workspace seats {@link AUTH_PROVIDER_AUTHORITY} for its verified
61
+ * immediate parent at genesis (so the parent may advance the child's verifier epoch and repair its
62
+ * device authentication), and every birthing parent seats {@link AUTH_PROVIDER_EPOCH_CONSUMER} for
63
+ * the child it births (so the child may ask for the parent's current verifier epoch). A tenant
64
+ * declares neither; `birth(...)` and the receiving-group birth seat both by construction.
65
+ */
66
+ /** The workspace whose signed provider material this workspace adopts (its verified birth parent). */
67
+ export const AUTH_PROVIDER_AUTHORITY = workspaceRole("authProviderAuthority");
68
+ /** Parent-owned permission for an immediate child to request the parent's current verifier epoch.
69
+ * Deliberately the inverse of {@link AUTH_PROVIDER_AUTHORITY}: requests flow up, epochs flow down. */
70
+ export const AUTH_PROVIDER_EPOCH_CONSUMER = workspaceRole("authProviderEpochConsumer");
71
+
58
72
  /** Declare target-owned roles which a born workspace may grant to its verified immediate parent.
59
73
  * The matching relation is necessarily on Nomos's canonical global `workspace` object and accepts
60
74
  * only another nominal workspace subject. Keeping this shape behind the branded WorkspaceRole API
@@ -240,7 +254,12 @@ export function birth<F extends Record<string, Field>>(
240
254
  "zero declares a leaf workspace and Nomos has no compiled fallback",
241
255
  );
242
256
  }
243
- const parentWorkspaceRoles = [...new Set(spec.parentWorkspace?.roles ?? [])].sort();
257
+ // The verified parent is ALWAYS this child's provider authority: the framework's child-authentication
258
+ // lane (verifier-epoch delivery + device repair) gates on it, so the framework seats it. A tenant that
259
+ // restates the constant is deduplicated, never doubled; a tenant adds only its own further roles.
260
+ const parentWorkspaceRoles = [
261
+ ...new Set([AUTH_PROVIDER_AUTHORITY, ...(spec.parentWorkspace?.roles ?? [])]),
262
+ ].sort();
244
263
  type Payload = BusinessBirthPayload<F>;
245
264
  const aggregateNoun = aggregate.id
246
265
  .replace(/([a-z0-9])([A-Z])/g, "$1-$2")
@@ -298,8 +317,14 @@ declare const nomos: { sha256(input: string): string };
298
317
  * minted-and-forgotten; it is a pure function of a key the caller already owns (a business id, an owner uid).
299
318
  * The `` (unit separator) makes `(ns,key)` unambiguous. `nomos.sha256` returns lowercase hex.
300
319
  *
301
- * The client mirror is `deriveWorkspaceName(namespace, key)` in `nomos_client` (Dart) and `@githolon/client`
302
- * (TS) — byte-identical output is contract-tested against {@link WORKSPACE_NAME_VECTORS}.
320
+ * THE ONE PEER-SIDE MIRROR is `keyedBirthNameOf` in `peer-office/src/runtime/workspace_naming.ts`, which the
321
+ * office serves to every client through `ResolveWorkspaceNameRequest`. Clients ASK; they do not re-derive —
322
+ * a client-local copy of this function is a second implementation of a law fact, and the two drift silently.
323
+ * The vectors below are pinned there by `peer-office/test/workspace_naming.test.ts`:
324
+ *
325
+ * ("estate", "estate-abc123") -> estate-83662e729ba18cd04bde52c7a8d854594833d70e
326
+ * ("home", "user:ada") -> home-561af44c94b5b80824f1a9e8cfa1095105f3cb34
327
+ * ("catalogue", "cat-42") -> catalogue-f50bf3b9732dc36961b05b8e6503d96df5f3e88e
303
328
  */
304
329
  export const WORKSPACE_NAME_SEP = "\u001f"; // unit separator disambiguates (namespace,key)
305
330
  export function workspaceNameForKey(namespace: string, key: string): string {
@@ -368,7 +393,7 @@ export const birthInstance = ({
368
393
  *
369
394
  * `birth.singleton(...)` — a keyed birth whose key is a NATURAL single key (one home per user: key = owner).
370
395
  * Sugar over {@link birthKeyed}: `birth.singleton({ keyedBy: owner, namespace: "home" })`. Same addressing
371
- * story — the client re-derives with `deriveWorkspaceName(namespace, keyedBy)`.
396
+ * story — the client asks the office for the name with `ResolveWorkspaceNameRequest.keyed`.
372
397
  */
373
398
  export const birthSingleton = ({
374
399
  keyedBy,