@githolon/dsl 0.102.5 → 0.103.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 (131) hide show
  1. package/compile_package.mjs +43 -2
  2. package/compile_proof.d.mts +47 -0
  3. package/compile_proof.mjs +343 -0
  4. package/dart/CHANGELOG.md +7 -0
  5. package/dart/lib/src/dev_cloud.dart +8 -8
  6. package/dart/lib/src/nms_generated_runtime.dart +2 -0
  7. package/dart/lib/src/runner/node-entry.mjs +5504 -1236
  8. package/dart/lib/src/runner/offline-install.mjs +4578 -528
  9. package/dart/lib/src/test_harness.dart +206 -152
  10. package/dart/lib/src/webview_bridge.dart +196 -89
  11. package/dart/pubspec.lock +1 -1
  12. package/dart/pubspec.yaml +2 -2
  13. package/dart/test/acquire_session_credentials_test.dart +4 -3
  14. package/dart/test/adv_realm_interference_test.dart +7 -6
  15. package/dart/test/cloud_bound_topology_test.dart +3 -3
  16. package/dart/test/cluster_births_test.dart +4 -3
  17. package/dart/test/cluster_device_keyed_acceptance_test.dart +10 -16
  18. package/dart/test/cluster_local_autosync_test.dart +3 -2
  19. package/dart/test/cluster_scale_test.dart +3 -2
  20. package/dart/test/dlq_session_surface_test.dart +7 -6
  21. package/dart/test/first_app_gauntlet_test.dart +14 -21
  22. package/dart/test/fixtures/wedge/fake-install.mjs +1 -1
  23. package/dart/test/four_layer_nested_topology_test.dart +4 -4
  24. package/dart/test/harness_phase_timeout_test.dart +2 -2
  25. package/dart/test/local_sync_inert_test.dart +1 -1
  26. package/dart/test/parent_signer_recovery_test.dart +2 -2
  27. package/dart/test/sealing_client_test.dart +4 -3
  28. package/dart/test/session_bound_test.dart +8 -7
  29. package/dart/test/test_harness_test.dart +13 -12
  30. package/dart/test/vo_read_typing_test.dart +4 -3
  31. package/dart/test/watch_after_offer_stall_test.dart +4 -3
  32. package/dart/test/webview_bridge_test.dart +102 -5
  33. package/dart_types/CHANGELOG.md +5 -0
  34. package/dart_types/lib/nomos_types.dart +9 -0
  35. package/dart_types/lib/src/nomos_ref.dart +20 -0
  36. package/dart_types/lib/src/optional_ref.dart +85 -0
  37. package/dart_types/lib/src/sha256_digest.dart +41 -0
  38. package/dart_types/lib/src/versioned_nomos_ref.dart +81 -0
  39. package/dart_types/pubspec.yaml +1 -1
  40. package/dart_types/test/optional_ref_test.dart +44 -0
  41. package/dart_types/test/sha256_digest_test.dart +18 -0
  42. package/package.json +9 -5
  43. package/runtime/generated-application-host.js +5365 -1105
  44. package/src/aggregate.ts +18 -0
  45. package/src/application_composition.ts +6 -32
  46. package/src/attested_core.ts +6 -3
  47. package/src/birth_meter.ts +1 -0
  48. package/src/birth_meter_ensure.ts +28 -2
  49. package/src/births.ts +1 -1
  50. package/src/build_package.ts +162 -538
  51. package/src/business_birth.ts +19 -3
  52. package/src/canonical_json.ts +21 -0
  53. package/src/check_package_main.ts +73 -33
  54. package/src/clear_directive.ts +19 -8
  55. package/src/codegen_dart.ts +305 -22
  56. package/src/codegen_dart_application.ts +844 -292
  57. package/src/codegen_dart_client.ts +1084 -407
  58. package/src/codegen_dart_scenario.ts +12 -12
  59. package/src/codegen_proof.ts +169 -39
  60. package/src/codegen_provider_dart.ts +17 -1
  61. package/src/codegen_ts.ts +298 -106
  62. package/src/compat_manifest.ts +2 -2
  63. package/src/compat_package_main.ts +8 -12
  64. package/src/compile_package_main.ts +1858 -590
  65. package/src/compose.ts +1 -1
  66. package/src/directive.ts +208 -15
  67. package/src/engine_entry.ts +165 -111
  68. package/src/evolve_preview.ts +29 -69
  69. package/src/extremum.ts +1 -1
  70. package/src/fields.ts +183 -4
  71. package/src/flow_gate.ts +75 -2
  72. package/src/framework/application_home.ts +2 -0
  73. package/src/framework/auth_provider_law.ts +2 -2
  74. package/src/framework/birth_meter.ts +7 -19
  75. package/src/framework/bootstrap.ts +0 -2
  76. package/src/framework/business_corrections.ts +9 -5
  77. package/src/framework/domain_lifecycle.ts +15 -29
  78. package/src/framework/encryption_scope.ts +2 -1
  79. package/src/framework/framework_aggregates.ts +2 -0
  80. package/src/framework/genesis.ts +16 -14
  81. package/src/framework/home.ts +20 -14
  82. package/src/framework/owned_custody.ts +9 -3
  83. package/src/framework/platform_admin.ts +3 -3
  84. package/src/framework/relations.ts +21 -13
  85. package/src/framework/signing_identity.ts +45 -40
  86. package/src/framework/workspace_invariant.ts +245 -0
  87. package/src/framework/workspaces.ts +23 -29
  88. package/src/index.ts +36 -16
  89. package/src/intent.ts +178 -6
  90. package/src/law_check.ts +32 -11
  91. package/src/law_graph.ts +2902 -0
  92. package/src/law_graph_usda.ts +1799 -0
  93. package/src/law_identity.ts +435 -0
  94. package/src/law_value_type.ts +230 -0
  95. package/src/lsp_server.ts +58 -29
  96. package/src/ops.ts +7 -0
  97. package/src/package_config.ts +141 -0
  98. package/src/package_dependencies.ts +108 -0
  99. package/src/package_dependency_usage.ts +150 -0
  100. package/src/package_source_imports.ts +71 -0
  101. package/src/plan_source_slice.ts +335 -0
  102. package/src/point_guard.ts +192 -0
  103. package/src/precondition.ts +44 -9
  104. package/src/read_projection_namespace.ts +1 -1
  105. package/src/runtime_zod.ts +44 -0
  106. package/src/secondary_ensure.ts +67 -14
  107. package/src/shape_compat.ts +1 -1
  108. package/src/stable_id_identity_bridge.ts +149 -0
  109. package/src/stable_id_lockfile.ts +140 -6
  110. package/src/stable_ids.ts +1 -1
  111. package/src/stable_ids_for_module.ts +348 -0
  112. package/src/stable_ids_types.ts +1 -1
  113. package/src/sum.ts +1 -1
  114. package/src/usd_state.ts +1 -1
  115. package/src/usda_relationship_integrity.ts +66 -0
  116. package/src/usda_text.ts +18 -0
  117. package/src/usdz.ts +78 -0
  118. package/src/value_object.ts +95 -0
  119. package/src/wire.ts +19 -0
  120. package/src/wire_encode.ts +22 -0
  121. package/src/workspace_sharding.ts +52 -36
  122. package/src/workspace_sharing.ts +28 -4
  123. package/src/zod_introspection.ts +1 -1
  124. package/dart/test/cluster_attested_test.dart +0 -201
  125. package/src/compile_engine.ts +0 -595
  126. package/src/manifest.ts +0 -1476
  127. package/src/read_projection_composition.ts +0 -117
  128. package/src/read_projection_manifest.ts +0 -115
  129. package/src/relation_composition.ts +0 -286
  130. package/src/usd.ts +0 -1967
  131. package/src/usd_layers.ts +0 -1776
@@ -27,10 +27,17 @@ import 'package:nomos_types/nomos_types.dart'
27
27
  NomosTraceContext,
28
28
  NomosErrorInfo,
29
29
  NomosRef,
30
+ VersionedNomosRef,
31
+ ReferenceFreshness,
32
+ ReferenceCurrent,
33
+ ReferenceStale,
34
+ ReferenceFreshnessUnavailable,
30
35
  CustodyBreakPolicy,
31
36
  NomosCustodyEvent,
32
37
  NomosCustodyStatus;
33
38
  import 'package:nomos_types/src/nomos_ref.dart' show NmsInternalNomosRefs;
39
+ import 'package:nomos_types/src/versioned_nomos_ref.dart'
40
+ show NmsInternalVersionedNomosRefs;
34
41
  import 'package:nomos_types/src/foreign_ref.dart'
35
42
  show NomosForeignReader, NomosForeignWorkspaceReader;
36
43
 
@@ -43,6 +50,17 @@ import 'session_bound.dart' show DomainNotInstalledForSession;
43
50
  /// bridge root so app code reads as the four-layer split (engine → session → client → values).
44
51
  typedef NomosEngine = NomosBridge;
45
52
 
53
+ /// One stable bounded page from an authenticated declared-query index. [cursor] is opaque framework
54
+ /// state: pass it back unchanged to request the next page; [done] means this page exhausted the query.
55
+ final class NomosPage<T> {
56
+ const NomosPage(
57
+ {required this.rows, required this.cursor, required this.done});
58
+
59
+ final List<T> rows;
60
+ final String? cursor;
61
+ final bool done;
62
+ }
63
+
46
64
  /// The protocol version the runner and the bridge agree on (`webview-runner.mjs` `WEBVIEW_BRIDGE_VERSION`).
47
65
  const int kWebviewBridgeVersion = 1;
48
66
 
@@ -1498,7 +1516,7 @@ class NomosBridge {
1498
1516
  {'id': id, if (session != null) 'session': session}));
1499
1517
 
1500
1518
  /// SALVAGE — best-effort extraction of this holon's local-only authored work (un-synced pending
1501
- /// writes + the DLQ), each with its full `intentB64` bytes: `{pendingWrites, deadLetters}`. The
1519
+ /// writes + the DLQ), each with its full `changeB64` bytes: `{pendingWrites, deadLetters}`. The
1502
1520
  /// read half of the recovery lane (`connect(recover: true)` runs salvage → [absorbSalvage]).
1503
1521
  Future<Map<String, Object?>> salvage({String? session}) async =>
1504
1522
  _asMap(await _call('salvage', {if (session != null) 'session': session}));
@@ -1537,11 +1555,9 @@ class NomosBridge {
1537
1555
  /// `encodeIntentOfferBytes`, with `security.actor` included iff [actor] is set). The generated Dart client
1538
1556
  /// calls THIS — it never hand-builds offer bytes, so there is exactly ONE copy of the wire format. Returns
1539
1557
  /// the new local head; durable on [sync]. ([offer] with opaque bytes stays for back-compat carriers.)
1540
- /// [attestations] hand-injects the signed foreign-read envelopes a captured-read directive consumes
1541
- /// (each `{queryId, result, attestation}` — the shape the kernel's attested-read gate re-derives against
1542
- /// its trust policy); [force] carries the deploy/evolve force flag through. Both are OMITTED from the wire
1543
- /// when absent, so a plain offer is byte-identical to before. [session] pins the colocated workspace in a
1544
- /// cluster harness (absent ⇒ the runner's sole-session default).
1558
+ /// Declared foreign reads are resolved and proved inside the shared kernel plane; callers never fetch or
1559
+ /// inject evidence. [force] carries the deploy/evolve force flag through. [session] pins the workspace in
1560
+ /// a cluster harness (absent ⇒ the runner's sole-session default).
1545
1561
  @Deprecated(
1546
1562
  'Engine-level string-y writes are the footgun (session_bound_clients.md ask 1/7): '
1547
1563
  'dispatch through a generated <Domain>Client bound to a NomosSession '
@@ -1553,7 +1569,6 @@ class NomosBridge {
1553
1569
  required Object? payload,
1554
1570
  String? actor,
1555
1571
  String? domainHash,
1556
- List<Map<String, Object?>>? attestations,
1557
1572
  List<Map<String, Object?>>? nomosConflictSnapshots,
1558
1573
  bool? force,
1559
1574
  String? session,
@@ -1567,7 +1582,6 @@ class NomosBridge {
1567
1582
  'payload': payload,
1568
1583
  if (actor != null) 'actor': actor,
1569
1584
  if (domainHash != null) 'domainHash': domainHash,
1570
- if (attestations != null) 'attestations': attestations,
1571
1585
  if (nomosConflictSnapshots != null)
1572
1586
  '__nomosConflictSnapshots': nomosConflictSnapshots,
1573
1587
  if (force != null) 'force': force,
@@ -1591,7 +1605,6 @@ class NomosBridge {
1591
1605
  required Object? payload,
1592
1606
  String? actor,
1593
1607
  String? domainHash,
1594
- List<Map<String, Object?>>? attestations,
1595
1608
  List<Map<String, Object?>>? nomosConflictSnapshots,
1596
1609
  bool? force,
1597
1610
  String? session,
@@ -1603,7 +1616,6 @@ class NomosBridge {
1603
1616
  payload: payload,
1604
1617
  actor: actor,
1605
1618
  domainHash: domainHash,
1606
- attestations: attestations,
1607
1619
  nomosConflictSnapshots: nomosConflictSnapshots,
1608
1620
  force: force,
1609
1621
  session: session,
@@ -1621,7 +1633,6 @@ class NomosBridge {
1621
1633
  required Object? payload,
1622
1634
  String? actor,
1623
1635
  String? domainHash,
1624
- List<Map<String, Object?>>? attestations,
1625
1636
  List<Map<String, Object?>>? nomosConflictSnapshots,
1626
1637
  bool? force,
1627
1638
  String? session,
@@ -1644,7 +1655,6 @@ class NomosBridge {
1644
1655
  'payload': payload,
1645
1656
  if (actor != null) 'actor': actor,
1646
1657
  if (domainHash != null) 'domainHash': domainHash,
1647
- if (attestations != null) 'attestations': attestations,
1648
1658
  if (nomosConflictSnapshots != null)
1649
1659
  '__nomosConflictSnapshots': nomosConflictSnapshots,
1650
1660
  if (force != null) 'force': force,
@@ -1786,6 +1796,22 @@ class NomosBridge {
1786
1796
  return (r['offer'] as Map?)?.cast<String, Object?>();
1787
1797
  }
1788
1798
 
1799
+ /// Prepare the exact authenticated execution slice a generated client is about to use. This is
1800
+ /// a readiness hint only: compilation, composition, cache custody and all trust decisions remain
1801
+ /// inside the kernel. Generated `bind` calls it so first-use cost is paid before the client becomes
1802
+ /// interactive, rather than on the person's first button press.
1803
+ Future<void> prepareDomain({
1804
+ required String domain,
1805
+ String? directiveId,
1806
+ required String packageHash,
1807
+ }) async {
1808
+ await _call('prepareDomain', {
1809
+ 'domain': domain,
1810
+ if (directiveId != null) 'directiveId': directiveId,
1811
+ 'packageHash': packageHash,
1812
+ });
1813
+ }
1814
+
1789
1815
  /// FOLLOW A [NomosRef] — THE cross-workspace primitive (session_bound_clients.md ask 8): mount
1790
1816
  /// the referenced workspace on this device's plane (idempotent — an already-open workspace is
1791
1817
  /// just returned) and hand back its [NomosSession]. Typed client construction is then one line:
@@ -1906,6 +1932,16 @@ class NomosBridge {
1906
1932
  (_asMap(await _call('mintId', {'typeTag': typeTag}))['id'] ?? '')
1907
1933
  .toString();
1908
1934
 
1935
+ /// @internal The immutable genesis of a sibling custody chain. This is observational metadata used to
1936
+ /// compose an ordinary source-enrolment offer; the kernel still reopens and proves every foreign read.
1937
+ Future<String> nomosWorkspaceGenesis(String workspace,
1938
+ {String? session}) async =>
1939
+ (_asMap(await _call('nomosWorkspaceGenesis', {
1940
+ 'workspace': workspace,
1941
+ if (session != null) 'session': session,
1942
+ }))['genesis'] ?? '')
1943
+ .toString();
1944
+
1909
1945
  /// UNDO the most-recent action via the kernel STRIKE primitive (the fold drops the struck intent by parity).
1910
1946
  /// [scope] `'all'` undoes any author's most-recent action; `'user'` only THIS replica's own. [actions] is the
1911
1947
  /// app's set of undoable directive ids (so undo never strikes a framework/control intent). [domain] +
@@ -1924,7 +1960,6 @@ class NomosBridge {
1924
1960
  required Object? payload,
1925
1961
  String? actor,
1926
1962
  String? domainHash,
1927
- List<Map<String, Object?>>? attestations,
1928
1963
  List<Map<String, Object?>>? nomosConflictSnapshots,
1929
1964
  bool? force,
1930
1965
  String? session,
@@ -1935,21 +1970,10 @@ class NomosBridge {
1935
1970
  payload: payload,
1936
1971
  actor: actor,
1937
1972
  domainHash: domainHash,
1938
- attestations: attestations,
1939
1973
  nomosConflictSnapshots: nomosConflictSnapshots,
1940
1974
  force: force,
1941
1975
  session: session);
1942
1976
 
1943
- /// ATTESTED READ — serve + SIGN a declared query over a COLOCATED source session's certified fold (the
1944
- /// dynamic-source trust lane; architecture/dynamic_source_attested_reads.md). In a [NomosTestHarness.cluster]
1945
- /// (one engine plane, many sessions) this fulfils the read IN-PROCESS from the [from] session and returns
1946
- /// `{ entry: {queryId, result, attestation:{source_genesis, public_key, …}} }` — pass `result['entry']`
1947
- /// (the whole entry map) as one [offerDirective] `attestations` element, or read
1948
- /// `entry['attestation']['source_genesis']` to enrol the source in a reader's allow-list. [args] are the
1949
- /// query params. [session] is the caller/reader session (defaults to this view's bound session).
1950
- /// [attestorSecret] is the source's base64 read-attestor secret (from [authorKeygen]); when omitted the
1951
- /// source session's connect-time `authorSecret` signs. [nowMs] stamps the freshness clock (defaults to the
1952
- /// source's own clock).
1953
1977
  /// RELEASE COHERENCE — assert the connected cloud's kernel supports every capability this law needs (the
1954
1978
  /// generated client passes its baked `requiresKernel`, e.g. `['compoundKeys']` for a compound-key query).
1955
1979
  /// Throws a typed [KernelCoherenceError] on a skew (a stale/mismatched cloud kernel) with the named reason,
@@ -1964,23 +1988,6 @@ class NomosBridge {
1964
1988
  });
1965
1989
  }
1966
1990
 
1967
- Future<Map<String, Object?>> attestedRead({
1968
- required String from,
1969
- required String queryId,
1970
- Map<String, Object?> args = const {},
1971
- String? attestorSecret,
1972
- int? nowMs,
1973
- String? session,
1974
- }) async =>
1975
- _asMap(await _call('attestedRead', {
1976
- 'from': from,
1977
- 'queryId': queryId,
1978
- 'args': args,
1979
- if (attestorSecret != null) 'attestorSecret': attestorSecret,
1980
- if (nowMs != null) 'nowMs': nowMs,
1981
- if (session != null) 'session': session,
1982
- }));
1983
-
1984
1991
  /// MAKE A WORKSPACE — the one-call tenant path. A child workspace under a platform/[parent] is a SIGNED
1985
1992
  /// control-plane offer; the holon seals `createWorkspace` (or [directiveId] `birthChild`, or ANY
1986
1993
  /// tenant-defined `.births()` directive — see [domain]) with the requester's device key ([authorSecret],
@@ -2196,8 +2203,10 @@ class NomosBridge {
2196
2203
 
2197
2204
  /// Watch the RUNTIME activity feed: a stream that re-emits the newest-first activity rows on every local
2198
2205
  /// change (doorbell/sync tick). `base` = a commit-oid cursor. Cancelling drops the watch on the runner.
2199
- Stream<List<dynamic>> watchActivity({String? base, String? session}) =>
2200
- _watchStream(kind: 'activity', base: base, session: session);
2206
+ Stream<List<dynamic>> watchActivity(
2207
+ {String? base, int? limit, String? session}) =>
2208
+ _watchStream(
2209
+ kind: 'activity', base: base, limit: limit, session: session);
2201
2210
 
2202
2211
  /// Mint an ed25519 DEVICE KEY in-kernel → `{secret, public}` (base64). The SECRET stays on this device
2203
2212
  /// (persist it in secure storage); pass it to [connect] as `authorSecret` (or per-offer `signWith`) so the
@@ -2388,16 +2397,46 @@ class NomosBridge {
2388
2397
  /// behavior). `principal` stamps the private-read gate (defaults to the connection's bound principal).
2389
2398
  /// Both are OMITTED from the wire when absent, so a public un-keyed read is byte-identical to before.
2390
2399
  Future<List<Map<String, dynamic>>> query(String queryId,
2391
- [Map<String, Object?> params = const {},
2392
- List<NomosScopeKey>? keys,
2393
- String? principal,
2394
- String? session]) async =>
2395
- _asRows(await _call('query', {
2396
- 'queryId': queryId,
2397
- 'params': params,
2398
- ...?_readAuthArgs(keys, principal),
2399
- if (session != null) 'session': session,
2400
- }));
2400
+ [Map<String, Object?> params = const {},
2401
+ List<NomosScopeKey>? keys,
2402
+ String? principal,
2403
+ String? session]) async {
2404
+ return _asRows(await _call('query', {
2405
+ 'queryId': queryId,
2406
+ 'params': params,
2407
+ ...?_readAuthArgs(keys, principal),
2408
+ if (session != null) 'session': session,
2409
+ }));
2410
+ }
2411
+
2412
+ /// One bounded query page. Unlike [query], this never requires the kernel to materialize or serialize
2413
+ /// the complete result set merely to show the first screen.
2414
+ Future<NomosPage<Map<String, dynamic>>> queryPage(
2415
+ String queryId, {
2416
+ Map<String, Object?> params = const {},
2417
+ int pageSize = 100,
2418
+ String? cursor,
2419
+ List<NomosScopeKey>? keys,
2420
+ String? principal,
2421
+ String? session,
2422
+ }) async {
2423
+ if (pageSize < 1 || pageSize > 1000) {
2424
+ throw RangeError.range(pageSize, 1, 1000, 'pageSize');
2425
+ }
2426
+ final result = _asMap(await _call('queryPage', {
2427
+ 'queryId': queryId,
2428
+ 'params': params,
2429
+ 'pageSize': pageSize,
2430
+ if (cursor != null) 'cursor': cursor,
2431
+ ...?_readAuthArgs(keys, principal),
2432
+ if (session != null) 'session': session,
2433
+ }));
2434
+ return NomosPage<Map<String, dynamic>>(
2435
+ rows: _asRows(result),
2436
+ cursor: result['cursor'] as String?,
2437
+ done: result['done'] == true,
2438
+ );
2439
+ }
2401
2440
 
2402
2441
  /// A single aggregate by id (the by-id read). `keys`/`principal`: see [query].
2403
2442
  Future<List<Map<String, dynamic>>> queryById(String id,
@@ -2410,6 +2449,42 @@ class NomosBridge {
2410
2449
  if (session != null) 'session': session,
2411
2450
  }));
2412
2451
 
2452
+ /// Compare an immutable publication pin with the stable series' one compiled
2453
+ /// current pointer. This is an O(current-state) check: it neither resolves the
2454
+ /// pinned publication nor reconstructs aggregate history.
2455
+ Future<ReferenceFreshness<TVersion>> checkReferenceFreshness<TVersion, TSeries>(
2456
+ VersionedNomosRef<TVersion, TSeries> reference,
2457
+ ) async {
2458
+ final result = _asMap(await _call('checkRefFreshness', {
2459
+ 'refs': [
2460
+ {
2461
+ 'pinned': NmsInternalNomosRefs.toJson(reference.pinned),
2462
+ 'series': NmsInternalNomosRefs.toJson(reference.series),
2463
+ 'currentField':
2464
+ NmsInternalVersionedNomosRefs.currentField(reference),
2465
+ },
2466
+ ],
2467
+ }));
2468
+ final unique = result['unique'] as List?;
2469
+ final first = unique == null || unique.isEmpty
2470
+ ? const <String, Object?>{}
2471
+ : _asMap(unique.first);
2472
+ final outcome = _asMap(first['outcome']);
2473
+ final status = (outcome['status'] ?? 'unknown').toString();
2474
+ if (status == 'current') {
2475
+ return ReferenceCurrent<TVersion>(reference.pinned);
2476
+ }
2477
+ if (status == 'stale') {
2478
+ final latest = outcome['latest'];
2479
+ if (latest != null) {
2480
+ return ReferenceStale<TVersion>(
2481
+ NmsInternalNomosRefs.fromJson<TVersion>(latest),
2482
+ );
2483
+ }
2484
+ }
2485
+ return ReferenceFreshnessUnavailable<TVersion>(status);
2486
+ }
2487
+
2413
2488
  /// Verified points in this workspace's history, oldest to newest. The generated client presents these
2414
2489
  /// as `client.past.moments()`; callers pass the opaque [NomosMoment] back to `past.at(moment)` and never
2415
2490
  /// need to know that the framework uses commit identities underneath.
@@ -2592,11 +2667,12 @@ class NomosBridge {
2592
2667
 
2593
2668
  /// FRAMEWORK-INTERNAL business birth: derive the unborn child's owned key from its deterministic address.
2594
2669
  Future<NomosScopeKey> nomosOwnedBirthScopeKey(
2595
- String scope, String namespace, Object? key) async {
2670
+ String scope, String namespace, Object? key, String recordType, String recordId) async {
2596
2671
  final out = _asMap(await _call('cryptoOwnedBirthScopeKey', {
2597
2672
  'scope': scope,
2598
2673
  'namespace': namespace,
2599
2674
  'key': key,
2675
+ 'record': {'type': recordType, 'id': recordId},
2600
2676
  }));
2601
2677
  return NomosScopeKey(
2602
2678
  scope: (out['scope'] ?? scope).toString(),
@@ -2876,13 +2952,15 @@ class NomosBridge {
2876
2952
  Stream<List<dynamic>> watch(String queryId,
2877
2953
  [Map<String, Object?> params = const {},
2878
2954
  String? session,
2879
- String? principal]) =>
2955
+ String? principal,
2956
+ int? intervalMs]) =>
2880
2957
  _watchStream(
2881
2958
  kind: 'query',
2882
2959
  queryId: queryId,
2883
2960
  params: params,
2884
2961
  session: session,
2885
- principal: principal);
2962
+ principal: principal,
2963
+ intervalMs: intervalMs);
2886
2964
  Stream<List<dynamic>> watchById(String id,
2887
2965
  [String? session, String? principal]) =>
2888
2966
  _watchStream(
@@ -3003,12 +3081,18 @@ class NomosBridge {
3003
3081
  final subId = 'c${++_eventPlane._seq}';
3004
3082
  late final StreamController<Map<String, Object?>> ctrl;
3005
3083
  ctrl = StreamController<Map<String, Object?>>(
3006
- onListen: () => _call('watch', {
3007
- 'subId': subId,
3008
- 'kind': 'compat',
3009
- 'requirement': requirement,
3010
- if (session != null) 'session': session,
3011
- }),
3084
+ onListen: () {
3085
+ unawaited(_call('watch', {
3086
+ 'subId': subId,
3087
+ 'kind': 'compat',
3088
+ 'requirement': requirement,
3089
+ if (session != null) 'session': session,
3090
+ }).then<void>((_) {}, onError: (Object error, StackTrace stackTrace) {
3091
+ if (!ctrl.isClosed && ctrl.hasListener) {
3092
+ ctrl.addError(error, stackTrace);
3093
+ }
3094
+ }));
3095
+ },
3012
3096
  onCancel: () {
3013
3097
  _eventPlane._compatWatches.remove(subId);
3014
3098
  // Cancellation is a local lifecycle boundary and must complete even if the runner is slow,
@@ -3029,25 +3113,35 @@ class NomosBridge {
3029
3113
  String? spatialId,
3030
3114
  NomosBbox? bbox,
3031
3115
  String? base,
3116
+ int? limit,
3032
3117
  String? session,
3033
- String? principal}) {
3118
+ String? principal,
3119
+ int? intervalMs}) {
3034
3120
  // Controllers live on the EVENT PLANE (where _onMessage dispatches); the registration `_call` stays
3035
3121
  // THIS instance's (a session's override stamps `args.session`) — see [_eventPlane].
3036
3122
  final subId = 'w${++_eventPlane._seq}';
3037
3123
  late final StreamController<List<dynamic>> ctrl;
3038
3124
  ctrl = StreamController<List<dynamic>>(
3039
- onListen: () => _call('watch', {
3040
- 'subId': subId,
3041
- 'kind': kind,
3042
- if (queryId != null) 'queryId': queryId,
3043
- if (params != null) 'params': params,
3044
- if (id != null) 'id': id,
3045
- if (spatialId != null) 'spatialId': spatialId,
3046
- if (bbox != null) 'bbox': bbox.toJson(),
3047
- if (base != null) 'base': base,
3048
- if (principal != null) 'principal': principal,
3049
- if (session != null) 'session': session,
3050
- }),
3125
+ onListen: () {
3126
+ unawaited(_call('watch', {
3127
+ 'subId': subId,
3128
+ 'kind': kind,
3129
+ if (queryId != null) 'queryId': queryId,
3130
+ if (params != null) 'params': params,
3131
+ if (id != null) 'id': id,
3132
+ if (spatialId != null) 'spatialId': spatialId,
3133
+ if (bbox != null) 'bbox': bbox.toJson(),
3134
+ if (base != null) 'base': base,
3135
+ if (limit != null) 'limit': limit,
3136
+ if (principal != null) 'principal': principal,
3137
+ if (intervalMs != null) 'intervalMs': intervalMs,
3138
+ if (session != null) 'session': session,
3139
+ }).then<void>((_) {}, onError: (Object error, StackTrace stackTrace) {
3140
+ if (!ctrl.isClosed && ctrl.hasListener) {
3141
+ ctrl.addError(error, stackTrace);
3142
+ }
3143
+ }));
3144
+ },
3051
3145
  onCancel: () {
3052
3146
  _eventPlane._watches.remove(subId);
3053
3147
  // A caller owns its local subscription; it must never need a healthy transport in order to release
@@ -3062,28 +3156,41 @@ class NomosBridge {
3062
3156
  /// Tear down: stop the transport subscription + fail any in-flight calls.
3063
3157
  Future<void> dispose() async {
3064
3158
  await _sub.cancel();
3065
- for (final entry in _pending.entries) {
3066
- final c = entry.value;
3159
+ final pending = _pending.entries
3160
+ .map((entry) => (
3161
+ completer: entry.value,
3162
+ trace: _pendingTraces[entry.key] ?? const NomosTraceContext(),
3163
+ operation: _pendingOps[entry.key],
3164
+ ))
3165
+ .toList(growable: false);
3166
+ _pending.clear();
3167
+ _pendingOps.clear();
3168
+ _pendingTraces.clear();
3169
+ for (final entry in pending) {
3170
+ final c = entry.completer;
3067
3171
  if (!c.isCompleted) {
3068
- final trace = _pendingTraces[entry.key] ?? const NomosTraceContext();
3069
- final info = NomosErrorInfo.legacy('bridge disposed',
3172
+ final operation = entry.operation ?? 'unknown';
3173
+ final info = NomosErrorInfo.legacy(
3174
+ 'bridge disposed while $operation was pending',
3070
3175
  code: 'nomos.bridge.disposed',
3071
- operation: _pendingOps[entry.key],
3072
- trace: trace);
3176
+ operation: entry.operation,
3177
+ trace: entry.trace);
3073
3178
  c.completeError(nomosErrorFromInfo(info));
3074
3179
  }
3075
3180
  }
3076
- _pending.clear();
3077
- _pendingOps.clear();
3078
- _pendingTraces.clear();
3079
- for (final w in _watches.values) {
3080
- if (!w.isClosed) await w.close();
3081
- }
3181
+ final watches = _watches.values.toList(growable: false);
3082
3182
  _watches.clear();
3083
- for (final w in _compatWatches.values) {
3084
- if (!w.isClosed) await w.close();
3183
+ for (final w in watches) {
3184
+ // A paused consumer can keep StreamController.close() pending forever.
3185
+ // Disposal is the owner boundary: detach the controller immediately and
3186
+ // let its done event complete if/when that consumer resumes.
3187
+ if (!w.isClosed) unawaited(w.close());
3085
3188
  }
3189
+ final compatWatches = _compatWatches.values.toList(growable: false);
3086
3190
  _compatWatches.clear();
3191
+ for (final w in compatWatches) {
3192
+ if (!w.isClosed) unawaited(w.close());
3193
+ }
3087
3194
  _stopStatusPoll();
3088
3195
  if (!_syncEvents.isClosed) await _syncEvents.close();
3089
3196
  if (!_signalEvents.isClosed) await _signalEvents.close();
package/dart/pubspec.lock CHANGED
@@ -191,7 +191,7 @@ packages:
191
191
  path: "../dart_types"
192
192
  relative: true
193
193
  source: path
194
- version: "0.8.0"
194
+ version: "0.8.1"
195
195
  package_config:
196
196
  dependency: "direct main"
197
197
  description:
package/dart/pubspec.yaml CHANGED
@@ -8,7 +8,7 @@ description: >-
8
8
  Nomos 2 Dart frontend types — the hand-written kernel wire types plus the
9
9
  code-generated per-domain aggregate + directive-payload types. (De)serializes
10
10
  the EXACT serde JSON the Rust kernel + projection produce/consume.
11
- version: 0.64.1
11
+ version: 0.64.2
12
12
 
13
13
  environment:
14
14
  sdk: ^3.5.0
@@ -17,7 +17,7 @@ dependencies:
17
17
  # The pure-data core (wire types, offer codec, NomosReads, schema validation, error
18
18
  # info). nomos_client depends on and re-exports it, so existing imports are unchanged.
19
19
  # For local monorepo dev, pubspec_overrides.yaml points this at ../dart_types.
20
- nomos_types: ^0.8.0
20
+ nomos_types: ^0.8.1
21
21
  json_schema: ^5.2.2
22
22
  crypto: ^3.0.0
23
23
  package_config: ^2.1.0
@@ -20,7 +20,7 @@ import 'package:test/test.dart';
20
20
  String get _root => '${Directory.current.path}/../..';
21
21
  String get _repoEntry => '$_root/cloud/web-client/runner/node-entry.ts';
22
22
  String get _docsDeploy =>
23
- '$_root/bench/scale/fixtures/surfaced/build/surfaced.deploy.json';
23
+ '$_root/bench/scale/fixtures/surfaced/build/surfaced.law.usdz';
24
24
  String get _runtimeDir =>
25
25
  Platform.environment['NOMOS_DART_RUNTIME'] ??
26
26
  Platform.environment['NOMOS_OFFLINE_RUNTIME'] ??
@@ -34,7 +34,8 @@ String? _missingPrereq() {
34
34
  '(cd bench/scale/fixtures/surfaced && node ../../../../dsl/compile_package.mjs)';
35
35
  }
36
36
  if (!File('$_runtimeDir/holon-testmint.wasm').existsSync() ||
37
- !File('$_runtimeDir/packages.json').existsSync()) {
37
+ !File('$_runtimeDir/bootstrap.law.usdz').existsSync() ||
38
+ !File('$_runtimeDir/nomos.law.usdz').existsSync()) {
38
39
  return 'warm ~/.holon/runtime first (run any `githolon` command once)';
39
40
  }
40
41
  return null;
@@ -65,7 +66,7 @@ void main() {
65
66
  return;
66
67
  }
67
68
  final harness = await NomosTestHarness.ephemeral(
68
- deployJsonPaths: [_docsDeploy],
69
+ lawUsdzPaths: [_docsDeploy],
69
70
  workspace: 'acquire-home',
70
71
  nodeEntry: _repoEntry,
71
72
  runtimeDir: _runtimeDir,
@@ -28,13 +28,13 @@ String get _root => '${Directory.current.path}/../..';
28
28
  String get _repoEntry => '$_root/cloud/web-client/runner/node-entry.ts';
29
29
  String get _installer => '$_root/cloud/web-client/runner/offline-install.ts';
30
30
  String get _guestbook =>
31
- '$_root/examples/guestbook/build/guestbook.deploy.json';
31
+ '$_root/examples/guestbook/build/guestbook.law.usdz';
32
32
  String get _surfaced =>
33
- '$_root/bench/scale/fixtures/surfaced/build/surfaced.deploy.json';
33
+ '$_root/bench/scale/fixtures/surfaced/build/surfaced.law.usdz';
34
34
  String get _attributed =>
35
- '$_root/bench/scale/fixtures/attributed/build/attributed.deploy.json';
35
+ '$_root/bench/scale/fixtures/attributed/build/attributed.law.usdz';
36
36
  String get _estateBirth =>
37
- '$_root/bench/scale/fixtures/estate-birth/build/estate-birth.deploy.json';
37
+ '$_root/bench/scale/fixtures/estate-birth/build/estate-birth.law.usdz';
38
38
  String get _runtimeDir =>
39
39
  Platform.environment['NOMOS_DART_RUNTIME'] ??
40
40
  Platform.environment['NOMOS_OFFLINE_RUNTIME'] ??
@@ -53,7 +53,8 @@ String? _missingPrereq() {
53
53
  return 'missing $p (compile the fixture / example first)';
54
54
  }
55
55
  if (!File('$_runtimeDir/holon-testmint.wasm').existsSync() ||
56
- !File('$_runtimeDir/packages.json').existsSync()) {
56
+ !File('$_runtimeDir/bootstrap.law.usdz').existsSync() ||
57
+ !File('$_runtimeDir/nomos.law.usdz').existsSync()) {
57
58
  return 'warm ~/.holon/runtime first (run any `githolon` command once)';
58
59
  }
59
60
  return null;
@@ -499,7 +500,7 @@ void main() {
499
500
  final out = '${workdir.path}/$ws.bin';
500
501
  final r = await Process.run('node', [
501
502
  _installer,
502
- '--deploy',
503
+ '--law',
503
504
  _guestbook,
504
505
  '--workspace',
505
506
  ws,
@@ -25,7 +25,7 @@ String get _root => '${Directory.current.path}/../..';
25
25
  String get _repoEntry => '$_root/cloud/web-client/runner/node-entry.ts';
26
26
  String get _devServer => '$_root/cloud/dev-server/server.ts';
27
27
  String get _deploy =>
28
- '$_root/bench/scale/fixtures/cloud-topology/build/cloud-topology.deploy.json';
28
+ '$_root/bench/scale/fixtures/cloud-topology/build/cloud-topology.law.usdz';
29
29
  const _subject = 'user:topology-test';
30
30
 
31
31
  String? _missingPrereq() {
@@ -48,13 +48,13 @@ void main() {
48
48
  final diag = Platform.environment['NOMOS_HARNESS_DIAG'] == '1'
49
49
  ? (String l) => stderr.writeln(l)
50
50
  : null;
51
- final cloud = await NomosTestCloud.start(serverEntry: _devServer, platform: 'topology-platform', deployJsonPath: _deploy, onDiagnostic: diag);
51
+ final cloud = await NomosTestCloud.start(serverEntry: _devServer, platform: 'topology-platform', lawUsdzPath: _deploy, onDiagnostic: diag);
52
52
  NomosTestHarness? harness;
53
53
  try {
54
54
  // 1+2 — the remote ceremony + the local mount, in one constructor (the app's boot path).
55
55
  harness = await NomosTestHarness.cloudBound(
56
56
  cloud: cloud,
57
- deployJsonPaths: [_deploy],
57
+ lawUsdzPaths: [_deploy],
58
58
  platformWorkspace: 'topology-platform',
59
59
  subject: _subject,
60
60
  nodeEntry: _repoEntry,
@@ -29,7 +29,7 @@ import 'package:test/test.dart';
29
29
  String get _root => '${Directory.current.path}/../..';
30
30
  String get _repoEntry => '$_root/cloud/web-client/runner/node-entry.ts';
31
31
  String get _birthDeploy =>
32
- '$_root/bench/scale/fixtures/estate-birth/build/estate-birth.deploy.json';
32
+ '$_root/bench/scale/fixtures/estate-birth/build/estate-birth.law.usdz';
33
33
  String get _runtimeDir =>
34
34
  Platform.environment['NOMOS_DART_RUNTIME'] ??
35
35
  Platform.environment['NOMOS_OFFLINE_RUNTIME'] ??
@@ -41,7 +41,8 @@ String? _missingPrereq() {
41
41
  return 'compile the fixture: cd bench/scale/fixtures/estate-birth && node ../../../../dsl/compile_package.mjs — missing $_birthDeploy';
42
42
  }
43
43
  if (!File('$_runtimeDir/holon-testmint.wasm').existsSync() ||
44
- !File('$_runtimeDir/packages.json').existsSync()) {
44
+ !File('$_runtimeDir/bootstrap.law.usdz').existsSync() ||
45
+ !File('$_runtimeDir/nomos.law.usdz').existsSync()) {
45
46
  return 'warm ~/.holon/runtime first (run any `githolon` command once)';
46
47
  }
47
48
  return null;
@@ -55,7 +56,7 @@ const _owner = 'sample-owner';
55
56
  Future<(NomosTestHarness, NomosOfferOutcome)> _birth(
56
57
  {String? frameworkHash, void Function(NomosTestHarness)? onHarness}) async {
57
58
  final harness = await NomosTestHarness.ephemeral(
58
- deployJsonPaths: [_birthDeploy],
59
+ lawUsdzPaths: [_birthDeploy],
59
60
  nodeEntry: _repoEntry,
60
61
  runtimeDir: _runtimeDir,
61
62
  workspace: 'estate-birth',