@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,84 @@
1
+ @internal
2
+ library;
3
+
4
+ import 'dart:typed_data';
5
+
6
+ import 'package:meta/meta.dart' show internal;
7
+ import 'package:nomos_types/nomos_kernel_protocol.dart' as protocol;
8
+
9
+ import 'runtime_v2_transport.dart' show NomosKernelCallTransport;
10
+
11
+ /// The exact kernel-owned signing-identity capability.
12
+ ///
13
+ /// This is deliberately not a generic command adapter. It carries one
14
+ /// generated request to the kernel and accepts only its generated response.
15
+ /// Platform code retains the returned secret; it does not implement key
16
+ /// generation or public-key derivation itself.
17
+ final class NmsSigningIdentityKernel {
18
+ const NmsSigningIdentityKernel({
19
+ required this.transport,
20
+ required this.session,
21
+ });
22
+
23
+ final NomosKernelCallTransport transport;
24
+ final String session;
25
+
26
+ Future<protocol.SigningKeyPair> generate({
27
+ required String requestId,
28
+ }) async {
29
+ final response = await _call(
30
+ requestId: requestId,
31
+ request: protocol.SigningIdentityRequest(
32
+ generate: protocol.GenerateSigningKeyPair(),
33
+ ),
34
+ );
35
+ if (!response.hasGenerated()) {
36
+ throw StateError(
37
+ 'Kernel returned ${response.whichResult().name} for signing-key generation',
38
+ );
39
+ }
40
+ return response.generated;
41
+ }
42
+
43
+ Future<protocol.SigningPublicKey> derivePublic({
44
+ required String requestId,
45
+ required Uint8List secret,
46
+ }) async {
47
+ final response = await _call(
48
+ requestId: requestId,
49
+ request: protocol.SigningIdentityRequest(
50
+ derivePublic: protocol.DeriveSigningPublicKey(secret: secret),
51
+ ),
52
+ );
53
+ if (!response.hasDerivedPublic()) {
54
+ throw StateError(
55
+ 'Kernel returned ${response.whichResult().name} for signing-public derivation',
56
+ );
57
+ }
58
+ return response.derivedPublic;
59
+ }
60
+
61
+ Future<protocol.SigningIdentityResponse> _call({
62
+ required String requestId,
63
+ required protocol.SigningIdentityRequest request,
64
+ }) async {
65
+ final response = await transport.callKernel(
66
+ session: session,
67
+ credentialUse: protocol.CredentialUse.CREDENTIAL_USE_NONE,
68
+ request: protocol.CallRequest(
69
+ protocolVersion: 1,
70
+ requestId: requestId,
71
+ signingIdentity: request,
72
+ ),
73
+ );
74
+ if (response.hasFailure()) {
75
+ throw StateError('${response.failure.code}: ${response.failure.message}');
76
+ }
77
+ if (!response.hasSigningIdentity()) {
78
+ throw StateError(
79
+ 'Kernel returned ${response.whichOutcome().name} for a signing-identity request',
80
+ );
81
+ }
82
+ return response.signingIdentity;
83
+ }
84
+ }
@@ -0,0 +1,550 @@
1
+ import 'dart:async';
2
+
3
+ import 'package:nomos_types/nomos_kernel_protocol.dart' as nomos_protocol;
4
+
5
+ import 'application_model_adoption.dart' show NomosModelAdoptionProgress;
6
+
7
+ /// Live byte progress reported by the peer runtime for one custody transfer.
8
+ final class TransferProgress {
9
+ const TransferProgress({
10
+ required this.received,
11
+ this.total,
12
+ this.kind = 'delta',
13
+ });
14
+
15
+ final int received;
16
+ final int? total;
17
+ final String kind;
18
+
19
+ double? get fraction => (total == null || total == 0)
20
+ ? null
21
+ : (received / total!).clamp(0.0, 1.0);
22
+ bool get done => total != null && received >= total!;
23
+
24
+ factory TransferProgress.fromRuntime(
25
+ nomos_protocol.TransferProgress progress,
26
+ ) =>
27
+ TransferProgress(
28
+ received: progress.receivedBytes.toInt(),
29
+ total: progress.hasTotalBytes() ? progress.totalBytes.toInt() : null,
30
+ kind: switch (progress.kind) {
31
+ nomos_protocol.TransferKind.TRANSFER_KIND_FULL => 'full',
32
+ nomos_protocol.TransferKind.TRANSFER_KIND_PUSH => 'push',
33
+ _ => 'delta',
34
+ },
35
+ );
36
+ }
37
+
38
+ enum SyncPhase {
39
+ checking,
40
+ idle,
41
+ syncing,
42
+ awaitingRemoteDurability,
43
+ transferring,
44
+ behind,
45
+ offline,
46
+ error
47
+ }
48
+
49
+ /// The durable Replica Follow's own state, observed without starting a round.
50
+ enum WorkspaceFollowPhase {
51
+ desired,
52
+ due,
53
+ running,
54
+ settled,
55
+ awaitingDependency,
56
+ offline,
57
+ invalidCredential,
58
+ backoff,
59
+ rebornRemotely,
60
+ replacingLocal,
61
+ localCopyUnreadable,
62
+ unknown,
63
+ }
64
+
65
+ final class WorkspaceFollowStatus {
66
+ const WorkspaceFollowStatus({
67
+ required this.workspace,
68
+ required this.phase,
69
+ required this.live,
70
+ this.detail,
71
+ this.retryAt,
72
+ this.dueAt,
73
+ this.attempt,
74
+ this.activeEffect,
75
+ });
76
+
77
+ factory WorkspaceFollowStatus.fromRuntime(
78
+ nomos_protocol.FollowObservation report,
79
+ ) =>
80
+ WorkspaceFollowStatus(
81
+ workspace: report.workspace,
82
+ phase: switch (report.phase) {
83
+ nomos_protocol
84
+ .FollowObservationPhase.FOLLOW_OBSERVATION_PHASE_DESIRED =>
85
+ WorkspaceFollowPhase.desired,
86
+ nomos_protocol.FollowObservationPhase.FOLLOW_OBSERVATION_PHASE_DUE =>
87
+ WorkspaceFollowPhase.due,
88
+ nomos_protocol
89
+ .FollowObservationPhase.FOLLOW_OBSERVATION_PHASE_RUNNING =>
90
+ WorkspaceFollowPhase.running,
91
+ nomos_protocol
92
+ .FollowObservationPhase.FOLLOW_OBSERVATION_PHASE_SETTLED =>
93
+ WorkspaceFollowPhase.settled,
94
+ nomos_protocol.FollowObservationPhase
95
+ .FOLLOW_OBSERVATION_PHASE_AWAITING_DEPENDENCY =>
96
+ WorkspaceFollowPhase.awaitingDependency,
97
+ nomos_protocol
98
+ .FollowObservationPhase.FOLLOW_OBSERVATION_PHASE_OFFLINE =>
99
+ WorkspaceFollowPhase.offline,
100
+ nomos_protocol.FollowObservationPhase
101
+ .FOLLOW_OBSERVATION_PHASE_INVALID_CREDENTIAL =>
102
+ WorkspaceFollowPhase.invalidCredential,
103
+ nomos_protocol
104
+ .FollowObservationPhase.FOLLOW_OBSERVATION_PHASE_BACKOFF =>
105
+ WorkspaceFollowPhase.backoff,
106
+ nomos_protocol.FollowObservationPhase
107
+ .FOLLOW_OBSERVATION_PHASE_REBORN_REMOTELY =>
108
+ WorkspaceFollowPhase.rebornRemotely,
109
+ nomos_protocol.FollowObservationPhase
110
+ .FOLLOW_OBSERVATION_PHASE_REPLACING_LOCAL =>
111
+ WorkspaceFollowPhase.replacingLocal,
112
+ nomos_protocol.FollowObservationPhase
113
+ .FOLLOW_OBSERVATION_PHASE_LOCAL_COPY_UNREADABLE =>
114
+ WorkspaceFollowPhase.localCopyUnreadable,
115
+ _ => WorkspaceFollowPhase.unknown,
116
+ },
117
+ live: report.live,
118
+ detail: report.hasDetail() ? report.detail : null,
119
+ retryAt: report.hasRetryAtUnixMillis()
120
+ ? DateTime.fromMillisecondsSinceEpoch(
121
+ report.retryAtUnixMillis.toInt())
122
+ : null,
123
+ dueAt: report.hasDueAtUnixMillis()
124
+ ? DateTime.fromMillisecondsSinceEpoch(
125
+ report.dueAtUnixMillis.toInt())
126
+ : null,
127
+ attempt: report.hasAttempt() ? report.attempt : null,
128
+ activeEffect: report.hasActiveEffect() ? report.activeEffect : null,
129
+ );
130
+
131
+ final String workspace;
132
+ final WorkspaceFollowPhase phase;
133
+ final bool live;
134
+ final String? detail;
135
+ final DateTime? retryAt;
136
+ final DateTime? dueAt;
137
+ final int? attempt;
138
+ final String? activeEffect;
139
+ bool get current => phase == WorkspaceFollowPhase.settled && live;
140
+ }
141
+
142
+ /// Disposable UI projection of typed peer-runtime sync observations.
143
+ final class SyncStatus {
144
+ const SyncStatus({
145
+ this.phase = SyncPhase.checking,
146
+ this.ahead,
147
+ this.behind,
148
+ this.saved,
149
+ this.onMain,
150
+ this.deadLetters,
151
+ this.transfer,
152
+ this.lastSyncedAt,
153
+ this.lastError,
154
+ this.lastResult,
155
+ this.modelAdoption,
156
+ this.live = false,
157
+ this.followed,
158
+ this.followsInSync,
159
+ this.followsLive,
160
+ this.followStatuses = const [],
161
+ this.followReportAt,
162
+ this.preparingWorkspaces = 0,
163
+ this.retryingWorkspaces = 0,
164
+ this.preparationActivity,
165
+ });
166
+
167
+ final SyncPhase phase;
168
+
169
+ /// The live link to this workspace's upstream is open, so a settled status is current NOW:
170
+ /// the upstream pushes every new frontier and an idle follow runs no round. On the
171
+ /// application status: every connected workspace is settled and live.
172
+ final bool live;
173
+
174
+ /// Application status only: the replica follows this peer keeps up with in the background,
175
+ /// and how many of them are in sync (settled at their exact local head). Null until the
176
+ /// runtime has reported its follow inventory.
177
+ final int? followed;
178
+ final int? followsInSync;
179
+ final int? followsLive;
180
+ final List<WorkspaceFollowStatus> followStatuses;
181
+
182
+ /// The peer's time for its most recent changed follow report.
183
+ final DateTime? followReportAt;
184
+
185
+ /// Counts of Peer Office preparation observations, not inferred from sync.
186
+ final int preparingWorkspaces;
187
+ final int retryingWorkspaces;
188
+ final String? preparationActivity;
189
+
190
+ /// Physical progress overlays the semantic custody observation; finishing a
191
+ /// transfer reveals that observation instead of inventing a saving state.
192
+ SyncPhase get presentationPhase =>
193
+ transfer != null && phase != SyncPhase.error && phase != SyncPhase.offline
194
+ ? SyncPhase.transferring
195
+ : phase;
196
+ // null means not reported, never zero. Counts are observations, not a
197
+ // running total of writes seen by this Reception process.
198
+ final int? ahead;
199
+ final int? behind;
200
+ final int? saved;
201
+ final int? onMain;
202
+ final int? deadLetters;
203
+ final TransferProgress? transfer;
204
+ final DateTime? lastSyncedAt;
205
+ final String? lastError;
206
+
207
+ /// Exact last runtime receipt, not counters reconstructed from callbacks.
208
+ final nomos_protocol.SyncWorkspaceResponse? lastResult;
209
+
210
+ /// The model-adoption desk's latest observation for this workspace: progress
211
+ /// while it brings the workspace onto the application's approved law, then
212
+ /// the terminal outcome (`current`, `adopted`, a typed `deferred`).
213
+ final NomosModelAdoptionProgress? modelAdoption;
214
+
215
+ SyncStatus copyWith({
216
+ SyncPhase? phase,
217
+ int? ahead,
218
+ int? behind,
219
+ int? saved,
220
+ int? onMain,
221
+ int? deadLetters,
222
+ TransferProgress? transfer,
223
+ bool clearTransfer = false,
224
+ DateTime? lastSyncedAt,
225
+ String? lastError,
226
+ bool clearError = false,
227
+ bool clearCounts = false,
228
+ nomos_protocol.SyncWorkspaceResponse? lastResult,
229
+ NomosModelAdoptionProgress? modelAdoption,
230
+ bool? live,
231
+ int? followed,
232
+ int? followsInSync,
233
+ int? followsLive,
234
+ List<WorkspaceFollowStatus>? followStatuses,
235
+ DateTime? followReportAt,
236
+ int? preparingWorkspaces,
237
+ int? retryingWorkspaces,
238
+ String? preparationActivity,
239
+ bool clearPreparationActivity = false,
240
+ }) =>
241
+ SyncStatus(
242
+ phase: phase ?? this.phase,
243
+ ahead: ahead ?? (clearCounts ? null : this.ahead),
244
+ behind: behind ?? (clearCounts ? null : this.behind),
245
+ saved: saved ?? (clearCounts ? null : this.saved),
246
+ onMain: onMain ?? (clearCounts ? null : this.onMain),
247
+ deadLetters: deadLetters ?? (clearCounts ? null : this.deadLetters),
248
+ transfer: clearTransfer ? null : (transfer ?? this.transfer),
249
+ lastSyncedAt: lastSyncedAt ?? this.lastSyncedAt,
250
+ lastError: clearError ? null : (lastError ?? this.lastError),
251
+ lastResult: lastResult ?? this.lastResult,
252
+ modelAdoption: modelAdoption ?? this.modelAdoption,
253
+ live: live ?? this.live,
254
+ followed: followed ?? this.followed,
255
+ followsInSync: followsInSync ?? this.followsInSync,
256
+ followsLive: followsLive ?? this.followsLive,
257
+ followStatuses: followStatuses ?? this.followStatuses,
258
+ followReportAt: followReportAt ?? this.followReportAt,
259
+ preparingWorkspaces: preparingWorkspaces ?? this.preparingWorkspaces,
260
+ retryingWorkspaces: retryingWorkspaces ?? this.retryingWorkspaces,
261
+ preparationActivity: clearPreparationActivity
262
+ ? null
263
+ : preparationActivity ?? this.preparationActivity,
264
+ );
265
+ }
266
+
267
+ bool _sameTransfer(TransferProgress? left, TransferProgress? right) =>
268
+ identical(left, right) ||
269
+ (left != null &&
270
+ right != null &&
271
+ left.received == right.received &&
272
+ left.total == right.total &&
273
+ left.kind == right.kind);
274
+
275
+ bool _sameFollows(
276
+ List<WorkspaceFollowStatus> left, List<WorkspaceFollowStatus> right) {
277
+ if (identical(left, right)) return true;
278
+ if (left.length != right.length) return false;
279
+ for (var i = 0; i < left.length; i++) {
280
+ final a = left[i];
281
+ final b = right[i];
282
+ if (a.workspace != b.workspace ||
283
+ a.phase != b.phase ||
284
+ a.live != b.live ||
285
+ a.detail != b.detail ||
286
+ a.retryAt != b.retryAt ||
287
+ a.dueAt != b.dueAt ||
288
+ a.attempt != b.attempt ||
289
+ a.activeEffect != b.activeEffect) return false;
290
+ }
291
+ return true;
292
+ }
293
+
294
+ bool _sameStatus(SyncStatus left, SyncStatus right) =>
295
+ left.phase == right.phase &&
296
+ left.ahead == right.ahead &&
297
+ left.behind == right.behind &&
298
+ left.saved == right.saved &&
299
+ left.onMain == right.onMain &&
300
+ left.deadLetters == right.deadLetters &&
301
+ _sameTransfer(left.transfer, right.transfer) &&
302
+ left.lastSyncedAt == right.lastSyncedAt &&
303
+ left.lastError == right.lastError &&
304
+ identical(left.lastResult, right.lastResult) &&
305
+ identical(left.modelAdoption, right.modelAdoption) &&
306
+ left.live == right.live &&
307
+ left.followed == right.followed &&
308
+ left.followsInSync == right.followsInSync &&
309
+ left.followsLive == right.followsLive &&
310
+ _sameFollows(left.followStatuses, right.followStatuses) &&
311
+ left.followReportAt == right.followReportAt &&
312
+ left.preparingWorkspaces == right.preparingWorkspaces &&
313
+ left.retryingWorkspaces == right.retryingWorkspaces &&
314
+ left.preparationActivity == right.preparationActivity;
315
+
316
+ /// One projection owner for application-wide and per-session sync status.
317
+ /// It has no timers, networking, retries, credentials, or custody authority.
318
+ final class NmsSyncStatusProjection {
319
+ final _changes = StreamController<void>.broadcast();
320
+
321
+ /// Invalidates views even when two workspace changes leave the total unchanged.
322
+ Stream<void> get changes => _changes.stream;
323
+ late final StreamController<SyncStatus> _application =
324
+ StreamController<SyncStatus>.broadcast(onListen: _seedApplication);
325
+ final Map<String, SyncStatus> _bySession = {};
326
+ final Map<String, StreamController<SyncStatus>> _sessionStreams = {};
327
+ SyncStatus _current = const SyncStatus();
328
+ int? _followed;
329
+ int? _followsInSync;
330
+ int? _followsLive;
331
+ List<WorkspaceFollowStatus> _followStatuses = const [];
332
+ DateTime? _followReportAt;
333
+ int _preparingWorkspaces = 0;
334
+ int _retryingWorkspaces = 0;
335
+ String? _preparationActivity;
336
+
337
+ Stream<SyncStatus> get applicationStream => _application.stream;
338
+ SyncStatus get applicationStatus => _current;
339
+
340
+ Stream<SyncStatus> sessionStream(String session) => _sessionStreams
341
+ .putIfAbsent(
342
+ session,
343
+ () => StreamController<SyncStatus>.broadcast(
344
+ onListen: () => _seedSession(session),
345
+ ),
346
+ )
347
+ .stream;
348
+
349
+ SyncStatus statusFor(String session) =>
350
+ _bySession[session] ?? const SyncStatus();
351
+
352
+ void ensureSession(String session, SyncStatus initial) {
353
+ if (_bySession.containsKey(session)) return;
354
+ _bySession[session] = initial;
355
+ if (!_changes.isClosed) _changes.add(null);
356
+ }
357
+
358
+ /// The runtime's application-wide follow inventory (`FollowsSummary`).
359
+ void updateFollows(int followed, int inSync,
360
+ {int? live,
361
+ List<WorkspaceFollowStatus> statuses = const [],
362
+ DateTime? observedAt}) {
363
+ _followed = followed;
364
+ _followsInSync = inSync;
365
+ _followsLive = live;
366
+ _followStatuses = List.unmodifiable(statuses);
367
+ _followReportAt = observedAt;
368
+ _pushApplication(
369
+ _current.copyWith(
370
+ followed: followed,
371
+ followsInSync: inSync,
372
+ followsLive: live,
373
+ followStatuses: _followStatuses,
374
+ followReportAt: _followReportAt),
375
+ );
376
+ }
377
+
378
+ /// Fold the peer's disposable preparation report into the existing status
379
+ /// stream. No lifecycle decision, read or maintenance request occurs here.
380
+ void updatePreparationCounts(int preparing, int retrying,
381
+ {String? activity}) {
382
+ _preparingWorkspaces = preparing;
383
+ _retryingWorkspaces = retrying;
384
+ _preparationActivity = activity;
385
+ _pushApplication(_current.copyWith(
386
+ preparingWorkspaces: preparing,
387
+ retryingWorkspaces: retrying,
388
+ preparationActivity: activity,
389
+ clearPreparationActivity: activity == null,
390
+ ));
391
+ }
392
+
393
+ void update({
394
+ required String? session,
395
+ required Iterable<String> connectedSessions,
396
+ required SyncStatus Function(SyncStatus current) change,
397
+ }) {
398
+ if (session == null || session.isEmpty) {
399
+ _pushApplication(change(_current));
400
+ return;
401
+ }
402
+ pushSession(session, change(statusFor(session)));
403
+ foldApplication(connectedSessions);
404
+ }
405
+
406
+ void pushSession(String session, SyncStatus status) {
407
+ final previous = _bySession[session];
408
+ if (previous != null && _sameStatus(previous, status)) return;
409
+ _bySession[session] = status;
410
+ if (!_changes.isClosed) _changes.add(null);
411
+ final controller = _sessionStreams[session];
412
+ if (controller != null && !controller.isClosed) controller.add(status);
413
+ }
414
+
415
+ void foldApplication(Iterable<String> connectedSessions) {
416
+ final statuses =
417
+ connectedSessions.toSet().map(statusFor).toList(growable: false);
418
+ if (statuses.isEmpty) {
419
+ _pushApplication(
420
+ SyncStatus(
421
+ followed: _followed,
422
+ followsInSync: _followsInSync,
423
+ followsLive: _followsLive,
424
+ followStatuses: _followStatuses,
425
+ followReportAt: _followReportAt,
426
+ preparingWorkspaces: _preparingWorkspaces,
427
+ retryingWorkspaces: _retryingWorkspaces,
428
+ preparationActivity: _preparationActivity,
429
+ ),
430
+ );
431
+ return;
432
+ }
433
+
434
+ final phase = statuses.any((status) => status.phase == SyncPhase.error)
435
+ ? SyncPhase.error
436
+ : statuses.any((status) => status.phase == SyncPhase.offline)
437
+ ? SyncPhase.offline
438
+ : statuses.any(
439
+ (status) => status.presentationPhase == SyncPhase.transferring,
440
+ )
441
+ ? SyncPhase.transferring
442
+ : statuses.any(
443
+ (status) =>
444
+ status.phase == SyncPhase.behind ||
445
+ (status.behind ?? 0) > 0,
446
+ )
447
+ ? SyncPhase.behind
448
+ : statuses.any(
449
+ (status) =>
450
+ status.phase == SyncPhase.syncing ||
451
+ (status.ahead ?? 0) > 0,
452
+ )
453
+ ? SyncPhase.syncing
454
+ : statuses.any((status) =>
455
+ status.phase ==
456
+ SyncPhase.awaitingRemoteDurability)
457
+ ? SyncPhase.awaitingRemoteDurability
458
+ : statuses.any((status) =>
459
+ status.phase == SyncPhase.checking)
460
+ ? SyncPhase.checking
461
+ : SyncPhase.idle;
462
+
463
+ int? total(int? Function(SyncStatus status) read) {
464
+ var sum = 0;
465
+ for (final status in statuses) {
466
+ final count = read(status);
467
+ if (count == null) return null;
468
+ sum += count;
469
+ }
470
+ return sum;
471
+ }
472
+
473
+ // "Last confirmed" is the oldest moment any connected workspace was KNOWN current. A settled
474
+ // workspace whose live link is open is current now (its upstream pushes every change), so it
475
+ // never holds the answer back; the rest contribute their last settled round.
476
+ bool liveNow(SyncStatus status) =>
477
+ status.live && status.phase == SyncPhase.idle;
478
+ final allLive = statuses.every(liveNow);
479
+ DateTime? lastSynced;
480
+ String? error;
481
+ TransferProgress? transfer;
482
+ for (final status in statuses) {
483
+ final at = status.lastSyncedAt;
484
+ if (!liveNow(status) &&
485
+ at != null &&
486
+ (lastSynced == null || at.isBefore(lastSynced))) {
487
+ lastSynced = at;
488
+ }
489
+ error ??= status.lastError;
490
+ transfer ??= status.transfer;
491
+ }
492
+ if (statuses
493
+ .any((status) => !liveNow(status) && status.lastSyncedAt == null)) {
494
+ lastSynced = null;
495
+ }
496
+ if (allLive) lastSynced = DateTime.now();
497
+
498
+ _pushApplication(
499
+ SyncStatus(
500
+ phase: phase,
501
+ ahead: total((status) => status.ahead),
502
+ behind: total((status) => status.behind),
503
+ saved: total((status) => status.saved),
504
+ onMain: total((status) => status.onMain),
505
+ deadLetters: total((status) => status.deadLetters),
506
+ transfer: transfer,
507
+ lastSyncedAt: lastSynced,
508
+ lastError: error,
509
+ live: allLive,
510
+ followed: _followed,
511
+ followsInSync: _followsInSync,
512
+ followsLive: _followsLive,
513
+ followStatuses: _followStatuses,
514
+ followReportAt: _followReportAt,
515
+ preparingWorkspaces: _preparingWorkspaces,
516
+ retryingWorkspaces: _retryingWorkspaces,
517
+ preparationActivity: _preparationActivity,
518
+ ),
519
+ );
520
+ }
521
+
522
+ void _pushApplication(SyncStatus status) {
523
+ if (_sameStatus(_current, status)) return;
524
+ _current = status;
525
+ if (!_application.isClosed) _application.add(status);
526
+ }
527
+
528
+ void _seedApplication() {
529
+ if (!_application.isClosed) _application.add(_current);
530
+ }
531
+
532
+ void _seedSession(String session) {
533
+ final controller = _sessionStreams[session];
534
+ if (controller != null && !controller.isClosed) {
535
+ controller.add(statusFor(session));
536
+ }
537
+ }
538
+
539
+ Future<void> close() async {
540
+ if (!_changes.isClosed) unawaited(_changes.close());
541
+ final streams = _sessionStreams.values.toList(growable: false);
542
+ _sessionStreams.clear();
543
+ for (final stream in streams) {
544
+ if (!stream.isClosed) unawaited(stream.close());
545
+ }
546
+ // A paused UI subscriber may not process the done event until it resumes.
547
+ // Closing this broadcast stream must not hold reception teardown open.
548
+ if (!_application.isClosed) unawaited(_application.close());
549
+ }
550
+ }