@githolon/dsl 0.102.4 → 0.103.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compile_package.mjs +43 -2
- package/compile_proof.d.mts +47 -0
- package/compile_proof.mjs +343 -0
- package/dart/CHANGELOG.md +7 -0
- package/dart/lib/src/dev_cloud.dart +8 -8
- package/dart/lib/src/nms_generated_runtime.dart +2 -0
- package/dart/lib/src/runner/node-entry.mjs +5504 -1236
- package/dart/lib/src/runner/offline-install.mjs +4578 -528
- package/dart/lib/src/test_harness.dart +206 -152
- package/dart/lib/src/webview_bridge.dart +196 -89
- package/dart/pubspec.lock +1 -1
- package/dart/pubspec.yaml +2 -2
- package/dart/test/acquire_session_credentials_test.dart +4 -3
- package/dart/test/adv_realm_interference_test.dart +7 -6
- package/dart/test/cloud_bound_topology_test.dart +3 -3
- package/dart/test/cluster_births_test.dart +4 -3
- package/dart/test/cluster_device_keyed_acceptance_test.dart +10 -16
- package/dart/test/cluster_local_autosync_test.dart +3 -2
- package/dart/test/cluster_scale_test.dart +3 -2
- package/dart/test/dlq_session_surface_test.dart +7 -6
- package/dart/test/first_app_gauntlet_test.dart +14 -21
- package/dart/test/fixtures/wedge/fake-install.mjs +1 -1
- package/dart/test/four_layer_nested_topology_test.dart +4 -4
- package/dart/test/harness_phase_timeout_test.dart +2 -2
- package/dart/test/local_sync_inert_test.dart +1 -1
- package/dart/test/parent_signer_recovery_test.dart +2 -2
- package/dart/test/sealing_client_test.dart +4 -3
- package/dart/test/session_bound_test.dart +8 -7
- package/dart/test/test_harness_test.dart +13 -12
- package/dart/test/vo_read_typing_test.dart +4 -3
- package/dart/test/watch_after_offer_stall_test.dart +4 -3
- package/dart/test/webview_bridge_test.dart +102 -5
- package/dart_types/CHANGELOG.md +5 -0
- package/dart_types/lib/nomos_types.dart +9 -0
- package/dart_types/lib/src/nomos_ref.dart +20 -0
- package/dart_types/lib/src/optional_ref.dart +85 -0
- package/dart_types/lib/src/sha256_digest.dart +41 -0
- package/dart_types/lib/src/versioned_nomos_ref.dart +81 -0
- package/dart_types/pubspec.yaml +1 -1
- package/dart_types/test/optional_ref_test.dart +44 -0
- package/dart_types/test/sha256_digest_test.dart +18 -0
- package/package.json +9 -5
- package/runtime/generated-application-host.js +5365 -1105
- package/src/aggregate.ts +18 -0
- package/src/application_composition.ts +6 -32
- package/src/attested_core.ts +6 -3
- package/src/birth_meter.ts +1 -0
- package/src/birth_meter_ensure.ts +28 -2
- package/src/births.ts +1 -1
- package/src/build_package.ts +162 -538
- package/src/business_birth.ts +19 -3
- package/src/canonical_json.ts +21 -0
- package/src/check_package_main.ts +76 -35
- package/src/clear_directive.ts +19 -8
- package/src/codegen_dart.ts +305 -22
- package/src/codegen_dart_application.ts +844 -292
- package/src/codegen_dart_client.ts +1084 -407
- package/src/codegen_dart_scenario.ts +12 -12
- package/src/codegen_proof.ts +169 -39
- package/src/codegen_provider_dart.ts +17 -1
- package/src/codegen_ts.ts +298 -106
- package/src/compat_manifest.ts +2 -2
- package/src/compat_package_main.ts +8 -12
- package/src/compile_package_main.ts +1866 -597
- package/src/compose.ts +1 -1
- package/src/directive.ts +208 -15
- package/src/engine_entry.ts +165 -111
- package/src/evolve_preview.ts +43 -81
- package/src/extremum.ts +1 -1
- package/src/fields.ts +183 -4
- package/src/flow_gate.ts +75 -2
- package/src/framework/application_home.ts +2 -0
- package/src/framework/auth_provider_law.ts +2 -2
- package/src/framework/birth_meter.ts +7 -19
- package/src/framework/bootstrap.ts +0 -2
- package/src/framework/business_corrections.ts +9 -5
- package/src/framework/domain_lifecycle.ts +15 -29
- package/src/framework/encryption_scope.ts +2 -1
- package/src/framework/framework_aggregates.ts +2 -0
- package/src/framework/genesis.ts +16 -14
- package/src/framework/home.ts +20 -14
- package/src/framework/owned_custody.ts +9 -3
- package/src/framework/platform_admin.ts +3 -3
- package/src/framework/relations.ts +21 -13
- package/src/framework/signing_identity.ts +45 -40
- package/src/framework/workspace_invariant.ts +245 -0
- package/src/framework/workspaces.ts +23 -29
- package/src/index.ts +36 -16
- package/src/intent.ts +178 -6
- package/src/law_check.ts +32 -11
- package/src/law_graph.ts +2902 -0
- package/src/law_graph_usda.ts +1799 -0
- package/src/law_identity.ts +435 -0
- package/src/law_value_type.ts +230 -0
- package/src/lsp_server.ts +61 -31
- package/src/ops.ts +7 -0
- package/src/package_config.ts +141 -0
- package/src/package_dependencies.ts +108 -0
- package/src/package_dependency_usage.ts +150 -0
- package/src/package_source_imports.ts +71 -0
- package/src/plan_source_slice.ts +335 -0
- package/src/point_guard.ts +192 -0
- package/src/precondition.ts +44 -9
- package/src/read_projection_namespace.ts +1 -1
- package/src/runtime_zod.ts +44 -0
- package/src/secondary_ensure.ts +67 -14
- package/src/shape_compat.ts +1 -1
- package/src/stable_id_identity_bridge.ts +149 -0
- package/src/stable_id_lockfile.ts +278 -0
- package/src/stable_ids.ts +7 -2
- package/src/stable_ids_for_module.ts +348 -0
- package/src/stable_ids_types.ts +1 -1
- package/src/sum.ts +1 -1
- package/src/usd_state.ts +1 -1
- package/src/usda_relationship_integrity.ts +66 -0
- package/src/usda_text.ts +18 -0
- package/src/usdz.ts +78 -0
- package/src/value_object.ts +95 -0
- package/src/wire.ts +19 -0
- package/src/wire_encode.ts +22 -0
- package/src/workspace_sharding.ts +52 -36
- package/src/workspace_sharing.ts +28 -4
- package/src/zod_introspection.ts +1 -1
- package/dart/test/cluster_attested_test.dart +0 -201
- package/src/compile_engine.ts +0 -595
- package/src/manifest.ts +0 -1476
- package/src/read_projection_composition.ts +0 -117
- package/src/read_projection_manifest.ts +0 -115
- package/src/relation_composition.ts +0 -286
- package/src/usd.ts +0 -1967
- package/src/usd_layers.ts +0 -1776
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
//
|
|
8
8
|
// ```dart
|
|
9
9
|
// final harness = await NomosTestHarness.ephemeral(
|
|
10
|
-
//
|
|
10
|
+
// lawUsdzPaths: ['build/guestbook.law.usdz'],
|
|
11
11
|
// registerTearDown: addTearDown, // auto-disposes when the test ends (see below)
|
|
12
12
|
// );
|
|
13
13
|
// final app = guestbookClient(harness.bridge); // the generated client — same one the app uses
|
|
@@ -85,10 +85,10 @@ Future<String?> _sourceCandidateRuntime(String nodeEntry) async {
|
|
|
85
85
|
'${root.path}/cloud/dev-server/kernel-testmint.wasm',
|
|
86
86
|
);
|
|
87
87
|
final bootstrap = File(
|
|
88
|
-
'${root.path}/cloud/cloudflare-shell/domains/domain_bootstrap.
|
|
88
|
+
'${root.path}/cloud/cloudflare-shell/domains/build/bootstrap/domain_bootstrap.law.usdz',
|
|
89
89
|
);
|
|
90
90
|
final nomos = File(
|
|
91
|
-
'${root.path}/cloud/cloudflare-shell/domains/
|
|
91
|
+
'${root.path}/cloud/cloudflare-shell/domains/build/nomos/nomos.law.usdz',
|
|
92
92
|
);
|
|
93
93
|
for (final file in [production, testMint, bootstrap, nomos]) {
|
|
94
94
|
if (!file.existsSync()) {
|
|
@@ -111,14 +111,14 @@ Future<String?> _sourceCandidateRuntime(String nodeEntry) async {
|
|
|
111
111
|
'${short(productionActual)}/${short(testMintActual)} — run the kernel candidate preparation before testing',
|
|
112
112
|
);
|
|
113
113
|
}
|
|
114
|
-
final
|
|
115
|
-
final
|
|
114
|
+
final bootstrapBytes = await bootstrap.readAsBytes();
|
|
115
|
+
final nomosBytes = await nomos.readAsBytes();
|
|
116
116
|
final bundleIdentity = sha256
|
|
117
117
|
.convert(
|
|
118
118
|
utf8.encode(
|
|
119
119
|
'$productionActual:$testMintActual:'
|
|
120
|
-
'${sha256.convert(
|
|
121
|
-
'${sha256.convert(
|
|
120
|
+
'${sha256.convert(bootstrapBytes)}:'
|
|
121
|
+
'${sha256.convert(nomosBytes)}',
|
|
122
122
|
),
|
|
123
123
|
)
|
|
124
124
|
.toString();
|
|
@@ -146,13 +146,8 @@ Future<String?> _sourceCandidateRuntime(String nodeEntry) async {
|
|
|
146
146
|
|
|
147
147
|
await publish('holon.wasm', productionBytes);
|
|
148
148
|
await publish('holon-testmint.wasm', testMintBytes);
|
|
149
|
-
await publish(
|
|
150
|
-
|
|
151
|
-
utf8.encode(jsonEncode(<String, String>{
|
|
152
|
-
'bootstrap': bootstrapText,
|
|
153
|
-
'nomos': nomosText,
|
|
154
|
-
})),
|
|
155
|
-
);
|
|
149
|
+
await publish('bootstrap.law.usdz', bootstrapBytes);
|
|
150
|
+
await publish('nomos.law.usdz', nomosBytes);
|
|
156
151
|
return runtime.path;
|
|
157
152
|
}
|
|
158
153
|
|
|
@@ -193,7 +188,7 @@ String? nmsPermanentConvergenceFailure(Map<String, Object?> result) {
|
|
|
193
188
|
|
|
194
189
|
/// The offline-install lane configuration a harness keeps so the CUSTODY DIAGNOSTIC helpers
|
|
195
190
|
/// (ask 9 of architecture/session_bound_clients.md) can re-run installs after construction:
|
|
196
|
-
/// [
|
|
191
|
+
/// [seedWorkspaceFromLawUsdz] / [resetWorkspaceCustody].
|
|
197
192
|
class _InstallLane {
|
|
198
193
|
final String node;
|
|
199
194
|
final bool useInRepoScript;
|
|
@@ -323,7 +318,7 @@ class NomosTestHarness {
|
|
|
323
318
|
/// workspace → the base64 restore snapshot it was seeded from (for [resetWorkspaceCustody]).
|
|
324
319
|
final Map<String, String> _snapshots;
|
|
325
320
|
|
|
326
|
-
/// The offline-install lane config (for [
|
|
321
|
+
/// The offline-install lane config (for [seedWorkspaceFromLawUsdz]); null when unknown.
|
|
327
322
|
final _InstallLane? _installLane;
|
|
328
323
|
|
|
329
324
|
/// When set, overrides EVERY phase's default deadline (see [HarnessPhaseTimeout]).
|
|
@@ -505,13 +500,13 @@ class NomosTestHarness {
|
|
|
505
500
|
return bridge.session(workspace);
|
|
506
501
|
}
|
|
507
502
|
|
|
508
|
-
/// Install [
|
|
503
|
+
/// Install [lawUsdzPaths] into a fresh LOCAL workspace and boot a bridge over it — NO cloud contact.
|
|
509
504
|
///
|
|
510
|
-
/// [
|
|
505
|
+
/// [lawUsdzPaths] — the `build/<name>.law.usdz` files `nomos-compile` emits (installed in order;
|
|
511
506
|
/// framework/platform law before a domain that depends on it).
|
|
512
507
|
/// [workspace] — the local workspace name (default `test-ws`; law-only, never leaves the machine).
|
|
513
508
|
/// [runtimeDir] — the runtime cache (default `~/.holon/runtime`); must hold `holon-testmint.wasm` +
|
|
514
|
-
/// `
|
|
509
|
+
/// `bootstrap.law.usdz` + `nomos.law.usdz`. The harness owns a throwaway test root; app code never handles its key material.
|
|
515
510
|
/// [nodeEntry] — path to `node-entry.ts`; when omitted it is resolved from [resolveFrom] via
|
|
516
511
|
/// `require.resolve('@githolon/client/runner/node-entry.ts')`.
|
|
517
512
|
/// [installHelper] — the offline law-install lane. An EXISTING `.ts` or `.mjs` file is run as a node script (the
|
|
@@ -523,7 +518,7 @@ class NomosTestHarness {
|
|
|
523
518
|
/// (feature-detecting `package:test` from a library is not clean in Dart, so this is explicit and safe).
|
|
524
519
|
/// [autoTearDown] — when true (default) and [registerTearDown] is given, registers [dispose] as a teardown.
|
|
525
520
|
static Future<NomosTestHarness> ephemeral({
|
|
526
|
-
required List<String>
|
|
521
|
+
required List<String> lawUsdzPaths,
|
|
527
522
|
String? ownerSubject,
|
|
528
523
|
String workspace = 'test-ws',
|
|
529
524
|
String clientId = 'test-runner',
|
|
@@ -540,9 +535,9 @@ class NomosTestHarness {
|
|
|
540
535
|
Duration? phaseTimeout,
|
|
541
536
|
bool decoySession = true,
|
|
542
537
|
}) async {
|
|
543
|
-
if (
|
|
538
|
+
if (lawUsdzPaths.isEmpty) {
|
|
544
539
|
throw NomosTestHarnessException(
|
|
545
|
-
'no
|
|
540
|
+
'no lawUsdzPaths — pass the build/<name>.law.usdz `npx githolon compile` emits',
|
|
546
541
|
);
|
|
547
542
|
}
|
|
548
543
|
final entry = nodeEntry ??
|
|
@@ -592,8 +587,7 @@ class NomosTestHarness {
|
|
|
592
587
|
try {
|
|
593
588
|
final snapshot = '${workdir.path}/snapshot.bin';
|
|
594
589
|
final info = await _phased(
|
|
595
|
-
phase:
|
|
596
|
-
'1/4: offline law install (${deployJsonPaths.length} package(s))',
|
|
590
|
+
phase: '1/4: offline law install (${lawUsdzPaths.length} package(s))',
|
|
597
591
|
workspace: installWorkspace,
|
|
598
592
|
awaiting:
|
|
599
593
|
useInRepoScript ? 'offline-install.ts' : 'githolon harness-install',
|
|
@@ -608,7 +602,7 @@ class NomosTestHarness {
|
|
|
608
602
|
useInRepoScript: useInRepoScript,
|
|
609
603
|
inRepoScript: resolvedHelper,
|
|
610
604
|
githolonCmd: githolonCmd,
|
|
611
|
-
|
|
605
|
+
lawUsdzPaths: lawUsdzPaths,
|
|
612
606
|
workspace: installWorkspace,
|
|
613
607
|
snapshot: snapshot,
|
|
614
608
|
runtimeDir: effectiveRuntimeDir,
|
|
@@ -647,7 +641,7 @@ class NomosTestHarness {
|
|
|
647
641
|
timeout: phaseTimeout ?? readyTimeout,
|
|
648
642
|
onDiagnostic: onDiagnostic,
|
|
649
643
|
likelyCauses: const [
|
|
650
|
-
'node is not on PATH, or ~/.holon/runtime is cold (holon-testmint.wasm +
|
|
644
|
+
'node is not on PATH, or ~/.holon/runtime is cold (holon-testmint.wasm + framework law archives)',
|
|
651
645
|
'the runner process failed to boot the engine — run with onDiagnostic to see its stderr',
|
|
652
646
|
],
|
|
653
647
|
run: () => plane.ready,
|
|
@@ -872,18 +866,16 @@ class NomosTestHarness {
|
|
|
872
866
|
// application-ready.
|
|
873
867
|
}
|
|
874
868
|
}
|
|
875
|
-
final installedHashes =
|
|
876
|
-
((
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
? 'installed'
|
|
883
|
-
: (birthsApplicationHome
|
|
869
|
+
final installedHashes = ((info['lawHashes'] as List?) ?? const [])
|
|
870
|
+
.map((hash) => hash.toString())
|
|
871
|
+
.where((hash) => hash.isNotEmpty)
|
|
872
|
+
.toList();
|
|
873
|
+
final sessionLaw = installedHashes.isEmpty
|
|
874
|
+
? 'installed'
|
|
875
|
+
: (birthsApplicationHome
|
|
884
876
|
? installedHashes.last
|
|
885
877
|
: installedHashes.first)
|
|
886
|
-
|
|
878
|
+
.substring(0, 12);
|
|
887
879
|
phase('ready — session "$workspace" connected (law $sessionLaw)');
|
|
888
880
|
// THE DECOY SESSION (multi-workspace by default in tests): sole-session inference is how
|
|
889
881
|
// single-workspace assumptions ship — with one session connected, an op that forgot its session
|
|
@@ -978,7 +970,7 @@ class NomosTestHarness {
|
|
|
978
970
|
/// the SAME offline lane [ephemeral] uses (one snapshot per workspace), then `connect`ed as its own session;
|
|
979
971
|
/// the per-connect restore snapshot loads it into the shared plane.
|
|
980
972
|
///
|
|
981
|
-
/// [workspaces] — `{ workspaceName: [
|
|
973
|
+
/// [workspaces] — `{ workspaceName: [lawUsdzPaths] }` (installed in order per workspace). Get a
|
|
982
974
|
/// session-scoped view with [session] and bind it to a generated client: `EstateClient(harness.session(
|
|
983
975
|
/// 'estate'))`. All other params mirror [ephemeral]. [clientId] is suffixed per session.
|
|
984
976
|
/// [deviceKeyed] names workspaces to open WITH a fresh in-kernel device key as their signing `authorSecret`
|
|
@@ -1003,13 +995,13 @@ class NomosTestHarness {
|
|
|
1003
995
|
}) async {
|
|
1004
996
|
if (workspaces.isEmpty) {
|
|
1005
997
|
throw NomosTestHarnessException(
|
|
1006
|
-
'no workspaces — pass { workspaceName: [build/<name>.
|
|
998
|
+
'no workspaces — pass { workspaceName: [build/<name>.law.usdz] } for each colocated workspace',
|
|
1007
999
|
);
|
|
1008
1000
|
}
|
|
1009
1001
|
for (final e in workspaces.entries) {
|
|
1010
1002
|
if (e.value.isEmpty) {
|
|
1011
1003
|
throw NomosTestHarnessException(
|
|
1012
|
-
'workspace "${e.key}" has no
|
|
1004
|
+
'workspace "${e.key}" has no lawUsdzPaths — pass its build/<name>.law.usdz',
|
|
1013
1005
|
);
|
|
1014
1006
|
}
|
|
1015
1007
|
}
|
|
@@ -1061,7 +1053,7 @@ class NomosTestHarness {
|
|
|
1061
1053
|
useInRepoScript: useInRepoScript,
|
|
1062
1054
|
inRepoScript: resolvedHelper,
|
|
1063
1055
|
githolonCmd: githolonCmd,
|
|
1064
|
-
|
|
1056
|
+
lawUsdzPaths: e.value,
|
|
1065
1057
|
workspace: e.key,
|
|
1066
1058
|
snapshot: snap,
|
|
1067
1059
|
runtimeDir: effectiveRuntimeDir,
|
|
@@ -1116,7 +1108,7 @@ class NomosTestHarness {
|
|
|
1116
1108
|
timeout: phaseTimeout ?? readyTimeout,
|
|
1117
1109
|
onDiagnostic: onDiagnostic,
|
|
1118
1110
|
likelyCauses: const [
|
|
1119
|
-
'node is not on PATH, or ~/.holon/runtime is cold (holon-testmint.wasm +
|
|
1111
|
+
'node is not on PATH, or ~/.holon/runtime is cold (holon-testmint.wasm + framework law archives)',
|
|
1120
1112
|
'the runner process failed to boot the engine — run with onDiagnostic to see its stderr',
|
|
1121
1113
|
],
|
|
1122
1114
|
run: () => bridge.ready,
|
|
@@ -1255,7 +1247,7 @@ class NomosTestHarness {
|
|
|
1255
1247
|
/// ```dart
|
|
1256
1248
|
/// final cloud = await NomosTestCloud.start(serverEntry: devServer);
|
|
1257
1249
|
/// final h = await NomosTestHarness.cloudBound(
|
|
1258
|
-
/// cloud: cloud,
|
|
1250
|
+
/// cloud: cloud, lawUsdzPaths: ['build/co2.law.usdz'],
|
|
1259
1251
|
/// platformWorkspace: 'co2-platform', subject: 'user:test');
|
|
1260
1252
|
/// final home = h.homeSession; // the app's entry point
|
|
1261
1253
|
/// cloud.setOnline(false); // PARTITION
|
|
@@ -1266,7 +1258,7 @@ class NomosTestHarness {
|
|
|
1266
1258
|
/// ```
|
|
1267
1259
|
static Future<NomosTestHarness> cloudBound({
|
|
1268
1260
|
required NomosTestCloud cloud,
|
|
1269
|
-
required List<String>
|
|
1261
|
+
required List<String> lawUsdzPaths,
|
|
1270
1262
|
String platformWorkspace = 'test-platform',
|
|
1271
1263
|
required String subject,
|
|
1272
1264
|
String? authToken,
|
|
@@ -1282,9 +1274,9 @@ class NomosTestHarness {
|
|
|
1282
1274
|
Duration? phaseTimeout,
|
|
1283
1275
|
bool decoySession = true,
|
|
1284
1276
|
}) async {
|
|
1285
|
-
if (
|
|
1277
|
+
if (lawUsdzPaths.isEmpty) {
|
|
1286
1278
|
throw NomosTestHarnessException(
|
|
1287
|
-
'no
|
|
1279
|
+
'no lawUsdzPaths — pass the build/<name>.law.usdz `npx githolon compile` emits',
|
|
1288
1280
|
);
|
|
1289
1281
|
}
|
|
1290
1282
|
final entry = nodeEntry ??
|
|
@@ -1333,8 +1325,9 @@ class NomosTestHarness {
|
|
|
1333
1325
|
final workdir = await Directory.systemTemp.createTemp('nomos-harness-');
|
|
1334
1326
|
NodeRunnerTransport? transport;
|
|
1335
1327
|
try {
|
|
1336
|
-
// 1/6 —
|
|
1337
|
-
//
|
|
1328
|
+
// 1/6 — BOOT the BORN platform and inspect the law already installed by the test cloud's operator
|
|
1329
|
+
// birth lane. Raw host deployment is retired: the harness never uploads or interprets law.
|
|
1330
|
+
// ERA-0: the host does not create workspaces — the platform must have been stood
|
|
1338
1331
|
// up BY THE CLOUD at boot (`NomosTestCloud.start(platform: '$platformWorkspace')`, the dev
|
|
1339
1332
|
// server's in-process operator lane). Booting a born workspace is idempotent; an unborn one
|
|
1340
1333
|
// refuses 409, surfaced here with the fix.
|
|
@@ -1343,9 +1336,9 @@ class NomosTestHarness {
|
|
|
1343
1336
|
<({String domain, String directive, String path})>[];
|
|
1344
1337
|
final lawHashes = <String>[];
|
|
1345
1338
|
await _phased(
|
|
1346
|
-
phase: '1/6: boot the born platform +
|
|
1339
|
+
phase: '1/6: boot the born platform + inspect installed law',
|
|
1347
1340
|
workspace: platformWorkspace,
|
|
1348
|
-
awaiting: 'POST /v2/workspaces + /domains',
|
|
1341
|
+
awaiting: 'POST /v2/workspaces + GET /domains + /openapi.json',
|
|
1349
1342
|
timeout: phaseTimeout ?? _kInstallDeadline,
|
|
1350
1343
|
onDiagnostic: onDiagnostic,
|
|
1351
1344
|
run: () async {
|
|
@@ -1368,50 +1361,52 @@ class NomosTestHarness {
|
|
|
1368
1361
|
final existing = await get(
|
|
1369
1362
|
'/v2/workspaces/$platformWorkspace/domains',
|
|
1370
1363
|
);
|
|
1371
|
-
final
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
...(((existing['currentLaw'] as Map?) ?? const {})
|
|
1377
|
-
.values
|
|
1378
|
-
.map((hash) => hash?.toString())
|
|
1379
|
-
.whereType<String>()
|
|
1380
|
-
.where((hash) => hash.isNotEmpty)),
|
|
1381
|
-
};
|
|
1382
|
-
for (final p in deployJsonPaths) {
|
|
1383
|
-
final body = await File(p).readAsString();
|
|
1384
|
-
final deploy = (jsonDecode(body) as Map);
|
|
1385
|
-
final summary = (deploy['summary'] as Map?);
|
|
1386
|
-
final h = summary?['domainHash']?.toString();
|
|
1387
|
-
// A composed test application is normally installed atomically by the bootstrap operator.
|
|
1388
|
-
// The signed-in scenario participant must not need installDomain authority merely because the
|
|
1389
|
-
// harness was also handed those artifacts for routes, hashes and child-law selection.
|
|
1390
|
-
if (h == null || h.isEmpty || !heldHashes.contains(h)) {
|
|
1391
|
-
await post('/v2/workspaces/$platformWorkspace/domains', body);
|
|
1392
|
-
if (h != null && h.isNotEmpty) heldHashes.add(h);
|
|
1364
|
+
for (final path in lawUsdzPaths) {
|
|
1365
|
+
if (!File(path).existsSync()) {
|
|
1366
|
+
throw NomosTestHarnessException(
|
|
1367
|
+
'cloudBound: compiled law archive is missing: $path',
|
|
1368
|
+
);
|
|
1393
1369
|
}
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
final
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1370
|
+
}
|
|
1371
|
+
void rememberLaw(Object? value) {
|
|
1372
|
+
final hash = value?.toString() ?? '';
|
|
1373
|
+
if (hash.isNotEmpty &&
|
|
1374
|
+
hash != frameworkHash &&
|
|
1375
|
+
!lawHashes.contains(hash)) {
|
|
1376
|
+
lawHashes.add(hash);
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
// DomainInstallation rows preserve the operator-authored install order; currentLaw fills
|
|
1381
|
+
// any projection-era gap. These are authenticated kernel reads, not host package metadata.
|
|
1382
|
+
for (final row in ((existing['domains'] as List?) ?? const [])) {
|
|
1383
|
+
rememberLaw((row as Map?)?['domainHash']);
|
|
1384
|
+
}
|
|
1385
|
+
for (final hash
|
|
1386
|
+
in (((existing['currentLaw'] as Map?) ?? const {}).values)) {
|
|
1387
|
+
rememberLaw(hash);
|
|
1388
|
+
}
|
|
1389
|
+
final openapi = await get(
|
|
1390
|
+
'/v2/workspaces/$platformWorkspace/openapi.json',
|
|
1391
|
+
);
|
|
1392
|
+
final pathSets = <Map>[
|
|
1393
|
+
(openapi['paths'] as Map?) ?? const {},
|
|
1394
|
+
(((openapi['x-nomos-framework-routes'] as Map?)?['paths'])
|
|
1395
|
+
as Map?) ??
|
|
1396
|
+
const {},
|
|
1397
|
+
];
|
|
1398
|
+
for (final paths in pathSets) {
|
|
1399
|
+
for (final entry in paths.entries) {
|
|
1400
|
+
final operation = (entry.value as Map?)?['post'] as Map?;
|
|
1401
|
+
final route = operation?['x-nomos'] as Map?;
|
|
1402
|
+
final domain = route?['domain']?.toString() ?? '';
|
|
1403
|
+
final directive = route?['directiveId']?.toString() ?? '';
|
|
1404
|
+
if (domain.isNotEmpty && directive.isNotEmpty) {
|
|
1405
|
+
frameworkPostRoutes.add((
|
|
1406
|
+
domain: domain,
|
|
1407
|
+
directive: directive,
|
|
1408
|
+
path: entry.key.toString(),
|
|
1409
|
+
));
|
|
1415
1410
|
}
|
|
1416
1411
|
}
|
|
1417
1412
|
}
|
|
@@ -1424,7 +1419,7 @@ class NomosTestHarness {
|
|
|
1424
1419
|
}
|
|
1425
1420
|
if (lawHashes.isEmpty) {
|
|
1426
1421
|
throw NomosTestHarnessException(
|
|
1427
|
-
'cloudBound: no
|
|
1422
|
+
'cloudBound: the born test platform reported no installed application law — cannot run the birth ceremony',
|
|
1428
1423
|
);
|
|
1429
1424
|
}
|
|
1430
1425
|
final birthRoutes = frameworkPostRoutes
|
|
@@ -1440,7 +1435,6 @@ class NomosTestHarness {
|
|
|
1440
1435
|
);
|
|
1441
1436
|
}
|
|
1442
1437
|
final applicationHomeDomain = birthRoutes.single.domain;
|
|
1443
|
-
final birthReceivingGroupPath = birthRoutes.single.path;
|
|
1444
1438
|
// A composed application has two distinct laws at this seam:
|
|
1445
1439
|
// * the parent's CURRENT control law authors enrollFirstDevice + the birth offer;
|
|
1446
1440
|
// * the application's final PAYLOAD package is the law installed into the born receiving group.
|
|
@@ -1462,7 +1456,7 @@ class NomosTestHarness {
|
|
|
1462
1456
|
}
|
|
1463
1457
|
if (!lawHashes.contains(controlHomeLawHash)) {
|
|
1464
1458
|
throw NomosTestHarnessException(
|
|
1465
|
-
"cloudBound: current application/home control law '$controlHomeLawHash' was not among the
|
|
1459
|
+
"cloudBound: current application/home control law '$controlHomeLawHash' was not among the platform's installed packages",
|
|
1466
1460
|
);
|
|
1467
1461
|
}
|
|
1468
1462
|
final applicationHomeLawHash = lawHashes.last;
|
|
@@ -1479,7 +1473,11 @@ class NomosTestHarness {
|
|
|
1479
1473
|
"cloudBound: application/home domain '$applicationHomeDomain' must expose exactly one semantic enrollFirstDevice route; found ${enrollmentRoutes.length}",
|
|
1480
1474
|
);
|
|
1481
1475
|
}
|
|
1482
|
-
|
|
1476
|
+
if (enrollmentRoutes.single.path.isEmpty) {
|
|
1477
|
+
throw NomosTestHarnessException(
|
|
1478
|
+
'cloudBound: enrollFirstDevice generated an empty relay path',
|
|
1479
|
+
);
|
|
1480
|
+
}
|
|
1483
1481
|
// 2/6 — the LOCAL engine plane, CLOUD-BOUND, spawned BEFORE the birth: the runner's local kernel is
|
|
1484
1482
|
// what mints the device key + signs the birth cert (era-0: a birth is ATTESTED or not at all).
|
|
1485
1483
|
// The executable is device custody too. A process restart while offline must not fetch the kernel
|
|
@@ -1586,7 +1584,7 @@ class NomosTestHarness {
|
|
|
1586
1584
|
final issuer = (minted['issuer'] ?? '').toString();
|
|
1587
1585
|
if (idpToken.isEmpty || issuer.isEmpty) {
|
|
1588
1586
|
throw NomosTestHarnessException(
|
|
1589
|
-
'cloudBound: /dev/mint-token returned no token — is the test cloud started with platform:/
|
|
1587
|
+
'cloudBound: /dev/mint-token returned no token — is the test cloud started with platform:/lawUsdzPath: and a law composing setAuthProvider?',
|
|
1590
1588
|
);
|
|
1591
1589
|
}
|
|
1592
1590
|
// THE KEYLESS ENROLL-ONCE DELEGATION (links[1] of the home cert chain) — device-self-signed,
|
|
@@ -1605,20 +1603,44 @@ class NomosTestHarness {
|
|
|
1605
1603
|
'capturedClock': DateTime.now().toUtc().millisecondsSinceEpoch,
|
|
1606
1604
|
'epoch': 0,
|
|
1607
1605
|
}, secret);
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1606
|
+
// Mount the target workspace in the SAME kernel plane and author through its own
|
|
1607
|
+
// installed law. The HTTP host is neither given raw business fields nor a signing key;
|
|
1608
|
+
// sync carries the already-sealed offer and the receiving kernel replays the one gate.
|
|
1609
|
+
// The enroll route was still resolved above to prove the generated public surface
|
|
1610
|
+
// names this semantic route exactly once, but route strings grant no authority here.
|
|
1611
|
+
final platformAuthorSession = '$clientId-platform-author';
|
|
1612
|
+
await plane.connect(
|
|
1613
|
+
cloud: cloud.origin,
|
|
1614
|
+
workspace: platformWorkspace,
|
|
1615
|
+
session: platformAuthorSession,
|
|
1616
|
+
clientId: '$clientId-platform-author',
|
|
1617
|
+
authToken: idpToken,
|
|
1618
|
+
authorSecret: secret,
|
|
1619
|
+
);
|
|
1620
|
+
await plane.offerDirective(
|
|
1621
|
+
domain: applicationHomeDomain,
|
|
1622
|
+
directiveId: 'enrollFirstDevice',
|
|
1623
|
+
domainHash: controlHomeLawHash,
|
|
1624
|
+
actor: subject,
|
|
1625
|
+
authorSecret: secret,
|
|
1626
|
+
session: platformAuthorSession,
|
|
1627
|
+
payload: {
|
|
1611
1628
|
'keyHash': keyHash,
|
|
1612
1629
|
'verifiedVia': issuer,
|
|
1613
1630
|
'validFromEpoch': 0,
|
|
1614
1631
|
'grantedAt': DateTime.now().toUtc().toIso8601String(),
|
|
1615
1632
|
'idpToken': idpToken,
|
|
1616
|
-
'actor': subject,
|
|
1617
|
-
'authorSecret': secret,
|
|
1618
1633
|
'delegationCert': jsonEncode(delegationCert),
|
|
1619
1634
|
},
|
|
1620
|
-
headers: {'x-nomos-auth': idpToken},
|
|
1621
1635
|
);
|
|
1636
|
+
final enrollmentSync = await plane.sync(
|
|
1637
|
+
session: platformAuthorSession,
|
|
1638
|
+
);
|
|
1639
|
+
if (enrollmentSync['ok'] == false) {
|
|
1640
|
+
throw NomosTestHarnessException(
|
|
1641
|
+
'cloudBound: sealed enrollFirstDevice offer did not converge: $enrollmentSync',
|
|
1642
|
+
);
|
|
1643
|
+
}
|
|
1622
1644
|
return (
|
|
1623
1645
|
publicB64: publicB64,
|
|
1624
1646
|
secret: secret,
|
|
@@ -1627,21 +1649,26 @@ class NomosTestHarness {
|
|
|
1627
1649
|
);
|
|
1628
1650
|
},
|
|
1629
1651
|
);
|
|
1630
|
-
// 5/6 — THE HOME CEREMONY
|
|
1631
|
-
//
|
|
1632
|
-
//
|
|
1633
|
-
//
|
|
1652
|
+
// 5/6 — THE HOME CEREMONY. Author the birth in the already-mounted PARENT kernel, then sync its sealed
|
|
1653
|
+
// intent to the remote parent where the same offer gate replays it. The HTTP host never receives
|
|
1654
|
+
// business fields or a signing secret and has no birth-authoring lane. The offer carries the
|
|
1655
|
+
// device-signed homeCert + the owner's device keyHash (the home's genesis ENROLS it, so the born
|
|
1656
|
+
// home is SIGNER-READY — it can birth its own estates/catalogues from intent 0).
|
|
1634
1657
|
String? home;
|
|
1635
1658
|
await _phased(
|
|
1636
1659
|
phase: '5/6: remote home-birth ceremony (attested)',
|
|
1637
1660
|
workspace: platformWorkspace,
|
|
1638
|
-
awaiting:
|
|
1639
|
-
'POST /v2/workspaces/$platformWorkspace$birthReceivingGroupPath',
|
|
1661
|
+
awaiting: 'parent-kernel offer → sealed sync replay',
|
|
1640
1662
|
timeout: phaseTimeout ?? _kInstallDeadline,
|
|
1641
1663
|
onDiagnostic: onDiagnostic,
|
|
1642
1664
|
run: () async {
|
|
1643
1665
|
final homeName = _receivingGroupNameOf(subject);
|
|
1644
|
-
|
|
1666
|
+
// The plane deliberately still has both the empty onboarding scratch holon and the
|
|
1667
|
+
// authenticated platform holon mounted. Bind this parent certificate operation to the
|
|
1668
|
+
// platform session explicitly; relying on the single-session convenience API becomes
|
|
1669
|
+
// ambiguous as soon as the ceremony correctly opens the target workspace in-kernel.
|
|
1670
|
+
final homeCert =
|
|
1671
|
+
await plane.session('$clientId-platform-author').signBirthCert({
|
|
1645
1672
|
'child': homeName,
|
|
1646
1673
|
'founder': subject,
|
|
1647
1674
|
// The cert's `verified` must MATCH the gate verdict (CAS-as-law): the offer carries the
|
|
@@ -1651,33 +1678,54 @@ class NomosTestHarness {
|
|
|
1651
1678
|
'parentId': platformWorkspace,
|
|
1652
1679
|
'epoch': 0,
|
|
1653
1680
|
}, device.secret);
|
|
1654
|
-
final
|
|
1655
|
-
|
|
1656
|
-
|
|
1681
|
+
final localBirth =
|
|
1682
|
+
await plane.session('$clientId-platform-author').offerOutcome(
|
|
1683
|
+
domain: applicationHomeDomain,
|
|
1684
|
+
directiveId: 'nomosInternalBirthReceivingGroup7c1f5e',
|
|
1685
|
+
actor: subject,
|
|
1686
|
+
authorSecret: device.secret,
|
|
1687
|
+
payload: <String, Object?>{
|
|
1657
1688
|
'owner': subject,
|
|
1658
1689
|
'ownerKeyHash': device.keyHash,
|
|
1659
1690
|
'frameworkHash': frameworkHash,
|
|
1660
1691
|
'lawHash': applicationHomeLawHash,
|
|
1661
1692
|
'bornAt': DateTime.now().toUtc().toIso8601String(),
|
|
1662
1693
|
'homeCert': jsonEncode(homeCert),
|
|
1663
|
-
'actor': subject,
|
|
1664
|
-
'authorSecret': device.secret,
|
|
1665
1694
|
},
|
|
1666
|
-
headers: {'x-nomos-auth': device.idpToken},
|
|
1667
1695
|
);
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
.
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1696
|
+
if (!localBirth.born.contains(homeName)) {
|
|
1697
|
+
throw NomosTestHarnessException(
|
|
1698
|
+
'cloudBound: the local parent kernel admitted the receiving-group offer but did not birth '
|
|
1699
|
+
'"$homeName" (born=${localBirth.born}, birthOutcomes=${localBirth.birthOutcomes})',
|
|
1700
|
+
);
|
|
1701
|
+
}
|
|
1702
|
+
final birthSync = await plane.sync(
|
|
1703
|
+
session: '$clientId-platform-author',
|
|
1704
|
+
);
|
|
1705
|
+
if (birthSync['pushFailed'] == true ||
|
|
1706
|
+
birthSync['diverged'] == true ||
|
|
1707
|
+
birthSync['stillAhead'] == true) {
|
|
1708
|
+
throw NomosTestHarnessException(
|
|
1709
|
+
'cloudBound: the sealed receiving-group offer did not converge: $birthSync',
|
|
1710
|
+
);
|
|
1711
|
+
}
|
|
1712
|
+
final remoteOutcomes =
|
|
1713
|
+
((birthSync['birthOutcomes'] as List?) ?? const [])
|
|
1714
|
+
.whereType<Map>()
|
|
1715
|
+
.map((entry) => entry.cast<String, Object?>())
|
|
1716
|
+
.toList();
|
|
1717
|
+
final failedRemoteBirth = remoteOutcomes.where(
|
|
1718
|
+
(entry) =>
|
|
1719
|
+
entry['workspace']?.toString() == homeName &&
|
|
1720
|
+
entry['status']?.toString() != 'born',
|
|
1721
|
+
);
|
|
1722
|
+
if (failedRemoteBirth.isNotEmpty) {
|
|
1677
1723
|
throw NomosTestHarnessException(
|
|
1678
|
-
'cloudBound:
|
|
1724
|
+
'cloudBound: the remote parent replay refused the receiving-group birth: '
|
|
1725
|
+
'${jsonEncode(failedRemoteBirth.toList())}',
|
|
1679
1726
|
);
|
|
1680
1727
|
}
|
|
1728
|
+
home = homeName;
|
|
1681
1729
|
},
|
|
1682
1730
|
);
|
|
1683
1731
|
// 6/6 — mount the born home as a normal cloud-bound session (clone from the test cloud).
|
|
@@ -1770,8 +1818,7 @@ class NomosTestHarness {
|
|
|
1770
1818
|
// Every LIVE session on the plane (not the harness's construction-time list — a locally-born child
|
|
1771
1819
|
// acquired after construction must converge too), PARENT-FIRST: the home syncs before its children, so
|
|
1772
1820
|
// its birth offer-effect creates each child's remote custody BEFORE that child pushes its own writes.
|
|
1773
|
-
final live =
|
|
1774
|
-
bridge.connectedSessions.where((w) => w != _decoyWorkspace).toList();
|
|
1821
|
+
final live = _applicationConnectedSessions();
|
|
1775
1822
|
final hw = homeWorkspace;
|
|
1776
1823
|
if (hw != null && live.remove(hw)) live.insert(0, hw); // the parent leads
|
|
1777
1824
|
final targets = workspaces ?? live;
|
|
@@ -1821,6 +1868,23 @@ class NomosTestHarness {
|
|
|
1821
1868
|
return b is NomosSession ? b.parent : b;
|
|
1822
1869
|
}
|
|
1823
1870
|
|
|
1871
|
+
/// Live sessions which belong to the simulated application. The cloud-bound constructor temporarily
|
|
1872
|
+
/// mounts a scratch holon and the application parent to author the receiving-group ceremony. Those are
|
|
1873
|
+
/// deliberately on the same kernel plane, but they are not application workspaces to persist, converge,
|
|
1874
|
+
/// restart or reinstall. In particular, a session label is not necessarily its workspace address:
|
|
1875
|
+
/// replaying `test-runner-platform-author` as an address silently points at a workspace that never existed.
|
|
1876
|
+
List<String> _applicationConnectedSessions() {
|
|
1877
|
+
final lane = _cloudRestartLane;
|
|
1878
|
+
final ceremonySessions = <String>{
|
|
1879
|
+
_decoyWorkspace,
|
|
1880
|
+
if (lane != null) '${lane.clientId}-onboard-scratch',
|
|
1881
|
+
if (lane != null) '${lane.clientId}-platform-author',
|
|
1882
|
+
};
|
|
1883
|
+
return _plane.connectedSessions
|
|
1884
|
+
.where((session) => !ceremonySessions.contains(session))
|
|
1885
|
+
.toList();
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1824
1888
|
/// RESET a workspace's LOCAL custody to the snapshot it was seeded from — the "stale/corrupt
|
|
1825
1889
|
/// local custody" repair move in tests. Reconnects the session in place (watches on the old
|
|
1826
1890
|
/// holon are stopped by the runner's re-connect semantics).
|
|
@@ -1848,22 +1912,22 @@ class NomosTestHarness {
|
|
|
1848
1912
|
);
|
|
1849
1913
|
}
|
|
1850
1914
|
|
|
1851
|
-
/// SEED a NEW workspace on this plane from `build/<name>.
|
|
1915
|
+
/// SEED a NEW workspace on this plane from `build/<name>.law.usdz` law packages — the
|
|
1852
1916
|
/// mid-test twin of [cluster]'s per-workspace install: runs the SAME offline install lane,
|
|
1853
1917
|
/// then connects the workspace as its own session. Returns its [NomosSession].
|
|
1854
|
-
Future<NomosSession>
|
|
1918
|
+
Future<NomosSession> seedWorkspaceFromLawUsdz({
|
|
1855
1919
|
required String workspace,
|
|
1856
|
-
required List<String>
|
|
1920
|
+
required List<String> lawUsdzPaths,
|
|
1857
1921
|
}) async {
|
|
1858
1922
|
final lane = _installLane;
|
|
1859
1923
|
if (lane == null) {
|
|
1860
1924
|
throw NomosTestHarnessException(
|
|
1861
|
-
'
|
|
1925
|
+
'seedWorkspaceFromLawUsdz: this harness holds no install-lane config',
|
|
1862
1926
|
);
|
|
1863
1927
|
}
|
|
1864
|
-
if (
|
|
1928
|
+
if (lawUsdzPaths.isEmpty) {
|
|
1865
1929
|
throw NomosTestHarnessException(
|
|
1866
|
-
'
|
|
1930
|
+
'seedWorkspaceFromLawUsdz("$workspace"): pass the build/<name>.law.usdz paths',
|
|
1867
1931
|
);
|
|
1868
1932
|
}
|
|
1869
1933
|
final snap = '${_workdir.path}/$workspace.bin';
|
|
@@ -1880,7 +1944,7 @@ class NomosTestHarness {
|
|
|
1880
1944
|
useInRepoScript: lane.useInRepoScript,
|
|
1881
1945
|
inRepoScript: lane.inRepoScript,
|
|
1882
1946
|
githolonCmd: lane.githolonCmd,
|
|
1883
|
-
|
|
1947
|
+
lawUsdzPaths: lawUsdzPaths,
|
|
1884
1948
|
workspace: workspace,
|
|
1885
1949
|
snapshot: snap,
|
|
1886
1950
|
runtimeDir: lane.runtimeDir,
|
|
@@ -1973,9 +2037,7 @@ class NomosTestHarness {
|
|
|
1973
2037
|
/// carried per entry). The plane-level twin of [snapshotWorkspace] — migrate a whole staged AppSim plane.
|
|
1974
2038
|
Future<PlaneSnapshot> snapshotPlane() async {
|
|
1975
2039
|
final out = <String, WorkspaceSnapshot>{};
|
|
1976
|
-
final connected =
|
|
1977
|
-
(ws) => ws != _decoyWorkspace && !ws.endsWith('-onboard-scratch'),
|
|
1978
|
-
);
|
|
2040
|
+
final connected = _applicationConnectedSessions();
|
|
1979
2041
|
for (final ws in connected) {
|
|
1980
2042
|
out[ws] = await snapshotWorkspace(ws);
|
|
1981
2043
|
}
|
|
@@ -2037,11 +2099,7 @@ class NomosTestHarness {
|
|
|
2037
2099
|
}
|
|
2038
2100
|
|
|
2039
2101
|
final oldPlane = _plane;
|
|
2040
|
-
final targets =
|
|
2041
|
-
.where(
|
|
2042
|
-
(ws) => ws != _decoyWorkspace && !ws.endsWith('-onboard-scratch'),
|
|
2043
|
-
)
|
|
2044
|
-
.toList();
|
|
2102
|
+
final targets = _applicationConnectedSessions();
|
|
2045
2103
|
if (targets.remove(home)) targets.insert(0, home);
|
|
2046
2104
|
final snapshots = <String, String>{};
|
|
2047
2105
|
for (final ws in targets) {
|
|
@@ -2139,11 +2197,7 @@ class NomosTestHarness {
|
|
|
2139
2197
|
}
|
|
2140
2198
|
|
|
2141
2199
|
final oldPlane = _plane;
|
|
2142
|
-
final targets =
|
|
2143
|
-
.where(
|
|
2144
|
-
(ws) => ws != _decoyWorkspace && !ws.endsWith('-onboard-scratch'),
|
|
2145
|
-
)
|
|
2146
|
-
.toList();
|
|
2200
|
+
final targets = _applicationConnectedSessions();
|
|
2147
2201
|
if (targets.remove(home)) targets.insert(0, home);
|
|
2148
2202
|
for (final ws in targets) {
|
|
2149
2203
|
await oldPlane.session(ws).stopAutoSync();
|
|
@@ -2249,17 +2303,17 @@ class NomosTestHarness {
|
|
|
2249
2303
|
required bool useInRepoScript,
|
|
2250
2304
|
required String inRepoScript,
|
|
2251
2305
|
required String githolonCmd,
|
|
2252
|
-
required List<String>
|
|
2306
|
+
required List<String> lawUsdzPaths,
|
|
2253
2307
|
required String workspace,
|
|
2254
2308
|
required String snapshot,
|
|
2255
2309
|
String? runtimeDir,
|
|
2256
2310
|
void Function(String line)? onDiagnostic,
|
|
2257
2311
|
}) async {
|
|
2258
|
-
// Both lanes take the SAME flags (--
|
|
2312
|
+
// Both lanes take the SAME flags (--law/--workspace/--out/--runtime) and print the SAME one-line JSON
|
|
2259
2313
|
// contract; only the leading command differs (node <script> vs `<githolon...> harness-install`).
|
|
2260
2314
|
final common = <String>[
|
|
2261
|
-
'--
|
|
2262
|
-
|
|
2315
|
+
'--law',
|
|
2316
|
+
lawUsdzPaths.join(','),
|
|
2263
2317
|
'--workspace',
|
|
2264
2318
|
workspace,
|
|
2265
2319
|
'--out',
|