@githolon/dsl 0.105.23 → 0.106.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.
@@ -55,8 +55,11 @@ typedef NomosEngine = NomosBridge;
55
55
  /// One stable bounded page from an authenticated declared-query index. [cursor] is opaque framework
56
56
  /// state: pass it back unchanged to request the next page; [done] means this page exhausted the query.
57
57
  final class NomosPage<T> {
58
- const NomosPage(
59
- {required this.rows, required this.cursor, required this.done});
58
+ const NomosPage({
59
+ required this.rows,
60
+ required this.cursor,
61
+ required this.done,
62
+ });
60
63
 
61
64
  final List<T> rows;
62
65
  final String? cursor;
@@ -98,57 +101,59 @@ final class NomosBirthCertificate {
98
101
  final String? authProviderAudience;
99
102
 
100
103
  static String _string(
101
- Map<String, Object?> wire, String camel, String snake) =>
102
- (wire[camel] ?? wire[snake] ?? '').toString();
104
+ Map<String, Object?> wire,
105
+ String camel,
106
+ String snake,
107
+ ) => (wire[camel] ?? wire[snake] ?? '').toString();
103
108
 
104
109
  static List<int> _bytes(
105
- Map<String, Object?> wire, String camel, String snake) =>
106
- ((wire[camel] ?? wire[snake]) as List? ?? const <Object?>[])
107
- .map((value) => (value as num).toInt())
108
- .toList(growable: false);
109
-
110
- factory NomosBirthCertificate.fromWire(Map<String, Object?> wire) =>
111
- NomosBirthCertificate(
112
- child: _string(wire, 'child', 'child'),
113
- founder: _string(wire, 'founder', 'founder'),
114
- verified: wire['verified'] == true,
115
- parentKey: _bytes(wire, 'parentKey', 'parent_key'),
116
- parentId: _string(wire, 'parentId', 'parent_id'),
117
- epoch: (wire['epoch'] as num?)?.toInt() ?? 0,
118
- signature: _bytes(wire, 'signature', 'signature'),
119
- verifiedVia: (wire['verifiedVia'] ?? wire['verified_via'])?.toString(),
120
- idpToken: (wire['idpToken'] ?? wire['idp_token'])?.toString(),
121
- capturedClock:
122
- ((wire['capturedClock'] ?? wire['captured_clock']) as num?)
123
- ?.toInt(),
124
- authProviderIssuer:
125
- (wire['authProviderIssuer'] ?? wire['auth_provider_issuer'])
126
- ?.toString(),
127
- authProviderJwks:
128
- (wire['authProviderJwks'] ?? wire['auth_provider_jwks'])
129
- ?.toString(),
130
- authProviderAudience:
131
- (wire['authProviderAudience'] ?? wire['auth_provider_audience'])
132
- ?.toString(),
133
- );
110
+ Map<String, Object?> wire,
111
+ String camel,
112
+ String snake,
113
+ ) => ((wire[camel] ?? wire[snake]) as List? ?? const <Object?>[])
114
+ .map((value) => (value as num).toInt())
115
+ .toList(growable: false);
116
+
117
+ factory NomosBirthCertificate.fromWire(
118
+ Map<String, Object?> wire,
119
+ ) => NomosBirthCertificate(
120
+ child: _string(wire, 'child', 'child'),
121
+ founder: _string(wire, 'founder', 'founder'),
122
+ verified: wire['verified'] == true,
123
+ parentKey: _bytes(wire, 'parentKey', 'parent_key'),
124
+ parentId: _string(wire, 'parentId', 'parent_id'),
125
+ epoch: (wire['epoch'] as num?)?.toInt() ?? 0,
126
+ signature: _bytes(wire, 'signature', 'signature'),
127
+ verifiedVia: (wire['verifiedVia'] ?? wire['verified_via'])?.toString(),
128
+ idpToken: (wire['idpToken'] ?? wire['idp_token'])?.toString(),
129
+ capturedClock: ((wire['capturedClock'] ?? wire['captured_clock']) as num?)
130
+ ?.toInt(),
131
+ authProviderIssuer:
132
+ (wire['authProviderIssuer'] ?? wire['auth_provider_issuer'])
133
+ ?.toString(),
134
+ authProviderJwks: (wire['authProviderJwks'] ?? wire['auth_provider_jwks'])
135
+ ?.toString(),
136
+ authProviderAudience:
137
+ (wire['authProviderAudience'] ?? wire['auth_provider_audience'])
138
+ ?.toString(),
139
+ );
134
140
 
135
141
  Map<String, Object?> toWire() => <String, Object?>{
136
- 'child': child,
137
- 'founder': founder,
138
- 'verified': verified,
139
- 'parentKey': parentKey,
140
- 'parentId': parentId,
141
- 'epoch': epoch,
142
- 'signature': signature,
143
- if (verifiedVia != null) 'verifiedVia': verifiedVia,
144
- if (idpToken != null) 'idpToken': idpToken,
145
- if (capturedClock != null) 'capturedClock': capturedClock,
146
- if (authProviderIssuer != null)
147
- 'authProviderIssuer': authProviderIssuer,
148
- if (authProviderJwks != null) 'authProviderJwks': authProviderJwks,
149
- if (authProviderAudience != null)
150
- 'authProviderAudience': authProviderAudience,
151
- };
142
+ 'child': child,
143
+ 'founder': founder,
144
+ 'verified': verified,
145
+ 'parentKey': parentKey,
146
+ 'parentId': parentId,
147
+ 'epoch': epoch,
148
+ 'signature': signature,
149
+ if (verifiedVia != null) 'verifiedVia': verifiedVia,
150
+ if (idpToken != null) 'idpToken': idpToken,
151
+ if (capturedClock != null) 'capturedClock': capturedClock,
152
+ if (authProviderIssuer != null) 'authProviderIssuer': authProviderIssuer,
153
+ if (authProviderJwks != null) 'authProviderJwks': authProviderJwks,
154
+ if (authProviderAudience != null)
155
+ 'authProviderAudience': authProviderAudience,
156
+ };
152
157
  }
153
158
 
154
159
  /// Facts signed by [NomosBridge.signBirthCert]. The secret remains a separate argument and never
@@ -183,21 +188,20 @@ final class NomosBirthCertificateSpec {
183
188
  final String? authProviderAudience;
184
189
 
185
190
  Map<String, Object?> toWire() => <String, Object?>{
186
- 'child': child,
187
- 'founder': founder,
188
- 'verified': verified,
189
- 'parentKey': parentKey,
190
- 'parentId': parentId,
191
- 'epoch': epoch,
192
- if (verifiedVia != null) 'verifiedVia': verifiedVia,
193
- if (idpToken != null) 'idpToken': idpToken,
194
- if (capturedClock != null) 'capturedClock': capturedClock,
195
- if (authProviderIssuer != null)
196
- 'authProviderIssuer': authProviderIssuer,
197
- if (authProviderJwks != null) 'authProviderJwks': authProviderJwks,
198
- if (authProviderAudience != null)
199
- 'authProviderAudience': authProviderAudience,
200
- };
191
+ 'child': child,
192
+ 'founder': founder,
193
+ 'verified': verified,
194
+ 'parentKey': parentKey,
195
+ 'parentId': parentId,
196
+ 'epoch': epoch,
197
+ if (verifiedVia != null) 'verifiedVia': verifiedVia,
198
+ if (idpToken != null) 'idpToken': idpToken,
199
+ if (capturedClock != null) 'capturedClock': capturedClock,
200
+ if (authProviderIssuer != null) 'authProviderIssuer': authProviderIssuer,
201
+ if (authProviderJwks != null) 'authProviderJwks': authProviderJwks,
202
+ if (authProviderAudience != null)
203
+ 'authProviderAudience': authProviderAudience,
204
+ };
201
205
  }
202
206
 
203
207
  /// The protocol version the runner and the bridge agree on (`webview-runner.mjs` `WEBVIEW_BRIDGE_VERSION`).
@@ -247,8 +251,11 @@ class TransferProgress {
247
251
  final int received;
248
252
  final int? total;
249
253
  final String kind; // 'delta' (incremental) | 'full' (initial clone)
250
- const TransferProgress(
251
- {required this.received, this.total, this.kind = 'delta'});
254
+ const TransferProgress({
255
+ required this.received,
256
+ this.total,
257
+ this.kind = 'delta',
258
+ });
252
259
  double? get fraction => (total == null || total == 0)
253
260
  ? null
254
261
  : (received / total!).clamp(0.0, 1.0);
@@ -332,18 +339,17 @@ class SyncStatus {
332
339
  DateTime? lastSyncedAt,
333
340
  String? lastError,
334
341
  bool clearError = false,
335
- }) =>
336
- SyncStatus(
337
- phase: phase ?? this.phase,
338
- ahead: ahead ?? this.ahead,
339
- behind: behind ?? this.behind,
340
- saved: saved ?? this.saved,
341
- onMain: onMain ?? this.onMain,
342
- deadLetters: deadLetters ?? this.deadLetters,
343
- transfer: clearTransfer ? null : (transfer ?? this.transfer),
344
- lastSyncedAt: lastSyncedAt ?? this.lastSyncedAt,
345
- lastError: clearError ? null : (lastError ?? this.lastError),
346
- );
342
+ }) => SyncStatus(
343
+ phase: phase ?? this.phase,
344
+ ahead: ahead ?? this.ahead,
345
+ behind: behind ?? this.behind,
346
+ saved: saved ?? this.saved,
347
+ onMain: onMain ?? this.onMain,
348
+ deadLetters: deadLetters ?? this.deadLetters,
349
+ transfer: clearTransfer ? null : (transfer ?? this.transfer),
350
+ lastSyncedAt: lastSyncedAt ?? this.lastSyncedAt,
351
+ lastError: clearError ? null : (lastError ?? this.lastError),
352
+ );
347
353
  }
348
354
 
349
355
  bool _sameTransferProgress(TransferProgress? left, TransferProgress? right) =>
@@ -374,13 +380,19 @@ class NomosCreated {
374
380
  /// The workspace the offer ran on (NomosRef). Additive — defaults to '' on older runners that
375
381
  /// don't stamp it; then [ref] is workspace-blank (still a valid locator on the local plane).
376
382
  final String workspace;
377
- const NomosCreated(
378
- {required this.type, required this.id, this.workspace = ''});
383
+ const NomosCreated({
384
+ required this.type,
385
+ required this.id,
386
+ this.workspace = '',
387
+ });
379
388
 
380
389
  /// The canonical [NomosRef] view — the `{workspace,type,id}` triple, followable/serializable
381
390
  /// (`nomos://<workspace>/<type>/<id>`). One reference, everywhere.
382
391
  NomosRef<Object?> get ref => NmsInternalNomosRefs.create<Object?>(
383
- workspace: workspace, type: type, id: id);
392
+ workspace: workspace,
393
+ type: type,
394
+ id: id,
395
+ );
384
396
  @override
385
397
  String toString() => id;
386
398
  }
@@ -407,36 +419,40 @@ class NomosBbox {
407
419
  double? minLat,
408
420
  double? maxLng,
409
421
  double? maxLat,
410
- }) : minX = (minX ?? minLng)!,
411
- minY = (minY ?? minLat)!,
412
- maxX = (maxX ?? maxLng)!,
413
- maxY = (maxY ?? maxLat)!;
422
+ }) : minX = (minX ?? minLng)!,
423
+ minY = (minY ?? minLat)!,
424
+ maxX = (maxX ?? maxLng)!,
425
+ maxY = (maxY ?? maxLat)!;
414
426
 
415
427
  /// Deprecated geographic accessors — spatial axes are generic. Prefer [minX]/[minY]/[maxX]/[maxY].
416
428
  @Deprecated(
417
- 'Spatial axes are generic; use minX/minY. Kept for source compat.')
429
+ 'Spatial axes are generic; use minX/minY. Kept for source compat.',
430
+ )
418
431
  double get minLng => minX;
419
432
  @Deprecated(
420
- 'Spatial axes are generic; use minX/minY. Kept for source compat.')
433
+ 'Spatial axes are generic; use minX/minY. Kept for source compat.',
434
+ )
421
435
  double get minLat => minY;
422
436
  @Deprecated(
423
- 'Spatial axes are generic; use maxX/maxY. Kept for source compat.')
437
+ 'Spatial axes are generic; use maxX/maxY. Kept for source compat.',
438
+ )
424
439
  double get maxLng => maxX;
425
440
  @Deprecated(
426
- 'Spatial axes are generic; use maxX/maxY. Kept for source compat.')
441
+ 'Spatial axes are generic; use maxX/maxY. Kept for source compat.',
442
+ )
427
443
  double get maxLat => maxY;
428
444
 
429
445
  Map<String, Object?> toJson() => {
430
- 'minX': minX,
431
- 'minY': minY,
432
- 'maxX': maxX,
433
- 'maxY': maxY,
434
- // Geographic aliases so a pre-genericisation runner (which reads minLng/…) still works.
435
- 'minLng': minX,
436
- 'minLat': minY,
437
- 'maxLng': maxX,
438
- 'maxLat': maxY,
439
- };
446
+ 'minX': minX,
447
+ 'minY': minY,
448
+ 'maxX': maxX,
449
+ 'maxY': maxY,
450
+ // Geographic aliases so a pre-genericisation runner (which reads minLng/…) still works.
451
+ 'minLng': minX,
452
+ 'minLat': minY,
453
+ 'maxLng': maxX,
454
+ 'maxLat': maxY,
455
+ };
440
456
  }
441
457
 
442
458
  /// One member's structural compat verdict — `ok`, plus the exact break [reason] when not.
@@ -497,8 +513,9 @@ class NomosCompat {
497
513
  final install = (m['install'] as Map?)?.cast<String, Object?>() ?? const {};
498
514
  return NomosCompat(
499
515
  generatedFor: (m['generatedFor'] as String?) ?? '',
500
- workspaceRuns:
501
- ((m['workspaceRuns'] as List?) ?? const []).map((e) => '$e').toList(),
516
+ workspaceRuns: ((m['workspaceRuns'] as List?) ?? const [])
517
+ .map((e) => '$e')
518
+ .toList(),
502
519
  matches: m['matches'] == true,
503
520
  offerKnown: m['offerKnown'] == true,
504
521
  structurallyCompatible: m['structurallyCompatible'] as bool?,
@@ -528,21 +545,22 @@ class NomosScopeKey {
528
545
 
529
546
  /// Framework-private deterministic child address, present only for an unborn owned business aggregate.
530
547
  final String? workspace;
531
- const NomosScopeKey(
532
- {required this.scope,
533
- required this.epoch,
534
- required this.key,
535
- this.ownedKeySubject,
536
- this.ownedCustody,
537
- this.workspace});
548
+ const NomosScopeKey({
549
+ required this.scope,
550
+ required this.epoch,
551
+ required this.key,
552
+ this.ownedKeySubject,
553
+ this.ownedCustody,
554
+ this.workspace,
555
+ });
538
556
  Map<String, Object?> toJson() => {
539
- 'scope': scope,
540
- 'epoch': epoch,
541
- 'key': key,
542
- if (ownedKeySubject != null) 'ownedKeySubject': ownedKeySubject,
543
- if (ownedCustody != null) 'ownedCustody': ownedCustody,
544
- if (workspace != null) 'workspace': workspace,
545
- };
557
+ 'scope': scope,
558
+ 'epoch': epoch,
559
+ 'key': key,
560
+ if (ownedKeySubject != null) 'ownedKeySubject': ownedKeySubject,
561
+ if (ownedCustody != null) 'ownedCustody': ownedCustody,
562
+ if (workspace != null) 'workspace': workspace,
563
+ };
546
564
  }
547
565
 
548
566
  /// The entire app-facing multi-device surface for owned business state. The request is deliberately an
@@ -569,8 +587,10 @@ class NomosDevices {
569
587
  if (principal == null || principal.trim().isEmpty) {
570
588
  throw StateError('devices.approve() needs a signed-in Nomos session');
571
589
  }
572
- final payload =
573
- await bridge.nomosPrepareOwnedDeviceApproval(request, scope);
590
+ final payload = await bridge.nomosPrepareOwnedDeviceApproval(
591
+ request,
592
+ scope,
593
+ );
574
594
  return bridge.offerOutcome(
575
595
  domain: domain,
576
596
  directiveId: 'nomos_internal_approve_owned_device_7c1f5e',
@@ -600,7 +620,8 @@ class NomosRecovery {
600
620
  Future<Map<String, Object?>> protectWith(String person) {
601
621
  if (person.trim().isEmpty) {
602
622
  throw ArgumentError(
603
- 'recovery.protectWith() needs the person who will recover you');
623
+ 'recovery.protectWith() needs the person who will recover you',
624
+ );
604
625
  }
605
626
  return bridge.nomosProtectOwnedIdentity(
606
627
  scope: scope,
@@ -644,25 +665,33 @@ class WrappedKeyStore implements NomosKeyStore {
644
665
  final String principal;
645
666
  final String deviceSecret;
646
667
  final Map<String, Future<List<NomosScopeKey>>> _cache = {};
647
- WrappedKeyStore(
648
- {required this.bridge,
649
- required this.principal,
650
- required this.deviceSecret});
668
+ WrappedKeyStore({
669
+ required this.bridge,
670
+ required this.principal,
671
+ required this.deviceSecret,
672
+ });
651
673
 
652
674
  Future<List<NomosScopeKey>> _loadScope(String scope) {
653
675
  return _cache.putIfAbsent(scope, () async {
654
- final rows = await bridge
655
- .query('wrappedKeysByRecipient', {'recipientPrincipal': principal});
676
+ final rows = await bridge.query('wrappedKeysByRecipient', {
677
+ 'recipientPrincipal': principal,
678
+ });
656
679
  final held = <NomosScopeKey>[];
657
680
  for (final row in rows) {
658
681
  final d = ((row['data'] ?? row) as Map);
659
682
  if (d['scope']?.toString() != scope) continue;
660
683
  final key = await bridge.cryptoUnwrapKey(
661
- secret: deviceSecret,
662
- hpkeEpk: d['hpkeEpk'].toString(),
663
- ct: d['ct'].toString());
664
- held.add(NomosScopeKey(
665
- scope: scope, epoch: (d['epoch'] as num?)?.toInt() ?? 0, key: key));
684
+ secret: deviceSecret,
685
+ hpkeEpk: d['hpkeEpk'].toString(),
686
+ ct: d['ct'].toString(),
687
+ );
688
+ held.add(
689
+ NomosScopeKey(
690
+ scope: scope,
691
+ epoch: (d['epoch'] as num?)?.toInt() ?? 0,
692
+ key: key,
693
+ ),
694
+ );
666
695
  }
667
696
  held.sort((a, b) => b.epoch - a.epoch);
668
697
  return held;
@@ -749,16 +778,19 @@ class BirthIncomplete implements Exception {
749
778
  @override
750
779
  String toString() {
751
780
  final list = failedBirths
752
- .map((o) =>
753
- '${o['workspace'] ?? '?'} [${o['status']}${o['code'] != null ? ': ${o['code']}' : ''}]'
754
- '${o['reason'] != null ? ' — ${o['reason']}' : ''}')
781
+ .map(
782
+ (o) =>
783
+ '${o['workspace'] ?? '?'} [${o['status']}${o['code'] != null ? ': ${o['code']}' : ''}]'
784
+ '${o['reason'] != null ? ' — ${o['reason']}' : ''}',
785
+ )
755
786
  .join('; ');
756
- final lawUnavailable =
757
- failedBirths.any((o) => o['code'] == 'BirthLawPackageUnavailable');
787
+ final lawUnavailable = failedBirths.any(
788
+ (o) => o['code'] == 'BirthLawPackageUnavailable',
789
+ );
758
790
  return 'BirthIncomplete: ${failedBirths.length} child workspace(s) did NOT come up — $list. '
759
791
  'The parent offer admitted, but the child was not fully born, so opening it would poll head:null '
760
792
  'until timeout. ${lawUnavailable ? "The child's law package is not resolvable from the parent's "
761
- "custody — deploy it to the parent (POST /v2/workspaces/<parent>/domains) BEFORE birthing." : "See the outcome's reason/code."}';
793
+ "custody — deploy it to the parent (POST /v2/workspaces/<parent>/domains) BEFORE birthing." : "See the outcome's reason/code."}';
762
794
  }
763
795
  }
764
796
 
@@ -768,8 +800,11 @@ class NomosEpochResult {
768
800
  final String head;
769
801
  final int epoch;
770
802
  final List<String> roster;
771
- const NomosEpochResult(
772
- {required this.head, required this.epoch, required this.roster});
803
+ const NomosEpochResult({
804
+ required this.head,
805
+ required this.epoch,
806
+ required this.roster,
807
+ });
773
808
  }
774
809
 
775
810
  /// THE OWNER CEREMONY surface — `<Domain>Client(bridge, actor: owner).encryption`. All three verbs open a
@@ -784,18 +819,20 @@ class NomosEncryption {
784
819
  /// The encryption scopes this domain defines (each a `requires(cap)`).
785
820
  final List<String> scopes;
786
821
 
787
- NomosEncryption(
788
- {required this.bridge,
789
- required this.domain,
790
- this.domainHash,
791
- this.actor,
792
- required this.scopes});
822
+ NomosEncryption({
823
+ required this.bridge,
824
+ required this.domain,
825
+ this.domainHash,
826
+ this.actor,
827
+ required this.scopes,
828
+ });
793
829
 
794
830
  String _defScope([String? scope]) {
795
831
  if (scope != null && scope.isNotEmpty) return scope;
796
832
  if (scopes.length == 1) return scopes.first;
797
833
  throw StateError(
798
- 'this domain defines ${scopes.length} encryption scopes $scopes — pass a scope to pick one');
834
+ 'this domain defines ${scopes.length} encryption scopes $scopes — pass a scope to pick one',
835
+ );
799
836
  }
800
837
 
801
838
  // A BARE scope IS `workspace:self#<relation>` (the era rule); a per-record scope is `<type>:<id>#<relation>`.
@@ -815,13 +852,14 @@ class NomosEncryption {
815
852
  final subjects = rows.isEmpty
816
853
  ? const <String, dynamic>{}
817
854
  : ((rows.first['data'] as Map?)?['subjects'] as Map?) ??
818
- const <String, dynamic>{};
855
+ const <String, dynamic>{};
819
856
  final out = <String>[];
820
857
  subjects.forEach((subj, leafRaw) {
821
858
  Map? leaf;
822
859
  try {
823
- leaf =
824
- leafRaw is String ? jsonDecode(leafRaw) as Map : (leafRaw as Map);
860
+ leaf = leafRaw is String
861
+ ? jsonDecode(leafRaw) as Map
862
+ : (leafRaw as Map);
825
863
  } catch (_) {
826
864
  leaf = null;
827
865
  }
@@ -844,31 +882,38 @@ class NomosEncryption {
844
882
  Future<String> _freshScopeKey() async {
845
883
  final kp = await bridge.cryptoKeygen();
846
884
  final secret = base64Decode((kp['secret'] ?? '').toString());
847
- return base64Encode(secret.sublist(
848
- 0, 32)); // the 32-byte epoch secret; NEVER lands on chain
885
+ return base64Encode(
886
+ secret.sublist(0, 32),
887
+ ); // the 32-byte epoch secret; NEVER lands on chain
849
888
  }
850
889
 
851
- Future<String?> _memberPubKey(String principal,
852
- {String? newcomer,
853
- String? newcomerPub,
854
- Map<String, String>? memberKeys}) async {
890
+ Future<String?> _memberPubKey(
891
+ String principal, {
892
+ String? newcomer,
893
+ String? newcomerPub,
894
+ Map<String, String>? memberKeys,
895
+ }) async {
855
896
  if (memberKeys != null && memberKeys[principal] != null) {
856
897
  return memberKeys[principal];
857
898
  }
858
899
  if (newcomer == principal && newcomerPub != null) return newcomerPub;
859
900
  // Published enc key (encKeyByPrincipal) — present only when the domain composed the registry; tolerate absence.
860
901
  try {
861
- final reg =
862
- await bridge.query('encKeyByPrincipal', {'principal': principal});
902
+ final reg = await bridge.query('encKeyByPrincipal', {
903
+ 'principal': principal,
904
+ });
863
905
  if (reg.isNotEmpty) {
864
906
  final pub = (reg.first['data'] as Map?)?['encPublicKey'];
865
907
  if (pub != null && pub.toString().isNotEmpty) return pub.toString();
866
908
  }
867
- } catch (_) {/* not routed — fall through to the prior-wrap lane */}
909
+ } catch (_) {
910
+ /* not routed — fall through to the prior-wrap lane */
911
+ }
868
912
  // The member's most-recent WrappedKey pins the pubkey it was last sealed to (rotation needs no re-publish).
869
913
  try {
870
- final wraps = await bridge
871
- .query('wrappedKeysByRecipient', {'recipientPrincipal': principal});
914
+ final wraps = await bridge.query('wrappedKeysByRecipient', {
915
+ 'recipientPrincipal': principal,
916
+ });
872
917
  int bestEpoch = -1;
873
918
  String? bestPub;
874
919
  for (final w in wraps) {
@@ -881,7 +926,9 @@ class NomosEncryption {
881
926
  }
882
927
  }
883
928
  if (bestPub != null) return bestPub;
884
- } catch (_) {/* not routed */}
929
+ } catch (_) {
930
+ /* not routed */
931
+ }
885
932
  return null;
886
933
  }
887
934
 
@@ -911,10 +958,12 @@ class NomosEncryption {
911
958
  final key = scopeKey ?? await _freshScopeKey();
912
959
  final wraps = <Map<String, Object?>>[];
913
960
  for (final principal in members) {
914
- final pub = await _memberPubKey(principal,
915
- newcomer: addPrincipal,
916
- newcomerPub: addPublicKey,
917
- memberKeys: memberKeys);
961
+ final pub = await _memberPubKey(
962
+ principal,
963
+ newcomer: addPrincipal,
964
+ newcomerPub: addPublicKey,
965
+ memberKeys: memberKeys,
966
+ );
918
967
  if (pub == null) throw EncryptionMemberKeyMissing(principal, scope);
919
968
  final w = await bridge.cryptoWrapKey(recipientPub: pub, scopeKey: key);
920
969
  wraps.add({
@@ -937,8 +986,7 @@ class NomosEncryption {
937
986
  },
938
987
  actor: actor,
939
988
  domainHash: domainHash,
940
- ))
941
- .head;
989
+ )).head;
942
990
  return NomosEpochResult(head: head, epoch: epoch, roster: members);
943
991
  }
944
992
 
@@ -958,18 +1006,21 @@ class NomosEncryption {
958
1006
  }) async {
959
1007
  final s = _defScope(scope);
960
1008
  await bridge.offerOutcome(
961
- domain: domain,
962
- directiveId: grantDirective,
963
- payload: grantPayload,
964
- actor: actor,
965
- domainHash: domainHash);
966
- return _advance(s,
967
- addPrincipal: principal,
968
- addPublicKey: publicKey,
969
- scopeKey: scopeKey,
970
- memberKeys: memberKeys,
971
- expectRoster: expectRoster,
972
- openedAt: openedAt);
1009
+ domain: domain,
1010
+ directiveId: grantDirective,
1011
+ payload: grantPayload,
1012
+ actor: actor,
1013
+ domainHash: domainHash,
1014
+ );
1015
+ return _advance(
1016
+ s,
1017
+ addPrincipal: principal,
1018
+ addPublicKey: publicKey,
1019
+ scopeKey: scopeKey,
1020
+ memberKeys: memberKeys,
1021
+ expectRoster: expectRoster,
1022
+ openedAt: openedAt,
1023
+ );
973
1024
  }
974
1025
 
975
1026
  /// Revoke a member + open a fresh epoch re-wrapping the key to the SURVIVORS only. FORWARD-ONLY: the removed
@@ -986,17 +1037,20 @@ class NomosEncryption {
986
1037
  }) async {
987
1038
  final s = _defScope(scope);
988
1039
  await bridge.offerOutcome(
989
- domain: domain,
990
- directiveId: revokeDirective,
991
- payload: revokePayload,
992
- actor: actor,
993
- domainHash: domainHash);
994
- return _advance(s,
995
- remove: [principal],
996
- scopeKey: scopeKey,
997
- memberKeys: memberKeys,
998
- expectRoster: expectRoster,
999
- openedAt: openedAt);
1040
+ domain: domain,
1041
+ directiveId: revokeDirective,
1042
+ payload: revokePayload,
1043
+ actor: actor,
1044
+ domainHash: domainHash,
1045
+ );
1046
+ return _advance(
1047
+ s,
1048
+ remove: [principal],
1049
+ scopeKey: scopeKey,
1050
+ memberKeys: memberKeys,
1051
+ expectRoster: expectRoster,
1052
+ openedAt: openedAt,
1053
+ );
1000
1054
  }
1001
1055
 
1002
1056
  /// Routine rotation — open a fresh epoch re-wrapping the key to the CURRENT roster (no membership change).
@@ -1006,12 +1060,13 @@ class NomosEncryption {
1006
1060
  Map<String, String>? memberKeys,
1007
1061
  List<String>? expectRoster,
1008
1062
  String? openedAt,
1009
- }) =>
1010
- _advance(_defScope(scope),
1011
- scopeKey: scopeKey,
1012
- memberKeys: memberKeys,
1013
- expectRoster: expectRoster,
1014
- openedAt: openedAt);
1063
+ }) => _advance(
1064
+ _defScope(scope),
1065
+ scopeKey: scopeKey,
1066
+ memberKeys: memberKeys,
1067
+ expectRoster: expectRoster,
1068
+ openedAt: openedAt,
1069
+ );
1015
1070
  }
1016
1071
 
1017
1072
  /// The result of [NomosBridge.offerCreates]: the new local [head], every aggregate the plan minted, and —
@@ -1036,9 +1091,8 @@ final class NomosOperationStatus {
1036
1091
  /// adopted that shared position. Callers never inspect workspaces, URLs, credentials, headers or Git heads.
1037
1092
  final class NomosOperationReceipt {
1038
1093
  NomosOperationReceipt._()
1039
- : _status =
1040
- const NomosOperationStatus(NomosOperationPhase.locallyAccepted),
1041
- _changes = StreamController<NomosOperationStatus>.broadcast();
1094
+ : _status = const NomosOperationStatus(NomosOperationPhase.locallyAccepted),
1095
+ _changes = StreamController<NomosOperationStatus>.broadcast();
1042
1096
 
1043
1097
  NomosOperationStatus _status;
1044
1098
  final StreamController<NomosOperationStatus> _changes;
@@ -1099,7 +1153,8 @@ class NomosOfferOutcome {
1099
1153
  NomosOperationReceipt get receipt =>
1100
1154
  _receipt ??
1101
1155
  (throw StateError(
1102
- 'This legacy offer outcome has no operation receipt; regenerate the application client.'));
1156
+ 'This legacy offer outcome has no operation receipt; regenerate the application client.',
1157
+ ));
1103
1158
 
1104
1159
  const NomosOfferOutcome({
1105
1160
  required this.head,
@@ -1123,8 +1178,13 @@ class NomosOfferOutcome {
1123
1178
  /// (`final estate = await engine.follow(outcome.bornRefs.single);`) so app code never carries a
1124
1179
  /// loose workspace string. The ref's type is the reserved `nomos.Workspace` locator tag.
1125
1180
  List<NomosRef<Object?>> get bornRefs => born
1126
- .map((w) => NmsInternalNomosRefs.create<Object?>(
1127
- workspace: w, type: 'nomos.Workspace', id: w))
1181
+ .map(
1182
+ (w) => NmsInternalNomosRefs.create<Object?>(
1183
+ workspace: w,
1184
+ type: 'nomos.Workspace',
1185
+ id: w,
1186
+ ),
1187
+ )
1128
1188
  .toList();
1129
1189
  }
1130
1190
 
@@ -1141,11 +1201,16 @@ extension NomosRefAcquire<TAggregate> on NomosRef<TAggregate> {
1141
1201
  }) {
1142
1202
  final workspace = NmsInternalNomosRefs.workspace(this);
1143
1203
  if (workspace.isEmpty) {
1144
- throw ArgumentError.value(this, 'ref',
1145
- 'NomosRef.acquire: the ref carries no workspace — only a workspace-complete ref is openable');
1204
+ throw ArgumentError.value(
1205
+ this,
1206
+ 'ref',
1207
+ 'NomosRef.acquire: the ref carries no workspace — only a workspace-complete ref is openable',
1208
+ );
1146
1209
  }
1147
- return bridge.acquireSession(workspace,
1148
- ifAbsentCredentials: ifAbsentCredentials);
1210
+ return bridge.acquireSession(
1211
+ workspace,
1212
+ ifAbsentCredentials: ifAbsentCredentials,
1213
+ );
1149
1214
  }
1150
1215
  }
1151
1216
 
@@ -1190,16 +1255,14 @@ final class _NomosRetainedCloudAccess {
1190
1255
  final String cloud;
1191
1256
  final Future<String?> Function() signIn;
1192
1257
 
1193
- const _NomosRetainedCloudAccess({
1194
- required this.cloud,
1195
- required this.signIn,
1196
- });
1258
+ const _NomosRetainedCloudAccess({required this.cloud, required this.signIn});
1197
1259
 
1198
1260
  Future<NomosSessionCredentials> credentials() async {
1199
1261
  final token = await signIn();
1200
1262
  if (token == null || token.trim().isEmpty) {
1201
1263
  throw StateError(
1202
- 'Nomos could not refresh the signed-in identity while opening a referenced business workspace');
1264
+ 'Nomos could not refresh the signed-in identity while opening a referenced business workspace',
1265
+ );
1203
1266
  }
1204
1267
  return NomosSessionCredentials(cloud: cloud, authToken: token);
1205
1268
  }
@@ -1247,9 +1310,9 @@ class NomosBridge {
1247
1310
  // The DERIVED app-ready sync state. seeded-on-listen with a metrics poll; updated on sync/signal/transfer.
1248
1311
  late final StreamController<SyncStatus> _syncStatusCtrl =
1249
1312
  StreamController<SyncStatus>.broadcast(
1250
- onListen: _startStatusPoll,
1251
- onCancel: _stopStatusPollIfUnused,
1252
- );
1313
+ onListen: _startStatusPoll,
1314
+ onCancel: _stopStatusPollIfUnused,
1315
+ );
1253
1316
  SyncStatus _status = const SyncStatus();
1254
1317
  final Map<String, SyncStatus> _statusBySession = {};
1255
1318
  final Map<String, StreamController<SyncStatus>> _syncStatusBySession = {};
@@ -1286,7 +1349,7 @@ class NomosBridge {
1286
1349
  // child sessions. The callback receives only the typed review object; child
1287
1350
  // addresses, archives, credentials and lifecycle directives remain private.
1288
1351
  final Map<String, Future<void> Function(DomainClientModelAdoptionRequired)>
1289
- _sessionModelChangeHandlers = {};
1352
+ _sessionModelChangeHandlers = {};
1290
1353
  // [acquireSession]'s per-workspace single-flight: workspace → the in-flight open every concurrent
1291
1354
  // acquirer joins (removed when it completes; a live session then hits the fast path instead).
1292
1355
  final Map<String, Future<NomosSession>> _acquiring = {};
@@ -1414,9 +1477,11 @@ class NomosBridge {
1414
1477
  final ctrl = _watches[m['subId']];
1415
1478
  if (ctrl != null && !ctrl.isClosed) {
1416
1479
  if (m['ok'] == false) {
1417
- final info = NomosErrorInfo.fromBridgeMessage(m,
1418
- fallbackMessage: 'the watch read failed',
1419
- fallbackOperation: 'watch');
1480
+ final info = NomosErrorInfo.fromBridgeMessage(
1481
+ m,
1482
+ fallbackMessage: 'the watch read failed',
1483
+ fallbackOperation: 'watch',
1484
+ );
1420
1485
  ctrl.addError(nomosErrorFromInfo(info));
1421
1486
  } else {
1422
1487
  ctrl.add((m['rows'] as List?) ?? const []);
@@ -1427,9 +1492,11 @@ class NomosBridge {
1427
1492
  final cctrl = _compatWatches[m['subId']];
1428
1493
  if (cctrl != null && !cctrl.isClosed) {
1429
1494
  if (m['ok'] == false) {
1430
- final info = NomosErrorInfo.fromBridgeMessage(m,
1431
- fallbackMessage: 'the compat watch failed',
1432
- fallbackOperation: 'watchCompatibility');
1495
+ final info = NomosErrorInfo.fromBridgeMessage(
1496
+ m,
1497
+ fallbackMessage: 'the compat watch failed',
1498
+ fallbackOperation: 'watchCompatibility',
1499
+ );
1433
1500
  cctrl.addError(nomosErrorFromInfo(info));
1434
1501
  } else {
1435
1502
  cctrl.add(((m['rows'] as Map?)?.cast<String, Object?>()) ?? const {});
@@ -1440,8 +1507,9 @@ class NomosBridge {
1440
1507
  if (event == 'lawChanged') {
1441
1508
  // COMPAT SUPERPOWER (#72b): a synced install/upgrade/retire moved the workspace's offer under this
1442
1509
  // live client. Cache the fresh verdict + emit it so the app re-checks feature flags without polling.
1443
- final c =
1444
- NomosCompat.fromJson((m['compat'] as Map?)?.cast<String, Object?>());
1510
+ final c = NomosCompat.fromJson(
1511
+ (m['compat'] as Map?)?.cast<String, Object?>(),
1512
+ );
1445
1513
  if (c != null) {
1446
1514
  _lastCompat = c;
1447
1515
  final sess = m['session'] as String?;
@@ -1468,21 +1536,24 @@ class NomosBridge {
1468
1536
  // telemetry; the app-ready status needs only one legible error and an honest error phase.
1469
1537
  final detail = m['error']?.toString() ?? 'automatic sync failed';
1470
1538
  _updateScopedStatus(
1471
- session,
1472
- (current) => current.copyWith(
1473
- phase: SyncPhase.error,
1474
- clearTransfer: true,
1475
- lastError: detail));
1539
+ session,
1540
+ (current) => current.copyWith(
1541
+ phase: SyncPhase.error,
1542
+ clearTransfer: true,
1543
+ lastError: detail,
1544
+ ),
1545
+ );
1476
1546
  } else if (result?['offline'] == true) {
1477
1547
  // Offline is expected transport debt. The durable local branch remains ahead and autoSync owns the
1478
1548
  // retry; do not claim a completed sync or overwrite the previous successful timestamp.
1479
1549
  _updateScopedStatus(
1480
- session,
1481
- (current) => current.copyWith(
1482
- phase: SyncPhase.offline,
1483
- clearTransfer: true,
1484
- lastError:
1485
- (result?['pushError'] as Map?)?['error']?.toString()));
1550
+ session,
1551
+ (current) => current.copyWith(
1552
+ phase: SyncPhase.offline,
1553
+ clearTransfer: true,
1554
+ lastError: (result?['pushError'] as Map?)?['error']?.toString(),
1555
+ ),
1556
+ );
1486
1557
  } else if (result?['pushFailed'] == true) {
1487
1558
  // The sync round itself completed, but shared custody refused the
1488
1559
  // push. `stillAhead` is durable debt, not success: surface the
@@ -1491,24 +1562,30 @@ class NomosBridge {
1491
1562
  // out behind a generic saving state.
1492
1563
  final detail = _syncPushFailureDetail(result!);
1493
1564
  _updateScopedStatus(
1494
- session,
1495
- (current) => current.copyWith(
1496
- phase: SyncPhase.error,
1497
- clearTransfer: true,
1498
- lastError: detail));
1565
+ session,
1566
+ (current) => current.copyWith(
1567
+ phase: SyncPhase.error,
1568
+ clearTransfer: true,
1569
+ lastError: detail,
1570
+ ),
1571
+ );
1499
1572
  } else {
1500
1573
  // A completed round → idle (the next metrics poll folds in residual ahead/behind), stamp the time.
1501
1574
  _updateScopedStatus(
1502
- session,
1503
- (current) => current.copyWith(
1504
- phase: SyncPhase.idle,
1505
- clearTransfer: true,
1506
- clearError: true,
1507
- lastSyncedAt: DateTime.now()));
1575
+ session,
1576
+ (current) => current.copyWith(
1577
+ phase: SyncPhase.idle,
1578
+ clearTransfer: true,
1579
+ clearError: true,
1580
+ lastSyncedAt: DateTime.now(),
1581
+ ),
1582
+ );
1508
1583
  }
1509
1584
  } else if (m['status'] == 'closed' || m['status'] == 'offline') {
1510
- _updateScopedStatus(m['session']?.toString(),
1511
- (current) => current.copyWith(phase: SyncPhase.offline));
1585
+ _updateScopedStatus(
1586
+ m['session']?.toString(),
1587
+ (current) => current.copyWith(phase: SyncPhase.offline),
1588
+ );
1512
1589
  }
1513
1590
  return;
1514
1591
  }
@@ -1535,11 +1612,13 @@ class NomosBridge {
1535
1612
  final tp = TransferProgress.fromMessage(m);
1536
1613
  if (!_transferEvents.isClosed) _transferEvents.add(tp);
1537
1614
  _updateScopedStatus(
1538
- m['session']?.toString(),
1539
- (current) => current.copyWith(
1540
- phase: tp.done ? SyncPhase.syncing : SyncPhase.transferring,
1541
- transfer: tp,
1542
- clearTransfer: tp.done));
1615
+ m['session']?.toString(),
1616
+ (current) => current.copyWith(
1617
+ phase: tp.done ? SyncPhase.syncing : SyncPhase.transferring,
1618
+ transfer: tp,
1619
+ clearTransfer: tp.done,
1620
+ ),
1621
+ );
1543
1622
  return;
1544
1623
  }
1545
1624
  if (event == 'trace') {
@@ -1552,10 +1631,11 @@ class NomosBridge {
1552
1631
  if (!_signalEvents.isClosed) _signalEvents.add(m);
1553
1632
  // the doorbell says canonical main advanced → we're behind until the next pull lands.
1554
1633
  _updateScopedStatus(
1555
- m['session']?.toString(),
1556
- (current) => current.phase == SyncPhase.idle
1557
- ? current.copyWith(phase: SyncPhase.behind)
1558
- : current);
1634
+ m['session']?.toString(),
1635
+ (current) => current.phase == SyncPhase.idle
1636
+ ? current.copyWith(phase: SyncPhase.behind)
1637
+ : current,
1638
+ );
1559
1639
  return;
1560
1640
  }
1561
1641
  if (event == 'span') {
@@ -1584,14 +1664,20 @@ class NomosBridge {
1584
1664
  .map((e) => e.cast<String, Object?>())
1585
1665
  .toList();
1586
1666
  if (failedBirths != null && failedBirths.isNotEmpty) {
1587
- completer.completeError(BirthIncomplete(failedBirths,
1588
- head: ((m['errorInfo'] as Map?)?['head'] ?? '').toString()));
1667
+ completer.completeError(
1668
+ BirthIncomplete(
1669
+ failedBirths,
1670
+ head: ((m['errorInfo'] as Map?)?['head'] ?? '').toString(),
1671
+ ),
1672
+ );
1589
1673
  return;
1590
1674
  }
1591
- final info = NomosErrorInfo.fromBridgeMessage(m,
1592
- fallbackOperation: op,
1593
- fallbackTrace: trace,
1594
- fallbackMessage: 'the githolon refused the request');
1675
+ final info = NomosErrorInfo.fromBridgeMessage(
1676
+ m,
1677
+ fallbackOperation: op,
1678
+ fallbackTrace: trace,
1679
+ fallbackMessage: 'the githolon refused the request',
1680
+ );
1595
1681
  completer.completeError(nomosErrorFromInfo(info));
1596
1682
  }
1597
1683
  }
@@ -1647,7 +1733,8 @@ class NomosBridge {
1647
1733
  final parts = token.split('.');
1648
1734
  if (parts.length < 2) return null;
1649
1735
  final payload = jsonDecode(
1650
- utf8.decode(base64Url.decode(base64Url.normalize(parts[1]))));
1736
+ utf8.decode(base64Url.decode(base64Url.normalize(parts[1]))),
1737
+ );
1651
1738
  final sub = payload is Map ? payload['sub'] : null;
1652
1739
  if (sub is! String || sub.trim().isEmpty) return null;
1653
1740
  final value = sub.trim();
@@ -1658,11 +1745,14 @@ class NomosBridge {
1658
1745
  }
1659
1746
 
1660
1747
  NomosOperationReceipt _registerOperationReceipt(
1661
- String workspace, String intentId) {
1748
+ String workspace,
1749
+ String intentId,
1750
+ ) {
1662
1751
  if (intentId.isEmpty) {
1663
1752
  throw StateError(
1664
- 'Nomos accepted a local write without returning its stable intent identity. '
1665
- 'The runtime and kernel release are incompatible.');
1753
+ 'Nomos accepted a local write without returning its stable intent identity. '
1754
+ 'The runtime and kernel release are incompatible.',
1755
+ );
1666
1756
  }
1667
1757
  final plane = _eventPlane;
1668
1758
  // A locally accepted write is not current until custody convergence
@@ -1684,7 +1774,9 @@ class NomosBridge {
1684
1774
  }
1685
1775
 
1686
1776
  void _advanceOperationReceipts(
1687
- String workspace, Map<String, Object?> result) {
1777
+ String workspace,
1778
+ Map<String, Object?> result,
1779
+ ) {
1688
1780
  final pending = _eventPlane._operationReceipts[workspace];
1689
1781
  if (pending == null || pending.isEmpty) return;
1690
1782
 
@@ -1703,12 +1795,14 @@ class NomosBridge {
1703
1795
  }
1704
1796
 
1705
1797
  bool _syncNeedsCredentialRefresh(
1706
- Map<String, Object?> message, Map<String, Object?> result) {
1798
+ Map<String, Object?> message,
1799
+ Map<String, Object?> result,
1800
+ ) {
1707
1801
  final push = (result['pushError'] as Map?)?.cast<String, Object?>();
1708
1802
  final status = push?['status'];
1709
1803
  if (status == 401 || status == 403) return true;
1710
- final text =
1711
- '${message['error'] ?? ''} ${push?['error'] ?? ''}'.toLowerCase();
1804
+ final text = '${message['error'] ?? ''} ${push?['error'] ?? ''}'
1805
+ .toLowerCase();
1712
1806
  return text.contains('unauthenticated') ||
1713
1807
  text.contains('token expired') ||
1714
1808
  text.contains('invalid token');
@@ -1735,7 +1829,8 @@ class NomosBridge {
1735
1829
  final token = (await retained.signIn())?.trim() ?? '';
1736
1830
  if (token.isEmpty) {
1737
1831
  throw StateError(
1738
- 'Nomos could not refresh the signed-in identity for background sync.');
1832
+ 'Nomos could not refresh the signed-in identity for background sync.',
1833
+ );
1739
1834
  }
1740
1835
  await plane.session(workspace)._call('refreshCredentials', {
1741
1836
  'authToken': token,
@@ -1804,24 +1899,27 @@ class NomosBridge {
1804
1899
  const [])
1805
1900
  .map((e) => (e as Map).cast<String, Object?>())
1806
1901
  .map((w) {
1807
- final ws = w['workspace']?.toString() ?? '';
1808
- // `connected` is the app's own connect() truth; `parked`/resident come from the runner realm.
1809
- w['state'] = (w['parked'] == true)
1810
- ? 'parked'
1811
- : (connected.contains(ws) ? 'connected' : 'resident');
1812
- return w;
1813
- }).toList();
1902
+ final ws = w['workspace']?.toString() ?? '';
1903
+ // `connected` is the app's own connect() truth; `parked`/resident come from the runner realm.
1904
+ w['state'] = (w['parked'] == true)
1905
+ ? 'parked'
1906
+ : (connected.contains(ws) ? 'connected' : 'resident');
1907
+ return w;
1908
+ })
1909
+ .toList();
1814
1910
  }
1815
1911
 
1816
1912
  /// Framework-private plane persistence primitive. It snapshots exact local custody even when [workspace]
1817
1913
  /// was born in the kernel but no Dart runner session has ever acquired it. This does not connect, mount a
1818
1914
  /// tenant session, fetch from cloud, or grant authority; the serving realm exports its resident plane view.
1819
1915
  Future<({String workspace, String head, String? snapshot, bool unchanged})>
1820
- nomosInternalSnapshotWorkspace(String workspace, {String? ifHead}) async {
1821
- final r = _asMap(await _call('snapshotWorkspace', {
1822
- 'workspace': workspace,
1823
- if (ifHead != null && ifHead.isNotEmpty) 'ifHead': ifHead,
1824
- }));
1916
+ nomosInternalSnapshotWorkspace(String workspace, {String? ifHead}) async {
1917
+ final r = _asMap(
1918
+ await _call('snapshotWorkspace', {
1919
+ 'workspace': workspace,
1920
+ if (ifHead != null && ifHead.isNotEmpty) 'ifHead': ifHead,
1921
+ }),
1922
+ );
1825
1923
  return (
1826
1924
  workspace: (r['workspace'] ?? workspace).toString(),
1827
1925
  head: (r['head'] ?? '').toString(),
@@ -1833,51 +1931,55 @@ class NomosBridge {
1833
1931
  /// Open a workspace on this plane: boot its LOCAL githolon under the installed law (from [restoreFrom]
1834
1932
  /// custody, or a cloud pull when [cloud] is given). This is the sugar the single-workspace path uses; a
1835
1933
  /// multi-workspace app names a [session] per workspace and holds their [session] handles.
1836
- Future<void> connect(
1837
- {String? cloud,
1838
- required String workspace,
1839
- String? clientId,
1840
- String? authToken,
1841
- String? authorSecret,
1842
- String? restoreFrom,
1843
- bool allowUnborn = false,
1844
- bool recover = false,
1845
- CustodyBreakPolicy? onCustodyBreak,
1846
- String? session,
1847
- int? maxMounted}) async {
1934
+ Future<void> connect({
1935
+ String? cloud,
1936
+ required String workspace,
1937
+ String? clientId,
1938
+ String? authToken,
1939
+ String? authorSecret,
1940
+ String? restoreFrom,
1941
+ bool allowUnborn = false,
1942
+ bool recover = false,
1943
+ CustodyBreakPolicy? onCustodyBreak,
1944
+ String? session,
1945
+ int? maxMounted,
1946
+ }) async {
1848
1947
  final ws = session ?? workspace;
1849
1948
  // Record the session name ONLY on a SUCCESSFUL open. Adding it before the await left a PHANTOM session
1850
1949
  // behind on a failed connect (a corrupt restoreFrom, an unreachable cloud): acquireSession/follow fast-path
1851
1950
  // on `_sessionNames.contains` and would hand back a dead handle whose every op refuses "session not
1852
1951
  // connected", and ifAbsentCredentials would never fire. The `_call` throwing skips the add below.
1853
- final r = _asMap(await _call('connect', {
1854
- // [cloud] is OPTIONAL — omit it to open a LOCAL-ONLY workspace (a local githolon with no cloud
1855
- // counterpart; the runner uses a `local://` sentinel and sync is a no-op). Pass it to make the
1856
- // workspace cloud-bound (per-workspace sync posture). A device's plane freely mixes both.
1857
- if (cloud != null) 'cloud': cloud,
1858
- 'workspace': workspace,
1859
- // MULTI-WORKSPACE: name the [session] this connect opens on this device's local plane (default =
1860
- // [workspace]). Omitted on the single-workspace sugar path (byte-identical wire to before).
1861
- if (session != null) 'session': session,
1862
- if (clientId != null) 'clientId': clientId,
1863
- if (authToken != null) 'authToken': authToken,
1864
- // The device signing secret (base64, from [authorKeygen]) — the warrant default for every offer.
1865
- // Stays client-side; the kernel mints the signed-author attestation so authorship is replay-provable.
1866
- if (authorSecret != null) 'authorSecret': authorSecret,
1867
- if (restoreFrom != null) 'restoreFrom': restoreFrom,
1868
- if (allowUnborn) 'allowUnborn': true,
1869
- // the realm's mounted-workspace budget (LRU park/remount above it) — first connect wins.
1870
- if (maxMounted != null) 'maxMounted': maxMounted,
1871
- // RECOVER-IN-PLACE over a corrupt restoreFrom snapshot: salvage local-only writes + DLQ, refold fresh
1872
- // from cloud, re-offer the salvaged work. The report lands on [lastRecovery]. Nothing local is lost.
1873
- if (recover) 'recover': true,
1874
- // CUSTODY-BREAK POLICY (rebirth_and_custody_lifecycle.md): "adopt"/"reject" decide automatically;
1875
- // "ask" surfaces the break on [custodyEvents] (kind "ask") and awaits [resolveCustodyBreak]. Omitted ⇒
1876
- // fail closed (the break surfaces as an error on the sync/pull that hit it; the holon stays local).
1877
- if (onCustodyBreak != null) 'onCustodyBreak': onCustodyBreak.wire,
1878
- }));
1879
- _eventPlane._sessionNames
1880
- .add(ws); // the open succeeded — the session is real now
1952
+ final r = _asMap(
1953
+ await _call('connect', {
1954
+ // [cloud] is OPTIONAL — omit it to open a LOCAL-ONLY workspace (a local githolon with no cloud
1955
+ // counterpart; the runner uses a `local://` sentinel and sync is a no-op). Pass it to make the
1956
+ // workspace cloud-bound (per-workspace sync posture). A device's plane freely mixes both.
1957
+ if (cloud != null) 'cloud': cloud,
1958
+ 'workspace': workspace,
1959
+ // MULTI-WORKSPACE: name the [session] this connect opens on this device's local plane (default =
1960
+ // [workspace]). Omitted on the single-workspace sugar path (byte-identical wire to before).
1961
+ if (session != null) 'session': session,
1962
+ if (clientId != null) 'clientId': clientId,
1963
+ if (authToken != null) 'authToken': authToken,
1964
+ // The device signing secret (base64, from [authorKeygen]) — the warrant default for every offer.
1965
+ // Stays client-side; the kernel mints the signed-author attestation so authorship is replay-provable.
1966
+ if (authorSecret != null) 'authorSecret': authorSecret,
1967
+ if (restoreFrom != null) 'restoreFrom': restoreFrom,
1968
+ if (allowUnborn) 'allowUnborn': true,
1969
+ // the realm's mounted-workspace budget (LRU park/remount above it) — first connect wins.
1970
+ if (maxMounted != null) 'maxMounted': maxMounted,
1971
+ // RECOVER-IN-PLACE over a corrupt restoreFrom snapshot: salvage local-only writes + DLQ, refold fresh
1972
+ // from cloud, re-offer the salvaged work. The report lands on [lastRecovery]. Nothing local is lost.
1973
+ if (recover) 'recover': true,
1974
+ // CUSTODY-BREAK POLICY (rebirth_and_custody_lifecycle.md): "adopt"/"reject" decide automatically;
1975
+ // "ask" surfaces the break on [custodyEvents] (kind "ask") and awaits [resolveCustodyBreak]. Omitted ⇒
1976
+ // fail closed (the break surfaces as an error on the sync/pull that hit it; the holon stays local).
1977
+ if (onCustodyBreak != null) 'onCustodyBreak': onCustodyBreak.wire,
1978
+ }),
1979
+ );
1980
+ _eventPlane._sessionNames.add(
1981
+ ws,
1982
+ ); // the open succeeded — the session is real now
1881
1983
  // Opening succeeded, but scoped metrics have not yet proved that this
1882
1984
  // workspace is current. Unknown readiness must fail closed: otherwise a
1883
1985
  // generated `whenCurrent()` can observe the default idle value and return
@@ -1893,8 +1995,8 @@ class NomosBridge {
1893
1995
  if (authorSecret != null && authorSecret.isNotEmpty) {
1894
1996
  _eventPlane._sessionAuthorSecrets[ws] = authorSecret;
1895
1997
  }
1896
- _eventPlane._lastRecovery =
1897
- (r['recovery'] as Map?)?.cast<String, Object?>();
1998
+ _eventPlane._lastRecovery = (r['recovery'] as Map?)
1999
+ ?.cast<String, Object?>();
1898
2000
  if (_eventPlane._syncStatusCtrl.hasListener) {
1899
2001
  unawaited(_eventPlane._pollMetricsOnce());
1900
2002
  }
@@ -1918,21 +2020,30 @@ class NomosBridge {
1918
2020
  /// Where a session sits on the custody spectrum (incarnation, posture, pending/salvaged local work,
1919
2021
  /// supersession history) — for a status banner or a "you're offline on old truth" indicator.
1920
2022
  Future<NomosCustodyStatus> custodyStatus({String? session}) async =>
1921
- NomosCustodyStatus.fromJson(_asMap(await _call(
1922
- 'custodyStatus', {if (session != null) 'session': session})));
2023
+ NomosCustodyStatus.fromJson(
2024
+ _asMap(
2025
+ await _call('custodyStatus', {
2026
+ if (session != null) 'session': session,
2027
+ }),
2028
+ ),
2029
+ );
1923
2030
 
1924
2031
  /// Continue an `ask`-policy custody break with the user's decision (adopt|reject) — the async UX lane.
1925
- Future<Map<String, Object?>> resolveCustodyBreak(String decision,
1926
- {String? session}) async =>
1927
- _asMap(await _call('resolveCustodyBreak', {
1928
- 'decision': decision,
1929
- if (session != null) 'session': session,
1930
- }));
2032
+ Future<Map<String, Object?>> resolveCustodyBreak(
2033
+ String decision, {
2034
+ String? session,
2035
+ }) async => _asMap(
2036
+ await _call('resolveCustodyBreak', {
2037
+ 'decision': decision,
2038
+ if (session != null) 'session': session,
2039
+ }),
2040
+ );
1931
2041
 
1932
2042
  /// The parked salvaged intents (re-offerable local work an `adopt` rescued) — metadata only.
1933
2043
  Future<List<Map<String, Object?>>> salvagedIntents({String? session}) async {
1934
- final r = _asMap(await _call(
1935
- 'salvagedIntents', {if (session != null) 'session': session}));
2044
+ final r = _asMap(
2045
+ await _call('salvagedIntents', {if (session != null) 'session': session}),
2046
+ );
1936
2047
  return ((r['salvaged'] as List?) ?? const [])
1937
2048
  .map((e) => (e as Map).cast<String, Object?>())
1938
2049
  .toList();
@@ -1940,16 +2051,26 @@ class NomosBridge {
1940
2051
 
1941
2052
  /// Re-offer parked salvaged work under the current (adopted) law — the explicit app move (never automatic
1942
2053
  /// across incarnations). [id] re-offers one; omitted re-offers all.
1943
- Future<Map<String, Object?>> reofferSalvaged(
1944
- {String? id, String? session}) async =>
1945
- _asMap(await _call('reofferSalvaged',
1946
- {if (id != null) 'id': id, if (session != null) 'session': session}));
2054
+ Future<Map<String, Object?>> reofferSalvaged({
2055
+ String? id,
2056
+ String? session,
2057
+ }) async => _asMap(
2058
+ await _call('reofferSalvaged', {
2059
+ if (id != null) 'id': id,
2060
+ if (session != null) 'session': session,
2061
+ }),
2062
+ );
1947
2063
 
1948
2064
  /// Discard one parked salvaged intent — the app's explicit choice.
1949
- Future<Map<String, Object?>> discardSalvaged(String id,
1950
- {String? session}) async =>
1951
- _asMap(await _call('discardSalvaged',
1952
- {'id': id, if (session != null) 'session': session}));
2065
+ Future<Map<String, Object?>> discardSalvaged(
2066
+ String id, {
2067
+ String? session,
2068
+ }) async => _asMap(
2069
+ await _call('discardSalvaged', {
2070
+ 'id': id,
2071
+ if (session != null) 'session': session,
2072
+ }),
2073
+ );
1953
2074
 
1954
2075
  // ── THE DEAD-LETTER QUEUE (refused-but-legitimate work is NEVER lost) — the Dart mirror of the
1955
2076
  // JS holon's DLQ surface (`deadLetters()` / `retryDeadLetter()` / `discardDeadLetter()`). A
@@ -1961,7 +2082,8 @@ class NomosBridge {
1961
2082
  /// the intent bytes stay in the durable queue.
1962
2083
  Future<List<Map<String, Object?>>> deadLetters({String? session}) async {
1963
2084
  final r = _asMap(
1964
- await _call('deadLetters', {if (session != null) 'session': session}));
2085
+ await _call('deadLetters', {if (session != null) 'session': session}),
2086
+ );
1965
2087
  return ((r['dead'] as List?) ?? const [])
1966
2088
  .map((e) => (e as Map).cast<String, Object?>())
1967
2089
  .toList();
@@ -1970,16 +2092,26 @@ class NomosBridge {
1970
2092
  /// Retry one dead letter (after the law fix landed): pulls canon first (it may already be
1971
2093
  /// admitted), else re-offers the parked bytes locally. `{ok, head?|alreadyOnMain?|error?}` —
1972
2094
  /// a refusal leaves the entry parked (with the fresh error), never lost.
1973
- Future<Map<String, Object?>> retryDeadLetter(String id,
1974
- {String? session}) async =>
1975
- _asMap(await _call('retryDeadLetter',
1976
- {'id': id, if (session != null) 'session': session}));
2095
+ Future<Map<String, Object?>> retryDeadLetter(
2096
+ String id, {
2097
+ String? session,
2098
+ }) async => _asMap(
2099
+ await _call('retryDeadLetter', {
2100
+ 'id': id,
2101
+ if (session != null) 'session': session,
2102
+ }),
2103
+ );
1977
2104
 
1978
2105
  /// Discard one dead letter — the app's EXPLICIT choice to drop the work. `{ok}`.
1979
- Future<Map<String, Object?>> discardDeadLetter(String id,
1980
- {String? session}) async =>
1981
- _asMap(await _call('discardDeadLetter',
1982
- {'id': id, if (session != null) 'session': session}));
2106
+ Future<Map<String, Object?>> discardDeadLetter(
2107
+ String id, {
2108
+ String? session,
2109
+ }) async => _asMap(
2110
+ await _call('discardDeadLetter', {
2111
+ 'id': id,
2112
+ if (session != null) 'session': session,
2113
+ }),
2114
+ );
1983
2115
 
1984
2116
  /// SALVAGE — best-effort extraction of this holon's local-only authored work (un-synced pending
1985
2117
  /// writes + the DLQ), each with its full `changeB64` bytes: `{pendingWrites, deadLetters}`. The
@@ -1990,15 +2122,17 @@ class NomosBridge {
1990
2122
  /// ABSORB salvaged work into THIS holon: pending writes are re-offered under the current law
1991
2123
  /// (a refusal dead-letters — never lost), and dead-letter entries are re-queued into the DLQ.
1992
2124
  /// Returns the recovery report `{salvagedWrites, requeuedDeadLetters, unsalvageable}`.
1993
- Future<Map<String, Object?>> absorbSalvage(
1994
- {List<Map<String, Object?>> pendingWrites = const [],
1995
- List<Map<String, Object?>> deadLetters = const [],
1996
- String? session}) async =>
1997
- _asMap(await _call('absorbSalvage', {
1998
- 'pendingWrites': pendingWrites,
1999
- 'deadLetters': deadLetters,
2000
- if (session != null) 'session': session,
2001
- }));
2125
+ Future<Map<String, Object?>> absorbSalvage({
2126
+ List<Map<String, Object?>> pendingWrites = const [],
2127
+ List<Map<String, Object?>> deadLetters = const [],
2128
+ String? session,
2129
+ }) async => _asMap(
2130
+ await _call('absorbSalvage', {
2131
+ 'pendingWrites': pendingWrites,
2132
+ 'deadLetters': deadLetters,
2133
+ if (session != null) 'session': session,
2134
+ }),
2135
+ );
2002
2136
 
2003
2137
  /// The salvage report from the most recent `connect(recover: true)` — `{ salvagedWrites, requeuedDeadLetters,
2004
2138
  /// unsalvageable }`, or null if the connection didn't recover. Show "recovered N writes" / surface
@@ -2008,9 +2142,10 @@ class NomosBridge {
2008
2142
 
2009
2143
  /// LOCAL-FIRST offer: carry opaque intent bytes into the GitHolon. Returns the new local head; durable on [sync].
2010
2144
  @Deprecated(
2011
- 'Engine-level generic writes are the footgun (session_bound_clients.md ask 1/7): '
2012
- 'dispatch through a generated <Domain>Client bound to a NomosSession '
2013
- '(XClient.bind(session)). Retained for CLIs/admin tooling/generated internals only.')
2145
+ 'Engine-level generic writes are the footgun (session_bound_clients.md ask 1/7): '
2146
+ 'dispatch through a generated <Domain>Client bound to a NomosSession '
2147
+ '(XClient.bind(session)). Retained for CLIs/admin tooling/generated internals only.',
2148
+ )
2014
2149
  Future<String> offer({required String intentBytes}) async {
2015
2150
  final r = _asMap(await _call('offer', {'intentBytes': intentBytes}));
2016
2151
  return r['head'].toString();
@@ -2025,10 +2160,11 @@ class NomosBridge {
2025
2160
  /// inject evidence. [force] carries the deploy/evolve force flag through. [session] pins the workspace in
2026
2161
  /// a cluster harness (absent ⇒ the runner's sole-session default).
2027
2162
  @Deprecated(
2028
- 'Engine-level string-y writes are the footgun (session_bound_clients.md ask 1/7): '
2029
- 'dispatch through a generated <Domain>Client bound to a NomosSession '
2030
- '(XClient.bind(session) — no domain/directive strings at call sites). '
2031
- 'Retained for CLIs/admin tooling/generated internals only.')
2163
+ 'Engine-level string-y writes are the footgun (session_bound_clients.md ask 1/7): '
2164
+ 'dispatch through a generated <Domain>Client bound to a NomosSession '
2165
+ '(XClient.bind(session) — no domain/directive strings at call sites). '
2166
+ 'Retained for CLIs/admin tooling/generated internals only.',
2167
+ )
2032
2168
  Future<String> offerDirective({
2033
2169
  required String domain,
2034
2170
  required String directiveId,
@@ -2043,20 +2179,22 @@ class NomosBridge {
2043
2179
  String? authorSecret,
2044
2180
  NomosBirthCertificate? birthCertificate,
2045
2181
  }) async {
2046
- final r = _asMap(await _call('offerDirective', {
2047
- 'domain': domain,
2048
- 'directiveId': directiveId,
2049
- 'payload': payload,
2050
- if (actor != null) 'actor': actor,
2051
- if (domainHash != null) 'domainHash': domainHash,
2052
- if (nomosConflictSnapshots != null)
2053
- '__nomosConflictSnapshots': nomosConflictSnapshots,
2054
- if (force != null) 'force': force,
2055
- if (session != null) 'session': session,
2056
- if (authorSecret != null) 'authorSecret': authorSecret,
2057
- if (birthCertificate != null)
2058
- 'birthCertificate': birthCertificate.toWire(),
2059
- }));
2182
+ final r = _asMap(
2183
+ await _call('offerDirective', {
2184
+ 'domain': domain,
2185
+ 'directiveId': directiveId,
2186
+ 'payload': payload,
2187
+ if (actor != null) 'actor': actor,
2188
+ if (domainHash != null) 'domainHash': domainHash,
2189
+ if (nomosConflictSnapshots != null)
2190
+ '__nomosConflictSnapshots': nomosConflictSnapshots,
2191
+ if (force != null) 'force': force,
2192
+ if (session != null) 'session': session,
2193
+ if (authorSecret != null) 'authorSecret': authorSecret,
2194
+ if (birthCertificate != null)
2195
+ 'birthCertificate': birthCertificate.toWire(),
2196
+ }),
2197
+ );
2060
2198
  return r['head'].toString();
2061
2199
  }
2062
2200
 
@@ -2065,9 +2203,10 @@ class NomosBridge {
2065
2203
  /// generated create method returns the typed id with NO query-after round-trip. [created] is empty for
2066
2204
  /// pure mutations.
2067
2205
  @Deprecated(
2068
- 'Engine-level string-y writes are the footgun (session_bound_clients.md ask 1/7): '
2069
- 'dispatch through a generated <Domain>Client bound to a NomosSession. '
2070
- 'Retained for CLIs/admin tooling/generated internals only.')
2206
+ 'Engine-level string-y writes are the footgun (session_bound_clients.md ask 1/7): '
2207
+ 'dispatch through a generated <Domain>Client bound to a NomosSession. '
2208
+ 'Retained for CLIs/admin tooling/generated internals only.',
2209
+ )
2071
2210
  Future<NomosOfferOutcome> offerCreates({
2072
2211
  required String domain,
2073
2212
  required String directiveId,
@@ -2079,19 +2218,18 @@ class NomosBridge {
2079
2218
  String? session,
2080
2219
  String? authorSecret,
2081
2220
  NomosBirthCertificate? birthCertificate,
2082
- }) =>
2083
- offerOutcome(
2084
- domain: domain,
2085
- directiveId: directiveId,
2086
- payload: payload,
2087
- actor: actor,
2088
- domainHash: domainHash,
2089
- nomosConflictSnapshots: nomosConflictSnapshots,
2090
- force: force,
2091
- session: session,
2092
- authorSecret: authorSecret,
2093
- birthCertificate: birthCertificate,
2094
- );
2221
+ }) => offerOutcome(
2222
+ domain: domain,
2223
+ directiveId: directiveId,
2224
+ payload: payload,
2225
+ actor: actor,
2226
+ domainHash: domainHash,
2227
+ nomosConflictSnapshots: nomosConflictSnapshots,
2228
+ force: force,
2229
+ session: session,
2230
+ authorSecret: authorSecret,
2231
+ birthCertificate: birthCertificate,
2232
+ );
2095
2233
 
2096
2234
  /// THE ONE OUTCOME-RETURNING OFFER LANE — [offerDirective] answering the full typed [NomosOfferOutcome]
2097
2235
  /// (head + law-minted `created` NomosRefs + births), for EVERY generated method (create or mutate). A pure
@@ -2121,23 +2259,29 @@ class NomosBridge {
2121
2259
  // attributable breakdown of one offer with no new API.
2122
2260
  final t0 = DateTime.now().millisecondsSinceEpoch;
2123
2261
  try {
2124
- r = _asMap(await _call('offerDirective', {
2125
- 'domain': domain,
2126
- 'directiveId': directiveId,
2127
- 'payload': payload,
2128
- if (actor != null) 'actor': actor,
2129
- if (domainHash != null) 'domainHash': domainHash,
2130
- if (nomosConflictSnapshots != null)
2131
- '__nomosConflictSnapshots': nomosConflictSnapshots,
2132
- if (force != null) 'force': force,
2133
- if (session != null) 'session': session,
2134
- if (authorSecret != null) 'authorSecret': authorSecret,
2135
- if (birthCertificate != null)
2136
- 'birthCertificate': birthCertificate.toWire(),
2137
- }));
2262
+ r = _asMap(
2263
+ await _call('offerDirective', {
2264
+ 'domain': domain,
2265
+ 'directiveId': directiveId,
2266
+ 'payload': payload,
2267
+ if (actor != null) 'actor': actor,
2268
+ if (domainHash != null) 'domainHash': domainHash,
2269
+ if (nomosConflictSnapshots != null)
2270
+ '__nomosConflictSnapshots': nomosConflictSnapshots,
2271
+ if (force != null) 'force': force,
2272
+ if (session != null) 'session': session,
2273
+ if (authorSecret != null) 'authorSecret': authorSecret,
2274
+ if (birthCertificate != null)
2275
+ 'birthCertificate': birthCertificate.toWire(),
2276
+ }),
2277
+ );
2138
2278
  } catch (e) {
2139
- throw await _wrapDomainNotInstalled(e,
2140
- domain: domain, domainHash: domainHash, requestedWorkspace: session);
2279
+ throw await _wrapDomainNotInstalled(
2280
+ e,
2281
+ domain: domain,
2282
+ domainHash: domainHash,
2283
+ requestedWorkspace: session,
2284
+ );
2141
2285
  }
2142
2286
  final t1 = DateTime.now().millisecondsSinceEpoch;
2143
2287
  final timing = r['timing'];
@@ -2150,17 +2294,19 @@ class NomosBridge {
2150
2294
  }
2151
2295
 
2152
2296
  noteLocalSpan(
2153
- track: 'submit',
2154
- name: 'offer: bridge ipc (up-leg)',
2155
- kind: 'bridge-ipc',
2156
- t0: t0,
2157
- t1: within(recv));
2297
+ track: 'submit',
2298
+ name: 'offer: bridge ipc (up-leg)',
2299
+ kind: 'bridge-ipc',
2300
+ t0: t0,
2301
+ t1: within(recv),
2302
+ );
2158
2303
  noteLocalSpan(
2159
- track: 'submit',
2160
- name: 'offer: response delivery (down-leg)',
2161
- kind: 'response-delivery',
2162
- t0: within(done),
2163
- t1: t1);
2304
+ track: 'submit',
2305
+ name: 'offer: response delivery (down-leg)',
2306
+ kind: 'response-delivery',
2307
+ t0: within(done),
2308
+ t1: t1,
2309
+ );
2164
2310
  }
2165
2311
  }
2166
2312
  final births = ((r['birthOutcomes'] as List?) ?? const [])
@@ -2182,8 +2328,9 @@ class NomosBridge {
2182
2328
  final bornRaw = (r['born'] as List?);
2183
2329
  final bornNames = <String>[
2184
2330
  if (bornRaw != null)
2185
- ...bornRaw
2186
- .map((e) => (e is Map ? (e['workspace'] ?? '') : e).toString())
2331
+ ...bornRaw.map(
2332
+ (e) => (e is Map ? (e['workspace'] ?? '') : e).toString(),
2333
+ )
2187
2334
  else
2188
2335
  ...births
2189
2336
  .map((m) => (m['workspace'] ?? '').toString())
@@ -2196,7 +2343,8 @@ class NomosBridge {
2196
2343
  }
2197
2344
  }
2198
2345
  final sourceSession = session ?? _sessionName;
2199
- final birthAuthorSecret = authorSecret ??
2346
+ final birthAuthorSecret =
2347
+ authorSecret ??
2200
2348
  (sourceSession == null
2201
2349
  ? null
2202
2350
  : _eventPlane._sessionAuthorSecrets[sourceSession]);
@@ -2219,17 +2367,23 @@ class NomosBridge {
2219
2367
  workspace: ws,
2220
2368
  created: ((r['created'] as List?) ?? const [])
2221
2369
  .whereType<Map>()
2222
- .map((m) => NomosCreated(
2370
+ .map(
2371
+ (m) => NomosCreated(
2223
2372
  type: (m['type'] ?? '').toString(),
2224
2373
  id: (m['id'] ?? '').toString(),
2225
- workspace: ws))
2374
+ workspace: ws,
2375
+ ),
2376
+ )
2226
2377
  .toList(),
2227
2378
  affected: ((r['affected'] as List?) ?? const [])
2228
2379
  .whereType<Map>()
2229
- .map((m) => NomosCreated(
2380
+ .map(
2381
+ (m) => NomosCreated(
2230
2382
  type: (m['type'] ?? '').toString(),
2231
2383
  id: (m['id'] ?? '').toString(),
2232
- workspace: ws))
2384
+ workspace: ws,
2385
+ ),
2386
+ )
2233
2387
  .toList(),
2234
2388
  born: bornNames,
2235
2389
  birthOutcomes: births,
@@ -2241,12 +2395,16 @@ class NomosBridge {
2241
2395
  /// context (which session, what it actually runs, the likely causes) — the string itself
2242
2396
  /// originates in the sealed kernel, so the bridge (not the kernel) owns the ergonomics.
2243
2397
  /// Anything that isn't that refusal passes through unchanged.
2244
- Future<Object> _wrapDomainNotInstalled(Object e,
2245
- {required String domain,
2246
- String? domainHash,
2247
- String? requestedWorkspace}) async {
2248
- if (!RegExp(r"no active installed law declares domain '")
2249
- .hasMatch(e.toString())) return e;
2398
+ Future<Object> _wrapDomainNotInstalled(
2399
+ Object e, {
2400
+ required String domain,
2401
+ String? domainHash,
2402
+ String? requestedWorkspace,
2403
+ }) async {
2404
+ if (!RegExp(
2405
+ r"no active installed law declares domain '",
2406
+ ).hasMatch(e.toString()))
2407
+ return e;
2250
2408
  final self = this;
2251
2409
  final sessionWs = self is NomosSession ? self.workspace : null;
2252
2410
  var activeKeys = const <String>[];
@@ -2272,8 +2430,9 @@ class NomosBridge {
2272
2430
  /// installed package, read off the LOCAL projection (offline). Keys = the ACTIVE domain keys the
2273
2431
  /// workspace serves. Null when no on-ledger interface is observed yet (pre-#72e law).
2274
2432
  Future<Map<String, Object?>?> interfaceOffer({String? session}) async {
2275
- final r = _asMap(await _call(
2276
- 'interfaceOffer', {if (session != null) 'session': session}));
2433
+ final r = _asMap(
2434
+ await _call('interfaceOffer', {if (session != null) 'session': session}),
2435
+ );
2277
2436
  return (r['offer'] as Map?)?.cast<String, Object?>();
2278
2437
  }
2279
2438
 
@@ -2301,26 +2460,32 @@ class NomosBridge {
2301
2460
  /// cloud (omit for the local plane — a locally-born child mounts in place, no re-clone).
2302
2461
  /// [authorSecret] — the device signing key for the followed session's writes (era-0: an attested-born
2303
2462
  /// child is WARRANTED; write access needs its owner's enrolled device key).
2304
- Future<NomosSession> follow<TAggregate>(NomosRef<TAggregate> ref,
2305
- {String? cloud,
2306
- String? clientId,
2307
- String? authToken,
2308
- bool allowUnborn = false,
2309
- String? authorSecret}) async {
2463
+ Future<NomosSession> follow<TAggregate>(
2464
+ NomosRef<TAggregate> ref, {
2465
+ String? cloud,
2466
+ String? clientId,
2467
+ String? authToken,
2468
+ bool allowUnborn = false,
2469
+ String? authorSecret,
2470
+ }) async {
2310
2471
  final ws = NmsInternalNomosRefs.workspace(ref);
2311
2472
  if (ws.isEmpty) {
2312
- throw ArgumentError.value(ref, 'ref',
2313
- 'follow: the NomosRef carries no workspace — only a workspace-complete ref is followable');
2473
+ throw ArgumentError.value(
2474
+ ref,
2475
+ 'ref',
2476
+ 'follow: the NomosRef carries no workspace — only a workspace-complete ref is followable',
2477
+ );
2314
2478
  }
2315
2479
  if (!_sessionNames.contains(ws)) {
2316
2480
  await connect(
2317
- cloud: cloud,
2318
- workspace: ws,
2319
- session: ws,
2320
- clientId: clientId,
2321
- authToken: authToken,
2322
- allowUnborn: allowUnborn,
2323
- authorSecret: authorSecret);
2481
+ cloud: cloud,
2482
+ workspace: ws,
2483
+ session: ws,
2484
+ clientId: clientId,
2485
+ authToken: authToken,
2486
+ allowUnborn: allowUnborn,
2487
+ authorSecret: authorSecret,
2488
+ );
2324
2489
  }
2325
2490
  return session(ws);
2326
2491
  }
@@ -2339,10 +2504,12 @@ class NomosBridge {
2339
2504
  /// which every attested-born child is — refuses unsigned writes, so a locally-born child the app will
2340
2505
  /// WRITE to must be acquired with its owner's device key). Applies to the resident (local) open;
2341
2506
  /// the credentials path takes its own `NomosSessionCredentials.authorSecret` (falling back to this).
2342
- Future<NomosSession> acquireSession(String workspace,
2343
- {Future<NomosSessionCredentials> Function()? ifAbsentCredentials,
2344
- String? authorSecret,
2345
- String? inheritedPrincipal}) {
2507
+ Future<NomosSession> acquireSession(
2508
+ String workspace, {
2509
+ Future<NomosSessionCredentials> Function()? ifAbsentCredentials,
2510
+ String? authorSecret,
2511
+ String? inheritedPrincipal,
2512
+ }) {
2346
2513
  // Fast path: a live session needs no runner round-trip (and never asks for credentials).
2347
2514
  final plane = _eventPlane;
2348
2515
  final sourcePrincipal = inheritedPrincipal ?? principal;
@@ -2351,7 +2518,8 @@ class NomosBridge {
2351
2518
  final existing = plane._sessionPrincipals[workspace];
2352
2519
  if (existing != null && existing != sourcePrincipal) {
2353
2520
  throw StateError(
2354
- 'acquireSession("$workspace"): the workspace is already bound to a different signed-in person');
2521
+ 'acquireSession("$workspace"): the workspace is already bound to a different signed-in person',
2522
+ );
2355
2523
  }
2356
2524
  plane._sessionPrincipals[workspace] = sourcePrincipal;
2357
2525
  final source = _sessionName;
@@ -2389,41 +2557,43 @@ class NomosBridge {
2389
2557
  return ready(session(workspace));
2390
2558
  }
2391
2559
  // SINGLE-FLIGHT: one in-flight open per workspace per plane; concurrent callers join it.
2392
- return plane._acquiring.putIfAbsent(workspace, () async {
2393
- // RESIDENCY IS THE KERNEL'S TO DEMAND AND OURS TO SATISFY. Replaying a chain can reach a
2394
- // colocated read whose source workspace is not mounted; the kernel refuses TYPED, naming it.
2395
- // Mount the named sibling — which consults local custody first and only then clones — and
2396
- // retry. Bounded, and each name is satisfied at most once, so a genuinely missing workspace
2397
- // fails with the kernel's own refusal instead of spinning.
2398
- final satisfied = <String>{};
2399
- for (var round = 0;; round++) {
2400
- try {
2401
- return await _acquireSessionOnce(
2402
- workspace,
2403
- ready: ready,
2404
- inheritPrincipal: inheritPrincipal,
2405
- ifAbsentCredentials: ifAbsentCredentials,
2406
- authorSecret: authorSecret,
2407
- );
2408
- } catch (error) {
2409
- final required = nomosInternalResidencyRequiredWorkspace(error);
2410
- if (required == null ||
2411
- required == workspace ||
2412
- !satisfied.add(required) ||
2413
- round >= 8) {
2414
- rethrow;
2560
+ return plane._acquiring
2561
+ .putIfAbsent(workspace, () async {
2562
+ // RESIDENCY IS THE KERNEL'S TO DEMAND AND OURS TO SATISFY. Replaying a chain can reach a
2563
+ // colocated read whose source workspace is not mounted; the kernel refuses TYPED, naming it.
2564
+ // Mount the named sibling — which consults local custody first and only then clones — and
2565
+ // retry. Bounded, and each name is satisfied at most once, so a genuinely missing workspace
2566
+ // fails with the kernel's own refusal instead of spinning.
2567
+ final satisfied = <String>{};
2568
+ for (var round = 0; ; round++) {
2569
+ try {
2570
+ return await _acquireSessionOnce(
2571
+ workspace,
2572
+ ready: ready,
2573
+ inheritPrincipal: inheritPrincipal,
2574
+ ifAbsentCredentials: ifAbsentCredentials,
2575
+ authorSecret: authorSecret,
2576
+ );
2577
+ } catch (error) {
2578
+ final required = nomosInternalResidencyRequiredWorkspace(error);
2579
+ if (required == null ||
2580
+ required == workspace ||
2581
+ !satisfied.add(required) ||
2582
+ round >= 8) {
2583
+ rethrow;
2584
+ }
2585
+ await acquireSession(
2586
+ required,
2587
+ ifAbsentCredentials: ifAbsentCredentials,
2588
+ authorSecret: authorSecret,
2589
+ inheritedPrincipal: sourcePrincipal,
2590
+ );
2591
+ }
2415
2592
  }
2416
- await acquireSession(
2417
- required,
2418
- ifAbsentCredentials: ifAbsentCredentials,
2419
- authorSecret: authorSecret,
2420
- inheritedPrincipal: sourcePrincipal,
2421
- );
2422
- }
2423
- }
2424
- }).whenComplete(() {
2425
- plane._acquiring.remove(workspace);
2426
- });
2593
+ })
2594
+ .whenComplete(() {
2595
+ plane._acquiring.remove(workspace);
2596
+ });
2427
2597
  }
2428
2598
 
2429
2599
  Future<NomosSession> _acquireSessionOnce(
@@ -2445,18 +2615,20 @@ class NomosBridge {
2445
2615
  // credentials (an explicitly local tool/test), preserve the local-only open.
2446
2616
  var resident = false;
2447
2617
  try {
2448
- resident = (await listWorkspaces())
2449
- .any((w) => w['workspace']?.toString() == workspace);
2618
+ resident = (await listWorkspaces()).any(
2619
+ (w) => w['workspace']?.toString() == workspace,
2620
+ );
2450
2621
  } catch (_) {
2451
2622
  // a pre-connect plane has no registry yet — fall through to the credentials decision below.
2452
2623
  }
2453
2624
  if (resident) {
2454
2625
  if (ifAbsentCredentials == null) {
2455
2626
  await connect(
2456
- workspace: workspace,
2457
- session: workspace,
2458
- allowUnborn: true,
2459
- authorSecret: authorSecret);
2627
+ workspace: workspace,
2628
+ session: workspace,
2629
+ allowUnborn: true,
2630
+ authorSecret: authorSecret,
2631
+ );
2460
2632
  } else {
2461
2633
  final c = await ifAbsentCredentials();
2462
2634
  await connect(
@@ -2496,8 +2668,9 @@ class NomosBridge {
2496
2668
  }
2497
2669
  if (ifAbsentCredentials == null) {
2498
2670
  throw StateError(
2499
- 'acquireSession("$workspace"): the workspace is not on this device\'s plane and no '
2500
- 'ifAbsentCredentials was given — a cloud open is genuinely required');
2671
+ 'acquireSession("$workspace"): the workspace is not on this device\'s plane and no '
2672
+ 'ifAbsentCredentials was given — a cloud open is genuinely required',
2673
+ );
2501
2674
  }
2502
2675
  final c = await ifAbsentCredentials();
2503
2676
  await connect(
@@ -2519,9 +2692,7 @@ class NomosBridge {
2519
2692
  /// to this process-wide plane. Child opens consult it before cloning from
2520
2693
  /// cloud; the kernel remains the only component that can authenticate or
2521
2694
  /// interpret the returned bytes.
2522
- void nomosInternalRetainLocalCustodyLoader(
2523
- NomosLocalCustodyLoader loader,
2524
- ) {
2695
+ void nomosInternalRetainLocalCustodyLoader(NomosLocalCustodyLoader loader) {
2525
2696
  _eventPlane._localCustodyLoader = loader;
2526
2697
  }
2527
2698
 
@@ -2535,8 +2706,10 @@ class NomosBridge {
2535
2706
  /// Refresh client-held credentials on this already-open workspace without
2536
2707
  /// replacing its resident holon or refolding custody. This grants no
2537
2708
  /// authority: every offer is still judged by the kernel's author gate.
2538
- Future<void> refreshCredentials(
2539
- {String? authToken, String? authorSecret}) async {
2709
+ Future<void> refreshCredentials({
2710
+ String? authToken,
2711
+ String? authorSecret,
2712
+ }) async {
2540
2713
  if (authToken == null && authorSecret == null) return;
2541
2714
  await _call('refreshCredentials', {
2542
2715
  if (authToken != null) 'authToken': authToken,
@@ -2557,12 +2730,16 @@ class NomosBridge {
2557
2730
 
2558
2731
  /// @internal The immutable genesis of a sibling custody chain. This is observational metadata used to
2559
2732
  /// compose an ordinary source-enrolment offer; the kernel still reopens and proves every foreign read.
2560
- Future<String> nomosWorkspaceGenesis(String workspace,
2561
- {String? session}) async =>
2562
- (_asMap(await _call('nomosWorkspaceGenesis', {
2563
- 'workspace': workspace,
2564
- if (session != null) 'session': session,
2565
- }))['genesis'] ??
2733
+ Future<String> nomosWorkspaceGenesis(
2734
+ String workspace, {
2735
+ String? session,
2736
+ }) async =>
2737
+ (_asMap(
2738
+ await _call('nomosWorkspaceGenesis', {
2739
+ 'workspace': workspace,
2740
+ if (session != null) 'session': session,
2741
+ }),
2742
+ )['genesis'] ??
2566
2743
  '')
2567
2744
  .toString();
2568
2745
 
@@ -2575,9 +2752,10 @@ class NomosBridge {
2575
2752
  /// the holon encodes the v2 offer envelope via the single codec and admits it. Returns the new local head.
2576
2753
  /// (This is the canonical name; [offerDirective] is the legacy alias the generated clients still call.)
2577
2754
  @Deprecated(
2578
- 'Engine-level string-y writes are the footgun (session_bound_clients.md ask 1/7): '
2579
- 'dispatch through a generated <Domain>Client bound to a NomosSession. '
2580
- 'Retained for CLIs/admin tooling/generated internals only.')
2755
+ 'Engine-level string-y writes are the footgun (session_bound_clients.md ask 1/7): '
2756
+ 'dispatch through a generated <Domain>Client bound to a NomosSession. '
2757
+ 'Retained for CLIs/admin tooling/generated internals only.',
2758
+ )
2581
2759
  Future<String> offerIntent({
2582
2760
  required String domain,
2583
2761
  required String directiveId,
@@ -2587,26 +2765,25 @@ class NomosBridge {
2587
2765
  List<Map<String, Object?>>? nomosConflictSnapshots,
2588
2766
  bool? force,
2589
2767
  String? session,
2590
- }) =>
2591
- offerDirective(
2592
- domain: domain,
2593
- directiveId: directiveId,
2594
- payload: payload,
2595
- actor: actor,
2596
- domainHash: domainHash,
2597
- nomosConflictSnapshots: nomosConflictSnapshots,
2598
- force: force,
2599
- session: session);
2600
-
2601
- /// RELEASE COHERENCE — assert the connected cloud's kernel supports every capability this law needs (the
2602
- /// generated client passes its baked `requiresKernel`, e.g. `['compoundKeys']` for a compound-key query).
2603
- /// Throws a typed [KernelCoherenceError] on a skew (a stale/mismatched cloud kernel) with the named reason,
2604
- /// instead of an opaque deep-fold refusal. A best-effort no-op when the cloud advertises no capabilities
2605
- /// (older worker / offline). Called automatically by the generated client at bind; safe to call directly.
2606
- Future<void> assertKernelCoherence(List<String> requiresKernel,
2607
- {String? lawHash, String? session}) async {
2768
+ }) => offerDirective(
2769
+ domain: domain,
2770
+ directiveId: directiveId,
2771
+ payload: payload,
2772
+ actor: actor,
2773
+ domainHash: domainHash,
2774
+ nomosConflictSnapshots: nomosConflictSnapshots,
2775
+ force: force,
2776
+ session: session,
2777
+ );
2778
+
2779
+ /// Resolve the generated law's standard OSGi Contract Namespace requirements before bind.
2780
+ Future<void> assertKernelCoherence(
2781
+ List<Map<String, String>> kernelContracts, {
2782
+ String? lawHash,
2783
+ String? session,
2784
+ }) async {
2608
2785
  await _call('assertKernelCoherence', {
2609
- 'requiresKernel': requiresKernel,
2786
+ 'kernelContracts': kernelContracts,
2610
2787
  if (lawHash != null) 'lawHash': lawHash,
2611
2788
  if (session != null) 'session': session,
2612
2789
  });
@@ -2626,10 +2803,11 @@ class NomosBridge {
2626
2803
  /// The kernel looks up `(domain, directiveId)` in the ACTIVE installed law — [domain] must name the law
2627
2804
  /// that actually declares [directiveId], never assume `'workspaces'`.
2628
2805
  @Deprecated(
2629
- 'Engine-level generic writes with a loose parent string are the footgun '
2630
- '(session_bound_clients.md asks 1/4/7): births go through the generated <Domain>Client '
2631
- 'bound to the PARENT NomosSession (its birth method infers parent = the session workspace). '
2632
- 'Retained for CLIs/admin tooling/generated compatibility shims only.')
2806
+ 'Engine-level generic writes with a loose parent string are the footgun '
2807
+ '(session_bound_clients.md asks 1/4/7): births go through the generated <Domain>Client '
2808
+ 'bound to the PARENT NomosSession (its birth method infers parent = the session workspace). '
2809
+ 'Retained for CLIs/admin tooling/generated compatibility shims only.',
2810
+ )
2633
2811
  Future<Map<String, Object?>> createWorkspace({
2634
2812
  required String parent,
2635
2813
  required String actor,
@@ -2645,20 +2823,26 @@ class NomosBridge {
2645
2823
  // a frontend dev never threads a key through.
2646
2824
  final Map<String, Object?> r;
2647
2825
  try {
2648
- r = _asMap(await _call('createWorkspace', {
2649
- 'parent': parent,
2650
- 'actor': actor,
2651
- if (authorSecret != null) 'authorSecret': authorSecret,
2652
- if (name != null) 'name': name,
2653
- if (payload != null) 'payload': payload,
2654
- 'directiveId': directiveId,
2655
- 'domain': domain,
2656
- if (domainHash != null) 'domainHash': domainHash,
2657
- if (authToken != null) 'authToken': authToken,
2658
- }));
2826
+ r = _asMap(
2827
+ await _call('createWorkspace', {
2828
+ 'parent': parent,
2829
+ 'actor': actor,
2830
+ if (authorSecret != null) 'authorSecret': authorSecret,
2831
+ if (name != null) 'name': name,
2832
+ if (payload != null) 'payload': payload,
2833
+ 'directiveId': directiveId,
2834
+ 'domain': domain,
2835
+ if (domainHash != null) 'domainHash': domainHash,
2836
+ if (authToken != null) 'authToken': authToken,
2837
+ }),
2838
+ );
2659
2839
  } catch (e) {
2660
- throw await _wrapDomainNotInstalled(e,
2661
- domain: domain, domainHash: domainHash, requestedWorkspace: parent);
2840
+ throw await _wrapDomainNotInstalled(
2841
+ e,
2842
+ domain: domain,
2843
+ domainHash: domainHash,
2844
+ requestedWorkspace: parent,
2845
+ );
2662
2846
  }
2663
2847
  final verdict = (r['verdict'] as Map?)?.cast<String, Object?>() ?? r;
2664
2848
  // STRUCTURED AUTHZ REFUSAL (co2 DX): a refused relay used to surface only the generic 422 body.
@@ -2695,17 +2879,19 @@ class NomosBridge {
2695
2879
  String? authToken,
2696
2880
  }) async {
2697
2881
  // [authorSecret] defaults to the connection's signing key — the frontend dev never handles a key.
2698
- final r = _asMap(await _call('shareWith', {
2699
- 'subject': subject,
2700
- 'relation': relation,
2701
- if (authorSecret != null) 'authorSecret': authorSecret,
2702
- 'asserterPrincipal': asserterPrincipal,
2703
- if (homeLawHash != null) 'homeLawHash': homeLawHash,
2704
- 'object': object,
2705
- if (asserterWorkspace != null) 'asserterWorkspace': asserterWorkspace,
2706
- if (attestedAt != null) 'attestedAt': attestedAt,
2707
- if (authToken != null) 'authToken': authToken,
2708
- }));
2882
+ final r = _asMap(
2883
+ await _call('shareWith', {
2884
+ 'subject': subject,
2885
+ 'relation': relation,
2886
+ if (authorSecret != null) 'authorSecret': authorSecret,
2887
+ 'asserterPrincipal': asserterPrincipal,
2888
+ if (homeLawHash != null) 'homeLawHash': homeLawHash,
2889
+ 'object': object,
2890
+ if (asserterWorkspace != null) 'asserterWorkspace': asserterWorkspace,
2891
+ if (attestedAt != null) 'attestedAt': attestedAt,
2892
+ if (authToken != null) 'authToken': authToken,
2893
+ }),
2894
+ );
2709
2895
  return (r['verdict'] as Map?)?.cast<String, Object?>() ?? r;
2710
2896
  }
2711
2897
 
@@ -2714,13 +2900,14 @@ class NomosBridge {
2714
2900
  List<String>? actions,
2715
2901
  String domain = '',
2716
2902
  String directiveId = 'undo',
2717
- }) async =>
2718
- _asMap(await _call('undoMostRecent', {
2719
- 'scope': scope,
2720
- if (actions != null) 'actions': actions,
2721
- 'domain': domain,
2722
- 'directiveId': directiveId,
2723
- }));
2903
+ }) async => _asMap(
2904
+ await _call('undoMostRecent', {
2905
+ 'scope': scope,
2906
+ if (actions != null) 'actions': actions,
2907
+ 'domain': domain,
2908
+ 'directiveId': directiveId,
2909
+ }),
2910
+ );
2724
2911
 
2725
2912
  /// REDO the most-recent undo (strike the strike → its target re-applies). Same [scope]/[domain]/[directiveId]
2726
2913
  /// as [undoMostRecent]. Returns `{ok, redone, head}`.
@@ -2728,12 +2915,13 @@ class NomosBridge {
2728
2915
  String scope = 'all',
2729
2916
  String domain = '',
2730
2917
  String directiveId = 'undo',
2731
- }) async =>
2732
- _asMap(await _call('redoMostRecent', {
2733
- 'scope': scope,
2734
- 'domain': domain,
2735
- 'directiveId': directiveId,
2736
- }));
2918
+ }) async => _asMap(
2919
+ await _call('redoMostRecent', {
2920
+ 'scope': scope,
2921
+ 'domain': domain,
2922
+ 'directiveId': directiveId,
2923
+ }),
2924
+ );
2737
2925
 
2738
2926
  /// Framework-private business correction seam. Generated clients bind the domain, its actions and actor;
2739
2927
  /// application code supplies only the correction kind and its mandatory reason.
@@ -2744,15 +2932,16 @@ class NomosBridge {
2744
2932
  required String reason,
2745
2933
  String? domainHash,
2746
2934
  String? actor,
2747
- }) async =>
2748
- _asMap(await _call('correctMostRecent', {
2749
- 'kind': kind,
2750
- 'domain': domain,
2751
- 'actions': actions,
2752
- 'reason': reason,
2753
- if (domainHash != null) 'domainHash': domainHash,
2754
- if (actor != null) 'actor': actor,
2755
- }));
2935
+ }) async => _asMap(
2936
+ await _call('correctMostRecent', {
2937
+ 'kind': kind,
2938
+ 'domain': domain,
2939
+ 'actions': actions,
2940
+ 'reason': reason,
2941
+ if (domainHash != null) 'domainHash': domainHash,
2942
+ if (actor != null) 'actor': actor,
2943
+ }),
2944
+ );
2756
2945
 
2757
2946
  /// Framework-private exact-history adoption seam. Generated clients expose this as
2758
2947
  /// `model.adopt()` and, only when prior business outcomes differ, a chained
@@ -2762,11 +2951,13 @@ class NomosBridge {
2762
2951
  required String candidateHash,
2763
2952
  String? because,
2764
2953
  }) async {
2765
- final r = _asMap(await _call('adoptGeneratedModel', {
2766
- 'domain': domain,
2767
- 'candidateHash': candidateHash,
2768
- if (because != null) 'because': because,
2769
- }));
2954
+ final r = _asMap(
2955
+ await _call('adoptGeneratedModel', {
2956
+ 'domain': domain,
2957
+ 'candidateHash': candidateHash,
2958
+ if (because != null) 'because': because,
2959
+ }),
2960
+ );
2770
2961
  return ((r['verdict'] as Map?) ?? const <String, Object?>{})
2771
2962
  .cast<String, Object?>();
2772
2963
  }
@@ -2794,11 +2985,13 @@ class NomosBridge {
2794
2985
  required String currentHash,
2795
2986
  String? because,
2796
2987
  }) async {
2797
- final r = _asMap(await _call('restorePreviousGeneratedModel', {
2798
- 'domain': domain,
2799
- 'currentHash': currentHash,
2800
- if (because != null) 'because': because,
2801
- }));
2988
+ final r = _asMap(
2989
+ await _call('restorePreviousGeneratedModel', {
2990
+ 'domain': domain,
2991
+ 'currentHash': currentHash,
2992
+ if (because != null) 'because': because,
2993
+ }),
2994
+ );
2802
2995
  return ((r['verdict'] as Map?) ?? const <String, Object?>{})
2803
2996
  .cast<String, Object?>();
2804
2997
  }
@@ -2820,12 +3013,16 @@ class NomosBridge {
2820
3013
  /// DECODED directive payload; affected = the aggregate refs its sealed events touched). `base` = a commit-
2821
3014
  /// oid cursor (only intents newer than it); omitted = from the tip bounded by
2822
3015
  /// `limit` (default 200). Generic raw rows — the typed shaping is the codegen `renderActivity`.
2823
- Future<List<Map<String, dynamic>>> activityLog(
2824
- {String? base, int? limit}) async {
2825
- final r = _asMap(await _call('activityLog', {
2826
- if (base != null) 'base': base,
2827
- if (limit != null) 'limit': limit,
2828
- }));
3016
+ Future<List<Map<String, dynamic>>> activityLog({
3017
+ String? base,
3018
+ int? limit,
3019
+ }) async {
3020
+ final r = _asMap(
3021
+ await _call('activityLog', {
3022
+ if (base != null) 'base': base,
3023
+ if (limit != null) 'limit': limit,
3024
+ }),
3025
+ );
2829
3026
  return ((r['rows'] as List?) ?? const [])
2830
3027
  .map((e) => (e as Map).cast<String, dynamic>())
2831
3028
  .toList();
@@ -2842,10 +3039,16 @@ class NomosBridge {
2842
3039
 
2843
3040
  /// Watch the RUNTIME activity feed: a stream that re-emits the newest-first activity rows on every local
2844
3041
  /// change (doorbell/sync tick). `base` = a commit-oid cursor. Cancelling drops the watch on the runner.
2845
- Stream<List<dynamic>> watchActivity(
2846
- {String? base, int? limit, String? session}) =>
2847
- _watchStream(
2848
- kind: 'activity', base: base, limit: limit, session: session);
3042
+ Stream<List<dynamic>> watchActivity({
3043
+ String? base,
3044
+ int? limit,
3045
+ String? session,
3046
+ }) => _watchStream(
3047
+ kind: 'activity',
3048
+ base: base,
3049
+ limit: limit,
3050
+ session: session,
3051
+ );
2849
3052
 
2850
3053
  /// Mint an ed25519 DEVICE KEY in-kernel → `{secret, public}` (base64). The SECRET stays on this device
2851
3054
  /// (persist it in secure storage); pass it to [connect] as `authorSecret` (or per-offer `signWith`) so the
@@ -2866,9 +3069,18 @@ class NomosBridge {
2866
3069
  /// ed25519 PUBLIC half), parentId, epoch}`; `deviceSecret` is that key's base64 SECRET half, handed to the
2867
3070
  /// LOCAL kernel only (it never reaches a server). Returns typed authority evidence suitable for an offer.
2868
3071
  Future<NomosBirthCertificate> signBirthCert(
2869
- NomosBirthCertificateSpec spec, String deviceSecret) async =>
2870
- NomosBirthCertificate.fromWire(_asMap(_asMap(await _call('signBirthCert',
2871
- {'fields': spec.toWire(), 'deviceSecret': deviceSecret}))['cert']));
3072
+ NomosBirthCertificateSpec spec,
3073
+ String deviceSecret,
3074
+ ) async => NomosBirthCertificate.fromWire(
3075
+ _asMap(
3076
+ _asMap(
3077
+ await _call('signBirthCert', {
3078
+ 'fields': spec.toWire(),
3079
+ 'deviceSecret': deviceSecret,
3080
+ }),
3081
+ )['cert'],
3082
+ ),
3083
+ );
2872
3084
 
2873
3085
  /// MINT THE KEYLESS ENROLL-ONCE DELEGATION (`enrollFirstDevice.delegationCert`, the verified-first-device
2874
3086
  /// lane). The device SELF-signs it; its authority is the carried `idpToken` (the kernel re-verifies that to
@@ -2877,26 +3089,33 @@ class NomosBridge {
2877
3089
  /// keyless home is born with a malformed cert chain (the platform_cert falls into the delegation slot →
2878
3090
  /// `home cert founder != delegation cert founder` at the home's own verify door).
2879
3091
  Future<Map<String, Object?>> mintKeylessDelegation(
2880
- Map<String, Object?> spec, String deviceSecret) async =>
2881
- _asMap(await _call('mintKeylessDelegation', {
2882
- 'spec': spec,
2883
- 'deviceSecret': deviceSecret,
2884
- }))['cert'] as Map<String, Object?>? ??
3092
+ Map<String, Object?> spec,
3093
+ String deviceSecret,
3094
+ ) async =>
3095
+ _asMap(
3096
+ await _call('mintKeylessDelegation', {
3097
+ 'spec': spec,
3098
+ 'deviceSecret': deviceSecret,
3099
+ }),
3100
+ )['cert']
3101
+ as Map<String, Object?>? ??
2885
3102
  const {};
2886
3103
 
2887
3104
  /// ENROLL THIS DEVICE — the LIVE gate: bind a device key's PUBLIC half to `principal` on-chain as a
2888
3105
  /// `signer` so the holon's gate can PROVE this device's authorship on replay (and the VA chain attest it).
2889
3106
  /// `publicKey` is the base64 PUBLIC half (from [mintDeviceKey]); `authToken` (defaults to the connect-time
2890
3107
  /// bearer) is the VERIFIED identity that authorizes the enrollment. Returns the offer verdict.
2891
- Future<Map<String, Object?>> enrollDevice(
2892
- {required String principal,
2893
- required String publicKey,
2894
- String? authToken}) async =>
2895
- _asMap(await _call('enrollDevice', {
2896
- 'principal': principal,
2897
- 'publicKey': publicKey,
2898
- if (authToken != null) 'authToken': authToken,
2899
- }));
3108
+ Future<Map<String, Object?>> enrollDevice({
3109
+ required String principal,
3110
+ required String publicKey,
3111
+ String? authToken,
3112
+ }) async => _asMap(
3113
+ await _call('enrollDevice', {
3114
+ 'principal': principal,
3115
+ 'publicKey': publicKey,
3116
+ if (authToken != null) 'authToken': authToken,
3117
+ }),
3118
+ );
2900
3119
 
2901
3120
  /// Derive the device key's base64 PUBLIC half from its base64 SECRET seed, inside the LOCAL kernel
2902
3121
  /// (the secret never leaves the device). The recovery lanes use this when only the secret was
@@ -2913,21 +3132,23 @@ class NomosBridge {
2913
3132
  /// Pass `remote: true` to force the CLOUD-gateway read even when this holon IS the parent — the
2914
3133
  /// seated barrier's proof-by-read: `enrolled: true` then means the shared chain durably holds this
2915
3134
  /// device's signer tuple, not merely that the local fold ran.
2916
- Future<Map<String, Object?>> parentSignerEnrollment(
2917
- {required String parent,
2918
- required String principal,
2919
- String? keyHash,
2920
- String? publicKey,
2921
- String? authToken,
2922
- bool remote = false}) async =>
2923
- _asMap(await _call('parentSignerEnrollment', {
2924
- 'parent': parent,
2925
- 'principal': principal,
2926
- if (keyHash != null) 'keyHash': keyHash,
2927
- if (publicKey != null) 'publicKey': publicKey,
2928
- if (authToken != null) 'authToken': authToken,
2929
- if (remote) 'remote': true,
2930
- }));
3135
+ Future<Map<String, Object?>> parentSignerEnrollment({
3136
+ required String parent,
3137
+ required String principal,
3138
+ String? keyHash,
3139
+ String? publicKey,
3140
+ String? authToken,
3141
+ bool remote = false,
3142
+ }) async => _asMap(
3143
+ await _call('parentSignerEnrollment', {
3144
+ 'parent': parent,
3145
+ 'principal': principal,
3146
+ if (keyHash != null) 'keyHash': keyHash,
3147
+ if (publicKey != null) 'publicKey': publicKey,
3148
+ if (authToken != null) 'authToken': authToken,
3149
+ if (remote) 'remote': true,
3150
+ }),
3151
+ );
2931
3152
 
2932
3153
  /// IDEMPOTENT PARENT-SIGNER RECOVERY — ensure this device key is a live `signer` on `parent`,
2933
3154
  /// lawfully restoring the tuple a parent custody REBIRTH lost (the device key + the born home stay
@@ -2940,27 +3161,29 @@ class NomosBridge {
2940
3161
  /// (+ [verifiedVia], the parent's AuthProvider issuer); a PRE-WARRANT parent admits the founding
2941
3162
  /// `enrollSigner` (no token needed).
2942
3163
  /// Returns `{parent, principal, keyHash, enrolled, repaired, domain?, directiveId?, checkedAt}`.
2943
- Future<Map<String, Object?>> ensureParentSignerEnrollment(
2944
- {required String parent,
2945
- required String principal,
2946
- String? publicKey,
2947
- String? authorSecret,
2948
- String? domain,
2949
- String? directiveId,
2950
- String? authToken,
2951
- String? idToken,
2952
- String? verifiedVia}) async =>
2953
- _asMap(await _call('ensureParentSignerEnrollment', {
2954
- 'parent': parent,
2955
- 'principal': principal,
2956
- if (publicKey != null) 'publicKey': publicKey,
2957
- if (authorSecret != null) 'authorSecret': authorSecret,
2958
- if (domain != null) 'domain': domain,
2959
- if (directiveId != null) 'directiveId': directiveId,
2960
- if (authToken != null) 'authToken': authToken,
2961
- if (idToken != null) 'idToken': idToken,
2962
- if (verifiedVia != null) 'verifiedVia': verifiedVia,
2963
- }));
3164
+ Future<Map<String, Object?>> ensureParentSignerEnrollment({
3165
+ required String parent,
3166
+ required String principal,
3167
+ String? publicKey,
3168
+ String? authorSecret,
3169
+ String? domain,
3170
+ String? directiveId,
3171
+ String? authToken,
3172
+ String? idToken,
3173
+ String? verifiedVia,
3174
+ }) async => _asMap(
3175
+ await _call('ensureParentSignerEnrollment', {
3176
+ 'parent': parent,
3177
+ 'principal': principal,
3178
+ if (publicKey != null) 'publicKey': publicKey,
3179
+ if (authorSecret != null) 'authorSecret': authorSecret,
3180
+ if (domain != null) 'domain': domain,
3181
+ if (directiveId != null) 'directiveId': directiveId,
3182
+ if (authToken != null) 'authToken': authToken,
3183
+ if (idToken != null) 'idToken': idToken,
3184
+ if (verifiedVia != null) 'verifiedVia': verifiedVia,
3185
+ }),
3186
+ );
2964
3187
 
2965
3188
  /// AUTHORING PREFLIGHT — evaluate the SAME premises the kernel's authorization gate judges,
2966
3189
  /// READ-ONLY, and return a typed verdict WITHOUT authoring — so the app catches a doomed write
@@ -2969,20 +3192,23 @@ class NomosBridge {
2969
3192
  /// `{ok, ws, directiveId, actor, keyHash, cause, causes, domain?, relation?, object?, error?, checkedAt}`
2970
3193
  /// where `cause` ∈ {directive-not-found, no-signing-key, missing-signer-enrollment, revoked-signer,
2971
3194
  /// missing-role, null}. Use [canAuthorTyped] on a [NomosSession] for the typed-error variant.
2972
- Future<Map<String, Object?>> canAuthor(String directiveId,
2973
- {String? ws,
2974
- String? principal,
2975
- String? publicKey,
2976
- String? keyHash,
2977
- String? authToken}) async =>
2978
- _asMap(await _call('canAuthor', {
2979
- 'directiveId': directiveId,
2980
- if (ws != null) 'ws': ws,
2981
- if (principal != null) 'principal': principal,
2982
- if (publicKey != null) 'publicKey': publicKey,
2983
- if (keyHash != null) 'keyHash': keyHash,
2984
- if (authToken != null) 'authToken': authToken,
2985
- }));
3195
+ Future<Map<String, Object?>> canAuthor(
3196
+ String directiveId, {
3197
+ String? ws,
3198
+ String? principal,
3199
+ String? publicKey,
3200
+ String? keyHash,
3201
+ String? authToken,
3202
+ }) async => _asMap(
3203
+ await _call('canAuthor', {
3204
+ 'directiveId': directiveId,
3205
+ if (ws != null) 'ws': ws,
3206
+ if (principal != null) 'principal': principal,
3207
+ if (publicKey != null) 'publicKey': publicKey,
3208
+ if (keyHash != null) 'keyHash': keyHash,
3209
+ if (authToken != null) 'authToken': authToken,
3210
+ }),
3211
+ );
2986
3212
 
2987
3213
  /// VERIFY_CHAIN — the kernel's from-genesis self-verification of THIS holon's folded ledger (read-only:
2988
3214
  /// contiguity → every intent re-admitted through the ONE gate against the state folded from the prefix). For
@@ -2995,16 +3221,16 @@ class NomosBridge {
2995
3221
  /// Framework-private receiving-group setup. Generated application hosts own this ceremony; tenant code
2996
3222
  /// receives only the resulting business application.
2997
3223
  Future<Map<String, Object?>> nomosInternalBirthReceivingGroup(
2998
- Map<String, Object?> spec) async =>
2999
- _asMap(await _call('nomosInternalBirthReceivingGroup', spec));
3224
+ Map<String, Object?> spec,
3225
+ ) async => _asMap(await _call('nomosInternalBirthReceivingGroup', spec));
3000
3226
 
3001
3227
  /// Sign a cross-custody SHARE attestation `{secret, asserterWorkspace, object, relation, subject,
3002
3228
  /// attestedAt}` → `{publicKey, signature}`. This is what drives `recordShare` into an invitee's home
3003
3229
  /// (the free global invitation system): the granting workspace signs that it shares `object#relation`
3004
3230
  /// with `subject`, and the invitee's home gate verifies the signature before recording it.
3005
3231
  Future<Map<String, Object?>> attestationSign(
3006
- Map<String, Object?> args) async =>
3007
- _asMap(await _call('attestationSign', {'args': args}));
3232
+ Map<String, Object?> args,
3233
+ ) async => _asMap(await _call('attestationSign', {'args': args}));
3008
3234
 
3009
3235
  /// Issue a ROLE-DELEGATION cert (the `.delegatedRole()` lane, kernel `delegationCertSign` op). A platform
3010
3236
  /// signs — with its grantor secret `signerSecret` (base64) — a cert granting `relation` to `subject`,
@@ -3021,33 +3247,38 @@ class NomosBridge {
3021
3247
  required String subject,
3022
3248
  int epoch = 1,
3023
3249
  List<Object?>? certChain,
3024
- }) async =>
3025
- _asMap(await _call('issueDelegationCert', {
3026
- 'args': {
3027
- 'signerSecret': signerSecret,
3028
- 'grantorId': grantorId,
3029
- 'relation': relation,
3030
- 'subject': subject,
3031
- 'epoch': epoch,
3032
- if (certChain != null) 'certChain': certChain,
3033
- },
3034
- }));
3250
+ }) async => _asMap(
3251
+ await _call('issueDelegationCert', {
3252
+ 'args': {
3253
+ 'signerSecret': signerSecret,
3254
+ 'grantorId': grantorId,
3255
+ 'relation': relation,
3256
+ 'subject': subject,
3257
+ 'epoch': epoch,
3258
+ if (certChain != null) 'certChain': certChain,
3259
+ },
3260
+ }),
3261
+ );
3035
3262
 
3036
3263
  /// A declared query's rows from the local projection. `keys` (E2E sealing) injects the reader's held
3037
3264
  /// scope keys so encrypted fields AEAD-open in-wasm; absent ⇒ they stay opaque envelopes (today's
3038
3265
  /// behavior). `principal` stamps the private-read gate (defaults to the connection's bound principal).
3039
3266
  /// Both are OMITTED from the wire when absent, so a public un-keyed read is byte-identical to before.
3040
- Future<List<Map<String, dynamic>>> query(String queryId,
3041
- [Map<String, Object?> params = const {},
3042
- List<NomosScopeKey>? keys,
3043
- String? principal,
3044
- String? session]) async {
3045
- return _asRows(await _call('query', {
3046
- 'queryId': queryId,
3047
- 'params': params,
3048
- ...?_readAuthArgs(keys, principal),
3049
- if (session != null) 'session': session,
3050
- }));
3267
+ Future<List<Map<String, dynamic>>> query(
3268
+ String queryId, [
3269
+ Map<String, Object?> params = const {},
3270
+ List<NomosScopeKey>? keys,
3271
+ String? principal,
3272
+ String? session,
3273
+ ]) async {
3274
+ return _asRows(
3275
+ await _call('query', {
3276
+ 'queryId': queryId,
3277
+ 'params': params,
3278
+ ...?_readAuthArgs(keys, principal),
3279
+ if (session != null) 'session': session,
3280
+ }),
3281
+ );
3051
3282
  }
3052
3283
 
3053
3284
  /// One bounded query page. Unlike [query], this never requires the kernel to materialize or serialize
@@ -3064,14 +3295,16 @@ class NomosBridge {
3064
3295
  if (pageSize < 1 || pageSize > 1000) {
3065
3296
  throw RangeError.range(pageSize, 1, 1000, 'pageSize');
3066
3297
  }
3067
- final result = _asMap(await _call('queryPage', {
3068
- 'queryId': queryId,
3069
- 'params': params,
3070
- 'pageSize': pageSize,
3071
- if (cursor != null) 'cursor': cursor,
3072
- ...?_readAuthArgs(keys, principal),
3073
- if (session != null) 'session': session,
3074
- }));
3298
+ final result = _asMap(
3299
+ await _call('queryPage', {
3300
+ 'queryId': queryId,
3301
+ 'params': params,
3302
+ 'pageSize': pageSize,
3303
+ if (cursor != null) 'cursor': cursor,
3304
+ ...?_readAuthArgs(keys, principal),
3305
+ if (session != null) 'session': session,
3306
+ }),
3307
+ );
3075
3308
  return NomosPage<Map<String, dynamic>>(
3076
3309
  rows: _asRows(result),
3077
3310
  cursor: result['cursor'] as String?,
@@ -3080,32 +3313,39 @@ class NomosBridge {
3080
3313
  }
3081
3314
 
3082
3315
  /// A single aggregate by id (the by-id read). `keys`/`principal`: see [query].
3083
- Future<List<Map<String, dynamic>>> queryById(String id,
3084
- [List<NomosScopeKey>? keys,
3085
- String? principal,
3086
- String? session]) async =>
3087
- _asRows(await _call('queryById', {
3088
- 'id': id,
3089
- ...?_readAuthArgs(keys, principal),
3090
- if (session != null) 'session': session,
3091
- }));
3316
+ Future<List<Map<String, dynamic>>> queryById(
3317
+ String id, [
3318
+ List<NomosScopeKey>? keys,
3319
+ String? principal,
3320
+ String? session,
3321
+ ]) async => _asRows(
3322
+ await _call('queryById', {
3323
+ 'id': id,
3324
+ ...?_readAuthArgs(keys, principal),
3325
+ if (session != null) 'session': session,
3326
+ }),
3327
+ );
3092
3328
 
3093
3329
  /// Compare an immutable publication pin with the stable series' one compiled
3094
3330
  /// current pointer. This is an O(current-state) check: it neither resolves the
3095
3331
  /// pinned publication nor reconstructs aggregate history.
3096
- Future<ReferenceVersionStatus<TVersion>>
3097
- checkReferenceVersion<TVersion, TSeries>(
3098
- VersionedNomosRef<TVersion, TSeries> reference,
3099
- ) async {
3100
- final result = _asMap(await _call('checkReferenceVersions', {
3101
- 'refs': [
3102
- {
3103
- 'pinned': NmsInternalNomosRefs.toJson(reference.pinned),
3104
- 'series': NmsInternalNomosRefs.toJson(reference.series),
3105
- 'currentField': NmsInternalVersionedNomosRefs.currentField(reference),
3106
- },
3107
- ],
3108
- }));
3332
+ Future<ReferenceVersionStatus<TVersion>> checkReferenceVersion<
3333
+ TVersion,
3334
+ TSeries
3335
+ >(VersionedNomosRef<TVersion, TSeries> reference) async {
3336
+ final result = _asMap(
3337
+ await _call('checkReferenceVersions', {
3338
+ 'refs': [
3339
+ {
3340
+ 'pinned': NmsInternalNomosRefs.toJson(reference.pinned),
3341
+ 'series': NmsInternalNomosRefs.toJson(reference.series),
3342
+ 'currentField': NmsInternalVersionedNomosRefs.currentField(
3343
+ reference,
3344
+ ),
3345
+ },
3346
+ ],
3347
+ }),
3348
+ );
3109
3349
  final unique = result['unique'] as List?;
3110
3350
  final first = unique == null || unique.isEmpty
3111
3351
  ? const <String, Object?>{}
@@ -3130,10 +3370,12 @@ class NomosBridge {
3130
3370
  /// as `client.past.moments()`; callers pass the opaque [NomosMoment] back to `past.at(moment)` and never
3131
3371
  /// need to know that the framework uses commit identities underneath.
3132
3372
  Future<List<NomosMoment>> moments({int limit = 100, String? session}) async {
3133
- final r = _asMap(await _call('commits', {
3134
- 'limit': limit,
3135
- if (session != null) 'session': session,
3136
- }));
3373
+ final r = _asMap(
3374
+ await _call('commits', {
3375
+ 'limit': limit,
3376
+ if (session != null) 'session': session,
3377
+ }),
3378
+ );
3137
3379
  return ((r['commits'] as List?) ?? const [])
3138
3380
  .map((e) => NomosMoment.fromJson((e as Map).cast<String, Object?>()))
3139
3381
  .toList();
@@ -3141,56 +3383,80 @@ class NomosBridge {
3141
3383
 
3142
3384
  /// Run one declared business query against the verified world at [moment], without moving the live world.
3143
3385
  /// `keys`/`principal` have exactly the same private/encrypted-read semantics as [query].
3144
- Future<List<Map<String, dynamic>>> queryAt(NomosMoment moment, String queryId,
3145
- [Map<String, Object?> params = const {},
3146
- List<NomosScopeKey>? keys,
3147
- String? principal,
3148
- String? session]) async =>
3149
- _asRows(await _call('queryAt', {
3150
- 'commit': moment.id,
3151
- 'queryId': queryId,
3152
- 'params': params,
3153
- ...?_readAuthArgs(keys, principal),
3154
- if (session != null) 'session': session,
3155
- }));
3386
+ Future<List<Map<String, dynamic>>> queryAt(
3387
+ NomosMoment moment,
3388
+ String queryId, [
3389
+ Map<String, Object?> params = const {},
3390
+ List<NomosScopeKey>? keys,
3391
+ String? principal,
3392
+ String? session,
3393
+ ]) async => _asRows(
3394
+ await _call('queryAt', {
3395
+ 'commit': moment.id,
3396
+ 'queryId': queryId,
3397
+ 'params': params,
3398
+ ...?_readAuthArgs(keys, principal),
3399
+ if (session != null) 'session': session,
3400
+ }),
3401
+ );
3156
3402
 
3157
3403
  /// Read one aggregate from the verified world at [moment]. `keys`/`principal`: see [queryAt].
3158
- Future<List<Map<String, dynamic>>> queryByIdAt(NomosMoment moment, String id,
3159
- [List<NomosScopeKey>? keys,
3160
- String? principal,
3161
- String? session]) async =>
3162
- _asRows(await _call('queryByIdAt', {
3163
- 'commit': moment.id,
3164
- 'id': id,
3165
- ...?_readAuthArgs(keys, principal),
3166
- if (session != null) 'session': session,
3167
- }));
3404
+ Future<List<Map<String, dynamic>>> queryByIdAt(
3405
+ NomosMoment moment,
3406
+ String id, [
3407
+ List<NomosScopeKey>? keys,
3408
+ String? principal,
3409
+ String? session,
3410
+ ]) async => _asRows(
3411
+ await _call('queryByIdAt', {
3412
+ 'commit': moment.id,
3413
+ 'id': id,
3414
+ ...?_readAuthArgs(keys, principal),
3415
+ if (session != null) 'session': session,
3416
+ }),
3417
+ );
3168
3418
 
3169
3419
  /// Maintained historic count/sum values at [moment]. These are the temporal counterparts of [count]/[sum].
3170
- Future<int> countAt(NomosMoment moment, String countId,
3171
- [String group = '', String? session]) async =>
3172
- ((_asMap(await _call('countAt', {
3173
- 'commit': moment.id,
3174
- 'countId': countId,
3175
- 'group': group,
3176
- if (session != null) 'session': session,
3177
- }))['count']) as num)
3420
+ Future<int> countAt(
3421
+ NomosMoment moment,
3422
+ String countId, [
3423
+ String group = '',
3424
+ String? session,
3425
+ ]) async =>
3426
+ ((_asMap(
3427
+ await _call('countAt', {
3428
+ 'commit': moment.id,
3429
+ 'countId': countId,
3430
+ 'group': group,
3431
+ if (session != null) 'session': session,
3432
+ }),
3433
+ )['count'])
3434
+ as num)
3178
3435
  .toInt();
3179
3436
 
3180
- Future<int> sumAt(NomosMoment moment, String sumId,
3181
- [String group = '', String? session]) async =>
3182
- ((_asMap(await _call('sumAt', {
3183
- 'commit': moment.id,
3184
- 'sumId': sumId,
3185
- 'group': group,
3186
- if (session != null) 'session': session,
3187
- }))['sum']) as num)
3437
+ Future<int> sumAt(
3438
+ NomosMoment moment,
3439
+ String sumId, [
3440
+ String group = '',
3441
+ String? session,
3442
+ ]) async =>
3443
+ ((_asMap(
3444
+ await _call('sumAt', {
3445
+ 'commit': moment.id,
3446
+ 'sumId': sumId,
3447
+ 'group': group,
3448
+ if (session != null) 'session': session,
3449
+ }),
3450
+ )['sum'])
3451
+ as num)
3188
3452
  .toInt();
3189
3453
 
3190
3454
  /// The read auth sidecar (private reads + E2E): `keys`/`principal` as top-level call args, or null when
3191
3455
  /// neither is present (so the wire is byte-identical to before this existed).
3192
3456
  Map<String, Object?>? _readAuthArgs(
3193
- List<NomosScopeKey>? keys, String? principal) {
3457
+ List<NomosScopeKey>? keys,
3458
+ String? principal,
3459
+ ) {
3194
3460
  final out = <String, Object?>{};
3195
3461
  if (principal != null && principal.isNotEmpty) out['principal'] = principal;
3196
3462
  if (keys != null && keys.isNotEmpty)
@@ -3207,33 +3473,41 @@ class NomosBridge {
3207
3473
 
3208
3474
  /// AUTHOR-SIDE SEAL: encrypt `plaintext` into its on-chain ciphertext envelope, bound to the scope + #58
3209
3475
  /// stable sids (rename-safe). Returns the envelope string the encrypted field value is written as.
3210
- Future<String> cryptoSealField(
3211
- {required String scopeKey,
3212
- required String scope,
3213
- required int epoch,
3214
- required String aggSid,
3215
- required String fieldSid,
3216
- required String plaintext}) async =>
3217
- (_asMap(await _call('cryptoSealField', {
3218
- 'scopeKey': scopeKey,
3219
- 'scope': scope,
3220
- 'epoch': epoch,
3221
- 'aggSid': aggSid,
3222
- 'fieldSid': fieldSid,
3223
- 'plaintext': plaintext,
3224
- }))['envelope'] ??
3476
+ Future<String> cryptoSealField({
3477
+ required String scopeKey,
3478
+ required String scope,
3479
+ required int epoch,
3480
+ required String aggSid,
3481
+ required String fieldSid,
3482
+ required String plaintext,
3483
+ }) async =>
3484
+ (_asMap(
3485
+ await _call('cryptoSealField', {
3486
+ 'scopeKey': scopeKey,
3487
+ 'scope': scope,
3488
+ 'epoch': epoch,
3489
+ 'aggSid': aggSid,
3490
+ 'fieldSid': fieldSid,
3491
+ 'plaintext': plaintext,
3492
+ }),
3493
+ )['envelope'] ??
3225
3494
  '')
3226
3495
  .toString();
3227
3496
 
3228
3497
  /// FRAMEWORK-INTERNAL `.owned()` key lane. The connected JS holon derives epoch 0 from its
3229
3498
  /// already-persisted device credential; generated clients call this without exposing key custody to apps.
3230
- Future<NomosScopeKey> nomosOwnedScopeKey(String scope,
3231
- {String? recordType, String? recordId}) async {
3232
- final out = _asMap(await _call('cryptoOwnedScopeKey', {
3233
- 'scope': scope,
3234
- if (recordType != null && recordId != null)
3235
- 'record': {'type': recordType, 'id': recordId},
3236
- }));
3499
+ Future<NomosScopeKey> nomosOwnedScopeKey(
3500
+ String scope, {
3501
+ String? recordType,
3502
+ String? recordId,
3503
+ }) async {
3504
+ final out = _asMap(
3505
+ await _call('cryptoOwnedScopeKey', {
3506
+ 'scope': scope,
3507
+ if (recordType != null && recordId != null)
3508
+ 'record': {'type': recordType, 'id': recordId},
3509
+ }),
3510
+ );
3237
3511
  return NomosScopeKey(
3238
3512
  scope: (out['scope'] ?? scope).toString(),
3239
3513
  epoch: (out['epoch'] as num?)?.toInt() ?? 0,
@@ -3245,30 +3519,38 @@ class NomosBridge {
3245
3519
 
3246
3520
  /// Every key epoch this principal currently holds for one owned record. Generated reads use the
3247
3521
  /// complete set so history remains readable after rotations, shares and transfers.
3248
- Future<List<NomosScopeKey>> nomosOwnedScopeKeys(String scope,
3249
- {required String recordType, required String recordId}) async {
3250
- final out = _asMap(await _call('cryptoOwnedScopeKeys', {
3251
- 'scope': scope,
3252
- 'record': {'type': recordType, 'id': recordId},
3253
- }));
3522
+ Future<List<NomosScopeKey>> nomosOwnedScopeKeys(
3523
+ String scope, {
3524
+ required String recordType,
3525
+ required String recordId,
3526
+ }) async {
3527
+ final out = _asMap(
3528
+ await _call('cryptoOwnedScopeKeys', {
3529
+ 'scope': scope,
3530
+ 'record': {'type': recordType, 'id': recordId},
3531
+ }),
3532
+ );
3254
3533
  return (out['keys'] as List? ?? const [])
3255
3534
  .map((raw) => _asMap(raw))
3256
- .map((key) => NomosScopeKey(
3257
- scope: (key['scope'] ?? scope).toString(),
3258
- epoch: (key['epoch'] as num?)?.toInt() ?? 0,
3259
- key: (key['key'] ?? '').toString(),
3260
- ownedKeySubject: key['ownedKeySubject']?.toString(),
3261
- ownedCustody: key['ownedCustody']?.toString(),
3262
- ))
3535
+ .map(
3536
+ (key) => NomosScopeKey(
3537
+ scope: (key['scope'] ?? scope).toString(),
3538
+ epoch: (key['epoch'] as num?)?.toInt() ?? 0,
3539
+ key: (key['key'] ?? '').toString(),
3540
+ ownedKeySubject: key['ownedKeySubject']?.toString(),
3541
+ ownedCustody: key['ownedCustody']?.toString(),
3542
+ ),
3543
+ )
3263
3544
  .toList();
3264
3545
  }
3265
3546
 
3266
3547
  /// Make this signed-in principal ready to receive record shares. Generated clients call this while
3267
3548
  /// binding; tenant code never handles a key, certificate or identity tuple.
3268
- Future<void> nomosEnsureOwnedIdentity(
3269
- {required String scope,
3270
- required String domain,
3271
- String? domainHash}) async {
3549
+ Future<void> nomosEnsureOwnedIdentity({
3550
+ required String scope,
3551
+ required String domain,
3552
+ String? domainHash,
3553
+ }) async {
3272
3554
  await _call('cryptoOwnedEnsureIdentity', {
3273
3555
  'scope': scope,
3274
3556
  'domain': domain,
@@ -3282,39 +3564,49 @@ class NomosBridge {
3282
3564
  required String domain,
3283
3565
  required String domainHash,
3284
3566
  required String recoveryPrincipal,
3285
- }) async =>
3286
- _asMap(await _call('cryptoOwnedProtectIdentity', {
3287
- 'scope': scope,
3288
- 'domain': domain,
3289
- 'domainHash': domainHash,
3290
- 'recoveryPrincipal': recoveryPrincipal,
3291
- }));
3567
+ }) async => _asMap(
3568
+ await _call('cryptoOwnedProtectIdentity', {
3569
+ 'scope': scope,
3570
+ 'domain': domain,
3571
+ 'domainHash': domainHash,
3572
+ 'recoveryPrincipal': recoveryPrincipal,
3573
+ }),
3574
+ );
3292
3575
 
3293
3576
  /// Prepare the private framework payload behind generated share/unshare/transfer methods.
3294
- Future<Map<String, Object?>> nomosPrepareOwnedRecordAction<TAggregate>(
3295
- {required String action,
3296
- required String scope,
3297
- required NomosRef<TAggregate> record,
3298
- required String peer}) async =>
3299
- _asMap(await _call('cryptoOwnedPrepareRecordAction', {
3300
- 'action': action,
3301
- 'scope': scope,
3302
- 'record': {
3303
- 'type': NmsInternalNomosRefs.type(record),
3304
- 'id': NmsInternalNomosRefs.id(record),
3305
- },
3306
- 'peer': peer,
3307
- }));
3577
+ Future<Map<String, Object?>> nomosPrepareOwnedRecordAction<TAggregate>({
3578
+ required String action,
3579
+ required String scope,
3580
+ required NomosRef<TAggregate> record,
3581
+ required String peer,
3582
+ }) async => _asMap(
3583
+ await _call('cryptoOwnedPrepareRecordAction', {
3584
+ 'action': action,
3585
+ 'scope': scope,
3586
+ 'record': {
3587
+ 'type': NmsInternalNomosRefs.type(record),
3588
+ 'id': NmsInternalNomosRefs.id(record),
3589
+ },
3590
+ 'peer': peer,
3591
+ }),
3592
+ );
3308
3593
 
3309
3594
  /// FRAMEWORK-INTERNAL business birth: derive the unborn child's owned key from its deterministic address.
3310
- Future<NomosScopeKey> nomosOwnedBirthScopeKey(String scope, String namespace,
3311
- Object? key, String recordType, String recordId) async {
3312
- final out = _asMap(await _call('cryptoOwnedBirthScopeKey', {
3313
- 'scope': scope,
3314
- 'namespace': namespace,
3315
- 'key': key,
3316
- 'record': {'type': recordType, 'id': recordId},
3317
- }));
3595
+ Future<NomosScopeKey> nomosOwnedBirthScopeKey(
3596
+ String scope,
3597
+ String namespace,
3598
+ Object? key,
3599
+ String recordType,
3600
+ String recordId,
3601
+ ) async {
3602
+ final out = _asMap(
3603
+ await _call('cryptoOwnedBirthScopeKey', {
3604
+ 'scope': scope,
3605
+ 'namespace': namespace,
3606
+ 'key': key,
3607
+ 'record': {'type': recordType, 'id': recordId},
3608
+ }),
3609
+ );
3318
3610
  return NomosScopeKey(
3319
3611
  scope: (out['scope'] ?? scope).toString(),
3320
3612
  epoch: (out['epoch'] as num?)?.toInt() ?? 0,
@@ -3327,64 +3619,88 @@ class NomosBridge {
3327
3619
 
3328
3620
  /// FRAMEWORK-INTERNAL: issue an opaque request for this session's current device.
3329
3621
  Future<String> nomosOwnedDeviceRequest(String scope) async =>
3330
- (_asMap(await _call('cryptoOwnedDeviceRequest', {'scope': scope}))[
3331
- 'request'] ??
3622
+ (_asMap(
3623
+ await _call('cryptoOwnedDeviceRequest', {'scope': scope}),
3624
+ )['request'] ??
3332
3625
  '')
3333
3626
  .toString();
3334
3627
 
3335
3628
  /// FRAMEWORK-INTERNAL: validate an opaque request and prepare the atomic signer + custody offer.
3336
3629
  Future<Map<String, Object?>> nomosPrepareOwnedDeviceApproval(
3337
- String request, String scope) async =>
3338
- _asMap(await _call('cryptoOwnedPrepareApproval', {
3339
- 'request': request,
3340
- 'scope': scope,
3341
- }));
3630
+ String request,
3631
+ String scope,
3632
+ ) async => _asMap(
3633
+ await _call('cryptoOwnedPrepareApproval', {
3634
+ 'request': request,
3635
+ 'scope': scope,
3636
+ }),
3637
+ );
3342
3638
 
3343
3639
  /// Wrap a scope key TO a recipient's public key (committer side): `{hpkeEpk, ct}` base64 ciphertext.
3344
- Future<Map<String, Object?>> cryptoWrapKey(
3345
- {required String recipientPub, required String scopeKey}) async =>
3346
- _asMap(await _call('cryptoWrapKey',
3347
- {'recipientPub': recipientPub, 'scopeKey': scopeKey}));
3640
+ Future<Map<String, Object?>> cryptoWrapKey({
3641
+ required String recipientPub,
3642
+ required String scopeKey,
3643
+ }) async => _asMap(
3644
+ await _call('cryptoWrapKey', {
3645
+ 'recipientPub': recipientPub,
3646
+ 'scopeKey': scopeKey,
3647
+ }),
3648
+ );
3348
3649
 
3349
3650
  /// Unwrap an on-chain WrappedKey with this device's secret → the base64 scope key (reader side).
3350
- Future<String> cryptoUnwrapKey(
3351
- {required String secret,
3352
- required String hpkeEpk,
3353
- required String ct}) async =>
3354
- (_asMap(await _call('cryptoUnwrapKey', {
3355
- 'secret': secret,
3356
- 'hpkeEpk': hpkeEpk,
3357
- 'ct': ct
3358
- }))['scopeKey'] ??
3651
+ Future<String> cryptoUnwrapKey({
3652
+ required String secret,
3653
+ required String hpkeEpk,
3654
+ required String ct,
3655
+ }) async =>
3656
+ (_asMap(
3657
+ await _call('cryptoUnwrapKey', {
3658
+ 'secret': secret,
3659
+ 'hpkeEpk': hpkeEpk,
3660
+ 'ct': ct,
3661
+ }),
3662
+ )['scopeKey'] ??
3359
3663
  '')
3360
3664
  .toString();
3361
3665
 
3362
3666
  /// THE SPATIAL READ: rows of the declared spatial index's of-type whose geometry
3363
3667
  /// intersects [bbox] — an R*Tree probe on the local projection, same row shape as [query].
3364
3668
  Future<List<Map<String, dynamic>>> spatialWithin(
3365
- String spatialId, NomosBbox bbox,
3366
- [String? session]) async =>
3367
- _asRows(await _call('spatialWithin', {
3368
- 'spatialId': spatialId,
3369
- 'bbox': bbox.toJson(),
3370
- if (session != null) 'session': session,
3371
- }));
3669
+ String spatialId,
3670
+ NomosBbox bbox, [
3671
+ String? session,
3672
+ ]) async => _asRows(
3673
+ await _call('spatialWithin', {
3674
+ 'spatialId': spatialId,
3675
+ 'bbox': bbox.toJson(),
3676
+ if (session != null) 'session': session,
3677
+ }),
3678
+ );
3372
3679
 
3373
3680
  /// A maintained O(1) count / sum.
3374
- Future<int> count(String countId,
3375
- [String group = '', String? session]) async =>
3376
- ((_asMap(await _call('count', {
3377
- 'countId': countId,
3378
- 'group': group,
3379
- if (session != null) 'session': session,
3380
- }))['count']) as num)
3681
+ Future<int> count(
3682
+ String countId, [
3683
+ String group = '',
3684
+ String? session,
3685
+ ]) async =>
3686
+ ((_asMap(
3687
+ await _call('count', {
3688
+ 'countId': countId,
3689
+ 'group': group,
3690
+ if (session != null) 'session': session,
3691
+ }),
3692
+ )['count'])
3693
+ as num)
3381
3694
  .toInt();
3382
3695
  Future<int> sum(String sumId, [String group = '', String? session]) async =>
3383
- ((_asMap(await _call('sum', {
3384
- 'sumId': sumId,
3385
- 'group': group,
3386
- if (session != null) 'session': session,
3387
- }))['sum']) as num)
3696
+ ((_asMap(
3697
+ await _call('sum', {
3698
+ 'sumId': sumId,
3699
+ 'group': group,
3700
+ if (session != null) 'session': session,
3701
+ }),
3702
+ )['sum'])
3703
+ as num)
3388
3704
  .toInt();
3389
3705
 
3390
3706
  /// THE PENDING-CONFLICTS READ (Surfaced merge conflicts — the DX layer): the MAINTAINED,
@@ -3394,35 +3710,43 @@ class NomosBridge {
3394
3710
  /// DERIVED, NOT DRAINED: a resolution write folding in removes its row by derivation.
3395
3711
  /// Empty for a Surfaced-free workspace. (The generated typed client wraps these rows as
3396
3712
  /// `PendingConflictRow` and pairs them with the designated `resolve…` helpers.)
3397
- Future<List<Map<String, dynamic>>> pendingConflicts(
3398
- [String? session]) async =>
3399
- _asRows(await _call('pendingConflicts', {
3400
- if (session != null) 'session': session,
3401
- }));
3713
+ Future<List<Map<String, dynamic>>> pendingConflicts([
3714
+ String? session,
3715
+ ]) async => _asRows(
3716
+ await _call('pendingConflicts', {if (session != null) 'session': session}),
3717
+ );
3402
3718
 
3403
3719
  /// Framework-filtered pending conflicts for one generated aggregate type. This is the
3404
3720
  /// primitive generated clients use; application code never filters the heterogeneous queue.
3405
- Future<List<Map<String, dynamic>>> pendingConflictsFor(String aggregateType,
3406
- [String? session]) async =>
3407
- _asRows(await _call('pendingConflicts', {
3408
- 'aggregateType': aggregateType,
3409
- if (session != null) 'session': session,
3410
- }));
3721
+ Future<List<Map<String, dynamic>>> pendingConflictsFor(
3722
+ String aggregateType, [
3723
+ String? session,
3724
+ ]) async => _asRows(
3725
+ await _call('pendingConflicts', {
3726
+ 'aggregateType': aggregateType,
3727
+ if (session != null) 'session': session,
3728
+ }),
3729
+ );
3411
3730
 
3412
3731
  /// Counts-only workspace summary over the maintained conflict index.
3413
- Future<Map<String, Object?>> pendingConflictSummary(
3414
- [String? session]) async =>
3415
- _asMap(await _call('pendingConflictSummary', {
3416
- if (session != null) 'session': session,
3417
- }));
3732
+ Future<Map<String, Object?>> pendingConflictSummary([
3733
+ String? session,
3734
+ ]) async => _asMap(
3735
+ await _call('pendingConflictSummary', {
3736
+ if (session != null) 'session': session,
3737
+ }),
3738
+ );
3418
3739
 
3419
3740
  /// Push local writes → the session branch → cloud admission, and pull canonical main back.
3420
- Future<Map<String, Object?>> sync(
3421
- {bool admit = true, String? session}) async =>
3422
- _asMap(await _call('sync', {
3423
- 'admit': admit,
3424
- if (session != null) 'session': session,
3425
- }));
3741
+ Future<Map<String, Object?>> sync({
3742
+ bool admit = true,
3743
+ String? session,
3744
+ }) async => _asMap(
3745
+ await _call('sync', {
3746
+ 'admit': admit,
3747
+ if (session != null) 'session': session,
3748
+ }),
3749
+ );
3426
3750
 
3427
3751
  /// PERSIST: a base64 snapshot of the whole local holon (the bare nomos.git incl. un-synced writes + the
3428
3752
  /// DLQ). Store it on disk and pass it back as `connect(restoreFrom:)` next launch — the app then boots from
@@ -3439,12 +3763,15 @@ class NomosBridge {
3439
3763
  /// local write flushes the up leg at once; `fallbackMs` is only a just-in-case safety pull (default 60s).
3440
3764
  /// Each round arrives on [syncEvents] as `{ event:'sync', reason, result }`; socket state as `{event:'link'}`.
3441
3765
  /// Call once after [connect] — then the app never polls.
3442
- Future<void> autoSync(
3443
- {int? debounceMs, int? fallbackMs, String? signalId}) async {
3766
+ Future<void> autoSync({
3767
+ int? debounceMs,
3768
+ int? fallbackMs,
3769
+ String? signalId,
3770
+ }) async {
3444
3771
  await _call('autoSync', {
3445
3772
  if (debounceMs != null) 'debounceMs': debounceMs,
3446
3773
  if (fallbackMs != null) 'fallbackMs': fallbackMs,
3447
- if (signalId != null) 'signalId': signalId
3774
+ if (signalId != null) 'signalId': signalId,
3448
3775
  });
3449
3776
  final workspace = _sessionName;
3450
3777
  if (workspace != null) _eventPlane._autoSyncSessions.add(workspace);
@@ -3482,16 +3809,16 @@ class NomosBridge {
3482
3809
  : _eventPlane._currentSessionStatus(session);
3483
3810
  }
3484
3811
 
3485
- Stream<SyncStatus> _sessionSyncStatus(String session) =>
3486
- _eventPlane._syncStatusBySession
3487
- .putIfAbsent(
3488
- session,
3489
- () => StreamController<SyncStatus>.broadcast(
3490
- onListen: () => _eventPlane._startSessionStatusPoll(session),
3491
- onCancel: _eventPlane._stopStatusPollIfUnused,
3492
- ),
3493
- )
3494
- .stream;
3812
+ Stream<SyncStatus> _sessionSyncStatus(String session) => _eventPlane
3813
+ ._syncStatusBySession
3814
+ .putIfAbsent(
3815
+ session,
3816
+ () => StreamController<SyncStatus>.broadcast(
3817
+ onListen: () => _eventPlane._startSessionStatusPoll(session),
3818
+ onCancel: _eventPlane._stopStatusPollIfUnused,
3819
+ ),
3820
+ )
3821
+ .stream;
3495
3822
 
3496
3823
  SyncStatus _currentSessionStatus(String session) =>
3497
3824
  _eventPlane._statusBySession[session] ?? const SyncStatus();
@@ -3534,14 +3861,17 @@ class NomosBridge {
3534
3861
  phase = SyncPhase.error;
3535
3862
  } else if (statuses.any((status) => status.phase == SyncPhase.offline)) {
3536
3863
  phase = SyncPhase.offline;
3537
- } else if (statuses
3538
- .any((status) => status.phase == SyncPhase.transferring)) {
3864
+ } else if (statuses.any(
3865
+ (status) => status.phase == SyncPhase.transferring,
3866
+ )) {
3539
3867
  phase = SyncPhase.transferring;
3540
3868
  } else if (statuses.any(
3541
- (status) => status.phase == SyncPhase.behind || status.behind > 0)) {
3869
+ (status) => status.phase == SyncPhase.behind || status.behind > 0,
3870
+ )) {
3542
3871
  phase = SyncPhase.behind;
3543
3872
  } else if (statuses.any(
3544
- (status) => status.phase == SyncPhase.syncing || status.ahead > 0)) {
3873
+ (status) => status.phase == SyncPhase.syncing || status.ahead > 0,
3874
+ )) {
3545
3875
  phase = SyncPhase.syncing;
3546
3876
  } else {
3547
3877
  phase = SyncPhase.idle;
@@ -3553,9 +3883,10 @@ class NomosBridge {
3553
3883
  .map((status) => status.lastSyncedAt)
3554
3884
  .whereType<DateTime>()
3555
3885
  .fold<DateTime?>(
3556
- null,
3557
- (oldest, value) =>
3558
- oldest == null || value.isBefore(oldest) ? value : oldest);
3886
+ null,
3887
+ (oldest, value) =>
3888
+ oldest == null || value.isBefore(oldest) ? value : oldest,
3889
+ );
3559
3890
  final error = statuses
3560
3891
  .map((status) => status.lastError)
3561
3892
  .whereType<String>()
@@ -3565,17 +3896,19 @@ class NomosBridge {
3565
3896
  .whereType<TransferProgress>()
3566
3897
  .firstOrNull;
3567
3898
 
3568
- _pushStatus(SyncStatus(
3569
- phase: phase,
3570
- ahead: total((status) => status.ahead),
3571
- behind: total((status) => status.behind),
3572
- saved: total((status) => status.saved),
3573
- onMain: total((status) => status.onMain),
3574
- deadLetters: total((status) => status.deadLetters),
3575
- transfer: transfer,
3576
- lastSyncedAt: lastSynced,
3577
- lastError: error,
3578
- ));
3899
+ _pushStatus(
3900
+ SyncStatus(
3901
+ phase: phase,
3902
+ ahead: total((status) => status.ahead),
3903
+ behind: total((status) => status.behind),
3904
+ saved: total((status) => status.saved),
3905
+ onMain: total((status) => status.onMain),
3906
+ deadLetters: total((status) => status.deadLetters),
3907
+ transfer: transfer,
3908
+ lastSyncedAt: lastSynced,
3909
+ lastError: error,
3910
+ ),
3911
+ );
3579
3912
  }
3580
3913
 
3581
3914
  void _startStatusPoll() {
@@ -3609,8 +3942,9 @@ class NomosBridge {
3609
3942
 
3610
3943
  void _stopStatusPollIfUnused() {
3611
3944
  if (_syncStatusCtrl.hasListener ||
3612
- _syncStatusBySession.values
3613
- .any((controller) => controller.hasListener)) {
3945
+ _syncStatusBySession.values.any(
3946
+ (controller) => controller.hasListener,
3947
+ )) {
3614
3948
  return;
3615
3949
  }
3616
3950
  _stopStatusPoll();
@@ -3682,13 +4016,14 @@ class NomosBridge {
3682
4016
  // Counters prove custody debt, but they do not prove that a failed or
3683
4017
  // offline transport recovered. Only a subsequent successful sync/link
3684
4018
  // event may clear those lifecycle states.
3685
- final phase = current.transfer != null ||
4019
+ final phase =
4020
+ current.transfer != null ||
3686
4021
  current.phase == SyncPhase.error ||
3687
4022
  current.phase == SyncPhase.offline
3688
4023
  ? current.phase
3689
4024
  : (behind > 0
3690
- ? SyncPhase.behind
3691
- : (ahead > 0 ? SyncPhase.syncing : SyncPhase.idle));
4025
+ ? SyncPhase.behind
4026
+ : (ahead > 0 ? SyncPhase.syncing : SyncPhase.idle));
3692
4027
  _pushSessionStatus(
3693
4028
  session,
3694
4029
  current.copyWith(
@@ -3705,7 +4040,8 @@ class NomosBridge {
3705
4040
  _foldApplicationStatusFromSessions();
3706
4041
  } catch (e) {
3707
4042
  _pushStatus(
3708
- _status.copyWith(phase: SyncPhase.error, lastError: e.toString()));
4043
+ _status.copyWith(phase: SyncPhase.error, lastError: e.toString()),
4044
+ );
3709
4045
  }
3710
4046
  }
3711
4047
 
@@ -3758,7 +4094,7 @@ class NomosBridge {
3758
4094
  'kind': kind,
3759
4095
  't0': t0,
3760
4096
  if (head != null && head.isNotEmpty) 'head': head,
3761
- }
4097
+ },
3762
4098
  },
3763
4099
  {
3764
4100
  'event': 'span',
@@ -3768,7 +4104,7 @@ class NomosBridge {
3768
4104
  'id': sid,
3769
4105
  't1': t1,
3770
4106
  if (head != null && head.isNotEmpty) 'head': head,
3771
- }
4107
+ },
3772
4108
  },
3773
4109
  ]) {
3774
4110
  plane._spanBuffer.add(m);
@@ -3781,9 +4117,7 @@ class NomosBridge {
3781
4117
 
3782
4118
  /// Ledger size (commits) + branch-vs-main (ahead) + projection bytes — the dashboard's slow state.
3783
4119
  Future<Map<String, Object?>> metrics([String? session]) async =>
3784
- _asMap(await _call('metrics', {
3785
- if (session != null) 'session': session,
3786
- }));
4120
+ _asMap(await _call('metrics', {if (session != null) 'session': session}));
3787
4121
 
3788
4122
  /// TIER-2 SYNC SIZE — how many bytes this workspace would transfer to catch up to main, WITHOUT downloading
3789
4123
  /// them (a HEAD on the serving pack; the host answers Content-Length only, cached ~15s). Sum across your open
@@ -3799,28 +4133,42 @@ class NomosBridge {
3799
4133
 
3800
4134
  /// Reactive local read: a stream of row-sets that re-emits on every local change. Cancelling the
3801
4135
  /// subscription tells the runner to drop the watch.
3802
- Stream<List<dynamic>> watch(String queryId,
3803
- [Map<String, Object?> params = const {},
3804
- String? session,
3805
- String? principal,
3806
- int? intervalMs]) =>
3807
- _watchStream(
3808
- kind: 'query',
3809
- queryId: queryId,
3810
- params: params,
3811
- session: session,
3812
- principal: principal,
3813
- intervalMs: intervalMs);
3814
- Stream<List<dynamic>> watchById(String id,
3815
- [String? session, String? principal]) =>
3816
- _watchStream(
3817
- kind: 'byId', id: id, session: session, principal: principal);
4136
+ Stream<List<dynamic>> watch(
4137
+ String queryId, [
4138
+ Map<String, Object?> params = const {},
4139
+ String? session,
4140
+ String? principal,
4141
+ int? intervalMs,
4142
+ ]) => _watchStream(
4143
+ kind: 'query',
4144
+ queryId: queryId,
4145
+ params: params,
4146
+ session: session,
4147
+ principal: principal,
4148
+ intervalMs: intervalMs,
4149
+ );
4150
+ Stream<List<dynamic>> watchById(
4151
+ String id, [
4152
+ String? session,
4153
+ String? principal,
4154
+ ]) => _watchStream(
4155
+ kind: 'byId',
4156
+ id: id,
4157
+ session: session,
4158
+ principal: principal,
4159
+ );
3818
4160
 
3819
4161
  /// Reactive SPATIAL read: re-emits the R*Tree probe's rows whenever they change locally.
3820
- Stream<List<dynamic>> watchSpatial(String spatialId, NomosBbox bbox,
3821
- [String? session]) =>
3822
- _watchStream(
3823
- kind: 'spatial', spatialId: spatialId, bbox: bbox, session: session);
4162
+ Stream<List<dynamic>> watchSpatial(
4163
+ String spatialId,
4164
+ NomosBbox bbox, [
4165
+ String? session,
4166
+ ]) => _watchStream(
4167
+ kind: 'spatial',
4168
+ spatialId: spatialId,
4169
+ bbox: bbox,
4170
+ session: session,
4171
+ );
3824
4172
 
3825
4173
  /// Reactive PENDING-CONFLICTS read: re-emits the maintained Surfaced "decisions needed"
3826
4174
  /// rows whenever the index changes (derived, not drained — a resolution write clears its row).
@@ -3831,12 +4179,14 @@ class NomosBridge {
3831
4179
  /// (its baked `…InterfaceJson`, decoded) against the workspace's ON-LEDGER offer (auto-sourced).
3832
4180
  /// `{ ok, missing[], incompatible[] }` — names the exact directive/aggregate/field, not a hash.
3833
4181
  Future<Map<String, Object?>> checkCompatibility(
3834
- Map<String, Object?> requirement,
3835
- [Map<String, Object?>? offer]) async =>
3836
- _asMap(await _call('checkCompatibility', {
3837
- 'requirement': requirement,
3838
- if (offer != null) 'offer': offer,
3839
- }));
4182
+ Map<String, Object?> requirement, [
4183
+ Map<String, Object?>? offer,
4184
+ ]) async => _asMap(
4185
+ await _call('checkCompatibility', {
4186
+ 'requirement': requirement,
4187
+ if (offer != null) 'offer': offer,
4188
+ }),
4189
+ );
3840
4190
 
3841
4191
  /// THE COMPAT SUPERPOWER (#72b): compute the LIVE per-member verdict for this client's baked
3842
4192
  /// [compat] manifest + structural [requirement] against the workspace's on-ledger offer. The
@@ -3844,14 +4194,19 @@ class NomosBridge {
3844
4194
  /// CLIENT-SIDE at write time ([LawMismatchError] surfaced through `offerDirective`) — the JS
3845
4195
  /// runtime holds the verdict + the pre-call guard, and a Dart offer through this bridge is guarded
3846
4196
  /// too. Returns the verdict (also cached on [compat]) with per-member `{ ok, reason? }`.
3847
- Future<NomosCompat?> checkCompat(Map<String, Object?> compat,
3848
- [Map<String, Object?>? requirement]) async {
3849
- final r = _asMap(await _call('checkCompat', {
3850
- 'compat': compat,
3851
- if (requirement != null) 'requirement': requirement,
3852
- }));
3853
- final v =
3854
- NomosCompat.fromJson((r['compat'] as Map?)?.cast<String, Object?>());
4197
+ Future<NomosCompat?> checkCompat(
4198
+ Map<String, Object?> compat, [
4199
+ Map<String, Object?>? requirement,
4200
+ ]) async {
4201
+ final r = _asMap(
4202
+ await _call('checkCompat', {
4203
+ 'compat': compat,
4204
+ if (requirement != null) 'requirement': requirement,
4205
+ }),
4206
+ );
4207
+ final v = NomosCompat.fromJson(
4208
+ (r['compat'] as Map?)?.cast<String, Object?>(),
4209
+ );
3855
4210
  if (v != null) {
3856
4211
  _eventPlane._lastCompat = v;
3857
4212
  // a session's verdict is per-workspace state on the PLANE (so `session.compat` reads it back).
@@ -3865,26 +4220,28 @@ class NomosBridge {
3865
4220
  /// the opened session in one call — `final home = await bridge.open(workspace: 'home-…');` then
3866
4221
  /// `HomeClient(home)`. The bare [NomosBridge] methods remain the ONE-workspace sugar over an inferred
3867
4222
  /// default session; an app that opens a second workspace should hold handles, not call the plane.
3868
- Future<NomosSession> open(
3869
- {String? cloud,
3870
- required String workspace,
3871
- String? clientId,
3872
- String? authToken,
3873
- String? authorSecret,
3874
- String? restoreFrom,
3875
- bool allowUnborn = false,
3876
- bool recover = false,
3877
- String? session}) async {
4223
+ Future<NomosSession> open({
4224
+ String? cloud,
4225
+ required String workspace,
4226
+ String? clientId,
4227
+ String? authToken,
4228
+ String? authorSecret,
4229
+ String? restoreFrom,
4230
+ bool allowUnborn = false,
4231
+ bool recover = false,
4232
+ String? session,
4233
+ }) async {
3878
4234
  await connect(
3879
- cloud: cloud,
3880
- workspace: workspace,
3881
- clientId: clientId,
3882
- authToken: authToken,
3883
- authorSecret: authorSecret,
3884
- restoreFrom: restoreFrom,
3885
- allowUnborn: allowUnborn,
3886
- recover: recover,
3887
- session: session);
4235
+ cloud: cloud,
4236
+ workspace: workspace,
4237
+ clientId: clientId,
4238
+ authToken: authToken,
4239
+ authorSecret: authorSecret,
4240
+ restoreFrom: restoreFrom,
4241
+ allowUnborn: allowUnborn,
4242
+ recover: recover,
4243
+ session: session,
4244
+ );
3888
4245
  return this.session(session ?? workspace);
3889
4246
  }
3890
4247
 
@@ -3924,24 +4281,30 @@ class NomosBridge {
3924
4281
  /// workspace's installed law changes (another peer installs/upgrades/retires a domain and it
3925
4282
  /// syncs in). The verdict carries `offerKnown`. The app degrades/prompts on `ok:false`.
3926
4283
  Stream<Map<String, Object?>> watchCompatibility(
3927
- Map<String, Object?> requirement,
3928
- [String? session]) {
4284
+ Map<String, Object?> requirement, [
4285
+ String? session,
4286
+ ]) {
3929
4287
  // Controllers live on the EVENT PLANE (where _onMessage dispatches); the registration `_call` stays
3930
4288
  // THIS instance's (a session's override stamps `args.session`) — see [_eventPlane].
3931
4289
  final subId = 'c${++_eventPlane._seq}';
3932
4290
  late final StreamController<Map<String, Object?>> ctrl;
3933
4291
  ctrl = StreamController<Map<String, Object?>>(
3934
4292
  onListen: () {
3935
- unawaited(_call('watch', {
3936
- 'subId': subId,
3937
- 'kind': 'compat',
3938
- 'requirement': requirement,
3939
- if (session != null) 'session': session,
3940
- }).then<void>((_) {}, onError: (Object error, StackTrace stackTrace) {
3941
- if (!ctrl.isClosed && ctrl.hasListener) {
3942
- ctrl.addError(error, stackTrace);
3943
- }
3944
- }));
4293
+ unawaited(
4294
+ _call('watch', {
4295
+ 'subId': subId,
4296
+ 'kind': 'compat',
4297
+ 'requirement': requirement,
4298
+ if (session != null) 'session': session,
4299
+ }).then<void>(
4300
+ (_) {},
4301
+ onError: (Object error, StackTrace stackTrace) {
4302
+ if (!ctrl.isClosed && ctrl.hasListener) {
4303
+ ctrl.addError(error, stackTrace);
4304
+ }
4305
+ },
4306
+ ),
4307
+ );
3945
4308
  },
3946
4309
  onCancel: () {
3947
4310
  _eventPlane._compatWatches.remove(subId);
@@ -3955,42 +4318,48 @@ class NomosBridge {
3955
4318
  return ctrl.stream;
3956
4319
  }
3957
4320
 
3958
- Stream<List<dynamic>> _watchStream(
3959
- {required String kind,
3960
- String? queryId,
3961
- Map<String, Object?>? params,
3962
- String? id,
3963
- String? spatialId,
3964
- NomosBbox? bbox,
3965
- String? base,
3966
- int? limit,
3967
- String? session,
3968
- String? principal,
3969
- int? intervalMs}) {
4321
+ Stream<List<dynamic>> _watchStream({
4322
+ required String kind,
4323
+ String? queryId,
4324
+ Map<String, Object?>? params,
4325
+ String? id,
4326
+ String? spatialId,
4327
+ NomosBbox? bbox,
4328
+ String? base,
4329
+ int? limit,
4330
+ String? session,
4331
+ String? principal,
4332
+ int? intervalMs,
4333
+ }) {
3970
4334
  // Controllers live on the EVENT PLANE (where _onMessage dispatches); the registration `_call` stays
3971
4335
  // THIS instance's (a session's override stamps `args.session`) — see [_eventPlane].
3972
4336
  final subId = 'w${++_eventPlane._seq}';
3973
4337
  late final StreamController<List<dynamic>> ctrl;
3974
4338
  ctrl = StreamController<List<dynamic>>(
3975
4339
  onListen: () {
3976
- unawaited(_call('watch', {
3977
- 'subId': subId,
3978
- 'kind': kind,
3979
- if (queryId != null) 'queryId': queryId,
3980
- if (params != null) 'params': params,
3981
- if (id != null) 'id': id,
3982
- if (spatialId != null) 'spatialId': spatialId,
3983
- if (bbox != null) 'bbox': bbox.toJson(),
3984
- if (base != null) 'base': base,
3985
- if (limit != null) 'limit': limit,
3986
- if (principal != null) 'principal': principal,
3987
- if (intervalMs != null) 'intervalMs': intervalMs,
3988
- if (session != null) 'session': session,
3989
- }).then<void>((_) {}, onError: (Object error, StackTrace stackTrace) {
3990
- if (!ctrl.isClosed && ctrl.hasListener) {
3991
- ctrl.addError(error, stackTrace);
3992
- }
3993
- }));
4340
+ unawaited(
4341
+ _call('watch', {
4342
+ 'subId': subId,
4343
+ 'kind': kind,
4344
+ if (queryId != null) 'queryId': queryId,
4345
+ if (params != null) 'params': params,
4346
+ if (id != null) 'id': id,
4347
+ if (spatialId != null) 'spatialId': spatialId,
4348
+ if (bbox != null) 'bbox': bbox.toJson(),
4349
+ if (base != null) 'base': base,
4350
+ if (limit != null) 'limit': limit,
4351
+ if (principal != null) 'principal': principal,
4352
+ if (intervalMs != null) 'intervalMs': intervalMs,
4353
+ if (session != null) 'session': session,
4354
+ }).then<void>(
4355
+ (_) {},
4356
+ onError: (Object error, StackTrace stackTrace) {
4357
+ if (!ctrl.isClosed && ctrl.hasListener) {
4358
+ ctrl.addError(error, stackTrace);
4359
+ }
4360
+ },
4361
+ ),
4362
+ );
3994
4363
  },
3995
4364
  onCancel: () {
3996
4365
  _eventPlane._watches.remove(subId);
@@ -4017,11 +4386,13 @@ class NomosBridge {
4017
4386
  }
4018
4387
  await _sub.cancel();
4019
4388
  final pending = _pending.entries
4020
- .map((entry) => (
4021
- completer: entry.value,
4022
- trace: _pendingTraces[entry.key] ?? const NomosTraceContext(),
4023
- operation: _pendingOps[entry.key],
4024
- ))
4389
+ .map(
4390
+ (entry) => (
4391
+ completer: entry.value,
4392
+ trace: _pendingTraces[entry.key] ?? const NomosTraceContext(),
4393
+ operation: _pendingOps[entry.key],
4394
+ ),
4395
+ )
4025
4396
  .toList(growable: false);
4026
4397
  _pending.clear();
4027
4398
  _pendingOps.clear();
@@ -4031,10 +4402,11 @@ class NomosBridge {
4031
4402
  if (!c.isCompleted) {
4032
4403
  final operation = entry.operation ?? 'unknown';
4033
4404
  final info = NomosErrorInfo.legacy(
4034
- 'bridge disposed while $operation was pending',
4035
- code: 'nomos.bridge.disposed',
4036
- operation: entry.operation,
4037
- trace: entry.trace);
4405
+ 'bridge disposed while $operation was pending',
4406
+ code: 'nomos.bridge.disposed',
4407
+ operation: entry.operation,
4408
+ trace: entry.trace,
4409
+ );
4038
4410
  c.completeError(nomosErrorFromInfo(info));
4039
4411
  }
4040
4412
  }
@@ -4132,15 +4504,18 @@ class NomosSession extends NomosBridge {
4132
4504
  /// An acquired session mounts on the SHARED plane — delegate to the parent (the plane owns the
4133
4505
  /// single-flight registry; a session never owns mounts).
4134
4506
  @override
4135
- Future<NomosSession> acquireSession(String workspace,
4136
- {Future<NomosSessionCredentials> Function()? ifAbsentCredentials,
4137
- String? authorSecret,
4138
- String? inheritedPrincipal}) {
4507
+ Future<NomosSession> acquireSession(
4508
+ String workspace, {
4509
+ Future<NomosSessionCredentials> Function()? ifAbsentCredentials,
4510
+ String? authorSecret,
4511
+ String? inheritedPrincipal,
4512
+ }) {
4139
4513
  final retained = parent._sessionCloudAccess[this.workspace];
4140
4514
  final modelChange = parent._sessionModelChangeHandlers[this.workspace];
4141
4515
  final opened = parent.acquireSession(
4142
4516
  workspace,
4143
- ifAbsentCredentials: ifAbsentCredentials ??
4517
+ ifAbsentCredentials:
4518
+ ifAbsentCredentials ??
4144
4519
  (retained == null ? null : retained.credentials),
4145
4520
  authorSecret:
4146
4521
  authorSecret ?? parent._sessionAuthorSecrets[this.workspace],
@@ -4174,7 +4549,8 @@ class NomosSession extends NomosBridge {
4174
4549
  final source = _sessionName;
4175
4550
  if (source == null || source.isEmpty) {
4176
4551
  throw StateError(
4177
- 'Generated child model inheritance needs a bound parent session.');
4552
+ 'Generated child model inheritance needs a bound parent session.',
4553
+ );
4178
4554
  }
4179
4555
  await _call('stageGeneratedModelFromParent', {
4180
4556
  'sourceSession': source,
@@ -4193,7 +4569,8 @@ class NomosSession extends NomosBridge {
4193
4569
  final workspace = _sessionName;
4194
4570
  if (workspace == null || workspace.isEmpty) {
4195
4571
  throw StateError(
4196
- 'Model change handling needs a bound application session.');
4572
+ 'Model change handling needs a bound application session.',
4573
+ );
4197
4574
  }
4198
4575
  _eventPlane._sessionModelChangeHandlers[workspace] = handler;
4199
4576
  }
@@ -4220,32 +4597,41 @@ class NomosSession extends NomosBridge {
4220
4597
  }) {
4221
4598
  if (cloud.trim().isEmpty) {
4222
4599
  throw ArgumentError.value(
4223
- cloud, 'cloud', 'Nomos cloud URL cannot be empty');
4600
+ cloud,
4601
+ 'cloud',
4602
+ 'Nomos cloud URL cannot be empty',
4603
+ );
4224
4604
  }
4225
- parent._sessionCloudAccess[workspace] =
4226
- _NomosRetainedCloudAccess(cloud: cloud, signIn: signIn);
4605
+ parent._sessionCloudAccess[workspace] = _NomosRetainedCloudAccess(
4606
+ cloud: cloud,
4607
+ signIn: signIn,
4608
+ );
4227
4609
  }
4228
4610
 
4229
4611
  /// A followed ref mounts on the SHARED plane — delegate to the parent (a session never owns mounts).
4230
4612
  @override
4231
- Future<NomosSession> follow<TAggregate>(NomosRef<TAggregate> ref,
4232
- {String? cloud,
4233
- String? clientId,
4234
- String? authToken,
4235
- bool allowUnborn = false,
4236
- String? authorSecret}) =>
4237
- parent.follow(ref,
4238
- cloud: cloud,
4239
- clientId: clientId,
4240
- authToken: authToken,
4241
- allowUnborn: allowUnborn,
4242
- authorSecret:
4243
- authorSecret ?? parent._sessionAuthorSecrets[workspace]);
4613
+ Future<NomosSession> follow<TAggregate>(
4614
+ NomosRef<TAggregate> ref, {
4615
+ String? cloud,
4616
+ String? clientId,
4617
+ String? authToken,
4618
+ bool allowUnborn = false,
4619
+ String? authorSecret,
4620
+ }) => parent.follow(
4621
+ ref,
4622
+ cloud: cloud,
4623
+ clientId: clientId,
4624
+ authToken: authToken,
4625
+ allowUnborn: allowUnborn,
4626
+ authorSecret: authorSecret ?? parent._sessionAuthorSecrets[workspace],
4627
+ );
4244
4628
 
4245
4629
  @override
4246
4630
  Future<Object?> _call(String op, [Map<String, Object?> args = const {}]) =>
4247
- parent._call(op,
4248
- args.containsKey('session') ? args : {...args, 'session': workspace});
4631
+ parent._call(
4632
+ op,
4633
+ args.containsKey('session') ? args : {...args, 'session': workspace},
4634
+ );
4249
4635
 
4250
4636
  // The parent owns the ONE message loop + lifecycle; a session never subscribes, never signals ready of its
4251
4637
  // own, and never tears the plane down (the harness owns `parent.dispose()`).
@@ -4259,18 +4645,22 @@ class NomosSession extends NomosBridge {
4259
4645
  /// tuple throws [ParentSignerEnrollmentMissing] (its `recommendedAction` names the ONE remedy —
4260
4646
  /// [ensureParentSignerEnrollment]); any other failing premise throws [AuthzRefusedError] carrying
4261
4647
  /// the evaluated {workspace, actor, keyHash, object, relation, cause}. Nothing is authored.
4262
- Future<Map<String, Object?>> canAuthorTyped(String directiveId,
4263
- {String? ws,
4264
- String? principal,
4265
- String? publicKey,
4266
- String? keyHash,
4267
- String? authToken}) async {
4268
- final v = await canAuthor(directiveId,
4269
- ws: ws,
4270
- principal: principal,
4271
- publicKey: publicKey,
4272
- keyHash: keyHash,
4273
- authToken: authToken);
4648
+ Future<Map<String, Object?>> canAuthorTyped(
4649
+ String directiveId, {
4650
+ String? ws,
4651
+ String? principal,
4652
+ String? publicKey,
4653
+ String? keyHash,
4654
+ String? authToken,
4655
+ }) async {
4656
+ final v = await canAuthor(
4657
+ directiveId,
4658
+ ws: ws,
4659
+ principal: principal,
4660
+ publicKey: publicKey,
4661
+ keyHash: keyHash,
4662
+ authToken: authToken,
4663
+ );
4274
4664
  if (v['ok'] == true) return v;
4275
4665
  final cause = v['cause']?.toString();
4276
4666
  final target = v['ws']?.toString() ?? ws ?? workspace;
@@ -4327,11 +4717,11 @@ class _BridgeForeignWorkspaceReader implements NomosForeignWorkspaceReader {
4327
4717
  // workspace reads empty (→ ForeignNotFound) rather than throwing. A genuinely unreachable cloud throws,
4328
4718
  // which resolveResult classifies as ForeignUnreachable.
4329
4719
  Future<void> _ensureMounted() => _mounting ??= _bridge.connect(
4330
- cloud: _cloud,
4331
- workspace: _workspace,
4332
- session: _workspace,
4333
- allowUnborn: true,
4334
- );
4720
+ cloud: _cloud,
4721
+ workspace: _workspace,
4722
+ session: _workspace,
4723
+ allowUnborn: true,
4724
+ );
4335
4725
 
4336
4726
  @override
4337
4727
  Future<List<Map<String, dynamic>>> queryById(String id) async {
@@ -4381,8 +4771,9 @@ class _BridgeForeignWorkspaceReader implements NomosForeignWorkspaceReader {
4381
4771
  String? nomosInternalResidencyRequiredWorkspace(Object error) {
4382
4772
  final text = error.toString();
4383
4773
  if (!text.contains('foreign-read-not-ready')) return null;
4384
- final match =
4385
- RegExp(r"source workspace '([^']+)' is not resident").firstMatch(text);
4774
+ final match = RegExp(
4775
+ r"source workspace '([^']+)' is not resident",
4776
+ ).firstMatch(text);
4386
4777
  final workspace = match?.group(1)?.trim();
4387
4778
  return (workspace == null || workspace.isEmpty) ? null : workspace;
4388
4779
  }