@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,600 +0,0 @@
1
- // ADVERSARIAL FINDER — MULTI-WORKSPACE REALM INTERFERENCE (one engine, many sessions).
2
- // Hammers the cross-session seams of the local holarchy plane (co2's production shape: a
3
- // dozen+ live workspaces, mixed laws) through the REAL runner + REAL kernel, fully offline:
4
- // 1. concurrent offers to different sessions (no cross-chain bleed, independent heads)
5
- // 2. watches on A while B floods (no tick bleed / starvation, per-subscription ordering)
6
- // 3. acquireSession racing a FAILED connect (the phantom-session window)
7
- // 4. pendingConflicts/activityLog isolation across same-named queries / sibling floods
8
- // 5. session re-connect (atomic replace) during in-flight offers — nothing hangs, typed only
9
- // 6. born child resident-then-session — one consistent view
10
- // 7. error isolation — one session's refusal never poisons siblings
11
- // 8. runner-level LRU (maxMounted) — watch survival + state preservation across park/remount
12
- //
13
- // A FAILING expectation here is a FINDING (evidence of a seam bug), not necessarily a
14
- // regression gate. Timing assertions use generous CI margins.
15
- @TestOn('vm')
16
- @Timeout(Duration(minutes: 12))
17
- library;
18
-
19
- import 'dart:async';
20
- import 'dart:convert';
21
- import 'dart:io';
22
-
23
- import 'package:nomos_client/src/nms_conformance_runtime.dart';
24
- import 'package:nomos_types/nomos_kernel_protocol.dart' as protocol;
25
- import 'package:nomos_types/src/nomos_ref.dart' show NmsInternalNomosRefs;
26
- import 'package:test/test.dart';
27
-
28
- String get _root => '${Directory.current.path}/../..';
29
- String get _repoEntry => '$_root/cloud/web-client/runner/node-entry.ts';
30
- String get _installer => '$_root/cloud/web-client/runner/offline-install.ts';
31
- String get _guestbook =>
32
- '$_root/examples/guestbook/build/guestbook.law.usdz';
33
- String get _surfaced =>
34
- '$_root/bench/scale/fixtures/surfaced/build/surfaced.law.usdz';
35
- String get _attributed =>
36
- '$_root/bench/scale/fixtures/attributed/build/attributed.law.usdz';
37
- String get _estateBirth =>
38
- '$_root/bench/scale/fixtures/estate-birth/build/estate-birth.law.usdz';
39
- String get _runtimeDir =>
40
- Platform.environment['NOMOS_DART_RUNTIME'] ??
41
- Platform.environment['NOMOS_OFFLINE_RUNTIME'] ??
42
- '${Platform.environment['HOME'] ?? ''}/.holon/runtime';
43
-
44
- String? _missingPrereq() {
45
- for (final p in [
46
- _repoEntry,
47
- _installer,
48
- _guestbook,
49
- _surfaced,
50
- _attributed,
51
- _estateBirth
52
- ]) {
53
- if (!File(p).existsSync())
54
- return 'missing $p (compile the fixture / example first)';
55
- }
56
- if (!File('$_runtimeDir/holon-testmint.wasm').existsSync() ||
57
- !File('$_runtimeDir/bootstrap.law.usdz').existsSync() ||
58
- !File('$_runtimeDir/nomos.law.usdz').existsSync()) {
59
- return 'warm ~/.holon/runtime first (run any `githolon` command once)';
60
- }
61
- return null;
62
- }
63
-
64
- String _nowIso() => DateTime.now().toUtc().toIso8601String();
65
-
66
- Future<String> _sign(NomosSession s, String author, String message) =>
67
- s.offerDirective(
68
- domain: 'guestbook',
69
- directiveId: 'signGuestbook',
70
- payload: {
71
- 'author': author,
72
- 'message': message,
73
- 'mood': 'delighted',
74
- 'signedAt': _nowIso()
75
- },
76
- actor: author);
77
-
78
- void main() {
79
- final skip = _missingPrereq();
80
-
81
- group('shared cluster (6 workspaces, mixed laws)', () {
82
- late NomosTestHarness harness;
83
- late NomosBridge plane;
84
-
85
- setUpAll(() async {
86
- if (skip != null) return;
87
- harness = await NomosTestHarness.cluster(
88
- workspaces: {
89
- 'home-birth': [_estateBirth],
90
- 'surf': [_surfaced],
91
- 'attr': [_attributed],
92
- 'gb-1': [_guestbook],
93
- 'gb-2': [_guestbook],
94
- 'gb-3': [_guestbook],
95
- },
96
- nodeEntry: _repoEntry,
97
- runtimeDir: _runtimeDir,
98
- );
99
- plane = harness.bridge;
100
- });
101
-
102
- tearDownAll(() async {
103
- if (skip != null) return;
104
- await harness.dispose();
105
- });
106
-
107
- test(
108
- '1. concurrent offers to different sessions: no cross-chain bleed, independent heads',
109
- () async {
110
- if (skip != null) return markTestSkipped(skip);
111
- const gbs = ['gb-1', 'gb-2', 'gb-3'];
112
- final sw = Stopwatch()..start();
113
- final futs = <Future<Object?>>[
114
- for (final ws in gbs)
115
- for (var i = 0; i < 4; i++)
116
- _sign(harness.session(ws), 'author-$ws', 'm$i'),
117
- for (var i = 0; i < 4; i++)
118
- harness.session('surf').offerDirective(
119
- domain: 'docs',
120
- directiveId: 'createDoc',
121
- payload: {'owner': 'owner-surf', 'title': 'doc-$i'},
122
- actor: 'owner-surf'),
123
- for (var i = 0; i < 4; i++)
124
- harness.session('attr').offerDirective(
125
- domain: 'sites',
126
- directiveId: 'createSite',
127
- payload: {'owner': 'owner-attr', 'name': 'site-$i'},
128
- actor: 'owner-attr'),
129
- ];
130
- final heads =
131
- await Future.wait(futs); // 20 concurrent offers across 5 sessions
132
- sw.stop();
133
- expect(heads, everyElement(isNotNull));
134
- // ack latency stays sane under concurrency (generous CI margin)
135
- expect(sw.elapsed, lessThan(const Duration(seconds: 90)),
136
- reason: '20 concurrent offers should settle well under 90s');
137
-
138
- // each chain holds ONLY its own writes
139
- for (final ws in gbs) {
140
- final mine = await harness
141
- .session(ws)
142
- .query('entriesByAuthor', {'author': 'author-$ws'});
143
- expect(mine, hasLength(4), reason: '$ws holds its own 4 writes');
144
- for (final other in gbs.where((o) => o != ws)) {
145
- final theirs = await harness
146
- .session(ws)
147
- .query('entriesByAuthor', {'author': 'author-$other'});
148
- expect(theirs, isEmpty,
149
- reason:
150
- "$ws must not hold $other's writes (cross-session bleed)");
151
- }
152
- }
153
- expect(
154
- await harness
155
- .session('surf')
156
- .query('docsByOwner', {'owner': 'owner-surf'}),
157
- hasLength(4));
158
- expect(
159
- await harness
160
- .session('attr')
161
- .query('sitesByOwner', {'owner': 'owner-attr'}),
162
- hasLength(4));
163
-
164
- // heads are independent per session
165
- final tips = <String>{};
166
- for (final ws in [...gbs, 'surf', 'attr']) {
167
- tips.add(await harness.session(ws).head());
168
- }
169
- expect(tips, hasLength(5),
170
- reason: 'every session has its own ledger tip');
171
- });
172
-
173
- test(
174
- '2. a watch on session A fires while session B floods (no starvation, no bleed, ordered)',
175
- () async {
176
- if (skip != null) return markTestSkipped(skip);
177
- final a = harness.session('gb-1');
178
- final b = harness.session('gb-2');
179
- final idle = harness.session('attr');
180
-
181
- final emissionsA = <int>[];
182
- final emissionsIdle = <int>[];
183
- final subA = a.watch('entriesByAuthor', {'author': 'watched'}).listen(
184
- (rows) => emissionsA.add(rows.length));
185
- final subIdle = idle
186
- .watch('sitesByOwner', {'owner': 'nobody-here'}).listen(
187
- (rows) => emissionsIdle.add(rows.length));
188
- await Future<void>.delayed(
189
- const Duration(milliseconds: 600)); // initial emissions
190
- final idleBase = emissionsIdle.length;
191
-
192
- // B floods 25 writes while A takes 3 interleaved writes
193
- final flood = () async {
194
- for (var i = 0; i < 25; i++) {
195
- await _sign(b, 'flooder', 'f$i');
196
- }
197
- }();
198
- final aWrites = () async {
199
- for (var i = 0; i < 3; i++) {
200
- await Future<void>.delayed(const Duration(milliseconds: 60));
201
- await _sign(a, 'watched', 'w$i');
202
- }
203
- }();
204
- await Future.wait([flood, aWrites]);
205
-
206
- // A's watch must reach 3 rows promptly despite the sibling flood (generous margin)
207
- final deadline = DateTime.now().add(const Duration(seconds: 15));
208
- while (DateTime.now().isBefore(deadline) &&
209
- (emissionsA.isEmpty || emissionsA.last < 3)) {
210
- await Future<void>.delayed(const Duration(milliseconds: 150));
211
- }
212
- await subA.cancel();
213
- await subIdle.cancel();
214
- expect(emissionsA.isNotEmpty && emissionsA.last == 3, isTrue,
215
- reason:
216
- "A's watch starved or missed rows under B's flood: $emissionsA");
217
- for (var i = 1; i < emissionsA.length; i++) {
218
- expect(emissionsA[i], greaterThanOrEqualTo(emissionsA[i - 1]),
219
- reason: 'per-subscription ordering must be monotonic: $emissionsA');
220
- }
221
- expect(emissionsIdle.length, idleBase,
222
- reason:
223
- "the idle sibling watch got spurious emissions from B's flood: $emissionsIdle");
224
- });
225
-
226
- test('3a. follow(ref) + racing acquireSession during a sibling offer flood',
227
- () async {
228
- if (skip != null) return markTestSkipped(skip);
229
- final flood = () async {
230
- for (var i = 0; i < 10; i++) {
231
- await _sign(harness.session('gb-3'), 'race-flood', 'f$i');
232
- }
233
- }();
234
- // follow an already-open sibling mid-flood → same handle, functional
235
- final followed = await plane.follow(NmsInternalNomosRefs.create<Object?>(
236
- workspace: 'gb-1', type: 'nomos.Workspace', id: 'gb-1'));
237
- expect(followed.workspace, 'gb-1');
238
- expect(await followed.query('entriesByAuthor', {'author': 'nobody'}),
239
- isEmpty);
240
-
241
- // concurrent acquireSession single-flights to the SAME handle
242
- final pair = await Future.wait([
243
- plane.acquireSession('gb-2'),
244
- plane.acquireSession('gb-2'),
245
- ]);
246
- expect(identical(pair[0], pair[1]), isTrue,
247
- reason:
248
- 'two concurrent acquireSession calls must yield the same session');
249
- await flood;
250
- });
251
-
252
- test(
253
- '3b. FINDER: a FAILED connect must not leave a phantom session acquireSession trusts',
254
- () async {
255
- if (skip != null) return markTestSkipped(skip);
256
- // a garbage restoreFrom makes the runner's connect throw AFTER the Dart bridge has
257
- // (prematurely?) registered the session name on the plane.
258
- Object? err;
259
- try {
260
- await plane.connect(
261
- workspace: 'phantom-ws',
262
- session: 'phantom-ws',
263
- restoreFrom: '%%%not-base64%%%');
264
- } catch (e) {
265
- err = e;
266
- }
267
- expect(err, isNotNull, reason: 'the corrupt-restore connect must throw');
268
-
269
- // CORRECT behavior: acquireSession must either ask for credentials, throw a named
270
- // StateError, or return a session whose ops actually work. Returning a handle whose
271
- // every op refuses "session not connected" is the phantom-session bug.
272
- var credentialsAsked = false;
273
- Object? acquireOutcome;
274
- try {
275
- final s = await plane.acquireSession('phantom-ws',
276
- ifAbsentCredentials: () async {
277
- credentialsAsked = true;
278
- throw StateError('credentials path taken (correct) — stop here');
279
- });
280
- // it returned a session: its ops must work, else it's a dead phantom handle
281
- await s.query('entriesByAuthor', {'author': 'x'});
282
- acquireOutcome = 'live session';
283
- } catch (e) {
284
- acquireOutcome = e;
285
- }
286
- final phantom = !credentialsAsked &&
287
- acquireOutcome is! StateError &&
288
- acquireOutcome != 'live session';
289
- expect(phantom, isFalse,
290
- reason:
291
- 'PHANTOM SESSION: after a failed connect, acquireSession("phantom-ws") '
292
- 'returned a dead handle (ops refuse) without asking for credentials — '
293
- 'observed: $acquireOutcome');
294
- });
295
-
296
- test(
297
- '4. pendingConflicts/activityLog on A are unaffected by B (same-named queries isolated)',
298
- () async {
299
- if (skip != null) return markTestSkipped(skip);
300
- final surf = harness.session('surf');
301
- final before = await surf.pendingConflicts();
302
-
303
- // sibling activity: a guestbook flood + surfaced-relevant field writes on ANOTHER session's law
304
- for (var i = 0; i < 10; i++) {
305
- await _sign(harness.session('gb-1'), 'iso-flood', 'f$i');
306
- }
307
- final after = await surf.pendingConflicts();
308
- expect(after, equals(before),
309
- reason:
310
- "surf's pendingConflicts moved on a sibling's writes (cache/key bleed)");
311
-
312
- // activityLog rows belong to the session's OWN chain only
313
- final actGb2 = await harness.session('gb-2').activityLog(limit: 100);
314
- final foreign = actGb2.where((r) =>
315
- r['directiveId'] == 'createSite' ||
316
- r['directiveId'] == 'createDoc' ||
317
- (r['payload'] is Map &&
318
- (r['payload'] as Map)['author'] == 'iso-flood'));
319
- expect(foreign, isEmpty,
320
- reason:
321
- "gb-2's activityLog carries sibling-session intents: ${foreign.toList()}");
322
-
323
- // the SAME query id with the SAME params on two sessions answers per-session
324
- final q1 = await harness
325
- .session('gb-1')
326
- .query('entriesByAuthor', {'author': 'iso-flood'});
327
- final q2 = await harness
328
- .session('gb-2')
329
- .query('entriesByAuthor', {'author': 'iso-flood'});
330
- expect(q1, hasLength(10));
331
- expect(q2, isEmpty,
332
- reason:
333
- 'same-named query with same params leaked rows across sessions (cache keying)');
334
- });
335
-
336
- test(
337
- '5. re-connect (atomic replace) during in-flight offers: everything settles typed',
338
- () async {
339
- if (skip != null) return markTestSkipped(skip);
340
- final s = harness.session('gb-3');
341
- // fire 10 offers WITHOUT awaiting, then immediately replace the session (new clientId
342
- // → identity change → dispose-then-connect under the hood)
343
- final futs = [
344
- for (var i = 0; i < 10; i++)
345
- _sign(s, 'replace-race', 'r$i')
346
- .then<Object?>((h) => 'ok:$h', onError: (Object e) => e)
347
- ];
348
- await plane.connect(
349
- workspace: 'gb-3', session: 'gb-3', clientId: 'replacer-1');
350
-
351
- final settled = await Future.wait(futs).timeout(
352
- const Duration(seconds: 60),
353
- onTimeout: () => throw StateError(
354
- 'offers in flight across an atomic replace HUNG (never settled)'));
355
- // every outcome is either an admitted head or a TYPED error — never a hang, never null
356
- for (final r in settled) {
357
- expect(r, isNotNull);
358
- if (r is! String) {
359
- expect(r, isA<Exception>(),
360
- reason: 'a race loser must fail typed, got: $r');
361
- }
362
- }
363
-
364
- // the replaced session is consistent: chain verifies, reads serve, writes admit
365
- final v = await harness.session('gb-3').verifyChain();
366
- expect(v['valid'], isTrue,
367
- reason:
368
- 'in-flight offers interleaved with the atomic replace corrupted the chain: $v');
369
- final admitted = settled.whereType<String>().length;
370
- final rows = await harness
371
- .session('gb-3')
372
- .query('entriesByAuthor', {'author': 'replace-race'});
373
- expect(rows.length, admitted,
374
- reason:
375
- '$admitted offers acked ok but ${rows.length} rows visible after the replace '
376
- '(an acked write was lost or duplicated across the replace window)');
377
- await _sign(harness.session('gb-3'), 'post-replace', 'alive');
378
- expect(
379
- await harness
380
- .session('gb-3')
381
- .query('entriesByAuthor', {'author': 'post-replace'}),
382
- hasLength(1));
383
- // siblings untouched by gb-3's replace
384
- expect(
385
- await harness
386
- .session('gb-1')
387
- .query('entriesByAuthor', {'author': 'nobody'}),
388
- isEmpty);
389
- });
390
-
391
- test(
392
- '6. born child resident-then-session: one consistent view, replace-safe',
393
- () async {
394
- if (skip != null) return markTestSkipped(skip);
395
- final fw = harness.frameworkHash;
396
- final law = harness.lawHashes('home-birth').first;
397
- final outcome = await harness.session('home-birth').offerCreates(
398
- domain: 'home',
399
- directiveId: 'birthEstateWorkspace',
400
- payload: {
401
- 'owner': 'adv-owner',
402
- 'frameworkHash': fw,
403
- 'lawHash': law,
404
- 'bornAt': _nowIso(),
405
- },
406
- actor: 'adv-owner',
407
- );
408
- expect(outcome.born, hasLength(1));
409
- expect(outcome.birthOutcomes.first.status,
410
- NomosBirthOutcomeStatus.BIRTH_OUTCOME_STATUS_BORN);
411
- final child = outcome.born.single;
412
-
413
- // the child is resident BEFORE any session opens it — listWorkspaces sees it
414
- final listedPre = await plane.listWorkspaces();
415
- expect(listedPre.map((workspace) => workspace.workspace), contains(child),
416
- reason:
417
- 'a born child is resident on the plane before it is connected');
418
-
419
- // open it as a session (mount in place) and read the genesis seed
420
- await plane.connect(workspace: child, session: child);
421
- final rows1 = await plane
422
- .session(child)
423
- .query('estatesByOwner', {'owner': 'adv-owner'});
424
- expect(rows1, hasLength(1));
425
-
426
- // write into the child, then RE-open it with a new clientId (identity replace on a
427
- // born-resident workspace) — the write must survive (one holon's truth, not two)
428
- await plane
429
- .session(child)
430
- .offerDirective(
431
- domain: 'estate',
432
- directiveId: 'renameEstate',
433
- payload: {'estateName': child, 'newName': 'renamed'},
434
- actor: 'adv-owner')
435
- .catchError(
436
- (Object _) => ''); // directive may not exist — best-effort
437
- await _noThrow(() => plane.connect(
438
- workspace: child, session: child, clientId: 'child-re'));
439
- final rows2 = await plane
440
- .session(child)
441
- .query('estatesByOwner', {'owner': 'adv-owner'});
442
- expect(rows2, hasLength(1),
443
- reason:
444
- 're-opening a born-resident child must present the SAME holon truth');
445
-
446
- // the parent is unaffected
447
- expect(
448
- await plane
449
- .session('home-birth')
450
- .verifyChain()
451
- .then((v) => v['valid']),
452
- isTrue);
453
- });
454
-
455
- test('7. error isolation: a refusal on one session never poisons siblings',
456
- () async {
457
- if (skip != null) return markTestSkipped(skip);
458
- // unknown directive → typed refusal
459
- await expectLater(
460
- harness.session('gb-1').offerDirective(
461
- domain: 'guestbook',
462
- directiveId: 'noSuchDirective',
463
- payload: {'x': 1}),
464
- throwsA(anything));
465
- // schema-violating payload → typed refusal
466
- await expectLater(
467
- harness.session('attr').offerDirective(
468
- domain: 'sites',
469
- directiveId: 'createSite',
470
- payload: {'owner': '', 'name': ''}),
471
- throwsA(anything));
472
- // unknown query → typed refusal, not a hang
473
- await expectLater(
474
- harness.session('surf').query('noSuchQuery', {'k': 'v'}),
475
- throwsA(anything));
476
-
477
- // every sibling still serves reads AND admits writes
478
- for (final ws in ['gb-1', 'gb-2', 'gb-3', 'surf', 'attr', 'home-birth']) {
479
- final h = await harness.session(ws).head();
480
- expect(h, isNotEmpty,
481
- reason: '$ws must still answer after a sibling refusal');
482
- }
483
- await _sign(harness.session('gb-2'), 'healthy', 'still-alive');
484
- expect(
485
- await harness
486
- .session('gb-2')
487
- .query('entriesByAuthor', {'author': 'healthy'}),
488
- hasLength(1));
489
- });
490
- });
491
-
492
- group('runner-level LRU (maxMounted: 2)', () {
493
- test('8. FINDER: watches + session state across LRU park/remount',
494
- () async {
495
- if (skip != null) return markTestSkipped(skip);
496
- final workdir = await Directory.systemTemp.createTemp('adv-lru-');
497
- NodeRunnerTransport? transport;
498
- try {
499
- // build 3 guestbook snapshots via the proven offline install lane
500
- final snaps = <String, String>{};
501
- for (final ws in ['lru-1', 'lru-2', 'lru-3']) {
502
- final out = '${workdir.path}/$ws.bin';
503
- final r = await Process.run('node', [
504
- _installer,
505
- '--law',
506
- _guestbook,
507
- '--workspace',
508
- ws,
509
- '--out',
510
- out,
511
- '--runtime',
512
- _runtimeDir,
513
- ]);
514
- expect(r.exitCode, 0,
515
- reason: 'offline install of $ws failed: ${r.stderr}');
516
- snaps[ws] = base64Encode(await File(out).readAsBytes());
517
- }
518
- transport = await NodeRunnerTransport.start(
519
- entry: _repoEntry,
520
- node: 'node',
521
- environment: {'NOMOS_OFFLINE': '1'});
522
- final bridge = NomosBridge(transport);
523
- await bridge.ready.timeout(const Duration(seconds: 30));
524
-
525
- // FIRST connect fixes the realm budget: maxMounted 2
526
- await bridge.connect(
527
- workspace: 'lru-1',
528
- session: 'lru-1',
529
- restoreFrom: snaps['lru-1'],
530
- allowUnborn: true,
531
- maxMounted: 2);
532
- final s1 = bridge.session('lru-1');
533
- await _sign(s1, 'survivor', 'before-eviction'); // un-synced local write
534
-
535
- final emissions = <int>[];
536
- Object? watchError;
537
- final sub = s1.watch('entriesByAuthor', {'author': 'survivor'}).listen(
538
- (rows) => emissions.add(rows.length),
539
- onError: (Object e) => watchError = e);
540
- await Future<void>.delayed(const Duration(milliseconds: 800));
541
- expect(emissions.isNotEmpty && emissions.last == 1, isTrue,
542
- reason:
543
- 'precondition: the watch saw the pre-eviction row: $emissions');
544
-
545
- // mount two more workspaces → lru-1 is LRU-evicted (parked)
546
- await bridge.connect(
547
- workspace: 'lru-2',
548
- session: 'lru-2',
549
- restoreFrom: snaps['lru-2'],
550
- allowUnborn: true);
551
- await bridge.connect(
552
- workspace: 'lru-3',
553
- session: 'lru-3',
554
- restoreFrom: snaps['lru-3'],
555
- allowUnborn: true);
556
- final listed = await bridge.listWorkspaces();
557
- final l1 = listed.firstWhere((workspace) => workspace.workspace == 'lru-1');
558
- expect(l1.residency, protocol.WorkspaceResidency.WORKSPACE_RESIDENCY_PARKED,
559
- reason: 'lru-1 should be LRU-parked: $listed');
560
-
561
- // (a) session ops transparently remount from the parked snapshot, state intact
562
- final rows = await s1.query('entriesByAuthor', {'author': 'survivor'});
563
- expect(rows, hasLength(1),
564
- reason:
565
- 'the un-synced local write must survive the park/remount cycle');
566
-
567
- // (b) the watch must SURVIVE the eviction+remount (or at least error TYPED —
568
- // a silently-dead stream is the bug)
569
- await _sign(s1, 'survivor', 'after-remount');
570
- final deadline = DateTime.now().add(const Duration(seconds: 10));
571
- while (DateTime.now().isBefore(deadline) &&
572
- (emissions.isEmpty || emissions.last < 2) &&
573
- watchError == null) {
574
- await Future<void>.delayed(const Duration(milliseconds: 200));
575
- }
576
- await sub.cancel();
577
- final died =
578
- (emissions.isEmpty || emissions.last < 2) && watchError == null;
579
- expect(died, isFalse,
580
- reason:
581
- 'SILENT WATCH DEATH: the lru-1 watch stopped emitting after an LRU '
582
- 'eviction+remount, with NO error event — emissions=$emissions '
583
- '(a Flutter app bound to this stream shows stale data forever)');
584
- } finally {
585
- try {
586
- await transport?.dispose();
587
- } catch (_) {}
588
- try {
589
- await workdir.delete(recursive: true);
590
- } catch (_) {}
591
- }
592
- });
593
- });
594
- }
595
-
596
- Future<void> _noThrow(Future<void> Function() fn) async {
597
- try {
598
- await fn();
599
- } catch (_) {}
600
- }