@githolon/dsl 0.108.7 → 0.110.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (308) hide show
  1. package/assets/operation-contract.wasm +0 -0
  2. package/check_package.mjs +12 -15
  3. package/compile_package.mjs +17 -18
  4. package/compile_proof.mjs +16 -1
  5. package/dart/CHANGELOG.md +80 -1
  6. package/dart/README.md +25 -2
  7. package/dart/lib/src/admission_decision.dart +41 -0
  8. package/dart/lib/src/application_framework_calls.dart +52 -2
  9. package/dart/lib/src/application_model_adoption.dart +187 -0
  10. package/dart/lib/src/application_status.dart +73 -0
  11. package/dart/lib/src/console_enrolment.dart +271 -0
  12. package/dart/lib/src/console_http_io.dart +26 -0
  13. package/dart/lib/src/console_http_stub.dart +7 -0
  14. package/dart/lib/src/dev_cloud.dart +69 -22
  15. package/dart/lib/src/generated_call.dart +83 -23
  16. package/dart/lib/src/kernel_primitives.dart +119 -0
  17. package/dart/lib/src/nms_conformance_runtime.dart +1 -1
  18. package/dart/lib/src/nms_generated_runtime.dart +32 -10
  19. package/dart/lib/src/nms_test_environment.dart +1 -1
  20. package/dart/lib/src/node_runner_transport.dart +41 -46
  21. package/dart/lib/src/observed_session_directory.dart +70 -0
  22. package/dart/lib/src/operation_receipts.dart +112 -0
  23. package/dart/lib/src/peer_application_streams.dart +103 -0
  24. package/dart/lib/src/reception/dart_reception.dart +4280 -0
  25. package/dart/lib/src/reception/event_desk.dart +214 -0
  26. package/dart/lib/src/reception/extremum_read.dart +46 -0
  27. package/dart/lib/src/reception/local_copies.dart +371 -0
  28. package/dart/lib/src/reception/peer_office_connection.dart +11 -0
  29. package/dart/lib/src/reception/progress_desk.dart +268 -0
  30. package/dart/lib/src/reception/reception_contract.dart +11 -0
  31. package/dart/lib/src/reception/reception_session.dart +284 -0
  32. package/dart/lib/src/reception/sync_diagnostics.dart +26 -0
  33. package/dart/lib/src/reception/workspace_preparation_report.dart +126 -0
  34. package/dart/lib/src/replica_readiness.dart +123 -0
  35. package/dart/lib/src/runner/node-entry.mjs +23950 -15776
  36. package/dart/lib/src/runtime_v2_gateway.dart +56 -3
  37. package/dart/lib/src/runtime_v2_transport.dart +544 -3
  38. package/dart/lib/src/session_bound.dart +200 -311
  39. package/dart/lib/src/signing_identity_kernel.dart +84 -0
  40. package/dart/lib/src/sync_status_projection.dart +550 -0
  41. package/dart/lib/src/sync_status_reducer.dart +170 -0
  42. package/dart/lib/src/test_harness.dart +809 -1153
  43. package/dart/lib/src/testing/test_custody.dart +66 -0
  44. package/dart/lib/src/testing/test_peer.dart +328 -0
  45. package/dart/lib/src/typed_watch_registry.dart +169 -0
  46. package/dart/lib/src/workspace_open_progress.dart +27 -0
  47. package/dart/pubspec.lock +24 -1
  48. package/dart/pubspec.yaml +7 -2
  49. package/dart/pubspec_overrides.yaml +2 -0
  50. package/dart/test/admission_decision_test.dart +48 -0
  51. package/dart/test/background_model_adoption_test.dart +228 -0
  52. package/dart/test/bundle_version_pairing_test.dart +4 -4
  53. package/dart/test/child_authentication_repair_test.dart +359 -0
  54. package/dart/test/cloud_bound_local_shell_test.dart +119 -0
  55. package/dart/test/console_enrolment_refusal_test.dart +52 -0
  56. package/dart/test/console_enrolment_test.dart +227 -0
  57. package/dart/test/errors_test.dart +5 -5
  58. package/dart/test/event_plane_seam_test.dart +50 -164
  59. package/dart/test/extremum_read_test.dart +58 -0
  60. package/dart/test/fixtures/wedge/wedge-entry.mjs +3 -21
  61. package/dart/test/generated_query_test.dart +32 -0
  62. package/dart/test/harness_phase_timeout_test.dart +18 -23
  63. package/dart/test/kernel_author_after_read_test.dart +242 -0
  64. package/dart/test/kernel_fence_test.dart +137 -0
  65. package/dart/test/local_copies_test.dart +222 -0
  66. package/dart/test/model_adoption_test.dart +198 -0
  67. package/dart/test/node_peer_local_custody_test.dart +153 -0
  68. package/dart/test/offer_authentication_proof_test.dart +352 -52
  69. package/dart/test/operation_compatibility_test.dart +151 -0
  70. package/dart/test/platform_credential_effect_test.dart +386 -0
  71. package/dart/test/receiving_group_open_test.dart +213 -0
  72. package/dart/test/reception/workspace_preparation_report_test.dart +103 -0
  73. package/dart/test/reception_event_desk_close_test.dart +16 -0
  74. package/dart/test/reception_span_trace_test.dart +23 -0
  75. package/dart/test/replica_readiness_test.dart +156 -0
  76. package/dart/test/runtime_projections_test.dart +92 -0
  77. package/dart/test/runtime_v2_gateway_test.dart +131 -0
  78. package/dart/test/signing_identity_kernel_test.dart +75 -0
  79. package/dart/test/sync_status_preparation_report_test.dart +29 -0
  80. package/dart/test/sync_status_reducer_test.dart +419 -0
  81. package/dart/test/test_harness_test.dart +71 -95
  82. package/dart/test/typed_query_watch_test.dart +170 -0
  83. package/dart/test/typed_watch_registry_test.dart +249 -0
  84. package/dart_lints/README.md +18 -0
  85. package/dart_lints/lib/nomos_lints.dart +70 -0
  86. package/dart_lints/pubspec.lock +580 -0
  87. package/dart_lints/pubspec.yaml +12 -0
  88. package/dart_lints/test/reference_identity_test.dart +75 -0
  89. package/dart_types/CHANGELOG.md +18 -0
  90. package/dart_types/lib/nomos_kernel_protocol.dart +21 -1
  91. package/dart_types/lib/nomos_types.dart +9 -1
  92. package/dart_types/lib/src/admission_decision.dart +37 -0
  93. package/dart_types/lib/src/aggregate_meta.dart +1 -1
  94. package/dart_types/lib/src/approved_model.dart +65 -0
  95. package/dart_types/lib/src/custody.dart +10 -18
  96. package/dart_types/lib/src/error_info.dart +4 -4
  97. package/dart_types/lib/src/errors.dart +29 -29
  98. package/dart_types/lib/src/foreign_ref.dart +62 -33
  99. package/dart_types/lib/src/local_work_rejected.dart +3 -3
  100. package/dart_types/lib/src/nomos_ref.dart +3 -1
  101. package/dart_types/lib/src/principal.dart +2 -2
  102. package/dart_types/lib/src/proto/google/protobuf/any.pb.dart +163 -0
  103. package/dart_types/lib/src/proto/google/protobuf/any.pbenum.dart +11 -0
  104. package/dart_types/lib/src/proto/nomos/discovery/v1/workspace_placement.pb.dart +137 -0
  105. package/dart_types/lib/src/proto/nomos/discovery/v1/workspace_placement.pbenum.dart +11 -0
  106. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pb.dart +20721 -10433
  107. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbenum.dart +555 -27
  108. package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pb.dart +387 -0
  109. package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pbenum.dart +46 -0
  110. package/dart_types/lib/src/proto/nomos/runtime/v2/application_readiness_desk.pb.dart +295 -0
  111. package/dart_types/lib/src/proto/nomos/runtime/v2/application_readiness_desk.pbenum.dart +139 -0
  112. package/dart_types/lib/src/proto/nomos/runtime/v2/authentication_desk.pb.dart +269 -0
  113. package/dart_types/lib/src/proto/nomos/runtime/v2/authentication_desk.pbenum.dart +103 -0
  114. package/dart_types/lib/src/proto/nomos/runtime/v2/compatibility_desk.pb.dart +417 -0
  115. package/dart_types/lib/src/proto/nomos/runtime/v2/compatibility_desk.pbenum.dart +62 -0
  116. package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pb.dart +9968 -0
  117. package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pbenum.dart +133 -0
  118. package/dart_types/lib/src/proto/nomos/runtime/v2/credentials_desk.pb.dart +204 -0
  119. package/dart_types/lib/src/proto/nomos/runtime/v2/credentials_desk.pbenum.dart +54 -0
  120. package/dart_types/lib/src/proto/nomos/runtime/v2/custody_desk.pb.dart +1550 -0
  121. package/dart_types/lib/src/proto/nomos/runtime/v2/custody_desk.pbenum.dart +11 -0
  122. package/dart_types/lib/src/proto/nomos/runtime/v2/framework.pb.dart +710 -0
  123. package/dart_types/lib/src/proto/nomos/runtime/v2/framework.pbenum.dart +11 -0
  124. package/dart_types/lib/src/proto/nomos/runtime/v2/identity_desk.pb.dart +845 -0
  125. package/dart_types/lib/src/proto/nomos/runtime/v2/identity_desk.pbenum.dart +11 -0
  126. package/dart_types/lib/src/proto/nomos/runtime/v2/interaction_desk.pb.dart +198 -0
  127. package/dart_types/lib/src/proto/nomos/runtime/v2/interaction_desk.pbenum.dart +11 -0
  128. package/dart_types/lib/src/proto/nomos/runtime/v2/law_desk.pb.dart +369 -0
  129. package/dart_types/lib/src/proto/nomos/runtime/v2/law_desk.pbenum.dart +11 -0
  130. package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pb.dart +609 -0
  131. package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pbenum.dart +65 -0
  132. package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pb.dart +358 -0
  133. package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pbenum.dart +100 -0
  134. package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pb.dart +2231 -0
  135. package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pbenum.dart +271 -0
  136. package/dart_types/lib/src/proto/nomos/runtime/v2/placement_desk.pb.dart +1286 -0
  137. package/dart_types/lib/src/proto/nomos/runtime/v2/placement_desk.pbenum.dart +11 -0
  138. package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pb.dart +636 -0
  139. package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pbenum.dart +116 -0
  140. package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pb.dart +1637 -0
  141. package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pbenum.dart +175 -0
  142. package/dart_types/lib/src/proto/nomos/runtime/v2/recovery_desk.pb.dart +1269 -0
  143. package/dart_types/lib/src/proto/nomos/runtime/v2/recovery_desk.pbenum.dart +129 -0
  144. package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pb.dart +2782 -0
  145. package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pbenum.dart +97 -0
  146. package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pb.dart +2232 -8627
  147. package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pbenum.dart +0 -358
  148. package/dart_types/lib/src/proto/nomos/runtime/v2/sealing_desk.pb.dart +1639 -0
  149. package/dart_types/lib/src/proto/nomos/runtime/v2/sealing_desk.pbenum.dart +46 -0
  150. package/dart_types/lib/src/runtime_outcome.dart +133 -0
  151. package/dart_types/lib/src/subscriptions.dart +116 -23
  152. package/dart_types/pubspec.yaml +1 -1
  153. package/dart_types/test/extremum_subscriptions_test.dart +40 -0
  154. package/dart_types/test/foreign_ref_resolution_test.dart +75 -0
  155. package/dart_types/test/nomos_ref_address_test.dart +25 -0
  156. package/dart_types/test/ordered_subscriptions_test.dart +34 -0
  157. package/operation_contract_check.mjs +24 -0
  158. package/package.json +11 -6
  159. package/src/aggregate.ts +70 -10
  160. package/src/application_topology.ts +18 -11
  161. package/src/attested.ts +3 -0
  162. package/src/attested_core.ts +46 -3
  163. package/src/birth_meter_ensure.ts +19 -7
  164. package/src/births.ts +29 -4
  165. package/src/build_package.ts +140 -13
  166. package/src/business_birth.ts +181 -4
  167. package/src/codegen_call_proto.ts +1276 -0
  168. package/src/codegen_call_usda.ts +49 -6
  169. package/src/codegen_dart.ts +83 -44
  170. package/src/codegen_dart_client.ts +458 -800
  171. package/src/{codegen_dart_application.ts → codegen_dart_facade.ts} +331 -582
  172. package/src/codegen_dart_framework_calls.ts +320 -37
  173. package/src/codegen_dart_indexed_variant.ts +159 -0
  174. package/src/codegen_dart_scenario.ts +42 -13
  175. package/src/codegen_proof.ts +3 -3
  176. package/src/codegen_provider_dart.ts +5 -0
  177. package/src/codegen_query_proto.ts +158 -0
  178. package/src/codegen_refusal.ts +189 -0
  179. package/src/codegen_result_proto.ts +931 -0
  180. package/src/codegen_result_usda.ts +4 -0
  181. package/src/codegen_ts.ts +464 -616
  182. package/src/codegen_workspace_access.ts +105 -0
  183. package/src/compatibility_graph.ts +759 -0
  184. package/src/compatibility_usda.ts +264 -0
  185. package/src/compile_package_main.ts +2017 -1195
  186. package/src/custody_placement.ts +156 -0
  187. package/src/custody_placement_usda.ts +91 -0
  188. package/src/decl_shapes.ts +21 -2
  189. package/src/describe_model.ts +1 -0
  190. package/src/directive.ts +59 -2
  191. package/src/drivers.ts +51 -0
  192. package/src/engine_entry.ts +52 -8
  193. package/src/evolve_preview.ts +11 -4
  194. package/src/exists.ts +2 -0
  195. package/src/fields.ts +75 -11
  196. package/src/flow_gate.ts +2 -1
  197. package/src/framework/application_home.ts +170 -18
  198. package/src/framework/attested_receipts.ts +8 -3
  199. package/src/framework/auth_provider_law.ts +169 -9
  200. package/src/framework/birth_certificate_law.ts +4 -0
  201. package/src/framework/capability.ts +34 -3
  202. package/src/framework/child_authentication.ts +346 -0
  203. package/src/framework/console_governance.ts +295 -0
  204. package/src/framework/delegated_roles.ts +301 -0
  205. package/src/framework/domain_lifecycle.ts +26 -13
  206. package/src/framework/founding_genesis.ts +193 -0
  207. package/src/framework/framework_aggregates.ts +1 -1
  208. package/src/framework/genesis.ts +6 -1
  209. package/src/framework/governance_relations.ts +19 -0
  210. package/src/framework/home.ts +5 -734
  211. package/src/framework/legacy/child_auth_provider_epoch.ts +73 -0
  212. package/src/framework/model_authority_repair.ts +116 -0
  213. package/src/framework/model_authority_seed.ts +185 -0
  214. package/src/framework/model_transition.ts +211 -0
  215. package/src/framework/owned_custody.ts +1 -1
  216. package/src/framework/principal_enrollment.ts +302 -0
  217. package/src/framework/receiving_group.ts +857 -0
  218. package/src/framework/receiving_group_recovery.ts +314 -0
  219. package/src/framework/relations.ts +4 -8
  220. package/src/framework/signing_identity.ts +242 -95
  221. package/src/framework/source_roster.ts +11 -5
  222. package/src/framework/workspace_access.ts +149 -0
  223. package/src/framework/workspace_invariant.ts +73 -52
  224. package/src/framework/workspaces.ts +407 -40
  225. package/src/framework_call_definitions.ts +32 -3
  226. package/src/generated_client_bundle_boundary.ts +97 -0
  227. package/src/generated_client_names.ts +1 -0
  228. package/src/identity_usda.ts +27 -0
  229. package/src/index.ts +183 -12
  230. package/src/indexed_variant.ts +83 -0
  231. package/src/intent.ts +173 -45
  232. package/src/law_check.ts +8 -5
  233. package/src/law_dependency_index.ts +231 -0
  234. package/src/law_graph.ts +508 -71
  235. package/src/law_graph_usda.ts +170 -24
  236. package/src/law_identity.ts +119 -81
  237. package/src/law_source_map.ts +4 -1
  238. package/src/law_units.ts +498 -0
  239. package/src/law_value_type.ts +7 -3
  240. package/src/member_authentication.ts +302 -0
  241. package/src/null_meta.ts +1 -1
  242. package/src/openusd_compiled_layer.ts +132 -0
  243. package/src/operation_contract_check.ts +31 -0
  244. package/src/ordered_read.ts +2 -1
  245. package/src/package_config.ts +33 -12
  246. package/src/package_dependencies.ts +1 -0
  247. package/src/plan_source_slice.ts +30 -1
  248. package/src/protobuf_field_number.ts +36 -0
  249. package/src/provenance_wire.ts +6 -0
  250. package/src/query.ts +2 -1
  251. package/src/read.ts +50 -0
  252. package/src/relation.ts +4 -1
  253. package/src/relation_schema.ts +15 -2
  254. package/src/report.ts +2 -2
  255. package/src/sqlite_computation_bundle.ts +78 -0
  256. package/src/sqlite_computed.ts +102 -0
  257. package/src/sqlite_counts.ts +154 -0
  258. package/src/sqlite_layout.ts +823 -0
  259. package/src/sqlite_reads.ts +275 -0
  260. package/src/sqlite_samples.ts +185 -0
  261. package/src/stable_id_identity_bridge.ts +1 -1
  262. package/src/stable_ids_for_module.ts +16 -4
  263. package/src/test_call_usda.ts +6 -6
  264. package/src/usda_relationship_integrity.ts +20 -3
  265. package/src/usda_text.ts +21 -1
  266. package/src/value_object.ts +81 -11
  267. package/src/wire.ts +22 -0
  268. package/src/wire_encode.ts +227 -20
  269. package/src/workspace_sharding.ts +15 -6
  270. package/src/workspace_sharing.ts +51 -49
  271. package/src/zod_introspection.ts +2 -0
  272. package/compat_package.mjs +0 -46
  273. package/dart/lib/src/home.dart +0 -15
  274. package/dart/lib/src/runner/offline-install.mjs +0 -15367
  275. package/dart/lib/src/source_trust.dart +0 -51
  276. package/dart/lib/src/webview_bridge.dart +0 -6540
  277. package/dart/lib/src/workspace_name.dart +0 -30
  278. package/dart/test/acquire_session_credentials_test.dart +0 -150
  279. package/dart/test/acquire_session_test.dart +0 -534
  280. package/dart/test/adv_realm_interference_test.dart +0 -600
  281. package/dart/test/cloud_bound_topology_test.dart +0 -168
  282. package/dart/test/cluster_births_test.dart +0 -202
  283. package/dart/test/cluster_device_keyed_acceptance_test.dart +0 -105
  284. package/dart/test/cluster_local_autosync_test.dart +0 -123
  285. package/dart/test/cluster_scale_test.dart +0 -122
  286. package/dart/test/compat_test.dart +0 -61
  287. package/dart/test/dlq_session_surface_test.dart +0 -222
  288. package/dart/test/first_app_gauntlet_test.dart +0 -386
  289. package/dart/test/fixtures/wedge/fake-install.mjs +0 -11
  290. package/dart/test/four_layer_nested_topology_test.dart +0 -290
  291. package/dart/test/home_parity_test.dart +0 -13
  292. package/dart/test/local_sync_inert_test.dart +0 -107
  293. package/dart/test/node_runner_transport_test.dart +0 -78
  294. package/dart/test/parent_signer_recovery_test.dart +0 -155
  295. package/dart/test/residency_refusal_test.dart +0 -49
  296. package/dart/test/sealing_client_test.dart +0 -181
  297. package/dart/test/session_bound_test.dart +0 -285
  298. package/dart/test/stale_bundle_test.dart +0 -80
  299. package/dart/test/vo_read_typing_test.dart +0 -131
  300. package/dart/test/watch_after_offer_stall_test.dart +0 -100
  301. package/dart/test/webview_bridge_test.dart +0 -1930
  302. package/dart/test/workspace_name_test.dart +0 -24
  303. package/runtime/generated-application-host.js +0 -22860
  304. package/src/codegen_query_usda.ts +0 -214
  305. package/src/compat_manifest.ts +0 -26
  306. package/src/compat_package_main.ts +0 -224
  307. package/src/shape_compat.ts +0 -352
  308. package/src/usd_state.ts +0 -505
@@ -1,290 +0,0 @@
1
- // THE FOUR-LAYER NESTED TOPOLOGY TEST — a real-app shape end-to-end:
2
- //
3
- // Layer 1: the remote PLATFORM (tenant root)
4
- // Layer 2: the HOME born under the platform (the per-user holon)
5
- // Layer 3: a SPACE app-workspace born offline under the home
6
- // Layer 4: a PROJECT app-workspace born offline under the home, with cross-workspace reference to Space
7
- // Plus: MULTI-USER offline-first — two devices/users write concurrently and CONVERGE
8
- //
9
- // Sequence:
10
- // 1. the remote platform births the home (the ONE control-plane contact)
11
- // 2. the app mounts the home locally (a normal cloud-bound NomosSession; platform is never cloned)
12
- // 3. the network PARTITIONS
13
- // 4. the home births the SPACE workspace LOCALLY
14
- // 5. the home births the PROJECT workspace LOCALLY (which can reference the Space)
15
- // 6. both space and project open as sibling sessions and ACCEPT LOCAL WRITES while offline
16
- // 7. UI-facing queries return those writes immediately (local projection; no sync awaited)
17
- // 8. the network HEALS
18
- // 9. `harness.converge()` establishes remote custody PARENT-FIRST (home's synced chain folds the
19
- // birth offer-effects remotely, creating the space/project remote custody; then they converge)
20
- // 10. a FRESH observer (the dev cloud's own remote projection) sees both space and project writes
21
- // 11. a SECOND USER writes to both workspaces offline, then converges (multi-user convergence)
22
- // 12. the FIRST USER sees the second user's writes in fresh queries (cross-user view)
23
- //
24
- import 'dart:convert';
25
- import 'dart:io';
26
-
27
- import 'package:crypto/crypto.dart' show sha256;
28
- import 'package:nomos_client/src/nms_conformance_runtime.dart';
29
- import 'package:test/test.dart';
30
-
31
- String get _root => '${Directory.current.path}/../..';
32
- String get _repoEntry => '$_root/cloud/web-client/runner/node-entry.ts';
33
- String get _devServer => '$_root/cloud/dev-server/server.ts';
34
- String get _deploy =>
35
- '$_root/bench/scale/fixtures/four-layer-nested-topology/build/four-layer-nested-topology.law.usdz';
36
- const _subject1 = 'user:four-layer-test-user1';
37
- const _subject2 = 'user:four-layer-test-user2';
38
-
39
- String? _missingPrereq() {
40
- if (!File(_repoEntry).existsSync()) return 'in-repo runner not found at $_repoEntry';
41
- if (!File(_devServer).existsSync()) return 'in-repo dev server not found at $_devServer';
42
- if (!File(_deploy).existsSync()) {
43
- return 'compile the fixture: cd bench/scale/fixtures/four-layer-nested-topology && node ../../../../dsl/compile_package.mjs — missing $_deploy';
44
- }
45
- return null;
46
- }
47
-
48
- void main() {
49
- test('the four-layer topology: platform → home → space + project (with cross-ref) offline + multi-user converge',
50
- () async {
51
- final skip = _missingPrereq();
52
- if (skip != null) {
53
- markTestSkipped(skip);
54
- return;
55
- }
56
- final diag = Platform.environment['NOMOS_HARNESS_DIAG'] == '1'
57
- ? (String l) => stderr.writeln(l)
58
- : null;
59
- final cloud = await NomosTestCloud.start(serverEntry: _devServer, platform: 'four-layer-platform', lawUsdzPath: _deploy, onDiagnostic: diag);
60
- NomosTestHarness? harness1;
61
- NomosTestHarness? harness2;
62
- try {
63
- // LAYER 1+2: the remote ceremony + the local mount (user 1)
64
- harness1 = await NomosTestHarness.cloudBound(
65
- cloud: cloud,
66
- lawUsdzPaths: [_deploy],
67
- platformWorkspace: 'four-layer-platform',
68
- subject: _subject1,
69
- nodeEntry: _repoEntry,
70
- onDiagnostic: diag,
71
- );
72
- final home1 = harness1.homeSession;
73
- expect(home1.workspace, startsWith('home-'),
74
- reason: 'the remote ceremony born the deterministic home slot for user1');
75
- final remoteHome1 = await cloud.remoteHead(home1.workspace);
76
- expect(remoteHome1.initialized, isTrue,
77
- reason: 'the home was born REMOTELY (custody exists before the app mounted it)');
78
-
79
- // PARTITION
80
- cloud.setOnline(false);
81
-
82
- // LAYER 3: birth a SPACE workspace LOCALLY (under home1)
83
- final spaceOutcome = await harness1.bridge.session(home1.workspace).offerCreates(
84
- domain: 'home',
85
- directiveId: 'birthSpaceWorkspace',
86
- payload: {
87
- 'owner': _subject1,
88
- 'frameworkHash': harness1.frameworkHash,
89
- 'lawHash': harness1.lawHashes(home1.workspace).first,
90
- 'bornAt': '2026-07-15T00:00:00Z',
91
- 'ownerKeyHash': sha256.convert(base64Decode(harness1.devicePublicKey(home1.workspace)!)).toString(),
92
- },
93
- actor: _subject1,
94
- );
95
- expect(spaceOutcome.born, isNotEmpty, reason: 'the local .births() offer spawned the space');
96
- final spaceWs = spaceOutcome.born.single;
97
- expect(spaceWs, startsWith('space-'));
98
-
99
- // LAYER 4: birth a PROJECT workspace LOCALLY (under home1, will reference space)
100
- final projectOutcome = await harness1.bridge.session(home1.workspace).offerCreates(
101
- domain: 'home',
102
- directiveId: 'birthProjectWorkspace',
103
- payload: {
104
- 'owner': _subject1,
105
- 'frameworkHash': harness1.frameworkHash,
106
- 'lawHash': harness1.lawHashes(home1.workspace).first,
107
- 'bornAt': '2026-07-15T00:00:01Z',
108
- 'ownerKeyHash': sha256.convert(base64Decode(harness1.devicePublicKey(home1.workspace)!)).toString(),
109
- },
110
- actor: _subject1,
111
- );
112
- expect(projectOutcome.born, isNotEmpty, reason: 'the local .births() offer spawned the project');
113
- final projectWs = projectOutcome.born.single;
114
- expect(projectWs, startsWith('project-'));
115
-
116
- // LAYER 3+4: Open both space and project as sibling sessions + make WRITES while offline
117
- final space = await harness1.bridge.acquireSession(spaceWs, authorSecret: harness1.deviceSecret(home1.workspace));
118
- final project = await harness1.bridge.acquireSession(projectWs, authorSecret: harness1.deviceSecret(home1.workspace));
119
-
120
- // Write to space
121
- await space.offerCreates(
122
- domain: 'space',
123
- directiveId: 'writeSpaceItem',
124
- payload: {
125
- 'author': _subject1,
126
- 'title': 'Space Item 1 (offline)',
127
- 'createdAt': '2026-07-15T00:00:02Z',
128
- },
129
- actor: _subject1,
130
- );
131
-
132
- // Write to project (with cross-workspace reference to space)
133
- await project.offerCreates(
134
- domain: 'project',
135
- directiveId: 'createTask',
136
- payload: {
137
- 'author': _subject1,
138
- 'title': 'Task 1: belongs to Space (offline)',
139
- 'spaceWorkspace': spaceWs, // CROSS-WORKSPACE REFERENCE
140
- 'createdAt': '2026-07-15T00:00:03Z',
141
- },
142
- actor: _subject1,
143
- );
144
-
145
- // LAYER 3+4: UI-facing queries return the writes IMMEDIATELY (local projection; nothing awaited sync)
146
- final spaceRows = await space.query('spaceItemsByAuthor', {'author': _subject1});
147
- expect(spaceRows, hasLength(1));
148
- expect((spaceRows.single['data'] as Map)['title'], 'Space Item 1 (offline)');
149
-
150
- final projectRows = await project.query('tasksByAuthor', {'author': _subject1});
151
- expect(projectRows, hasLength(1));
152
- expect((projectRows.single['data'] as Map)['title'], 'Task 1: belongs to Space (offline)');
153
- expect((projectRows.single['data'] as Map)['spaceWorkspace'], spaceWs);
154
-
155
- // Verify NO remote custody yet (the partition is real)
156
- final remoteSpaceOffline = await cloud.remoteHead(spaceWs);
157
- final remoteProjectOffline = await cloud.remoteHead(projectWs);
158
- expect(remoteSpaceOffline.initialized, isFalse,
159
- reason: 'while partitioned, the locally-born space has NO remote custody');
160
- expect(remoteProjectOffline.initialized, isFalse,
161
- reason: 'while partitioned, the locally-born project has NO remote custody');
162
-
163
- // HEAL the network BEFORE creating harness2 (user2 needs network to be created)
164
- cloud.setOnline(true);
165
-
166
- // === MULTI-USER SCENARIO: USER 2 ===
167
- // Create harness2 for user 2 (same platform, different home)
168
- harness2 = await NomosTestHarness.cloudBound(
169
- cloud: cloud,
170
- lawUsdzPaths: [_deploy],
171
- platformWorkspace: 'four-layer-platform',
172
- subject: _subject2,
173
- nodeEntry: _repoEntry,
174
- onDiagnostic: diag,
175
- );
176
- final home2 = harness2.homeSession;
177
- expect(home2.workspace, startsWith('home-'),
178
- reason: 'user2 gets its own deterministic home slot');
179
-
180
- // PARTITION again for user2's offline writes
181
- cloud.setOnline(false);
182
-
183
- // User2 births its own space locally (while partitioned)
184
- final space2Outcome = await harness2.bridge.session(home2.workspace).offerCreates(
185
- domain: 'home',
186
- directiveId: 'birthSpaceWorkspace',
187
- payload: {
188
- 'owner': _subject2,
189
- 'frameworkHash': harness2.frameworkHash,
190
- 'lawHash': harness2.lawHashes(home2.workspace).first,
191
- 'bornAt': '2026-07-15T00:00:04Z',
192
- 'ownerKeyHash': sha256.convert(base64Decode(harness2.devicePublicKey(home2.workspace)!)).toString(),
193
- },
194
- actor: _subject2,
195
- );
196
- final space2Ws = space2Outcome.born.single;
197
- final space2 = await harness2.bridge.acquireSession(space2Ws, authorSecret: harness2.deviceSecret(home2.workspace));
198
-
199
- // User2 writes to its own space while partitioned
200
- await space2.offerCreates(
201
- domain: 'space',
202
- directiveId: 'writeSpaceItem',
203
- payload: {
204
- 'author': _subject2,
205
- 'title': 'Space Item from User2 (offline)',
206
- 'createdAt': '2026-07-15T00:00:05Z',
207
- },
208
- actor: _subject2,
209
- );
210
-
211
- // HEAL the network again for final convergence
212
- cloud.setOnline(true);
213
-
214
- // CONVERGE (parent-first: home's synced chain folds the birth offer-effects remotely,
215
- // creating the space/project remote custody; then they converge)
216
- await harness1.converge(timeout: const Duration(seconds: 180));
217
- await harness2.converge(timeout: const Duration(seconds: 180));
218
-
219
- // Verify FRESH observer (the dev cloud's own remote projection) sees the offline-written data
220
- final http = HttpClient();
221
- try {
222
- // Check space1 (user1's space)
223
- final req1 = await http.getUrl(Uri.parse(
224
- '${cloud.origin}/v2/workspaces/$spaceWs/spaceItemsByAuthor?author=${Uri.encodeQueryComponent(_subject1)}'));
225
- final resp1 = await req1.close();
226
- final body1 = jsonDecode(await resp1.transform(utf8.decoder).join());
227
- final rows1 = ((body1 as Map)['rows'] as List?) ?? const [];
228
- expect(rows1, hasLength(1),
229
- reason: 'the remote projection serves user1\'s offline-written space item after converge');
230
- expect(((rows1.single as Map)['data'] as Map)['title'], 'Space Item 1 (offline)');
231
-
232
- // Check project1 (user1's project with cross-reference)
233
- final req2 = await http.getUrl(Uri.parse(
234
- '${cloud.origin}/v2/workspaces/$projectWs/tasksByAuthor?author=${Uri.encodeQueryComponent(_subject1)}'));
235
- final resp2 = await req2.close();
236
- final body2 = jsonDecode(await resp2.transform(utf8.decoder).join());
237
- final rows2 = ((body2 as Map)['rows'] as List?) ?? const [];
238
- expect(rows2, hasLength(1),
239
- reason: 'the remote projection serves user1\'s offline-written project task after converge');
240
- final task = ((rows2.single as Map)['data'] as Map);
241
- expect(task['title'], 'Task 1: belongs to Space (offline)');
242
- expect(task['spaceWorkspace'], spaceWs, reason: 'cross-workspace reference is persisted');
243
-
244
- // Check space2 (user2's space)
245
- final req3 = await http.getUrl(Uri.parse(
246
- '${cloud.origin}/v2/workspaces/$space2Ws/spaceItemsByAuthor?author=${Uri.encodeQueryComponent(_subject2)}'));
247
- final resp3 = await req3.close();
248
- final body3 = jsonDecode(await resp3.transform(utf8.decoder).join());
249
- final rows3 = ((body3 as Map)['rows'] as List?) ?? const [];
250
- expect(rows3, hasLength(1),
251
- reason: 'the remote projection serves user2\'s offline-written space item after converge');
252
- expect(((rows3.single as Map)['data'] as Map)['title'], 'Space Item from User2 (offline)');
253
- } finally {
254
- http.close(force: true);
255
- }
256
-
257
- // Verify custody is REAL on all layers
258
- final remoteHome12 = await cloud.remoteHead(home1.workspace);
259
- final remoteSpace12 = await cloud.remoteHead(spaceWs);
260
- final remoteProject12 = await cloud.remoteHead(projectWs);
261
- final remoteHome22 = await cloud.remoteHead(home2.workspace);
262
- final remoteSpace22 = await cloud.remoteHead(space2Ws);
263
-
264
- expect(remoteHome12.initialized, isTrue);
265
- expect(remoteHome12.commits, greaterThan(0));
266
- expect(remoteSpace12.initialized, isTrue,
267
- reason: 'converge established space\'s remote custody (home birth folded remotely)');
268
- expect(remoteSpace12.commits, greaterThan(0));
269
- expect(remoteProject12.initialized, isTrue,
270
- reason: 'converge established project\'s remote custody (home birth folded remotely)');
271
- expect(remoteProject12.commits, greaterThan(0));
272
- expect(remoteHome22.initialized, isTrue);
273
- expect(remoteHome22.commits, greaterThan(0));
274
- expect(remoteSpace22.initialized, isTrue);
275
- expect(remoteSpace22.commits, greaterThan(0));
276
-
277
- // Cross-user view: User1 queries its space and project, should still see its own writes
278
- final space1ViewFromUser1 = await space.query('spaceItemsByAuthor', {'author': _subject1});
279
- expect(space1ViewFromUser1, hasLength(1));
280
- expect(((space1ViewFromUser1.single['data'] as Map)['title']), 'Space Item 1 (offline)');
281
-
282
- // (Note: user2 doesn't have read access to user1's space by design, but both write to different workspaces,
283
- // so each user sees their own data in their own workspace)
284
- } finally {
285
- try { await harness1?.dispose(); } catch (_) {}
286
- try { await harness2?.dispose(); } catch (_) {}
287
- await cloud.stop();
288
- }
289
- }, timeout: const Timeout(Duration(minutes: 8)));
290
- }
@@ -1,13 +0,0 @@
1
- import 'package:nomos_client/src/nms_generated_runtime.dart';
2
- import 'package:test/test.dart';
3
-
4
- void main() {
5
- test('the private receiving-group address matches the JS/law formula', () {
6
- expect(nmsInternalReceivingGroupNameOf('user:abc-123'),
7
- 'nomos-rg-5094641143043f9281b6b9146cc345a254a5d3af');
8
- expect(
9
- nmsInternalReceivingGroupNameOf('user:abc-123').startsWith('nomos-rg-'),
10
- isTrue);
11
- expect(nmsInternalReceivingGroupNameOf('user:abc-123').length, 49);
12
- });
13
- }
@@ -1,107 +0,0 @@
1
- // LOCAL-ONLY SYNC MUST BE OBSERVATIONALLY INERT (co2's 0.75.1 blocker). Their repro, verbatim shape:
2
- // open a local:// session from a snapshot → offer → query returns the row → sync(admit: true) returns
3
- // {localOnly: true} → the SAME query must return the SAME row, and the head must be unchanged.
4
- // No branch/fold mutation, no network, identical results before and after.
5
- @TestOn('vm')
6
- library;
7
-
8
- import 'dart:convert';
9
- import 'dart:io';
10
-
11
- import 'package:nomos_client/src/nms_conformance_runtime.dart';
12
- import 'package:crypto/crypto.dart' show sha256;
13
- import 'package:test/test.dart';
14
-
15
- import '../../../bench/scale/fixtures/estate-birth/build/dart/src/generated/estate.application.dart';
16
-
17
- String get _root => '${Directory.current.path}/../..';
18
- String get _repoEntry => '$_root/cloud/web-client/runner/node-entry.ts';
19
- String get _deployJson =>
20
- '$_root/bench/scale/fixtures/estate-birth/build/estate-birth.law.usdz';
21
- String get _runtimeDir =>
22
- Platform.environment['NOMOS_DART_RUNTIME'] ??
23
- Platform.environment['NOMOS_OFFLINE_RUNTIME'] ??
24
- '${Platform.environment['HOME'] ?? ''}/.holon/runtime';
25
-
26
- const _workspace = 'estate-birth';
27
- const _owner = 'user:local-sync-inert';
28
-
29
- String? _missingPrereq() {
30
- if (!File(_repoEntry).existsSync())
31
- return 'in-repo runner not found at $_repoEntry';
32
- if (!File(_deployJson).existsSync()) {
33
- return 'compile the fixture: (cd bench/scale/fixtures/estate-birth && node ../../../../dsl/compile_package.mjs)';
34
- }
35
- if (!File('$_runtimeDir/holon-testmint.wasm').existsSync())
36
- return 'warm ~/.holon/runtime first';
37
- return null;
38
- }
39
-
40
- void main() {
41
- test(
42
- 'local-only sync is OBSERVATIONALLY INERT: offer -> query -> sync -> head unchanged -> same row',
43
- () async {
44
- final skip = _missingPrereq();
45
- if (skip != null) {
46
- markTestSkipped(skip);
47
- return;
48
- }
49
- final harness = await NomosTestHarness.cluster(
50
- workspaces: {
51
- _workspace: [_deployJson],
52
- },
53
- deviceKeyed: {_workspace},
54
- nodeEntry: _repoEntry,
55
- runtimeDir: _runtimeDir,
56
- );
57
- try {
58
- final session = harness.session(_workspace);
59
- final application =
60
- await nomosInternalBindEstateApplication(session, _owner);
61
-
62
- // 1-2. Offer a normal generated business call on the snapshot-opened local
63
- // session. This test is about local sync, not the separate child-workspace
64
- // birth ceremony.
65
- final bornAt = DateTime.now().toUtc().toIso8601String();
66
- final facts = SeedEstateWorkspacePayload(
67
- owner: _owner,
68
- estateName:
69
- 'estate-${sha256.convert(utf8.encode(_owner)).toString().substring(0, 40)}',
70
- bornAt: bornAt,
71
- );
72
- await application.estateOwners.seedEstateWorkspace(facts);
73
-
74
- // 3. Read through the generated, typed application surface.
75
- final before =
76
- await application.estateOwners.estatesByOwner(owner: _owner);
77
- expect(before, hasLength(1),
78
- reason: 'the offer must be readable BEFORE sync');
79
- final statusBefore = await session.custodyStatus();
80
- expect(statusBefore.pendingLocalIntents, greaterThan(0),
81
- reason: 'the local write is pending (nothing upstream to land on)');
82
-
83
- // 4. local-only sync — must be a no-op in EVERY observable respect.
84
- final summary = await session.sync(admit: true);
85
- expect(summary.localOnly, true,
86
- reason: 'a local:// session syncs as the documented no-op');
87
-
88
- // 5. THE REGRESSION (co2): custody unchanged, same query returns the SAME row.
89
- final statusAfter = await session.custodyStatus();
90
- expect(statusAfter.pendingLocalIntents, statusBefore.pendingLocalIntents,
91
- reason:
92
- 'local-only sync must not consume/move pending local intents');
93
- final after =
94
- await application.estateOwners.estatesByOwner(owner: _owner);
95
- expect(after, hasLength(1),
96
- reason:
97
- 'local-only sync must be observationally inert — the row must still read');
98
- expect(after.single.owner, before.single.owner,
99
- reason: 'the SAME typed business record remains visible');
100
- expect(after.single.estateName, before.single.estateName);
101
- expect(after.single.bornAt, before.single.bornAt);
102
- expect(after.single.ref.toString(), before.single.ref.toString());
103
- } finally {
104
- await harness.dispose();
105
- }
106
- }, timeout: const Timeout(Duration(minutes: 4)));
107
- }
@@ -1,78 +0,0 @@
1
- // The headless transport against the REAL node runner (`cloud/web-client/runner/node-entry.ts`):
2
- // protocol round-trip through NomosBridge without touching the cloud — ready handshake, typed refusal
3
- // on a pre-connect op, unknown-op error. The live connect path is covered by the env-gated smoke below.
4
- @TestOn('vm')
5
- library;
6
-
7
- import 'dart:io';
8
-
9
- import 'package:nomos_client/src/nms_generated_runtime.dart';
10
- import 'package:nomos_client/src/nms_conformance_runtime.dart';
11
- import 'package:test/test.dart';
12
-
13
- String _repoEntry() {
14
- // In-repo the runner lives beside us; consumers resolve it from node_modules instead.
15
- final here = Directory.current.path;
16
- final p = '$here/../../cloud/web-client/runner/node-entry.ts';
17
- if (!File(p).existsSync()) {
18
- throw StateError('node-entry.ts not found at $p');
19
- }
20
- return p;
21
- }
22
-
23
- void main() {
24
- late NodeRunnerTransport transport;
25
- late NomosBridge bridge;
26
-
27
- setUp(() async {
28
- transport = await NodeRunnerTransport.start(
29
- entry: _repoEntry(),
30
- // An explicit packaged entry is self-contained; a stale discovery root must not become its cwd.
31
- resolveFrom: '${Directory.systemTemp.path}/nomos-does-not-exist',
32
- );
33
- bridge = NomosBridge(transport);
34
- });
35
-
36
- tearDown(() async {
37
- await transport.dispose();
38
- });
39
-
40
- test('posts the ready handshake', () async {
41
- await bridge.ready.timeout(const Duration(seconds: 15));
42
- });
43
-
44
- test('a pre-connect position read fails before crossing the runtime boundary',
45
- () async {
46
- await bridge.ready.timeout(const Duration(seconds: 15));
47
- await expectLater(
48
- bridge.head(),
49
- throwsA(isA<StateError>().having((e) => e.message, 'message',
50
- contains('No Nomos workspace is connected'))),
51
- );
52
- });
53
-
54
- test('requests are id-correlated (two in flight)', () async {
55
- await bridge.ready.timeout(const Duration(seconds: 15));
56
- final a = bridge.head().then((_) => 'ok', onError: (_) => 'err');
57
- final b = bridge.metrics().then((_) => 'ok', onError: (_) => 'err');
58
- expect(await a, 'err');
59
- expect(await b, 'err');
60
- });
61
-
62
- test(
63
- 'LIVE smoke: connect + query against a real workspace',
64
- skip: Platform.environment['NOMOS_HEADLESS_E2E'] == null
65
- ? 'set NOMOS_HEADLESS_E2E=<workspace> (and optionally NOMOS_CLOUD) to run'
66
- : null,
67
- () async {
68
- final ws = Platform.environment['NOMOS_HEADLESS_E2E']!;
69
- final cloud = Platform.environment['NOMOS_CLOUD'] ?? 'https://nomos.cafe';
70
- await bridge.ready.timeout(const Duration(seconds: 15));
71
- await bridge.connect(
72
- cloud: cloud, workspace: ws, clientId: 'headless-test');
73
- final head = await bridge.head();
74
- expect(head, isNotEmpty);
75
- },
76
- timeout: const Timeout(Duration(minutes: 3)),
77
- );
78
- }
@@ -1,155 +0,0 @@
1
- // PARENT-SIGNER RECOVERY — the Dart surface leg (offline, the real runner + wasm).
2
- //
3
- // The co2 gap: after a parent custody rebirth the device key stays valid but the parent's chain no
4
- // longer carries `identity:<uid>#signer` — and there was NO published Dart recovery path. This proves
5
- // the new surfaces end-to-end over the node runner:
6
- // · `canAuthor` / `canAuthorTyped` — the read-only preflight returns the TYPED verdict
7
- // (cause `missing-signer-enrollment` → ParentSignerEnrollmentMissing) BEFORE any authoring;
8
- // · `ensureParentSignerEnrollment` — idempotent verify-and-repair on the parent (repairs when the
9
- // tuple is absent, pure no-op when live) with the signing directive DISCOVERED from the law;
10
- // · `parentSignerEnrollment` — the diagnostic posture read;
11
- // · after the repair, canAuthor flips ok and a SIGNED birthEstateWorkspace admits.
12
- // (The warranted-reborn-parent kernel legs — the exact refusal + the keyless enrollFirstDevice
13
- // recovery — are proven on the wasm by bench/scale/parent_signer_recovery.smoke.mjs.)
14
- @TestOn('vm')
15
- library;
16
-
17
- import 'dart:io';
18
-
19
- import 'package:nomos_client/src/nms_conformance_runtime.dart';
20
- import 'package:nomos_types/nomos_types.dart'
21
- show ParentSignerEnrollmentMissing, NomosRecommendedAction;
22
- import 'package:test/test.dart';
23
-
24
- String get _root => '${Directory.current.path}/../..';
25
- String get _repoEntry => '$_root/cloud/web-client/runner/node-entry.ts';
26
- String get _deploy =>
27
- '$_root/bench/scale/fixtures/estate-birth-signed/build/estate-birth-signed.law.usdz';
28
- String get _runtimeDir =>
29
- Platform.environment['NOMOS_DART_RUNTIME'] ??
30
- Platform.environment['NOMOS_OFFLINE_RUNTIME'] ??
31
- '${Platform.environment['HOME'] ?? ''}/.holon/runtime';
32
-
33
- const _ws = 'estate-birth-signed';
34
- const _uid = 'sample-owner';
35
- // THE co2 REGRESSION SHAPE: the principal arrives ALREADY `user:`-qualified (the captured-actor wire
36
- // form — exactly what NomosScope/co2 pass). KERNEL TRUTH: the gate checks
37
- // `identity:<captured principal VERBATIM>`, so every read/repair below must resolve the object to
38
- // `identity:user:<uid>` — a stripped `identity:<uid>` is the normalization bug this test pins.
39
- const _principal = 'user:$_uid';
40
-
41
- String? _missingPrereq() {
42
- if (!File(_repoEntry).existsSync()) return 'in-repo runner not found at $_repoEntry';
43
- if (!File(_deploy).existsSync()) {
44
- return 'compile the fixture: cd bench/scale/fixtures/estate-birth-signed && node ../../../../dsl/compile_package.mjs';
45
- }
46
- if (!File('$_runtimeDir/holon-testmint.wasm').existsSync()) {
47
- return 'warm ~/.holon/runtime first (run any `githolon` command once)';
48
- }
49
- return null;
50
- }
51
-
52
- void main() {
53
- test(
54
- 'canAuthor preflights the missing parent signer TYPED, ensureParentSignerEnrollment repairs it, '
55
- 'and the signed birth then admits', () async {
56
- final skip = _missingPrereq();
57
- if (skip != null) {
58
- markTestSkipped(skip);
59
- return;
60
- }
61
- final harness = await NomosTestHarness.ephemeral(
62
- lawUsdzPaths: [_deploy],
63
- nodeEntry: _repoEntry,
64
- runtimeDir: _runtimeDir,
65
- workspace: _ws,
66
- );
67
- try {
68
- final session = harness.bridge.session(_ws);
69
- final device = await session.mintDeviceKey();
70
- final pub = device['public']!.toString();
71
- final secret = device['secret']!.toString();
72
-
73
- // devicePublicOf re-derives the public half from the persisted seed (the Dart key store keeps
74
- // only the secret) — the recovery lanes depend on this.
75
- expect(await session.devicePublicOf(secret), pub);
76
-
77
- // 1. THE PREFLIGHT, broken state: the verdict names the failing premise BEFORE any authoring.
78
- final v0 = await session.canAuthor('birthEstateWorkspace',
79
- ws: _ws, principal: _principal, publicKey: pub);
80
- expect(v0['ok'], isNot(true));
81
- expect(v0['cause'], 'missing-signer-enrollment');
82
- expect(v0['actor'], _principal,
83
- reason: 'the canonical actor is the principal VERBATIM — never stripped, never double-prefixed');
84
- expect((v0['keyHash'] ?? '').toString(), hasLength(64));
85
-
86
- // ...and the TYPED variant throws the ONE error with the ONE remedy.
87
- await expectLater(
88
- session.canAuthorTyped('birthEstateWorkspace', ws: _ws, principal: _principal, publicKey: pub),
89
- throwsA(isA<ParentSignerEnrollmentMissing>()
90
- .having((e) => e.recommendedAction, 'recommendedAction',
91
- NomosRecommendedAction.ensureParentSignerEnrollment)
92
- .having((e) => e.workspace, 'workspace', _ws)),
93
- );
94
-
95
- // 2. THE POSTURE READ agrees (diagnostic; nothing authored).
96
- final posture0 = await session.parentSignerEnrollment(
97
- parent: _ws, principal: _principal, publicKey: pub);
98
- expect(posture0['enrolled'], false);
99
- // KERNEL TRUTH (executor::signer_object): the object is identity:<captured principal VERBATIM>
100
- // — `identity:user:<uid>`, NOT a stripped `identity:<uid>` (the co2 normalization bug).
101
- expect(posture0['object'], 'identity:$_principal');
102
- expect(posture0['relation'], 'signer');
103
-
104
- // 3. THE REPAIR — idempotent; the signing directive is DISCOVERED from the installed law
105
- // (this fixture composes it under domain "home", NOT the framework "workspaces").
106
- final r1 = await session.ensureParentSignerEnrollment(
107
- // era-0: BOTH domains compose enrollSigner (the estate is signer-ready too) — name the parent's.
108
- parent: _ws, principal: _principal, publicKey: pub, authorSecret: secret, domain: 'home');
109
- expect(r1['enrolled'], true);
110
- expect(r1['repaired'], true);
111
- expect(r1['domain'], 'home',
112
- reason: 'discovery must find the tenant-composed domain, never assume "workspaces"');
113
-
114
- // 4. Re-run = pure no-op (the idempotence contract).
115
- final r2 = await session.ensureParentSignerEnrollment(
116
- parent: _ws, principal: _principal, publicKey: pub, authorSecret: secret, domain: 'home');
117
- expect(r2['enrolled'], true);
118
- expect(r2['repaired'], false);
119
-
120
- // 5. The preflight flips OK…
121
- final v1 = await session.canAuthor('birthEstateWorkspace',
122
- ws: _ws, principal: _principal, publicKey: pub);
123
- expect(v1['ok'], true, reason: 'causes: ${v1['causes']}');
124
-
125
- // 5b. co2's step-5 regression VERBATIM: immediately after repaired:true, the POSTURE read (with
126
- // the SAME user:-qualified principal) reports enrolled:true against the canonical object.
127
- final posture1 = await session.parentSignerEnrollment(
128
- parent: _ws, principal: _principal, publicKey: pub);
129
- expect(posture1['enrolled'], true,
130
- reason: 'the repair wrote identity:$_principal — a second normalizer that strips user: '
131
- 'would read identity:$_uid and wrongly report missing-signer-enrollment');
132
- expect(posture1['object'], 'identity:$_principal');
133
-
134
- // 6. …and the SIGNED sibling birth ADMITS (per-offer signing key — the repaired device).
135
- final lawHash = harness.lawHashes(_ws).first;
136
- final outcome = await session.offerCreates(
137
- domain: 'home',
138
- directiveId: 'birthEstateWorkspace',
139
- payload: {
140
- 'owner': 'user:$_uid',
141
- 'frameworkHash': harness.frameworkHash,
142
- 'lawHash': lawHash,
143
- 'bornAt': '2026-07-12T00:00:00Z',
144
- },
145
- actor: 'user:$_uid',
146
- authorSecret: secret,
147
- );
148
- expect(outcome.head, isNotEmpty);
149
- expect(outcome.born, isNotEmpty,
150
- reason: 'the estate child births after the signer repair');
151
- } finally {
152
- await harness.dispose();
153
- }
154
- }, timeout: const Timeout(Duration(minutes: 3)));
155
- }