@githolon/dsl 0.107.4 → 0.108.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dart/CHANGELOG.md +19 -0
  2. package/dart/lib/src/runner/node-entry.mjs +4044 -3831
  3. package/dart/lib/src/runner/offline-install.mjs +2633 -2537
  4. package/dart/lib/src/test_harness.dart +249 -239
  5. package/dart/lib/src/webview_bridge.dart +38 -168
  6. package/dart/pubspec.lock +17 -1
  7. package/dart/pubspec.yaml +3 -2
  8. package/dart_types/CHANGELOG.md +10 -0
  9. package/dart_types/lib/nomos_kernel_protocol.dart +10 -0
  10. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pb.dart +16158 -0
  11. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbenum.dart +766 -0
  12. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbjson.dart +6096 -0
  13. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbserver.dart +50 -0
  14. package/dart_types/lib/src/proto/nomos/kernel/v1/progress.pb.dart +245 -0
  15. package/dart_types/lib/src/proto/nomos/kernel/v1/progress.pbenum.dart +160 -0
  16. package/dart_types/lib/src/proto/nomos/kernel/v1/progress.pbjson.dart +178 -0
  17. package/dart_types/lib/src/proto/nomos/runtime/v1/runtime.pb.dart +828 -0
  18. package/dart_types/lib/src/proto/nomos/runtime/v1/runtime.pbenum.dart +465 -0
  19. package/dart_types/lib/src/proto/nomos/runtime/v1/runtime.pbjson.dart +573 -0
  20. package/dart_types/lib/src/proto/nomos/runtime/v1/runtime.pbserver.dart +50 -0
  21. package/dart_types/pubspec.lock +16 -0
  22. package/dart_types/pubspec.yaml +3 -1
  23. package/package.json +1 -1
  24. package/runtime/generated-application-host.js +298 -106
  25. package/src/birth_meter_ensure.ts +2 -0
  26. package/src/business_birth.ts +2 -0
  27. package/src/compile_package_main.ts +36 -18
  28. package/src/framework/application_home.ts +2 -0
  29. package/src/framework/auth_provider_law.ts +34 -0
  30. package/src/framework/birth_certificate_law.ts +24 -0
  31. package/src/framework/home.ts +53 -14
  32. package/src/framework/owned_custody.ts +6 -1
  33. package/src/framework/signing_identity.ts +66 -17
  34. package/src/framework/workspaces.ts +8 -1
  35. package/src/index.ts +2 -0
  36. package/src/shape_compat.ts +6 -3
@@ -41,13 +41,14 @@ import 'dart:convert';
41
41
  import 'dart:io';
42
42
 
43
43
  import 'package:crypto/crypto.dart' show sha256;
44
+ import 'package:fixnum/fixnum.dart' show Int64;
45
+ import 'package:nomos_types/nomos_kernel_protocol.dart' show BirthCertificate;
44
46
  import 'package:package_config/package_config.dart' show loadPackageConfigUri;
45
47
 
46
48
  import 'dev_cloud.dart';
47
49
  import 'node_runner_transport.dart';
48
50
  import 'session_bound.dart' show DomainNotInstalledForSession;
49
- import 'webview_bridge.dart'
50
- show NomosBirthCertificateSpec, NomosBridge, NomosSession;
51
+ import 'webview_bridge.dart' show NomosBridge, NomosSession;
51
52
 
52
53
  enum _KernelRuntimeArtifact {
53
54
  executable('holon.wasm'),
@@ -68,6 +69,30 @@ enum _CandidateKernelBinary {
68
69
  final String fileName;
69
70
  }
70
71
 
72
+ /// Lower the generated protobuf certificate into the framework law's USDA-authored value shape. This is
73
+ /// deliberately not a second certificate type: callers cannot construct it independently, and every field
74
+ /// is read from the compiler-generated message.
75
+ Map<String, Object?> _birthCertificateLawValue(BirthCertificate certificate) =>
76
+ <String, Object?>{
77
+ 'child': certificate.child,
78
+ 'founder': certificate.founder,
79
+ 'verified': certificate.verified,
80
+ 'parentKey': base64Encode(certificate.parentKey),
81
+ 'parentId': certificate.parentId,
82
+ 'epoch': certificate.epoch.toInt(),
83
+ 'signature': base64Encode(certificate.signature),
84
+ if (certificate.hasVerifiedVia()) 'verifiedVia': certificate.verifiedVia,
85
+ if (certificate.hasIdpToken()) 'idpToken': certificate.idpToken,
86
+ if (certificate.hasCapturedClock())
87
+ 'capturedClock': certificate.capturedClock.toInt(),
88
+ if (certificate.hasAuthProviderIssuer())
89
+ 'authProviderIssuer': certificate.authProviderIssuer,
90
+ if (certificate.hasAuthProviderJwks())
91
+ 'authProviderJwks': certificate.authProviderJwks,
92
+ if (certificate.hasAuthProviderAudience())
93
+ 'authProviderAudience': certificate.authProviderAudience,
94
+ };
95
+
71
96
  /// A source checkout is itself a coordinated release candidate: compiler, framework packages and kernels
72
97
  /// move together. Generated scenarios must not combine those source-generated laws with a recently-written
73
98
  /// but older global runtime cache. Published packages have no release/ directory and keep the normal
@@ -104,18 +129,21 @@ Future<String?> _sourceCandidateRuntime(String nodeEntry) async {
104
129
  if (host == null) {
105
130
  throw NomosTestHarnessException('rustc did not report its host target');
106
131
  }
107
- final result = await Process.run('cargo', [
108
- 'run',
109
- '--quiet',
110
- '--target',
111
- host,
112
- '--manifest-path',
113
- '${sourceRoot.path}/tools/kernel-resource/Cargo.toml',
114
- '--',
115
- 'read',
116
- '${sourceRoot.path}/release/kernel-candidate.usda',
117
- field,
118
- ], workingDirectory: sourceRoot.path);
132
+ final result = await Process.run(
133
+ 'cargo',
134
+ [
135
+ 'run',
136
+ '--quiet',
137
+ '--target',
138
+ host,
139
+ '--manifest-path',
140
+ '${sourceRoot.path}/tools/kernel-resource/Cargo.toml',
141
+ '--',
142
+ 'read',
143
+ '${sourceRoot.path}/release/kernel-candidate.usda',
144
+ field,
145
+ ],
146
+ workingDirectory: sourceRoot.path);
119
147
  if (result.exitCode != 0) {
120
148
  throw NomosTestHarnessException(
121
149
  'could not read typed kernel candidate: ${result.stderr}',
@@ -241,8 +269,8 @@ String? nmsPermanentConvergenceFailure(Map<String, Object?> result) {
241
269
  final converged = result['converged'];
242
270
  final replayDeadLetters =
243
271
  converged is Map && converged['deadLettered'] is List
244
- ? converged['deadLettered'] as List
245
- : const [];
272
+ ? converged['deadLettered'] as List
273
+ : const [];
246
274
  final pushError = result['pushError'];
247
275
  final refused = pushError is Map && pushError['refused'] == true;
248
276
  if (rejected.isEmpty &&
@@ -311,10 +339,10 @@ class HarnessPhaseTimeout extends NomosTestHarnessException {
311
339
  required this.awaiting,
312
340
  this.likelyCauses = const [],
313
341
  }) : super(
314
- 'phase "$phase"${workspace != null ? ' (workspace "$workspace")' : ''} '
315
- 'did not complete within ${elapsed.inSeconds}s while awaiting `$awaiting`'
316
- '${likelyCauses.isEmpty ? '' : ' — likely causes: ${likelyCauses.map((c) => '\n • $c').join()}'}',
317
- );
342
+ 'phase "$phase"${workspace != null ? ' (workspace "$workspace")' : ''} '
343
+ 'did not complete within ${elapsed.inSeconds}s while awaiting `$awaiting`'
344
+ '${likelyCauses.isEmpty ? '' : ' — likely causes: ${likelyCauses.map((c) => '\n • $c').join()}'}',
345
+ );
318
346
  @override
319
347
  String toString() => 'HarnessPhaseTimeout: $message';
320
348
  }
@@ -432,16 +460,16 @@ class NomosTestHarness {
432
460
  _CloudRestartLane? cloudRestartLane,
433
461
  this.testCloud,
434
462
  this.homeWorkspace,
435
- }) : _sessionNames = List.of(
436
- sessionNames,
437
- ), // GROWABLE — restoreWorkspace may add a fresh session
438
- _devicePublics = devicePublics,
439
- _deviceSecrets = deviceSecrets,
440
- _lawHashes = Map.of(lawHashes),
441
- _snapshots = Map.of(snapshots),
442
- _installLane = installLane,
443
- _phaseTimeout = phaseTimeout,
444
- _cloudRestartLane = cloudRestartLane;
463
+ }) : _sessionNames = List.of(
464
+ sessionNames,
465
+ ), // GROWABLE — restoreWorkspace may add a fresh session
466
+ _devicePublics = devicePublics,
467
+ _deviceSecrets = deviceSecrets,
468
+ _lawHashes = Map.of(lawHashes),
469
+ _snapshots = Map.of(snapshots),
470
+ _installLane = installLane,
471
+ _phaseTimeout = phaseTimeout,
472
+ _cloudRestartLane = cloudRestartLane;
445
473
 
446
474
  // ── THE "NO SILENT HANGS" MACHINERY — every lifecycle await runs as a named, deadlined PHASE ──
447
475
 
@@ -609,11 +637,9 @@ class NomosTestHarness {
609
637
  'no lawUsdzPaths — pass the build/<name>.law.usdz `npx githolon compile` emits',
610
638
  );
611
639
  }
612
- final entry =
613
- nodeEntry ??
640
+ final entry = nodeEntry ??
614
641
  await _resolveEntry(node, resolveFrom ?? Directory.current.path);
615
- final effectiveRuntimeDir =
616
- runtimeDir ??
642
+ final effectiveRuntimeDir = runtimeDir ??
617
643
  Platform.environment['NOMOS_CANDIDATE_RUNTIME'] ??
618
644
  Platform.environment['NOMOS_DART_RUNTIME'] ??
619
645
  Platform.environment['NOMOS_OFFLINE_RUNTIME'] ??
@@ -626,19 +652,17 @@ class NomosTestHarness {
626
652
  // (default `npx githolon` when the in-repo helper is absent; the test forces this lane by passing
627
653
  // e.g. `installHelper: 'node /abs/cli/dist/cli.mjs'`).
628
654
  final adjacentInstaller = '${File(entry).parent.path}/offline-install.mjs';
629
- final resolvedHelper =
630
- installHelper ??
655
+ final resolvedHelper = installHelper ??
631
656
  (File(adjacentInstaller).existsSync()
632
657
  ? adjacentInstaller
633
658
  : '${File(entry).parent.path}/offline-install.ts');
634
659
  final useInRepoScript =
635
660
  (resolvedHelper.endsWith('.ts') || resolvedHelper.endsWith('.mjs')) &&
636
- File(resolvedHelper).existsSync();
661
+ File(resolvedHelper).existsSync();
637
662
  final githolonCmd = installHelper == null ? 'npx githolon' : resolvedHelper;
638
663
 
639
664
  final owner = ownerSubject?.trim();
640
- final birthsApplicationHome =
641
- owner != null &&
665
+ final birthsApplicationHome = owner != null &&
642
666
  owner.isNotEmpty &&
643
667
  workspace == _receivingGroupNameOf(owner);
644
668
  // A receiving-group address is not an arbitrary law-install target. It is valid only when a parent
@@ -662,9 +686,8 @@ class NomosTestHarness {
662
686
  final info = await _phased(
663
687
  phase: '1/4: offline law install (${lawUsdzPaths.length} package(s))',
664
688
  workspace: installWorkspace,
665
- awaiting: useInRepoScript
666
- ? 'offline-install.ts'
667
- : 'githolon harness-install',
689
+ awaiting:
690
+ useInRepoScript ? 'offline-install.ts' : 'githolon harness-install',
668
691
  timeout: phaseTimeout ?? _kInstallDeadline,
669
692
  onDiagnostic: onDiagnostic,
670
693
  likelyCauses: const [
@@ -781,19 +804,17 @@ class NomosTestHarness {
781
804
  // scenario carries the value from the same package declaration that compiled the
782
805
  // receiving-group plan; the harness does not choose or default it. The ordinary
783
806
  // seed offer crosses the kernel author gate before the first birth is attempted.
784
- run: () => plane
785
- .session(installWorkspace)
786
- .offerOutcome(
787
- domain: '__nomos_application_home_v1',
788
- directiveId: 'seedBirthPolicy',
789
- actor: owner,
790
- authorSecret: deviceSecret,
791
- payload: <String, Object?>{
792
- 'maxBirthsPerPrincipal': maxChildWorkspaces,
793
- 'setBy': owner,
794
- 'setAt': bornAt,
795
- },
796
- ),
807
+ run: () => plane.session(installWorkspace).offerOutcome(
808
+ domain: '__nomos_application_home_v1',
809
+ directiveId: 'seedBirthPolicy',
810
+ actor: owner,
811
+ authorSecret: deviceSecret,
812
+ payload: <String, Object?>{
813
+ 'maxBirthsPerPrincipal': maxChildWorkspaces,
814
+ 'setBy': owner,
815
+ 'setAt': bornAt,
816
+ },
817
+ ),
797
818
  );
798
819
  final outcome = await _phased(
799
820
  phase: 'application: birth the signed-in application',
@@ -801,22 +822,20 @@ class NomosTestHarness {
801
822
  awaiting: 'attested receiving-group birth',
802
823
  timeout: phaseTimeout ?? _kOtherDeadline,
803
824
  onDiagnostic: onDiagnostic,
804
- run: () => plane
805
- .session(installWorkspace)
806
- .offerOutcome(
807
- domain: '__nomos_application_home_v1',
808
- directiveId: 'nomosInternalBirthReceivingGroup7c1f5e',
809
- actor: owner,
810
- authorSecret: deviceSecret,
811
- payload: <String, Object?>{
812
- 'owner': owner,
813
- 'frameworkHash': framework,
814
- // Ordered application deployment is reference packages first, deferred child payload last.
815
- // The parent serves the references; the receiving group is born with only its payload law.
816
- 'lawHash': laws.last,
817
- 'bornAt': bornAt,
818
- },
819
- ),
825
+ run: () => plane.session(installWorkspace).offerOutcome(
826
+ domain: '__nomos_application_home_v1',
827
+ directiveId: 'nomosInternalBirthReceivingGroup7c1f5e',
828
+ actor: owner,
829
+ authorSecret: deviceSecret,
830
+ payload: <String, Object?>{
831
+ 'owner': owner,
832
+ 'frameworkHash': framework,
833
+ // Ordered application deployment is reference packages first, deferred child payload last.
834
+ // The parent serves the references; the receiving group is born with only its payload law.
835
+ 'lawHash': laws.last,
836
+ 'bornAt': bornAt,
837
+ },
838
+ ),
820
839
  );
821
840
  String? bornStatus;
822
841
  for (final entry in outcome.birthOutcomes) {
@@ -870,28 +889,26 @@ class NomosTestHarness {
870
889
  // harness device through the installed framework law with the same verified provenance
871
890
  // that the private test root later attests. Production clients never manufacture this
872
891
  // marker; it exists only inside the offline test-mint ceremony.
873
- run: () => plane
874
- .session(workspace)
875
- .offerOutcome(
876
- domain: '__nomos_application_home_v1',
877
- directiveId: 'enrollSigner',
878
- actor: owner,
879
- authorSecret: deviceSecret,
880
- payload: <String, Object?>{
881
- 'keyHash': keyHash,
882
- 'grantedBy': owner,
883
- 'grantedAt': 1,
884
- 'verifiedVia': 'nomos:test-mint',
885
- },
886
- ),
892
+ run: () => plane.session(workspace).offerOutcome(
893
+ domain: '__nomos_application_home_v1',
894
+ directiveId: 'enrollSigner',
895
+ actor: owner,
896
+ authorSecret: deviceSecret,
897
+ payload: <String, Object?>{
898
+ 'keyHash': keyHash,
899
+ 'grantedBy': owner,
900
+ 'grantedAt': 1,
901
+ 'verifiedVia': 'nomos:test-mint',
902
+ },
903
+ ),
887
904
  );
888
905
  } catch (error) {
889
906
  // Pre-application-home packages neither expose nor enforce this
890
907
  // signer route. Preserve their old harness behaviour, while keeping
891
908
  // every refusal from a current package fatal.
892
909
  if (!error.toString().contains(
893
- "no active installed law declares domain '__nomos_application_home_v1'",
894
- )) {
910
+ "no active installed law declares domain '__nomos_application_home_v1'",
911
+ )) {
895
912
  rethrow;
896
913
  }
897
914
  }
@@ -904,36 +921,32 @@ class NomosTestHarness {
904
921
  timeout: phaseTimeout ?? _kOtherDeadline,
905
922
  onDiagnostic: onDiagnostic,
906
923
  run: () async {
907
- await plane
908
- .session(workspace)
909
- .offerOutcome(
910
- domain: '__nomos_application_home_v1',
911
- directiveId: 'nomosInternalSeedReceivingGroupPolicy7c1f5e',
912
- actor: 'host-birth',
913
- payload: <String, Object?>{'setAt': bornAt},
914
- );
915
- await plane
916
- .session(workspace)
917
- .offerOutcome(
918
- domain: '__nomos_application_home_v1',
919
- directiveId: 'nomosInternalSeedReceivingGroup7c1f5e',
920
- actor: 'host-birth',
921
- payload: <String, Object?>{
922
- 'principal': owner,
923
- 'workspaceName': _receivingGroupNameOf(owner),
924
- 'bornAt': bornAt,
925
- },
926
- );
924
+ await plane.session(workspace).offerOutcome(
925
+ domain: '__nomos_application_home_v1',
926
+ directiveId: 'nomosInternalSeedReceivingGroupPolicy7c1f5e',
927
+ actor: 'host-birth',
928
+ payload: <String, Object?>{'setAt': bornAt},
929
+ );
930
+ await plane.session(workspace).offerOutcome(
931
+ domain: '__nomos_application_home_v1',
932
+ directiveId: 'nomosInternalSeedReceivingGroup7c1f5e',
933
+ actor: 'host-birth',
934
+ payload: <String, Object?>{
935
+ 'principal': owner,
936
+ 'workspaceName': _receivingGroupNameOf(owner),
937
+ 'bornAt': bornAt,
938
+ },
939
+ );
927
940
  },
928
941
  );
929
942
  final parentCert = await plane.signBirthCert(
930
- NomosBirthCertificateSpec(
943
+ BirthCertificate(
931
944
  child: workspace,
932
945
  founder: owner,
933
946
  verified: true,
934
- parentKey: devicePublic,
947
+ parentKey: base64Decode(devicePublic),
935
948
  parentId: 'root',
936
- epoch: 0,
949
+ epoch: Int64.ZERO,
937
950
  ),
938
951
  deviceSecret,
939
952
  );
@@ -944,38 +957,36 @@ class NomosTestHarness {
944
957
  timeout: phaseTimeout ?? _kOtherDeadline,
945
958
  onDiagnostic: onDiagnostic,
946
959
  run: () async {
947
- await plane
948
- .session(workspace)
949
- .offerOutcome(
950
- domain: '__nomos_application_home_v1',
951
- directiveId: 'seedBirthAttestation',
952
- actor: owner,
953
- authorSecret: deviceSecret,
954
- payload: <String, Object?>{
955
- 'child': workspace,
956
- 'founder': owner,
957
- 'verified': 'true',
958
- 'parentId': 'root',
959
- 'parentKey': devicePublic,
960
- 'epoch': 0,
961
- 'certChain': jsonEncode(<String, Object?>{
962
- 'links': <Object?>[parentCert.toWire()],
963
- }),
964
- },
965
- );
966
- await plane
967
- .session(workspace)
968
- .offerOutcome(
969
- domain: '__nomos_application_home_v1',
970
- directiveId: 'seedGenesis',
971
- actor: owner,
972
- authorSecret: deviceSecret,
973
- payload: <String, Object?>{
974
- 'principal': owner,
975
- 'workspaceId': workspace,
976
- 'bornAt': '2000-01-01T00:00:00.000Z',
977
- },
978
- );
960
+ await plane.session(workspace).offerOutcome(
961
+ domain: '__nomos_application_home_v1',
962
+ directiveId: 'seedBirthAttestation',
963
+ actor: owner,
964
+ authorSecret: deviceSecret,
965
+ payload: <String, Object?>{
966
+ 'child': workspace,
967
+ 'founder': owner,
968
+ 'verified': 'true',
969
+ 'parentId': 'root',
970
+ 'parentKey': devicePublic,
971
+ 'epoch': 0,
972
+ 'certChain': jsonEncode(<String, Object?>{
973
+ 'links': <Object?>[
974
+ _birthCertificateLawValue(parentCert),
975
+ ],
976
+ }),
977
+ },
978
+ );
979
+ await plane.session(workspace).offerOutcome(
980
+ domain: '__nomos_application_home_v1',
981
+ directiveId: 'seedGenesis',
982
+ actor: owner,
983
+ authorSecret: deviceSecret,
984
+ payload: <String, Object?>{
985
+ 'principal': owner,
986
+ 'workspaceId': workspace,
987
+ 'bornAt': '2000-01-01T00:00:00.000Z',
988
+ },
989
+ );
979
990
  },
980
991
  );
981
992
  } on DomainNotInstalledForSession {
@@ -993,9 +1004,9 @@ class NomosTestHarness {
993
1004
  final sessionLaw = installedHashes.isEmpty
994
1005
  ? 'installed'
995
1006
  : (birthsApplicationHome
996
- ? installedHashes.last
997
- : installedHashes.first)
998
- .substring(0, 12);
1007
+ ? installedHashes.last
1008
+ : installedHashes.first)
1009
+ .substring(0, 12);
999
1010
  phase('ready — session "$workspace" connected (law $sessionLaw)');
1000
1011
  // THE DECOY SESSION (multi-workspace by default in tests): sole-session inference is how
1001
1012
  // single-workspace assumptions ship — with one session connected, an op that forgot its session
@@ -1125,24 +1136,21 @@ class NomosTestHarness {
1125
1136
  );
1126
1137
  }
1127
1138
  }
1128
- final entry =
1129
- nodeEntry ??
1139
+ final entry = nodeEntry ??
1130
1140
  await _resolveEntry(node, resolveFrom ?? Directory.current.path);
1131
- final effectiveRuntimeDir =
1132
- runtimeDir ??
1141
+ final effectiveRuntimeDir = runtimeDir ??
1133
1142
  Platform.environment['NOMOS_CANDIDATE_RUNTIME'] ??
1134
1143
  Platform.environment['NOMOS_DART_RUNTIME'] ??
1135
1144
  Platform.environment['NOMOS_OFFLINE_RUNTIME'] ??
1136
1145
  await _sourceCandidateRuntime(entry);
1137
1146
  final adjacentInstaller = '${File(entry).parent.path}/offline-install.mjs';
1138
- final resolvedHelper =
1139
- installHelper ??
1147
+ final resolvedHelper = installHelper ??
1140
1148
  (File(adjacentInstaller).existsSync()
1141
1149
  ? adjacentInstaller
1142
1150
  : '${File(entry).parent.path}/offline-install.ts');
1143
1151
  final useInRepoScript =
1144
1152
  (resolvedHelper.endsWith('.ts') || resolvedHelper.endsWith('.mjs')) &&
1145
- File(resolvedHelper).existsSync();
1153
+ File(resolvedHelper).existsSync();
1146
1154
  final githolonCmd = installHelper == null ? 'npx githolon' : resolvedHelper;
1147
1155
 
1148
1156
  final workdir = await Directory.systemTemp.createTemp('nomos-cluster-');
@@ -1402,12 +1410,10 @@ class NomosTestHarness {
1402
1410
  'no lawUsdzPaths — pass the build/<name>.law.usdz `npx githolon compile` emits',
1403
1411
  );
1404
1412
  }
1405
- final entry =
1406
- nodeEntry ??
1413
+ final entry = nodeEntry ??
1407
1414
  await _resolveEntry(node, resolveFrom ?? Directory.current.path);
1408
- final bareUid = subject.startsWith('user:')
1409
- ? subject.substring(5)
1410
- : subject;
1415
+ final bareUid =
1416
+ subject.startsWith('user:') ? subject.substring(5) : subject;
1411
1417
  final http = HttpClient();
1412
1418
  Future<Map<String, Object?>> post(
1413
1419
  String path,
@@ -1481,8 +1487,8 @@ class NomosTestHarness {
1481
1487
  }
1482
1488
  rethrow;
1483
1489
  }
1484
- frameworkHash = ((created['controller'] as Map?)?['domainHash'])
1485
- ?.toString();
1490
+ frameworkHash =
1491
+ ((created['controller'] as Map?)?['domainHash'])?.toString();
1486
1492
  final existing = await get(
1487
1493
  '/v2/workspaces/$platformWorkspace/domains',
1488
1494
  );
@@ -1571,7 +1577,7 @@ class NomosTestHarness {
1571
1577
  );
1572
1578
  final currentLaw =
1573
1579
  (parentDomains['currentLaw'] as Map?)?.cast<String, Object?>() ??
1574
- const <String, Object?>{};
1580
+ const <String, Object?>{};
1575
1581
  final controlHomeLawHash =
1576
1582
  currentLaw[applicationHomeDomain]?.toString() ?? '';
1577
1583
  if (controlHomeLawHash.isEmpty) {
@@ -1609,8 +1615,7 @@ class NomosTestHarness {
1609
1615
  // restart while offline must not fetch any member before it can replay the persisted ledger. Release CI
1610
1616
  // supplies the exact candidate bundle. An ordinary local test pins all three artifacts advertised by its
1611
1617
  // ephemeral cloud while still online; never combine a candidate kernel with stale framework law.
1612
- final candidateRuntimeDir =
1613
- Platform.environment['NOMOS_DART_RUNTIME'] ??
1618
+ final candidateRuntimeDir = Platform.environment['NOMOS_DART_RUNTIME'] ??
1614
1619
  Platform.environment['NOMOS_OFFLINE_RUNTIME'];
1615
1620
  final localRuntime = await Directory(
1616
1621
  '${workdir.path}/client-runtime',
@@ -1630,8 +1635,8 @@ class NomosTestHarness {
1630
1635
  _KernelRuntimeArtifact.controllerLaw:
1631
1636
  '$candidateRuntimeDir/${_KernelRuntimeArtifact.controllerLaw.fileName}',
1632
1637
  };
1633
- final hasCandidateBundle =
1634
- candidateArtifacts.length == _KernelRuntimeArtifact.values.length &&
1638
+ final hasCandidateBundle = candidateArtifacts.length ==
1639
+ _KernelRuntimeArtifact.values.length &&
1635
1640
  candidateArtifacts.values.every((path) => File(path).existsSync());
1636
1641
  if (hasCandidateBundle) {
1637
1642
  for (final artifact in candidateArtifacts.entries) {
@@ -1785,7 +1790,12 @@ class NomosTestHarness {
1785
1790
  'validFromEpoch': 0,
1786
1791
  'grantedAt': DateTime.now().toUtc().toIso8601String(),
1787
1792
  'idpToken': idpToken,
1788
- 'delegationCert': jsonEncode(delegationCert),
1793
+ // The framework law owns this USDA object shape. Keep the
1794
+ // compiler-generated protobuf certificate structured while
1795
+ // lowering it into that domain value; never resurrect the
1796
+ // retired JSON string envelope (`delegationCert`).
1797
+ 'delegationCertificate':
1798
+ _birthCertificateLawValue(delegationCert),
1789
1799
  },
1790
1800
  );
1791
1801
  final enrollmentSync = await plane.sync(
@@ -1821,36 +1831,34 @@ class NomosTestHarness {
1821
1831
  // authenticated platform holon mounted. Bind this parent certificate operation to the
1822
1832
  // platform session explicitly; relying on the single-session convenience API becomes
1823
1833
  // ambiguous as soon as the ceremony correctly opens the target workspace in-kernel.
1824
- final homeCert = await plane
1825
- .session('$clientId-platform-author')
1826
- .signBirthCert(
1827
- NomosBirthCertificateSpec(
1828
- child: homeName,
1829
- founder: subject,
1830
- // The cert's `verified` must MATCH the gate verdict (CAS-as-law): the offer carries
1831
- // the dev-IdP token, so the gate resolves a VERIFIED founder.
1832
- verified: true,
1833
- parentKey: device.publicB64,
1834
- parentId: platformWorkspace,
1835
- epoch: 0,
1836
- ),
1837
- device.secret,
1838
- );
1839
- final localBirth = await plane
1840
- .session('$clientId-platform-author')
1841
- .offerOutcome(
1842
- domain: applicationHomeDomain,
1843
- directiveId: 'nomosInternalBirthReceivingGroup7c1f5e',
1844
- actor: subject,
1845
- authorSecret: device.secret,
1846
- payload: <String, Object?>{
1847
- 'owner': subject,
1848
- 'frameworkHash': frameworkHash,
1849
- 'lawHash': applicationHomeLawHash,
1850
- 'bornAt': DateTime.now().toUtc().toIso8601String(),
1851
- },
1852
- birthCertificate: homeCert,
1853
- );
1834
+ final homeCert =
1835
+ await plane.session('$clientId-platform-author').signBirthCert(
1836
+ BirthCertificate(
1837
+ child: homeName,
1838
+ founder: subject,
1839
+ // The cert's `verified` must MATCH the gate verdict (CAS-as-law): the offer carries
1840
+ // the dev-IdP token, so the gate resolves a VERIFIED founder.
1841
+ verified: true,
1842
+ parentKey: base64Decode(device.publicB64),
1843
+ parentId: platformWorkspace,
1844
+ epoch: Int64.ZERO,
1845
+ ),
1846
+ device.secret,
1847
+ );
1848
+ final localBirth =
1849
+ await plane.session('$clientId-platform-author').offerOutcome(
1850
+ domain: applicationHomeDomain,
1851
+ directiveId: 'nomosInternalBirthReceivingGroup7c1f5e',
1852
+ actor: subject,
1853
+ authorSecret: device.secret,
1854
+ payload: <String, Object?>{
1855
+ 'owner': subject,
1856
+ 'frameworkHash': frameworkHash,
1857
+ 'lawHash': applicationHomeLawHash,
1858
+ 'bornAt': DateTime.now().toUtc().toIso8601String(),
1859
+ },
1860
+ birthCertificate: homeCert,
1861
+ );
1854
1862
  if (!localBirth.born.contains(homeName)) {
1855
1863
  throw NomosTestHarnessException(
1856
1864
  'cloudBound: the local parent kernel admitted the receiving-group offer but did not birth '
@@ -2006,8 +2014,7 @@ class NomosTestHarness {
2006
2014
  if (r['pushFailed'] != true &&
2007
2015
  r['stillAhead'] != true &&
2008
2016
  r['offline'] != true &&
2009
- r['deferred'] != true)
2010
- pending.remove(ws);
2017
+ r['deferred'] != true) pending.remove(ws);
2011
2018
  } on NomosTestHarnessException {
2012
2019
  rethrow;
2013
2020
  } catch (e) {
@@ -2418,14 +2425,14 @@ class NomosTestHarness {
2418
2425
  // transport first makes its incoming stream wait on consumers that can no longer receive their
2419
2426
  // unwatch/done messages—a deterministic teardown deadlock in reactive Flutter tests.
2420
2427
  await _plane.dispose().timeout(
2421
- _phaseTimeout ?? _kOtherDeadline,
2422
- onTimeout: () => throw HarnessPhaseTimeout(
2423
- phase: 'dispose: close bridge watches',
2424
- workspace: workspace,
2425
- elapsed: _phaseTimeout ?? _kOtherDeadline,
2426
- awaiting: 'NomosBridge.dispose',
2427
- ),
2428
- );
2428
+ _phaseTimeout ?? _kOtherDeadline,
2429
+ onTimeout: () => throw HarnessPhaseTimeout(
2430
+ phase: 'dispose: close bridge watches',
2431
+ workspace: workspace,
2432
+ elapsed: _phaseTimeout ?? _kOtherDeadline,
2433
+ awaiting: 'NomosBridge.dispose',
2434
+ ),
2435
+ );
2429
2436
  } catch (e) {
2430
2437
  _diag(
2431
2438
  '[nomos-test-harness] bridge dispose error (continuing to runner cleanup): $e',
@@ -2433,14 +2440,14 @@ class NomosTestHarness {
2433
2440
  }
2434
2441
  try {
2435
2442
  await _transport.dispose().timeout(
2436
- _phaseTimeout ?? _kOtherDeadline,
2437
- onTimeout: () => throw HarnessPhaseTimeout(
2438
- phase: 'dispose: stop the node runner',
2439
- workspace: workspace,
2440
- elapsed: _phaseTimeout ?? _kOtherDeadline,
2441
- awaiting: 'NodeRunnerTransport.dispose',
2442
- ),
2443
- );
2443
+ _phaseTimeout ?? _kOtherDeadline,
2444
+ onTimeout: () => throw HarnessPhaseTimeout(
2445
+ phase: 'dispose: stop the node runner',
2446
+ workspace: workspace,
2447
+ elapsed: _phaseTimeout ?? _kOtherDeadline,
2448
+ awaiting: 'NodeRunnerTransport.dispose',
2449
+ ),
2450
+ );
2444
2451
  } catch (e) {
2445
2452
  _diag(
2446
2453
  '[nomos-test-harness] transport dispose error (continuing to temp-dir cleanup): $e',
@@ -2574,10 +2581,13 @@ class NomosTestHarness {
2574
2581
  '@githolon/client/runner/node-entry.mjs',
2575
2582
  '@githolon/client/runner/node-entry.ts',
2576
2583
  ]) {
2577
- final r = await Process.run(node, [
2578
- '-p',
2579
- "require.resolve('$entry')",
2580
- ], workingDirectory: from);
2584
+ final r = await Process.run(
2585
+ node,
2586
+ [
2587
+ '-p',
2588
+ "require.resolve('$entry')",
2589
+ ],
2590
+ workingDirectory: from);
2581
2591
  final out = (r.stdout as String).trim();
2582
2592
  if (r.exitCode == 0 && out.isNotEmpty) return out;
2583
2593
  }
@@ -2610,21 +2620,20 @@ class WorkspaceSnapshot {
2610
2620
 
2611
2621
  /// JSON round-trip so a staged snapshot persists to disk verbatim (the bytes + provenance).
2612
2622
  Map<String, Object?> toJson() => {
2613
- 'workspace': workspace,
2614
- 'snapshotB64': snapshotB64,
2615
- 'head': head,
2616
- 'lawHashes': lawHashes,
2617
- 'capturedAt': capturedAt.toUtc().toIso8601String(),
2618
- };
2623
+ 'workspace': workspace,
2624
+ 'snapshotB64': snapshotB64,
2625
+ 'head': head,
2626
+ 'lawHashes': lawHashes,
2627
+ 'capturedAt': capturedAt.toUtc().toIso8601String(),
2628
+ };
2619
2629
 
2620
2630
  factory WorkspaceSnapshot.fromJson(Map<String, Object?> j) =>
2621
2631
  WorkspaceSnapshot(
2622
2632
  workspace: j['workspace'] as String,
2623
2633
  snapshotB64: j['snapshotB64'] as String,
2624
2634
  head: (j['head'] ?? '') as String,
2625
- lawHashes: ((j['lawHashes'] as List?) ?? const [])
2626
- .map((e) => '$e')
2627
- .toList(),
2635
+ lawHashes:
2636
+ ((j['lawHashes'] as List?) ?? const []).map((e) => '$e').toList(),
2628
2637
  capturedAt: DateTime.parse(
2629
2638
  (j['capturedAt'] ?? DateTime.now().toUtc().toIso8601String())
2630
2639
  as String,
@@ -2646,19 +2655,20 @@ class PlaneSnapshot {
2646
2655
  const PlaneSnapshot({required this.workspaces, required this.capturedAt});
2647
2656
 
2648
2657
  Map<String, Object?> toJson() => {
2649
- 'workspaces': workspaces.map((k, v) => MapEntry(k, v.toJson())),
2650
- 'capturedAt': capturedAt.toUtc().toIso8601String(),
2651
- };
2658
+ 'workspaces': workspaces.map((k, v) => MapEntry(k, v.toJson())),
2659
+ 'capturedAt': capturedAt.toUtc().toIso8601String(),
2660
+ };
2652
2661
 
2653
2662
  factory PlaneSnapshot.fromJson(Map<String, Object?> j) => PlaneSnapshot(
2654
- workspaces: ((j['workspaces'] as Map?) ?? const {}).map(
2655
- (k, v) => MapEntry(
2656
- '$k',
2657
- WorkspaceSnapshot.fromJson((v as Map).cast<String, Object?>()),
2658
- ),
2659
- ),
2660
- capturedAt: DateTime.parse(
2661
- (j['capturedAt'] ?? DateTime.now().toUtc().toIso8601String()) as String,
2662
- ),
2663
- );
2663
+ workspaces: ((j['workspaces'] as Map?) ?? const {}).map(
2664
+ (k, v) => MapEntry(
2665
+ '$k',
2666
+ WorkspaceSnapshot.fromJson((v as Map).cast<String, Object?>()),
2667
+ ),
2668
+ ),
2669
+ capturedAt: DateTime.parse(
2670
+ (j['capturedAt'] ?? DateTime.now().toUtc().toIso8601String())
2671
+ as String,
2672
+ ),
2673
+ );
2664
2674
  }