@githolon/dsl 0.105.23 → 0.105.24

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.
@@ -51,11 +51,13 @@ import 'webview_bridge.dart'
51
51
 
52
52
  enum _KernelRuntimeArtifact {
53
53
  executable('holon.wasm'),
54
+ repository('kernel.repository.xml', 'repository.xml'),
54
55
  stageZeroLaw('bootstrap.law.usdz'),
55
56
  controllerLaw('nomos.law.usdz');
56
57
 
57
- const _KernelRuntimeArtifact(this.fileName);
58
+ const _KernelRuntimeArtifact(this.fileName, [this.providerFileName]);
58
59
  final String fileName;
60
+ final String? providerFileName;
59
61
  }
60
62
 
61
63
  enum _CandidateKernelBinary {
@@ -74,9 +76,10 @@ Future<String?> _sourceCandidateRuntime(String nodeEntry) async {
74
76
  var cursor = File(nodeEntry).absolute.parent;
75
77
  Directory? root;
76
78
  for (var depth = 0; depth < 12; depth++) {
77
- if (File('${cursor.path}/release/kernel-candidate.json').existsSync() &&
78
- File('${cursor.path}/cloud/dev-server/kernel-testmint.wasm')
79
- .existsSync()) {
79
+ if (File('${cursor.path}/release/kernel-candidate.usda').existsSync() &&
80
+ File(
81
+ '${cursor.path}/cloud/dev-server/kernel-testmint.wasm',
82
+ ).existsSync()) {
80
83
  root = cursor;
81
84
  break;
82
85
  }
@@ -84,31 +87,71 @@ Future<String?> _sourceCandidateRuntime(String nodeEntry) async {
84
87
  if (parent.path == cursor.path) break;
85
88
  cursor = parent;
86
89
  }
87
- if (root == null) return null;
88
-
89
- final descriptor = (jsonDecode(
90
- await File(
91
- '${root.path}/release/kernel-candidate.json',
92
- ).readAsString(),
93
- ) as Map)
94
- .cast<String, Object?>();
95
- String expected(String key) =>
96
- (((descriptor[key] as Map?)?['sha256'])?.toString() ?? '').trim();
97
- final productionExpected = expected('production');
98
- final testMintExpected = expected('testMint');
90
+ final sourceRoot = root;
91
+ if (sourceRoot == null) return null;
92
+
93
+ Future<String> candidateField(String field) async {
94
+ final rust = await Process.run('rustc', ['-vV']);
95
+ if (rust.exitCode != 0) {
96
+ throw NomosTestHarnessException(
97
+ 'rustc could not report its host target: ${rust.stderr}',
98
+ );
99
+ }
100
+ final host = RegExp(
101
+ r'^host: (\S+)$',
102
+ multiLine: true,
103
+ ).firstMatch(rust.stdout.toString())?.group(1);
104
+ if (host == null) {
105
+ throw NomosTestHarnessException('rustc did not report its host target');
106
+ }
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);
119
+ if (result.exitCode != 0) {
120
+ throw NomosTestHarnessException(
121
+ 'could not read typed kernel candidate: ${result.stderr}',
122
+ );
123
+ }
124
+ return result.stdout.toString().trim();
125
+ }
126
+
127
+ final productionExpected = await candidateField('production-sha256');
128
+ final testMintExpected = await candidateField('test-mint-sha256');
99
129
  final production = File(
100
- '${root.path}/cloud/cloudflare-shell/vendor/holon_bytes.wasmbin',
130
+ '${sourceRoot.path}/cloud/cloudflare-shell/vendor/holon_bytes.wasmbin',
101
131
  );
102
132
  final testMint = File(
103
- '${root.path}/cloud/dev-server/kernel-testmint.wasm',
133
+ '${sourceRoot.path}/cloud/dev-server/kernel-testmint.wasm',
134
+ );
135
+ final productionRepository = File(
136
+ '${sourceRoot.path}/cloud/cloudflare-shell/runtime/kernel.repository.xml',
137
+ );
138
+ final testMintRepository = File(
139
+ '${sourceRoot.path}/cloud/cloudflare-shell/runtime/kernel-testmint.repository.xml',
104
140
  );
105
141
  final bootstrap = File(
106
- '${root.path}/cloud/cloudflare-shell/domains/build/bootstrap/domain_bootstrap.law.usdz',
142
+ '${sourceRoot.path}/cloud/cloudflare-shell/domains/build/bootstrap/domain_bootstrap.law.usdz',
107
143
  );
108
144
  final nomos = File(
109
- '${root.path}/cloud/cloudflare-shell/domains/build/nomos/nomos.law.usdz',
145
+ '${sourceRoot.path}/cloud/cloudflare-shell/domains/build/nomos/nomos.law.usdz',
110
146
  );
111
- for (final file in [production, testMint, bootstrap, nomos]) {
147
+ for (final file in [
148
+ production,
149
+ testMint,
150
+ productionRepository,
151
+ testMintRepository,
152
+ bootstrap,
153
+ nomos,
154
+ ]) {
112
155
  if (!file.existsSync()) {
113
156
  throw NomosTestHarnessException(
114
157
  'source release candidate is incomplete: ${file.path} is missing',
@@ -117,6 +160,8 @@ Future<String?> _sourceCandidateRuntime(String nodeEntry) async {
117
160
  }
118
161
  final productionBytes = await production.readAsBytes();
119
162
  final testMintBytes = await testMint.readAsBytes();
163
+ final productionRepositoryBytes = await productionRepository.readAsBytes();
164
+ final testMintRepositoryBytes = await testMintRepository.readAsBytes();
120
165
  final productionActual = sha256.convert(productionBytes).toString();
121
166
  final testMintActual = sha256.convert(testMintBytes).toString();
122
167
  String short(String value) =>
@@ -124,7 +169,7 @@ Future<String?> _sourceCandidateRuntime(String nodeEntry) async {
124
169
  if (productionActual != productionExpected ||
125
170
  testMintActual != testMintExpected) {
126
171
  throw NomosTestHarnessException(
127
- 'source release candidate kernel mismatch: release/kernel-candidate.json '
172
+ 'source release candidate kernel mismatch: release/kernel-candidate.usda '
128
173
  'expects ${short(productionExpected)}/${short(testMintExpected)}, but the staged artifacts are '
129
174
  '${short(productionActual)}/${short(testMintActual)} — run the kernel candidate preparation before testing',
130
175
  );
@@ -135,6 +180,8 @@ Future<String?> _sourceCandidateRuntime(String nodeEntry) async {
135
180
  .convert(
136
181
  utf8.encode(
137
182
  '$productionActual:$testMintActual:'
183
+ '${sha256.convert(productionRepositoryBytes)}:'
184
+ '${sha256.convert(testMintRepositoryBytes)}:'
138
185
  '${sha256.convert(bootstrapBytes)}:'
139
186
  '${sha256.convert(nomosBytes)}',
140
187
  ),
@@ -148,7 +195,8 @@ Future<String?> _sourceCandidateRuntime(String nodeEntry) async {
148
195
  final target = File('${runtime.path}/$name');
149
196
  if (target.existsSync() && target.lengthSync() == bytes.length) return;
150
197
  final candidate = File(
151
- '${target.path}.tmp-${pid}-${DateTime.now().microsecondsSinceEpoch}');
198
+ '${target.path}.tmp-${pid}-${DateTime.now().microsecondsSinceEpoch}',
199
+ );
152
200
  await candidate.writeAsBytes(bytes, flush: true);
153
201
  try {
154
202
  await candidate.rename(target.path);
@@ -164,6 +212,8 @@ Future<String?> _sourceCandidateRuntime(String nodeEntry) async {
164
212
 
165
213
  await publish('holon.wasm', productionBytes);
166
214
  await publish('holon-testmint.wasm', testMintBytes);
215
+ await publish('kernel.repository.xml', productionRepositoryBytes);
216
+ await publish('kernel-testmint.repository.xml', testMintRepositoryBytes);
167
217
  await publish('bootstrap.law.usdz', bootstrapBytes);
168
218
  await publish('nomos.law.usdz', nomosBytes);
169
219
  return runtime.path;
@@ -191,8 +241,8 @@ String? nmsPermanentConvergenceFailure(Map<String, Object?> result) {
191
241
  final converged = result['converged'];
192
242
  final replayDeadLetters =
193
243
  converged is Map && converged['deadLettered'] is List
194
- ? converged['deadLettered'] as List
195
- : const [];
244
+ ? converged['deadLettered'] as List
245
+ : const [];
196
246
  final pushError = result['pushError'];
197
247
  final refused = pushError is Map && pushError['refused'] == true;
198
248
  if (rejected.isEmpty &&
@@ -261,10 +311,10 @@ class HarnessPhaseTimeout extends NomosTestHarnessException {
261
311
  required this.awaiting,
262
312
  this.likelyCauses = const [],
263
313
  }) : super(
264
- 'phase "$phase"${workspace != null ? ' (workspace "$workspace")' : ''} '
265
- 'did not complete within ${elapsed.inSeconds}s while awaiting `$awaiting`'
266
- '${likelyCauses.isEmpty ? '' : ' — likely causes: ${likelyCauses.map((c) => '\n • $c').join()}'}',
267
- );
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
+ );
268
318
  @override
269
319
  String toString() => 'HarnessPhaseTimeout: $message';
270
320
  }
@@ -382,16 +432,16 @@ class NomosTestHarness {
382
432
  _CloudRestartLane? cloudRestartLane,
383
433
  this.testCloud,
384
434
  this.homeWorkspace,
385
- }) : _sessionNames = List.of(
386
- sessionNames,
387
- ), // GROWABLE — restoreWorkspace may add a fresh session
388
- _devicePublics = devicePublics,
389
- _deviceSecrets = deviceSecrets,
390
- _lawHashes = Map.of(lawHashes),
391
- _snapshots = Map.of(snapshots),
392
- _installLane = installLane,
393
- _phaseTimeout = phaseTimeout,
394
- _cloudRestartLane = cloudRestartLane;
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;
395
445
 
396
446
  // ── THE "NO SILENT HANGS" MACHINERY — every lifecycle await runs as a named, deadlined PHASE ──
397
447
 
@@ -559,9 +609,11 @@ class NomosTestHarness {
559
609
  'no lawUsdzPaths — pass the build/<name>.law.usdz `npx githolon compile` emits',
560
610
  );
561
611
  }
562
- final entry = nodeEntry ??
612
+ final entry =
613
+ nodeEntry ??
563
614
  await _resolveEntry(node, resolveFrom ?? Directory.current.path);
564
- final effectiveRuntimeDir = runtimeDir ??
615
+ final effectiveRuntimeDir =
616
+ runtimeDir ??
565
617
  Platform.environment['NOMOS_CANDIDATE_RUNTIME'] ??
566
618
  Platform.environment['NOMOS_DART_RUNTIME'] ??
567
619
  Platform.environment['NOMOS_OFFLINE_RUNTIME'] ??
@@ -574,17 +626,19 @@ class NomosTestHarness {
574
626
  // (default `npx githolon` when the in-repo helper is absent; the test forces this lane by passing
575
627
  // e.g. `installHelper: 'node /abs/cli/dist/cli.mjs'`).
576
628
  final adjacentInstaller = '${File(entry).parent.path}/offline-install.mjs';
577
- final resolvedHelper = installHelper ??
629
+ final resolvedHelper =
630
+ installHelper ??
578
631
  (File(adjacentInstaller).existsSync()
579
632
  ? adjacentInstaller
580
633
  : '${File(entry).parent.path}/offline-install.ts');
581
634
  final useInRepoScript =
582
635
  (resolvedHelper.endsWith('.ts') || resolvedHelper.endsWith('.mjs')) &&
583
- File(resolvedHelper).existsSync();
636
+ File(resolvedHelper).existsSync();
584
637
  final githolonCmd = installHelper == null ? 'npx githolon' : resolvedHelper;
585
638
 
586
639
  final owner = ownerSubject?.trim();
587
- final birthsApplicationHome = owner != null &&
640
+ final birthsApplicationHome =
641
+ owner != null &&
588
642
  owner.isNotEmpty &&
589
643
  workspace == _receivingGroupNameOf(owner);
590
644
  // A receiving-group address is not an arbitrary law-install target. It is valid only when a parent
@@ -608,8 +662,9 @@ class NomosTestHarness {
608
662
  final info = await _phased(
609
663
  phase: '1/4: offline law install (${lawUsdzPaths.length} package(s))',
610
664
  workspace: installWorkspace,
611
- awaiting:
612
- useInRepoScript ? 'offline-install.ts' : 'githolon harness-install',
665
+ awaiting: useInRepoScript
666
+ ? 'offline-install.ts'
667
+ : 'githolon harness-install',
613
668
  timeout: phaseTimeout ?? _kInstallDeadline,
614
669
  onDiagnostic: onDiagnostic,
615
670
  likelyCauses: const [
@@ -726,17 +781,19 @@ class NomosTestHarness {
726
781
  // scenario carries the value from the same package declaration that compiled the
727
782
  // receiving-group plan; the harness does not choose or default it. The ordinary
728
783
  // seed offer crosses the kernel author gate before the first birth is attempted.
729
- run: () => plane.session(installWorkspace).offerOutcome(
730
- domain: '__nomos_application_home_v1',
731
- directiveId: 'seedBirthPolicy',
732
- actor: owner,
733
- authorSecret: deviceSecret,
734
- payload: <String, Object?>{
735
- 'maxBirthsPerPrincipal': maxChildWorkspaces,
736
- 'setBy': owner,
737
- 'setAt': bornAt,
738
- },
739
- ),
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
+ ),
740
797
  );
741
798
  final outcome = await _phased(
742
799
  phase: 'application: birth the signed-in application',
@@ -744,20 +801,22 @@ class NomosTestHarness {
744
801
  awaiting: 'attested receiving-group birth',
745
802
  timeout: phaseTimeout ?? _kOtherDeadline,
746
803
  onDiagnostic: onDiagnostic,
747
- run: () => plane.session(installWorkspace).offerOutcome(
748
- domain: '__nomos_application_home_v1',
749
- directiveId: 'nomosInternalBirthReceivingGroup7c1f5e',
750
- actor: owner,
751
- authorSecret: deviceSecret,
752
- payload: <String, Object?>{
753
- 'owner': owner,
754
- 'frameworkHash': framework,
755
- // Ordered application deployment is reference packages first, deferred child payload last.
756
- // The parent serves the references; the receiving group is born with only its payload law.
757
- 'lawHash': laws.last,
758
- 'bornAt': bornAt,
759
- },
760
- ),
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
+ ),
761
820
  );
762
821
  String? bornStatus;
763
822
  for (final entry in outcome.birthOutcomes) {
@@ -811,26 +870,28 @@ class NomosTestHarness {
811
870
  // harness device through the installed framework law with the same verified provenance
812
871
  // that the private test root later attests. Production clients never manufacture this
813
872
  // marker; it exists only inside the offline test-mint ceremony.
814
- run: () => plane.session(workspace).offerOutcome(
815
- domain: '__nomos_application_home_v1',
816
- directiveId: 'enrollSigner',
817
- actor: owner,
818
- authorSecret: deviceSecret,
819
- payload: <String, Object?>{
820
- 'keyHash': keyHash,
821
- 'grantedBy': owner,
822
- 'grantedAt': 1,
823
- 'verifiedVia': 'nomos:test-mint',
824
- },
825
- ),
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
+ ),
826
887
  );
827
888
  } catch (error) {
828
889
  // Pre-application-home packages neither expose nor enforce this
829
890
  // signer route. Preserve their old harness behaviour, while keeping
830
891
  // every refusal from a current package fatal.
831
892
  if (!error.toString().contains(
832
- "no active installed law declares domain '__nomos_application_home_v1'",
833
- )) {
893
+ "no active installed law declares domain '__nomos_application_home_v1'",
894
+ )) {
834
895
  rethrow;
835
896
  }
836
897
  }
@@ -843,34 +904,39 @@ class NomosTestHarness {
843
904
  timeout: phaseTimeout ?? _kOtherDeadline,
844
905
  onDiagnostic: onDiagnostic,
845
906
  run: () async {
846
- await plane.session(workspace).offerOutcome(
847
- domain: '__nomos_application_home_v1',
848
- directiveId: 'nomosInternalSeedReceivingGroupPolicy7c1f5e',
849
- actor: 'host-birth',
850
- payload: <String, Object?>{'setAt': bornAt},
851
- );
852
- await plane.session(workspace).offerOutcome(
853
- domain: '__nomos_application_home_v1',
854
- directiveId: 'nomosInternalSeedReceivingGroup7c1f5e',
855
- actor: 'host-birth',
856
- payload: <String, Object?>{
857
- 'principal': owner,
858
- 'workspaceName': _receivingGroupNameOf(owner),
859
- 'bornAt': bornAt,
860
- },
861
- );
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
+ );
862
927
  },
863
928
  );
864
929
  final parentCert = await plane.signBirthCert(
865
- NomosBirthCertificateSpec(
866
- child: workspace,
867
- founder: owner,
868
- verified: true,
869
- parentKey: devicePublic,
870
- parentId: 'root',
871
- epoch: 0,
872
- ),
873
- deviceSecret);
930
+ NomosBirthCertificateSpec(
931
+ child: workspace,
932
+ founder: owner,
933
+ verified: true,
934
+ parentKey: devicePublic,
935
+ parentId: 'root',
936
+ epoch: 0,
937
+ ),
938
+ deviceSecret,
939
+ );
874
940
  await _phased(
875
941
  phase: 'owner: establish the verified test application root',
876
942
  workspace: workspace,
@@ -878,34 +944,38 @@ class NomosTestHarness {
878
944
  timeout: phaseTimeout ?? _kOtherDeadline,
879
945
  onDiagnostic: onDiagnostic,
880
946
  run: () async {
881
- await plane.session(workspace).offerOutcome(
882
- domain: '__nomos_application_home_v1',
883
- directiveId: 'seedBirthAttestation',
884
- actor: owner,
885
- authorSecret: deviceSecret,
886
- payload: <String, Object?>{
887
- 'child': workspace,
888
- 'founder': owner,
889
- 'verified': 'true',
890
- 'parentId': 'root',
891
- 'parentKey': devicePublic,
892
- 'epoch': 0,
893
- 'certChain': jsonEncode(<String, Object?>{
894
- 'links': <Object?>[parentCert.toWire()],
895
- }),
896
- },
897
- );
898
- await plane.session(workspace).offerOutcome(
899
- domain: '__nomos_application_home_v1',
900
- directiveId: 'seedGenesis',
901
- actor: owner,
902
- authorSecret: deviceSecret,
903
- payload: <String, Object?>{
904
- 'principal': owner,
905
- 'workspaceId': workspace,
906
- 'bornAt': '2000-01-01T00:00:00.000Z',
907
- },
908
- );
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
+ );
909
979
  },
910
980
  );
911
981
  } on DomainNotInstalledForSession {
@@ -923,9 +993,9 @@ class NomosTestHarness {
923
993
  final sessionLaw = installedHashes.isEmpty
924
994
  ? 'installed'
925
995
  : (birthsApplicationHome
926
- ? installedHashes.last
927
- : installedHashes.first)
928
- .substring(0, 12);
996
+ ? installedHashes.last
997
+ : installedHashes.first)
998
+ .substring(0, 12);
929
999
  phase('ready — session "$workspace" connected (law $sessionLaw)');
930
1000
  // THE DECOY SESSION (multi-workspace by default in tests): sole-session inference is how
931
1001
  // single-workspace assumptions ship — with one session connected, an op that forgot its session
@@ -1055,21 +1125,24 @@ class NomosTestHarness {
1055
1125
  );
1056
1126
  }
1057
1127
  }
1058
- final entry = nodeEntry ??
1128
+ final entry =
1129
+ nodeEntry ??
1059
1130
  await _resolveEntry(node, resolveFrom ?? Directory.current.path);
1060
- final effectiveRuntimeDir = runtimeDir ??
1131
+ final effectiveRuntimeDir =
1132
+ runtimeDir ??
1061
1133
  Platform.environment['NOMOS_CANDIDATE_RUNTIME'] ??
1062
1134
  Platform.environment['NOMOS_DART_RUNTIME'] ??
1063
1135
  Platform.environment['NOMOS_OFFLINE_RUNTIME'] ??
1064
1136
  await _sourceCandidateRuntime(entry);
1065
1137
  final adjacentInstaller = '${File(entry).parent.path}/offline-install.mjs';
1066
- final resolvedHelper = installHelper ??
1138
+ final resolvedHelper =
1139
+ installHelper ??
1067
1140
  (File(adjacentInstaller).existsSync()
1068
1141
  ? adjacentInstaller
1069
1142
  : '${File(entry).parent.path}/offline-install.ts');
1070
1143
  final useInRepoScript =
1071
1144
  (resolvedHelper.endsWith('.ts') || resolvedHelper.endsWith('.mjs')) &&
1072
- File(resolvedHelper).existsSync();
1145
+ File(resolvedHelper).existsSync();
1073
1146
  final githolonCmd = installHelper == null ? 'npx githolon' : resolvedHelper;
1074
1147
 
1075
1148
  final workdir = await Directory.systemTemp.createTemp('nomos-cluster-');
@@ -1329,10 +1402,12 @@ class NomosTestHarness {
1329
1402
  'no lawUsdzPaths — pass the build/<name>.law.usdz `npx githolon compile` emits',
1330
1403
  );
1331
1404
  }
1332
- final entry = nodeEntry ??
1405
+ final entry =
1406
+ nodeEntry ??
1333
1407
  await _resolveEntry(node, resolveFrom ?? Directory.current.path);
1334
- final bareUid =
1335
- subject.startsWith('user:') ? subject.substring(5) : subject;
1408
+ final bareUid = subject.startsWith('user:')
1409
+ ? subject.substring(5)
1410
+ : subject;
1336
1411
  final http = HttpClient();
1337
1412
  Future<Map<String, Object?>> post(
1338
1413
  String path,
@@ -1406,8 +1481,8 @@ class NomosTestHarness {
1406
1481
  }
1407
1482
  rethrow;
1408
1483
  }
1409
- frameworkHash =
1410
- ((created['controller'] as Map?)?['domainHash'])?.toString();
1484
+ frameworkHash = ((created['controller'] as Map?)?['domainHash'])
1485
+ ?.toString();
1411
1486
  final existing = await get(
1412
1487
  '/v2/workspaces/$platformWorkspace/domains',
1413
1488
  );
@@ -1496,7 +1571,7 @@ class NomosTestHarness {
1496
1571
  );
1497
1572
  final currentLaw =
1498
1573
  (parentDomains['currentLaw'] as Map?)?.cast<String, Object?>() ??
1499
- const <String, Object?>{};
1574
+ const <String, Object?>{};
1500
1575
  final controlHomeLawHash =
1501
1576
  currentLaw[applicationHomeDomain]?.toString() ?? '';
1502
1577
  if (controlHomeLawHash.isEmpty) {
@@ -1534,10 +1609,12 @@ class NomosTestHarness {
1534
1609
  // restart while offline must not fetch any member before it can replay the persisted ledger. Release CI
1535
1610
  // supplies the exact candidate bundle. An ordinary local test pins all three artifacts advertised by its
1536
1611
  // ephemeral cloud while still online; never combine a candidate kernel with stale framework law.
1537
- final candidateRuntimeDir = Platform.environment['NOMOS_DART_RUNTIME'] ??
1612
+ final candidateRuntimeDir =
1613
+ Platform.environment['NOMOS_DART_RUNTIME'] ??
1538
1614
  Platform.environment['NOMOS_OFFLINE_RUNTIME'];
1539
- final localRuntime = await Directory('${workdir.path}/client-runtime')
1540
- .create(recursive: true);
1615
+ final localRuntime = await Directory(
1616
+ '${workdir.path}/client-runtime',
1617
+ ).create(recursive: true);
1541
1618
  final selectedKernel = cloud.testKRootPublic == null
1542
1619
  ? _CandidateKernelBinary.production
1543
1620
  : _CandidateKernelBinary.testMint;
@@ -1546,18 +1623,21 @@ class NomosTestHarness {
1546
1623
  : <_KernelRuntimeArtifact, String>{
1547
1624
  _KernelRuntimeArtifact.executable:
1548
1625
  '$candidateRuntimeDir/${selectedKernel.fileName}',
1626
+ _KernelRuntimeArtifact.repository:
1627
+ '$candidateRuntimeDir/${selectedKernel == _CandidateKernelBinary.production ? 'kernel.repository.xml' : 'kernel-testmint.repository.xml'}',
1549
1628
  _KernelRuntimeArtifact.stageZeroLaw:
1550
1629
  '$candidateRuntimeDir/${_KernelRuntimeArtifact.stageZeroLaw.fileName}',
1551
1630
  _KernelRuntimeArtifact.controllerLaw:
1552
1631
  '$candidateRuntimeDir/${_KernelRuntimeArtifact.controllerLaw.fileName}',
1553
1632
  };
1554
- final hasCandidateBundle = candidateArtifacts.length ==
1555
- _KernelRuntimeArtifact.values.length &&
1633
+ final hasCandidateBundle =
1634
+ candidateArtifacts.length == _KernelRuntimeArtifact.values.length &&
1556
1635
  candidateArtifacts.values.every((path) => File(path).existsSync());
1557
1636
  if (hasCandidateBundle) {
1558
1637
  for (final artifact in candidateArtifacts.entries) {
1559
- await File(artifact.value)
1560
- .copy('${localRuntime.path}/${artifact.key.fileName}');
1638
+ await File(
1639
+ artifact.value,
1640
+ ).copy('${localRuntime.path}/${artifact.key.fileName}');
1561
1641
  }
1562
1642
  } else {
1563
1643
  await _phased(
@@ -1569,7 +1649,7 @@ class NomosTestHarness {
1569
1649
  for (final artifact in _KernelRuntimeArtifact.values) {
1570
1650
  final request = await http.getUrl(
1571
1651
  Uri.parse(
1572
- '${cloud.origin}/v1/runtime/${artifact.fileName}',
1652
+ '${cloud.origin}/v1/runtime/${artifact.providerFileName ?? artifact.fileName}',
1573
1653
  ),
1574
1654
  );
1575
1655
  final response = await request.close();
@@ -1583,8 +1663,9 @@ class NomosTestHarness {
1583
1663
  '(status ${response.statusCode}, ${bytes.length} bytes)',
1584
1664
  );
1585
1665
  }
1586
- await File('${localRuntime.path}/${artifact.fileName}')
1587
- .writeAsBytes(bytes, flush: true);
1666
+ await File(
1667
+ '${localRuntime.path}/${artifact.fileName}',
1668
+ ).writeAsBytes(bytes, flush: true);
1588
1669
  }
1589
1670
  },
1590
1671
  );
@@ -1740,34 +1821,36 @@ class NomosTestHarness {
1740
1821
  // authenticated platform holon mounted. Bind this parent certificate operation to the
1741
1822
  // platform session explicitly; relying on the single-session convenience API becomes
1742
1823
  // ambiguous as soon as the ceremony correctly opens the target workspace in-kernel.
1743
- final homeCert =
1744
- await plane.session('$clientId-platform-author').signBirthCert(
1745
- NomosBirthCertificateSpec(
1746
- child: homeName,
1747
- founder: subject,
1748
- // The cert's `verified` must MATCH the gate verdict (CAS-as-law): the offer carries
1749
- // the dev-IdP token, so the gate resolves a VERIFIED founder.
1750
- verified: true,
1751
- parentKey: device.publicB64,
1752
- parentId: platformWorkspace,
1753
- epoch: 0,
1754
- ),
1755
- device.secret,
1756
- );
1757
- final localBirth =
1758
- await plane.session('$clientId-platform-author').offerOutcome(
1759
- domain: applicationHomeDomain,
1760
- directiveId: 'nomosInternalBirthReceivingGroup7c1f5e',
1761
- actor: subject,
1762
- authorSecret: device.secret,
1763
- payload: <String, Object?>{
1764
- 'owner': subject,
1765
- 'frameworkHash': frameworkHash,
1766
- 'lawHash': applicationHomeLawHash,
1767
- 'bornAt': DateTime.now().toUtc().toIso8601String(),
1768
- },
1769
- birthCertificate: homeCert,
1770
- );
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
+ );
1771
1854
  if (!localBirth.born.contains(homeName)) {
1772
1855
  throw NomosTestHarnessException(
1773
1856
  'cloudBound: the local parent kernel admitted the receiving-group offer but did not birth '
@@ -1923,7 +2006,8 @@ class NomosTestHarness {
1923
2006
  if (r['pushFailed'] != true &&
1924
2007
  r['stillAhead'] != true &&
1925
2008
  r['offline'] != true &&
1926
- r['deferred'] != true) pending.remove(ws);
2009
+ r['deferred'] != true)
2010
+ pending.remove(ws);
1927
2011
  } on NomosTestHarnessException {
1928
2012
  rethrow;
1929
2013
  } catch (e) {
@@ -2334,14 +2418,14 @@ class NomosTestHarness {
2334
2418
  // transport first makes its incoming stream wait on consumers that can no longer receive their
2335
2419
  // unwatch/done messages—a deterministic teardown deadlock in reactive Flutter tests.
2336
2420
  await _plane.dispose().timeout(
2337
- _phaseTimeout ?? _kOtherDeadline,
2338
- onTimeout: () => throw HarnessPhaseTimeout(
2339
- phase: 'dispose: close bridge watches',
2340
- workspace: workspace,
2341
- elapsed: _phaseTimeout ?? _kOtherDeadline,
2342
- awaiting: 'NomosBridge.dispose',
2343
- ),
2344
- );
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
+ );
2345
2429
  } catch (e) {
2346
2430
  _diag(
2347
2431
  '[nomos-test-harness] bridge dispose error (continuing to runner cleanup): $e',
@@ -2349,14 +2433,14 @@ class NomosTestHarness {
2349
2433
  }
2350
2434
  try {
2351
2435
  await _transport.dispose().timeout(
2352
- _phaseTimeout ?? _kOtherDeadline,
2353
- onTimeout: () => throw HarnessPhaseTimeout(
2354
- phase: 'dispose: stop the node runner',
2355
- workspace: workspace,
2356
- elapsed: _phaseTimeout ?? _kOtherDeadline,
2357
- awaiting: 'NodeRunnerTransport.dispose',
2358
- ),
2359
- );
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
+ );
2360
2444
  } catch (e) {
2361
2445
  _diag(
2362
2446
  '[nomos-test-harness] transport dispose error (continuing to temp-dir cleanup): $e',
@@ -2490,13 +2574,10 @@ class NomosTestHarness {
2490
2574
  '@githolon/client/runner/node-entry.mjs',
2491
2575
  '@githolon/client/runner/node-entry.ts',
2492
2576
  ]) {
2493
- final r = await Process.run(
2494
- node,
2495
- [
2496
- '-p',
2497
- "require.resolve('$entry')",
2498
- ],
2499
- workingDirectory: from);
2577
+ final r = await Process.run(node, [
2578
+ '-p',
2579
+ "require.resolve('$entry')",
2580
+ ], workingDirectory: from);
2500
2581
  final out = (r.stdout as String).trim();
2501
2582
  if (r.exitCode == 0 && out.isNotEmpty) return out;
2502
2583
  }
@@ -2529,20 +2610,21 @@ class WorkspaceSnapshot {
2529
2610
 
2530
2611
  /// JSON round-trip so a staged snapshot persists to disk verbatim (the bytes + provenance).
2531
2612
  Map<String, Object?> toJson() => {
2532
- 'workspace': workspace,
2533
- 'snapshotB64': snapshotB64,
2534
- 'head': head,
2535
- 'lawHashes': lawHashes,
2536
- 'capturedAt': capturedAt.toUtc().toIso8601String(),
2537
- };
2613
+ 'workspace': workspace,
2614
+ 'snapshotB64': snapshotB64,
2615
+ 'head': head,
2616
+ 'lawHashes': lawHashes,
2617
+ 'capturedAt': capturedAt.toUtc().toIso8601String(),
2618
+ };
2538
2619
 
2539
2620
  factory WorkspaceSnapshot.fromJson(Map<String, Object?> j) =>
2540
2621
  WorkspaceSnapshot(
2541
2622
  workspace: j['workspace'] as String,
2542
2623
  snapshotB64: j['snapshotB64'] as String,
2543
2624
  head: (j['head'] ?? '') as String,
2544
- lawHashes:
2545
- ((j['lawHashes'] as List?) ?? const []).map((e) => '$e').toList(),
2625
+ lawHashes: ((j['lawHashes'] as List?) ?? const [])
2626
+ .map((e) => '$e')
2627
+ .toList(),
2546
2628
  capturedAt: DateTime.parse(
2547
2629
  (j['capturedAt'] ?? DateTime.now().toUtc().toIso8601String())
2548
2630
  as String,
@@ -2564,20 +2646,19 @@ class PlaneSnapshot {
2564
2646
  const PlaneSnapshot({required this.workspaces, required this.capturedAt});
2565
2647
 
2566
2648
  Map<String, Object?> toJson() => {
2567
- 'workspaces': workspaces.map((k, v) => MapEntry(k, v.toJson())),
2568
- 'capturedAt': capturedAt.toUtc().toIso8601String(),
2569
- };
2649
+ 'workspaces': workspaces.map((k, v) => MapEntry(k, v.toJson())),
2650
+ 'capturedAt': capturedAt.toUtc().toIso8601String(),
2651
+ };
2570
2652
 
2571
2653
  factory PlaneSnapshot.fromJson(Map<String, Object?> j) => PlaneSnapshot(
2572
- workspaces: ((j['workspaces'] as Map?) ?? const {}).map(
2573
- (k, v) => MapEntry(
2574
- '$k',
2575
- WorkspaceSnapshot.fromJson((v as Map).cast<String, Object?>()),
2576
- ),
2577
- ),
2578
- capturedAt: DateTime.parse(
2579
- (j['capturedAt'] ?? DateTime.now().toUtc().toIso8601String())
2580
- as String,
2581
- ),
2582
- );
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
+ );
2583
2664
  }