@githolon/dsl 0.113.4 → 0.115.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/assets/operation-contract.wasm +0 -0
- package/compile_package.mjs +23 -3
- package/compile_proof.d.mts +7 -0
- package/compile_proof.mjs +38 -3
- package/dart/CHANGELOG.md +60 -0
- package/dart/lib/src/application_framework_calls.dart +16 -6
- package/dart/lib/src/console_enrolment.dart +7 -0
- package/dart/lib/src/console_http_io.dart +22 -15
- package/dart/lib/src/dev_cloud.dart +62 -0
- package/dart/lib/src/generated_call.dart +14 -3
- package/dart/lib/src/nms_generated_runtime.dart +7 -0
- package/dart/lib/src/node_runner_transport.dart +77 -8
- package/dart/lib/src/nomos_trace.dart +107 -0
- package/dart/lib/src/operation_receipts.dart +74 -7
- package/dart/lib/src/reception/dart_reception.dart +174 -17
- package/dart/lib/src/rows_watch.dart +390 -0
- package/dart/lib/src/runner/node-entry.mjs +7678 -2860
- package/dart/lib/src/runtime_v2_transport.dart +3 -0
- package/dart/lib/src/sync_status_projection.dart +94 -1
- package/dart/lib/src/sync_status_reducer.dart +5 -2
- package/dart/lib/src/test_harness.dart +107 -41
- package/dart/lib/src/testing/test_peer.dart +107 -99
- package/dart/lib/src/testing/test_peer_host.dart +305 -0
- package/dart/lib/src/typed_watch_registry.dart +26 -3
- package/dart/pubspec.lock +1 -1
- package/dart/pubspec.yaml +2 -2
- package/dart/test/background_model_adoption_test.dart +1 -0
- package/dart/test/console_enrolment_test.dart +47 -0
- package/dart/test/custodian_warming_test.dart +105 -0
- package/dart/test/fixtures/offer_watch_order_peer.dart +74 -0
- package/dart/test/kernel_author_after_read_test.dart +6 -3
- package/dart/test/kernel_fence_test.dart +4 -1
- package/dart/test/model_adoption_test.dart +66 -0
- package/dart/test/nomos_trace_test.dart +91 -0
- package/dart/test/own_offer_watch_order_test.dart +90 -0
- package/dart/test/receiving_group_open_test.dart +4 -8
- package/dart/test/replica_readiness_test.dart +14 -4
- package/dart/test/rows_watch_test.dart +331 -0
- package/dart/test/runtime_projections_test.dart +41 -0
- package/dart/test/test_peer_host_selection_test.dart +49 -0
- package/dart_types/CHANGELOG.md +39 -0
- package/dart_types/lib/src/approved_model.dart +100 -6
- package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pb.dart +1063 -3
- package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbenum.dart +89 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pb.dart +14 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pb.dart +2272 -130
- package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pbenum.dart +65 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pb.dart +118 -2
- package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pb.dart +278 -9
- package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pb.dart +7 -85
- package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pb.dart +16 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pb.dart +35 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pb.dart +43 -2
- package/dart_types/pubspec.yaml +1 -1
- package/package.json +6 -2
- package/src/aggregate.ts +93 -0
- package/src/anonymous_value_interning.ts +205 -0
- package/src/baseline.ts +27 -0
- package/src/baseline_export.ts +432 -0
- package/src/baseline_import.ts +312 -0
- package/src/baseline_usda.ts +186 -0
- package/src/birth_key_rule.ts +472 -0
- package/src/births.ts +207 -24
- package/src/build_provenance.ts +17 -2
- package/src/business_birth.ts +50 -4
- package/src/check_package_main.ts +50 -6
- package/src/codegen_call_proto.ts +5 -18
- package/src/codegen_dart.ts +95 -3
- package/src/codegen_dart_client.ts +73 -15
- package/src/codegen_dart_facade.ts +58 -35
- package/src/codegen_dart_framework_calls.ts +32 -10
- package/src/codegen_dart_scenario.ts +7 -0
- package/src/codegen_proof.ts +5 -2
- package/src/codegen_result_proto.ts +8 -6
- package/src/codegen_result_usda.ts +4 -0
- package/src/codegen_ts.ts +135 -34
- package/src/compatibility_graph.ts +41 -4
- package/src/compatibility_usda.ts +11 -1
- package/src/compile_package_main.ts +272 -105
- package/src/composed_wire_identity.ts +24 -0
- package/src/directive.ts +6 -0
- package/src/domain_requirements.ts +75 -0
- package/src/engine_entry.ts +4 -48
- package/src/engine_javascript.ts +12 -6
- package/src/fields.ts +109 -0
- package/src/identity_usda.ts +77 -51
- package/src/index.ts +25 -1
- package/src/intent.ts +5 -0
- package/src/law_graph.ts +142 -1
- package/src/law_graph_usda.ts +25 -141
- package/src/law_layer_prims.ts +199 -0
- package/src/law_unit_selection.ts +45 -0
- package/src/law_unit_set_identity.ts +44 -0
- package/src/law_version.ts +107 -0
- package/src/package_config.ts +44 -0
- package/src/package_dependencies.ts +8 -0
- package/src/query/compile.ts +0 -0
- package/src/release_inspection.ts +24 -0
- package/src/release_publication.ts +298 -0
- package/src/value_object.ts +6 -3
- package/src/wire.ts +2 -0
- package/src/wire_paths.ts +17 -0
- package/src/workspace_routing.ts +1 -1
|
@@ -280,6 +280,9 @@ mixin NmsPlatformCredentialProviderHost
|
|
|
280
280
|
} on NomosConsoleEnrolmentRefused catch (refusal) {
|
|
281
281
|
return refused(refusal.status, refusal.message,
|
|
282
282
|
code: refusal.code, retryable: refusal.retryable);
|
|
283
|
+
} on TimeoutException {
|
|
284
|
+
return failed('the console did not answer within '
|
|
285
|
+
'${nomosConsoleEnrolmentTimeout.inSeconds} s; asking again');
|
|
283
286
|
} catch (error) {
|
|
284
287
|
// Never echo the request: it carries the token.
|
|
285
288
|
return failed('the console could not be reached (${error.runtimeType})');
|
|
@@ -46,6 +46,73 @@ enum SyncPhase {
|
|
|
46
46
|
error
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
/// The custodian is opening a workspace from verified custody — warming up after a restart,
|
|
50
|
+
/// a cold container or a cloud roll. It is not an error: nothing was refused, nothing is lost,
|
|
51
|
+
/// and the peer asks again at the custodian's own hint. A follow that carries this posture is
|
|
52
|
+
/// "waking up", never "needs attention", until [isStalled] says the wait has run well past what
|
|
53
|
+
/// the custodian promised.
|
|
54
|
+
final class CustodianWarmingPosture {
|
|
55
|
+
const CustodianWarmingPosture({
|
|
56
|
+
required this.since,
|
|
57
|
+
required this.expectedReadyAt,
|
|
58
|
+
required this.stalledAfter,
|
|
59
|
+
this.stalled = false,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
factory CustodianWarmingPosture.fromRuntime(
|
|
63
|
+
nomos_protocol.CustodianWarming warming,
|
|
64
|
+
) =>
|
|
65
|
+
CustodianWarmingPosture(
|
|
66
|
+
since: DateTime.fromMillisecondsSinceEpoch(
|
|
67
|
+
warming.sinceUnixMillis.toInt()),
|
|
68
|
+
expectedReadyAt: DateTime.fromMillisecondsSinceEpoch(
|
|
69
|
+
warming.expectedReadyUnixMillis.toInt()),
|
|
70
|
+
stalledAfter: DateTime.fromMillisecondsSinceEpoch(
|
|
71
|
+
warming.stalledAfterUnixMillis.toInt()),
|
|
72
|
+
stalled: warming.stalled,
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
/// When this peer first met the custodian's opening answer in this warming spell.
|
|
76
|
+
final DateTime since;
|
|
77
|
+
|
|
78
|
+
/// When the custodian said it expects to be ready (its latest retry hint).
|
|
79
|
+
final DateTime expectedReadyAt;
|
|
80
|
+
|
|
81
|
+
/// After this moment the warming has run well past the promise and counts as stalled.
|
|
82
|
+
final DateTime stalledAfter;
|
|
83
|
+
|
|
84
|
+
/// The peer had already judged the warming stalled when it reported it.
|
|
85
|
+
final bool stalled;
|
|
86
|
+
|
|
87
|
+
/// Stalled warming should escalate exactly like any other attention state. Judged against
|
|
88
|
+
/// [now] as well as the peer's report, so a quiet report still escalates on time.
|
|
89
|
+
bool isStalled(DateTime now) => stalled || !now.isBefore(stalledAfter);
|
|
90
|
+
|
|
91
|
+
/// How long the custodian has been warming up.
|
|
92
|
+
Duration elapsed(DateTime now) {
|
|
93
|
+
final value = now.difference(since);
|
|
94
|
+
return value.isNegative ? Duration.zero : value;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/// How long until the custodian expects to be ready; zero once that time has passed.
|
|
98
|
+
Duration remaining(DateTime now) {
|
|
99
|
+
final value = expectedReadyAt.difference(now);
|
|
100
|
+
return value.isNegative ? Duration.zero : value;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@override
|
|
104
|
+
bool operator ==(Object other) =>
|
|
105
|
+
other is CustodianWarmingPosture &&
|
|
106
|
+
other.since == since &&
|
|
107
|
+
other.expectedReadyAt == expectedReadyAt &&
|
|
108
|
+
other.stalledAfter == stalledAfter &&
|
|
109
|
+
other.stalled == stalled;
|
|
110
|
+
|
|
111
|
+
@override
|
|
112
|
+
int get hashCode =>
|
|
113
|
+
Object.hash(since, expectedReadyAt, stalledAfter, stalled);
|
|
114
|
+
}
|
|
115
|
+
|
|
49
116
|
/// The durable Replica Follow's own state, observed without starting a round.
|
|
50
117
|
enum WorkspaceFollowPhase {
|
|
51
118
|
desired,
|
|
@@ -74,6 +141,7 @@ final class WorkspaceFollowStatus {
|
|
|
74
141
|
this.activeEffect,
|
|
75
142
|
this.unsent,
|
|
76
143
|
this.unacknowledged,
|
|
144
|
+
this.custodianWarming,
|
|
77
145
|
});
|
|
78
146
|
|
|
79
147
|
factory WorkspaceFollowStatus.fromRuntime(
|
|
@@ -131,6 +199,9 @@ final class WorkspaceFollowStatus {
|
|
|
131
199
|
unsent: report.hasUnsent() ? report.unsent : null,
|
|
132
200
|
unacknowledged:
|
|
133
201
|
report.hasUnacknowledged() ? report.unacknowledged : null,
|
|
202
|
+
custodianWarming: report.hasCustodianWarming()
|
|
203
|
+
? CustodianWarmingPosture.fromRuntime(report.custodianWarming)
|
|
204
|
+
: null,
|
|
134
205
|
);
|
|
135
206
|
|
|
136
207
|
final String workspace;
|
|
@@ -146,7 +217,20 @@ final class WorkspaceFollowStatus {
|
|
|
146
217
|
/// whose admission has not yet come back. Null when the live link is not streaming.
|
|
147
218
|
final int? unsent;
|
|
148
219
|
final int? unacknowledged;
|
|
220
|
+
|
|
221
|
+
/// Present while the custodian is opening this workspace from verified custody and that is
|
|
222
|
+
/// the only reason this follow is not yet current. Null for every ordinary error retry.
|
|
223
|
+
final CustodianWarmingPosture? custodianWarming;
|
|
224
|
+
|
|
149
225
|
bool get current => phase == WorkspaceFollowPhase.settled && live;
|
|
226
|
+
|
|
227
|
+
/// The custodian is warming up and has not yet overrun its promise: calm, not an error.
|
|
228
|
+
bool isWarming(DateTime now) =>
|
|
229
|
+
!current && custodianWarming != null && !custodianWarming!.isStalled(now);
|
|
230
|
+
|
|
231
|
+
/// The custodian has been warming well past what it promised: escalate as attention.
|
|
232
|
+
bool isWarmingStalled(DateTime now) =>
|
|
233
|
+
!current && (custodianWarming?.isStalled(now) ?? false);
|
|
150
234
|
}
|
|
151
235
|
|
|
152
236
|
/// Disposable UI projection of typed peer-runtime sync observations.
|
|
@@ -192,6 +276,14 @@ final class SyncStatus {
|
|
|
192
276
|
/// The peer's time for its most recent changed follow report.
|
|
193
277
|
final DateTime? followReportAt;
|
|
194
278
|
|
|
279
|
+
/// Follows whose custodian is warming up within its promise: calm, not an error.
|
|
280
|
+
Iterable<WorkspaceFollowStatus> warmingFollows(DateTime now) =>
|
|
281
|
+
followStatuses.where((follow) => follow.isWarming(now));
|
|
282
|
+
|
|
283
|
+
/// Follows whose custodian has been warming well past its promise: attention.
|
|
284
|
+
Iterable<WorkspaceFollowStatus> stalledWarmingFollows(DateTime now) =>
|
|
285
|
+
followStatuses.where((follow) => follow.isWarmingStalled(now));
|
|
286
|
+
|
|
195
287
|
/// Counts of Peer Office preparation observations, not inferred from sync.
|
|
196
288
|
final int preparingWorkspaces;
|
|
197
289
|
final int retryingWorkspaces;
|
|
@@ -298,7 +390,8 @@ bool _sameFollows(
|
|
|
298
390
|
a.attempt != b.attempt ||
|
|
299
391
|
a.activeEffect != b.activeEffect ||
|
|
300
392
|
a.unsent != b.unsent ||
|
|
301
|
-
a.unacknowledged != b.unacknowledged
|
|
393
|
+
a.unacknowledged != b.unacknowledged ||
|
|
394
|
+
a.custodianWarming != b.custodianWarming) return false;
|
|
302
395
|
}
|
|
303
396
|
return true;
|
|
304
397
|
}
|
|
@@ -42,8 +42,11 @@ SyncStatus reduceSyncCompletion(
|
|
|
42
42
|
}
|
|
43
43
|
final result = completed.result;
|
|
44
44
|
final receiptIssue = _receiptIssue(result.offerReceipt);
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
// A custodian warming up judged nothing: the round is still owed, never settled or failed.
|
|
46
|
+
final pending = result.stillAhead ||
|
|
47
|
+
result.directOfferDeferred ||
|
|
48
|
+
result.deferred > 0 ||
|
|
49
|
+
result.hasCustodianWarmingRetryAfterMillis();
|
|
47
50
|
final awaitingRemoteDurability = result.stillAhead &&
|
|
48
51
|
!result.directOfferDeferred &&
|
|
49
52
|
!result.reconnectRequired &&
|
|
@@ -53,7 +53,10 @@ import 'runtime_v2_transport.dart';
|
|
|
53
53
|
import 'session_bound.dart' show DomainNotInstalledForSession;
|
|
54
54
|
import 'reception/dart_reception.dart'
|
|
55
55
|
show DartReception, NomosBoundInteraction, NomosSession;
|
|
56
|
+
import 'reception/peer_office_connection.dart' show PeerOfficeConnection;
|
|
56
57
|
import 'testing/test_peer.dart' show NmsTestPeer;
|
|
58
|
+
import 'testing/test_peer_host.dart'
|
|
59
|
+
show NmsTestPeerHostKind, nmsTestPeerHostVariable;
|
|
57
60
|
|
|
58
61
|
enum _KernelRuntimeArtifact {
|
|
59
62
|
executable('holon.wasm'),
|
|
@@ -325,7 +328,9 @@ class NomosTestHarness {
|
|
|
325
328
|
/// The local workspace name the law was installed into.
|
|
326
329
|
final String workspace;
|
|
327
330
|
|
|
328
|
-
|
|
331
|
+
/// The Peer Office connection under [reception]: the Node runner, or the Apple native
|
|
332
|
+
/// host when the local test peer runs there (`NOMOS_TEST_PEER_HOST=native`).
|
|
333
|
+
PeerOfficeConnection _transport;
|
|
329
334
|
final Directory _workdir;
|
|
330
335
|
final void Function(String line)? _onDiagnostic;
|
|
331
336
|
bool _disposed = false;
|
|
@@ -541,14 +546,15 @@ class NomosTestHarness {
|
|
|
541
546
|
'no lawUsdzPaths — pass the build/<name>.law.usdz `npx githolon compile` emits',
|
|
542
547
|
);
|
|
543
548
|
}
|
|
544
|
-
final
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
549
|
+
final hostKind = NmsTestPeerHostKind.selected();
|
|
550
|
+
final (:entry, :runtimeDirectory) = await _localPeerInputs(
|
|
551
|
+
hostKind: hostKind,
|
|
552
|
+
nodeEntry: nodeEntry,
|
|
553
|
+
runtimeDir: runtimeDir,
|
|
554
|
+
node: node,
|
|
555
|
+
resolveFrom: resolveFrom,
|
|
556
|
+
);
|
|
557
|
+
final effectiveRuntimeDir = runtimeDirectory;
|
|
552
558
|
final owner = ownerSubject?.trim();
|
|
553
559
|
// THE CALLER DECLARES ITS LANE. Naming an owner IS the application-home lane;
|
|
554
560
|
// the harness no longer re-derives a receiving-group name to recognise one by
|
|
@@ -574,18 +580,19 @@ class NomosTestHarness {
|
|
|
574
580
|
? 'nms-app-${DateTime.now().microsecondsSinceEpoch.toRadixString(36)}'
|
|
575
581
|
: (workspace ?? 'test-ws');
|
|
576
582
|
|
|
577
|
-
|
|
578
|
-
NodeRunnerTransport? transport;
|
|
583
|
+
NmsTestPeer? startedPeer;
|
|
579
584
|
final phase = (String m) {
|
|
580
585
|
if (onDiagnostic != null) onDiagnostic('[nomos-test-harness] $m');
|
|
581
586
|
};
|
|
587
|
+
phase('test peer host: ${hostKind.name} ($nmsTestPeerHostVariable)');
|
|
582
588
|
try {
|
|
583
|
-
final peer = await _phased(
|
|
589
|
+
final NmsTestPeer peer = await _phased(
|
|
584
590
|
phase: '1/2: open canonical test peer',
|
|
585
591
|
awaiting: 'Peer Office + filesystem Git custody',
|
|
586
592
|
timeout: phaseTimeout ?? _kSpawnDeadline,
|
|
587
593
|
onDiagnostic: onDiagnostic,
|
|
588
594
|
run: () => NmsTestPeer.start(
|
|
595
|
+
kind: hostKind,
|
|
589
596
|
entry: entry,
|
|
590
597
|
runtimeDirectory: effectiveRuntimeDir,
|
|
591
598
|
resolveFrom: resolveFrom,
|
|
@@ -593,25 +600,26 @@ class NomosTestHarness {
|
|
|
593
600
|
onDiagnostic: onDiagnostic,
|
|
594
601
|
),
|
|
595
602
|
);
|
|
596
|
-
|
|
597
|
-
workdir = peer.
|
|
603
|
+
startedPeer = peer;
|
|
604
|
+
final workdir = peer.custodyDirectory;
|
|
598
605
|
var plane = peer.reception;
|
|
599
606
|
// An application home is warranted by certificates the founding device signs, so that key is
|
|
600
|
-
// minted FIRST and the peer is
|
|
607
|
+
// minted FIRST and the peer is made to trust it (typed boot configuration) before genesis.
|
|
601
608
|
protocol.SigningKeyPair? founder;
|
|
602
609
|
if (birthsApplicationHome) {
|
|
603
610
|
founder = await _phased(
|
|
604
|
-
phase: '1b/2: mint the founding device and
|
|
605
|
-
awaiting:
|
|
611
|
+
phase: '1b/2: mint the founding device and make the peer trust it',
|
|
612
|
+
awaiting: hostKind == NmsTestPeerHostKind.node
|
|
613
|
+
? 'authorKeygen + peer reboot with --trust-root-public-key'
|
|
614
|
+
: 'authorKeygen + resident kernel trust-root configuration',
|
|
606
615
|
timeout: phaseTimeout ?? _kSpawnDeadline,
|
|
607
616
|
onDiagnostic: onDiagnostic,
|
|
608
617
|
run: () async {
|
|
609
618
|
final key = await peer.mintFoundingDevice();
|
|
610
|
-
await peer.
|
|
619
|
+
await peer.trustFoundingRoot(key.publicKey);
|
|
611
620
|
return key;
|
|
612
621
|
},
|
|
613
622
|
);
|
|
614
|
-
transport = peer.transport;
|
|
615
623
|
plane = peer.reception;
|
|
616
624
|
}
|
|
617
625
|
// THE OFFICE NAMES THE APPLICATION HOME. The harness holds no copy of the
|
|
@@ -1008,7 +1016,7 @@ class NomosTestHarness {
|
|
|
1008
1016
|
final harness = NomosTestHarness._(
|
|
1009
1017
|
plane.session(openWorkspace),
|
|
1010
1018
|
openWorkspace,
|
|
1011
|
-
transport,
|
|
1019
|
+
peer.transport,
|
|
1012
1020
|
workdir,
|
|
1013
1021
|
onDiagnostic,
|
|
1014
1022
|
[openWorkspace],
|
|
@@ -1042,18 +1050,46 @@ class NomosTestHarness {
|
|
|
1042
1050
|
return harness;
|
|
1043
1051
|
} catch (_) {
|
|
1044
1052
|
// Setup failed — tear down deterministically so nothing is left running or on disk.
|
|
1045
|
-
if (transport != null) {
|
|
1046
|
-
try {
|
|
1047
|
-
await transport.dispose();
|
|
1048
|
-
} catch (_) {}
|
|
1049
|
-
}
|
|
1050
1053
|
try {
|
|
1051
|
-
await
|
|
1054
|
+
await startedPeer?.dispose();
|
|
1052
1055
|
} catch (_) {}
|
|
1053
1056
|
rethrow;
|
|
1054
1057
|
}
|
|
1055
1058
|
}
|
|
1056
1059
|
|
|
1060
|
+
/// The local test peer's inputs for [hostKind]. The Node host needs its runner entry (and may
|
|
1061
|
+
/// take the source checkout's coordinated candidate runtime from it); the native host runs no
|
|
1062
|
+
/// Node, so no entry is resolved for it. Both read the framework controller law from the
|
|
1063
|
+
/// runtime directory.
|
|
1064
|
+
static Future<({String? entry, String runtimeDirectory})> _localPeerInputs({
|
|
1065
|
+
required NmsTestPeerHostKind hostKind,
|
|
1066
|
+
required String? nodeEntry,
|
|
1067
|
+
required String? runtimeDir,
|
|
1068
|
+
required String node,
|
|
1069
|
+
required String? resolveFrom,
|
|
1070
|
+
}) async {
|
|
1071
|
+
final explicitRuntime = runtimeDir ??
|
|
1072
|
+
Platform.environment['NOMOS_CANDIDATE_RUNTIME'] ??
|
|
1073
|
+
Platform.environment['NOMOS_DART_RUNTIME'] ??
|
|
1074
|
+
Platform.environment['NOMOS_OFFLINE_RUNTIME'];
|
|
1075
|
+
final defaultRuntime =
|
|
1076
|
+
'${Platform.environment['HOME'] ?? ''}/.holon/runtime';
|
|
1077
|
+
switch (hostKind) {
|
|
1078
|
+
case NmsTestPeerHostKind.node:
|
|
1079
|
+
final entry = nodeEntry ??
|
|
1080
|
+
await _resolveTestEntry(
|
|
1081
|
+
node, resolveFrom ?? Directory.current.path);
|
|
1082
|
+
return (
|
|
1083
|
+
entry: entry,
|
|
1084
|
+
runtimeDirectory: explicitRuntime ??
|
|
1085
|
+
await _sourceCandidateRuntime(entry) ??
|
|
1086
|
+
defaultRuntime,
|
|
1087
|
+
);
|
|
1088
|
+
case NmsTestPeerHostKind.native:
|
|
1089
|
+
return (entry: null, runtimeDirectory: explicitRuntime ?? defaultRuntime);
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1057
1093
|
/// A COLOCATED TEST CLUSTER — install MANY workspaces' law offline and connect one SESSION per workspace on
|
|
1058
1094
|
/// ONE kernel host (one runner, one transport, one [reception]), so a cross-workspace scenario (attested
|
|
1059
1095
|
/// reads, foreign-source imports) runs in-process with ZERO network. Each workspace has separate real Git
|
|
@@ -1101,14 +1137,18 @@ class NomosTestHarness {
|
|
|
1101
1137
|
);
|
|
1102
1138
|
}
|
|
1103
1139
|
}
|
|
1104
|
-
final
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1140
|
+
final hostKind = NmsTestPeerHostKind.selected();
|
|
1141
|
+
final (:entry, :runtimeDirectory) = await _localPeerInputs(
|
|
1142
|
+
hostKind: hostKind,
|
|
1143
|
+
nodeEntry: nodeEntry,
|
|
1144
|
+
runtimeDir: runtimeDir,
|
|
1145
|
+
node: node,
|
|
1146
|
+
resolveFrom: resolveFrom,
|
|
1147
|
+
);
|
|
1148
|
+
final effectiveRuntimeDir = runtimeDirectory;
|
|
1149
|
+
onDiagnostic?.call(
|
|
1150
|
+
'[nomos-test-harness] test peer host: ${hostKind.name} ($nmsTestPeerHostVariable)',
|
|
1151
|
+
);
|
|
1112
1152
|
NmsTestPeer? peer;
|
|
1113
1153
|
try {
|
|
1114
1154
|
peer = await _phased(
|
|
@@ -1117,6 +1157,7 @@ class NomosTestHarness {
|
|
|
1117
1157
|
timeout: phaseTimeout ?? _kSpawnDeadline,
|
|
1118
1158
|
onDiagnostic: onDiagnostic,
|
|
1119
1159
|
run: () => NmsTestPeer.start(
|
|
1160
|
+
kind: hostKind,
|
|
1120
1161
|
entry: entry,
|
|
1121
1162
|
runtimeDirectory: effectiveRuntimeDir,
|
|
1122
1163
|
resolveFrom: resolveFrom,
|
|
@@ -1153,7 +1194,7 @@ class NomosTestHarness {
|
|
|
1153
1194
|
peer!.reception,
|
|
1154
1195
|
names.first,
|
|
1155
1196
|
peer.transport,
|
|
1156
|
-
peer.
|
|
1197
|
+
peer.custodyDirectory,
|
|
1157
1198
|
onDiagnostic,
|
|
1158
1199
|
names,
|
|
1159
1200
|
devicePublics: devicePublics,
|
|
@@ -1230,6 +1271,14 @@ class NomosTestHarness {
|
|
|
1230
1271
|
'NmsApplicationFrameworkCalls; raw framework directive transport is not supported',
|
|
1231
1272
|
);
|
|
1232
1273
|
}
|
|
1274
|
+
// The cloud-bound device is the Node peer talking to a test cloud; there is no native lane
|
|
1275
|
+
// for it. Selecting native here is refused, never quietly run on Node.
|
|
1276
|
+
if (NmsTestPeerHostKind.selected() != NmsTestPeerHostKind.node) {
|
|
1277
|
+
throw NomosTestHarnessException(
|
|
1278
|
+
'cloudBound runs only on the Node test peer: unset $nmsTestPeerHostVariable '
|
|
1279
|
+
'(the native host serves the offline ephemeral and cluster harnesses)',
|
|
1280
|
+
);
|
|
1281
|
+
}
|
|
1233
1282
|
final entry = nodeEntry ??
|
|
1234
1283
|
await _resolveEntry(node, resolveFrom ?? Directory.current.path);
|
|
1235
1284
|
final bareUid =
|
|
@@ -2069,8 +2118,7 @@ class NomosTestHarness {
|
|
|
2069
2118
|
if (!RegExp(r'^[A-Za-z0-9_-]+$').hasMatch(fixture.workspace)) {
|
|
2070
2119
|
throw StateError('Invalid fixture workspace');
|
|
2071
2120
|
}
|
|
2072
|
-
final repo =
|
|
2073
|
-
'${peer.custody.directory.path}/custody/holon-${fixture.workspace}.git';
|
|
2121
|
+
final repo = peer.host.workspaceGitDirectory(fixture.workspace);
|
|
2074
2122
|
// `update-ref <new> <old>` is a compare-and-swap, and git reads an
|
|
2075
2123
|
// EMPTY <old> as "the ref must not exist". `prior` comes from the
|
|
2076
2124
|
// plane's workspace status, which answers '' whenever the workspace
|
|
@@ -2170,7 +2218,7 @@ class NomosTestHarness {
|
|
|
2170
2218
|
final targets = _applicationConnectedSessions();
|
|
2171
2219
|
if (targets.remove(home)) targets.insert(0, home);
|
|
2172
2220
|
await _plane.dispose();
|
|
2173
|
-
await
|
|
2221
|
+
await _stopTransport();
|
|
2174
2222
|
|
|
2175
2223
|
_transport = await NodeRunnerTransport.start(
|
|
2176
2224
|
entry: lane.entry,
|
|
@@ -2245,13 +2293,13 @@ class NomosTestHarness {
|
|
|
2245
2293
|
);
|
|
2246
2294
|
}
|
|
2247
2295
|
try {
|
|
2248
|
-
await
|
|
2296
|
+
await _stopTransport().timeout(
|
|
2249
2297
|
_phaseTimeout ?? _kOtherDeadline,
|
|
2250
2298
|
onTimeout: () => throw HarnessPhaseTimeout(
|
|
2251
|
-
phase: 'dispose: stop the
|
|
2299
|
+
phase: 'dispose: stop the peer host',
|
|
2252
2300
|
workspace: workspace,
|
|
2253
2301
|
elapsed: _phaseTimeout ?? _kOtherDeadline,
|
|
2254
|
-
awaiting: '
|
|
2302
|
+
awaiting: 'test peer host stop',
|
|
2255
2303
|
),
|
|
2256
2304
|
);
|
|
2257
2305
|
} catch (e) {
|
|
@@ -2267,6 +2315,24 @@ class NomosTestHarness {
|
|
|
2267
2315
|
if (f != null) f('[nomos-test-harness] phase dispose done');
|
|
2268
2316
|
}
|
|
2269
2317
|
|
|
2318
|
+
/// Stop whatever hosts [_transport]: the local test peer's host when this harness owns one
|
|
2319
|
+
/// (its connection may have been replaced by a restart), otherwise the cloud-bound Node runner.
|
|
2320
|
+
Future<void> _stopTransport() async {
|
|
2321
|
+
final peer = _testPeer;
|
|
2322
|
+
if (peer != null && identical(peer.transport, _transport)) {
|
|
2323
|
+
await peer.host.stop();
|
|
2324
|
+
return;
|
|
2325
|
+
}
|
|
2326
|
+
final transport = _transport;
|
|
2327
|
+
if (transport is NodeRunnerTransport) {
|
|
2328
|
+
await transport.dispose();
|
|
2329
|
+
return;
|
|
2330
|
+
}
|
|
2331
|
+
throw StateError(
|
|
2332
|
+
'the harness connection ${transport.runtimeType} has no owner to stop it',
|
|
2333
|
+
);
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2270
2336
|
void _diag(String line) {
|
|
2271
2337
|
final f = _onDiagnostic;
|
|
2272
2338
|
if (f != null) {
|