@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
@@ -0,0 +1,16 @@
1
+ import 'package:nomos_client/src/reception/event_desk.dart';
2
+ import 'package:test/test.dart';
3
+
4
+ void main() {
5
+ test('reception closes while presentation observers are paused', () async {
6
+ final desk = ReceptionEventDesk();
7
+ final copies = desk.copyChanges.listen((_) {})..pause();
8
+ final application = desk.syncStatuses.applicationStream.listen((_) {})
9
+ ..pause();
10
+
11
+ await desk.close().timeout(const Duration(seconds: 1));
12
+
13
+ await copies.cancel();
14
+ await application.cancel();
15
+ });
16
+ }
@@ -0,0 +1,23 @@
1
+ import 'package:fixnum/fixnum.dart';
2
+ import 'package:nomos_client/src/reception/progress_desk.dart';
3
+ import 'package:nomos_types/nomos_kernel_protocol.dart' as protocol;
4
+ import 'package:test/test.dart';
5
+
6
+ void main() {
7
+ test('reception preserves the peer trace id and measured duration', () {
8
+ final span = protocol.RuntimeSpan(
9
+ traceId: List<int>.generate(16, (index) => index),
10
+ spanId: List<int>.filled(8, 1),
11
+ name: 'peer.correspondence.inspect-outbox',
12
+ startedUnixNanos: Int64(1700000000000000000),
13
+ durationNanos: Int64(22000000),
14
+ );
15
+
16
+ final observed = NomosSpanEvent.fromRuntime(span, session: 'home');
17
+
18
+ expect(observed.traceId, '000102030405060708090a0b0c0d0e0f');
19
+ expect(observed.startedAtMillis, 1700000000000);
20
+ expect(observed.endedAtMillis, 1700000000022);
21
+ expect(observed.session, 'home');
22
+ });
23
+ }
@@ -0,0 +1,156 @@
1
+ // One-shot reads wait out a copy that is not ready yet; nothing else waits.
2
+ import 'dart:async';
3
+ import 'dart:io';
4
+
5
+ import 'package:nomos_client/src/replica_readiness.dart';
6
+ import 'package:nomos_types/nomos_types.dart'
7
+ show NomosUnavailable, NomosUnavailableReason;
8
+ import 'package:test/test.dart';
9
+
10
+ NomosUnavailable _unavailable(
11
+ NomosUnavailableReason reason, {
12
+ bool retryable = true,
13
+ }) =>
14
+ NomosUnavailable(
15
+ reason: reason,
16
+ workspace: 'catalogue-cars',
17
+ retryable: retryable,
18
+ );
19
+
20
+ void main() {
21
+ setUp(() {
22
+ nmsReplicaReadyFirstDelay = const Duration(milliseconds: 1);
23
+ nmsReplicaReadyGiveUpAfter = const Duration(minutes: 10);
24
+ });
25
+
26
+ test('a copy still arriving is asked again until it answers', () async {
27
+ var reads = 0;
28
+ final rows = await nmsUntilReplicaReady(() async {
29
+ reads++;
30
+ if (reads == 1) {
31
+ throw _unavailable(NomosUnavailableReason.workspaceOpening);
32
+ }
33
+ if (reads < 4) {
34
+ throw _unavailable(NomosUnavailableReason.localReplicaNotReady);
35
+ }
36
+ return ['Hatchback', 'Saloon'];
37
+ });
38
+ expect(rows, ['Hatchback', 'Saloon']);
39
+ expect(reads, 4);
40
+ });
41
+
42
+ for (final (label, error) in [
43
+ (
44
+ 'a non-retryable unavailable answer',
45
+ _unavailable(NomosUnavailableReason.localReplicaNotReady,
46
+ retryable: false),
47
+ ),
48
+ (
49
+ 'a remote-side unavailable answer',
50
+ _unavailable(NomosUnavailableReason.remoteUnreachable),
51
+ ),
52
+ ('any other error', StateError('NMS-KERNEL-CALL: query failed')),
53
+ ]) {
54
+ test('$label is reported at once', () async {
55
+ var reads = 0;
56
+ await expectLater(
57
+ nmsUntilReplicaReady<void>(() async {
58
+ reads++;
59
+ throw error;
60
+ }),
61
+ throwsA(same(error)),
62
+ );
63
+ expect(reads, 1);
64
+ });
65
+ }
66
+
67
+ test('a copy that never arrives still ends in an error', () async {
68
+ nmsReplicaReadyGiveUpAfter = const Duration(milliseconds: 20);
69
+ await expectLater(
70
+ nmsUntilReplicaReady<void>(
71
+ () async =>
72
+ throw _unavailable(NomosUnavailableReason.localReplicaNotReady),
73
+ ),
74
+ throwsA(isA<NomosUnavailable>()),
75
+ );
76
+ });
77
+
78
+ group('a completed sync wakes the wait', () {
79
+ // The runtime asks for a long pause; only the sync event can end it early.
80
+ NomosUnavailable notReady(String workspace) => NomosUnavailable(
81
+ reason: NomosUnavailableReason.localReplicaNotReady,
82
+ workspace: workspace,
83
+ retryable: true,
84
+ retryAfter: const Duration(minutes: 1),
85
+ );
86
+
87
+ test('the read asks again the moment its workspace syncs', () async {
88
+ final synced = StreamController<String>.broadcast();
89
+ final asked = Completer<void>();
90
+ var reads = 0;
91
+ final rows = nmsUntilReplicaReady(() async {
92
+ reads++;
93
+ if (!asked.isCompleted) asked.complete();
94
+ if (reads == 1) throw notReady('estate-a');
95
+ return ['Boiler'];
96
+ }, synced: synced.stream);
97
+ await asked.future;
98
+ await pumpEventQueue();
99
+ synced.add('estate-b');
100
+ await pumpEventQueue();
101
+ expect(reads, 1, reason: 'another workspace syncing is not this copy');
102
+ synced.add('estate-a');
103
+ expect(
104
+ await rows.timeout(const Duration(seconds: 1)),
105
+ ['Boiler'],
106
+ );
107
+ expect(reads, 2);
108
+ await synced.close();
109
+ });
110
+
111
+ test('a sync that lands before the wait begins is not missed', () async {
112
+ final synced = StreamController<String>.broadcast(sync: true);
113
+ var reads = 0;
114
+ final rows = await nmsUntilReplicaReady(() async {
115
+ reads++;
116
+ if (reads == 1) {
117
+ // The copy becomes ready while the "not yet" answer is in flight.
118
+ synced.add('estate-a');
119
+ throw notReady('estate-a');
120
+ }
121
+ return ['Boiler'];
122
+ }, synced: synced.stream)
123
+ .timeout(const Duration(seconds: 1));
124
+ expect(rows, ['Boiler']);
125
+ await synced.close();
126
+ });
127
+ });
128
+
129
+ // The guard: every one-shot read reaches the kernel through one of these two
130
+ // request shapes. Each must sit inside nmsUntilReplicaReady, so a new read
131
+ // path cannot hand callers a "not ready yet" as an error.
132
+ test('every kernel query request is built inside nmsUntilReplicaReady', () {
133
+ final source = File(
134
+ 'lib/src/reception/dart_reception.dart',
135
+ ).readAsLinesSync();
136
+ final requestSites = <int>[];
137
+ for (var i = 0; i < source.length; i++) {
138
+ final line = source[i];
139
+ if (line.contains(r":query:$") ||
140
+ line.contains(
141
+ 'historicQuery: nomos_protocol.HistoricQueryRequest(')) {
142
+ requestSites.add(i);
143
+ }
144
+ }
145
+ expect(requestSites, hasLength(2), reason: 'query request sites moved');
146
+ for (final site in requestSites) {
147
+ final window = source.sublist(site - 16 < 0 ? 0 : site - 16, site);
148
+ expect(
149
+ window.any((line) => line.contains('nmsUntilReplicaReady(')),
150
+ isTrue,
151
+ reason: 'dart_reception.dart:${site + 1} sends a kernel query outside '
152
+ 'nmsUntilReplicaReady',
153
+ );
154
+ }
155
+ });
156
+ }
@@ -0,0 +1,92 @@
1
+ import 'dart:async';
2
+
3
+ import 'package:nomos_client/src/observed_session_directory.dart';
4
+ import 'package:nomos_client/src/operation_receipts.dart';
5
+ import 'package:nomos_client/src/sync_status_projection.dart';
6
+ import 'package:nomos_client/src/typed_watch_registry.dart';
7
+ import 'package:nomos_types/nomos_kernel_protocol.dart' as protocol;
8
+ import 'package:test/test.dart';
9
+
10
+ void main() {
11
+ test('observed session directory is a stable facade over successful opens',
12
+ () {
13
+ final directory = NmsObservedSessionDirectory<Object>();
14
+ final handle = Object();
15
+ directory.observeBound(
16
+ session: 'estate',
17
+ workspace: 'estate-1',
18
+ credentialCapability: 'device:jack',
19
+ );
20
+
21
+ expect(directory.isBound('estate'), isTrue);
22
+ expect(directory.workspaceFor('estate'), 'estate-1');
23
+ expect(directory.credentialCapabilityFor('estate'), 'device:jack');
24
+ expect(directory.handle('estate', () => handle), same(handle));
25
+ expect(directory.handle('estate', Object.new), same(handle));
26
+ });
27
+
28
+ test('sync projection folds typed per-session observations for the UI', () {
29
+ final projection = NmsSyncStatusProjection();
30
+ projection.ensureSession(
31
+ 'home',
32
+ const SyncStatus(phase: SyncPhase.idle, behind: 0, saved: 2),
33
+ );
34
+ projection.ensureSession(
35
+ 'estate',
36
+ const SyncStatus(phase: SyncPhase.behind, behind: 3, saved: 5),
37
+ );
38
+ projection.foldApplication(const ['home', 'estate']);
39
+
40
+ expect(projection.applicationStatus.phase, SyncPhase.behind);
41
+ expect(projection.applicationStatus.behind, 3);
42
+ expect(projection.applicationStatus.saved, 7);
43
+ });
44
+
45
+ test('operation receipts advance only from runtime-issued stable identities',
46
+ () async {
47
+ final projection = NmsOperationReceiptProjection();
48
+ final receipt = projection.register('estate', 'intent-1');
49
+ projection.advance(
50
+ 'estate',
51
+ protocol.SyncWorkspaceResponse(admittedIntentIds: const ['intent-1']),
52
+ );
53
+ await receipt.admitted;
54
+ expect(receipt.current.phase, NomosOperationPhase.admittedToRemoteCustody);
55
+
56
+ projection.advance(
57
+ 'estate',
58
+ protocol.SyncWorkspaceResponse(convergedIntentIds: const ['intent-1']),
59
+ );
60
+ await receipt.converged;
61
+ expect(receipt.current.phase, NomosOperationPhase.converged);
62
+ });
63
+
64
+ test('typed watch registry only correlates runtime events and cancellation',
65
+ () async {
66
+ final registry = NmsTypedWatchRegistry();
67
+ final stopped = <String>[];
68
+ final handle = registry.open<List<int>>(
69
+ session: 'estate',
70
+ decode: (event) => event.resultUsda,
71
+ start: (_) async {},
72
+ stop: (id) async => stopped.add(id),
73
+ );
74
+ final values = <List<int>>[];
75
+ final subscription = handle.stream.listen(values.add);
76
+
77
+ registry.deliver(
78
+ protocol.WatchChanged(
79
+ subscriptionId: handle.id,
80
+ resultUsda: const [1, 2, 3],
81
+ ),
82
+ );
83
+ await Future<void>.delayed(Duration.zero);
84
+ expect(values, const [
85
+ <int>[1, 2, 3]
86
+ ]);
87
+
88
+ await subscription.cancel();
89
+ expect(stopped, [handle.id]);
90
+ registry.close();
91
+ });
92
+ }
@@ -0,0 +1,131 @@
1
+ import 'package:nomos_client/src/runtime_v2_gateway.dart';
2
+ import 'package:nomos_client/src/runtime_v2_transport.dart';
3
+ import 'package:nomos_types/nomos_kernel_protocol.dart' as protocol;
4
+ import 'package:nomos_types/nomos_types.dart';
5
+ import 'package:test/test.dart';
6
+
7
+ final class _ScriptedRuntime implements NomosRuntimeV2Transport {
8
+ _ScriptedRuntime(this.answer);
9
+
10
+ final protocol.RuntimeResponse Function(protocol.RuntimeRequest request)
11
+ answer;
12
+ var _requestId = 0;
13
+
14
+ @override
15
+ int nextRuntimeRequestId() => ++_requestId;
16
+
17
+ @override
18
+ Future<protocol.RuntimeResponse> invokeRuntime(
19
+ protocol.RuntimeRequest request,
20
+ ) async =>
21
+ answer(request);
22
+ }
23
+
24
+ final class _ScriptedKernelCalls extends _ScriptedRuntime
25
+ with NomosRuntimeV2KernelCalls {
26
+ _ScriptedKernelCalls(super.answer);
27
+
28
+ @override
29
+ int nextRuntimeRequestId() => super.nextRuntimeRequestId();
30
+ }
31
+
32
+ void main() {
33
+ test('a kernel call on a copy not ready yet is the typed wait reads sit out',
34
+ () async {
35
+ // A fresh device's first read of a workspace it is still fetching (node, web and in-app
36
+ // transports): once a bare "Runtime v2 returned unavailable for a kernel call".
37
+ final runtime = _ScriptedKernelCalls(
38
+ (request) => protocol.RuntimeResponse(
39
+ protocolVersion: 2,
40
+ requestId: request.requestId,
41
+ unavailable: protocol.RuntimeUnavailable(
42
+ reason: protocol.RuntimeUnavailableReason
43
+ .RUNTIME_UNAVAILABLE_REASON_LOCAL_REPLICA_NOT_READY,
44
+ workspace: 'estate',
45
+ retryable: true,
46
+ retryAfterMillis: 250,
47
+ detail:
48
+ 'placement requires a local replica which Replica Follow has not established yet',
49
+ ),
50
+ ),
51
+ );
52
+ await expectLater(
53
+ runtime.callKernel(
54
+ session: 'estate',
55
+ request: protocol.CallRequest(),
56
+ credentialUse: protocol.CredentialUse.CREDENTIAL_USE_SESSION,
57
+ ),
58
+ throwsA(
59
+ isA<NomosUnavailable>()
60
+ .having((value) => value.reason, 'reason',
61
+ NomosUnavailableReason.localReplicaNotReady)
62
+ .having((value) => value.workspace, 'workspace', 'estate')
63
+ .having((value) => value.retryable, 'retryable', true),
64
+ ),
65
+ );
66
+ });
67
+
68
+ test('unavailability is exhaustive application control state', () async {
69
+ final runtime = _ScriptedRuntime(
70
+ (request) => protocol.RuntimeResponse(
71
+ protocolVersion: 2,
72
+ requestId: request.requestId,
73
+ unavailable: protocol.RuntimeUnavailable(
74
+ reason: protocol.RuntimeUnavailableReason
75
+ .RUNTIME_UNAVAILABLE_REASON_REMOTE_ONLY_OFFLINE,
76
+ workspace: 'catalogue',
77
+ retryable: true,
78
+ retryAfterMillis: 1200,
79
+ detail: 'diagnostic text is not control flow',
80
+ ),
81
+ ),
82
+ );
83
+
84
+ await expectLater(
85
+ NmsRuntimeV2Gateway(runtime).invoke(protocol.RuntimeRequest()),
86
+ throwsA(
87
+ isA<NomosUnavailable>()
88
+ .having(
89
+ (value) => value.reason,
90
+ 'reason',
91
+ NomosUnavailableReason.remoteOnlyOffline,
92
+ )
93
+ .having((value) => value.workspace, 'workspace', 'catalogue')
94
+ .having(
95
+ (value) => value.retryAfter,
96
+ 'retryAfter',
97
+ const Duration(milliseconds: 1200),
98
+ ),
99
+ ),
100
+ );
101
+ });
102
+
103
+ test('policy refusal does not collapse into a string failure', () async {
104
+ final runtime = _ScriptedRuntime(
105
+ (request) => protocol.RuntimeResponse(
106
+ protocolVersion: 2,
107
+ requestId: request.requestId,
108
+ refused: protocol.RuntimeRefusal(
109
+ reason: protocol
110
+ .RuntimeRefusalReason.RUNTIME_REFUSAL_REASON_PLACEMENT_POLICY,
111
+ workspace: 'catalogue',
112
+ retryable: false,
113
+ detail: 'local residency is not permitted',
114
+ ),
115
+ ),
116
+ );
117
+
118
+ await expectLater(
119
+ NmsRuntimeV2Gateway(runtime).invoke(protocol.RuntimeRequest()),
120
+ throwsA(
121
+ isA<NomosRefused>()
122
+ .having(
123
+ (value) => value.reason,
124
+ 'reason',
125
+ NomosRefusalReason.placementPolicy,
126
+ )
127
+ .having((value) => value.retryable, 'retryable', false),
128
+ ),
129
+ );
130
+ });
131
+ }
@@ -0,0 +1,75 @@
1
+ import 'dart:typed_data';
2
+
3
+ import 'package:nomos_client/src/runtime_v2_transport.dart'
4
+ show NomosKernelCallTransport;
5
+ import 'package:nomos_client/src/signing_identity_kernel.dart'
6
+ show NmsSigningIdentityKernel;
7
+ import 'package:nomos_types/nomos_kernel_protocol.dart' as protocol;
8
+ import 'package:test/test.dart';
9
+
10
+ final class _KernelTransport implements NomosKernelCallTransport {
11
+ final List<protocol.CallRequest> requests = [];
12
+
13
+ @override
14
+ Future<protocol.CallResponse> callKernel({
15
+ required String session,
16
+ required protocol.CallRequest request,
17
+ protocol.CredentialUse credentialUse =
18
+ protocol.CredentialUse.CREDENTIAL_USE_UNSPECIFIED,
19
+ }) async {
20
+ expect(session, 'home-123');
21
+ expect(credentialUse, protocol.CredentialUse.CREDENTIAL_USE_NONE);
22
+ requests.add(request);
23
+ final signing = request.signingIdentity;
24
+ return switch (signing.whichOperation()) {
25
+ protocol.SigningIdentityRequest_Operation.generate =>
26
+ protocol.CallResponse(
27
+ protocolVersion: 1,
28
+ requestId: request.requestId,
29
+ signingIdentity: protocol.SigningIdentityResponse(
30
+ generated: protocol.SigningKeyPair(
31
+ secret: List<int>.filled(32, 7),
32
+ publicKey: List<int>.filled(32, 9),
33
+ ),
34
+ ),
35
+ ),
36
+ protocol.SigningIdentityRequest_Operation.derivePublic =>
37
+ protocol.CallResponse(
38
+ protocolVersion: 1,
39
+ requestId: request.requestId,
40
+ signingIdentity: protocol.SigningIdentityResponse(
41
+ derivedPublic: protocol.SigningPublicKey(
42
+ publicKey: List<int>.filled(32, 11),
43
+ ),
44
+ ),
45
+ ),
46
+ protocol.SigningIdentityRequest_Operation.notSet =>
47
+ throw StateError('missing signing operation'),
48
+ };
49
+ }
50
+ }
51
+
52
+ void main() {
53
+ test('signing identity crosses only the generated kernel contract', () async {
54
+ final transport = _KernelTransport();
55
+ final identity = NmsSigningIdentityKernel(
56
+ transport: transport,
57
+ session: 'home-123',
58
+ );
59
+
60
+ final generated = await identity.generate(requestId: 'generate-1');
61
+ expect(generated.secret, List<int>.filled(32, 7));
62
+ expect(generated.publicKey, List<int>.filled(32, 9));
63
+
64
+ final derived = await identity.derivePublic(
65
+ requestId: 'derive-1',
66
+ secret: Uint8List.fromList(List<int>.filled(32, 7)),
67
+ );
68
+ expect(derived.publicKey, List<int>.filled(32, 11));
69
+
70
+ expect(transport.requests, hasLength(2));
71
+ expect(transport.requests.first.hasSigningIdentity(), isTrue);
72
+ expect(transport.requests.last.signingIdentity.derivePublic.secret,
73
+ List<int>.filled(32, 7));
74
+ });
75
+ }
@@ -0,0 +1,29 @@
1
+ import 'package:nomos_client/src/sync_status_projection.dart';
2
+ import 'package:test/test.dart';
3
+
4
+ void main() {
5
+ test(
6
+ 'peer preparation reaches application status without a workspace session',
7
+ () async {
8
+ final projection = NmsSyncStatusProjection();
9
+ projection.updatePreparationCounts(1, 1,
10
+ activity: 'Installing workspace rules');
11
+ expect(projection.applicationStatus.preparingWorkspaces, 1);
12
+ expect(projection.applicationStatus.retryingWorkspaces, 1);
13
+ expect(projection.applicationStatus.preparationActivity,
14
+ 'Installing workspace rules');
15
+
16
+ // Ordinary sync folding cannot erase the independently reported work.
17
+ projection.foldApplication(const []);
18
+ expect(projection.applicationStatus.preparingWorkspaces, 1);
19
+ expect(projection.applicationStatus.retryingWorkspaces, 1);
20
+ expect(projection.applicationStatus.preparationActivity,
21
+ 'Installing workspace rules');
22
+
23
+ projection.updatePreparationCounts(0, 0);
24
+ expect(projection.applicationStatus.preparingWorkspaces, 0);
25
+ expect(projection.applicationStatus.retryingWorkspaces, 0);
26
+ expect(projection.applicationStatus.preparationActivity, isNull);
27
+ await projection.close();
28
+ });
29
+ }