@githolon/dsl 0.108.6 → 0.110.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (307) hide show
  1. package/assets/operation-contract.wasm +0 -0
  2. package/check_package.mjs +12 -15
  3. package/compile_package.mjs +17 -18
  4. package/compile_proof.mjs +16 -1
  5. package/dart/CHANGELOG.md +76 -1
  6. package/dart/README.md +25 -2
  7. package/dart/lib/src/admission_decision.dart +41 -0
  8. package/dart/lib/src/application_framework_calls.dart +52 -2
  9. package/dart/lib/src/application_model_adoption.dart +187 -0
  10. package/dart/lib/src/application_status.dart +73 -0
  11. package/dart/lib/src/console_enrolment.dart +271 -0
  12. package/dart/lib/src/console_http_io.dart +26 -0
  13. package/dart/lib/src/console_http_stub.dart +7 -0
  14. package/dart/lib/src/dev_cloud.dart +69 -22
  15. package/dart/lib/src/generated_call.dart +83 -23
  16. package/dart/lib/src/kernel_primitives.dart +119 -0
  17. package/dart/lib/src/nms_conformance_runtime.dart +1 -1
  18. package/dart/lib/src/nms_generated_runtime.dart +32 -10
  19. package/dart/lib/src/nms_test_environment.dart +1 -1
  20. package/dart/lib/src/node_runner_transport.dart +41 -46
  21. package/dart/lib/src/observed_session_directory.dart +70 -0
  22. package/dart/lib/src/operation_receipts.dart +112 -0
  23. package/dart/lib/src/peer_application_streams.dart +103 -0
  24. package/dart/lib/src/reception/dart_reception.dart +4280 -0
  25. package/dart/lib/src/reception/event_desk.dart +211 -0
  26. package/dart/lib/src/reception/extremum_read.dart +46 -0
  27. package/dart/lib/src/reception/local_copies.dart +371 -0
  28. package/dart/lib/src/reception/peer_office_connection.dart +11 -0
  29. package/dart/lib/src/reception/progress_desk.dart +267 -0
  30. package/dart/lib/src/reception/reception_contract.dart +11 -0
  31. package/dart/lib/src/reception/reception_session.dart +284 -0
  32. package/dart/lib/src/reception/sync_diagnostics.dart +26 -0
  33. package/dart/lib/src/reception/workspace_preparation_report.dart +126 -0
  34. package/dart/lib/src/replica_readiness.dart +123 -0
  35. package/dart/lib/src/runner/node-entry.mjs +23905 -15774
  36. package/dart/lib/src/runtime_v2_gateway.dart +56 -3
  37. package/dart/lib/src/runtime_v2_transport.dart +544 -3
  38. package/dart/lib/src/session_bound.dart +200 -311
  39. package/dart/lib/src/signing_identity_kernel.dart +84 -0
  40. package/dart/lib/src/sync_status_projection.dart +548 -0
  41. package/dart/lib/src/sync_status_reducer.dart +170 -0
  42. package/dart/lib/src/test_harness.dart +809 -1153
  43. package/dart/lib/src/testing/test_custody.dart +66 -0
  44. package/dart/lib/src/testing/test_peer.dart +328 -0
  45. package/dart/lib/src/typed_watch_registry.dart +169 -0
  46. package/dart/lib/src/workspace_open_progress.dart +27 -0
  47. package/dart/pubspec.lock +24 -1
  48. package/dart/pubspec.yaml +7 -2
  49. package/dart/pubspec_overrides.yaml +2 -0
  50. package/dart/test/admission_decision_test.dart +48 -0
  51. package/dart/test/background_model_adoption_test.dart +228 -0
  52. package/dart/test/bundle_version_pairing_test.dart +4 -4
  53. package/dart/test/child_authentication_repair_test.dart +359 -0
  54. package/dart/test/cloud_bound_local_shell_test.dart +119 -0
  55. package/dart/test/console_enrolment_refusal_test.dart +52 -0
  56. package/dart/test/console_enrolment_test.dart +227 -0
  57. package/dart/test/errors_test.dart +5 -5
  58. package/dart/test/event_plane_seam_test.dart +50 -164
  59. package/dart/test/extremum_read_test.dart +58 -0
  60. package/dart/test/fixtures/wedge/wedge-entry.mjs +3 -21
  61. package/dart/test/generated_query_test.dart +32 -0
  62. package/dart/test/harness_phase_timeout_test.dart +18 -23
  63. package/dart/test/kernel_author_after_read_test.dart +242 -0
  64. package/dart/test/kernel_fence_test.dart +137 -0
  65. package/dart/test/local_copies_test.dart +222 -0
  66. package/dart/test/model_adoption_test.dart +198 -0
  67. package/dart/test/node_peer_local_custody_test.dart +153 -0
  68. package/dart/test/offer_authentication_proof_test.dart +352 -52
  69. package/dart/test/operation_compatibility_test.dart +151 -0
  70. package/dart/test/platform_credential_effect_test.dart +386 -0
  71. package/dart/test/receiving_group_open_test.dart +213 -0
  72. package/dart/test/reception/workspace_preparation_report_test.dart +103 -0
  73. package/dart/test/reception_span_trace_test.dart +23 -0
  74. package/dart/test/replica_readiness_test.dart +156 -0
  75. package/dart/test/runtime_projections_test.dart +92 -0
  76. package/dart/test/runtime_v2_gateway_test.dart +131 -0
  77. package/dart/test/signing_identity_kernel_test.dart +75 -0
  78. package/dart/test/sync_status_preparation_report_test.dart +29 -0
  79. package/dart/test/sync_status_reducer_test.dart +419 -0
  80. package/dart/test/test_harness_test.dart +71 -95
  81. package/dart/test/typed_query_watch_test.dart +170 -0
  82. package/dart/test/typed_watch_registry_test.dart +249 -0
  83. package/dart_lints/README.md +18 -0
  84. package/dart_lints/lib/nomos_lints.dart +70 -0
  85. package/dart_lints/pubspec.lock +580 -0
  86. package/dart_lints/pubspec.yaml +12 -0
  87. package/dart_lints/test/reference_identity_test.dart +75 -0
  88. package/dart_types/CHANGELOG.md +18 -0
  89. package/dart_types/lib/nomos_kernel_protocol.dart +21 -1
  90. package/dart_types/lib/nomos_types.dart +9 -1
  91. package/dart_types/lib/src/admission_decision.dart +37 -0
  92. package/dart_types/lib/src/aggregate_meta.dart +1 -1
  93. package/dart_types/lib/src/approved_model.dart +65 -0
  94. package/dart_types/lib/src/custody.dart +10 -18
  95. package/dart_types/lib/src/error_info.dart +4 -4
  96. package/dart_types/lib/src/errors.dart +29 -29
  97. package/dart_types/lib/src/foreign_ref.dart +62 -33
  98. package/dart_types/lib/src/local_work_rejected.dart +3 -3
  99. package/dart_types/lib/src/nomos_ref.dart +3 -1
  100. package/dart_types/lib/src/principal.dart +2 -2
  101. package/dart_types/lib/src/proto/google/protobuf/any.pb.dart +163 -0
  102. package/dart_types/lib/src/proto/google/protobuf/any.pbenum.dart +11 -0
  103. package/dart_types/lib/src/proto/nomos/discovery/v1/workspace_placement.pb.dart +137 -0
  104. package/dart_types/lib/src/proto/nomos/discovery/v1/workspace_placement.pbenum.dart +11 -0
  105. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pb.dart +20721 -10433
  106. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbenum.dart +555 -27
  107. package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pb.dart +387 -0
  108. package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pbenum.dart +46 -0
  109. package/dart_types/lib/src/proto/nomos/runtime/v2/application_readiness_desk.pb.dart +295 -0
  110. package/dart_types/lib/src/proto/nomos/runtime/v2/application_readiness_desk.pbenum.dart +139 -0
  111. package/dart_types/lib/src/proto/nomos/runtime/v2/authentication_desk.pb.dart +269 -0
  112. package/dart_types/lib/src/proto/nomos/runtime/v2/authentication_desk.pbenum.dart +103 -0
  113. package/dart_types/lib/src/proto/nomos/runtime/v2/compatibility_desk.pb.dart +417 -0
  114. package/dart_types/lib/src/proto/nomos/runtime/v2/compatibility_desk.pbenum.dart +62 -0
  115. package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pb.dart +9968 -0
  116. package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pbenum.dart +133 -0
  117. package/dart_types/lib/src/proto/nomos/runtime/v2/credentials_desk.pb.dart +204 -0
  118. package/dart_types/lib/src/proto/nomos/runtime/v2/credentials_desk.pbenum.dart +54 -0
  119. package/dart_types/lib/src/proto/nomos/runtime/v2/custody_desk.pb.dart +1550 -0
  120. package/dart_types/lib/src/proto/nomos/runtime/v2/custody_desk.pbenum.dart +11 -0
  121. package/dart_types/lib/src/proto/nomos/runtime/v2/framework.pb.dart +710 -0
  122. package/dart_types/lib/src/proto/nomos/runtime/v2/framework.pbenum.dart +11 -0
  123. package/dart_types/lib/src/proto/nomos/runtime/v2/identity_desk.pb.dart +845 -0
  124. package/dart_types/lib/src/proto/nomos/runtime/v2/identity_desk.pbenum.dart +11 -0
  125. package/dart_types/lib/src/proto/nomos/runtime/v2/interaction_desk.pb.dart +198 -0
  126. package/dart_types/lib/src/proto/nomos/runtime/v2/interaction_desk.pbenum.dart +11 -0
  127. package/dart_types/lib/src/proto/nomos/runtime/v2/law_desk.pb.dart +369 -0
  128. package/dart_types/lib/src/proto/nomos/runtime/v2/law_desk.pbenum.dart +11 -0
  129. package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pb.dart +609 -0
  130. package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pbenum.dart +65 -0
  131. package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pb.dart +358 -0
  132. package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pbenum.dart +100 -0
  133. package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pb.dart +2231 -0
  134. package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pbenum.dart +271 -0
  135. package/dart_types/lib/src/proto/nomos/runtime/v2/placement_desk.pb.dart +1286 -0
  136. package/dart_types/lib/src/proto/nomos/runtime/v2/placement_desk.pbenum.dart +11 -0
  137. package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pb.dart +636 -0
  138. package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pbenum.dart +116 -0
  139. package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pb.dart +1637 -0
  140. package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pbenum.dart +175 -0
  141. package/dart_types/lib/src/proto/nomos/runtime/v2/recovery_desk.pb.dart +1269 -0
  142. package/dart_types/lib/src/proto/nomos/runtime/v2/recovery_desk.pbenum.dart +129 -0
  143. package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pb.dart +2782 -0
  144. package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pbenum.dart +97 -0
  145. package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pb.dart +2232 -8627
  146. package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pbenum.dart +0 -358
  147. package/dart_types/lib/src/proto/nomos/runtime/v2/sealing_desk.pb.dart +1639 -0
  148. package/dart_types/lib/src/proto/nomos/runtime/v2/sealing_desk.pbenum.dart +46 -0
  149. package/dart_types/lib/src/runtime_outcome.dart +133 -0
  150. package/dart_types/lib/src/subscriptions.dart +116 -23
  151. package/dart_types/pubspec.yaml +1 -1
  152. package/dart_types/test/extremum_subscriptions_test.dart +40 -0
  153. package/dart_types/test/foreign_ref_resolution_test.dart +75 -0
  154. package/dart_types/test/nomos_ref_address_test.dart +25 -0
  155. package/dart_types/test/ordered_subscriptions_test.dart +34 -0
  156. package/operation_contract_check.mjs +24 -0
  157. package/package.json +11 -6
  158. package/src/aggregate.ts +70 -10
  159. package/src/application_topology.ts +18 -11
  160. package/src/attested.ts +3 -0
  161. package/src/attested_core.ts +46 -3
  162. package/src/birth_meter_ensure.ts +19 -7
  163. package/src/births.ts +29 -4
  164. package/src/build_package.ts +140 -13
  165. package/src/business_birth.ts +181 -4
  166. package/src/codegen_call_proto.ts +1274 -0
  167. package/src/codegen_call_usda.ts +49 -6
  168. package/src/codegen_dart.ts +79 -44
  169. package/src/codegen_dart_client.ts +458 -800
  170. package/src/{codegen_dart_application.ts → codegen_dart_facade.ts} +331 -582
  171. package/src/codegen_dart_framework_calls.ts +320 -37
  172. package/src/codegen_dart_indexed_variant.ts +159 -0
  173. package/src/codegen_dart_scenario.ts +42 -13
  174. package/src/codegen_proof.ts +3 -3
  175. package/src/codegen_provider_dart.ts +5 -0
  176. package/src/codegen_query_proto.ts +158 -0
  177. package/src/codegen_refusal.ts +189 -0
  178. package/src/codegen_result_proto.ts +931 -0
  179. package/src/codegen_result_usda.ts +4 -0
  180. package/src/codegen_ts.ts +464 -616
  181. package/src/codegen_workspace_access.ts +105 -0
  182. package/src/compatibility_graph.ts +759 -0
  183. package/src/compatibility_usda.ts +264 -0
  184. package/src/compile_package_main.ts +2083 -1248
  185. package/src/custody_placement.ts +156 -0
  186. package/src/custody_placement_usda.ts +91 -0
  187. package/src/decl_shapes.ts +21 -2
  188. package/src/describe_model.ts +1 -0
  189. package/src/directive.ts +59 -2
  190. package/src/drivers.ts +51 -0
  191. package/src/engine_entry.ts +52 -8
  192. package/src/evolve_preview.ts +11 -4
  193. package/src/exists.ts +2 -0
  194. package/src/fields.ts +75 -11
  195. package/src/flow_gate.ts +2 -1
  196. package/src/framework/application_home.ts +170 -18
  197. package/src/framework/attested_receipts.ts +8 -3
  198. package/src/framework/auth_provider_law.ts +169 -9
  199. package/src/framework/birth_certificate_law.ts +4 -0
  200. package/src/framework/capability.ts +34 -3
  201. package/src/framework/child_authentication.ts +346 -0
  202. package/src/framework/console_governance.ts +295 -0
  203. package/src/framework/delegated_roles.ts +301 -0
  204. package/src/framework/domain_lifecycle.ts +26 -13
  205. package/src/framework/founding_genesis.ts +193 -0
  206. package/src/framework/framework_aggregates.ts +1 -1
  207. package/src/framework/genesis.ts +6 -1
  208. package/src/framework/governance_relations.ts +19 -0
  209. package/src/framework/home.ts +5 -734
  210. package/src/framework/legacy/child_auth_provider_epoch.ts +73 -0
  211. package/src/framework/model_authority_repair.ts +116 -0
  212. package/src/framework/model_authority_seed.ts +185 -0
  213. package/src/framework/model_transition.ts +211 -0
  214. package/src/framework/owned_custody.ts +1 -1
  215. package/src/framework/principal_enrollment.ts +302 -0
  216. package/src/framework/receiving_group.ts +857 -0
  217. package/src/framework/receiving_group_recovery.ts +314 -0
  218. package/src/framework/relations.ts +4 -8
  219. package/src/framework/signing_identity.ts +242 -95
  220. package/src/framework/source_roster.ts +11 -5
  221. package/src/framework/workspace_access.ts +149 -0
  222. package/src/framework/workspace_invariant.ts +73 -52
  223. package/src/framework/workspaces.ts +407 -40
  224. package/src/framework_call_definitions.ts +32 -3
  225. package/src/generated_client_bundle_boundary.ts +97 -0
  226. package/src/generated_client_names.ts +1 -0
  227. package/src/identity_usda.ts +27 -0
  228. package/src/index.ts +183 -12
  229. package/src/indexed_variant.ts +83 -0
  230. package/src/intent.ts +173 -45
  231. package/src/law_check.ts +8 -5
  232. package/src/law_dependency_index.ts +231 -0
  233. package/src/law_graph.ts +508 -71
  234. package/src/law_graph_usda.ts +170 -24
  235. package/src/law_identity.ts +119 -81
  236. package/src/law_source_map.ts +4 -1
  237. package/src/law_units.ts +498 -0
  238. package/src/law_value_type.ts +7 -3
  239. package/src/member_authentication.ts +302 -0
  240. package/src/null_meta.ts +1 -1
  241. package/src/openusd_compiled_layer.ts +132 -0
  242. package/src/operation_contract_check.ts +31 -0
  243. package/src/ordered_read.ts +2 -1
  244. package/src/package_config.ts +33 -12
  245. package/src/package_dependencies.ts +1 -0
  246. package/src/plan_source_slice.ts +30 -1
  247. package/src/protobuf_field_number.ts +36 -0
  248. package/src/provenance_wire.ts +6 -0
  249. package/src/query.ts +2 -1
  250. package/src/read.ts +50 -0
  251. package/src/relation.ts +4 -1
  252. package/src/relation_schema.ts +15 -2
  253. package/src/report.ts +2 -2
  254. package/src/sqlite_computation_bundle.ts +78 -0
  255. package/src/sqlite_computed.ts +102 -0
  256. package/src/sqlite_counts.ts +154 -0
  257. package/src/sqlite_layout.ts +823 -0
  258. package/src/sqlite_reads.ts +275 -0
  259. package/src/sqlite_samples.ts +185 -0
  260. package/src/stable_id_identity_bridge.ts +1 -1
  261. package/src/stable_ids_for_module.ts +16 -4
  262. package/src/test_call_usda.ts +6 -6
  263. package/src/usda_relationship_integrity.ts +20 -3
  264. package/src/usda_text.ts +21 -1
  265. package/src/value_object.ts +81 -11
  266. package/src/wire.ts +22 -0
  267. package/src/wire_encode.ts +227 -20
  268. package/src/workspace_sharding.ts +15 -6
  269. package/src/workspace_sharing.ts +51 -49
  270. package/src/zod_introspection.ts +2 -0
  271. package/compat_package.mjs +0 -46
  272. package/dart/lib/src/home.dart +0 -15
  273. package/dart/lib/src/runner/offline-install.mjs +0 -15367
  274. package/dart/lib/src/source_trust.dart +0 -51
  275. package/dart/lib/src/webview_bridge.dart +0 -6540
  276. package/dart/lib/src/workspace_name.dart +0 -30
  277. package/dart/test/acquire_session_credentials_test.dart +0 -150
  278. package/dart/test/acquire_session_test.dart +0 -534
  279. package/dart/test/adv_realm_interference_test.dart +0 -600
  280. package/dart/test/cloud_bound_topology_test.dart +0 -168
  281. package/dart/test/cluster_births_test.dart +0 -202
  282. package/dart/test/cluster_device_keyed_acceptance_test.dart +0 -105
  283. package/dart/test/cluster_local_autosync_test.dart +0 -123
  284. package/dart/test/cluster_scale_test.dart +0 -122
  285. package/dart/test/compat_test.dart +0 -61
  286. package/dart/test/dlq_session_surface_test.dart +0 -222
  287. package/dart/test/first_app_gauntlet_test.dart +0 -386
  288. package/dart/test/fixtures/wedge/fake-install.mjs +0 -11
  289. package/dart/test/four_layer_nested_topology_test.dart +0 -290
  290. package/dart/test/home_parity_test.dart +0 -13
  291. package/dart/test/local_sync_inert_test.dart +0 -107
  292. package/dart/test/node_runner_transport_test.dart +0 -78
  293. package/dart/test/parent_signer_recovery_test.dart +0 -155
  294. package/dart/test/residency_refusal_test.dart +0 -49
  295. package/dart/test/sealing_client_test.dart +0 -181
  296. package/dart/test/session_bound_test.dart +0 -285
  297. package/dart/test/stale_bundle_test.dart +0 -80
  298. package/dart/test/vo_read_typing_test.dart +0 -131
  299. package/dart/test/watch_after_offer_stall_test.dart +0 -100
  300. package/dart/test/webview_bridge_test.dart +0 -1930
  301. package/dart/test/workspace_name_test.dart +0 -24
  302. package/runtime/generated-application-host.js +0 -22860
  303. package/src/codegen_query_usda.ts +0 -214
  304. package/src/compat_manifest.ts +0 -26
  305. package/src/compat_package_main.ts +0 -224
  306. package/src/shape_compat.ts +0 -352
  307. package/src/usd_state.ts +0 -505
@@ -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
+ }