@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
@@ -1,3 +1,4 @@
1
+ import type { Directive } from "./directive.js";
1
2
  // NOMOS — Nomos Sovereign: participants act · verify · remember LOCALLY; hosted
2
3
  // remotes are replaceable custody/transport, not truth. ⇒ ONE Nomos GitHolon
3
4
  // wasm32-wasip1 artifact {kernel · projection · embedded
@@ -30,6 +31,7 @@
30
31
  * It emits into `outDir` (default `<config dir>/build`):
31
32
  * * `<name>.law/` — the exact logically named, human-readable USDA stage.
32
33
  * * `<name>.law.usdz` — a lossless transport archive of that closure.
34
+ * * `<name>.compatibility.usda` — the generated application's opaque interface requirement.
33
35
  * * `<name>.proof.mts` — the GENERATED PROOF (codegen_proof.ts): a
34
36
  * runnable, domain-shaped live e2e synthesized from the law's own directives /
35
37
  * queries / counts. Run it with `githolon proof`. Never fatal: an unsampleable
@@ -41,8 +43,18 @@
41
43
  * --platform=neutral --charset=utf8`, no minify), and refused if it does not assign
42
44
  * `globalThis.plan`.
43
45
  */
44
- import { execFileSync } from "node:child_process";
46
+ import { execFile, execFileSync } from "node:child_process";
47
+ import { promisify } from "node:util";
48
+ import { checkOperationContracts } from "./operation_contract_check.js";
49
+ import {
50
+ APPLICATION_CONTRACT_MODULE,
51
+ assertGeneratedClientBundleBoundary,
52
+ protobufPackageRoot,
53
+ } from "./generated_client_bundle_boundary.js";
45
54
  import { createHash } from "node:crypto";
55
+ import { tmpdir } from "node:os";
56
+ import { inspectSqliteLayouts } from "./sqlite_layout.js";
57
+ import { sqliteDescriptorSamples } from "./sqlite_samples.js";
46
58
  import {
47
59
  expandCapabilityExports,
48
60
  isCapabilityDecl,
@@ -51,8 +63,10 @@ import {
51
63
  copyFileSync,
52
64
  existsSync,
53
65
  mkdirSync,
66
+ mkdtempSync,
54
67
  readdirSync,
55
68
  readFileSync,
69
+ realpathSync,
56
70
  renameSync,
57
71
  rmSync,
58
72
  writeFileSync,
@@ -82,13 +96,21 @@ import {
82
96
  } from "./evolution_files.js";
83
97
  import { assertEvolutionModuleExports } from "./evolution.js";
84
98
  import { readHistoricalIdentityFrontier } from "./identity_frontier.js";
85
- import { generateDartBridgeClient } from "./codegen_dart_client.js";
86
- import { generateDartApplicationFrameworkCalls } from "./codegen_dart_framework_calls.js";
99
+ import {
100
+ dartBridgeClientOperationDirectiveIds,
101
+ generateDartDomainClient,
102
+ } from "./codegen_dart_client.js";
103
+ import {
104
+ dartFrameworkOperationDirectiveIds,
105
+ generateDartApplicationFrameworkCalls,
106
+ } from "./codegen_dart_framework_calls.js";
107
+ import { fixedFrameworkCallDefinitions } from "./framework_call_definitions.js";
108
+ import { BUSINESS_CORRECTION_DIRECTIVE } from "./framework/business_corrections.js";
87
109
  import {
88
110
  dartApplicationClassName,
89
111
  dartApplicationPublicClassNames,
90
112
  generateDartApplication,
91
- } from "./codegen_dart_application.js";
113
+ } from "./codegen_dart_facade.js";
92
114
  import {
93
115
  dartScenarioGivenClassName,
94
116
  dartScenarioClassName,
@@ -110,6 +132,7 @@ import {
110
132
  import { isNamespacedAggregate } from "./read_projection_namespace.js";
111
133
  import {
112
134
  collectPayloadValueObjectDescriptors,
135
+ collectSchemaValueObjectDescriptors,
113
136
  collectValueObjectDescriptors,
114
137
  } from "./value_object.js";
115
138
  import { finishCount, type AnyCount } from "./count.js";
@@ -137,13 +160,22 @@ import {
137
160
  bindBusinessBirthPolicyDomains,
138
161
  expandBusinessBirthExports,
139
162
  } from "./business_birth.js";
140
- import { withOwnedCustodyExports } from "./framework/owned_custody.js";
163
+ import {
164
+ OWNED_DEVICE_APPROVAL_DIRECTIVE,
165
+ OWNED_INITIALIZE_DIRECTIVE,
166
+ OWNED_RECOVERY_ACCEPT_DIRECTIVE,
167
+ OWNED_RECOVERY_COMPLETE_DIRECTIVE,
168
+ OWNED_RECOVERY_PROTECT_DIRECTIVE,
169
+ OWNED_RECOVERY_REQUEST_DIRECTIVE,
170
+ withOwnedCustodyExports,
171
+ } from "./framework/owned_custody.js";
141
172
  import {
142
173
  OwnedCustodyValue,
143
174
  OwnedDeviceRequestValue,
144
175
  } from "./framework/owned_custody_value.js";
145
176
  import { expandRoleExports, rolesOf } from "./role.js";
146
177
  import { collectEngineRouting } from "./engine_entry.js";
178
+ import { sqliteComputationBundle } from "./sqlite_computation_bundle.js";
147
179
  import {
148
180
  canonicalizeEngineJavascript,
149
181
  canonicalizeGeneratedClientJavascript,
@@ -154,6 +186,9 @@ import {
154
186
  isQueryDecl,
155
187
  isSpatialDecl,
156
188
  isSumDecl,
189
+ isExtremumDecl,
190
+ isExistsDecl,
191
+ isOrderedReadDecl,
157
192
  isWorkspaceInvariantDecl,
158
193
  type DeclBag,
159
194
  } from "./decl_shapes.js";
@@ -170,6 +205,8 @@ import {
170
205
  type ShardingLawSpec,
171
206
  } from "./workspace_sharding.js";
172
207
  import { finishExtremum, type AnyExtremum } from "./extremum.js";
208
+ import { finishExists, type AnyExists } from "./exists.js";
209
+ import type { OrderedReadDecl } from "./ordered_read.js";
173
210
  import type { WorkspaceInvariantDecl } from "./framework/workspace_invariant.js";
174
211
  import {
175
212
  discoverRelationTypeDeclarations,
@@ -197,6 +234,8 @@ import {
197
234
  import { reconcileStableIdsWithIdentity } from "./stable_id_identity_bridge.js";
198
235
  import { LAW_FRAMEWORK_PARENT } from "./law_identity.js";
199
236
  import { discoverLawGraph, type LawGraph } from "./law_graph.js";
237
+ import { LawDependencyIndex, commandWorkingSetReport } from "./law_dependency_index.js";
238
+ import { generateDartWorkspaceAccessRead } from "./codegen_workspace_access.js";
200
239
  import {
201
240
  compileSparseLawGraphUsda,
202
241
  emitFrameworkLawUsda,
@@ -207,7 +246,6 @@ import {
207
246
  } from "./dart_barrel_guard.js";
208
247
  import {
209
248
  generateTsClient,
210
- generateTsLifecycleOperator,
211
249
  tsApplicationName,
212
250
  tsClientFactoryName,
213
251
  recursiveLazyTypes,
@@ -216,25 +254,54 @@ import {
216
254
  generateOpenApiFragment,
217
255
  mergeOpenApiFragments,
218
256
  } from "./codegen_openapi.js";
219
- import { domainInterface, type DomainInterface } from "./shape_compat.js";
220
- import { generateTsProof, proofLegsIndex } from "./codegen_proof.js";
257
+ import {
258
+ domainInterfaceFromLawGraph,
259
+ publicClientInterface,
260
+ type DomainInterface,
261
+ } from "./compatibility_graph.js";
262
+ import { proofLegsIndex } from "./codegen_proof.js";
221
263
  import { usdzPack } from "./usdz.js";
264
+ import {
265
+ LAW_UNIT_MANIFEST,
266
+ assertLawUnitsCoverPackage,
267
+ lawUnitDirectoryName,
268
+ packageRoutesLayerUsda,
269
+ splitLawUnits,
270
+ } from "./law_units.js";
222
271
  import { checkDomainModules, formatFindings, hasRefusal } from "./law_check.js";
223
272
  import {
224
273
  currentEvolveLaw,
225
274
  evolveDiff,
226
275
  evolveLawFromLockfile,
227
276
  } from "./evolve_preview.js";
228
- import type { NomosCompat } from "./compat_manifest.js";
229
277
  import {
230
278
  requiredKernelContracts,
231
279
  type KernelContractProbeModule,
232
280
  } from "./kernel_capabilities.js";
233
281
  import { APPLICATION_HOME_DOMAIN } from "./framework/application_home_domain.js";
234
282
  import { applicationHomePolicyLaw } from "./framework/application_home.js";
283
+ import * as childAuthenticationModule from "./framework/child_authentication.js";
284
+ import * as receivingGroupRecoveryModule from "./framework/receiving_group_recovery.js";
285
+ import * as modelTransitionModule from "./framework/model_transition.js";
286
+ import * as modelAuthorityRepairModule from "./framework/model_authority_repair.js";
287
+ import { SEAT_INHERITED_MODEL_AUTHORITY_ID } from "./framework/model_authority_seed.js";
235
288
  import { isApplicationHomeGenesis } from "./application_home_genesis.js";
236
289
  import { resolveApplicationTopology } from "./application_topology.js";
237
- import { isBusinessIntent } from "./intent.js";
290
+ import {
291
+ isCustodyPlacementDeclaration,
292
+ type CustodyPlacementDeclaration,
293
+ } from "./custody_placement.js";
294
+ import {
295
+ custodyPlacementDeclarationsUsda,
296
+ type DomainCustodyPlacement,
297
+ } from "./custody_placement_usda.js";
298
+ import { isApplicationLanguage, isBusinessIntent } from "./intent.js";
299
+ import {
300
+ emitTsCallProtoEncoder,
301
+ generateOperationProtoSchema,
302
+ type OperationProtoLaw,
303
+ TS_CALL_PROTO_PRELUDE,
304
+ } from "./codegen_call_proto.js";
238
305
  import {
239
306
  emitTsCallUsdaEncoder,
240
307
  TS_CALL_USDA_PRELUDE,
@@ -266,6 +333,11 @@ import {
266
333
  PlanSourceSliceProgram,
267
334
  type PlanSourceSliceTarget,
268
335
  } from "./plan_source_slice.js";
336
+ import {
337
+ encodeOpenUsdUsdc,
338
+ flattenOpenUsdToUsda,
339
+ } from "./openusd_compiled_layer.js";
340
+ import { emitApplicationCompatibilityUsda } from "./compatibility_usda.js";
269
341
 
270
342
  const DSL_DIR = path.resolve(
271
343
  path.dirname(fileURLToPath(import.meta.url)),
@@ -293,7 +365,7 @@ const BUSINESS_BIRTH_FRAMEWORK_PLAN_SOURCES: Readonly<
293
365
  exportName: "seedAuthProvider",
294
366
  },
295
367
  seedBirthAttestation: {
296
- modulePath: path.join(DSL_DIR, "src", "framework", "home.ts"),
368
+ modulePath: path.join(DSL_DIR, "src", "framework", "receiving_group.ts"),
297
369
  exportName: "seedBirthAttestation",
298
370
  },
299
371
  seedGenesis: {
@@ -304,8 +376,47 @@ const BUSINESS_BIRTH_FRAMEWORK_PLAN_SOURCES: Readonly<
304
376
  modulePath: path.join(DSL_DIR, "src", "birth_meter_ensure.ts"),
305
377
  exportName: "nomosBirthRecordEnsure",
306
378
  },
379
+ // MODEL AUTHORITY DESCENDS (architecture/parent_directed_model_transition.md, lane A): the child-genesis
380
+ // seed every framework birth embeds; its plan closure is the seed module, never the whole tenant domain.
381
+ [SEAT_INHERITED_MODEL_AUTHORITY_ID]: {
382
+ modulePath: path.join(DSL_DIR, "src", "framework", "model_authority_seed.ts"),
383
+ exportName: "seatInheritedModelAuthority",
384
+ },
307
385
  };
308
386
  const BUSINESS_BIRTH_FRAMEWORK_PLAN_DOMAIN = "__nomos_birth_genesis_plan_v1";
387
+ /** The framework child-authentication law composed into the application-home domain; its members are
388
+ * module-level exports so plan slicing and workspace-effect target encoders resolve them by name. */
389
+ const CHILD_AUTHENTICATION_MODULE_PATH = path.join(
390
+ DSL_DIR,
391
+ "src",
392
+ "framework",
393
+ "child_authentication.ts"
394
+ );
395
+ /** The framework receiving-group recovery door (composed only when the package declares the
396
+ * `platform-identity-provider` posture); its members are module-level singletons for the same reason. */
397
+ const RECEIVING_GROUP_RECOVERY_MODULE_PATH = path.join(
398
+ DSL_DIR,
399
+ "src",
400
+ "framework",
401
+ "receiving_group_recovery.ts"
402
+ );
403
+ /** The framework parent-directed model transition (the platform's direction + the child's attested-parent
404
+ * acceptor, composed whenever the package births children); module-level singletons for the same reason. */
405
+ const MODEL_TRANSITION_MODULE_PATH = path.join(
406
+ DSL_DIR,
407
+ "src",
408
+ "framework",
409
+ "model_transition.ts"
410
+ );
411
+ /** The framework model-authority self-heal (the child's ask, the parent's serve and the child's
412
+ * attested-parent acceptor, composed whenever the package births children); module-level singletons so
413
+ * plan slicing and workspace-effect target encoders resolve them by name. */
414
+ const MODEL_AUTHORITY_REPAIR_MODULE_PATH = path.join(
415
+ DSL_DIR,
416
+ "src",
417
+ "framework",
418
+ "model_authority_repair.ts"
419
+ );
309
420
 
310
421
  interface DomainConfig extends NormalizedNomosPackageDomainConfig {
311
422
  readonly key: string;
@@ -316,9 +427,11 @@ interface DomainConfig extends NormalizedNomosPackageDomainConfig {
316
427
  readonly counts?: readonly string[];
317
428
  /** Escape hatch only since #57 — exported SumDecls auto-discover like queries/counts. */
318
429
  readonly sums?: readonly string[];
319
- /** Maintained MIN/MAX reads (#47) — export names (config-named; not yet auto-discovered). */
430
+ /** Maintained MIN/MAX export-name escape hatches; exported declarations auto-discover. */
320
431
  readonly mins?: readonly string[];
321
432
  readonly maxes?: readonly string[];
433
+ readonly exists_?: readonly string[];
434
+ readonly orderedReads?: readonly string[];
322
435
  readonly spatials?: readonly string[];
323
436
  readonly deriveds?: readonly string[];
324
437
  readonly combineds?: readonly string[];
@@ -349,7 +462,7 @@ interface DomainConfig extends NormalizedNomosPackageDomainConfig {
349
462
  };
350
463
  }
351
464
 
352
- type DartSupportImport = "vendored" | "package";
465
+ type DartSupportImport = "package";
353
466
 
354
467
  interface PackageConfig extends NomosPackageConfig {
355
468
  readonly domains: readonly DomainConfig[];
@@ -362,14 +475,13 @@ interface PackageConfig extends NomosPackageConfig {
362
475
  * Typed DART frontend (the Flutter sibling of the generated TS client; #codegen_dart).
363
476
  * `false`/absent = skip (default). `true` = emit into `<outDir>/dart/`;
364
477
  * `{ out: "relative/dir" }` = emit there (resolved against the config dir).
365
- * `{ support: "package" }` keeps the framework-internal runtime import for generated Flutter applications that
366
- * already depend on `nomos_client`/`nomos_flutter`. The default `"vendored"` mode preserves the old
367
- * self-contained directory.
478
+ * Generated Flutter applications import the fixed `nomos_types` and
479
+ * `nomos_client` packages. Runtime implementation is never copied into a
480
+ * generated domain package.
368
481
  * Per composed domain one `<name>.dart` (payload classes + read models + typed read
369
482
  * accessors via the SAME `generateDartDomain` the co2 tenant emit uses), PLUS the
370
- * framework `nomos_client` Dart support files vendored alongside (the generated files'
371
- * `package:nomos_client/...` import is rewritten to a relative one) — so a Flutter app
372
- * can vendor the directory as-is, no pub deps.
483
+ * A generated directory contains only domain stationery, value types,
484
+ * stateless facades and its compatibility matrix.
373
485
  */
374
486
  readonly dart?:
375
487
  | boolean
@@ -388,16 +500,16 @@ interface PackageConfig extends NomosPackageConfig {
388
500
  * produces THREE sibling pub packages under `out` instead of one flat tree:
389
501
  * • `types` — pure data (payload classes, read models, VOs/enums, typed accessors over
390
502
  * the injected NomosReads seams, the aggregate-meta registry). Depends ONLY on the
391
- * pure `nomos_types` core — frontend-safe: no bridge, no sessions, no lifecycle.
503
+ * pure `nomos_types` core — frontend-safe: no session, no sessions, no lifecycle.
392
504
  * • `client` — the effectful runtime clients (`<name>.client.dart`, offer methods over
393
- * NomosBridge). Depends on the types package + `nomos_client`.
505
+ * NomosSession). Depends on the types package + `nomos_client`.
394
506
  * • `test` — optional; generated business scenarios for widget/integration tests.
395
507
  * The layering line: UI code depends on `types`; composition/runtime code on `client`;
396
508
  * generated TYPES may flow to the UI, generated CLIENTS may not.
397
509
  * Each package's `pubspec.yaml` keeps tenant-owned versioning/publishing metadata and custom
398
510
  * dependencies; Nomos-owned framework dependency floors are reconciled on every compile.
399
511
  * Barrels + `lib/src/generated/` are generated-owned and rewritten every compile.
400
- * Requires `support: "package"` (vendored mode contradicts pub-package layering).
512
+ * Uses the fixed runtime packages; generated code never vendors lifecycle machinery.
401
513
  */
402
514
  readonly packages?: {
403
515
  readonly types: string;
@@ -445,6 +557,8 @@ interface PackageConfig extends NomosPackageConfig {
445
557
  readonly compact?: boolean;
446
558
  }
447
559
 
560
+ const execFileAsync = promisify(execFile);
561
+
448
562
  function fail(msg: string): never {
449
563
  console.error(`nomos-compile: ${msg}`);
450
564
  process.exit(1);
@@ -456,6 +570,12 @@ interface EmittedLawClosureAsset {
456
570
  readonly utf8: string;
457
571
  }
458
572
 
573
+ interface EmittedBinaryLawClosureAsset {
574
+ readonly logicalPath: string;
575
+ readonly sha256: string;
576
+ readonly bytes: Uint8Array;
577
+ }
578
+
459
579
  interface UsdPlanAsset {
460
580
  readonly sha256: string;
461
581
  readonly javascript: string;
@@ -491,6 +611,15 @@ interface EmittedLawClosure {
491
611
  }[];
492
612
  }
493
613
 
614
+ /** Match Rust's `BTreeMap<String, _>` ordering for authenticated logical paths.
615
+ * `localeCompare` is collation, not byte ordering: for example it places
616
+ * `catalogue_directory.usda` before `catalogue.usda`, which gives the compiler
617
+ * and kernel different closure identities for the same package. Logical paths
618
+ * are restricted to ASCII by the kernel, so ordinary string comparison is the
619
+ * exact shared ordering here. */
620
+ const compareLogicalPath = (left: string, right: string): number =>
621
+ left < right ? -1 : left > right ? 1 : 0;
622
+
494
623
  /** Build the production-shaped v3 stage entirely from compiler-authored bytes. The deploy host may
495
624
  * shuttle this object, but it cannot select layers, compose opinions or mint any identity: the kernel
496
625
  * inventories the USDZ/Git tree and re-hashes every logically named layer. */
@@ -501,7 +630,11 @@ function emitLawClosure(
501
630
  domainAssets: readonly EmittedLawClosureAsset[],
502
631
  runtime: UsdRuntimeAsset,
503
632
  plans: readonly UsdPlanAsset[],
504
- routes: readonly UsdPlanRoute[]
633
+ routes: readonly UsdPlanRoute[],
634
+ derivedAssets: readonly (
635
+ | EmittedLawClosureAsset
636
+ | EmittedBinaryLawClosureAsset
637
+ )[] = []
505
638
  ): EmittedLawClosure {
506
639
  const planByHash = new Map(plans.map((plan) => [plan.sha256, plan] as const));
507
640
  if (sha256HexUtf8(runtime.javascript) !== runtime.sha256) {
@@ -517,44 +650,13 @@ function emitLawClosure(
517
650
  }
518
651
  }
519
652
  const contentAsset = (sha256: string): string => `@nomos+sha256:${sha256}@`;
520
- const routeOpinions: string[] = [
521
- "#usda 1.0",
522
- "(",
523
- " customLayerData = {",
524
- ' string "nomos:format" = "nomos.law-layer.v3"',
525
- ' string "nomos:logicalPath" = "runtime/routes"',
526
- " }",
527
- ")",
528
- "",
529
- 'def Scope "__NomosRuntime_7e9c"',
530
- "{",
531
- ' custom string nomos:kind = "runtime"',
532
- ' custom string nomos:format = "nomos.openusd-plan-metadata.v3"',
533
- "",
534
- ' def Scope "Routes"',
535
- " {",
536
- ];
537
- [...routes]
538
- .sort((left, right) => left.selector.localeCompare(right.selector))
539
- .forEach((route, index) => {
540
- if (!planByHash.has(route.planSha256)) {
541
- fail(
542
- `v3 closure: route '${route.selector}' names missing plan '${route.planSha256}'`
543
- );
544
- }
545
- routeOpinions.push(
546
- ` def Scope "r${String(index).padStart(6, "0")}"`,
547
- " {",
548
- ` custom string nomos:selector = ${JSON.stringify(
549
- route.selector
550
- )}`,
551
- ` custom string nomos:plan = "${route.planSha256}"`,
552
- " }",
553
- ""
653
+ for (const route of routes) {
654
+ if (!planByHash.has(route.planSha256)) {
655
+ fail(
656
+ `v3 closure: route '${route.selector}' names missing plan '${route.planSha256}'`
554
657
  );
555
- });
556
- if (routeOpinions[routeOpinions.length - 1] === "") routeOpinions.pop();
557
- routeOpinions.push(" }", "}", "");
658
+ }
659
+ }
558
660
 
559
661
  const textAsset = (
560
662
  logicalPath: string,
@@ -573,7 +675,7 @@ function emitLawClosure(
573
675
  runtimeDependencies,
574
676
  })
575
677
  );
576
- const routeLayer = textAsset("runtime/routes.usda", routeOpinions.join("\n"));
678
+ const routeLayer = textAsset("runtime/routes.usda", packageRoutesLayerUsda(routes));
577
679
  const runtimeSource = textAsset(
578
680
  `runtime/bases/${runtime.sha256}.mjs`,
579
681
  runtime.javascript
@@ -658,9 +760,26 @@ function emitLawClosure(
658
760
  ...domainAssets,
659
761
  framework,
660
762
  ]);
661
- const custodyAssets = [...sublayers, runtimeSource, ...planSources].sort(
662
- (left, right) => left.logicalPath.localeCompare(right.logicalPath)
763
+ // Derived assets are compiler-produced, authenticated finished goods. They are deliberately
764
+ // inventoried by the closure without becoming sublayers of the authoritative source stage.
765
+ // In particular, the runtime read projection is an OpenUSD flatten of a population-masked
766
+ // source stage: composing it back into that stage would duplicate every read opinion. Its USDA
767
+ // source remains relationship-checked; the mechanically equivalent USDC cache is binary OpenUSD.
768
+ assertUsdaRelationshipIntegrity(
769
+ derivedAssets.filter(
770
+ (asset): asset is EmittedLawClosureAsset => "utf8" in asset
771
+ ),
772
+ // Standard OpenUSD population masks do not pull relationship targets into the stage. The read
773
+ // engine consumes authored storage kinds and exact ref/field paths; nominal value-type prims
774
+ // remain in the full authenticated law and are intentionally absent from this acceleration view.
775
+ { allowMissingRelationships: new Set(["nomos:type", "nomos:returns"]) }
663
776
  );
777
+ const custodyAssets = [
778
+ ...sublayers,
779
+ runtimeSource,
780
+ ...planSources,
781
+ ...derivedAssets,
782
+ ].sort((left, right) => compareLogicalPath(left.logicalPath, right.logicalPath));
664
783
  const root = textAsset(
665
784
  "root.usda",
666
785
  [
@@ -704,7 +823,7 @@ function emitLawClosure(
704
823
  ].join("\n")
705
824
  );
706
825
  const assets = [root, ...custodyAssets].sort((left, right) =>
707
- left.logicalPath.localeCompare(right.logicalPath)
826
+ compareLogicalPath(left.logicalPath, right.logicalPath)
708
827
  );
709
828
  const identity = createHash("sha256");
710
829
  identity.update("nomos-law-stage-v3\0");
@@ -719,10 +838,11 @@ function emitLawClosure(
719
838
  return {
720
839
  rootLogicalPath: "root.usda",
721
840
  identity: identity.digest("hex"),
722
- assets: assets.map(({ logicalPath, sha256, utf8 }) => ({
723
- logicalPath,
724
- sha256,
725
- bytes: new TextEncoder().encode(utf8),
841
+ assets: assets.map((asset) => ({
842
+ logicalPath: asset.logicalPath,
843
+ sha256: asset.sha256,
844
+ bytes:
845
+ "bytes" in asset ? asset.bytes : new TextEncoder().encode(asset.utf8),
726
846
  })),
727
847
  };
728
848
  }
@@ -990,142 +1110,34 @@ function resolveNamed<T>(
990
1110
  * self-contained (no pub path-dependency on the framework package).
991
1111
  */
992
1112
  const DART_PACKAGE_IMPORT = `import 'package:nomos_client/src/nms_generated_runtime.dart';`;
993
- const DART_RELATIVE_IMPORT = `import 'nomos_client.dart';`;
994
1113
 
995
- /**
996
- * The framework `nomos_client` Dart support files the generated code resolves through
997
- * the barrel (`dsl/dart/lib/`). `schema_validation.dart` is deliberately NOT
998
- * vendored: nothing the codegen emits references it, and it would drag the external
999
- * `json_schema` pub dependency into every vendoring app.
1000
- */
1001
- // The PURE CORE support files, vendored from the `nomos_types` package (dsl/dart_types/lib/src —
1002
- // the layered split: wire types, offer codec, NomosReads, aggregate meta, error info). Zero
1003
- // external pub deps. `schema_validation.dart` is deliberately NOT vendored: nothing the codegen
1004
- // emits references it, and it would drag the external `json_schema` pub dependency into every
1005
- // vendoring app.
1006
- const DART_SUPPORT_CORE_SRC = [
1114
+ // Only used to delete output produced by the retired self-contained runtime generator.
1115
+ const LEGACY_VENDORED_DART_SUPPORT = [
1007
1116
  "wire.dart",
1008
1117
  "driver.dart",
1009
1118
  "offer.dart",
1010
1119
  "subscriptions.dart",
1011
- // Nominal custody/content identities used by generated payloads and read models.
1012
1120
  "sha256_digest.dart",
1013
1121
  "git_object_id.dart",
1014
- // The tri-state slot (NomosSlot<T>) for `nullable`/`clearable` fields (null_semantics.md) — the
1015
- // barrel re-exports it and generated read-model/payload classes reference it, so it must vendor.
1016
1122
  "null_slot.dart",
1017
- // Runtime aggregate-meta support types (NomosAggregateMeta/NomosFieldMeta) — the generated
1018
- // per-domain registry consts reference them through the barrel.
1019
1123
  "aggregate_meta.dart",
1020
- // The data-only error surface (NomosErrorInfo/NomosBridgeError) + the typed diagnostics
1021
- // (nomosErrorFromInfo / MissingOfferAuthority / …) — the vendored bridge AND the generated
1022
- // `<name>.client.dart` reference these through the barrel. No external pub deps.
1023
1124
  "error_info.dart",
1024
1125
  "errors.dart",
1025
- // The foreign-ref locator runtime (ForeignRef<T>/NomosForeignReader/…, t.foreignRef) — a generated read
1026
- // model with a `t.foreignRef` field references `ForeignRef<Target>` through the barrel, so it must vendor.
1027
1126
  "foreign_ref.dart",
1028
1127
  "activity.dart",
1029
1128
  "nomos_ref.dart",
1030
- // Immutable publication pin + stable series and the required version-status sum
1031
- // type. Pure data; the generated application owns the effectful check.
1032
1129
  "versioned_nomos_ref.dart",
1033
- // Optional first-class references are explicit required choices in generated
1034
- // action inputs: absent or present, never an accidentally omitted argument.
1035
1130
  "optional_ref.dart",
1036
- // Branded identity wrappers (NomosPrincipal/NomosDeviceSecret) — the generated cross-workspace birth
1037
- // methods type `actor`/`authorSecret` as these (not bare strings), so both must vendor. Pure data.
1038
1131
  "principal.dart",
1039
- // The custody-lifecycle types (NomosCustodyBreak/NomosCustodyStatus/CustodyBreakPolicy) — the generated
1040
- // client + the vendored bridge reference them through the barrel, so they must vendor. Pure data.
1041
1132
  "custody.dart",
1042
- // THE LOCAL-WORK-REJECTED LIFECYCLE (seated_barrier_and_seal_obligation.md Part 4):
1043
- // NomosLocalWorkRejectedEvent/NomosRejectedIntent — the vendored bridge's localWorkRejectedEvents
1044
- // stream types ride through the barrel, so they must vendor. Pure data.
1045
1133
  "local_work_rejected.dart",
1046
- // SURFACED MERGE CONFLICTS: SurfacedValue<T>/SurfacedObservation<T> — a generated read model with a
1047
- // `.merge(Surfaced(...))` field references them through the barrel, so they must vendor. Pure data.
1048
1134
  "surfaced.dart",
1049
- ] as const;
1050
- // The RUNTIME support files, vendored from `nomos_client` (dsl/dart/lib/src): the async WebView
1051
- // bridge the generated `<name>.client.dart` drives, and the provider surface. Their
1052
- // `package:nomos_types/...` imports are rewritten to the local vendored shim (see emitDart).
1053
- // (`home.dart` is NOT vendored — the home-onboarding runtime drags `package:crypto`; nothing the
1054
- // codegen emits references it.)
1055
- const DART_SUPPORT_RUNTIME_SRC = [
1056
1135
  "provider.dart",
1057
- "webview_bridge.dart",
1058
1136
  "session_bound.dart",
1059
1137
  "record_origin.dart",
1060
- // Canonical USDA application-call/result syntax. Generated clients refer to
1061
- // these through the support barrel; omitting them from a vendored package
1062
- // silently resurrected the old map bridge at integration time.
1063
1138
  "generated_call.dart",
1064
1139
  "result_usda.dart",
1065
1140
  ] as const;
1066
- const DART_SUPPORT_SRC = [
1067
- ...DART_SUPPORT_CORE_SRC,
1068
- ...DART_SUPPORT_RUNTIME_SRC,
1069
- ] as const;
1070
- /** The local shim replacing `package:nomos_types/nomos_types.dart` inside vendored runtime files. */
1071
- const VENDORED_TYPES_SHIM = "nomos_types.dart";
1072
- const VENDORED_KERNEL_PROTOCOL_SHIM = "nomos_kernel_protocol.dart";
1073
- const VENDORED_META_DEP = "^1.12.0";
1074
- const VENDORED_PROTOBUF_DEP = "^5.0.0";
1075
- const VENDORED_FIXNUM_DEP = "^1.1.1";
1076
- const VENDORED_DART_SDK_FLOOR = "3.3.0";
1077
-
1078
- /**
1079
- * Vendored runtime files carry package:meta's @internal boundary. Keep the
1080
- * containing Dart package honest by declaring that implementation dependency
1081
- * instead of stripping the annotation from generated copies.
1082
- */
1083
- function ensureVendoredPackageRequirements(dartOut: string): void {
1084
- let dir = path.resolve(dartOut);
1085
- let pubspec: string | undefined;
1086
- for (let hops = 0; hops < 8; hops++) {
1087
- const candidate = path.join(dir, "pubspec.yaml");
1088
- if (existsSync(candidate)) {
1089
- pubspec = candidate;
1090
- break;
1091
- }
1092
- const parent = path.dirname(dir);
1093
- if (parent === dir) break;
1094
- dir = parent;
1095
- }
1096
- if (pubspec === undefined) return;
1097
- const source = readFileSync(pubspec, "utf8");
1098
- let next = source;
1099
- const sdkLine = /^(\s+sdk:\s*)([^#\r\n]+)(\s*(?:#.*)?)$/m.exec(next);
1100
- if (sdkLine === null) {
1101
- fail(
1102
- "generated Dart semantic types need an environment.sdk constraint in " +
1103
- pubspec
1104
- );
1105
- }
1106
- const declared = sdkLine[2]!.trim().replace(/^['"]|['"]$/g, "");
1107
- const firstVersion = /(\d+)\.(\d+)(?:\.\d+)?/.exec(declared);
1108
- if (firstVersion === null) {
1109
- fail(
1110
- `generated Dart semantic types need SDK >=${VENDORED_DART_SDK_FLOOR}; ` +
1111
- `cannot interpret environment.sdk '${declared}' in ${pubspec}`
1112
- );
1113
- }
1114
- const major = Number(firstVersion[1]);
1115
- const minor = Number(firstVersion[2]);
1116
- if (major < 3 || (major === 3 && minor < 3)) {
1117
- next = next.replace(
1118
- sdkLine[0],
1119
- `${sdkLine[1]}\">=${VENDORED_DART_SDK_FLOOR} <4.0.0\"${sdkLine[3] ?? ""}`
1120
- );
1121
- }
1122
- if (next !== source) writeFileSync(pubspec, next, "utf8");
1123
- reconcilePubspec(dir, "", {
1124
- meta: VENDORED_META_DEP,
1125
- protobuf: VENDORED_PROTOBUF_DEP,
1126
- fixnum: VENDORED_FIXNUM_DEP,
1127
- });
1128
- }
1129
1141
 
1130
1142
  /** The generated-owned subdirectory, relative to `dartOut` (the pub package's `lib/`). WIPED and
1131
1143
  * rewritten on every compile — nothing hand-written may live in it. The stable facade (the package
@@ -1157,7 +1169,7 @@ function collectPackageContracts(
1157
1169
  /**
1158
1170
  * CROSS-DOMAIN REF TRAVERSAL map (dart_codegen_parity.md §2a follow-on): every aggregate id in the package
1159
1171
  * → its owning domain (`domain` for the generated client class name; `name` for the sibling
1160
- * `<name>.client.dart` import). Handed to `generateDartBridgeClient` so a same-workspace `t.ref` whose
1172
+ * `<name>.client.dart` import). Handed to `generateDartDomainClient` so a same-workspace `t.ref` whose
1161
1173
  * target lives in ANOTHER domain of the package emits a resolver taking that domain's client. A same-domain
1162
1174
  * ref never consults this (its target is in the local module); a foreign ref uses its own locator.
1163
1175
  */
@@ -1358,8 +1370,13 @@ function dartValueObjectsForModule(
1358
1370
  >();
1359
1371
  for (const descriptor of [
1360
1372
  ...(mod.valueObjects ?? []),
1373
+ ...(mod.indexedVariants ?? []).flatMap((binding) => [binding.value.descriptor, ...binding.value.dependencies]),
1361
1374
  ...collectValueObjectDescriptors(mod.aggregates),
1362
1375
  ...collectPayloadValueObjectDescriptors(codegenDirectives),
1376
+ ...collectSchemaValueObjectDescriptors([
1377
+ ...(mod.deriveds ?? []).map((read) => read.returns),
1378
+ ...(mod.combineds ?? []).map((read) => read.returns),
1379
+ ]),
1363
1380
  ]) {
1364
1381
  const prior = byName.get(descriptor.name);
1365
1382
  if (
@@ -1508,7 +1525,7 @@ function collectDartSharedSymbols(
1508
1525
  }
1509
1526
 
1510
1527
  /** Per-domain skip/import options against the shared-symbol dedup — the SAME derivation the flat
1511
- * emit and the layered-packages emit both apply before generateDartDomain / the bridge client. */
1528
+ * emit and the layered-packages emit both apply before generateDartDomain / the session client. */
1512
1529
  function domainDedupOpts(
1513
1530
  mod: DomainModule,
1514
1531
  shared: DartSharedSymbols
@@ -1564,7 +1581,7 @@ function domainDedupOpts(
1564
1581
  }
1565
1582
 
1566
1583
  /** Remove the PRE-barrel flat layout's generated files from `dartOut` root (they now live under
1567
- * `src/generated/`): the vendored support set + the per-domain files. Targeted by exact known
1584
+ * `src/generated/`): retired copied support + the per-domain files. Targeted by exact known
1568
1585
  * name — `dartOut` is typically the tenant package's `lib/`, so never sweep directories. */
1569
1586
  function removeLegacyFlatDartLayout(
1570
1587
  dartOut: string,
@@ -1572,7 +1589,7 @@ function removeLegacyFlatDartLayout(
1572
1589
  ): void {
1573
1590
  rmSync(path.join(dartOut, "nomos_client.dart"), { force: true });
1574
1591
  rmSync(path.join(dartOut, "nomos_framework.dart"), { force: true });
1575
- for (const f of DART_SUPPORT_SRC)
1592
+ for (const f of LEGACY_VENDORED_DART_SUPPORT)
1576
1593
  rmSync(path.join(dartOut, "src", f), { force: true });
1577
1594
  for (const name of moduleNames) {
1578
1595
  rmSync(path.join(dartOut, `${name}.dart`), { force: true });
@@ -1583,8 +1600,8 @@ function removeLegacyFlatDartLayout(
1583
1600
  /**
1584
1601
  * Emit the typed Dart for the composed domain modules into `dartOut`: one
1585
1602
  * `<name>.dart` per domain (the SAME `generateDartDomain` path the co2 tenant's
1586
- * `emit_dart.ts` drives) + the vendored `nomos_client.dart` barrel and its `src/`
1587
- * support files, with the generated files' `package:` import rewritten relative.
1603
+ * `emit_dart.ts` drives). Runtime support is always imported from the fixed
1604
+ * `nomos_client` package.
1588
1605
  * Everything lands under `dartOut/src/generated/` (generated-owned, wiped per
1589
1606
  * compile) behind ONE generated package barrel `dartOut/<barrelName>.dart` — the
1590
1607
  * analyzer-clean plug-and-play entrypoint (zero `show`/`hide`; framework-shared
@@ -1644,6 +1661,491 @@ function beginAtomicGenDir(genDir: string): void {
1644
1661
  });
1645
1662
  }
1646
1663
 
1664
+ /** Select the operation contracts shared by both generated language bindings. */
1665
+ function operationProtoLaws(
1666
+ modules: readonly DomainModule[],
1667
+ lawGraphs: ReadonlyMap<string, LawGraph>,
1668
+ lawPaths: ReadonlyMap<string, ReadonlyMap<string, string>>
1669
+ ): readonly OperationProtoLaw[] {
1670
+ const fixedDirectives = (graph: LawGraph): string[] => {
1671
+ const selected = fixedFrameworkCallDefinitions.flatMap((definition) => {
1672
+ const route = definition.routeId ?? definition.directive.id;
1673
+ return graph.nodes.some(
1674
+ (node) =>
1675
+ node.kind === "intent" &&
1676
+ (node.entity.label === route ||
1677
+ node.publicRoute === route ||
1678
+ node.entity.label === definition.directive.id ||
1679
+ node.publicRoute === definition.directive.id)
1680
+ )
1681
+ ? [route]
1682
+ : [];
1683
+ });
1684
+ if (
1685
+ graph.nodes.some(
1686
+ (node) =>
1687
+ node.kind === "intent" &&
1688
+ (node.entity.label === BUSINESS_CORRECTION_DIRECTIVE ||
1689
+ node.publicRoute === BUSINESS_CORRECTION_DIRECTIVE)
1690
+ )
1691
+ ) {
1692
+ selected.push(BUSINESS_CORRECTION_DIRECTIVE);
1693
+ }
1694
+ // Generated owned-record actions and the device approval door are emitted
1695
+ // by the TypeScript Reception as nominal calls. Their private law routes
1696
+ // still need operation payload messages in the packed client schema.
1697
+ for (const node of graph.nodes) {
1698
+ if (node.kind !== "intent") continue;
1699
+ const route = node.publicRoute ?? node.entity.label;
1700
+ if (/^nomos_internal_(?:share|unshare|transfer)_owned_record_[0-9a-f_]+$/.test(route)
1701
+ || [OWNED_DEVICE_APPROVAL_DIRECTIVE, OWNED_INITIALIZE_DIRECTIVE,
1702
+ OWNED_RECOVERY_ACCEPT_DIRECTIVE, OWNED_RECOVERY_COMPLETE_DIRECTIVE,
1703
+ OWNED_RECOVERY_PROTECT_DIRECTIVE, OWNED_RECOVERY_REQUEST_DIRECTIVE].includes(route)) {
1704
+ selected.push(route);
1705
+ }
1706
+ }
1707
+ // The declared receiving-group recovery door lives in the compiler-owned application-home graph
1708
+ // (no tenant DomainModule reaches this list through `dartFrameworkOperationDirectiveIds`); the
1709
+ // generated Dart binds it like a fixed framework call, so its payload rides the same contract.
1710
+ if (
1711
+ graph.nodes.some(
1712
+ (node) =>
1713
+ node.kind === "intent" &&
1714
+ node.entity.label === receivingGroupRecoveryModule.RECOVER_RECEIVING_GROUP_DEVICE_ID
1715
+ )
1716
+ ) {
1717
+ selected.push(receivingGroupRecoveryModule.RECOVER_RECEIVING_GROUP_DEVICE_ID);
1718
+ }
1719
+ // The framework child-authentication repair is composed into the same graph whenever the package
1720
+ // births children and is bound by the generated Dart the same way (by id), so its payload message
1721
+ // must ride the operation transport contract too.
1722
+ if (
1723
+ graph.nodes.some(
1724
+ (node) =>
1725
+ node.kind === "intent" &&
1726
+ node.entity.label === childAuthenticationModule.REPAIR_CHILD_AUTHENTICATION_ID
1727
+ )
1728
+ ) {
1729
+ selected.push(childAuthenticationModule.REPAIR_CHILD_AUTHENTICATION_ID);
1730
+ }
1731
+ // The parent-directed model transition's PLATFORM DOOR (architecture/parent_directed_model_transition.md)
1732
+ // is composed into the same graph whenever the package births children, and the operator CLI authors it
1733
+ // remotely as a nominal DirectiveCall — so its payload message must ride the operation transport
1734
+ // contract exactly as the child-authentication repair's does. The child-side acceptor needs no entry:
1735
+ // nothing outside the kernel ever encodes it (it arrives as a carried effect, never as a client call).
1736
+ if (
1737
+ graph.nodes.some(
1738
+ (node) =>
1739
+ node.kind === "intent" &&
1740
+ node.entity.label === modelTransitionModule.DIRECT_CHILD_MODEL_TRANSITION_ID
1741
+ )
1742
+ ) {
1743
+ selected.push(modelTransitionModule.DIRECT_CHILD_MODEL_TRANSITION_ID);
1744
+ }
1745
+ // THE MODEL-AUTHORITY SELF-SEAT (same design, §9): the Reception offers it on the workspace itself
1746
+ // as a nominal DirectiveCall, so its holder-set payload rides the operation transport contract too.
1747
+ if (
1748
+ graph.nodes.some(
1749
+ (node) =>
1750
+ node.kind === "intent" &&
1751
+ node.entity.label === modelAuthorityRepairModule.SEAT_MODEL_AUTHORITY_ID
1752
+ )
1753
+ ) {
1754
+ selected.push(modelAuthorityRepairModule.SEAT_MODEL_AUTHORITY_ID);
1755
+ }
1756
+ return [...new Set(selected)];
1757
+ };
1758
+ const covered = new Set<string>();
1759
+ const laws = modules.flatMap((module) => {
1760
+ const graph = lawGraphs.get(module.name);
1761
+ const paths = lawPaths.get(module.name);
1762
+ if (graph === undefined || paths === undefined) return [];
1763
+ covered.add(module.name);
1764
+ // The transport schema is the typed boundary between an application and
1765
+ // the Peer Office, not a second encoding of the kernel's entire interior.
1766
+ // generateOperationProtoSchema includes every public intent itself. Add
1767
+ // only the private framework capabilities which the Peer Office invokes
1768
+ // directly; private law steps remain inside the kernel and need no host
1769
+ // transport contract.
1770
+ const directives = new Set<string>();
1771
+ for (const directive of dartBridgeClientOperationDirectiveIds(module, graph)) {
1772
+ directives.add(directive);
1773
+ }
1774
+ for (const directive of dartFrameworkOperationDirectiveIds({
1775
+ module,
1776
+ graph,
1777
+ paths,
1778
+ })) {
1779
+ directives.add(directive);
1780
+ }
1781
+ for (const directive of fixedDirectives(graph)) directives.add(directive);
1782
+ for (const policy of module.applicationWorkspaceSharing ?? []) {
1783
+ directives.add(policy.invitationLaw.redeem.id);
1784
+ }
1785
+ return [{ graph, paths, directives: [...directives] }];
1786
+ });
1787
+ // Dependency and compiler-owned framework graphs also participate in the
1788
+ // generated Peer Office surface even when they have no tenant DomainModule
1789
+ // in this package. Their public members are included automatically; retain
1790
+ // only the fixed private capabilities that generated runtime code invokes.
1791
+ for (const [name, graph] of lawGraphs) {
1792
+ if (covered.has(name)) continue;
1793
+ const paths = lawPaths.get(name);
1794
+ if (paths === undefined) continue;
1795
+ laws.push({ graph, paths, directives: fixedDirectives(graph) });
1796
+ }
1797
+ return laws;
1798
+ }
1799
+
1800
+ function operationProtoSchema(
1801
+ modules: readonly DomainModule[],
1802
+ lawGraphs: ReadonlyMap<string, LawGraph>,
1803
+ lawPaths: ReadonlyMap<string, ReadonlyMap<string, string>>
1804
+ ) {
1805
+ return generateOperationProtoSchema(
1806
+ operationProtoLaws(modules, lawGraphs, lawPaths)
1807
+ );
1808
+ }
1809
+
1810
+ function protocGenEs(): string {
1811
+ try {
1812
+ const packageRoot = path.dirname(
1813
+ createRequire(import.meta.url).resolve(
1814
+ "@bufbuild/protoc-gen-es/package.json"
1815
+ )
1816
+ );
1817
+ return path.join(packageRoot, "bin", "protoc-gen-es");
1818
+ } catch {
1819
+ throw new Error(
1820
+ "TypeScript operation Protobuf generation requires @bufbuild/protoc-gen-es"
1821
+ );
1822
+ }
1823
+ }
1824
+
1825
+ /** Emit the single application operation schema and its generated TypeScript
1826
+ * binding. The generated application bundle consumes this module directly;
1827
+ * no client is permitted to reproduce Protobuf field tags. */
1828
+ function emitTsOperationProto(
1829
+ cacheRoot: string,
1830
+ outDir: string,
1831
+ lawPath: string,
1832
+ modules: readonly DomainModule[],
1833
+ lawGraphs: ReadonlyMap<string, LawGraph>,
1834
+ lawPaths: ReadonlyMap<string, ReadonlyMap<string, string>>
1835
+ ): string[] {
1836
+ const schema = operationProtoSchema(modules, lawGraphs, lawPaths);
1837
+ const protoPath = path.join(outDir, schema.fileName);
1838
+ writeFileSync(protoPath, schema.source, "utf8");
1839
+ // Standard protoc owns the descriptor format. The kernel checker compares
1840
+ // this complete schema, not sample values or strings in a generated client.
1841
+ execFileSync("protoc", ["-I", outDir, "--include_imports",
1842
+ `--descriptor_set_out=${path.join(outDir, "operation_payloads.descriptor.pb")}`, protoPath], { stdio: "pipe" });
1843
+ checkOperationContracts(lawPath, path.join(outDir, "operation_payloads.descriptor.pb"), DSL_DIR, cacheRoot);
1844
+ const binding = path.join(outDir, "operation_payloads_pb.ts");
1845
+ const generator = protocGenEs();
1846
+ const protocVersion = execFileSync("protoc", ["--version"], {
1847
+ encoding: "utf8",
1848
+ }).trim();
1849
+ const { cacheDir, cachedBinding } = tsOperationProtoCache(
1850
+ cacheRoot,
1851
+ schema.source,
1852
+ protocVersion,
1853
+ generator
1854
+ );
1855
+ if (existsSync(cachedBinding)) {
1856
+ copyFileSync(cachedBinding, binding);
1857
+ return [protoPath, binding];
1858
+ }
1859
+ try {
1860
+ execFileSync("protoc", tsProtocArgs(outDir, protoPath, generator), {
1861
+ stdio: "pipe",
1862
+ });
1863
+ } catch (error) {
1864
+ const detail = error instanceof Error ? error.message : String(error);
1865
+ throw new Error(
1866
+ `TypeScript operation Protobuf generation failed. Install protoc and ` +
1867
+ `@bufbuild/protoc-gen-es. ${detail}`
1868
+ );
1869
+ }
1870
+ if (!existsSync(binding)) {
1871
+ throw new Error(
1872
+ "TypeScript operation Protobuf generation did not emit 'operation_payloads_pb.ts'"
1873
+ );
1874
+ }
1875
+ publishCachedBinding(binding, cacheDir, cachedBinding);
1876
+ return [protoPath, binding];
1877
+ }
1878
+
1879
+ // protoc-gen-es / protoc-gen-dart are pure, expensive transforms over the application schema
1880
+ // (the Dart plugin is a Dart script whose JIT start alone is seconds). Their exact output is
1881
+ // cached by every input that can affect the bytes. There is no semantic cache manifest: the
1882
+ // key is content-addressed, the generated binding is disposable, and the normal TypeScript /
1883
+ // Dart compile still validates every hit. The prewarm variants below populate the same cache
1884
+ // from a scratch directory while the rest of the compile proceeds; the emit then hits it.
1885
+ function tsOperationProtoCache(
1886
+ cacheRoot: string,
1887
+ schemaSource: string,
1888
+ protocVersion: string,
1889
+ generator: string
1890
+ ): { cacheDir: string; cachedBinding: string } {
1891
+ const generatorPackage = path.join(
1892
+ path.dirname(path.dirname(generator)),
1893
+ "package.json"
1894
+ );
1895
+ const cacheIdentity = createHash("sha256")
1896
+ .update("nomos.operation-protobuf.typescript.v1\0")
1897
+ .update(schemaSource)
1898
+ .update("\0")
1899
+ .update(protocVersion)
1900
+ .update("\0target=ts,import_extension=js\0")
1901
+ .update(readFileSync(generatorPackage))
1902
+ .digest("hex");
1903
+ const cacheDir = path.join(
1904
+ cacheRoot,
1905
+ "node_modules",
1906
+ ".cache",
1907
+ "nomos",
1908
+ "operation-protobuf",
1909
+ "typescript"
1910
+ );
1911
+ return { cacheDir, cachedBinding: path.join(cacheDir, `${cacheIdentity}.ts`) };
1912
+ }
1913
+
1914
+ function tsProtocArgs(
1915
+ includeDir: string,
1916
+ protoPath: string,
1917
+ generator: string
1918
+ ): string[] {
1919
+ return [
1920
+ "-I",
1921
+ includeDir,
1922
+ `--plugin=protoc-gen-es=${generator}`,
1923
+ `--es_out=${includeDir}`,
1924
+ "--es_opt=target=ts,import_extension=js",
1925
+ protoPath,
1926
+ ];
1927
+ }
1928
+
1929
+ function dartProtocPlugin(): string | undefined {
1930
+ const localPlugin = path.resolve(
1931
+ DSL_DIR,
1932
+ "..",
1933
+ "nomos_flutter",
1934
+ "tool",
1935
+ "protoc-gen-dart"
1936
+ );
1937
+ const configuredPlugin = process.env.NOMOS_PROTOC_GEN_DART;
1938
+ return configuredPlugin !== undefined && configuredPlugin !== ""
1939
+ ? path.resolve(configuredPlugin)
1940
+ : existsSync(localPlugin)
1941
+ ? localPlugin
1942
+ : undefined;
1943
+ }
1944
+
1945
+ function dartProtocArgs(
1946
+ includeDir: string,
1947
+ protoPath: string,
1948
+ plugin: string | undefined
1949
+ ): string[] {
1950
+ return [
1951
+ "-I",
1952
+ includeDir,
1953
+ ...(plugin === undefined ? [] : [`--plugin=protoc-gen-dart=${plugin}`]),
1954
+ `--dart_out=${includeDir}`,
1955
+ protoPath,
1956
+ ];
1957
+ }
1958
+
1959
+ function dartOperationProtoCache(
1960
+ schemaSource: string,
1961
+ protocVersion: string,
1962
+ plugin: string | undefined
1963
+ ): { cacheDir: string; cachedBinding: string } {
1964
+ const generatorIdentity = (() => {
1965
+ if (plugin === undefined) return "PATH:protoc-gen-dart";
1966
+ const files = [
1967
+ plugin,
1968
+ path.join(path.dirname(plugin), "protoc_plugin.dart"),
1969
+ path.join(path.dirname(path.dirname(plugin)), "pubspec.lock"),
1970
+ ];
1971
+ const digest = createHash("sha256");
1972
+ for (const file of files) {
1973
+ digest.update(`\0${file}\0`);
1974
+ if (existsSync(file)) digest.update(readFileSync(file));
1975
+ }
1976
+ return digest.digest("hex");
1977
+ })();
1978
+ const cacheIdentity = createHash("sha256")
1979
+ .update("nomos.operation-protobuf.dart.v1\0")
1980
+ .update(schemaSource)
1981
+ .update("\0")
1982
+ .update(protocVersion)
1983
+ .update("\0")
1984
+ .update(generatorIdentity)
1985
+ .digest("hex");
1986
+ const cacheDir = path.join(
1987
+ process.cwd(),
1988
+ "node_modules",
1989
+ ".cache",
1990
+ "nomos",
1991
+ "operation-protobuf",
1992
+ "dart"
1993
+ );
1994
+ return { cacheDir, cachedBinding: path.join(cacheDir, `${cacheIdentity}.dart`) };
1995
+ }
1996
+
1997
+ function publishCachedBinding(
1998
+ generated: string,
1999
+ cacheDir: string,
2000
+ cachedBinding: string
2001
+ ): void {
2002
+ mkdirSync(cacheDir, { recursive: true });
2003
+ const temporaryCache = `${cachedBinding}.tmp-${process.pid}`;
2004
+ copyFileSync(generated, temporaryCache);
2005
+ renameSync(temporaryCache, cachedBinding);
2006
+ }
2007
+
2008
+ /** Generate into a scratch directory beside the cache and publish only the binding the emit
2009
+ * copies out; a failure here is silent because the real emit reports it with its own remedy. */
2010
+ async function prewarmOperationProtoCache(
2011
+ cacheDir: string,
2012
+ cachedBinding: string,
2013
+ schema: { readonly fileName: string; readonly source: string },
2014
+ generatedName: string,
2015
+ args: (includeDir: string, protoPath: string) => string[]
2016
+ ): Promise<void> {
2017
+ if (existsSync(cachedBinding)) return;
2018
+ mkdirSync(cacheDir, { recursive: true });
2019
+ const scratch = mkdtempSync(path.join(cacheDir, ".prewarm-"));
2020
+ try {
2021
+ const protoPath = path.join(scratch, schema.fileName);
2022
+ writeFileSync(protoPath, schema.source, "utf8");
2023
+ await execFileAsync("protoc", args(scratch, protoPath));
2024
+ const generated = path.join(scratch, generatedName);
2025
+ if (existsSync(generated))
2026
+ publishCachedBinding(generated, cacheDir, cachedBinding);
2027
+ } catch {
2028
+ // The emit's own protoc invocation names the remedy.
2029
+ } finally {
2030
+ rmSync(scratch, { recursive: true, force: true });
2031
+ }
2032
+ }
2033
+
2034
+ async function prewarmTsOperationProto(
2035
+ cacheRoot: string,
2036
+ modules: readonly DomainModule[],
2037
+ lawGraphs: ReadonlyMap<string, LawGraph>,
2038
+ lawPaths: ReadonlyMap<string, ReadonlyMap<string, string>>
2039
+ ): Promise<void> {
2040
+ try {
2041
+ const schema = operationProtoSchema(modules, lawGraphs, lawPaths);
2042
+ const generator = protocGenEs();
2043
+ const { stdout } = await execFileAsync("protoc", ["--version"], {
2044
+ encoding: "utf8",
2045
+ });
2046
+ const { cacheDir, cachedBinding } = tsOperationProtoCache(
2047
+ cacheRoot,
2048
+ schema.source,
2049
+ stdout.trim(),
2050
+ generator
2051
+ );
2052
+ await prewarmOperationProtoCache(
2053
+ cacheDir,
2054
+ cachedBinding,
2055
+ schema,
2056
+ "operation_payloads_pb.ts",
2057
+ (includeDir, protoPath) => tsProtocArgs(includeDir, protoPath, generator)
2058
+ );
2059
+ } catch {
2060
+ // The emit's own protoc invocation names the remedy.
2061
+ }
2062
+ }
2063
+
2064
+ async function prewarmDartOperationProto(
2065
+ modules: readonly DomainModule[],
2066
+ lawGraphs: ReadonlyMap<string, LawGraph>,
2067
+ lawPaths: ReadonlyMap<string, ReadonlyMap<string, string>>
2068
+ ): Promise<void> {
2069
+ try {
2070
+ const schema = operationProtoSchema(modules, lawGraphs, lawPaths);
2071
+ const plugin = dartProtocPlugin();
2072
+ const { stdout } = await execFileAsync("protoc", ["--version"], {
2073
+ encoding: "utf8",
2074
+ });
2075
+ const { cacheDir, cachedBinding } = dartOperationProtoCache(
2076
+ schema.source,
2077
+ stdout.trim(),
2078
+ plugin
2079
+ );
2080
+ await prewarmOperationProtoCache(
2081
+ cacheDir,
2082
+ cachedBinding,
2083
+ schema,
2084
+ schema.dartFileName,
2085
+ (includeDir, protoPath) => dartProtocArgs(includeDir, protoPath, plugin)
2086
+ );
2087
+ } catch {
2088
+ // The emit's own protoc invocation names the remedy.
2089
+ }
2090
+ }
2091
+
2092
+ function emitDartOperationProto(
2093
+ genDir: string,
2094
+ modules: readonly DomainModule[],
2095
+ lawGraphs: ReadonlyMap<string, LawGraph>,
2096
+ lawPaths: ReadonlyMap<string, ReadonlyMap<string, string>>
2097
+ ): string[] {
2098
+ const schema = operationProtoSchema(modules, lawGraphs, lawPaths);
2099
+ const protoPath = path.join(genDir, schema.fileName);
2100
+ writeFileSync(protoPath, schema.source, "utf8");
2101
+ const plugin = dartProtocPlugin();
2102
+ const protocVersion = execFileSync("protoc", ["--version"], {
2103
+ encoding: "utf8",
2104
+ }).trim();
2105
+ const { cacheDir, cachedBinding } = dartOperationProtoCache(
2106
+ schema.source,
2107
+ protocVersion,
2108
+ plugin
2109
+ );
2110
+ const generatedBinding = path.join(genDir, schema.dartFileName);
2111
+ if (existsSync(cachedBinding)) {
2112
+ copyFileSync(cachedBinding, generatedBinding);
2113
+ return [protoPath, generatedBinding];
2114
+ }
2115
+ try {
2116
+ execFileSync("protoc", dartProtocArgs(genDir, protoPath, plugin), {
2117
+ stdio: "pipe",
2118
+ });
2119
+ } catch (error) {
2120
+ const detail = error instanceof Error ? error.message : String(error);
2121
+ throw new Error(
2122
+ `Dart operation Protobuf generation failed. Install protoc and protoc-gen-dart ` +
2123
+ `(or set NOMOS_PROTOC_GEN_DART). ${detail}`
2124
+ );
2125
+ }
2126
+ const generated = readdirSync(genDir)
2127
+ .filter(
2128
+ (name) =>
2129
+ name.startsWith("operation_payloads.pb") && name.endsWith(".dart")
2130
+ )
2131
+ .sort()
2132
+ .map((name) => path.join(genDir, name));
2133
+ if (!generated.some((file) => path.basename(file) === schema.dartFileName)) {
2134
+ throw new Error(
2135
+ `Dart operation Protobuf generation did not emit '${schema.dartFileName}'`
2136
+ );
2137
+ }
2138
+ // Runtime clients use only the generated messages. Reflection JSON and the
2139
+ // empty enum companion are generator by-products, not another contract or
2140
+ // shipped source of truth.
2141
+ for (const file of generated) {
2142
+ if (path.basename(file) !== schema.dartFileName)
2143
+ rmSync(file, { force: true });
2144
+ }
2145
+ publishCachedBinding(generatedBinding, cacheDir, cachedBinding);
2146
+ return [protoPath, generatedBinding];
2147
+ }
2148
+
1647
2149
  function emitDart(
1648
2150
  modules: readonly DomainModule[],
1649
2151
  dartOut: string,
@@ -1651,9 +2153,8 @@ function emitDart(
1651
2153
  lawGraphs: ReadonlyMap<string, LawGraph>,
1652
2154
  lawPaths: ReadonlyMap<string, ReadonlyMap<string, string>>,
1653
2155
  packageInterface: Record<string, DomainInterface> = {},
1654
- supportImport: DartSupportImport = "vendored",
2156
+ supportImport: DartSupportImport = "package",
1655
2157
  barrelName = "nomos_domains",
1656
- compat?: NomosCompat,
1657
2158
  names?: Readonly<Record<string, string>>,
1658
2159
  describePackageName = barrelName
1659
2160
  ): string[] {
@@ -1667,133 +2168,8 @@ function emitDart(
1667
2168
  // Transactional: back the prior generated dir aside instead of destroying it (compile atomicity).
1668
2169
  beginAtomicGenDir(genDir);
1669
2170
 
1670
- // 1. optional vendored framework support (barrel + src/), copied byte-for-byte except
1671
- // the barrel's schema_validation export (see DART_SUPPORT_SRC).
1672
- if (supportImport === "vendored") {
1673
- ensureVendoredPackageRequirements(dartOut);
1674
- mkdirSync(path.join(genDir, "src"), { recursive: true });
1675
- // Core files come from the pure `nomos_types` package, runtime files from `nomos_client`
1676
- // (the layered split). Runtime files import the core via `package:nomos_types/...` — the
1677
- // vendored copy rewrites that to the local shim so the directory stays self-contained.
1678
- const coreDir = path.join(DSL_DIR, "dart_types", "lib", "src");
1679
- const clientDir = path.join(DSL_DIR, "dart", "lib", "src");
1680
- for (const f of DART_SUPPORT_CORE_SRC)
1681
- copyFileSync(path.join(coreDir, f), path.join(genDir, "src", f));
1682
- for (const f of DART_SUPPORT_RUNTIME_SRC) {
1683
- const src = readFileSync(path.join(clientDir, f), "utf8")
1684
- .replace(
1685
- /import 'package:nomos_types\/nomos_types\.dart'/g,
1686
- `import '${VENDORED_TYPES_SHIM}'`
1687
- )
1688
- .replace(
1689
- /import 'package:nomos_types\/src\/(?:nomos_ref|foreign_ref|versioned_nomos_ref)\.dart'/g,
1690
- `import '${VENDORED_TYPES_SHIM}'`
1691
- )
1692
- .replace(
1693
- /import 'package:nomos_types\/nomos_kernel_protocol\.dart'/g,
1694
- `import '${VENDORED_KERNEL_PROTOCOL_SHIM}'`
1695
- );
1696
- writeFileSync(path.join(genDir, "src", f), src, "utf8");
1697
- }
1698
- // Protobuf is the sole kernel/host message schema. Flat vendored packages receive a generated
1699
- // projection of that schema, never a handwritten certificate mirror. The compiler copies only the
1700
- // generated kernel files this runtime uses; `generate:kernel-proto-dart` remains their source.
1701
- const generatedKernelProto = path.join(
1702
- DSL_DIR,
1703
- "dart_types",
1704
- "lib",
1705
- "src",
1706
- "proto",
1707
- "nomos",
1708
- "kernel",
1709
- "v1"
1710
- );
1711
- const vendoredKernelProto = path.join(
1712
- genDir,
1713
- "src",
1714
- "proto",
1715
- "nomos",
1716
- "kernel",
1717
- "v1"
1718
- );
1719
- mkdirSync(vendoredKernelProto, { recursive: true });
1720
- for (const f of [
1721
- "kernel.pb.dart",
1722
- "kernel.pbenum.dart",
1723
- "progress.pb.dart",
1724
- "progress.pbenum.dart",
1725
- ]) {
1726
- copyFileSync(
1727
- path.join(generatedKernelProto, f),
1728
- path.join(vendoredKernelProto, f)
1729
- );
1730
- }
1731
- const generatedRuntimeProto = path.join(
1732
- DSL_DIR,
1733
- "dart_types",
1734
- "lib",
1735
- "src",
1736
- "proto",
1737
- "nomos",
1738
- "runtime",
1739
- "v2"
1740
- );
1741
- const vendoredRuntimeProto = path.join(
1742
- genDir,
1743
- "src",
1744
- "proto",
1745
- "nomos",
1746
- "runtime",
1747
- "v2"
1748
- );
1749
- mkdirSync(vendoredRuntimeProto, { recursive: true });
1750
- for (const f of ["runtime.pb.dart", "runtime.pbenum.dart"]) {
1751
- copyFileSync(
1752
- path.join(generatedRuntimeProto, f),
1753
- path.join(vendoredRuntimeProto, f)
1754
- );
1755
- }
1756
- writeFileSync(
1757
- path.join(genDir, "src", VENDORED_KERNEL_PROTOCOL_SHIM),
1758
- [
1759
- "// GENERATED from proto/nomos/kernel/v1/kernel.proto by nomos-compile. DO NOT EDIT.",
1760
- "library;",
1761
- "",
1762
- "export 'proto/nomos/kernel/v1/kernel.pb.dart';",
1763
- "export 'proto/nomos/kernel/v1/progress.pb.dart';",
1764
- "export 'proto/nomos/runtime/v2/runtime.pb.dart';",
1765
- "",
1766
- ].join("\n"),
1767
- "utf8"
1768
- );
1769
- // The local shim the rewritten runtime imports resolve through (the vendored twin of the
1770
- // nomos_types barrel — core files only).
1771
- writeFileSync(
1772
- path.join(genDir, "src", VENDORED_TYPES_SHIM),
1773
- [
1774
- "// GENERATED by nomos-compile — the vendored twin of the nomos_types barrel. DO NOT EDIT.",
1775
- "library;",
1776
- "",
1777
- ...DART_SUPPORT_CORE_SRC.map((f) => `export '${f}';`),
1778
- "",
1779
- ].join("\n"),
1780
- "utf8"
1781
- );
1782
- // The vendored support barrel — explicit (never derived from the real nomos_client barrel,
1783
- // which post-split re-exports the nomos_types PACKAGE the vendored tree must not depend on).
1784
- writeFileSync(
1785
- path.join(genDir, "nomos_client.dart"),
1786
- [
1787
- "// GENERATED by nomos-compile — the vendored nomos_client support barrel. DO NOT EDIT.",
1788
- "library;",
1789
- "",
1790
- ...DART_SUPPORT_SRC.map((f) => `export 'src/${f}';`),
1791
- "",
1792
- ].join("\n"),
1793
- "utf8"
1794
- );
1795
- }
1796
- // (package-support mode vendors nothing — the genDir wipe above already cleared any prior set)
2171
+ // Fixed runtime support is imported from nomos_client; generated packages never copy it.
2172
+ ensureFlatDartPackageRequirements(dartOut);
1797
2173
 
1798
2174
  // 2. FRAMEWORK-SHARED symbol dedup (Dart-codegen hygiene). Framework-owned symbols fold into MANY
1799
2175
  // domains, so their generated Dart would be DEFINED once per domain — a package barrel re-exporting
@@ -1822,6 +2198,7 @@ function emitDart(
1822
2198
  : undefined;
1823
2199
 
1824
2200
  const written: string[] = [];
2201
+ written.push(...emitDartOperationProto(genDir, modules, lawGraphs, lawPaths));
1825
2202
 
1826
2203
  const writeGenerated = (fileBase: string, src: string): string => {
1827
2204
  if (!src.includes(DART_PACKAGE_IMPORT)) {
@@ -1830,23 +2207,16 @@ function emitDart(
1830
2207
  );
1831
2208
  }
1832
2209
  const p = path.join(genDir, `${fileBase}.dart`);
1833
- writeFileSync(
1834
- p,
1835
- supportImport === "vendored"
1836
- ? src.replace(DART_PACKAGE_IMPORT, DART_RELATIVE_IMPORT)
1837
- : src,
1838
- "utf8"
1839
- );
2210
+ writeFileSync(p, src, "utf8");
1840
2211
  domainSources.push(src);
1841
2212
  sourceByFile.set(fileBase, src);
1842
2213
  written.push(p);
1843
2214
  return p;
1844
2215
  };
1845
2216
 
1846
- // Compiler-owned framework ceremonies (birth policy, signer seating, genesis, invitation
1847
- // redemption) need the same generated USDA call types in flat/vendored output as in layered pub
1848
- // packages. Keeping this file private to `src/generated` gives tests and generated application
1849
- // infrastructure a typed setup lane without reintroducing raw payload maps.
2217
+ // Compiler-owned framework ceremonies are generated stationery over the
2218
+ // fixed nomos_client API. Keeping this file private to `src/generated` gives
2219
+ // tests and generated facades a typed setup lane without runtime lifecycle.
1850
2220
  const applicationFrameworkCallsSource = generateDartApplicationFrameworkCalls(
1851
2221
  modules.flatMap((module) => {
1852
2222
  const graph = lawGraphs.get(module.name);
@@ -1854,37 +2224,22 @@ function emitDart(
1854
2224
  return graph === undefined || paths === undefined
1855
2225
  ? []
1856
2226
  : [{ module, graph, paths }];
1857
- })
2227
+ }),
2228
+ { lawSha256: domainHash }
1858
2229
  );
1859
2230
  if (applicationFrameworkCallsSource !== undefined) {
1860
- const source =
1861
- supportImport === "vendored"
1862
- ? applicationFrameworkCallsSource
1863
- .replace(
1864
- "import 'package:nomos_types/nomos_kernel_protocol.dart' show BirthCertificate;",
1865
- `import 'src/${VENDORED_KERNEL_PROTOCOL_SHIM}' show BirthCertificate;`
1866
- )
1867
- .replace(
1868
- "import 'package:nomos_types/nomos_types.dart' show NomosSlot, Sha256Digest;",
1869
- `import 'src/${VENDORED_TYPES_SHIM}' show NomosSlot, Sha256Digest;`
1870
- )
1871
- .replace(
1872
- "import 'package:nomos_client/src/application_framework_calls.dart';",
1873
- "import 'src/application_framework_calls.dart';"
1874
- )
1875
- .replace(
1876
- "import 'package:nomos_client/src/generated_call.dart';",
1877
- "import 'src/generated_call.dart';"
1878
- )
1879
- : applicationFrameworkCallsSource;
1880
2231
  const applicationFrameworkCallsPath = path.join(
1881
2232
  genDir,
1882
2233
  "application.framework_calls.dart"
1883
2234
  );
1884
- writeFileSync(applicationFrameworkCallsPath, source, "utf8");
1885
- written.push(applicationFrameworkCallsPath);
1886
- }
1887
-
2235
+ writeFileSync(
2236
+ applicationFrameworkCallsPath,
2237
+ applicationFrameworkCallsSource,
2238
+ "utf8"
2239
+ );
2240
+ written.push(applicationFrameworkCallsPath);
2241
+ }
2242
+
1888
2243
  // 2a. the shared framework file — the framework aggregates + directives + queries, ONCE. Its own
1889
2244
  // surface is emitted in full (no skip). Imported by every domain that folds any framework symbol.
1890
2245
  if (shared.frameworkMod !== undefined) {
@@ -1934,11 +2289,11 @@ function emitDart(
1934
2289
  // break — fail with a clear diagnostic. Contract-mediated refs go through contracts.dart (no concrete edge).
1935
2290
  guardNoConcreteImportCycle(modules, aggIdToDartFile);
1936
2291
 
1937
- // 2b. one generated file per composed domain — the TYPES (`<name>.dart`) + the async bridge
2292
+ // 2b. one generated file per composed domain — the TYPES (`<name>.dart`) + the async session
1938
2293
  // CLIENT (`<name>.client.dart`, the Flutter sibling of `<name>.client.ts`).
1939
2294
  for (const mod of modules) {
1940
2295
  // Default the read-model allowlist to EVERY aggregate when the domain didn't pin one, so
1941
- // `<name>.dart` carries a read model per aggregate — the bridge client imports them. A domain
2296
+ // `<name>.dart` carries a read model per aggregate — the session client imports them. A domain
1942
2297
  // that pins `readModelAggregates` keeps its choice (byte-identical to before this default).
1943
2298
  // Skip framework symbols this domain folds (they live in the shared file); reference them via the
1944
2299
  // framework import. Only pass the import when this domain actually folds a framework symbol.
@@ -2009,14 +2364,13 @@ function emitDart(
2009
2364
  });
2010
2365
  writeGenerated(mod.name, src);
2011
2366
 
2012
- const clientSrc = generateDartBridgeClient(modForDart, {
2367
+ const clientSrc = generateDartDomainClient(modForDart, {
2013
2368
  domainHash,
2014
2369
  callUsda: {
2015
2370
  graph: lawGraphs.get(mod.name)!,
2016
2371
  paths: lawPaths.get(mod.name)!,
2017
2372
  },
2018
2373
  interface: packageInterface[modForDart.domain ?? modForDart.name],
2019
- ...(compat !== undefined ? { compat } : {}),
2020
2374
  supportImport,
2021
2375
  ...(domainFrameworkImport !== undefined
2022
2376
  ? { frameworkImportUri: domainFrameworkImport }
@@ -2028,6 +2382,7 @@ function emitDart(
2028
2382
  const applicationSrc = generateDartApplication(modForDart, {
2029
2383
  supportImport,
2030
2384
  packageAggregateDomains,
2385
+ callUsda: { graph: lawGraphs.get(mod.name)!, paths: lawPaths.get(mod.name)! },
2031
2386
  });
2032
2387
  const clientFile = `${mod.name}.client.dart`;
2033
2388
  const applicationFile = `${mod.name}.application.dart`;
@@ -2036,7 +2391,7 @@ function emitDart(
2036
2391
  applicationSrc,
2037
2392
  clientFile,
2038
2393
  applicationFile,
2039
- new Set(dartApplicationPublicClassNames(modForDart))
2394
+ new Set(dartApplicationPublicClassNames(modForDart, { callUsda: { graph: lawGraphs.get(mod.name)!, paths: lawPaths.get(mod.name)! } }))
2040
2395
  );
2041
2396
  const cp = path.join(genDir, clientFile);
2042
2397
  writeFileSync(cp, encapsulated.clientPart, "utf8");
@@ -2101,9 +2456,7 @@ function emitDart(
2101
2456
  ),
2102
2457
  // The generated clients import their runtime support directly. Keeping it out of the public barrel is
2103
2458
  // the surface firewall: a tenant import receives business types and application APIs, never raw runtime.
2104
- supportImport === "vendored"
2105
- ? `export '${genRel}/src/${VENDORED_TYPES_SHIM}' show NomosRef, NomosOptionalRef, NomosSlot, VersionedNomosRef, ReferenceVersionStatus, ReferenceVersionCurrent, ReferenceVersionOutdated, ReferenceVersionUnavailable${sha256DigestExport}${gitObjectIdExport};`
2106
- : `export 'package:nomos_types/nomos_types.dart' show NomosRef, NomosOptionalRef, NomosSlot, VersionedNomosRef, ReferenceVersionStatus, ReferenceVersionCurrent, ReferenceVersionOutdated, ReferenceVersionUnavailable${sha256DigestExport}${gitObjectIdExport};`,
2459
+ `export 'package:nomos_types/nomos_types.dart' show NomosRef, NomosOptionalRef, NomosSlot, VersionedNomosRef, ReferenceVersionStatus, ReferenceVersionCurrent, ReferenceVersionOutdated, ReferenceVersionUnavailable${sha256DigestExport}${gitObjectIdExport};`,
2107
2460
  ...(contractsImportUri !== undefined
2108
2461
  ? tenantDartExport(
2109
2462
  `${genRel}/${CONTRACTS_FILE}.dart`,
@@ -2124,7 +2477,7 @@ function emitDart(
2124
2477
  ),
2125
2478
  `export '${genRel}/${
2126
2479
  m.name
2127
- }.application.dart' show ${dartApplicationPublicClassNames(m).join(
2480
+ }.application.dart' show ${dartApplicationPublicClassNames(m, { callUsda: { graph: lawGraphs.get(m.name)!, paths: lawPaths.get(m.name)! } }).join(
2128
2481
  ", "
2129
2482
  )};`,
2130
2483
  ]),
@@ -2187,18 +2540,44 @@ const dartTypesImportFor = (generatedSource: string): string => {
2187
2540
  * just the floor generated code needs — a lagging pin forces tenants into dependency_overrides. */
2188
2541
  // 0.3.0: SurfacedValue<T>/SurfacedObservation/PendingConflictRow — generated Surfaced-field typing +
2189
2542
  // the pendingConflicts read rows.
2190
- const NOMOS_TYPES_DEP = "^0.10.1";
2543
+ const NOMOS_TYPES_DEP = "^0.11.2";
2191
2544
  // 0.48.x: session-bound clients (XClient.bind/NomosClientInterface/DomainClientSessionMismatch/
2192
2545
  // DomainNotInstalledForSession, engine.follow, pendingConflicts lanes) — the generated client
2193
2546
  // REQUIRES this surface. 0.48.1 fixes NomosSession.watchPendingConflicts delegation.
2194
- const NOMOS_CLIENT_DEP = "^0.65.3";
2195
- const NOMOS_FLUTTER_DEP = "^0.64.3";
2547
+ const NOMOS_CLIENT_DEP = "^0.66.5";
2196
2548
  // DRIFT TRIPWIRE: pubspec_pins.test.ts asserts these equal the CURRENT versions in dsl/dart/pubspec.yaml
2197
2549
  // and dsl/dart_types/pubspec.yaml — a Dart release without this bump fails the suite, so tenants never
2198
2550
  // land back in dependency_overrides (the exact co2 report this closes, third occurrence).
2199
2551
 
2200
2552
  const isPubPackageName = (s: string): boolean => /^[a-z][a-z0-9_]*$/.test(s);
2201
2553
 
2554
+ /** The tenant pub package a FLAT `dart.out` writes into, if any: the nearest pubspec at or above
2555
+ * the output directory. ONE definition, because two places need the same answer — the dependency
2556
+ * reconciler that pins nomos_client into that pubspec, and the source-dogfood override that makes
2557
+ * the pin resolvable from a checkout. A flat `dart.out: pkg/lib` puts that pubspec ABOVE
2558
+ * `dart.out`, where the analyze gate's own package walk deliberately stops. */
2559
+ function flatDartPackageRoot(dartOut: string): string | undefined {
2560
+ let directory = path.resolve(dartOut);
2561
+ for (let hops = 0; hops < 8; hops++) {
2562
+ if (existsSync(path.join(directory, "pubspec.yaml"))) return directory;
2563
+ const parent = path.dirname(directory);
2564
+ if (parent === directory) return undefined;
2565
+ directory = parent;
2566
+ }
2567
+ return undefined;
2568
+ }
2569
+
2570
+ function ensureFlatDartPackageRequirements(dartOut: string): void {
2571
+ const directory = flatDartPackageRoot(dartOut);
2572
+ if (directory === undefined) return;
2573
+ reconcilePubspec(directory, "", {
2574
+ nomos_types: NOMOS_TYPES_DEP,
2575
+ nomos_client: NOMOS_CLIENT_DEP,
2576
+ protobuf: "^5.0.0",
2577
+ fixnum: "^1.1.1",
2578
+ });
2579
+ }
2580
+
2202
2581
  /** `import` lines a barrel needs so its `typedef` aliases can SEE the generated symbols
2203
2582
  * (an `export` alone re-exports but does not bring names into scope). Empty when no aliases. */
2204
2583
  const aliasImportLines = (
@@ -2253,7 +2632,7 @@ function tenantDartExportNames(src: string): string[] {
2253
2632
  }
2254
2633
 
2255
2634
  /**
2256
- * Turn the generated bridge client into a Dart `part` of the business application library.
2635
+ * Turn the generated session client into a Dart `part` of the business application library.
2257
2636
  *
2258
2637
  * `lib/src` is only a convention: a tenant can deep-import a normal Dart library. A part file is a
2259
2638
  * language boundary — it cannot be imported on its own — and its underscore-prefixed declarations are
@@ -2330,7 +2709,7 @@ function encapsulateDartClient(
2330
2709
  continue;
2331
2710
  // Preserve business-type re-exports so one application import carries its
2332
2711
  // payloads and read models. Framework/runtime exports belonged to the retired
2333
- // bridge library and must not enter tenant autocomplete.
2712
+ // session library and must not enter tenant autocomplete.
2334
2713
  if (
2335
2714
  kind === "export" &&
2336
2715
  client.directives.includes(line) &&
@@ -2406,7 +2785,7 @@ function generatedPackageApplication(
2406
2785
  }[],
2407
2786
  homeDomain: string,
2408
2787
  rootDomains: readonly string[],
2409
- remoteDomains: readonly string[],
2788
+ parentWorkspaceDomains: readonly string[],
2410
2789
  hasApplicationFrameworkCalls: boolean
2411
2790
  ): string {
2412
2791
  const application = dartApplicationClassName(packageName);
@@ -2431,31 +2810,26 @@ function generatedPackageApplication(
2431
2810
  "generated Dart package application could not bind every co-resident root domain"
2432
2811
  );
2433
2812
  }
2434
- const remoteMembers = domains
2435
- .filter(({ domain }) => remoteDomains.includes(domain))
2813
+ const parentMembers = domains
2814
+ .filter(({ domain }) => parentWorkspaceDomains.includes(domain))
2436
2815
  .map(({ domain, file }, index) => ({
2437
2816
  domain,
2438
2817
  file,
2439
- prefix: `_nmsRemoteDomain${index}`,
2818
+ prefix: `_nmsParentDomain${index}`,
2440
2819
  member: dartApplicationMember(domain),
2441
2820
  application: dartApplicationClassName(domain),
2442
2821
  }));
2443
- if (remoteMembers.length !== remoteDomains.length) {
2822
+ if (parentMembers.length !== parentWorkspaceDomains.length) {
2444
2823
  throw new Error(
2445
- "generated Dart package application could not describe every verified remote ancestor domain"
2824
+ "generated Dart package application could not describe every verified parent-workspace domain"
2446
2825
  );
2447
2826
  }
2448
- const invitationPolicies = members.flatMap(({ domain, invitationPolicies }) =>
2449
- invitationPolicies.map((policy) => ({ domain, ...policy }))
2827
+ const invitationPolicies = members.flatMap(
2828
+ ({ domain, member, invitationPolicies }) =>
2829
+ invitationPolicies.map((policy) => ({ domain, member, ...policy }))
2450
2830
  );
2451
2831
  const hasInvitations = invitationPolicies.length > 0;
2452
- const rootMember = members.find(({ domain }) => domain === homeDomain);
2453
- if (rootMember === undefined) {
2454
- throw new Error(
2455
- `generated Dart package application cannot expose the model for home domain '${homeDomain}'`
2456
- );
2457
- }
2458
- const surfaceMembers = [...members, ...remoteMembers];
2832
+ const surfaceMembers = [...members, ...parentMembers];
2459
2833
  const duplicate = surfaceMembers.find(
2460
2834
  (candidate, index) =>
2461
2835
  surfaceMembers.findIndex((other) => other.member === candidate.member) !==
@@ -2467,11 +2841,9 @@ function generatedPackageApplication(
2467
2841
  );
2468
2842
  }
2469
2843
  const frameworkMembers = new Map<string, string>([
2470
- ["nomosWidget", "the generated Nomos Flutter bootstrap"],
2471
2844
  ["currentUser", "the generated signed-in application participant"],
2472
2845
  ["currentStatus", "the generated application status"],
2473
2846
  ["watchStatus", "the generated application status watch"],
2474
- ["model", "the generated model lifecycle"],
2475
2847
  ["recipient", "the generated typed recipient boundary"],
2476
2848
  ["createInvitation", "the generated invitation capability"],
2477
2849
  ["redeemInvitation", "the generated invitation redemption"],
@@ -2492,16 +2864,21 @@ function generatedPackageApplication(
2492
2864
  }
2493
2865
  return [
2494
2866
  `// AUTO-GENERATED by nomos-compile — the one tenant application for "${packageName}".`,
2495
- `// Domain clients remain generated implementation details behind this stateful business surface.`,
2867
+ `// Domain clients remain generated implementation details behind this stateless business facade.`,
2496
2868
  `// ignore_for_file: invalid_use_of_internal_member`,
2497
- `import 'package:flutter/widgets.dart';`,
2498
- `import 'package:nomos_flutter/src/application_status.dart' show ApplicationSignIn, ApplicationStatus;`,
2499
- `import 'package:nomos_client/src/nms_generated_runtime.dart' show NmsInternalUsers, NomosSession, User${
2869
+ `import 'package:nomos_client/src/application_status.dart' show ApplicationStatus;`,
2870
+ `import 'package:nomos_client/src/nms_generated_runtime.dart' show NmsInternalUsers, User${
2500
2871
  hasInvitations ? ", Invitation, Recipient" : ""
2501
2872
  };`,
2502
- `import 'package:nomos_flutter/src/generated_application_host.dart' show ApplicationModelChange, NomosApplicationStartupState, NmsInternalApplicationHost, NmsInternalApplicationUserBinding${
2503
- hasInvitations ? ", NmsInternalInvitationPolicy" : ""
2873
+ `import 'package:nomos_client/src/reception/reception_session.dart' show NmsReceptionSession${
2874
+ hasInvitations ? ", NmsReceptionInvitationPolicy" : ""
2504
2875
  };`,
2876
+ ...(hasApplicationFrameworkCalls
2877
+ ? [
2878
+ `import 'package:nomos_client/src/application_framework_calls.dart' show NmsApplicationFrameworkCalls;`,
2879
+ `import 'package:nomos_client/src/reception/reception_contract.dart' show NmsReceptionContract;`,
2880
+ ]
2881
+ : []),
2505
2882
  ...(hasApplicationFrameworkCalls
2506
2883
  ? [
2507
2884
  `import 'application.framework_calls.dart' show nomosInternalApplicationFrameworkCalls;`,
@@ -2510,151 +2887,116 @@ function generatedPackageApplication(
2510
2887
  ...members.map(
2511
2888
  ({ file, prefix }) => `import '${file}.application.dart' as ${prefix};`
2512
2889
  ),
2513
- ...remoteMembers.map(
2890
+ ...parentMembers.map(
2514
2891
  ({ file, prefix }) => `import '${file}.application.dart' as ${prefix};`
2515
2892
  ),
2516
2893
  ``,
2517
- `/// The one canonical stateful application for ${packageName}.`,
2894
+ `/// The typed ${packageName} vocabulary bound to one Peer Office handle.`,
2518
2895
  `///`,
2519
2896
  `/// Keep business UI and services on this object. Nomos owns identity, persistence,`,
2520
2897
  `/// synchronization, workspace navigation, access delivery and recovery underneath it;`,
2521
2898
  `/// do not build a second runtime host, repository, session, or application-state mirror.`,
2522
2899
  `///`,
2523
- `/// Canonical bootstrap:`,
2524
- "/// ```dart",
2525
- `/// return ${application}.nomosWidget(`,
2526
- `/// cloud: cloud,`,
2527
- `/// application: '${packageName}',`,
2528
- `/// signIn: signIn,`,
2529
- `/// builder: (context, application) => MyBusinessUi(application: application),`,
2530
- `/// );`,
2531
- "/// ```",
2532
- `/// Pass that same [${application}] down. It is the application state.`,
2900
+ `/// The platform shell opens Reception, then calls [receive${application}] once.`,
2901
+ `/// Pass that same [${application}] down; generated code never opens custody or a kernel.`,
2533
2902
  `final class ${application} {`,
2534
- ` final NomosSession _nmsSession;`,
2535
- ...(hasInvitations
2536
- ? [` final NmsInternalApplicationUserBinding _nmsUser;`]
2537
- : []),
2903
+ ` final NmsReceptionSession _nmsReception;`,
2538
2904
  ` final User currentUser;`,
2539
2905
  ...members.flatMap(({ domain, member, application: child, prefix }) => [
2540
2906
  ` /// Business actions, records and watches declared by ${domain}.`,
2541
2907
  ` final ${prefix}.${child} ${member};`,
2542
2908
  ]),
2543
- ...remoteMembers.flatMap(
2909
+ ...parentMembers.flatMap(
2544
2910
  ({ domain, member, application: child, prefix }) => [
2545
- ` /// Typed surface for verified remote ancestor ${domain}. It is never opened in local custody.`,
2546
- ` /// Remote reads and offers are not yet generated for this ancestor surface.`,
2911
+ ` /// Typed law surface for verified parent workspace domain ${domain}.`,
2912
+ ` /// Its separate workspace is lineage; where that custody runs is an independent placement decision.`,
2547
2913
  ` ${prefix}.${child} get ${member} => throw StateError(`,
2548
- ` 'The verified ancestor ${domain} is remote and cannot be opened in this device workspace.',`,
2914
+ ` 'The ${domain} domain belongs to the verified parent workspace and needs a typed remote-workspace binding.',`,
2549
2915
  ` );`,
2550
2916
  ]
2551
2917
  ),
2552
- ` const ${application}._({required NomosSession nmsSession, ${
2553
- hasInvitations
2554
- ? "required NmsInternalApplicationUserBinding nmsUser, "
2555
- : ""
2556
- }required this.currentUser, ${members
2918
+ ` const ${application}._({required NmsReceptionSession nmsReception, required this.currentUser, ${members
2557
2919
  .map(({ member }) => `required this.${member}`)
2558
- .join(", ")}}) : _nmsSession = nmsSession${
2559
- hasInvitations ? ", _nmsUser = nmsUser" : ""
2560
- };`,
2561
- ``,
2562
- ` /// Prove, adopt or restore the package law against this workspace's exact history.`,
2563
- ` ${rootMember.prefix}.NomosModel get model => ${rootMember.member}.model;`,
2920
+ .join(", ")}}) : _nmsReception = nmsReception;`,
2564
2921
  ``,
2565
2922
  ` /// Current durability and convergence for the receiving-group workspace.`,
2566
2923
  ` /// A slow or offline sibling never makes this workspace's readiness ambiguous.`,
2567
- ` ApplicationStatus get currentStatus => NmsInternalApplicationHost.currentStatus(_nmsSession);`,
2924
+ ` ApplicationStatus get currentStatus => _nmsReception.currentStatus;`,
2568
2925
  ``,
2569
2926
  ` /// React whenever this receiving group saves, receives work, goes offline or needs attention.`,
2570
- ` Stream<ApplicationStatus> watchStatus() => NmsInternalApplicationHost.watchStatus(_nmsSession);`,
2927
+ ` Stream<ApplicationStatus> watchStatus() => _nmsReception.streams.status;`,
2571
2928
  ``,
2572
2929
  ` /// Wait until this receiving group's accepted work is in custody and caught up.`,
2573
2930
  ` Future<void> whenCurrent({Duration timeout = const Duration(minutes: 2)}) =>`,
2574
- ` NmsInternalApplicationHost.whenCurrent(_nmsSession, timeout: timeout);`,
2931
+ ` _nmsReception.whenCurrent(timeout: timeout);`,
2575
2932
  ``,
2576
2933
  ` /// Aggregate posture across every child currently owned by this generated application.`,
2577
- ` ApplicationStatus get applicationStatus => NmsInternalApplicationHost.currentApplicationStatus(_nmsSession);`,
2934
+ ` ApplicationStatus get applicationStatus => _nmsReception.applicationStatus;`,
2578
2935
  ``,
2579
2936
  ` /// React to aggregate posture across every generated child currently in this application plane.`,
2580
- ` Stream<ApplicationStatus> watchApplicationStatus() => NmsInternalApplicationHost.watchApplicationStatus(_nmsSession);`,
2937
+ ` Stream<ApplicationStatus> watchApplicationStatus() => _nmsReception.streams.applicationStatus;`,
2581
2938
  ``,
2582
2939
  ` /// Explicitly wait for every generated child currently owned by this application.`,
2583
2940
  ` Future<void> whenApplicationCurrent({Duration timeout = const Duration(minutes: 2)}) =>`,
2584
- ` NmsInternalApplicationHost.whenApplicationCurrent(_nmsSession, timeout: timeout);`,
2941
+ ` _nmsReception.whenApplicationCurrent(timeout: timeout);`,
2585
2942
  ...(hasInvitations
2586
2943
  ? [
2587
2944
  ``,
2588
2945
  ` /// Turn an identity-provider uid into a typed invitation recipient.`,
2589
- ` Recipient<User> recipient(String uid) => NmsInternalApplicationHost.recipient<User>(uid);`,
2946
+ ` Recipient<User> recipient(String uid) => _nmsReception.recipient<User>(uid);`,
2590
2947
  ``,
2591
2948
  ` /// Create an invitation; transport its link however your product chooses.`,
2592
- ` Future<Invitation<User>> createInvitation() => NmsInternalApplicationHost.createInvitation<User>(_nmsSession);`,
2949
+ ` Future<Invitation<User>> createInvitation() => _nmsReception.createInvitation<User>();`,
2593
2950
  ``,
2594
2951
  ` /// Redeem a transported invitation as [currentUser].`,
2595
- ` Future<void> redeemInvitation(String link) => NmsInternalApplicationHost.redeemInvitation<User>(`,
2596
- ` _nmsSession,`,
2952
+ ` Future<void> redeemInvitation(String link) => _nmsReception.redeemInvitation<User>(`,
2597
2953
  ` frameworkCalls: nomosInternalApplicationFrameworkCalls,`,
2598
2954
  ` link: link,`,
2599
- ` identity: _nmsUser.nmsInternalActor,`,
2955
+ // Compose each domain application's own policies. Rebuilding them here cannot work: the
2956
+ // pending lookup is a framework-internal sharing query that lives on the domain CLIENT
2957
+ // and is deliberately absent from the domain APPLICATION this member holds.
2600
2958
  ` policies: [`,
2601
- ...invitationPolicies.flatMap((policy) => [
2602
- ` NmsInternalInvitationPolicy(`,
2603
- ` domain: ${JSON.stringify(policy.domain)},`,
2604
- ` domainHash: ${JSON.stringify(policy.domainHash)},`,
2605
- ` query: ${JSON.stringify(policy.query)},`,
2606
- ` redeem: ${JSON.stringify(policy.redeem)},`,
2607
- ` personField: ${JSON.stringify(policy.personField)},`,
2608
- ` roleField: ${JSON.stringify(policy.roleField)},`,
2609
- ` scopeField: ${JSON.stringify(policy.scopeField)},`,
2610
- ` ),`,
2611
- ]),
2959
+ ...[...new Set(invitationPolicies.map((policy) => policy.member))].map(
2960
+ (member) => ` ...${member}.nomosInternalInvitationPolicies,`
2961
+ ),
2612
2962
  ` ],`,
2613
2963
  ` );`,
2614
2964
  ]
2615
2965
  : []),
2616
2966
  ``,
2617
- ` /// Mount the one application at the UI boundary and pass [application] down.`,
2618
- ` ///`,
2619
- ` /// This is the only application bootstrap tenant code should own.`,
2620
- ` ///`,
2621
- ` /// The object supplied to [builder] is the application state. Keep it as the only`,
2622
- ` /// business-state dependency; do not recreate its records in providers, repositories or caches.`,
2623
- ` static Widget nomosWidget({`,
2624
- ` Key? key,`,
2625
- ` required String cloud,`,
2626
- ` required String application,`,
2627
- ` required ApplicationSignIn signIn,`,
2628
- ` required Widget Function(BuildContext context, ${application} application) builder,`,
2629
- ` Widget Function(BuildContext context)? loadingBuilder,`,
2630
- ` Widget Function(BuildContext context, Object error, VoidCallback retry)? errorBuilder,`,
2631
- ` ApplicationModelChange? onModelChange,`,
2632
- ` ValueChanged<NomosApplicationStartupState>? onStartupStateChanged,`,
2633
- ` Duration startupTimeout = const Duration(minutes: 3),`,
2634
- ` }) => NmsInternalApplicationHost.home<${application}>(`,
2635
- ` key: key, cloud: cloud, application: application, signIn: signIn,`,
2636
- ...(hasApplicationFrameworkCalls
2637
- ? [` frameworkCalls: nomosInternalApplicationFrameworkCalls,`]
2638
- : []),
2639
- ` bind: nomosInternalBind${application}, builder: builder,`,
2640
- ` loadingBuilder: loadingBuilder, errorBuilder: errorBuilder,`,
2641
- ` onModelChange: onModelChange,`,
2642
- ` onStartupStateChanged: onStartupStateChanged,`,
2643
- ` startupTimeout: startupTimeout,`,
2644
- ` );`,
2967
+ ` Future<void> close() => _nmsReception.close();`,
2645
2968
  `}`,
2646
2969
  ``,
2647
- `Future<${application}> nomosInternalBind${application}(`,
2648
- ` NomosSession session, NmsInternalApplicationUserBinding user,`,
2970
+ `Future<${application}> receive${application}(`,
2971
+ ` NmsReceptionSession reception,`,
2649
2972
  `) async => ${application}._(`,
2650
- ` nmsSession: session,`,
2651
- ...(hasInvitations ? [` nmsUser: user,`] : []),
2652
- ` currentUser: NmsInternalUsers.create(identityId: user.nmsInternalIdentityId, identityAddress: user.nmsInternalIdentityAddress),`,
2973
+ ` nmsReception: reception,`,
2974
+ ` currentUser: NmsInternalUsers.create(identityId: reception.user.identityId, identityAddress: reception.user.identityAddress),`,
2653
2975
  ...members.map(
2654
2976
  ({ member, application: child, prefix }) =>
2655
- ` ${member}: await ${prefix}.nomosInternalBind${child}(session, user),`
2977
+ ` ${member}: await ${prefix}.nomosInternalReceive${child}(reception),`
2656
2978
  ),
2657
2979
  `);`,
2980
+ ...(hasApplicationFrameworkCalls
2981
+ ? [
2982
+ ``,
2983
+ `/// Static law vocabulary handed to the fixed Reception office.`,
2984
+ `/// It owns no lifecycle, custody, kernel, credentials, timers, or sync state.`,
2985
+ `final class ${application}Reception implements NmsReceptionContract<${application}> {`,
2986
+ ` const ${application}Reception();`,
2987
+ ``,
2988
+ ` @override`,
2989
+ ` NmsApplicationFrameworkCalls get frameworkCalls => nomosInternalApplicationFrameworkCalls;`,
2990
+ ``,
2991
+ ` @override`,
2992
+ ` Future<${application}> receive(NmsReceptionSession session) => receive${application}(session);`,
2993
+ `}`,
2994
+ ``,
2995
+ `const ${application}Reception ${dartApplicationMember(
2996
+ packageName
2997
+ )}Reception = ${application}Reception();`,
2998
+ ]
2999
+ : []),
2658
3000
  ``,
2659
3001
  ].join("\n");
2660
3002
  }
@@ -2759,7 +3101,8 @@ function cursedNameHints(
2759
3101
  function reconcilePubspec(
2760
3102
  pkgDir: string,
2761
3103
  yaml: string,
2762
- managedDependencies: Readonly<Record<string, string>> = {}
3104
+ managedDependencies: Readonly<Record<string, string>> = {},
3105
+ retiredDependencies: readonly string[] = []
2763
3106
  ): boolean {
2764
3107
  const p = path.join(pkgDir, "pubspec.yaml");
2765
3108
  if (!existsSync(p)) {
@@ -2767,7 +3110,11 @@ function reconcilePubspec(
2767
3110
  writeFileSync(p, yaml, "utf8");
2768
3111
  return true;
2769
3112
  }
2770
- if (Object.keys(managedDependencies).length === 0) return false;
3113
+ if (
3114
+ Object.keys(managedDependencies).length === 0 &&
3115
+ retiredDependencies.length === 0
3116
+ )
3117
+ return false;
2771
3118
 
2772
3119
  const source = readFileSync(p, "utf8");
2773
3120
  const trailingNewline = source.endsWith("\n");
@@ -2790,6 +3137,27 @@ function reconcilePubspec(
2790
3137
  dependenciesEnd += 1;
2791
3138
  }
2792
3139
 
3140
+ // Retire dependencies emitted by an older compiler without disturbing any
3141
+ // tenant-owned metadata or unrelated dependency.
3142
+ for (const name of retiredDependencies) {
3143
+ const prefix = ` ${name}:`;
3144
+ const existing = lines.findIndex(
3145
+ (line, index) =>
3146
+ index > dependenciesStart &&
3147
+ index < dependenciesEnd &&
3148
+ line.startsWith(prefix)
3149
+ );
3150
+ if (existing < 0) continue;
3151
+ let nestedEnd = existing + 1;
3152
+ while (
3153
+ nestedEnd < dependenciesEnd &&
3154
+ /^(?: {4,}|\t)/.test(lines[nestedEnd]!)
3155
+ )
3156
+ nestedEnd += 1;
3157
+ lines.splice(existing, nestedEnd - existing);
3158
+ dependenciesEnd -= nestedEnd - existing;
3159
+ }
3160
+
2793
3161
  for (const [name, constraint] of Object.entries(managedDependencies)) {
2794
3162
  const prefix = ` ${name}:`;
2795
3163
  const existing = lines.findIndex(
@@ -2869,6 +3237,34 @@ function reconcileGeneratedLawAsset(
2869
3237
  return true;
2870
3238
  }
2871
3239
 
3240
+ /** Remove compiler-owned law assets from a generated client package.
3241
+ * Workspace law is rolled out by its cloud host/operator; a consumer must not
3242
+ * carry a candidate capable of mutating shared custody during application open. */
3243
+ function removeGeneratedClientLawAssets(pkgDir: string): boolean {
3244
+ const p = path.join(pkgDir, "pubspec.yaml");
3245
+ if (!existsSync(p)) return false;
3246
+ const source = readFileSync(p, "utf8");
3247
+ const withoutGeneratedAssets = source
3248
+ .split(/(?<=\n)/)
3249
+ .filter(
3250
+ (line) =>
3251
+ !/^\s+-\s+lib\/src\/generated\/[^\s]+\.(?:deploy\.json|law\.usdz)\s*$/.test(
3252
+ line.trimEnd()
3253
+ )
3254
+ )
3255
+ .join("");
3256
+ // A previously generated client may contain no tenant Flutter settings once
3257
+ // its embedded law is removed. Do not leave an empty `assets:` mapping: it
3258
+ // is invalid pubspec YAML and makes the generated package fail analysis.
3259
+ const next = withoutGeneratedAssets.replace(
3260
+ /(?:^|\n)flutter:\s*\n\s{2}assets:\s*\n?$/,
3261
+ "\n"
3262
+ );
3263
+ if (next === source) return false;
3264
+ writeFileSync(p, next, "utf8");
3265
+ return true;
3266
+ }
3267
+
2872
3268
  /**
2873
3269
  * THE LAYERED-PACKAGES EMIT (`dart.packages`) — three sibling pub packages under `dartOut`:
2874
3270
  * `<types>/` (pure data, depends only on `nomos_types`), `<client>/` (effectful runtime clients,
@@ -2895,7 +3291,6 @@ function emitDartPackages(
2895
3291
  readonly barrelExports?: "all" | "explicit";
2896
3292
  },
2897
3293
  names: Readonly<Record<string, string>> | undefined,
2898
- compat?: NomosCompat,
2899
3294
  describePackageName = packages.client,
2900
3295
  lawUsdzPath?: string,
2901
3296
  applicationMaxChildWorkspacesPerUser?: number,
@@ -2917,7 +3312,7 @@ function emitDartPackages(
2917
3312
  ...(packages.compat !== undefined ? [packages.compat] : []),
2918
3313
  ];
2919
3314
  if (new Set(roleNames).size !== roleNames.length)
2920
- fail(`dart.packages: types/client/test/compat names must be distinct`);
3315
+ fail(`dart.packages: types/peer-office/test/compat names must be distinct`);
2921
3316
 
2922
3317
  const packageContracts = collectPackageContracts(modules);
2923
3318
  const describeModel = buildNomosDescribeModel(modules, describePackageName);
@@ -2938,7 +3333,7 @@ function emitDartPackages(
2938
3333
  const typesSources = new Map<string, string>(); // fileBase → src (for alias validation + purity)
2939
3334
  // The SIBLING generated type files a types-package file MAY import (a cross-domain `t.foreignRef` types its
2940
3335
  // field as the target aggregate's class, which lives in the target domain's file). A sibling types file is
2941
- // still PURE (no runtime/bridge surface — the purity gate exists to keep nomos_client out, not to isolate
3336
+ // still PURE (no runtime/session surface — the purity gate exists to keep nomos_client out, not to isolate
2942
3337
  // domain files from each other; Dart allows type-import cycles for mutual refs).
2943
3338
  const siblingTypeFiles = new Set<string>([
2944
3339
  ...modules.map((m) => `${m.name}.dart`),
@@ -2954,14 +3349,14 @@ function emitDartPackages(
2954
3349
  }
2955
3350
  const pure = src.replace(DART_PACKAGE_IMPORT, dartTypesImportFor(src));
2956
3351
  // PURITY, fail-closed: only the nomos_types barrel, the framework file, and SIBLING generated type files
2957
- // (cross-domain foreignRef targets) may be imported. Runtime/bridge surfaces (nomos_client) stay out.
3352
+ // (cross-domain foreignRef targets) may be imported. Runtime/session surfaces (nomos_client) stay out.
2958
3353
  for (const line of pure.split("\n")) {
2959
3354
  const m = /^import\s+'([^']+)'/.exec(line.trim());
2960
3355
  if (
2961
3356
  m !== null &&
2962
3357
  // `dart:*` is a pure SDK library (dart:convert for jsonEncode of a json field, dart:math, …) — it
2963
3358
  // carries NO cross-PACKAGE coupling, which is the only thing this gate exists to prevent. The
2964
- // purpose is keeping nomos_client (the runtime/bridge surface) out of the pure types layer, not
3359
+ // purpose is keeping nomos_client (the runtime/session surface) out of the pure types layer, not
2965
3360
  // banning the Dart SDK. Allow every dart:* import; keep the cross-package allowlist otherwise.
2966
3361
  !m[1]!.startsWith("dart:") &&
2967
3362
  m[1] !== "package:nomos_types/nomos_types.dart" &&
@@ -3214,7 +3609,7 @@ function emitDartPackages(
3214
3609
  const typesBarrel = [
3215
3610
  "// GENERATED by nomos-compile — the TYPES package's public entrypoint. DO NOT EDIT.",
3216
3611
  "// Pure data: payload classes, read models, VOs/enums, typed read accessors over the",
3217
- "// injected NomosReads seams. Frontend-safe — no bridge, no sessions, no lifecycle.",
3612
+ "// injected NomosReads seams. Frontend-safe — no session, no sessions, no lifecycle.",
3218
3613
  "library;",
3219
3614
  "",
3220
3615
  ...explicitBarrelNote,
@@ -3241,7 +3636,7 @@ function emitDartPackages(
3241
3636
  pkgDir(packages.types),
3242
3637
  [
3243
3638
  `name: ${packages.types}`,
3244
- `description: Generated Nomos domain TYPES — pure data, frontend-safe (no bridge, no runtime).`,
3639
+ `description: Generated Nomos domain TYPES — pure data, frontend-safe (no session, no runtime).`,
3245
3640
  `version: 0.1.0`,
3246
3641
  `publish_to: none # tenant-owned: remove to publish`,
3247
3642
  `environment:`,
@@ -3255,6 +3650,21 @@ function emitDartPackages(
3255
3650
 
3256
3651
  // ── the CLIENT package — effectful runtime, depends on types + nomos_client ──
3257
3652
  const clientGen = freshGenDir(packages.client);
3653
+ const operationProtoModules = [
3654
+ ...new Map(
3655
+ [...modules, ...frameworkModules].map(
3656
+ (module) => [module.name, module] as const
3657
+ )
3658
+ ).values(),
3659
+ ];
3660
+ written.push(
3661
+ ...emitDartOperationProto(
3662
+ clientGen,
3663
+ operationProtoModules,
3664
+ lawGraphs,
3665
+ lawPaths
3666
+ )
3667
+ );
3258
3668
  const applicationFrameworkCallsFile = "application.framework_calls";
3259
3669
  const applicationFrameworkCallsSource = generateDartApplicationFrameworkCalls(
3260
3670
  frameworkModules.flatMap((module) => {
@@ -3263,8 +3673,21 @@ function emitDartPackages(
3263
3673
  return graph === undefined || paths === undefined
3264
3674
  ? []
3265
3675
  : [{ module, graph, paths }];
3266
- })
3676
+ }),
3677
+ { lawSha256: domainHash }
3267
3678
  );
3679
+ const workspaceAccessReadImports = new Map<string, string>();
3680
+ for (const module of operationProtoModules) {
3681
+ const graph = lawGraphs.get(module.name);
3682
+ const paths = lawPaths.get(module.name);
3683
+ if (graph === undefined || paths === undefined) continue;
3684
+ if (!graph.nodes.some(node => node.kind === "read" && node.entity.label === "receivedWorkspaceAccess")) continue;
3685
+ const file = `${module.name}.workspace_access.dart`;
3686
+ const target = path.join(clientGen, file);
3687
+ writeFileSync(target, generateDartWorkspaceAccessRead(graph, paths), "utf8");
3688
+ written.push(target);
3689
+ workspaceAccessReadImports.set(module.name, file);
3690
+ }
3268
3691
  if (applicationFrameworkCallsSource !== undefined) {
3269
3692
  const applicationFrameworkCallsPath = path.join(
3270
3693
  clientGen,
@@ -3278,18 +3701,6 @@ function emitDartPackages(
3278
3701
  written.push(applicationFrameworkCallsPath);
3279
3702
  }
3280
3703
  const embeddedLawName = `${describePackageName}.law.usdz`;
3281
- const applicationLawPath =
3282
- lawUsdzPath === undefined
3283
- ? undefined
3284
- : path.join(clientGen, embeddedLawName);
3285
- if (applicationLawPath !== undefined) {
3286
- copyFileSync(lawUsdzPath!, applicationLawPath);
3287
- written.push(applicationLawPath);
3288
- }
3289
- const applicationLawAssetKey =
3290
- applicationLawPath === undefined
3291
- ? undefined
3292
- : `packages/${packages.client}/lib/${genRel}/${embeddedLawName}`;
3293
3704
  const typesImportUri = `package:${packages.types}/${packages.types}.dart`;
3294
3705
  const implementationTypeImports = explicitBarrel
3295
3706
  ? modules.map((m) => `package:${packages.types}/${genRel}/${m.name}.dart`)
@@ -3310,18 +3721,15 @@ function emitDartPackages(
3310
3721
  name !== OwnedDeviceRequestValue.name
3311
3722
  );
3312
3723
  for (const { mod, dd } of perDomain) {
3724
+ const workspaceAccessReadImportUri = workspaceAccessReadImports.get(mod.name);
3313
3725
  const makeClient = (imports: readonly string[]) =>
3314
- generateDartBridgeClient(dd.modForDart, {
3726
+ generateDartDomainClient(dd.modForDart, {
3315
3727
  domainHash,
3316
3728
  callUsda: {
3317
3729
  graph: lawGraphs.get(mod.name)!,
3318
3730
  paths: lawPaths.get(mod.name)!,
3319
3731
  },
3320
- ...(applicationLawAssetKey !== undefined
3321
- ? { lawAssetKey: applicationLawAssetKey }
3322
- : {}),
3323
3732
  interface: packageInterface[dd.modForDart.domain ?? dd.modForDart.name],
3324
- ...(compat !== undefined ? { compat } : {}),
3325
3733
  supportImport: "package",
3326
3734
  typesImportUri,
3327
3735
  emitActivity: true,
@@ -3336,6 +3744,7 @@ function emitDartPackages(
3336
3744
  const makeApplication = (imports: readonly string[]) =>
3337
3745
  generateDartApplication(dd.modForDart, {
3338
3746
  supportImport: "package",
3747
+ callUsda: { graph: lawGraphs.get(mod.name)!, paths: lawPaths.get(mod.name)! },
3339
3748
  typesImportUri,
3340
3749
  packageAggregateDomains: buildPackageAggregateDomains(modules),
3341
3750
  flutterHost: true,
@@ -3345,6 +3754,9 @@ function emitDartPackages(
3345
3754
  : {
3346
3755
  applicationFrameworkCallsImportUri: `${applicationFrameworkCallsFile}.dart`,
3347
3756
  }),
3757
+ ...(workspaceAccessReadImportUri === undefined
3758
+ ? {}
3759
+ : { workspaceAccessReadImportUri }),
3348
3760
  ...(imports.length > 0 ? { explicitTypeImports: imports } : {}),
3349
3761
  });
3350
3762
  let clientSrc = makeClient(implementationTypeImports);
@@ -3377,7 +3789,7 @@ function emitDartPackages(
3377
3789
  applicationSrc,
3378
3790
  clientFile,
3379
3791
  applicationFile,
3380
- new Set(dartApplicationPublicClassNames(dd.modForDart))
3792
+ new Set(dartApplicationPublicClassNames(dd.modForDart, { callUsda: { graph: lawGraphs.get(mod.name)!, paths: lawPaths.get(mod.name)! } }))
3381
3793
  );
3382
3794
  const cp = path.join(clientGen, clientFile);
3383
3795
  writeFileSync(cp, encapsulated.clientPart, "utf8");
@@ -3413,7 +3825,7 @@ function emitDartPackages(
3413
3825
  })),
3414
3826
  topology.homeDomain,
3415
3827
  topology.rootDomains,
3416
- topology.remoteDomains,
3828
+ topology.parentWorkspaceDomains,
3417
3829
  applicationFrameworkCallsSource !== undefined
3418
3830
  ),
3419
3831
  "utf8"
@@ -3424,10 +3836,8 @@ function emitDartPackages(
3424
3836
  "// GENERATED by nomos-compile — the CLIENT package's public entrypoint. DO NOT EDIT.",
3425
3837
  `/// The generated business application for ${describePackageName}.`,
3426
3838
  `///`,
3427
- `/// Import this library, mount [${dartApplicationClassName(
3428
- describePackageName
3429
- )}.nomosWidget] once, and pass the`,
3430
- `/// supplied [${dartApplicationClassName(
3839
+ `/// Import this library, bind one Peer Office handle, and pass the supplied`,
3840
+ `/// [${dartApplicationClassName(
3431
3841
  describePackageName
3432
3842
  )}] through the UI. Its typed actions, records,`,
3433
3843
  `/// watches, references and status are the canonical application state. Do not recreate them`,
@@ -3439,7 +3849,7 @@ function emitDartPackages(
3439
3849
  "library;",
3440
3850
  "",
3441
3851
  `export 'package:${packages.types}/${genRel}/${businessSharedFile}';`,
3442
- `export 'package:nomos_flutter/src/generated_application_host.dart' show ApplicationModelApproval, ApplicationModelDecision, ApplicationModelPolicy, ApplicationModelReview, ApplicationWorkspaceOpenProgress, NomosApplicationStartupPhase, NomosApplicationStartupState, NomosApplicationStartupTimeout, NomosWorkspaceOpenTimeout;`,
3852
+ `export 'package:nomos_client/src/workspace_open_progress.dart' show ApplicationWorkspaceOpenProgress;`,
3443
3853
  `export 'package:nomos_types/nomos_types.dart' show CustodyError, CustodyPhase, CustodySource, NomosRecommendedAction;`,
3444
3854
  ...tenantTypesExportKeys
3445
3855
  .map((file) => {
@@ -3460,14 +3870,23 @@ function emitDartPackages(
3460
3870
  : []),
3461
3871
  ...(perDomain.length > 1
3462
3872
  ? [
3463
- `export 'package:nomos_flutter/src/application_status.dart' show ApplicationSignIn, ApplicationStatus, ApplicationStatusPhase;`,
3873
+ `export 'package:nomos_client/src/application_status.dart' show ApplicationStatus, ApplicationStatusPhase;`,
3464
3874
  `export '${genRel}/${packageApplicationFile}.dart' show ${dartApplicationClassName(
3465
3875
  describePackageName
3466
- )};`,
3876
+ )}${
3877
+ applicationFrameworkCallsSource === undefined
3878
+ ? ""
3879
+ : `, ${dartApplicationClassName(
3880
+ describePackageName
3881
+ )}Reception, ${dartApplicationMember(
3882
+ describePackageName
3883
+ )}Reception`
3884
+ };`,
3467
3885
  ...perDomain.map(({ mod, dd }) => {
3468
3886
  const packageClass = dartApplicationClassName(describePackageName);
3469
3887
  const publicNames = dartApplicationPublicClassNames(
3470
- dd.modForDart
3888
+ dd.modForDart,
3889
+ { callUsda: { graph: lawGraphs.get(mod.name)!, paths: lawPaths.get(mod.name)! } }
3471
3890
  ).filter((name) => name !== packageClass);
3472
3891
  return `export '${genRel}/${
3473
3892
  mod.name
@@ -3480,7 +3899,7 @@ function emitDartPackages(
3480
3899
  mod.name
3481
3900
  }.application.dart' show ${dartApplicationPublicClassNames(
3482
3901
  dd.modForDart,
3483
- { tenantRoot: true }
3902
+ { tenantRoot: true, callUsda: { graph: lawGraphs.get(mod.name)!, paths: lawPaths.get(mod.name)! } }
3484
3903
  ).join(", ")};`
3485
3904
  )),
3486
3905
  "",
@@ -3501,27 +3920,22 @@ function emitDartPackages(
3501
3920
  `publish_to: none # tenant-owned: remove to publish`,
3502
3921
  `environment:`,
3503
3922
  ` sdk: ^3.5.0`,
3504
- ` flutter: ">=3.10.0"`,
3505
3923
  `dependencies:`,
3506
- ` flutter:`,
3507
- ` sdk: flutter`,
3508
3924
  ` nomos_client: ${NOMOS_CLIENT_DEP}`,
3509
- ` nomos_flutter: ${NOMOS_FLUTTER_DEP}`,
3925
+ ` protobuf: ^5.0.0`,
3926
+ ` fixnum: ^1.1.1`,
3510
3927
  ` ${packages.types}:`,
3511
3928
  ` path: ../${packages.types} # co-emitted sibling; replace with a hosted constraint when publishing`,
3512
- ...(applicationLawPath === undefined
3513
- ? []
3514
- : [`flutter:`, ` assets:`, ` - lib/${genRel}/${embeddedLawName}`]),
3515
3929
  ``,
3516
3930
  ].join("\n"),
3517
- { nomos_client: NOMOS_CLIENT_DEP, nomos_flutter: NOMOS_FLUTTER_DEP }
3931
+ {
3932
+ nomos_client: NOMOS_CLIENT_DEP,
3933
+ protobuf: "^5.0.0",
3934
+ fixnum: "^1.1.1",
3935
+ },
3936
+ ["flutter", "nomos_flutter"]
3518
3937
  );
3519
- if (applicationLawPath !== undefined) {
3520
- reconcileGeneratedLawAsset(
3521
- pkgDir(packages.client),
3522
- `lib/${genRel}/${embeddedLawName}`
3523
- );
3524
- }
3938
+ removeGeneratedClientLawAssets(pkgDir(packages.client));
3525
3939
 
3526
3940
  // ── the TEST package (optional) — generated business scenarios, test-only dependency ──
3527
3941
  if (packages.test !== undefined) {
@@ -3726,6 +4140,45 @@ function emitDartPackages(
3726
4140
 
3727
4141
  // ── main ──────────────────────────────────────────────────────────────────────────
3728
4142
 
4143
+ /**
4144
+ * Fail unless the `@githolon/dsl` the law in `lawDir` resolves is this compiler's own module instance:
4145
+ * the same package directory, and the same loaded module (namespace identity), so the law and the
4146
+ * compiler share one authoring drain and one endorsement registry.
4147
+ */
4148
+ async function assertOneDslInstance(lawDir: string): Promise<void> {
4149
+ const ownPackage = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "package.json");
4150
+ let lawPackage: string;
4151
+ let lawEntry: string;
4152
+ try {
4153
+ const lawRequire = createRequire(pathToFileURL(path.join(lawDir, "noop.js")));
4154
+ lawPackage = realpathSync(lawRequire.resolve("@githolon/dsl/package.json"));
4155
+ lawEntry = realpathSync(path.join(path.dirname(lawPackage), "src", "index.ts"));
4156
+ } catch {
4157
+ // The law cannot resolve the DSL by name (a workspace law importing framework sources by path):
4158
+ // the compiler's own instance is the only one its modules can reach.
4159
+ return;
4160
+ }
4161
+ const versionOf = (file: string): string => {
4162
+ try {
4163
+ return (JSON.parse(readFileSync(file, "utf8")) as { version?: string }).version ?? "?";
4164
+ } catch {
4165
+ return "?";
4166
+ }
4167
+ };
4168
+ const split = (why: string): never =>
4169
+ fail(
4170
+ `TWO @githolon/dsl instances (${why}): the law resolves ${versionOf(lawPackage)} at ` +
4171
+ `${path.dirname(lawPackage)}, this compiler runs ${versionOf(ownPackage)} at ${path.dirname(ownPackage)}. ` +
4172
+ `Split instances split the DSL's registries, so plan probes come back empty and authority flows are not ` +
4173
+ `harvested: the law would compile weaker than written. Run the law package's own \`npx githolon compile\`, ` +
4174
+ `or make its @githolon/dsl resolve to this compiler's.`
4175
+ );
4176
+ if (realpathSync(ownPackage) !== lawPackage) split("two package directories");
4177
+ const own = await import(pathToFileURL(realpathSync(fileURLToPath(new URL("./index.ts", import.meta.url)))).href);
4178
+ const law = await import(pathToFileURL(lawEntry).href);
4179
+ if (own !== law) split("two loaded module instances");
4180
+ }
4181
+
3729
4182
  async function main(): Promise<void> {
3730
4183
  const argv = process.argv.slice(2);
3731
4184
  const supportedFlags = new Set([
@@ -3733,6 +4186,8 @@ async function main(): Promise<void> {
3733
4186
  "--compact",
3734
4187
  "--analyze",
3735
4188
  "--preflight",
4189
+ "--inspect-sqlite",
4190
+ "--inspect-law",
3736
4191
  ]);
3737
4192
  for (const flag of argv.filter((arg) => arg.startsWith("--"))) {
3738
4193
  if (supportedFlags.has(flag)) continue;
@@ -3755,6 +4210,15 @@ async function main(): Promise<void> {
3755
4210
  // spends minutes producing artifacts that will immediately be discarded.
3756
4211
  const preflightFlag =
3757
4212
  argv.includes("--preflight") || process.env.NOMOS_PREFLIGHT === "1";
4213
+ const sqliteInspection = argv.includes("--inspect-sqlite");
4214
+ const lawInspection = argv.includes("--inspect-law");
4215
+ const graphInspection = sqliteInspection || lawInspection;
4216
+ if (graphInspection && preflightFlag) {
4217
+ fail("graph inspection requires the typed graph; do not combine it with --preflight");
4218
+ }
4219
+ if (sqliteInspection && lawInspection) {
4220
+ fail("select one graph inspection: --inspect-law or --inspect-sqlite");
4221
+ }
3758
4222
  // §"Shipped (1–4)" follow-up: `githolon compile --analyze` (or NOMOS_ANALYZE=1) runs
3759
4223
  // `dart analyze` on the generated Dart package after emission and fails the compile on red.
3760
4224
  const analyzeFlag =
@@ -3763,51 +4227,45 @@ async function main(): Promise<void> {
3763
4227
  const configPath = path.resolve(process.cwd(), configArg);
3764
4228
  if (!existsSync(configPath)) fail(`config not found: ${configPath}`);
3765
4229
  const cfgDir = path.dirname(configPath);
3766
-
3767
- // DUAL-INSTANCE GUARD (DX): the law's `import "@githolon/dsl"` resolves from the LAW's own node_modules,
3768
- // while this compiler runs from wherever it was invoked. Two DIFFERENT dsl instances silently split every
3769
- // module-level registry (the fluent-authoring drain, brands, markers) — plans probe empty, lints
3770
- // false-fire, ids drift. Detect the skew and WARN LOUDLY with the two paths (never fail — a
3771
- // deliberately-vendored setup may be intentional, and same-version split instances still work for
3772
- // compilation itself, which imports the law through the compiler's own module graph).
3773
- try {
3774
- const lawReq = createRequire(pathToFileURL(path.join(cfgDir, "noop.js")));
3775
- const lawDslPkg = lawReq.resolve("@githolon/dsl/package.json");
3776
- const ownDslPkg = path.join(
3777
- path.dirname(fileURLToPath(import.meta.url)),
3778
- "..",
3779
- "package.json"
3780
- );
3781
- if (path.resolve(lawDslPkg) !== path.resolve(ownDslPkg)) {
3782
- const vOf = (p: string): string => {
3783
- try {
3784
- return (
3785
- (JSON.parse(readFileSync(p, "utf8")) as { version?: string })
3786
- .version ?? "?"
3787
- );
3788
- } catch {
3789
- return "?";
3790
- }
3791
- };
3792
- console.error(
3793
- `⚠ nomos-compile: TWO @githolon/dsl instances — the law resolves ${vOf(
3794
- lawDslPkg
3795
- )} at ${path.dirname(lawDslPkg)}, but this compiler runs ${vOf(
3796
- path.resolve(ownDslPkg)
3797
- )} at ${path.dirname(path.resolve(ownDslPkg))}. ` +
3798
- `Split instances split the DSL's module registries (phantom lint errors, empty plan probes, id drift) — even at the same version string when the code differs. ` +
3799
- `Run the law package's OWN \`npx githolon compile\`, or align the two installs.`
3800
- );
3801
- }
3802
- } catch {
3803
- /* no dsl resolvable from the law dir (vendored/workspace setups) — nothing to compare */
4230
+ const timing = process.env.NOMOS_COMPILE_TIMING === "1";
4231
+ const timingStart = performance.now();
4232
+ let timingLast = timingStart;
4233
+ const stage = (label: string): void => {
4234
+ if (!timing) return;
4235
+ const now = performance.now();
4236
+ console.error(
4237
+ `[nomos-timing] ${label.padEnd(36)} +${((now - timingLast) / 1000)
4238
+ .toFixed(3)
4239
+ .padStart(6)}s t=${((now - timingStart) / 1000).toFixed(3)}s`
4240
+ );
4241
+ timingLast = now;
4242
+ };
4243
+ if (timing) {
4244
+ console.error(
4245
+ `[nomos-timing] ${"process boot → main()".padEnd(36)} +${(
4246
+ timingStart / 1000
4247
+ )
4248
+ .toFixed(3)
4249
+ .padStart(6)}s`
4250
+ );
3804
4251
  }
3805
4252
 
4253
+ // ONE DSL INSTANCE, OR NO COMPILE. The law's `import "@githolon/dsl"` resolves from the LAW's own
4254
+ // node_modules, while this compiler runs from wherever it was invoked. Two instances split every
4255
+ // module-level registry (the fluent-authoring drain, the endorsement records, brands): plan probes come
4256
+ // back empty, authority flows are silently not harvested, and the compiled law is weaker than the one
4257
+ // written. That is a different law, so it is an error, never a warning.
4258
+ await assertOneDslInstance(cfgDir);
4259
+
3806
4260
  const cfgModule = (await import(pathToFileURL(configPath).href)) as {
3807
4261
  default?: unknown;
3808
4262
  };
3809
4263
  const configValue = cfgModule.default;
4264
+ stage("config import");
3810
4265
  if (isApplicationCompositionConfig(configValue)) {
4266
+ if (graphInspection) {
4267
+ fail("graph inspection requires a law package, not an application composition; nothing was emitted");
4268
+ }
3811
4269
  try {
3812
4270
  const emitted = compileApplicationComposition(configValue, configPath, {
3813
4271
  requireUsdcat,
@@ -3949,6 +4407,7 @@ async function main(): Promise<void> {
3949
4407
  applicationBirthPolicyDomains.push(domain.key);
3950
4408
  }
3951
4409
  }
4410
+ stage("domain discovery imports");
3952
4411
  if (
3953
4412
  cfg.application?.homeGenesis !== undefined &&
3954
4413
  applicationHomeGenesisSource === undefined
@@ -3980,7 +4439,11 @@ async function main(): Promise<void> {
3980
4439
  ]),
3981
4440
  ];
3982
4441
 
3983
- const outDir = path.resolve(cfgDir, cfg.outDir ?? "build");
4442
+ // Inspection executes normal declaration/composition/identity resolution but
4443
+ // NEVER cleans tenant build output, writes its identity, or emits a release.
4444
+ const outDir = graphInspection
4445
+ ? mkdtempSync(path.join(tmpdir(), lawInspection ? "nomos-law-inspect-" : "nomos-sqlite-inspect-"))
4446
+ : path.resolve(cfgDir, cfg.outDir ?? "build");
3984
4447
  mkdirSync(outDir, { recursive: true });
3985
4448
  // Interrupted pre-cutover compiles left thousands of private source/probe files beside public
3986
4449
  // artifacts. They are neither custody nor diagnostics; clear only the compiler-owned namespace.
@@ -4058,6 +4521,7 @@ async function main(): Promise<void> {
4058
4521
  const nativeWorkspaceInvariantReadIds = new Set<string>();
4059
4522
  const routingInput: Record<string, Mod[]> = {};
4060
4523
  const domainModules: DomainModule[] = [];
4524
+ const custodyPlacementDeclarations: DomainCustodyPlacement[] = [];
4061
4525
  const directiveExportSources: Array<{
4062
4526
  readonly directive: object;
4063
4527
  readonly domainKey: string;
@@ -4170,6 +4634,10 @@ async function main(): Promise<void> {
4170
4634
  exportName: string;
4171
4635
  }
4172
4636
  >();
4637
+ const sharingRedeems: Array<{
4638
+ readonly directive: object & { readonly id: string };
4639
+ readonly modulePath: string;
4640
+ }> = [];
4173
4641
  for (const rel of d.modules) {
4174
4642
  const abs = path.resolve(cfgDir, rel);
4175
4643
  if (isEvolutionModulePath(abs)) {
@@ -4210,6 +4678,19 @@ async function main(): Promise<void> {
4210
4678
  aggregateId: (value as { aggregateId: string }).aggregateId,
4211
4679
  });
4212
4680
  }
4681
+ // A `workspaceSharing(...)` policy LOWERS an invitation-redeem directive that carries a
4682
+ // workspace effect (it publishes the received access into the recipient's Home). Collect
4683
+ // them here and register after the whole domain is loaded — a tenant MAY also export the
4684
+ // same object directly (`export const redeem = sharing.invitationLaw.redeem`), and two
4685
+ // entries for one directive is the same refusal as none.
4686
+ if (isApplicationLanguage(value)) {
4687
+ for (const policy of value.workspaceSharing) {
4688
+ sharingRedeems.push({
4689
+ directive: policy.invitationLaw.redeem,
4690
+ modulePath: abs,
4691
+ });
4692
+ }
4693
+ }
4213
4694
  // `birth(Aggregate)` is intentionally a high-level declaration. Its public parent birth and
4214
4695
  // private child seed are compiler-lowered executable offers, so preserve their authored
4215
4696
  // symbol origin here rather than condemning both to the whole-domain fallback plan.
@@ -4239,6 +4720,26 @@ async function main(): Promise<void> {
4239
4720
  }
4240
4721
  }
4241
4722
  }
4723
+ // The lowered redeem has no authored export of its own — `withWorkspaceSharingInvitationExports`
4724
+ // folds it into the merged bag under a synthetic name so custody and the executable engine
4725
+ // registry see the one seam. Register that same synthetic name, or the workspace effect it
4726
+ // carries resolves to zero configured exports and the compile refuses law the tenant never
4727
+ // wrote, with an instruction ("export it once") they cannot follow.
4728
+ for (const redeem of sharingRedeems) {
4729
+ if (
4730
+ directiveExportSources.some(
4731
+ (candidate) => candidate.directive === redeem.directive
4732
+ )
4733
+ )
4734
+ continue;
4735
+ directiveExportSources.push({
4736
+ directive: redeem.directive,
4737
+ domainKey: d.key,
4738
+ graphName: d.name ?? d.key,
4739
+ modulePath: redeem.modulePath,
4740
+ exportName: `__nomosWorkspaceSharingDirective:${redeem.directive.id}`,
4741
+ });
4742
+ }
4242
4743
  const configuredPaths = new Set(
4243
4744
  d.modules.map((rel) => path.resolve(cfgDir, rel))
4244
4745
  );
@@ -4272,19 +4773,58 @@ async function main(): Promise<void> {
4272
4773
  );
4273
4774
  }
4274
4775
  const homeGenesis = cfg.application?.homeGenesis;
4275
- const homeParentWorkspaceRoles =
4276
- cfg.application?.parentWorkspaceRoles ?? [];
4776
+ const receivingGroupRecovery = cfg.application?.receivingGroup?.recovery;
4277
4777
  const policyMod = applicationHomePolicyLaw(
4278
4778
  max,
4279
4779
  applicationBirthPolicyDomains,
4280
4780
  homeGenesis,
4281
- homeParentWorkspaceRoles,
4781
+ receivingGroupRecovery,
4282
4782
  ) as Mod;
4283
4783
  mods.push(policyMod);
4284
- const homeGenesisExpr = applicationHomeGenesisSource === undefined
4285
- ? "undefined"
4286
- : `require(${JSON.stringify(applicationHomeGenesisSource.modulePath)})` +
4287
- `[${JSON.stringify(applicationHomeGenesisSource.exportName)}]`;
4784
+ // The composed policy law composes the framework's child-authentication singletons into THIS
4785
+ // domain (`childAuthenticationExports`). Each is a module-level export of
4786
+ // `framework/child_authentication.ts`, so it is a declaration export of this domain exactly like an
4787
+ // authored module export: the workspace effects it authors (verifier-epoch pair, child repair) resolve
4788
+ // to one owning domain, and the runtime target encoder can `require` the very object by name.
4789
+ // The receiving-group recovery door (`receivingGroupRecoveryLaw`, composed only under the declared
4790
+ // `platform-identity-provider` posture) is registered the same way: its members are module-level
4791
+ // singletons of `framework/receiving_group_recovery.ts`, never function-scoped copies.
4792
+ const composedPolicyValues = new Set(Object.values(policyMod));
4793
+ const registeredComposedDirectives = new Set<object>();
4794
+ for (const [modulePath, frameworkModule] of [
4795
+ [CHILD_AUTHENTICATION_MODULE_PATH, childAuthenticationModule],
4796
+ [RECEIVING_GROUP_RECOVERY_MODULE_PATH, receivingGroupRecoveryModule],
4797
+ [MODEL_TRANSITION_MODULE_PATH, modelTransitionModule],
4798
+ [MODEL_AUTHORITY_REPAIR_MODULE_PATH, modelAuthorityRepairModule],
4799
+ ] as const) {
4800
+ for (const [exportName, value] of Object.entries(frameworkModule)) {
4801
+ if (
4802
+ composedPolicyValues.has(value) &&
4803
+ value !== null &&
4804
+ typeof value === "object" &&
4805
+ typeof (value as { id?: unknown }).id === "string" &&
4806
+ typeof (value as { plan?: unknown }).plan === "function" &&
4807
+ typeof (value as { aggregateId?: unknown }).aggregateId === "string" &&
4808
+ !registeredComposedDirectives.has(value)
4809
+ ) {
4810
+ registeredComposedDirectives.add(value);
4811
+ directiveExportSources.push({
4812
+ directive: value,
4813
+ domainKey: d.key,
4814
+ graphName: d.name ?? d.key,
4815
+ modulePath,
4816
+ exportName,
4817
+ });
4818
+ }
4819
+ }
4820
+ }
4821
+ const homeGenesisExpr =
4822
+ applicationHomeGenesisSource === undefined
4823
+ ? "undefined"
4824
+ : `require(${JSON.stringify(
4825
+ applicationHomeGenesisSource.modulePath
4826
+ )})` +
4827
+ `[${JSON.stringify(applicationHomeGenesisSource.exportName)}]`;
4288
4828
  sourceExprs.push(
4289
4829
  `() => require(${JSON.stringify(
4290
4830
  path.join(DSL_DIR, "src", "framework", "application_home.ts")
@@ -4292,7 +4832,10 @@ async function main(): Promise<void> {
4292
4832
  `.applicationHomePolicyLaw(` +
4293
4833
  `${JSON.stringify(max)}, ${JSON.stringify(
4294
4834
  applicationBirthPolicyDomains
4295
- )}, ${homeGenesisExpr}, ${JSON.stringify(homeParentWorkspaceRoles)})`
4835
+ )}, ${homeGenesisExpr}` +
4836
+ // A declared posture rides the re-emitted source as a JSON string; an undeclared one is
4837
+ // omitted (not `undefined`), keeping the emitted expression byte-identical for older packages.
4838
+ `${receivingGroupRecovery === undefined ? "" : `, ${JSON.stringify(receivingGroupRecovery)}`})`
4296
4839
  );
4297
4840
  }
4298
4841
  let merged: Mod = {};
@@ -4315,6 +4858,19 @@ async function main(): Promise<void> {
4315
4858
  // `relationType("workspace")` (the same seam composeDomainModule expands) so this plane's openapi /
4316
4859
  // relation-schema discovery sees the grant/revoke/seed directives + the teeth-bearing schema.
4317
4860
  merged = expandRoleExports(merged);
4861
+
4862
+ // Placement is intentionally discovered beside law, never composed into it.
4863
+ // The compiler ships these typed options as a separate finished good. A
4864
+ // workspace must explicitly adopt one on its placement frontier; installing
4865
+ // or evolving main law can therefore never relocate custody as a side effect.
4866
+ for (const declaration of Object.values(merged).filter(
4867
+ isCustodyPlacementDeclaration
4868
+ ) as CustodyPlacementDeclaration[]) {
4869
+ custodyPlacementDeclarations.push({
4870
+ domain: d.key,
4871
+ declaration,
4872
+ });
4873
+ }
4318
4874
  let shardingSums: AnySum[] = [];
4319
4875
  let shardingMins: AnyExtremum[] = [];
4320
4876
  let shardingMaxes: AnyExtremum[] = [];
@@ -4561,7 +5117,14 @@ async function main(): Promise<void> {
4561
5117
  usesRelations &&
4562
5118
  !queries.some((q) => q.id === relationTuplesByObject.id)
4563
5119
  ) {
4564
- queries.push(relationTuplesByObject);
5120
+ // The audit query is framework law carried by the package, not tenant
5121
+ // application vocabulary. Keep it available to the Peer Office while
5122
+ // preventing the generated reception facade from treating it as a
5123
+ // public realm member.
5124
+ queries.push({
5125
+ ...relationTuplesByObject,
5126
+ businessBirthInternal: true,
5127
+ } as QueryDecl);
4565
5128
  }
4566
5129
  }
4567
5130
  const counts = [
@@ -4623,14 +5186,25 @@ async function main(): Promise<void> {
4623
5186
  ...shardingSums,
4624
5187
  ]),
4625
5188
  ];
4626
- const mins = [
5189
+ const extrema = discover<AnyExtremum>(merged, isExtremumDecl);
5190
+ const exists_ = [...new Set([
5191
+ ...discover<AnyExists>(merged, isExistsDecl),
5192
+ ...resolveNamed<AnyExists>(merged, d.exists_, "exists_"),
5193
+ ])];
5194
+ const orderedReads = [...new Set([
5195
+ ...discover<OrderedReadDecl>(merged, isOrderedReadDecl),
5196
+ ...resolveNamed<OrderedReadDecl>(merged, d.orderedReads, "orderedReads"),
5197
+ ])];
5198
+ const mins = [...new Set([
5199
+ ...extrema.filter((read) => read.kind === "min"),
4627
5200
  ...resolveNamed<AnyExtremum>(merged, d.mins, "mins"),
4628
5201
  ...shardingMins,
4629
- ];
4630
- const maxes = [
5202
+ ])];
5203
+ const maxes = [...new Set([
5204
+ ...extrema.filter((read) => read.kind === "max"),
4631
5205
  ...resolveNamed<AnyExtremum>(merged, d.maxes, "maxes"),
4632
5206
  ...shardingMaxes,
4633
- ];
5207
+ ])];
4634
5208
  // `capability()` declarations AUTO-DISCOVER (the marketplace lane, M2 —
4635
5209
  // "declare ONE thing"); the config-named lane stays for hand-rolled
4636
5210
  // impureCapability quartets. Deduped by task-aggregate id (a config-named
@@ -4679,6 +5253,8 @@ async function main(): Promise<void> {
4679
5253
  ...(sums.length > 0 ? { sums } : {}),
4680
5254
  ...(mins.length > 0 ? { mins } : {}),
4681
5255
  ...(maxes.length > 0 ? { maxes } : {}),
5256
+ ...(exists_.length > 0 ? { exists_ } : {}),
5257
+ ...(orderedReads.length > 0 ? { orderedReads } : {}),
4682
5258
  ...(workspaceTypes.length > 0 ? { workspaceTypes } : {}),
4683
5259
  ...(workspaceInvariants.length > 0 ? { workspaceInvariants } : {}),
4684
5260
  // Relation declarations are already present in `modules` (including config-named exports), and
@@ -4716,12 +5292,25 @@ async function main(): Promise<void> {
4716
5292
  executableDirectives.map((directive) => [directive.id, directive])
4717
5293
  )
4718
5294
  );
5295
+ const publicIntentSourceFor = (directiveId: string) => {
5296
+ const publicIntent = (composed.applicationIntents ?? []).find(
5297
+ (intent) =>
5298
+ isBusinessIntent(intent) &&
5299
+ intent.steps.length === 1 &&
5300
+ intent.steps[0]?.directive.id === directiveId
5301
+ );
5302
+ return (
5303
+ directOfferExports.get(publicIntent?.id ?? directiveId) ??
5304
+ directOfferExports.get(directiveId)
5305
+ );
5306
+ };
4719
5307
  offerSourceExportsByDomain.set(
4720
5308
  d.key,
4721
5309
  new Map(
4722
- [...directOfferExports].filter(([directiveId]) =>
4723
- executableDirectiveIds.includes(directiveId)
4724
- )
5310
+ executableDirectiveIds.flatMap((directiveId) => {
5311
+ const source = publicIntentSourceFor(directiveId);
5312
+ return source === undefined ? [] : [[directiveId, source] as const];
5313
+ })
4725
5314
  )
4726
5315
  );
4727
5316
  const invariantSources = new Map<
@@ -4765,8 +5354,8 @@ async function main(): Promise<void> {
4765
5354
 
4766
5355
  // ── THE SELF-DESCRIBE DECLS (G1): the FINISHED plain read/maintenance decls the lump's
4767
5356
  // `describe` reconstructs the openapi fragment from. Directives + aggregates are read from
4768
- // the live module exports IN the lump; these ride alongside (mins/maxes are config-named +
4769
- // sharding sums are framework-appended — not shape-rediscoverable). Omit-when-empty so a
5357
+ // the live module exports IN the lump; these ride alongside (sharding reads are
5358
+ // framework-appended — not shape-rediscoverable). Omit-when-empty so a
4770
5359
  // decl-free domain contributes nothing (and offers still derive from its directives).
4771
5360
  {
4772
5361
  const declsForDomain: Record<string, unknown[]> = {};
@@ -4783,6 +5372,10 @@ async function main(): Promise<void> {
4783
5372
  declsForDomain.mins = mins.map((e) => finishExtremum(e));
4784
5373
  if (maxes.length > 0)
4785
5374
  declsForDomain.maxes = maxes.map((e) => finishExtremum(e));
5375
+ if (exists_.length > 0)
5376
+ declsForDomain.exists_ = exists_.map(finishExists);
5377
+ if (orderedReads.length > 0)
5378
+ declsForDomain.orderedReads = orderedReads;
4786
5379
  if (spatials.length > 0)
4787
5380
  declsForDomain.spatials = spatials.map((s) => ({
4788
5381
  id: s.id,
@@ -4871,6 +5464,22 @@ async function main(): Promise<void> {
4871
5464
  }
4872
5465
  }
4873
5466
 
5467
+ const custodyPlacementDeclarationsPath = path.join(
5468
+ outDir,
5469
+ `${cfg.name}.custody-placement.usda`
5470
+ );
5471
+ if (custodyPlacementDeclarations.length > 0) {
5472
+ writeFileSync(
5473
+ custodyPlacementDeclarationsPath,
5474
+ custodyPlacementDeclarationsUsda(custodyPlacementDeclarations),
5475
+ "utf8"
5476
+ );
5477
+ } else {
5478
+ // A recompile after declarations are removed must not leave an apparently
5479
+ // current operational-policy artifact beside the package.
5480
+ rmSync(custodyPlacementDeclarationsPath, { force: true });
5481
+ }
5482
+
4874
5483
  const dependencyUseFindings = packageDependencyUsageFindings({
4875
5484
  packageName: cfg.name,
4876
5485
  exportedDomains: cfg.domains.map((domain) => domain.key),
@@ -4903,6 +5512,20 @@ async function main(): Promise<void> {
4903
5512
  // custody emission. A statically invalid package should fail in seconds and must never leave a
4904
5513
  // plausible deployable artifact behind merely because the runtime representation changed shape.
4905
5514
  const authorTimeFindings = checkDomainModules(domainModules);
5515
+ // Framework machinery composed into an application is not that application's business law: the private
5516
+ // receiving group, nomos_internal_ ids, and every directive a birth-capable law received from the framework
5517
+ // (marked businessBirthInternal). Their refusals still surface, as private refusals, below.
5518
+ const frameworkComposedDirectives = new Set(domainModules.flatMap((mod) =>
5519
+ (mod.directives as readonly { readonly id: string; readonly businessBirthInternal?: unknown }[])
5520
+ .filter((directive) => directive.businessBirthInternal === true)
5521
+ .map((directive) => `${mod.domain ?? mod.name}\0${directive.id}`)));
5522
+ const isFrameworkAuthorTimeFinding = (
5523
+ finding: (typeof authorTimeFindings)[number]
5524
+ ) =>
5525
+ finding.domain === APPLICATION_HOME_DOMAIN ||
5526
+ finding.directiveId?.startsWith("nomos_internal_") === true ||
5527
+ (finding.directiveId !== undefined &&
5528
+ frameworkComposedDirectives.has(`${finding.domain}\0${finding.directiveId}`));
4906
5529
  // Self-description is a compiler-owned presentation projection, not executable law. Build it
4907
5530
  // once from the same composed modules and place it only in the separately routed describe plan;
4908
5531
  // exact offer/read plans must not carry OpenAPI generation and Zod JSON-Schema machinery.
@@ -4915,9 +5538,7 @@ async function main(): Promise<void> {
4915
5538
 
4916
5539
  if (preflightFlag) {
4917
5540
  const businessFindings = authorTimeFindings.filter(
4918
- (finding) =>
4919
- finding.domain !== APPLICATION_HOME_DOMAIN &&
4920
- finding.directiveId?.startsWith("nomos_internal_") !== true
5541
+ (finding) => !isFrameworkAuthorTimeFinding(finding)
4921
5542
  );
4922
5543
  console.log(
4923
5544
  `nomos-preflight: ${cfg.name} (${domainModules.length} domain(s))`
@@ -4943,11 +5564,6 @@ async function main(): Promise<void> {
4943
5564
  return;
4944
5565
  }
4945
5566
 
4946
- const isFrameworkAuthorTimeFinding = (
4947
- finding: (typeof authorTimeFindings)[number]
4948
- ) =>
4949
- finding.domain === APPLICATION_HOME_DOMAIN ||
4950
- finding.directiveId?.startsWith("nomos_internal_") === true;
4951
5567
  const earlyBusinessRefusals = authorTimeFindings.filter(
4952
5568
  (finding) =>
4953
5569
  !isFrameworkAuthorTimeFinding(finding) && finding.level === "refuse"
@@ -4976,6 +5592,7 @@ async function main(): Promise<void> {
4976
5592
  process.exitCode = 1;
4977
5593
  return;
4978
5594
  }
5595
+ stage("import + compose domains");
4979
5596
 
4980
5597
  // ── STABLE-ID CONTINUITY (#58 — names are labels, identity is minted) ──
4981
5598
  // Derive each composed domain's stable ids from the PRIOR compile: the committed
@@ -5093,7 +5710,12 @@ async function main(): Promise<void> {
5093
5710
  const discoveredGraph = discoverLawGraph(
5094
5711
  graphModule,
5095
5712
  emittedStableIds,
5096
- nextIdentity
5713
+ nextIdentity,
5714
+ { workspaceEffectDomain: (target) => {
5715
+ const owners = directiveExportSources.filter(candidate => candidate.directive === target);
5716
+ if (owners.length !== 1) fail(`workspace effect '${target.id}' must have exactly one declaration export; found ${owners.length}`);
5717
+ return owners[0]!.domainKey;
5718
+ } }
5097
5719
  );
5098
5720
  for (const node of discoveredGraph.graph.nodes) {
5099
5721
  if (
@@ -5155,6 +5777,53 @@ async function main(): Promise<void> {
5155
5777
  };
5156
5778
  }
5157
5779
 
5780
+ if (lawInspection) {
5781
+ const graphs = [...typedLawGraphs.values()];
5782
+ const paths = new Map<LawGraph["nodes"][number], string>();
5783
+ const compiledGraphs = new Map<LawGraph, ReturnType<typeof compileSparseLawGraphUsda>>();
5784
+ for (const graph of graphs) {
5785
+ const compiled = compileSparseLawGraphUsda(graph, `domains/${graph.domain.entity.label}.usda`, graphs);
5786
+ compiledGraphs.set(graph, compiled);
5787
+ for (const node of graph.nodes) {
5788
+ const lawPath = compiled.paths.get(node.entity.id);
5789
+ if (lawPath !== undefined) paths.set(node, lawPath);
5790
+ }
5791
+ }
5792
+ const report = {
5793
+ ...commandWorkingSetReport(graphs, new LawDependencyIndex(graphs, paths,
5794
+ (graph, dependency) => compiledGraphs.get(graph)!.resolveDependency(dependency))),
5795
+ packageName: cfg.name, configPath,
5796
+ identitySha256: existsSync(lockPath)
5797
+ ? createHash("sha256").update(readFileSync(lockPath)).digest("hex") : null,
5798
+ };
5799
+ const reportPath = path.join(outDir, "command-working-sets.json");
5800
+ writeFileSync(reportPath, JSON.stringify(report, null, 2) + "\n");
5801
+ console.log(JSON.stringify({ kind: report.kind, graphNodes: report.graphNodes,
5802
+ commands: report.commands.length, reportPath }));
5803
+ return;
5804
+ }
5805
+
5806
+ if (sqliteInspection) {
5807
+ const report = {
5808
+ ...inspectSqliteLayouts([...typedLawGraphs.values()]),
5809
+ computationEngine: await sqliteComputationBundle(
5810
+ entryDomainSourceByKey,
5811
+ collectEngineRouting(routingInput)
5812
+ ),
5813
+ packageName: cfg.name,
5814
+ configPath,
5815
+ descriptorSamples: sqliteDescriptorSamples(domainModules, typedLawGraphs),
5816
+ identitySha256: existsSync(lockPath)
5817
+ ? createHash("sha256").update(readFileSync(lockPath)).digest("hex")
5818
+ : null,
5819
+ scope: "declaration/storage mapping only; not runtime reads, query coverage or release approval",
5820
+ };
5821
+ const reportPath = path.join(outDir, "sqlite-layout-report.json");
5822
+ writeFileSync(reportPath, JSON.stringify(report, null, 2) + "\n");
5823
+ console.log(JSON.stringify({ kind: report.kind, ...report.summary, reportPath }));
5824
+ return;
5825
+ }
5826
+
5158
5827
  // ── ORDINARY-COMPILE EVOLUTION GATE ──────────────────────────────────────────────
5159
5828
  // Agents reliably run `compile`; they do not reliably discover an optional migration command.
5160
5829
  // Compare the candidate against the committed identity frontier here, after stable-id
@@ -5182,25 +5851,13 @@ async function main(): Promise<void> {
5182
5851
  const locked =
5183
5852
  historicalLock.domains[mod.name] ?? historicalLock.domains[domainKey];
5184
5853
  if (locked !== undefined) {
5185
- const currentImplementationDetailAggregateIds = new Set(
5186
- mod.aggregates
5187
- .filter(
5188
- (aggregate) =>
5189
- (aggregate as { businessBirthInternal?: unknown })
5190
- .businessBirthInternal === true
5191
- )
5192
- .map((aggregate) => aggregate.id)
5193
- );
5194
5854
  // Framework aggregates are shared implementation law, reconciled under @framework rather
5195
5855
  // than owned by this tenant domain. Domain-scoped implementation aggregates such as
5196
- // NomosSourceFrontier deliberately retain the tenant domain's law namespace, but are still
5197
- // hidden from its structural interface. A package may compose a different framework subset
5198
- // as its topology changes; never instruct a tenant to author a retirement for Nomos internals.
5856
+ // NomosSourceFrontier still hold durable state even when hidden from the public interface.
5857
+ // Keep them in the evolution check: hiding an aggregate is not authority to replace its SID.
5199
5858
  const localAggregates = Object.fromEntries(
5200
5859
  Object.entries(locked.stableIds.aggregates).filter(
5201
- ([aggregateId, aggregate]) =>
5202
- !frameworkAggregateSids.has(aggregate.sid) &&
5203
- !currentImplementationDetailAggregateIds.has(aggregateId)
5860
+ ([, aggregate]) => !frameworkAggregateSids.has(aggregate.sid)
5204
5861
  )
5205
5862
  );
5206
5863
  priorDomainsForPackage[domainKey] = {
@@ -5253,13 +5910,14 @@ async function main(): Promise<void> {
5253
5910
  const packageInterface: Record<string, DomainInterface> = {};
5254
5911
  for (const mod of presentationDomainModules) {
5255
5912
  const key = mod.domain ?? mod.name;
5256
- const di = domainInterface(mod, nextLock.domains[mod.name]?.stableIds);
5257
5913
  const graph = typedLawGraphs.get(mod.name);
5258
5914
  if (graph === undefined) {
5259
5915
  fail(
5260
5916
  `compiler surface invariant: domain '${key}' has no typed USDA graph`
5261
5917
  );
5262
5918
  }
5919
+ const offeredInterface = domainInterfaceFromLawGraph(graph);
5920
+ const di = publicClientInterface([mod], { [key]: offeredInterface })[key]!;
5263
5921
  const graphRoutes = graph.nodes
5264
5922
  .filter((node) => node.kind === "intent" && node.public)
5265
5923
  .map((node) => (node.kind === "intent" ? node.publicRoute : ""))
@@ -5308,12 +5966,16 @@ async function main(): Promise<void> {
5308
5966
  }
5309
5967
  : di;
5310
5968
  }
5311
- // NO INTERFACE SIDECAR. The structural interface is an in-memory codegen model only: it is baked
5312
- // INTO the generated clients where compiler tooling can inspect the requirement that will ship.
5313
- // Emitting it as JSON created a second representation of law that DRIFTED: the prior sidecar
5314
- // omitted `principal` and `bornAt`, two fields the USDA declares
5315
- // `nomos:required = true`, so anything reading it saw a contract the kernel would refuse.
5316
- // One fact, one representation: the typed USDA closure is the contract.
5969
+ // The application compatibility requirement is a first-class USDA build artifact. It is deliberately
5970
+ // NOT installed law: clients carry these opaque bytes to the kernel, which compares the requirement
5971
+ // against authenticated installed-law stages. The compiler and generated clients all derive it from
5972
+ // this same in-memory graph; no JSON/hash compatibility twin exists.
5973
+ const compatibilityUsda = emitApplicationCompatibilityUsda(packageInterface);
5974
+ const compatibilityUsdaPath = path.join(
5975
+ outDir,
5976
+ `${cfg.name}.compatibility.usda`
5977
+ );
5978
+ writeFileSync(compatibilityUsdaPath, compatibilityUsda, "utf8");
5317
5979
 
5318
5980
  // ── RECURSIVE-PAYLOAD NOTES (z.lazy) — a self-referencing tree/JSON payload field is FULLY
5319
5981
  // supported: each codegen layer mints a named recursive type for it (TS `type <Name>`, Dart
@@ -5351,6 +6013,9 @@ async function main(): Promise<void> {
5351
6013
  // exact texts under their logical paths; no parallel layer directory or JSON reconstruction exists.
5352
6014
  const v3Closure = [...typedLawGraphs.values()];
5353
6015
  const v3DomainAssets: EmittedLawClosureAsset[] = [];
6016
+ const v3DomainRoots: string[] = [];
6017
+ const v3LawPathByNode = new Map<LawGraph["nodes"][number], string>();
6018
+ const v3CompiledGraphs = new Map<LawGraph, ReturnType<typeof compileSparseLawGraphUsda>>();
5354
6019
  const typedDomainIdentityByName = new Map<string, string>();
5355
6020
  const typedLawPathsByName = new Map<string, ReadonlyMap<string, string>>();
5356
6021
  for (const [domain, graph] of typedLawGraphs) {
@@ -5361,6 +6026,7 @@ async function main(): Promise<void> {
5361
6026
  logicalPath,
5362
6027
  v3Closure
5363
6028
  );
6029
+ v3CompiledGraphs.set(graph, compiledLayer);
5364
6030
  const layerText = compiledLayer.text;
5365
6031
  const sha256 = sha256HexUtf8(layerText);
5366
6032
  v3DomainAssets.push({
@@ -5368,10 +6034,51 @@ async function main(): Promise<void> {
5368
6034
  sha256,
5369
6035
  utf8: layerText,
5370
6036
  });
6037
+ const domainRoot = compiledLayer.paths.get(graph.domain.entity.id);
6038
+ if (domainRoot === undefined) {
6039
+ fail(`domain '${domain}' did not emit its canonical OpenUSD root path`);
6040
+ }
6041
+ v3DomainRoots.push(domainRoot);
6042
+ for (const node of graph.nodes) {
6043
+ const lawPath = compiledLayer.paths.get(node.entity.id);
6044
+ if (lawPath !== undefined) v3LawPathByNode.set(node, lawPath);
6045
+ }
5371
6046
  typedDomainIdentityByName.set(domain, sha256);
5372
6047
  typedLawPathsByName.set(domain, compiledLayer.paths);
5373
6048
  }
5374
6049
 
6050
+ // Index the transient graph once. Both projections follow the same nominal edges, including
6051
+ // transitive cross-domain dependencies. Per-command selection also follows reverse guard edges;
6052
+ // the retained package command projection remains the compatibility consumer until the runtime
6053
+ // can carry a member-scoped contract without presenting it as a complete package.
6054
+ const lawDependencies = new LawDependencyIndex(v3Closure, v3LawPathByNode,
6055
+ (graph, dependency) => v3CompiledGraphs.get(graph)!.resolveDependency(dependency));
6056
+ const commonProjectionPaths = [
6057
+ "/Nomos/Package",
6058
+ "/Nomos/Framework",
6059
+ "/__NomosRuntime_7e9c/Routes",
6060
+ ...v3DomainRoots.map((root) => `${root}/Retirements`),
6061
+ ];
6062
+ const readSeeds = v3Closure.flatMap(graph => graph.nodes.filter(node =>
6063
+ node.kind === "aggregate" || node.kind === "read" ||
6064
+ node.kind === "relation" || node.kind === "object-type"));
6065
+ const commandSeeds = v3Closure.flatMap(graph => graph.nodes.filter(node =>
6066
+ node.kind === "intent" || node.kind === "policy" || node.kind === "invariant" ||
6067
+ node.kind === "capability" || node.kind === "workspace-type" ||
6068
+ (node.kind === "structural" && node.structuralKind === "flow")));
6069
+ const readProjectionPaths = new Set([
6070
+ ...commonProjectionPaths, ...lawDependencies.select(readSeeds).paths,
6071
+ ]);
6072
+ const commandProjectionPaths = new Set([
6073
+ ...commonProjectionPaths, ...lawDependencies.select(commandSeeds, true).paths,
6074
+ ]);
6075
+ // Diagnostic only: these paths are not executable authority or a new law file format. A future
6076
+ // member-scoped USD artifact must prove its complete semantic contract before replacing the
6077
+ // package-wide runtime view. In particular USD masks include descendants, so node counts alone
6078
+ // are not a claim about materialized prim count, file I/O, RSS, or first-command latency.
6079
+ writeFileSync(path.join(outDir, "command-working-sets.json"),
6080
+ JSON.stringify(commandWorkingSetReport(v3Closure, lawDependencies), null, 2) + "\n");
6081
+
5375
6082
  // Some exact directives are deliberately private implementation details: they must not appear in
5376
6083
  // the tenant's generated client, but typed application-law edges can still require the kernel to
5377
6084
  // execute them. Preserve that distinction here. The exact Directive object and its one configured
@@ -5380,39 +6087,44 @@ async function main(): Promise<void> {
5380
6087
  const retainExactExecutableDirective = (
5381
6088
  edge: string,
5382
6089
  declaredDomain: string | undefined,
5383
- directive: Parameters<typeof synthesizePreconditionInvariants>[0],
6090
+ directive: Parameters<typeof synthesizePreconditionInvariants>[0]
5384
6091
  ) => {
5385
6092
  const matches = directiveExportSources.filter(
5386
- (candidate) => candidate.directive === directive,
6093
+ (candidate) => candidate.directive === directive
5387
6094
  );
5388
6095
  if (matches.length !== 1) {
5389
6096
  fail(
5390
- `${edge} targets '${declaredDomain === undefined ? directive.id : `${declaredDomain}/${directive.id}`}', ` +
6097
+ `${edge} targets '${
6098
+ declaredDomain === undefined
6099
+ ? directive.id
6100
+ : `${declaredDomain}/${directive.id}`
6101
+ }', ` +
5391
6102
  `but that exact Directive object is ` +
5392
- `exported by ${matches.length} configured law modules; export it once`,
6103
+ `exported by ${matches.length} configured law modules; export it once`
5393
6104
  );
5394
6105
  }
5395
6106
  const target = matches[0]!;
5396
6107
  if (declaredDomain !== undefined && target.domainKey !== declaredDomain) {
5397
6108
  fail(
5398
6109
  `${edge} declares '${declaredDomain}/${directive.id}', but the exact Directive export belongs ` +
5399
- `to domain '${target.domainKey}'`,
6110
+ `to domain '${target.domainKey}'`
5400
6111
  );
5401
6112
  }
5402
- const targetIds = executableDirectiveIdsByDomain.get(target.domainKey) ?? [];
6113
+ const targetIds =
6114
+ executableDirectiveIdsByDomain.get(target.domainKey) ?? [];
5403
6115
  if (!targetIds.includes(directive.id)) {
5404
6116
  executableDirectiveIdsByDomain.set(
5405
6117
  target.domainKey,
5406
- [...targetIds, directive.id].sort(),
6118
+ [...targetIds, directive.id].sort()
5407
6119
  );
5408
6120
  }
5409
6121
  const targetDirectives = new Map(
5410
- executableDirectivesByDomain.get(target.domainKey) ?? [],
6122
+ executableDirectivesByDomain.get(target.domainKey) ?? []
5411
6123
  );
5412
6124
  targetDirectives.set(directive.id, directive);
5413
6125
  executableDirectivesByDomain.set(target.domainKey, targetDirectives);
5414
6126
  const targetExports = new Map(
5415
- offerSourceExportsByDomain.get(target.domainKey) ?? [],
6127
+ offerSourceExportsByDomain.get(target.domainKey) ?? []
5416
6128
  );
5417
6129
  targetExports.set(directive.id, {
5418
6130
  modulePath: target.modulePath,
@@ -5430,7 +6142,7 @@ async function main(): Promise<void> {
5430
6142
  retainExactExecutableDirective(
5431
6143
  "application home genesis",
5432
6144
  step.domain,
5433
- step.directive,
6145
+ step.directive
5434
6146
  );
5435
6147
  }
5436
6148
 
@@ -5450,10 +6162,19 @@ async function main(): Promise<void> {
5450
6162
  for (const module of domainModules) {
5451
6163
  const sourceDomain = module.domain ?? module.name;
5452
6164
  for (const directive of module.directives ?? []) {
5453
- if (!isBusinessIntent(directive)) continue;
5454
- for (const effect of directive.workspaceEffects) {
6165
+ for (const effect of directive.workspaceEffects ?? []) {
5455
6166
  const sourceMatches = directiveExportSources.filter(
5456
- (candidate) => candidate.directive === directive
6167
+ (candidate) => {
6168
+ if (candidate.directive === directive) return true;
6169
+ // Composition may add static read/auth requirements by copying a declaration.
6170
+ // Its executable plan, payload contract and outbound papers remain the exact
6171
+ // source objects; do not confuse that with an unrelated same-named command.
6172
+ const source = candidate.directive as Directive<unknown>;
6173
+ return candidate.domainKey === sourceDomain &&
6174
+ source.id === directive.id && source.plan === directive.plan &&
6175
+ source.payloadSchema === directive.payloadSchema &&
6176
+ source.workspaceEffects === directive.workspaceEffects;
6177
+ }
5457
6178
  );
5458
6179
  if (sourceMatches.length !== 1) {
5459
6180
  fail(
@@ -5465,7 +6186,7 @@ async function main(): Promise<void> {
5465
6186
  const target = retainExactExecutableDirective(
5466
6187
  `workspace effect '${sourceDomain}/${directive.id}'`,
5467
6188
  undefined,
5468
- effect.target,
6189
+ effect.target
5469
6190
  );
5470
6191
  const graph = typedLawGraphs.get(target.graphName);
5471
6192
  const paths = typedLawPathsByName.get(target.graphName);
@@ -5474,6 +6195,9 @@ async function main(): Promise<void> {
5474
6195
  `workspace effect target '${target.graphName}/${effect.target.id}' has no canonical law graph`
5475
6196
  );
5476
6197
  }
6198
+ // This call is authored by authenticated source law inside the kernel
6199
+ // plan and becomes a carried Layer. It does not cross the public client
6200
+ // ABI whose operation payload is Protobuf.
5477
6201
  const generated = emitTsCallUsdaEncoder(
5478
6202
  graph,
5479
6203
  paths,
@@ -5493,6 +6217,7 @@ async function main(): Promise<void> {
5493
6217
  }
5494
6218
  }
5495
6219
  }
6220
+ stage("stable ids + evolve gate + interface");
5496
6221
 
5497
6222
  // ── reports ──
5498
6223
  const entryReports: string[] = [];
@@ -5521,6 +6246,67 @@ async function main(): Promise<void> {
5521
6246
  // law whose invariant declarations are inconsistent refuses to COMPILE, and the
5522
6247
  // table can never disagree with what a full force would register).
5523
6248
  const routing = collectEngineRouting(routingInput);
6249
+ stage(" ↳ collectEngineRouting");
6250
+ const dartCfg = cfg.dart ?? false;
6251
+ if (
6252
+ dartCfg !== false &&
6253
+ dartCfg !== true &&
6254
+ (typeof dartCfg !== "object" ||
6255
+ dartCfg === null ||
6256
+ (dartCfg.out !== undefined && typeof dartCfg.out !== "string") ||
6257
+ (dartCfg.support !== undefined && dartCfg.support !== "package") ||
6258
+ (dartCfg.barrel !== undefined &&
6259
+ !/^[a-z][a-z0-9_]*$/.test(String(dartCfg.barrel))) ||
6260
+ (dartCfg.names !== undefined &&
6261
+ (typeof dartCfg.names !== "object" ||
6262
+ dartCfg.names === null ||
6263
+ Array.isArray(dartCfg.names))) ||
6264
+ (dartCfg.packages !== undefined &&
6265
+ (typeof dartCfg.packages !== "object" ||
6266
+ dartCfg.packages === null ||
6267
+ typeof dartCfg.packages.types !== "string" ||
6268
+ typeof dartCfg.packages.client !== "string")))
6269
+ ) {
6270
+ fail(
6271
+ `config key 'dart' must be false, true, or { out?: "relative/dir", support?: "package", barrel?: "snake_case_name", packages?: { types, client, test?, compat? }, names?: { Generated: "Alias" } }; got ${JSON.stringify(
6272
+ dartCfg
6273
+ )}`
6274
+ );
6275
+ }
6276
+ const dartPackages =
6277
+ typeof dartCfg === "object" ? dartCfg.packages : undefined;
6278
+ const dartNames = typeof dartCfg === "object" ? dartCfg.names : undefined;
6279
+ // The operation Protobuf bindings depend only on the composed law, so start their generators
6280
+ // now: protoc-gen-dart alone costs seconds of JIT start on a cold cache, and nothing below
6281
+ // needs the bindings until the TypeScript and Dart emits await these.
6282
+ const tsOperationProtoPrewarm = prewarmTsOperationProto(
6283
+ cfgDir,
6284
+ [
6285
+ ...new Map(
6286
+ [...closedDomainModules, ...presentationDomainModules].map(
6287
+ (module) => [module.name, module] as const
6288
+ )
6289
+ ).values(),
6290
+ ],
6291
+ typedLawGraphs,
6292
+ typedLawPathsByName
6293
+ );
6294
+ const dartOperationProtoPrewarm =
6295
+ dartCfg === false
6296
+ ? Promise.resolve()
6297
+ : prewarmDartOperationProto(
6298
+ dartPackages === undefined
6299
+ ? presentationDomainModules
6300
+ : [
6301
+ ...new Map(
6302
+ [...presentationDomainModules, ...closedDomainModules].map(
6303
+ (module) => [module.name, module] as const
6304
+ )
6305
+ ).values(),
6306
+ ],
6307
+ typedLawGraphs,
6308
+ typedLawPathsByName
6309
+ );
5524
6310
  // Zanzibar identities are namespaced by the domain's STABLE USDA identity, not its mutable dispatch
5525
6311
  // label. Carry that one label→identity table into every plan asset so writeTuple() emits the same
5526
6312
  // object type the typed law graph placed on gates and relation declarations.
@@ -5610,6 +6396,7 @@ async function main(): Promise<void> {
5610
6396
  pathToFileURL(path.join(path.dirname(esbuildPkgJson), "lib", "main.js"))
5611
6397
  .href
5612
6398
  )) as typeof import("esbuild");
6399
+ stage(" ↳ esbuild import");
5613
6400
 
5614
6401
  const compact =
5615
6402
  process.env.NOMOS_READABLE === "1"
@@ -5687,18 +6474,25 @@ async function main(): Promise<void> {
5687
6474
  ])
5688
6475
  );
5689
6476
  let planSequence = 0;
6477
+ const PLAN_ENTRY_SOURCEFILE = ".nomos_plan.ts";
5690
6478
  const orderedDomainKeys = [...entryDomainSourceByKey.keys()];
5691
6479
  const frameworkPlanModulePaths = [
5692
- ...new Set(
5693
- Object.values(BUSINESS_BIRTH_FRAMEWORK_PLAN_SOURCES).map(
6480
+ ...new Set([
6481
+ ...Object.values(BUSINESS_BIRTH_FRAMEWORK_PLAN_SOURCES).map(
5694
6482
  (source) => source.modulePath
5695
- )
5696
- ),
6483
+ ),
6484
+ CHILD_AUTHENTICATION_MODULE_PATH,
6485
+ RECEIVING_GROUP_RECOVERY_MODULE_PATH,
6486
+ MODEL_TRANSITION_MODULE_PATH,
6487
+ MODEL_AUTHORITY_REPAIR_MODULE_PATH,
6488
+ ]),
5697
6489
  ];
6490
+ stage(" ↳ plan compiler identity");
5698
6491
  const planSourceSlicer = new PlanSourceSliceProgram(
5699
6492
  [...configuredModulePaths, ...frameworkPlanModulePaths],
5700
6493
  cfgDir
5701
6494
  );
6495
+ stage(" ↳ PlanSourceSliceProgram (ts.createProgram)");
5702
6496
  const routingFor = (domainKeys: readonly string[]) => {
5703
6497
  const wanted = new Set(domainKeys);
5704
6498
  const retain = (
@@ -5886,6 +6680,7 @@ async function main(): Promise<void> {
5886
6680
  const authoredRuntimeImports = planSourceSlicer.externalRuntimeImports(
5887
6681
  publicRuntimeCandidates
5888
6682
  );
6683
+ stage(" ↳ externalRuntimeImports");
5889
6684
  const sharedRuntimeModules: Array<{
5890
6685
  readonly id: string;
5891
6686
  readonly source: string;
@@ -5925,7 +6720,10 @@ async function main(): Promise<void> {
5925
6720
  let sharedRuntimeProfileInputs:
5926
6721
  | readonly { readonly module: string; readonly bytesInOutput: number }[]
5927
6722
  | undefined;
5928
- const compileSharedRuntimeProfile = async (): Promise<number> => {
6723
+ let sharedRuntimeProfileCompilation: Promise<number> | undefined;
6724
+ const compileSharedRuntimeProfile = (): Promise<number> =>
6725
+ (sharedRuntimeProfileCompilation ??= compileSharedRuntimeProfileOnce());
6726
+ const compileSharedRuntimeProfileOnce = async (): Promise<number> => {
5929
6727
  if (sharedRuntimeProfileBytes !== undefined)
5930
6728
  return sharedRuntimeProfileBytes;
5931
6729
  const sourcePath = path.join(outDir, ".nomos_shared_runtime_profile.ts");
@@ -6120,10 +6918,16 @@ async function main(): Promise<void> {
6120
6918
  if (cached !== undefined) {
6121
6919
  bundled = cached.javascript;
6122
6920
  } else {
6123
- const sourcePath = path.join(outDir, `.nomos_plan_${planSequence++}.ts`);
6124
- writeFileSync(sourcePath, entrySource, "utf8");
6921
+ // The entry rides esbuild's stdin under one constant name: a numbered temp file put its
6922
+ // sequence number into the bundle's module header, so identical law could hash differently
6923
+ // depending on how many plans a warm cache had skipped before it.
6125
6924
  const result = await esbuildMod.build({
6126
- entryPoints: [sourcePath],
6925
+ stdin: {
6926
+ contents: entrySource,
6927
+ loader: "ts",
6928
+ resolveDir: cfgDir,
6929
+ sourcefile: PLAN_ENTRY_SOURCEFILE,
6930
+ },
6127
6931
  absWorkingDir: cfgDir,
6128
6932
  bundle: true,
6129
6933
  write: false,
@@ -6140,7 +6944,6 @@ async function main(): Promise<void> {
6140
6944
  ? {}
6141
6945
  : { plugins: [planSourceSlicer.plugin(effectiveSliceTargets)] }),
6142
6946
  });
6143
- rmSync(sourcePath, { force: true });
6144
6947
  const output = result.outputFiles?.[0];
6145
6948
  if (output === undefined)
6146
6949
  fail("shared-runtime plan compile produced no JavaScript output");
@@ -6159,13 +6962,11 @@ async function main(): Promise<void> {
6159
6962
  );
6160
6963
  }
6161
6964
  const inputs = Object.keys(emitted)
6965
+ .filter((module) => module !== PLAN_ENTRY_SOURCEFILE)
6162
6966
  .map((module) =>
6163
6967
  path.isAbsolute(module) ? module : path.resolve(cfgDir, module)
6164
6968
  )
6165
- .filter(
6166
- (file) =>
6167
- path.resolve(file) !== path.resolve(sourcePath) && existsSync(file)
6168
- )
6969
+ .filter((file) => existsSync(file))
6169
6970
  .map((file) => ({ path: file, sha256: planInputDigest(file) }))
6170
6971
  .sort((left, right) => left.path.localeCompare(right.path));
6171
6972
  if (inputs.length > 0) {
@@ -6246,11 +7047,28 @@ async function main(): Promise<void> {
6246
7047
  }
6247
7048
  }
6248
7049
  };
6249
- for (const key of orderedDomainKeys) {
6250
- const asset = await compilePlan([key], false, false);
7050
+ const publicOfferSelector = (domain: string, directiveId: string): string => {
7051
+ const graph = typedLawGraphs.get(domain);
7052
+ const intent = graph?.nodes.find(
7053
+ (node) => node.kind === "intent" && node.entity.label === directiveId
7054
+ );
7055
+ return `plan:offer:${domain}:${
7056
+ intent?.kind === "intent" ? intent.publicRoute : directiveId
7057
+ }`;
7058
+ };
7059
+ stage("engine entry + routing");
7060
+ // Every plan below is an independent esbuild bundle (memoized by signature, deduplicated by
7061
+ // content hash, and emitted sorted by selector/hash), so compile them concurrently and bind
7062
+ // routes afterwards in declaration order.
7063
+ const domainPlans = await Promise.all(
7064
+ orderedDomainKeys.map((key) => compilePlan([key], false, false))
7065
+ );
7066
+ orderedDomainKeys.forEach((key, index) => {
7067
+ const asset = domainPlans[index]!;
6251
7068
  domainPlanByKey.set(key, asset);
6252
7069
  routeAsset(`plan:domain:${key}`, asset);
6253
- }
7070
+ });
7071
+ stage("shared runtime + domain plans");
6254
7072
 
6255
7073
  // Public offers are addressed more narrowly than domains. Split direct TypeScript exports by
6256
7074
  // authored source span, then symbol-slice each shard so one large domain file cannot impose one
@@ -6267,10 +7085,14 @@ async function main(): Promise<void> {
6267
7085
  // 8 KiB; an irreducible single offer remains explicit and attributable instead of being hidden
6268
7086
  // inside a domain-wide fallback.
6269
7087
  const OFFER_PLAN_MAX_UTF8_BYTES = 48 * 1024;
6270
- for (const domain of orderedDomainKeys) {
6271
- const fallback = domainPlanByKey.get(domain)!;
7088
+ interface OfferShardEntry {
7089
+ directiveId: string;
7090
+ exportName: string;
7091
+ aggregateId: string;
7092
+ position: number;
7093
+ }
7094
+ const domainOfferPlans = await Promise.all(orderedDomainKeys.map(async (domain) => {
6272
7095
  const exports = offerSourceExportsByDomain.get(domain) ?? new Map();
6273
- const routed = new Set<string>();
6274
7096
 
6275
7097
  // Business-birth lowering injects several framework offers which execute as one child-genesis
6276
7098
  // transaction. Compile their typed source closures into one shared plan: preparing any one exact
@@ -6283,6 +7105,7 @@ async function main(): Promise<void> {
6283
7105
  const target = BUSINESS_BIRTH_FRAMEWORK_PLAN_SOURCES[directiveId];
6284
7106
  return target === undefined ? [] : [{ directiveId, ...target }];
6285
7107
  });
7108
+ let frameworkPlan: Promise<UsdPlanAsset> | undefined;
6286
7109
  if (frameworkTargets.length > 0) {
6287
7110
  const selectedModules = new Map<
6288
7111
  string,
@@ -6310,7 +7133,7 @@ async function main(): Promise<void> {
6310
7133
  const source = ` ${JSON.stringify(
6311
7134
  BUSINESS_BIRTH_FRAMEWORK_PLAN_DOMAIN
6312
7135
  )}: [${selectedSources.join(", ")}],`;
6313
- const asset = await compilePlan([domain], false, false, {
7136
+ frameworkPlan = compilePlan([domain], false, false, {
6314
7137
  sourceOverrides: new Map([[domain, source]]),
6315
7138
  includePreloads: false,
6316
7139
  cacheKey: "business-birth-framework",
@@ -6318,23 +7141,13 @@ async function main(): Promise<void> {
6318
7141
  routedDomainFallback: BUSINESS_BIRTH_FRAMEWORK_PLAN_DOMAIN,
6319
7142
  includeMintInstalls: false,
6320
7143
  });
6321
- for (const target of frameworkTargets) {
6322
- routeAsset(`plan:offer:${domain}:${target.directiveId}`, asset);
6323
- retainGeneratedInvariantRoutes(domain, target.directiveId, asset);
6324
- routed.add(target.directiveId);
6325
- }
6326
7144
  }
6327
- const byModule = new Map<
6328
- string,
6329
- Array<{
6330
- directiveId: string;
6331
- exportName: string;
6332
- aggregateId: string;
6333
- position: number;
6334
- }>
6335
- >();
7145
+ const routedByFramework = new Set(
7146
+ frameworkTargets.map((target) => target.directiveId)
7147
+ );
7148
+ const byModule = new Map<string, OfferShardEntry[]>();
6336
7149
  for (const [directiveId, source] of exports) {
6337
- if (routed.has(directiveId)) continue;
7150
+ if (routedByFramework.has(directiveId)) continue;
6338
7151
  const position = planSourceSlicer.exportPosition({
6339
7152
  modulePath: source.modulePath,
6340
7153
  exportName: source.exportName,
@@ -6349,14 +7162,14 @@ async function main(): Promise<void> {
6349
7162
  });
6350
7163
  byModule.set(source.modulePath, entries);
6351
7164
  }
6352
- for (const [modulePath, entries] of byModule) {
7165
+ const offers = await Promise.all([...byModule].map(async ([modulePath, entries]) => {
6353
7166
  entries.sort(
6354
7167
  (left, right) =>
6355
7168
  left.position - right.position ||
6356
7169
  left.directiveId.localeCompare(right.directiveId)
6357
7170
  );
6358
- const shards: (typeof entries)[] = [];
6359
- let current: typeof entries = [];
7171
+ const shards: OfferShardEntry[][] = [];
7172
+ let current: OfferShardEntry[] = [];
6360
7173
  let shardStart = 0;
6361
7174
  for (const entry of entries) {
6362
7175
  if (
@@ -6371,8 +7184,8 @@ async function main(): Promise<void> {
6371
7184
  }
6372
7185
  if (current.length > 0) shards.push(current);
6373
7186
  const compileOfferShard = async (
6374
- shard: typeof entries
6375
- ): Promise<void> => {
7187
+ shard: OfferShardEntry[]
7188
+ ): Promise<Array<{ entry: OfferShardEntry; asset: UsdPlanAsset }>> => {
6376
7189
  const selected: PlanSourceSliceTarget[] = shard.map((entry) => ({
6377
7190
  modulePath,
6378
7191
  exportName: entry.exportName,
@@ -6404,34 +7217,55 @@ async function main(): Promise<void> {
6404
7217
  let splitAt = shard.findIndex((entry) => entry.position >= midpoint);
6405
7218
  if (splitAt <= 0 || splitAt >= shard.length)
6406
7219
  splitAt = Math.ceil(shard.length / 2);
6407
- await compileOfferShard(shard.slice(0, splitAt));
6408
- await compileOfferShard(shard.slice(splitAt));
6409
- return;
6410
- }
6411
- for (const entry of shard) {
6412
- routeAsset(`plan:offer:${domain}:${entry.directiveId}`, asset);
6413
- retainGeneratedInvariantRoutes(domain, entry.directiveId, asset);
6414
- if (entry.directiveId.startsWith("nomosSeed")) {
6415
- // The generated seed writes the born aggregate immediately. Its `.fromBirth()` / user
6416
- // aggregate invariant is part of the same authored symbol closure, so route that first
6417
- // gate check through the already-warmed slice instead of cold-loading the whole domain.
6418
- aggregateInvariantPlanOverrides.set(entry.aggregateId, asset);
6419
- aggregateInvariantPlanOverrides.set(
6420
- `${domain}.${entry.aggregateId}`,
6421
- asset
6422
- );
6423
- }
6424
- routed.add(entry.directiveId);
7220
+ const [left, right] = await Promise.all([
7221
+ compileOfferShard(shard.slice(0, splitAt)),
7222
+ compileOfferShard(shard.slice(splitAt)),
7223
+ ]);
7224
+ return [...left, ...right];
6425
7225
  }
7226
+ return shard.map((entry) => ({ entry, asset }));
6426
7227
  };
6427
- for (const shard of shards) await compileOfferShard(shard);
7228
+ return (await Promise.all(shards.map(compileOfferShard))).flat();
7229
+ }));
7230
+ return {
7231
+ domain,
7232
+ frameworkTargets,
7233
+ frameworkAsset: frameworkPlan === undefined ? undefined : await frameworkPlan,
7234
+ offers: offers.flat(),
7235
+ };
7236
+ }));
7237
+ for (const { domain, frameworkTargets, frameworkAsset, offers } of domainOfferPlans) {
7238
+ const fallback = domainPlanByKey.get(domain)!;
7239
+ const routed = new Set<string>();
7240
+ if (frameworkAsset !== undefined) {
7241
+ for (const target of frameworkTargets) {
7242
+ routeAsset(publicOfferSelector(domain, target.directiveId), frameworkAsset);
7243
+ retainGeneratedInvariantRoutes(domain, target.directiveId, frameworkAsset);
7244
+ routed.add(target.directiveId);
7245
+ }
7246
+ }
7247
+ for (const { entry, asset } of offers) {
7248
+ routeAsset(publicOfferSelector(domain, entry.directiveId), asset);
7249
+ retainGeneratedInvariantRoutes(domain, entry.directiveId, asset);
7250
+ if (entry.directiveId.startsWith("nomosSeed")) {
7251
+ // The generated seed writes the born aggregate immediately. Its `.fromBirth()` / user
7252
+ // aggregate invariant is part of the same authored symbol closure, so route that first
7253
+ // gate check through the already-warmed slice instead of cold-loading the whole domain.
7254
+ aggregateInvariantPlanOverrides.set(entry.aggregateId, asset);
7255
+ aggregateInvariantPlanOverrides.set(
7256
+ `${domain}.${entry.aggregateId}`,
7257
+ asset
7258
+ );
7259
+ }
7260
+ routed.add(entry.directiveId);
6428
7261
  }
6429
7262
  for (const directiveId of executableDirectiveIdsByDomain.get(domain) ??
6430
7263
  []) {
6431
7264
  if (!routed.has(directiveId))
6432
- routeAsset(`plan:offer:${domain}:${directiveId}`, fallback);
7265
+ routeAsset(publicOfferSelector(domain, directiveId), fallback);
6433
7266
  }
6434
7267
  }
7268
+ stage("offer shard plans");
6435
7269
  // Hand-authored workspace invariants are independently addressable law too. The previous
6436
7270
  // fallback compiled their entire domain merely to run one `reads`/`assert` pair, even though
6437
7271
  // offer compilation already knows how to retain one authored export closure. Build that exact
@@ -6439,6 +7273,10 @@ async function main(): Promise<void> {
6439
7273
  // When `language(...)` remapped private effect invariants onto public intents, retain only those
6440
7274
  // public intent exports and reconstruct that language so the existing remapping machinery remains
6441
7275
  // the sole owner of payload adaptation.
7276
+ const workspaceInvariantPlans: Array<{
7277
+ readonly invariantIds: readonly string[];
7278
+ readonly plan: Promise<UsdPlanAsset>;
7279
+ }> = [];
6442
7280
  for (const domain of orderedDomainKeys) {
6443
7281
  const byDirective = new Map<
6444
7282
  string,
@@ -6521,39 +7359,48 @@ async function main(): Promise<void> {
6521
7359
  const domainSource = ` ${JSON.stringify(
6522
7360
  domain
6523
7361
  )}: [${moduleExpression}],`;
6524
- const asset = await compilePlan([domain], false, false, {
6525
- sourceOverrides: new Map([[domain, domainSource]]),
6526
- sliceTargets: [...selectedByKey.values()],
6527
- cacheKey: `workspace-invariants:${directiveId}:${members
6528
- .map(([id]) => id)
6529
- .join(",")}`,
7362
+ workspaceInvariantPlans.push({
7363
+ invariantIds: members.map(([id]) => id),
7364
+ plan: compilePlan([domain], false, false, {
7365
+ sourceOverrides: new Map([[domain, domainSource]]),
7366
+ sliceTargets: [...selectedByKey.values()],
7367
+ cacheKey: `workspace-invariants:${directiveId}:${members
7368
+ .map(([id]) => id)
7369
+ .join(",")}`,
7370
+ }),
6530
7371
  });
6531
- for (const [invariantId] of members) {
6532
- workspaceInvariantPlanOverrides.set(invariantId, asset);
6533
- }
6534
7372
  }
6535
7373
  }
6536
- await routePlan("describe", orderedDomainKeys, false, true);
7374
+ for (const { invariantIds, plan } of workspaceInvariantPlans) {
7375
+ const asset = await plan;
7376
+ for (const invariantId of invariantIds) {
7377
+ workspaceInvariantPlanOverrides.set(invariantId, asset);
7378
+ }
7379
+ }
7380
+ stage("workspace invariant plans");
7381
+ const reportRoutes: Promise<void>[] = [
7382
+ routePlan("describe", orderedDomainKeys, false, true),
7383
+ ];
6537
7384
  for (const reportId of Object.keys(cfg.reports ?? {}))
6538
- await routePlan(`report:report:${reportId}`, [], true);
6539
- const routeTable = async (
7385
+ reportRoutes.push(routePlan(`report:report:${reportId}`, [], true));
7386
+ const routeTable = (
6540
7387
  prefix: string,
6541
7388
  table: Readonly<Record<string, readonly string[]>> | undefined
6542
- ): Promise<void> => {
7389
+ ): void => {
6543
7390
  for (const [id, domains] of Object.entries(table ?? {}))
6544
- await routePlan(`${prefix}${id}`, domains);
7391
+ reportRoutes.push(routePlan(`${prefix}${id}`, domains));
6545
7392
  };
6546
- await routeTable("report:derive:", routing.derivedOf);
6547
- await routeTable("report:combine:", routing.combinedOf);
7393
+ routeTable("report:derive:", routing.derivedOf);
7394
+ routeTable("report:combine:", routing.combinedOf);
6548
7395
  for (const [id, domains] of Object.entries(
6549
7396
  routing.aggregateInvariantOf ?? {}
6550
7397
  )) {
6551
7398
  const override = aggregateInvariantPlanOverrides.get(id);
6552
7399
  if (override !== undefined)
6553
7400
  routeAsset(`report:aggregateInvariant:${id}`, override);
6554
- else await routePlan(`report:aggregateInvariant:${id}`, domains);
7401
+ else reportRoutes.push(routePlan(`report:aggregateInvariant:${id}`, domains));
6555
7402
  }
6556
- await routeTable("report:invariant:", routing.relationOf);
7403
+ routeTable("report:invariant:", routing.relationOf);
6557
7404
  for (const [id, domains] of Object.entries(
6558
7405
  routing.workspaceInvariantOf ?? {}
6559
7406
  )) {
@@ -6569,11 +7416,13 @@ async function main(): Promise<void> {
6569
7416
  routeAsset(`report:workspaceInvariant:${id}`, override);
6570
7417
  } else {
6571
7418
  if (!nativeWorkspaceInvariantReadIds.has(id)) {
6572
- await routePlan(`report:workspaceInvariantReads:${id}`, domains);
7419
+ reportRoutes.push(routePlan(`report:workspaceInvariantReads:${id}`, domains));
6573
7420
  }
6574
- await routePlan(`report:workspaceInvariant:${id}`, domains);
7421
+ reportRoutes.push(routePlan(`report:workspaceInvariant:${id}`, domains));
6575
7422
  }
6576
7423
  }
7424
+ await Promise.all(reportRoutes);
7425
+ stage("describe + report plans");
6577
7426
  // Adaptive offer splitting deliberately compiles candidate parents before retaining their children.
6578
7427
  // Only route-reachable content is custody; exploratory compiler work never becomes law bytes.
6579
7428
  const routedPlanHashes = new Set(planRoutes.map((route) => route.planSha256));
@@ -6711,6 +7560,8 @@ async function main(): Promise<void> {
6711
7560
  }
6712
7561
  }
6713
7562
 
7563
+ stage("captured-read gate");
7564
+ let bootProbeFailure: Promise<string | undefined> | undefined;
6714
7565
  // ── 1b. THE FROZEN-SANDBOX BOOT PROBE (fail-closed) ──
6715
7566
  // The sealed engine freezes globalThis after a plan asset's top level; a lazy
6716
7567
  // domain's module init runs LATER, inside the dispatch. Any module that writes
@@ -6833,28 +7684,32 @@ async function main(): Promise<void> {
6833
7684
  return { planPath, checks };
6834
7685
  });
6835
7686
  writeFileSync(probeJobsPath, JSON.stringify(probeJobs), "utf8");
6836
- try {
6837
- execFileSync(process.execPath, [probePath, probeJobsPath], {
6838
- encoding: "utf8",
6839
- stdio: ["ignore", "ignore", "pipe"],
6840
- });
6841
- } catch (e) {
7687
+ // The probe is its own child process: let it run while the law closure is composed and
7688
+ // usdcat projects it, and judge its verdict before the package is sealed.
7689
+ bootProbeFailure = execFileAsync(process.execPath, [probePath, probeJobsPath], {
7690
+ encoding: "utf8",
7691
+ maxBuffer: 64 * 1024 * 1024,
7692
+ })
7693
+ .then(() => undefined)
7694
+ .catch((e: unknown) => {
6842
7695
  const stderr = String((e as { stderr?: string }).stderr ?? "").trim();
6843
7696
  const [domainKey = "?", msg = stderr] = stderr.includes("\u0000")
6844
7697
  ? stderr.split("\u0000")
6845
7698
  : ["?", stderr];
6846
- fail(
7699
+ return (
6847
7700
  `domain '${domainKey}' failed its selected-plan registry probe — the compiler routed an ` +
6848
7701
  `offer to a plan that cannot dispatch it, or a module initializer failed after the ` +
6849
7702
  `sandbox freeze: ${msg}`
6850
7703
  );
6851
- } finally {
6852
- rmSync(probePath, { force: true });
6853
- rmSync(probeJobsPath, { force: true });
6854
- for (const planPath of probePlanPaths) rmSync(planPath, { force: true });
6855
- }
7704
+ })
7705
+ .finally(() => {
7706
+ rmSync(probePath, { force: true });
7707
+ rmSync(probeJobsPath, { force: true });
7708
+ for (const planPath of probePlanPaths) rmSync(planPath, { force: true });
7709
+ });
6856
7710
  }
6857
7711
 
7712
+ stage("boot probe (started)");
6858
7713
  // ── 2. Canonical typed USDA closure ──
6859
7714
  // There is no flattened package and no JSON law intermediate. Sparse typed source opinions plus
6860
7715
  // compiler-owned plan/framework layers are the package; composition is deferred to LawStage.
@@ -6865,7 +7720,7 @@ async function main(): Promise<void> {
6865
7720
  sha256: sha256HexUtf8(sharedRuntimeProfileJavascript),
6866
7721
  javascript: sharedRuntimeProfileJavascript,
6867
7722
  };
6868
- const lawClosure = emitLawClosure(
7723
+ let lawClosure = emitLawClosure(
6869
7724
  cfg.name,
6870
7725
  domainModules.map((module) => module.domain ?? module.name),
6871
7726
  runtimeDependencies,
@@ -6874,9 +7729,6 @@ async function main(): Promise<void> {
6874
7729
  routedPlanAssets,
6875
7730
  planRoutes
6876
7731
  );
6877
- // The composed USDA closure is the law. Generated clients, lifecycle rows and
6878
- // compatibility metadata all bind this identity, never the retired flattened envelope.
6879
- const domainHash = lawClosure.identity;
6880
7732
  const lawClosureAssetsPath = path.join(outDir, `${cfg.name}.law`);
6881
7733
  const lawUsdzPath = path.join(outDir, `${cfg.name}.law.usdz`);
6882
7734
  rmSync(path.join(outDir, `${cfg.name}.law-closure.usda`), { force: true });
@@ -6884,13 +7736,166 @@ async function main(): Promise<void> {
6884
7736
  recursive: true,
6885
7737
  force: true,
6886
7738
  });
6887
- rmSync(lawClosureAssetsPath, { recursive: true, force: true });
6888
- mkdirSync(lawClosureAssetsPath, { recursive: true });
6889
- for (const asset of lawClosure.assets) {
6890
- const assetPath = path.join(lawClosureAssetsPath, asset.logicalPath);
6891
- mkdirSync(path.dirname(assetPath), { recursive: true });
6892
- writeFileSync(assetPath, asset.bytes);
7739
+ const writeClosureAssets = (closure: EmittedLawClosure): void => {
7740
+ rmSync(lawClosureAssetsPath, { recursive: true, force: true });
7741
+ mkdirSync(lawClosureAssetsPath, { recursive: true });
7742
+ for (const asset of closure.assets) {
7743
+ const assetPath = path.join(lawClosureAssetsPath, asset.logicalPath);
7744
+ mkdirSync(path.dirname(assetPath), { recursive: true });
7745
+ writeFileSync(assetPath, asset.bytes);
7746
+ }
7747
+ };
7748
+ writeClosureAssets(lawClosure);
7749
+
7750
+ // Package standard OpenUSD finished goods once. Sparse USDA remains the authored, inspectable
7751
+ // source law. The full USDC layer is its reversible compiled form for one-time installation;
7752
+ // the smaller read projection is the population-masked form used for ordinary reopen. Both are
7753
+ // covered by the final closure inventory, so a host can never substitute an unbound cache.
7754
+ const usdcat = resolveUsdcat();
7755
+ let compiledReadProjection = false;
7756
+ let compiledCommandProjection = false;
7757
+ if (usdcat !== undefined) {
7758
+ const provisionalRootPath = path.join(
7759
+ lawClosureAssetsPath,
7760
+ lawClosure.rootLogicalPath
7761
+ );
7762
+ let composedLawText: string;
7763
+ try {
7764
+ composedLawText = await flattenOpenUsdToUsda({
7765
+ usdcat,
7766
+ sourcePath: provisionalRootPath,
7767
+ description: "the complete authenticated law stage",
7768
+ });
7769
+ } catch (error) {
7770
+ fail(error instanceof Error ? error.message : String(error));
7771
+ }
7772
+ // The composed stage feeds three independent usdcat pipelines — its own USDC encoding, and the
7773
+ // read and command projections (a masked flatten, then an encoding). Each step is a separate
7774
+ // stock-OpenUSD process with its own temporary files, so run the pipelines concurrently.
7775
+ const temporaryComposedLaw = path.join(
7776
+ outDir,
7777
+ ".nomos-composed-law.mask-source.usda"
7778
+ );
7779
+ let composedLawUsdc: Uint8Array;
7780
+ let readProjectionText: string;
7781
+ let readProjectionUsdc: Uint8Array;
7782
+ let commandProjectionText: string;
7783
+ let commandProjectionUsdc: Uint8Array;
7784
+ try {
7785
+ writeFileSync(temporaryComposedLaw, composedLawText);
7786
+ const project = async (
7787
+ populationMask: readonly string[],
7788
+ temporaryStem: string,
7789
+ description: string
7790
+ ): Promise<[string, Uint8Array]> => {
7791
+ const usda = await flattenOpenUsdToUsda({
7792
+ usdcat,
7793
+ sourcePath: temporaryComposedLaw,
7794
+ populationMask,
7795
+ description,
7796
+ });
7797
+ return [
7798
+ usda,
7799
+ await encodeOpenUsdUsdc({
7800
+ usdcat,
7801
+ usda,
7802
+ temporaryDirectory: outDir,
7803
+ temporaryStem,
7804
+ description,
7805
+ }),
7806
+ ];
7807
+ };
7808
+ [
7809
+ composedLawUsdc,
7810
+ [readProjectionText, readProjectionUsdc],
7811
+ [commandProjectionText, commandProjectionUsdc],
7812
+ ] = await Promise.all([
7813
+ encodeOpenUsdUsdc({
7814
+ usdcat,
7815
+ usda: composedLawText,
7816
+ temporaryDirectory: outDir,
7817
+ temporaryStem: "nomos-composed-law",
7818
+ description: "the complete authenticated law stage",
7819
+ }),
7820
+ project(
7821
+ [...readProjectionPaths],
7822
+ "nomos-read-projection",
7823
+ "the authenticated read projection"
7824
+ ),
7825
+ project(
7826
+ [...commandProjectionPaths],
7827
+ "nomos-command-projection",
7828
+ "the authenticated command projection"
7829
+ ),
7830
+ ]);
7831
+ } catch (error) {
7832
+ fail(error instanceof Error ? error.message : String(error));
7833
+ } finally {
7834
+ rmSync(temporaryComposedLaw, { force: true });
7835
+ }
7836
+ const composedLawCache: EmittedBinaryLawClosureAsset = {
7837
+ logicalPath: "runtime/composed-law.usdc",
7838
+ sha256: createHash("sha256").update(composedLawUsdc).digest("hex"),
7839
+ bytes: composedLawUsdc,
7840
+ };
7841
+ const readProjection: EmittedLawClosureAsset = {
7842
+ logicalPath: "runtime/read-projection.usda",
7843
+ sha256: sha256HexUtf8(readProjectionText),
7844
+ utf8: readProjectionText,
7845
+ };
7846
+ // Unlike the command acceleration view, the read projection is a complete nominal contract:
7847
+ // it must be able to encode every declared result without consulting the full package. Catch a
7848
+ // missing transitive value type at compile time rather than during an application's first read.
7849
+ assertUsdaRelationshipIntegrity([readProjection]);
7850
+ // USDA remains the canonical, inspectable derived layer. USDC is the OpenUSD-standard parsed
7851
+ // cache of those exact bytes, produced by the same stock tool during package compilation. Every
7852
+ // Nomos host executes the same WASM kernel containing mxpv/openusd, so this cache crosses no
7853
+ // language boundary and requires no second runtime implementation.
7854
+ const readProjectionCache: EmittedBinaryLawClosureAsset = {
7855
+ logicalPath: "runtime/read-projection.usdc",
7856
+ sha256: createHash("sha256").update(readProjectionUsdc).digest("hex"),
7857
+ bytes: readProjectionUsdc,
7858
+ };
7859
+ const commandProjection: EmittedLawClosureAsset = {
7860
+ logicalPath: "runtime/command-projection.usda",
7861
+ sha256: sha256HexUtf8(commandProjectionText),
7862
+ utf8: commandProjectionText,
7863
+ };
7864
+ const commandProjectionCache: EmittedBinaryLawClosureAsset = {
7865
+ logicalPath: "runtime/command-projection.usdc",
7866
+ sha256: createHash("sha256").update(commandProjectionUsdc).digest("hex"),
7867
+ bytes: commandProjectionUsdc,
7868
+ };
7869
+ lawClosure = emitLawClosure(
7870
+ cfg.name,
7871
+ domainModules.map((module) => module.domain ?? module.name),
7872
+ runtimeDependencies,
7873
+ v3DomainAssets,
7874
+ sharedRuntimeAsset,
7875
+ routedPlanAssets,
7876
+ planRoutes,
7877
+ [
7878
+ composedLawCache,
7879
+ readProjection,
7880
+ readProjectionCache,
7881
+ commandProjection,
7882
+ commandProjectionCache,
7883
+ ]
7884
+ );
7885
+ writeClosureAssets(lawClosure);
7886
+ compiledReadProjection = true;
7887
+ compiledCommandProjection = true;
7888
+ }
7889
+
7890
+ stage("law closure + usdcat projections");
7891
+ {
7892
+ const probeFailure = await bootProbeFailure;
7893
+ if (probeFailure !== undefined) fail(probeFailure);
6893
7894
  }
7895
+ stage("boot probe (verdict)");
7896
+ // The composed USDA closure is the law. Generated clients, lifecycle rows and
7897
+ // compatibility metadata all bind this identity, never the retired flattened envelope.
7898
+ const domainHash = lawClosure.identity;
6894
7899
  const rootAsset = lawClosure.assets.find(
6895
7900
  (asset) => asset.logicalPath === lawClosure.rootLogicalPath
6896
7901
  );
@@ -6903,6 +7908,33 @@ async function main(): Promise<void> {
6903
7908
  .map((asset) => [`layers/${asset.logicalPath}`, asset.bytes] as const),
6904
7909
  ]);
6905
7910
  writeFileSync(lawUsdzPath, lawUsdz);
7911
+ // The compiled law's identity on its own line, for release gates that need the hash and nothing else.
7912
+ writeFileSync(path.join(outDir, `${cfg.name}.law.sha256`), `${domainHash}\n`, "utf8");
7913
+ // D21: the same law, authored as per-domain units the kernel composes. Split from the closure just
7914
+ // emitted (its bytes and identity are untouched), previewed for exact coverage, and written as plain
7915
+ // trees: in custody each unit is a git tree, so git owns compression and deduplication.
7916
+ {
7917
+ const lawUnits = (() => {
7918
+ try {
7919
+ const units = splitLawUnits(lawClosure.assets, planRoutes);
7920
+ assertLawUnitsCoverPackage(units, lawClosure.assets, planRoutes);
7921
+ return units;
7922
+ } catch (error) {
7923
+ return fail(error instanceof Error ? error.message : String(error));
7924
+ }
7925
+ })();
7926
+ const unitsPath = path.join(outDir, `${cfg.name}.units`);
7927
+ rmSync(unitsPath, { recursive: true, force: true });
7928
+ for (const unit of lawUnits) {
7929
+ const unitPath = path.join(unitsPath, lawUnitDirectoryName(unit));
7930
+ for (const asset of unit.assets) {
7931
+ const assetPath = path.join(unitPath, asset.logicalPath);
7932
+ mkdirSync(path.dirname(assetPath), { recursive: true });
7933
+ writeFileSync(assetPath, asset.bytes);
7934
+ }
7935
+ writeFileSync(path.join(unitPath, LAW_UNIT_MANIFEST), unit.manifest);
7936
+ }
7937
+ }
6906
7938
  const layeredRootPath = path.join(
6907
7939
  lawClosureAssetsPath,
6908
7940
  lawClosure.rootLogicalPath
@@ -6911,7 +7943,6 @@ async function main(): Promise<void> {
6911
7943
  let usdcatFlattenVerified = false;
6912
7944
  let usdcatFlattenCached = false;
6913
7945
  const composedUsdIdentity = domainHash;
6914
- const usdcat = resolveUsdcat();
6915
7946
  if (usdcat === undefined) {
6916
7947
  if (requireUsdcat) {
6917
7948
  fail(
@@ -6970,6 +8001,7 @@ async function main(): Promise<void> {
6970
8001
  }
6971
8002
  }
6972
8003
 
8004
+ stage("usdz pack + usdcat load proof");
6973
8005
  // ── 2c. THE PUBLISHED OPENUSD PACKAGE ASSET ──
6974
8006
  // `rootPart` is the explicit package/application boundary from the OpenUSD composition ADR.
6975
8007
  // The asset references the REAL authored layer stack. It deliberately contains neither the
@@ -7011,7 +8043,7 @@ async function main(): Promise<void> {
7011
8043
  }
7012
8044
  }
7013
8045
 
7014
- // ── 3. typed per-domain identity (diagnostic/client compatibility metadata) ──
8046
+ // ── 3. typed per-domain identity (diagnostic/peer-office compatibility metadata) ──
7015
8047
  // A domain's identity is the digest of its canonical sparse USDA layer. Do not rebuild the retired
7016
8048
  // canonical JSON manifest merely to print or embed a second, competing identity.
7017
8049
  const identityHashes: Record<string, string> = {};
@@ -7023,13 +8055,9 @@ async function main(): Promise<void> {
7023
8055
  identityHashes[module.domain ?? module.name] = hash;
7024
8056
  }
7025
8057
 
7026
- // ── 3a. the CLIENT/LAW COMPATIBILITY MANIFEST (architecture/shape_compatibility.md).
7027
- // A DIAGNOSIS surface, NEVER a gate: the shape-compat arc (#72) established that clients
7028
- // fit workspaces by STRUCTURAL INTERFACE (stable-id subtyping), not by law-hash pin. This
7029
- // block is baked into both generated clients (TS + Dart) and mirrored into the deploy JSON
7030
- // so a connect() can classify silent / shape-compat / incompatible. FRONTEND-ONLY: it is
7031
- // emitted like the dart codegen hints — it does NOT enter the USD IR / package bytes /
7032
- // domainHash (the package `text` above is already hashed and does not move).
8058
+ // ── 3a. CLIENT/LAW compatibility is emitted solely as a USDA matrix from the
8059
+ // canonical compiled interface graph by each language generator. There is deliberately
8060
+ // no parallel JSON/hash manifest: the kernel compares that matrix with authenticated law.
7033
8061
  const dslVersion = ((): string => {
7034
8062
  try {
7035
8063
  return (
@@ -7040,12 +8068,6 @@ async function main(): Promise<void> {
7040
8068
  return "unknown";
7041
8069
  }
7042
8070
  })();
7043
- const compatDomains: NomosCompat["domains"] = {};
7044
- for (const m of presentationDomainModules) {
7045
- const key = m.domain ?? m.name;
7046
- const identityHash = identityHashes[key];
7047
- compatDomains[key] = identityHash === undefined ? {} : { identityHash };
7048
- }
7049
8071
  // RELEASE COHERENCE — typed OSGi Contract Namespace requirements derived from law shape.
7050
8072
  const kernelContracts = (() => {
7051
8073
  const probes: KernelContractProbeModule[] = domainModules.map((m) => ({
@@ -7058,14 +8080,6 @@ async function main(): Promise<void> {
7058
8080
  }));
7059
8081
  return requiredKernelContracts(probes);
7060
8082
  })();
7061
- const nomosCompat: NomosCompat = {
7062
- packageName: cfg.name,
7063
- lawHash: domainHash,
7064
- interfaceHash: sha256HexUtf8(canonicalJson(packageInterface)),
7065
- domains: compatDomains,
7066
- generator: { dsl: dslVersion, schema: 1 },
7067
- kernelContracts,
7068
- };
7069
8083
 
7070
8084
  // ── 3b. NO OPENAPI SIDECAR. OpenAPI remains the sanctioned standard for describing this package's
7071
8085
  // API — but it is SERVED, not stored: the kernel derives it from the installed typed USDA IR
@@ -7090,78 +8104,26 @@ async function main(): Promise<void> {
7090
8104
  identityHash: identityHashes[m.domain ?? m.name],
7091
8105
  })),
7092
8106
  };
7093
- // ── 4b. the GENERATED PROOF — a runnable, domain-shaped e2e synthesized from the
7094
- // law itself (codegen_proof.ts): reshape the law, recompile, the proof
7095
- // reshapes itself — no test rewriting. NEVER FATAL: a domain the
7096
- // synthesizer can't sample yet still compiles — the skip names its remedy.
7097
- let proofPath: string | undefined;
7098
- let proofSkip: string | undefined;
7099
- let proofDomains: string[] = [];
8107
+ // ── 4b. BIRTHS DX — the per-domain legs index (codegen_proof.ts) is the compiler's audit of every
8108
+ // creating directive: a `.births()` directive that can't be proven says WHY here — never a
8109
+ // silent skip. (The runnable `<name>.proof.mts` this once emitted is gone with its proof cloud.)
7100
8110
  let birthWarnings: string[] = [];
7101
8111
  let proofNotes: string[] = [];
7102
8112
  try {
7103
- const proofSrc = generateTsProof(presentationDomainModules, {
7104
- packageName: cfg.name,
7105
- domainHash,
7106
- interface: packageInterface,
7107
- ...(cfg.application?.maxChildWorkspacesPerUser === undefined
7108
- ? {}
7109
- : {
7110
- applicationMaxChildWorkspacesPerUser:
7111
- cfg.application.maxChildWorkspacesPerUser,
7112
- }),
7113
- });
7114
- proofPath = path.join(outDir, `${cfg.name}.proof.mts`);
7115
- writeFileSync(proofPath, proofSrc, "utf8");
7116
- // THE PER-DOMAIN LEGS INDEX: every domain with a creating write, so
7117
- // `githolon proof --domain <key>` proves ANY of them offline (the .mts
7118
- // proves the primary one live). A multi-domain package no longer locks the
7119
- // proof to whichever domain happens to be first.
7120
- const legsIndex = proofLegsIndex(
7121
- presentationDomainModules,
7122
- packageInterface
7123
- );
7124
- if (legsIndex !== null) {
7125
- writeFileSync(
7126
- path.join(outDir, `${cfg.name}.proof-legs.json`),
7127
- JSON.stringify(legsIndex, null, 2) + "\n",
7128
- "utf8"
7129
- );
7130
- proofDomains = Object.keys(legsIndex.domains);
7131
- birthWarnings = Object.values(legsIndex.domains).flatMap(
8113
+ const idx = proofLegsIndex(presentationDomainModules, packageInterface);
8114
+ if (idx !== null) {
8115
+ birthWarnings = Object.values(idx.domains).flatMap(
7132
8116
  (l) => l.birthDiagnostics ?? []
7133
8117
  );
7134
- // TAXONOMY AUDIT: every creating directive not covered by a leg is NAMED-WHY (exact-path synthesis
7135
- // failure / attestation lane / gated-create authority gap) — never a silent drop.
7136
- proofNotes = Object.values(legsIndex.domains).flatMap(
8118
+ proofNotes = Object.values(idx.domains).flatMap(
7137
8119
  (l) => l.proofDiagnostics ?? []
7138
8120
  );
7139
8121
  }
7140
- } catch (e) {
7141
- proofSkip = (e as Error).message;
7142
- // A skipped generation must not leave yesterday's client story in build/. Otherwise editors and CI
7143
- // typecheck a stale proof which no longer describes the compiled law — worse than having no proof.
7144
- rmSync(path.join(outDir, `${cfg.name}.proof.mts`), { force: true });
7145
- rmSync(path.join(outDir, `${cfg.name}.proof-legs.json`), { force: true });
7146
- }
7147
- // BIRTHS DX: a `.births()` directive that can't be (or can only shakily be) proven says WHY here —
7148
- // never a silent skip, never an opaque runtime `born=[]` first contact. When the .mts generation
7149
- // itself failed, still derive the per-domain diagnostics (best-effort).
7150
- if (proofSkip !== undefined && birthWarnings.length === 0) {
7151
- try {
7152
- const idx = proofLegsIndex(presentationDomainModules, packageInterface);
7153
- if (idx !== null) {
7154
- birthWarnings = Object.values(idx.domains).flatMap(
7155
- (l) => l.birthDiagnostics ?? []
7156
- );
7157
- proofNotes = Object.values(idx.domains).flatMap(
7158
- (l) => l.proofDiagnostics ?? []
7159
- );
7160
- }
7161
- } catch {
7162
- /* diagnostics are best-effort */
7163
- }
8122
+ } catch {
8123
+ /* diagnostics are best-effort */
7164
8124
  }
8125
+ rmSync(path.join(outDir, `${cfg.name}.proof.mts`), { force: true });
8126
+ rmSync(path.join(outDir, `${cfg.name}.proof-legs.json`), { force: true });
7165
8127
 
7166
8128
  const summaryTxt = [
7167
8129
  `${cfg.name} — compiled Nomos domain package`,
@@ -7172,6 +8134,9 @@ async function main(): Promise<void> {
7172
8134
  `canonical law: ${path.basename(
7173
8135
  lawClosureAssetsPath
7174
8136
  )}/ # exact logically named, human-readable USDA`,
8137
+ `client compatibility: ${path.basename(
8138
+ compatibilityUsdaPath
8139
+ )} # opaque USDA requirement; the kernel compares it with installed law`,
7175
8140
  ...summary.domains.flatMap((d) => [
7176
8141
  `domain '${d.domain}'${
7177
8142
  d.identityHash
@@ -7190,14 +8155,11 @@ async function main(): Promise<void> {
7190
8155
  packageAssetPath
7191
8156
  )} — the real USD package root consumed by application composition`
7192
8157
  : `package asset: NOT EMITTED — declare rootPart: { name, kind: "component" | "assembly" }`,
7193
- `typed application: ${cfg.name}.client.mjs — open${cfg.name
8158
+ `typed application: ${cfg.name}.reception.mjs — bind${cfg.name
7194
8159
  .split(/[^A-Za-z0-9]+/)
7195
8160
  .filter(Boolean)
7196
8161
  .map((part) => part[0]!.toUpperCase() + part.slice(1))
7197
- .join("")}Application({ cloud, signIn })`,
7198
- proofPath !== undefined
7199
- ? `generated proof: ${cfg.name}.proof.mts — fresh: githolon proof; existing custody: githolon proof --upgrade`
7200
- : `generated proof: SKIPPED — ${proofSkip}`,
8162
+ .join("")}Application(peerOfficeBinding)`,
7201
8163
  ...(priorLock === undefined
7202
8164
  ? []
7203
8165
  : [
@@ -7221,6 +8183,10 @@ async function main(): Promise<void> {
7221
8183
  domainHash,
7222
8184
  artifacts: [
7223
8185
  { name: path.basename(lawUsdzPath) },
8186
+ {
8187
+ name: path.basename(compatibilityUsdaPath),
8188
+ sha256: createHash("sha256").update(compatibilityUsda).digest("hex"),
8189
+ },
7224
8190
  ...(packageAssetPath === undefined
7225
8191
  ? []
7226
8192
  : [{ name: path.basename(packageAssetPath) }]),
@@ -7245,20 +8211,15 @@ async function main(): Promise<void> {
7245
8211
  "utf8"
7246
8212
  );
7247
8213
 
8214
+ stage("package asset + identity + summary");
7248
8215
  // ── 5. the typed TS client (#10 — the web sibling of codegen_dart), with the
7249
8216
  // deployed law's content hash baked in (emitted AFTER the package is hashed) ──
7250
- const clientPath = path.join(outDir, `${cfg.name}.client.mjs`);
7251
- const clientDeclarationPath = path.join(outDir, `${cfg.name}.client.d.mts`);
7252
- const headlessClientPath = path.join(outDir, `${cfg.name}.headless.mjs`);
7253
- const headlessClientDeclarationPath = path.join(
7254
- outDir,
7255
- `${cfg.name}.headless.d.mts`
7256
- );
7257
- const operatorPath = path.join(outDir, `${cfg.name}.operator.mjs`);
7258
- const operatorDeclarationPath = path.join(
7259
- outDir,
7260
- `${cfg.name}.operator.d.mts`
7261
- );
8217
+ // ONE TypeScript Reception per application: the typed surface tenant code binds
8218
+ // (`receive<Name>Application`) and, in the same file, the holon-level factories a code-mode or
8219
+ // unattended peer binds to any NomosHolon. It was two artifacts (".client" + ".headless") that
8220
+ // differed only in whether those factories were exported.
8221
+ const clientPath = path.join(outDir, `${cfg.name}.reception.mjs`);
8222
+ const clientDeclarationPath = path.join(outDir, `${cfg.name}.reception.d.mts`);
7262
8223
  const frameworkOperatorPath = path.join(outDir, `${cfg.name}.framework.mjs`);
7263
8224
  const frameworkOperatorDeclarationPath = path.join(
7264
8225
  outDir,
@@ -7273,7 +8234,7 @@ async function main(): Promise<void> {
7273
8234
  domainHash,
7274
8235
  lawAssetName: `${cfg.name}.law.usdz`,
7275
8236
  interface: packageInterface,
7276
- compat: nomosCompat,
8237
+ kernelContracts,
7277
8238
  homeDomain: resolveApplicationTopology(presentationDomainModules)
7278
8239
  .homeDomain,
7279
8240
  callUsda: [...typedLawGraphs].map(([name, graph]) => {
@@ -7283,84 +8244,94 @@ async function main(): Promise<void> {
7283
8244
  return { graph, paths };
7284
8245
  }),
7285
8246
  } as const;
7286
- const clientSource = generateTsClient(
7287
- presentationDomainModules,
7288
- clientOptions
8247
+ // Both language clients are generated from this one composed application
8248
+ // schema. The TypeScript binding is bundled into the application client;
8249
+ // Dart's generated binding is emitted into its package below.
8250
+ await tsOperationProtoPrewarm;
8251
+ emitTsOperationProto(
8252
+ cfgDir,
8253
+ outDir,
8254
+ lawUsdzPath,
8255
+ [
8256
+ ...new Map(
8257
+ [...closedDomainModules, ...presentationDomainModules].map(
8258
+ (module) => [module.name, module] as const
8259
+ )
8260
+ ).values(),
8261
+ ],
8262
+ typedLawGraphs,
8263
+ typedLawPathsByName
7289
8264
  );
7290
- const headlessClientSource = generateTsClient(presentationDomainModules, {
8265
+ const clientSource = generateTsClient(presentationDomainModules, {
7291
8266
  ...clientOptions,
7292
8267
  exportHolonFactory: true,
7293
8268
  });
7294
- const operatorSource = generateTsLifecycleOperator(clientOptions.callUsda);
7295
8269
  const frameworkOperatorSource = generateTsClient(presentationDomainModules, {
7296
8270
  ...clientOptions,
7297
8271
  frameworkOperatorOnly: true,
7298
8272
  });
7299
- const runtimePath = path.join(
7300
- DSL_DIR,
7301
- "runtime",
7302
- "generated-application-host.js"
8273
+ // Generated bundles consume the compiler's declared Protobuf runtime. Do not rely on
8274
+ // npm incidentally hoisting @bufbuild/protobuf into the tenant package: linked packages,
8275
+ // pnpm and isolated installs are all entitled to use a different node_modules layout.
8276
+ const compilerRequire = createRequire(import.meta.url);
8277
+ const protobufEntry = compilerRequire.resolve("@bufbuild/protobuf");
8278
+ const protobufCodegenEntry = compilerRequire.resolve(
8279
+ "@bufbuild/protobuf/codegenv2"
8280
+ );
8281
+ const protobufTypes = protobufEntry.replace(/\.js$/, ".d.ts");
8282
+ const protobufCodegenTypes = protobufCodegenEntry.replace(/\.js$/, ".d.ts");
8283
+ const nodeModulesMarker = `${path.sep}node_modules${path.sep}`;
8284
+ const nodeModulesIndex = protobufEntry.lastIndexOf(nodeModulesMarker);
8285
+ if (nodeModulesIndex < 0) {
8286
+ fail(`@bufbuild/protobuf resolved outside node_modules: ${protobufEntry}`);
8287
+ }
8288
+ const compilerNodeModules = protobufEntry.slice(
8289
+ 0,
8290
+ nodeModulesIndex + nodeModulesMarker.length - 1
7303
8291
  );
7304
- if (!existsSync(runtimePath)) {
8292
+ const protobufRuntimePlugin: import("esbuild").Plugin = {
8293
+ name: "nomos-compiler-protobuf-runtime",
8294
+ setup(build) {
8295
+ build.onResolve({ filter: /^@bufbuild\/protobuf(?:\/.*)?$/ }, (args) => ({
8296
+ path: compilerRequire.resolve(args.path),
8297
+ }));
8298
+ },
8299
+ };
8300
+ let runtimeTypesPath: string;
8301
+ try {
8302
+ runtimeTypesPath = path.join(
8303
+ path.dirname(
8304
+ createRequire(import.meta.url).resolve(
8305
+ "@githolon/runtime/package.json"
8306
+ )
8307
+ ),
8308
+ "src",
8309
+ "application_reception.ts"
8310
+ );
8311
+ } catch {
7305
8312
  fail(
7306
- "the compiler-private generated application runtime is missing — reinstall @githolon/dsl"
8313
+ "generated TypeScript applications require @githolon/runtime so they can link the shared Peer Office runtime"
7307
8314
  );
7308
8315
  }
7309
8316
  // Validate the generated ESM clients as ESM even when the tenant project has no package.json yet.
7310
8317
  // NodeNext classifies a plain `.ts` file from the nearest package boundary, so a freshly scaffolded
7311
- // or fixture-only project would otherwise be treated as CommonJS and reject the intentional
7312
- // `import.meta.url` used to locate its adjacent law asset. The compiler options below describe the
8318
+ // or fixture-only project would otherwise be treated as CommonJS. The compiler options below describe the
7313
8319
  // generated `.mjs` runtime contract directly instead of inheriting unrelated tenant metadata.
7314
8320
  const temporaryClientPath = path.join(
7315
8321
  outDir,
7316
- `.${cfg.name}.client.source.ts`
7317
- );
7318
- const temporaryHeadlessClientPath = path.join(
7319
- outDir,
7320
- `.${cfg.name}.headless.source.ts`
7321
- );
7322
- const temporaryRuntimeTypesPath = path.join(
7323
- outDir,
7324
- `.${cfg.name}.application-runtime.d.ts`
8322
+ `.${cfg.name}.reception.source.ts`
7325
8323
  );
7326
8324
  const temporaryDeclarationPath = temporaryClientPath.replace(
7327
8325
  /\.ts$/,
7328
8326
  ".d.ts"
7329
8327
  );
7330
- const temporaryHeadlessDeclarationPath = temporaryHeadlessClientPath.replace(
7331
- /\.ts$/,
7332
- ".d.ts"
7333
- );
7334
- const runtimeTypesSource = [
7335
- `export declare function nomosInternalOpenGeneratedApplication<T extends object>(`,
7336
- ` options: { cloud: string; application: string; signIn: (request?: { readonly deviceChallenge?: string }) => Promise<string>; onPerformance?: (sample: { readonly operation: "open" | "offer" | "birth" | "read" | "sync" | "maintenance"; readonly name: string; readonly durationMs: number; readonly domain?: string; readonly directive?: string }) => void; approveModel?: (decision: { readonly summary: string; readonly review: { checkedActions: number; candidateActions: number; sameOutcome: boolean | null; differences: { kind: "rules" | "outcome" | "evidence"; area?: string; action?: string; summary: string }[] } }) => Promise<{ readonly because: string }>; lawHash: string; readyCommands?: readonly { readonly domain: string; readonly packageHash: string; readonly directiveId: string }[]; frameworkCalls: unknown },`,
7337
- ` bind: (holon: unknown, user: { readonly identityId: string; readonly identityAddress: string }) => T,`,
7338
- `): Promise<T & {`,
7339
- ` currentStatus(): Promise<any>;`,
7340
- ` watchStatus(listener: (status: any) => void): () => void;`,
7341
- `}>;`,
7342
- `export declare function nomosInternalOpenGeneratedChildApplication<T extends object>(`,
7343
- ` parent: unknown,`,
7344
- ` workspace: string,`,
7345
- ` bind: (holon: unknown) => T,`,
7346
- `): Promise<T>;`,
7347
- `export declare function nomosInternalCreateInvitation(`,
7348
- ` holon: unknown,`,
7349
- `): Promise<{ readonly address: string; readonly token: string }>;`,
7350
- `export declare function nomosInternalRedeemInvitation(`,
7351
- ` holon: unknown,`,
7352
- ` link: string,`,
7353
- ` policies: readonly { readonly domain: string; readonly domainHash: string; readonly query: string; readonly redeem: string; readonly personField: string; readonly roleField: string; readonly scopeField: string }[],`,
7354
- ` frameworkCalls: unknown,`,
7355
- `): Promise<void>;`,
7356
- ``,
7357
- ].join("\n");
7358
8328
  // Declaration generation used to ask TypeScript to rebuild and reduce the same 800 KiB public type graph
7359
8329
  // on every law compile, even when an implementation-only plan edit left that graph byte-identical. Cache
7360
8330
  // the result by the exact generated source, runtime declaration boundary, TypeScript version and pruning
7361
8331
  // algorithm. The installed law hash is normalized because it changes runtime compatibility identity but
7362
8332
  // has type `string` in every public declaration. A typed USDA proof binds the cached declaration bytes;
7363
8333
  // there is no JSON cache manifest and no cached file can become runtime or custody authority.
8334
+ stage("ts client source gen");
7364
8335
  const declarationProofCodec = (await import(
7365
8336
  "../compile_proof.mjs" as string
7366
8337
  )) as {
@@ -7376,14 +8347,7 @@ async function main(): Promise<void> {
7376
8347
  .update(`${declarationProofFormat}\0`)
7377
8348
  .update(normalizedClientSource)
7378
8349
  .update("\0")
7379
- .update(
7380
- headlessClientSource.replaceAll(
7381
- domainHash,
7382
- "<nomos-composed-law-identity>"
7383
- )
7384
- )
7385
- .update("\0")
7386
- .update(runtimeTypesSource)
8350
+ .update(readFileSync(runtimeTypesPath))
7387
8351
  .update("\0")
7388
8352
  .update(ts.version)
7389
8353
  .update("\0")
@@ -7398,22 +8362,14 @@ async function main(): Promise<void> {
7398
8362
  );
7399
8363
  const declarationCachePath = path.join(
7400
8364
  declarationCacheDir,
7401
- `${declarationInputIdentity}.client.d.mts`
7402
- );
7403
- const headlessDeclarationCachePath = path.join(
7404
- declarationCacheDir,
7405
- `${declarationInputIdentity}.headless.d.mts`
8365
+ `${declarationInputIdentity}.reception.d.mts`
7406
8366
  );
7407
8367
  const declarationCacheProofPath = path.join(
7408
8368
  declarationCacheDir,
7409
- `${declarationInputIdentity}.client.proof.usda`
7410
- );
7411
- const headlessDeclarationCacheProofPath = path.join(
7412
- declarationCacheDir,
7413
- `${declarationInputIdentity}.headless.proof.usda`
8369
+ `${declarationInputIdentity}.reception.proof.usda`
7414
8370
  );
7415
8371
  const declarationProofKey = (
7416
- artifact: "client" | "headless",
8372
+ artifact: "reception",
7417
8373
  bytes: Buffer
7418
8374
  ): string =>
7419
8375
  createHash("sha256")
@@ -7425,29 +8381,18 @@ async function main(): Promise<void> {
7425
8381
  const cachedDeclaration = existsSync(declarationCachePath)
7426
8382
  ? readFileSync(declarationCachePath)
7427
8383
  : undefined;
7428
- const cachedHeadlessDeclaration = existsSync(headlessDeclarationCachePath)
7429
- ? readFileSync(headlessDeclarationCachePath)
7430
- : undefined;
7431
8384
  const declarationCacheHit =
7432
8385
  cachedDeclaration !== undefined &&
7433
- cachedHeadlessDeclaration !== undefined &&
7434
8386
  declarationProofCodec.readKeyProofUsda(
7435
8387
  declarationCacheProofPath,
7436
8388
  declarationProofFormat
7437
- ) === declarationProofKey("client", cachedDeclaration) &&
7438
- declarationProofCodec.readKeyProofUsda(
7439
- headlessDeclarationCacheProofPath,
7440
- declarationProofFormat
7441
- ) === declarationProofKey("headless", cachedHeadlessDeclaration);
8389
+ ) === declarationProofKey("reception", cachedDeclaration);
7442
8390
  if (declarationCacheHit) {
7443
8391
  copyFileSync(declarationCachePath, clientDeclarationPath);
7444
- copyFileSync(headlessDeclarationCachePath, headlessClientDeclarationPath);
7445
8392
  }
7446
8393
  try {
7447
8394
  if (!declarationCacheHit) {
7448
8395
  writeFileSync(temporaryClientPath, clientSource, "utf8");
7449
- writeFileSync(temporaryHeadlessClientPath, headlessClientSource, "utf8");
7450
- writeFileSync(temporaryRuntimeTypesPath, runtimeTypesSource, "utf8");
7451
8396
  const typescriptEntry = createRequire(import.meta.url).resolve(
7452
8397
  "typescript/lib/typescript.js"
7453
8398
  );
@@ -7455,11 +8400,7 @@ async function main(): Promise<void> {
7455
8400
  pathToFileURL(typescriptEntry).href
7456
8401
  )) as typeof import("typescript");
7457
8402
  const program = ts.createProgram(
7458
- [
7459
- temporaryClientPath,
7460
- temporaryHeadlessClientPath,
7461
- temporaryRuntimeTypesPath,
7462
- ],
8403
+ [temporaryClientPath],
7463
8404
  {
7464
8405
  target: ts.ScriptTarget.ES2022,
7465
8406
  module: ts.ModuleKind.ESNext,
@@ -7472,14 +8413,28 @@ async function main(): Promise<void> {
7472
8413
  allowImportingTsExtensions: true,
7473
8414
  baseUrl: outDir,
7474
8415
  paths: {
7475
- "@githolon/client/_generated_application_host_7c1f5e": [
7476
- temporaryRuntimeTypesPath,
7477
- ],
8416
+ "@githolon/runtime/application": [runtimeTypesPath],
8417
+ "@bufbuild/protobuf": [protobufTypes],
8418
+ "@bufbuild/protobuf/codegenv2": [protobufCodegenTypes],
7478
8419
  },
7479
8420
  outDir,
7480
8421
  }
7481
8422
  );
7482
- const emit = program.emit();
8423
+ // The runtime contract deliberately imports its generated framework types from the client package.
8424
+ // Those are dependencies, not compiler output. Capture only declarations whose source is one of the
8425
+ // two generated application roots; otherwise TypeScript mirrors the client package's source tree under
8426
+ // the tenant build directory and makes a second apparent type system.
8427
+ const emit = program.emit(
8428
+ undefined,
8429
+ (_fileName, data, _writeByteOrderMark, _onError, sourceFiles) => {
8430
+ const sourceNames = (sourceFiles ?? []).map((source) =>
8431
+ path.resolve(source.fileName)
8432
+ );
8433
+ if (sourceNames.includes(path.resolve(temporaryClientPath))) {
8434
+ writeFileSync(temporaryDeclarationPath, data, "utf8");
8435
+ }
8436
+ }
8437
+ );
7483
8438
  const diagnostics = [
7484
8439
  ...ts.getPreEmitDiagnostics(program),
7485
8440
  ...emit.diagnostics,
@@ -7487,8 +8442,7 @@ async function main(): Promise<void> {
7487
8442
  if (
7488
8443
  diagnostics.length > 0 ||
7489
8444
  emit.emitSkipped ||
7490
- !existsSync(temporaryDeclarationPath) ||
7491
- !existsSync(temporaryHeadlessDeclarationPath)
8445
+ !existsSync(temporaryDeclarationPath)
7492
8446
  ) {
7493
8447
  const detail = diagnostics
7494
8448
  .map((diagnostic) => {
@@ -7706,38 +8660,18 @@ async function main(): Promise<void> {
7706
8660
  ].join("\n");
7707
8661
  writeFileSync(temporaryDeclarationPath, declarationSource, "utf8");
7708
8662
  renameSync(temporaryDeclarationPath, clientDeclarationPath);
7709
- renameSync(
7710
- temporaryHeadlessDeclarationPath,
7711
- headlessClientDeclarationPath
7712
- );
7713
8663
  mkdirSync(declarationCacheDir, { recursive: true });
7714
8664
  const declarationCacheTemporaryPath = `${declarationCachePath}.tmp-${process.pid}`;
7715
- const headlessDeclarationCacheTemporaryPath = `${headlessDeclarationCachePath}.tmp-${process.pid}`;
7716
8665
  copyFileSync(clientDeclarationPath, declarationCacheTemporaryPath);
7717
- copyFileSync(
7718
- headlessClientDeclarationPath,
7719
- headlessDeclarationCacheTemporaryPath
7720
- );
7721
8666
  renameSync(declarationCacheTemporaryPath, declarationCachePath);
7722
- renameSync(
7723
- headlessDeclarationCacheTemporaryPath,
7724
- headlessDeclarationCachePath
7725
- );
7726
8667
  declarationProofCodec.writeKeyProofUsda(
7727
8668
  declarationCacheProofPath,
7728
8669
  declarationProofFormat,
7729
- declarationProofKey("client", readFileSync(declarationCachePath))
7730
- );
7731
- declarationProofCodec.writeKeyProofUsda(
7732
- headlessDeclarationCacheProofPath,
7733
- declarationProofFormat,
7734
- declarationProofKey(
7735
- "headless",
7736
- readFileSync(headlessDeclarationCachePath)
7737
- )
8670
+ declarationProofKey("reception", readFileSync(declarationCachePath))
7738
8671
  );
7739
8672
  }
7740
- await esbuildMod.build({
8673
+ stage("ts declarations");
8674
+ const clientBundle = await esbuildMod.build({
7741
8675
  stdin: {
7742
8676
  contents: clientSource,
7743
8677
  loader: "ts",
@@ -7745,6 +8679,8 @@ async function main(): Promise<void> {
7745
8679
  sourcefile: `${cfg.name}.client.ts`,
7746
8680
  },
7747
8681
  outfile: clientPath,
8682
+ write: false,
8683
+ metafile: true,
7748
8684
  bundle: true,
7749
8685
  packages: "bundle",
7750
8686
  platform: "node",
@@ -7752,132 +8688,42 @@ async function main(): Promise<void> {
7752
8688
  target: "node20",
7753
8689
  legalComments: "none",
7754
8690
  logLevel: "silent",
7755
- plugins: [
7756
- {
7757
- name: "nomos-generated-application-runtime",
7758
- setup(build) {
7759
- build.onResolve(
7760
- {
7761
- filter:
7762
- /^@githolon\/client\/_generated_application_host_7c1f5e$/,
7763
- },
7764
- () => ({ path: runtimePath })
7765
- );
7766
- },
7767
- },
7768
- ],
8691
+ nodePaths: [compilerNodeModules],
8692
+ plugins: [protobufRuntimePlugin],
8693
+ external: [APPLICATION_CONTRACT_MODULE],
8694
+ });
8695
+ assertGeneratedClientBundleBoundary(clientBundle.metafile, {
8696
+ workingDirectory: process.cwd(),
8697
+ generatedEntry: path.join(outDir, `${cfg.name}.client.ts`),
8698
+ operationCodec: path.join(outDir, "operation_payloads_pb.ts"),
8699
+ protobufPackageRoot: protobufPackageRoot(protobufEntry),
7769
8700
  });
7770
8701
  const bundled = canonicalizeGeneratedClientJavascript(
7771
- readFileSync(clientPath, "utf8")
8702
+ clientBundle.outputFiles[0]!.text
7772
8703
  );
7773
8704
  writeFileSync(clientPath, bundled, "utf8");
7774
8705
  if (
7775
- /^import\s+.*["']@githolon\/client/m.test(bundled) ||
7776
- /export\s*\{[^}]*nomosInternalOpenGeneratedApplication/s.test(bundled)
8706
+ !/^import\s+.*["']@githolon\/runtime\/application["']/m.test(bundled)
7777
8707
  ) {
7778
8708
  fail(
7779
- "generated TypeScript application did not seal its private runtime binding"
8709
+ "generated TypeScript application did not link the shared Peer Office runtime"
7780
8710
  );
7781
8711
  }
7782
- await esbuildMod.build({
7783
- stdin: {
7784
- contents: headlessClientSource,
7785
- loader: "ts",
7786
- resolveDir: outDir,
7787
- sourcefile: `${cfg.name}.headless.ts`,
7788
- },
7789
- outfile: headlessClientPath,
7790
- bundle: true,
7791
- packages: "bundle",
7792
- platform: "node",
7793
- format: "esm",
7794
- target: "node20",
7795
- legalComments: "none",
7796
- logLevel: "silent",
7797
- plugins: [
7798
- {
7799
- name: "nomos-generated-headless-runtime",
7800
- setup(build) {
7801
- build.onResolve(
7802
- {
7803
- filter:
7804
- /^@githolon\/client\/_generated_application_host_7c1f5e$/,
7805
- },
7806
- () => ({ path: runtimePath })
7807
- );
7808
- },
7809
- },
7810
- ],
7811
- });
7812
- const headlessBundled = canonicalizeGeneratedClientJavascript(
7813
- readFileSync(headlessClientPath, "utf8")
7814
- );
7815
- writeFileSync(headlessClientPath, headlessBundled, "utf8");
7816
8712
  if (
7817
8713
  !new RegExp(
7818
8714
  `export\\s*\\{[^}]*${tsClientFactoryName(
7819
8715
  resolveApplicationTopology(presentationDomainModules).homeDomain
7820
8716
  )}`,
7821
8717
  "s"
7822
- ).test(headlessBundled)
8718
+ ).test(bundled)
7823
8719
  ) {
7824
- fail(
7825
- "generated headless application omitted its structural holon factory"
7826
- );
7827
- }
7828
- if (operatorSource !== undefined) {
7829
- const operatorDeclaration = ts.transpileDeclaration(operatorSource, {
7830
- fileName: `${cfg.name}.operator.ts`,
7831
- compilerOptions: {
7832
- target: ts.ScriptTarget.ES2022,
7833
- module: ts.ModuleKind.ESNext,
7834
- strict: true,
7835
- },
7836
- reportDiagnostics: true,
7837
- });
7838
- const operatorErrors = (operatorDeclaration.diagnostics ?? []).filter(
7839
- (diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error
7840
- );
7841
- if (operatorErrors.length > 0) {
7842
- fail(
7843
- `generated TypeScript lifecycle operator declarations are invalid — this is a Nomos codegen bug:\n${operatorErrors
7844
- .map((diagnostic) =>
7845
- ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n")
7846
- )
7847
- .join("\n")}`
7848
- );
7849
- }
7850
- writeFileSync(
7851
- operatorDeclarationPath,
7852
- operatorDeclaration.outputText,
7853
- "utf8"
7854
- );
7855
- await esbuildMod.build({
7856
- stdin: {
7857
- contents: operatorSource,
7858
- loader: "ts",
7859
- resolveDir: outDir,
7860
- sourcefile: `${cfg.name}.operator.ts`,
7861
- },
7862
- outfile: operatorPath,
7863
- bundle: true,
7864
- platform: "neutral",
7865
- format: "esm",
7866
- target: "es2022",
7867
- legalComments: "none",
7868
- logLevel: "silent",
7869
- });
7870
- writeFileSync(
7871
- operatorPath,
7872
- canonicalizeGeneratedClientJavascript(
7873
- readFileSync(operatorPath, "utf8")
7874
- ),
7875
- "utf8"
7876
- );
7877
- } else {
7878
- rmSync(operatorPath, { force: true });
7879
- rmSync(operatorDeclarationPath, { force: true });
8720
+ fail("generated Reception omitted its structural holon factory");
7880
8721
  }
8722
+ stage("client bundle");
8723
+ // Lifecycle belongs to Peer Office. Remove outputs from older compiler
8724
+ // versions instead of generating a tenant-specific operator runtime.
8725
+ rmSync(path.join(outDir, `${cfg.name}.operator.mjs`), { force: true });
8726
+ rmSync(path.join(outDir, `${cfg.name}.operator.d.mts`), { force: true });
7881
8727
  const frameworkOperatorDeclaration = ts.transpileDeclaration(
7882
8728
  frameworkOperatorSource,
7883
8729
  {
@@ -7910,7 +8756,7 @@ async function main(): Promise<void> {
7910
8756
  "utf8"
7911
8757
  );
7912
8758
  writeFileSync(frameworkOperatorSourcePath, frameworkOperatorSource, "utf8");
7913
- await esbuildMod.build({
8759
+ const frameworkBundle = await esbuildMod.build({
7914
8760
  stdin: {
7915
8761
  contents: frameworkOperatorSource,
7916
8762
  loader: "ts",
@@ -7918,17 +8764,28 @@ async function main(): Promise<void> {
7918
8764
  sourcefile: `${cfg.name}.framework.ts`,
7919
8765
  },
7920
8766
  outfile: frameworkOperatorPath,
8767
+ write: false,
8768
+ metafile: true,
7921
8769
  bundle: true,
7922
8770
  platform: "neutral",
7923
8771
  format: "esm",
7924
8772
  target: "es2022",
7925
8773
  legalComments: "none",
7926
8774
  logLevel: "silent",
8775
+ nodePaths: [compilerNodeModules],
8776
+ plugins: [protobufRuntimePlugin],
8777
+ external: [APPLICATION_CONTRACT_MODULE],
8778
+ });
8779
+ assertGeneratedClientBundleBoundary(frameworkBundle.metafile, {
8780
+ workingDirectory: process.cwd(),
8781
+ generatedEntry: path.join(outDir, `${cfg.name}.framework.ts`),
8782
+ operationCodec: path.join(outDir, "operation_payloads_pb.ts"),
8783
+ protobufPackageRoot: protobufPackageRoot(protobufEntry),
7927
8784
  });
7928
8785
  writeFileSync(
7929
8786
  frameworkOperatorPath,
7930
8787
  canonicalizeGeneratedClientJavascript(
7931
- readFileSync(frameworkOperatorPath, "utf8")
8788
+ frameworkBundle.outputFiles[0]!.text
7932
8789
  ),
7933
8790
  "utf8"
7934
8791
  );
@@ -7940,62 +8797,18 @@ async function main(): Promise<void> {
7940
8797
  );
7941
8798
  } finally {
7942
8799
  rmSync(temporaryClientPath, { force: true });
7943
- rmSync(temporaryHeadlessClientPath, { force: true });
7944
- rmSync(temporaryRuntimeTypesPath, { force: true });
7945
8800
  rmSync(temporaryDeclarationPath, { force: true });
7946
- rmSync(temporaryHeadlessDeclarationPath, { force: true });
7947
- rmSync(path.join(outDir, `${cfg.name}.client.ts`), { force: true });
8801
+ // Outputs of earlier compiler versions: the two-artifact split and its source sidecar.
8802
+ for (const stale of [".client.ts", ".client.mjs", ".client.d.mts", ".headless.mjs", ".headless.d.mts"]) {
8803
+ rmSync(path.join(outDir, `${cfg.name}${stale}`), { force: true });
8804
+ }
7948
8805
  }
7949
8806
 
8807
+ stage("framework bundle");
7950
8808
  // ── 6. the typed Dart frontend (opt-in; the Flutter sibling of step 5, emitted
7951
8809
  // after the package hash like the TS client) ──
7952
- const dartCfg = cfg.dart ?? false;
7953
- if (
7954
- dartCfg !== false &&
7955
- dartCfg !== true &&
7956
- (typeof dartCfg !== "object" ||
7957
- dartCfg === null ||
7958
- (dartCfg.out !== undefined && typeof dartCfg.out !== "string") ||
7959
- (dartCfg.support !== undefined &&
7960
- dartCfg.support !== "vendored" &&
7961
- dartCfg.support !== "package") ||
7962
- (dartCfg.barrel !== undefined &&
7963
- !/^[a-z][a-z0-9_]*$/.test(String(dartCfg.barrel))) ||
7964
- (dartCfg.names !== undefined &&
7965
- (typeof dartCfg.names !== "object" ||
7966
- dartCfg.names === null ||
7967
- Array.isArray(dartCfg.names))) ||
7968
- (dartCfg.packages !== undefined &&
7969
- (typeof dartCfg.packages !== "object" ||
7970
- dartCfg.packages === null ||
7971
- typeof dartCfg.packages.types !== "string" ||
7972
- typeof dartCfg.packages.client !== "string")))
7973
- ) {
7974
- fail(
7975
- `config key 'dart' must be false, true, or { out?: "relative/dir", support?: "vendored" | "package", barrel?: "snake_case_name", packages?: { types, client, test?, compat? }, names?: { Generated: "Alias" } }; got ${JSON.stringify(
7976
- dartCfg
7977
- )}`
7978
- );
7979
- }
7980
- const dartPackages =
7981
- typeof dartCfg === "object" ? dartCfg.packages : undefined;
7982
- const dartNames = typeof dartCfg === "object" ? dartCfg.names : undefined;
7983
- if (
7984
- dartPackages !== undefined &&
7985
- typeof dartCfg === "object" &&
7986
- dartCfg.support === "vendored"
7987
- ) {
7988
- fail(
7989
- `dart.packages requires support: "package" — layered pub packages depend on the published ` +
7990
- `nomos_types/nomos_client cores; vendoring contradicts the package boundary`
7991
- );
7992
- }
7993
- const dartSupportImport: DartSupportImport =
7994
- typeof dartCfg === "object" && dartCfg.support !== undefined
7995
- ? dartCfg.support
7996
- : dartPackages !== undefined
7997
- ? "package"
7998
- : "vendored";
8810
+ await dartOperationProtoPrewarm;
8811
+ const dartSupportImport: DartSupportImport = "package";
7999
8812
  let dartOut: string | undefined;
8000
8813
  let dartFiles: string[] = [];
8001
8814
  let dartCursedHints: string[] = [];
@@ -8018,7 +8831,6 @@ async function main(): Promise<void> {
8018
8831
  packageInterface,
8019
8832
  dartPackages,
8020
8833
  dartNames,
8021
- nomosCompat,
8022
8834
  cfg.name,
8023
8835
  lawUsdzPath,
8024
8836
  cfg.application?.maxChildWorkspacesPerUser,
@@ -8047,11 +8859,11 @@ async function main(): Promise<void> {
8047
8859
  packageInterface,
8048
8860
  dartSupportImport,
8049
8861
  dartBarrel,
8050
- nomosCompat,
8051
8862
  dartNames,
8052
8863
  cfg.name
8053
8864
  );
8054
8865
  }
8866
+ stage("dart emit");
8055
8867
  // COMPILE-VALID MUST MEAN COMPILABLE (co2): `githolon compile` reported success while the generated Dart
8056
8868
  // had hard compiler errors (a duplicate id symbol, missing contract-id defs). Run `dart analyze` over the
8057
8869
  // emitted Dart as a SYMBOL-RESOLUTION GATE — a real analysis ERROR fails the compile (with the analyzer's
@@ -8067,7 +8879,7 @@ async function main(): Promise<void> {
8067
8879
  // Analyze exactly THE FILES THIS COMPILE EMITTED (`dartFiles`) — never the whole dir, which would
8068
8880
  // sweep in the transactional `.nomos-bak` backup and any stale legacy-layout leftovers and judge the
8069
8881
  // compile on files it did not write. First `dart pub get` each emitted PACKAGE (a dir up-chain with a
8070
- // pubspec) so `package:` imports resolve; a self-contained vendored emit has no pubspec (no get needed).
8882
+ // pubspec) so fixed `package:` imports resolve.
8071
8883
  const emittedDart = dartFiles.filter((f) => f.endsWith(".dart"));
8072
8884
  const pkgDirOf = (file: string): string | undefined => {
8073
8885
  let dir = path.dirname(file);
@@ -8082,9 +8894,9 @@ async function main(): Promise<void> {
8082
8894
  }
8083
8895
  return undefined;
8084
8896
  };
8085
- // Only files under a PUB PACKAGE (a pubspec up-chain) are verifiable — a VENDORED no-pubspec emit's
8086
- // runtime `package:nomos_client` imports cannot resolve without a package config, so judging those
8087
- // files would false-fail every vendored fixture. Analyze the in-package set; note the loose remainder.
8897
+ // Only files under a PUB PACKAGE (a pubspec up-chain) are verifiable.
8898
+ // A loose output directory has no package configuration against which
8899
+ // `nomos_client` can be resolved; note it rather than inventing runtime copies.
8088
8900
  const inPackage = emittedDart.filter((f) => pkgDirOf(f) !== undefined);
8089
8901
  const pkgDirs = [
8090
8902
  ...new Set(
@@ -8096,8 +8908,8 @@ async function main(): Promise<void> {
8096
8908
  // necessarily LAG the local source the compiler was built alongside (tooling+client move in lockstep,
8097
8909
  // CLAUDE.md). So the analyze gate must resolve against the LOCAL Dart source, else it judges freshly-
8098
8910
  // emitted client code against a stale hosted runtime (e.g. a method the current codegen calls that the
8099
- // last publish predates). We write a `pubspec_overrides.yaml` pointing nomos_client/nomos_types at the
8100
- // repo's own `dsl/dart` / `dsl/dart_types`. An INSTALLED tenant (`npx githolon`, compiler under
8911
+ // last publish predates). We write a `pubspec_overrides.yaml` pointing the runtime packages at this
8912
+ // checkout's `dsl/dart`, `dsl/dart_types` and `nomos_peer_host`. An INSTALLED tenant (`npx githolon`, compiler under
8101
8913
  // node_modules) gets NO override — it verifies against exactly the hosted runtime it will ship against.
8102
8914
  const localDart = (() => {
8103
8915
  const selfDir = path.dirname(fileURLToPath(import.meta.url));
@@ -8105,18 +8917,13 @@ async function main(): Promise<void> {
8105
8917
  return undefined;
8106
8918
  const client = path.resolve(selfDir, "..", "dart");
8107
8919
  const types = path.resolve(selfDir, "..", "dart_types");
8108
- const flutter = path.resolve(selfDir, "..", "..", "nomos_flutter");
8920
+ const peerHost = path.resolve(selfDir, "..", "..", "nomos_peer_host");
8109
8921
  if (
8110
8922
  existsSync(path.join(client, "pubspec.yaml")) &&
8111
- existsSync(path.join(types, "pubspec.yaml"))
8923
+ existsSync(path.join(types, "pubspec.yaml")) &&
8924
+ existsSync(path.join(peerHost, "pubspec.yaml"))
8112
8925
  ) {
8113
- return {
8114
- client,
8115
- types,
8116
- ...(existsSync(path.join(flutter, "pubspec.yaml"))
8117
- ? { flutter }
8118
- : {}),
8119
- };
8926
+ return { client, types, peerHost };
8120
8927
  }
8121
8928
  return undefined;
8122
8929
  })();
@@ -8127,7 +8934,21 @@ async function main(): Promise<void> {
8127
8934
  if (existsSync(file) && readFileSync(file, "utf8") === text) return;
8128
8935
  writeFileSync(file, text, "utf8");
8129
8936
  };
8130
- for (const pkg of pkgDirs) {
8937
+ // The dependency reconciler pins nomos_client/nomos_types into the tenant package a FLAT
8938
+ // `dart.out` writes into, which for `dart.out: pkg/lib` sits ABOVE `dart.out` and so is
8939
+ // invisible to `pkgDirOf`. Pinning a dep there while withholding the override left a source
8940
+ // checkout emitting a package that cannot `pub get` at all (the pinned version is the one
8941
+ // this compiler is being released WITH, so it is not on pub yet). Override it too. It is not
8942
+ // added to `pkgDirs`: the analyze gate judges what the compiler emitted, never a tenant
8943
+ // package's own hand-written code.
8944
+ const overrideDirs = [
8945
+ ...new Set(
8946
+ [...pkgDirs, flatDartPackageRoot(dOut)].filter(
8947
+ (dir): dir is string => dir !== undefined
8948
+ )
8949
+ ),
8950
+ ];
8951
+ for (const pkg of overrideDirs) {
8131
8952
  // Layered Dart output is generated-owned, including the compiler's temporary source-dogfood
8132
8953
  // override. A package may have been generated previously by a source checkout and then rebuilt
8133
8954
  // by the published compiler. In that transition, doing nothing here leaves the old absolute
@@ -8140,7 +8961,6 @@ async function main(): Promise<void> {
8140
8961
  const lines = ["dependency_overrides:"];
8141
8962
  const usesClient = /(^|\n)\s+nomos_client\s*:/.test(spec);
8142
8963
  const usesTypes = /(^|\n)\s+nomos_types\s*:/.test(spec);
8143
- const usesFlutter = /(^|\n)\s+nomos_flutter\s*:/.test(spec);
8144
8964
  if (usesClient)
8145
8965
  lines.push(` nomos_client:`, ` path: ${localDart.client}`);
8146
8966
  // A generated CLIENT or TEST package may only mention nomos_client directly, but that local
@@ -8151,12 +8971,13 @@ async function main(): Promise<void> {
8151
8971
  // to absorb for tenant developers.
8152
8972
  if (usesTypes || usesClient)
8153
8973
  lines.push(` nomos_types:`, ` path: ${localDart.types}`);
8154
- // The generated client application imports nomos_flutter, and a TEST package reaches that
8155
- // application transitively through its generated client sibling. Overrides do not propagate,
8156
- // so every local nomos_client solve root must also seat the matching local Flutter host even
8157
- // when its own pubspec does not name nomos_flutter directly.
8158
- if ((usesFlutter || usesClient) && localDart.flutter)
8159
- lines.push(` nomos_flutter:`, ` path: ${localDart.flutter}`);
8974
+ // nomos_client consumes the peer-driven shell capability contract.
8975
+ // Overrides do not propagate into this package's solve root.
8976
+ if (usesClient)
8977
+ lines.push(
8978
+ ` nomos_peer_host:`,
8979
+ ` path: ${localDart.peerHost}`
8980
+ );
8160
8981
  if (lines.length > 1) {
8161
8982
  writeTextIfChanged(overrideFile, lines.join("\n") + "\n");
8162
8983
  }
@@ -8246,9 +9067,10 @@ async function main(): Promise<void> {
8246
9067
  .join(" ")}`;
8247
9068
  }
8248
9069
  }
9070
+ stage("dart pub get");
8249
9071
  // The analyzer is a real codegen gate, but an identical generated Dart graph against identical
8250
9072
  // resolved packages and framework sources does not become more valid by launching another analyzer
8251
- // process. Content-address that verdict as USDA. Any generated type/client change, pub resolution,
9073
+ // process. Content-address that verdict as USDA. Any generated type/peer-office change, pub resolution,
8252
9074
  // local dogfood runtime change, or Dart SDK version change produces a different key and runs the real
8253
9075
  // analyzer before the proof is written.
8254
9076
  const collectDartAnalysisFiles = (root: string): string[] => {
@@ -8374,6 +9196,7 @@ async function main(): Promise<void> {
8374
9196
  }
8375
9197
  }
8376
9198
  }
9199
+ stage("dart analyze");
8377
9200
  // Only analyze when EVERY package's deps resolved: `dart analyze` against an unresolved package reports
8378
9201
  // every framework symbol (NomosRef, …) as undefined — a meaningless flood, not a real defect. An
8379
9202
  // unresolved dep is already surfaced as UNVERIFIED below (e.g. a fixture pinning an as-yet-unpublished
@@ -8396,7 +9219,7 @@ async function main(): Promise<void> {
8396
9219
  );
8397
9220
  } else if (emittedDart.length > 0) {
8398
9221
  console.log(
8399
- ` dart — vendored (no pub package) emit — runtime package imports are unverifiable without a pubspec; analysis skipped (use dart.packages for a fully-gated pub package)`
9222
+ ` dart ⚠ UNVERIFIED — generated output has no pubspec, so fixed nomos_client imports cannot be analyzed (emit a Dart package to enable the gate)`
8400
9223
  );
8401
9224
  }
8402
9225
  }
@@ -8409,6 +9232,13 @@ async function main(): Promise<void> {
8409
9232
  ` law ${rel(lawUsdzPath)} (${lawUsdz.byteLength} transport bytes)`
8410
9233
  );
8411
9234
  console.log(` domainHash ${domainHash}`);
9235
+ console.log(
9236
+ custodyPlacementDeclarations.length > 0
9237
+ ? ` placement ${rel(custodyPlacementDeclarationsPath)} (${
9238
+ custodyPlacementDeclarations.length
9239
+ } explicit declaration(s); separate frontier)`
9240
+ : " placement — no custody-placement declarations"
9241
+ );
8412
9242
  console.log(
8413
9243
  ` custody ${rel(
8414
9244
  layeredRootPath
@@ -8421,6 +9251,17 @@ async function main(): Promise<void> {
8421
9251
  }`
8422
9252
  : ` usdcat ⚠ unavailable — set NOMOS_REQUIRE_USDCAT=1 in release CI to make this proof mandatory`
8423
9253
  );
9254
+ console.log(" contracts ✓ generated Protobuf query results and public payloads match the kernel's law-derived contracts");
9255
+ console.log(
9256
+ compiledReadProjection
9257
+ ? " read-law ✓ authenticated OpenUSD read projection packaged"
9258
+ : " read-law ⚠ compiler unavailable; runtime will compose source law on first open"
9259
+ );
9260
+ console.log(
9261
+ compiledCommandProjection
9262
+ ? " commands ✓ authenticated OpenUSD command projection packaged"
9263
+ : " commands ⚠ compiler unavailable; runtime will compose source law before first command"
9264
+ );
8424
9265
  {
8425
9266
  const sum = (
8426
9267
  pick: (
@@ -8490,28 +9331,14 @@ async function main(): Promise<void> {
8490
9331
  }
8491
9332
  }
8492
9333
  console.log(
8493
- ` client ${rel(
8494
- clientPath
8495
- )} (typed TS client — payloads, read models, query accessors)`
8496
- );
8497
- console.log(
8498
- ` headless ${rel(headlessClientPath)} + ${rel(
8499
- headlessClientDeclarationPath
8500
- )} (typed business binding for code-mode/edge holons)`
9334
+ ` reception ${rel(clientPath)} + ${rel(
9335
+ clientDeclarationPath
9336
+ )} (the application's TypeScript Reception: typed surface + holon-level factories)`
8501
9337
  );
8502
9338
  for (const note of recursiveNotes) console.log(` note ${note}`);
8503
9339
  console.log(
8504
9340
  ` openapi served by the holon from installed USDA (GET /v2/workspaces/<ws>/openapi.json) — no build-time copy to drift`
8505
9341
  );
8506
- if (proofPath !== undefined) {
8507
- console.log(
8508
- ` proof ${rel(
8509
- proofPath
8510
- )} (a runnable e2e GENERATED from your law — run: githolon proof)`
8511
- );
8512
- } else {
8513
- console.log(` proof SKIPPED — ${proofSkip}`);
8514
- }
8515
9342
  for (const w of birthWarnings) {
8516
9343
  console.log(` ⚠ births ${w}`);
8517
9344
  }
@@ -8523,9 +9350,7 @@ async function main(): Promise<void> {
8523
9350
  // compile runs it implicitly and REFUSES on any refuse-level finding (a statically-provable
8524
9351
  // runtime refusal shipped as law). Warns/notes are printed but never block the compile.
8525
9352
  const allCheckFindings = authorTimeFindings;
8526
- const isFrameworkFinding = (finding: (typeof allCheckFindings)[number]) =>
8527
- finding.domain === APPLICATION_HOME_DOMAIN ||
8528
- finding.directiveId?.startsWith("nomos_internal_") === true;
9353
+ const isFrameworkFinding = isFrameworkAuthorTimeFinding;
8529
9354
  const checkFindings = allCheckFindings.filter(
8530
9355
  (finding) => !isFrameworkFinding(finding)
8531
9356
  );
@@ -8594,14 +9419,10 @@ async function main(): Promise<void> {
8594
9419
  );
8595
9420
  }
8596
9421
  } else {
8597
- const supportText =
8598
- dartSupportImport === "vendored"
8599
- ? "vendored nomos_client support"
8600
- : "package nomos_client imports";
8601
9422
  console.log(
8602
9423
  ` dart ${rel(dartOut)}${path.sep} (${
8603
9424
  dartFiles.length
8604
- } domain file(s) + ${supportText} — typed Dart for Flutter)`
9425
+ } domain file(s) + package nomos_client imports — typed Dart for Flutter)`
8605
9426
  );
8606
9427
  }
8607
9428
  }
@@ -8610,34 +9431,17 @@ async function main(): Promise<void> {
8610
9431
  );
8611
9432
  console.log(``);
8612
9433
  console.log(`your business path:`);
8613
- if (priorLock !== undefined) {
8614
- console.log(
8615
- ` npx githolon proof --upgrade # candidate -> exact existing history -> disposable promotion -> cold restart`
8616
- );
8617
- }
8618
- if (proofPath !== undefined) {
8619
- console.log(
8620
- ` npx githolon proof # generated birth -> verify -> durable restore -> business read-back`
8621
- );
8622
- console.log(
8623
- ` npx githolon dev # rerun that proof on every save`
8624
- );
8625
- console.log(
8626
- ` npx githolon proof --live # the same business proof against Nomos Cloud`
8627
- );
8628
- if (proofDomains.length > 1) {
8629
- console.log(
8630
- ` this package proves all ${
8631
- proofDomains.length
8632
- } business areas together (${proofDomains.join(", ")})`
8633
- );
8634
- }
8635
- } else {
8636
- console.log(
8637
- ` add a business birth declaration to generate an end-to-end proof`
8638
- );
8639
- }
9434
+ console.log(
9435
+ ` npx githolon test # generated Dart scenarios against the real local custody plane`
9436
+ );
9437
+ console.log(
9438
+ ` vitest + @githolon/testing # drive ${cfg.name}.reception.mjs through the real kernel from TypeScript`
9439
+ );
9440
+ console.log(
9441
+ ` npx githolon dev # recompile and re-check on every save`
9442
+ );
8640
9443
 
9444
+ stage("summary + author-time gate");
8641
9445
  if (analyzeFlag) runDartAnalyze(dartOut);
8642
9446
 
8643
9447
  // The unchanged lane is itself USDA: a disposable content proof under
@@ -8689,6 +9493,7 @@ async function main(): Promise<void> {
8689
9493
  law: lawUsdzPath,
8690
9494
  });
8691
9495
  }
9496
+ stage("compile proof");
8692
9497
  }
8693
9498
 
8694
9499
  /**