@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
|
@@ -10,9 +10,9 @@ import 'package:fixnum/fixnum.dart' show Int64;
|
|
|
10
10
|
import 'package:meta/meta.dart' show internal;
|
|
11
11
|
import 'package:nomos_types/nomos_kernel_protocol.dart' as protocol;
|
|
12
12
|
|
|
13
|
-
import '../node_runner_transport.dart';
|
|
14
13
|
import '../reception/dart_reception.dart';
|
|
15
|
-
import '
|
|
14
|
+
import '../reception/peer_office_connection.dart';
|
|
15
|
+
import 'test_peer_host.dart';
|
|
16
16
|
|
|
17
17
|
@internal
|
|
18
18
|
final class NmsInstalledTestWorkspace {
|
|
@@ -31,78 +31,88 @@ final class NmsInstalledTestWorkspace {
|
|
|
31
31
|
final Set<String> domainKeys;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
/// A canonical Peer Office hosted by
|
|
34
|
+
/// A canonical Peer Office hosted by one test Platform Shell ([NmsTestPeerHost]).
|
|
35
35
|
///
|
|
36
36
|
/// This class owns fixture setup only. Every operation crosses Runtime v2 and
|
|
37
37
|
/// the kernel's generated Protobuf ABI; it contains no workspace lifecycle,
|
|
38
|
-
/// USDA parser, generic payload map or alternate admission path.
|
|
38
|
+
/// USDA parser, generic payload map or alternate admission path. The host is the
|
|
39
|
+
/// Node shell by default and the Apple native host when `NOMOS_TEST_PEER_HOST=native`.
|
|
39
40
|
@internal
|
|
40
41
|
final class NmsTestPeer {
|
|
41
42
|
NmsTestPeer._({
|
|
42
|
-
required this.
|
|
43
|
+
required this.host,
|
|
43
44
|
required this.reception,
|
|
44
|
-
required this.custody,
|
|
45
45
|
required this.runtimeDirectory,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}) : _entry = entry,
|
|
51
|
-
_node = node,
|
|
52
|
-
_resolveFrom = resolveFrom,
|
|
53
|
-
_onDiagnostic = onDiagnostic;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
/// The platform host this peer stands on.
|
|
49
|
+
final NmsTestPeerHost host;
|
|
54
50
|
|
|
55
|
-
/// The live
|
|
56
|
-
|
|
51
|
+
/// The live Peer Office connection; replaced when the host restarts.
|
|
52
|
+
PeerOfficeConnection get transport => host.connection;
|
|
57
53
|
|
|
58
|
-
/// Reception over [transport]; replaced by [reboot].
|
|
54
|
+
/// Reception over [transport]; replaced by [reboot] and [trustFoundingRoot].
|
|
59
55
|
DartReception reception;
|
|
60
|
-
|
|
56
|
+
|
|
57
|
+
/// The temporary directory holding this peer's Git custody.
|
|
58
|
+
Directory get custodyDirectory => host.custodyDirectory;
|
|
61
59
|
final String runtimeDirectory;
|
|
62
|
-
final String _entry;
|
|
63
|
-
final String _node;
|
|
64
|
-
final String? _resolveFrom;
|
|
65
|
-
final void Function(String line)? _onDiagnostic;
|
|
66
60
|
final Random _random = Random.secure();
|
|
67
61
|
int _requestSequence = 0;
|
|
68
62
|
int _authoringSequence = 0;
|
|
69
63
|
|
|
70
|
-
|
|
71
|
-
|
|
64
|
+
/// Open a canonical test peer on an already started [host]. The peer owns the host from
|
|
65
|
+
/// here on: a failure disposes it.
|
|
66
|
+
static Future<NmsTestPeer> open({
|
|
67
|
+
required NmsTestPeerHost host,
|
|
72
68
|
required String runtimeDirectory,
|
|
73
|
-
String node = 'node',
|
|
74
|
-
String? resolveFrom,
|
|
75
|
-
void Function(String line)? onDiagnostic,
|
|
76
69
|
}) async {
|
|
77
|
-
final custody = await NmsTestCustody.create();
|
|
78
|
-
NodeRunnerTransport? transport;
|
|
79
70
|
try {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
resolveFrom: resolveFrom,
|
|
83
|
-
node: node,
|
|
84
|
-
arguments: custody.processArguments(runtimeDirectory),
|
|
85
|
-
onDiagnostic: onDiagnostic,
|
|
86
|
-
);
|
|
87
|
-
final reception = DartReception(transport);
|
|
88
|
-
await reception.ready;
|
|
71
|
+
final reception = DartReception(host.connection);
|
|
72
|
+
await host.awaitReady(reception);
|
|
89
73
|
return NmsTestPeer._(
|
|
90
|
-
|
|
74
|
+
host: host,
|
|
91
75
|
reception: reception,
|
|
92
|
-
custody: custody,
|
|
93
76
|
runtimeDirectory: runtimeDirectory,
|
|
94
|
-
entry: entry,
|
|
95
|
-
node: node,
|
|
96
|
-
resolveFrom: resolveFrom,
|
|
97
|
-
onDiagnostic: onDiagnostic,
|
|
98
77
|
);
|
|
99
78
|
} catch (_) {
|
|
100
|
-
await
|
|
101
|
-
await custody.dispose();
|
|
79
|
+
await host.dispose();
|
|
102
80
|
rethrow;
|
|
103
81
|
}
|
|
104
82
|
}
|
|
105
83
|
|
|
84
|
+
/// Start a test peer on the host [kind]: the Node shell at [entry] (required exactly for
|
|
85
|
+
/// Node), or the registered Apple native host.
|
|
86
|
+
static Future<NmsTestPeer> start({
|
|
87
|
+
required NmsTestPeerHostKind kind,
|
|
88
|
+
required String runtimeDirectory,
|
|
89
|
+
String? entry,
|
|
90
|
+
String node = 'node',
|
|
91
|
+
String? resolveFrom,
|
|
92
|
+
void Function(String line)? onDiagnostic,
|
|
93
|
+
}) async {
|
|
94
|
+
final request = NmsTestPeerHostRequest(
|
|
95
|
+
runtimeDirectory: runtimeDirectory,
|
|
96
|
+
onDiagnostic: onDiagnostic,
|
|
97
|
+
);
|
|
98
|
+
final NmsTestPeerHost host;
|
|
99
|
+
switch (kind) {
|
|
100
|
+
case NmsTestPeerHostKind.node:
|
|
101
|
+
if (entry == null) {
|
|
102
|
+
throw ArgumentError.notNull('entry');
|
|
103
|
+
}
|
|
104
|
+
host = await NmsNodeTestPeerHost.start(
|
|
105
|
+
entry: entry,
|
|
106
|
+
request: request,
|
|
107
|
+
node: node,
|
|
108
|
+
resolveFrom: resolveFrom,
|
|
109
|
+
);
|
|
110
|
+
case NmsTestPeerHostKind.native:
|
|
111
|
+
host = await NmsTestPeerHosts.requireNative()(request);
|
|
112
|
+
}
|
|
113
|
+
return open(host: host, runtimeDirectory: runtimeDirectory);
|
|
114
|
+
}
|
|
115
|
+
|
|
106
116
|
protocol.AuthoringOptions _authoring(String actor) {
|
|
107
117
|
_authoringSequence += 1;
|
|
108
118
|
return protocol.AuthoringOptions(
|
|
@@ -123,7 +133,7 @@ final class NmsTestPeer {
|
|
|
123
133
|
String session,
|
|
124
134
|
protocol.CallRequest request,
|
|
125
135
|
) async {
|
|
126
|
-
final response = await
|
|
136
|
+
final response = await host.kernelCalls.callKernel(
|
|
127
137
|
session: session,
|
|
128
138
|
request: request,
|
|
129
139
|
credentialUse: protocol.CredentialUse.CREDENTIAL_USE_NONE,
|
|
@@ -138,31 +148,15 @@ final class NmsTestPeer {
|
|
|
138
148
|
Uint8List bytes,
|
|
139
149
|
String label,
|
|
140
150
|
) async {
|
|
141
|
-
final
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
protocolVersion: 2,
|
|
145
|
-
requestId: id,
|
|
146
|
-
inspectLawPackage: protocol.InspectLawPackageRequest(
|
|
147
|
-
lawUsdz: bytes,
|
|
148
|
-
label: label,
|
|
149
|
-
),
|
|
150
|
-
),
|
|
151
|
-
);
|
|
152
|
-
if (response.hasFailure()) {
|
|
153
|
-
throw StateError('${response.failure.code}: ${response.failure.message}');
|
|
154
|
-
}
|
|
155
|
-
if (!response.hasLawPackageInspection() ||
|
|
156
|
-
response.lawPackageInspection.identity.isEmpty) {
|
|
157
|
-
throw StateError(
|
|
158
|
-
'Peer Office returned ${response.whichOutcome().name} while inspecting $label',
|
|
159
|
-
);
|
|
151
|
+
final inspection = await host.inspectLawPackage(bytes, label);
|
|
152
|
+
if (inspection.identity.isEmpty) {
|
|
153
|
+
throw StateError('the kernel named no identity for $label');
|
|
160
154
|
}
|
|
161
|
-
return
|
|
155
|
+
return inspection;
|
|
162
156
|
}
|
|
163
157
|
|
|
164
158
|
/// Mint a founding device key on a scratch session BEFORE any custody exists, so the peer can be
|
|
165
|
-
///
|
|
159
|
+
/// made to trust it (typed boot configuration) ahead of the genesis it will sign.
|
|
166
160
|
Future<protocol.SigningKeyPair> mintFoundingDevice() async {
|
|
167
161
|
final scratch = 'nms-founder-${_random.nextInt(1 << 30).toRadixString(36)}';
|
|
168
162
|
await reception.bindSession(
|
|
@@ -182,7 +176,7 @@ final class NmsTestPeer {
|
|
|
182
176
|
throw ArgumentError.value(
|
|
183
177
|
lawUsdzPaths, 'lawUsdzPaths', 'must not be empty');
|
|
184
178
|
}
|
|
185
|
-
await
|
|
179
|
+
await host.provisionWorkspace(workspace);
|
|
186
180
|
await reception.bindSession(
|
|
187
181
|
workspace: workspace,
|
|
188
182
|
session: workspace,
|
|
@@ -270,38 +264,37 @@ final class NmsTestPeer {
|
|
|
270
264
|
);
|
|
271
265
|
}
|
|
272
266
|
|
|
273
|
-
///
|
|
274
|
-
/// boot configuration
|
|
275
|
-
///
|
|
276
|
-
|
|
267
|
+
/// Trust [trustRootPublicKey] as this peer's universe root before the genesis it will sign.
|
|
268
|
+
/// The trust root is boot configuration of the shell, never a kernel variant: the Node host
|
|
269
|
+
/// reboots its process with it; the native host configures its resident kernel once.
|
|
270
|
+
Future<void> trustFoundingRoot(List<int> trustRootPublicKey) async {
|
|
271
|
+
_requireRootKey(trustRootPublicKey);
|
|
272
|
+
final replaced = await host.trustFoundingRoot(
|
|
273
|
+
trustRootPublicKey,
|
|
274
|
+
beforeReplacingConnection: reception.dispose,
|
|
275
|
+
);
|
|
276
|
+
if (!replaced) return;
|
|
277
|
+
final reopened = DartReception(host.connection);
|
|
278
|
+
await host.awaitReady(reopened);
|
|
279
|
+
reception = reopened;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/// Reboot the peer on the SAME Git custody with [trustRootPublicKey] as its typed boot
|
|
283
|
+
/// configuration, then reopen every workspace named in [authorSecrets]. A host that cannot
|
|
284
|
+
/// restart in-process (native: its resident kernel is process-wide) refuses loudly.
|
|
277
285
|
Future<void> reboot({
|
|
278
286
|
required List<int> trustRootPublicKey,
|
|
279
287
|
Map<String, String> authorSecrets = const {},
|
|
280
288
|
Future<void> Function()? arrangeStoppedCustody,
|
|
281
289
|
}) async {
|
|
282
|
-
|
|
283
|
-
throw ArgumentError.value(
|
|
284
|
-
trustRootPublicKey.length,
|
|
285
|
-
'trustRootPublicKey',
|
|
286
|
-
'must be a 32-byte public key',
|
|
287
|
-
);
|
|
288
|
-
}
|
|
290
|
+
_requireRootKey(trustRootPublicKey);
|
|
289
291
|
await reception.dispose();
|
|
290
|
-
await
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
entry: _entry,
|
|
294
|
-
resolveFrom: _resolveFrom,
|
|
295
|
-
node: _node,
|
|
296
|
-
arguments: custody.processArguments(
|
|
297
|
-
runtimeDirectory,
|
|
298
|
-
trustRootPublicKey: base64Encode(trustRootPublicKey),
|
|
299
|
-
),
|
|
300
|
-
onDiagnostic: _onDiagnostic,
|
|
292
|
+
await host.restart(
|
|
293
|
+
trustRootPublicKey: trustRootPublicKey,
|
|
294
|
+
arrangeStoppedCustody: arrangeStoppedCustody,
|
|
301
295
|
);
|
|
302
|
-
final reopened = DartReception(
|
|
303
|
-
await reopened
|
|
304
|
-
transport = restarted;
|
|
296
|
+
final reopened = DartReception(host.connection);
|
|
297
|
+
await host.awaitReady(reopened);
|
|
305
298
|
reception = reopened;
|
|
306
299
|
for (final entry in authorSecrets.entries) {
|
|
307
300
|
await reception.bindSession(
|
|
@@ -314,15 +307,30 @@ final class NmsTestPeer {
|
|
|
314
307
|
}
|
|
315
308
|
}
|
|
316
309
|
|
|
310
|
+
void _requireRootKey(List<int> trustRootPublicKey) {
|
|
311
|
+
if (trustRootPublicKey.length != 32) {
|
|
312
|
+
throw ArgumentError.value(
|
|
313
|
+
trustRootPublicKey.length,
|
|
314
|
+
'trustRootPublicKey',
|
|
315
|
+
'must be a 32-byte public key',
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/// Close Reception and stop the host's connection; custody stays on disk.
|
|
321
|
+
Future<void> stop() async {
|
|
322
|
+
try {
|
|
323
|
+
await reception.dispose();
|
|
324
|
+
} finally {
|
|
325
|
+
await host.stop();
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
317
329
|
Future<void> dispose() async {
|
|
318
330
|
try {
|
|
319
331
|
await reception.dispose();
|
|
320
332
|
} finally {
|
|
321
|
-
|
|
322
|
-
await transport.dispose();
|
|
323
|
-
} finally {
|
|
324
|
-
await custody.dispose();
|
|
325
|
-
}
|
|
333
|
+
await host.dispose();
|
|
326
334
|
}
|
|
327
335
|
}
|
|
328
336
|
}
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
@internal
|
|
2
|
+
library;
|
|
3
|
+
|
|
4
|
+
import 'dart:convert' show base64Encode;
|
|
5
|
+
import 'dart:io';
|
|
6
|
+
import 'dart:typed_data';
|
|
7
|
+
|
|
8
|
+
import 'package:fixnum/fixnum.dart' show Int64;
|
|
9
|
+
import 'package:meta/meta.dart' show internal;
|
|
10
|
+
import 'package:nomos_types/nomos_kernel_protocol.dart' as protocol;
|
|
11
|
+
|
|
12
|
+
import '../node_runner_transport.dart';
|
|
13
|
+
import '../reception/dart_reception.dart' show DartReception;
|
|
14
|
+
import '../reception/peer_office_connection.dart';
|
|
15
|
+
import '../runtime_v2_transport.dart' show NomosKernelCallTransport;
|
|
16
|
+
import 'test_custody.dart';
|
|
17
|
+
|
|
18
|
+
/// The environment variable that selects the platform host of the canonical test peer.
|
|
19
|
+
///
|
|
20
|
+
/// Unset (or `node`) is the default fast iteration loop: the Node Platform Shell over the
|
|
21
|
+
/// WASM kernel. `native` selects the Apple native host (the release-linked AOT kernel and the
|
|
22
|
+
/// Peer Office in JavaScriptCore) for production-equivalent figures on a Mac; it must have been
|
|
23
|
+
/// registered by the caller (see [NmsTestPeerHosts.registerNative]). Any other value is refused.
|
|
24
|
+
const nmsTestPeerHostVariable = 'NOMOS_TEST_PEER_HOST';
|
|
25
|
+
|
|
26
|
+
/// Which platform shell hosts the test peer.
|
|
27
|
+
enum NmsTestPeerHostKind {
|
|
28
|
+
node,
|
|
29
|
+
native;
|
|
30
|
+
|
|
31
|
+
/// The host selected by [nmsTestPeerHostVariable]. A value that names no host is refused —
|
|
32
|
+
/// a typo never silently becomes the default Node lane.
|
|
33
|
+
static NmsTestPeerHostKind selected([Map<String, String>? environment]) {
|
|
34
|
+
final raw = (environment ?? Platform.environment)[nmsTestPeerHostVariable];
|
|
35
|
+
final value = raw?.trim() ?? '';
|
|
36
|
+
switch (value) {
|
|
37
|
+
case '':
|
|
38
|
+
case 'node':
|
|
39
|
+
return NmsTestPeerHostKind.node;
|
|
40
|
+
case 'native':
|
|
41
|
+
return NmsTestPeerHostKind.native;
|
|
42
|
+
default:
|
|
43
|
+
throw StateError(
|
|
44
|
+
'$nmsTestPeerHostVariable="$raw" names no test peer host: '
|
|
45
|
+
'leave it unset (or "node") for the Node peer, or set "native" for the Apple native host',
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/// What a host needs to open one canonical test peer.
|
|
52
|
+
final class NmsTestPeerHostRequest {
|
|
53
|
+
const NmsTestPeerHostRequest({
|
|
54
|
+
required this.runtimeDirectory,
|
|
55
|
+
this.onDiagnostic,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
/// The runtime bundle directory. Every host reads the framework controller
|
|
59
|
+
/// `nomos.law.usdz` from it for the constitutional genesis offer; only the Node host
|
|
60
|
+
/// executes its `holon.wasm`.
|
|
61
|
+
final String runtimeDirectory;
|
|
62
|
+
final void Function(String line)? onDiagnostic;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/// One platform host of a canonical test Peer Office: the physical boundary the harness
|
|
66
|
+
/// stands on. It owns process/library lifetime and the custody directory LAYOUT; it owns no
|
|
67
|
+
/// workspace lifecycle, admission or law. Every operation the harness performs still crosses
|
|
68
|
+
/// Runtime v2 through [connection].
|
|
69
|
+
abstract interface class NmsTestPeerHost {
|
|
70
|
+
NmsTestPeerHostKind get kind;
|
|
71
|
+
|
|
72
|
+
/// The live Peer Office connection. A host whose trust-root application replaces the
|
|
73
|
+
/// connection (Node reboots its process) answers the new one afterwards.
|
|
74
|
+
PeerOfficeConnection get connection;
|
|
75
|
+
|
|
76
|
+
/// The generated binary kernel lane of [connection] (fixture genesis and law installs).
|
|
77
|
+
NomosKernelCallTransport get kernelCalls;
|
|
78
|
+
|
|
79
|
+
/// The temporary directory holding this peer's Git custody. Deleted by [dispose].
|
|
80
|
+
Directory get custodyDirectory;
|
|
81
|
+
|
|
82
|
+
/// The bare Git directory of [workspace] in this host's custody layout.
|
|
83
|
+
String workspaceGitDirectory(String workspace);
|
|
84
|
+
|
|
85
|
+
/// Provision the platform-owned empty custody into which the one parentless genesis
|
|
86
|
+
/// offer for [workspace] may write, in this host's layout. Fixture plumbing, not an
|
|
87
|
+
/// application lifecycle operation.
|
|
88
|
+
Future<void> provisionWorkspace(String workspace);
|
|
89
|
+
|
|
90
|
+
/// The kernel's inspection of an opaque law package: its content identity and domain keys.
|
|
91
|
+
/// Node asks its Peer Office (Runtime v2 `inspectLawPackage`). The Apple host's office has no
|
|
92
|
+
/// inspection port, so the native shell asks its resident kernel directly — the same
|
|
93
|
+
/// `custody.lawClosureInfo` call the Node adapter makes for its office.
|
|
94
|
+
Future<protocol.LawPackageInspection> inspectLawPackage(
|
|
95
|
+
Uint8List lawUsdz,
|
|
96
|
+
String label,
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
/// Complete once [reception] (a new Reception over [connection]) may be used: the Node shell
|
|
100
|
+
/// posts a `ready` event when its process has booted; the native host booted its resident
|
|
101
|
+
/// kernel and Peer Office when it started and posts no such event.
|
|
102
|
+
Future<void> awaitReady(DartReception reception);
|
|
103
|
+
|
|
104
|
+
/// Trust [publicKey] as the root of this peer's universe before the genesis it will sign.
|
|
105
|
+
/// Typed boot configuration, never a kernel variant. Node reboots its process with
|
|
106
|
+
/// `--trust-root-public-key` (calling [beforeReplacingConnection] first, and answering true:
|
|
107
|
+
/// the connection was replaced); native issues the kernel's one-time `configure` call on the
|
|
108
|
+
/// live connection and answers false.
|
|
109
|
+
Future<bool> trustFoundingRoot(
|
|
110
|
+
List<int> publicKey, {
|
|
111
|
+
required Future<void> Function() beforeReplacingConnection,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
/// Restart the peer over the SAME custody with [trustRootPublicKey]. A host that cannot
|
|
115
|
+
/// restart in-process refuses loudly; it never pretends to have restarted.
|
|
116
|
+
Future<void> restart({
|
|
117
|
+
required List<int> trustRootPublicKey,
|
|
118
|
+
Future<void> Function()? arrangeStoppedCustody,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
/// Stop the host's connection (and process, where it owns one). Custody stays on disk.
|
|
122
|
+
Future<void> stop();
|
|
123
|
+
|
|
124
|
+
/// [stop], then delete [custodyDirectory].
|
|
125
|
+
Future<void> dispose();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
typedef NmsTestPeerHostFactory = Future<NmsTestPeerHost> Function(
|
|
129
|
+
NmsTestPeerHostRequest request,
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
/// The registry the harness selects a host from. nomos_client cannot depend on the Flutter
|
|
133
|
+
/// shell, so the native host is injected by the caller (`nomos_flutter/testing.dart`).
|
|
134
|
+
abstract final class NmsTestPeerHosts {
|
|
135
|
+
static NmsTestPeerHostFactory? _native;
|
|
136
|
+
|
|
137
|
+
/// Register the Apple native host. Registering does not select it: selection is
|
|
138
|
+
/// [nmsTestPeerHostVariable]=native.
|
|
139
|
+
static void registerNative(NmsTestPeerHostFactory factory) {
|
|
140
|
+
_native = factory;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/// The registered native host factory. Selected-but-unregistered is a loud refusal with
|
|
144
|
+
/// the exact remedy, never a quiet fall back to Node.
|
|
145
|
+
static NmsTestPeerHostFactory requireNative() {
|
|
146
|
+
final factory = _native;
|
|
147
|
+
if (factory == null) {
|
|
148
|
+
throw StateError(
|
|
149
|
+
'$nmsTestPeerHostVariable=native selects the Apple native test peer host, but none is '
|
|
150
|
+
'registered in this process. Generated business scenarios register it themselves; a '
|
|
151
|
+
'raw harness in a macOS `flutter test` imports '
|
|
152
|
+
"'package:nomos_flutter/testing.dart' and calls registerNomosNativeTestPeerHost() "
|
|
153
|
+
'(e.g. in setUpAll) before opening a NomosTestHarness. Unset $nmsTestPeerHostVariable '
|
|
154
|
+
'to run on the default Node peer.',
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
return factory;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/// The default host: the Node test Platform Shell over the WASM kernel, custody laid out as
|
|
162
|
+
/// `<root>/custody/holon-<ws>.git`.
|
|
163
|
+
final class NmsNodeTestPeerHost implements NmsTestPeerHost {
|
|
164
|
+
NmsNodeTestPeerHost._({
|
|
165
|
+
required NodeRunnerTransport transport,
|
|
166
|
+
required this.custody,
|
|
167
|
+
required this.runtimeDirectory,
|
|
168
|
+
required String entry,
|
|
169
|
+
required String node,
|
|
170
|
+
required String? resolveFrom,
|
|
171
|
+
required void Function(String line)? onDiagnostic,
|
|
172
|
+
}) : _transport = transport,
|
|
173
|
+
_entry = entry,
|
|
174
|
+
_node = node,
|
|
175
|
+
_resolveFrom = resolveFrom,
|
|
176
|
+
_onDiagnostic = onDiagnostic;
|
|
177
|
+
|
|
178
|
+
NodeRunnerTransport _transport;
|
|
179
|
+
final NmsTestCustody custody;
|
|
180
|
+
final String runtimeDirectory;
|
|
181
|
+
final String _entry;
|
|
182
|
+
final String _node;
|
|
183
|
+
final String? _resolveFrom;
|
|
184
|
+
final void Function(String line)? _onDiagnostic;
|
|
185
|
+
|
|
186
|
+
static Future<NmsNodeTestPeerHost> start({
|
|
187
|
+
required String entry,
|
|
188
|
+
required NmsTestPeerHostRequest request,
|
|
189
|
+
String node = 'node',
|
|
190
|
+
String? resolveFrom,
|
|
191
|
+
}) async {
|
|
192
|
+
final custody = await NmsTestCustody.create();
|
|
193
|
+
try {
|
|
194
|
+
final transport = await NodeRunnerTransport.start(
|
|
195
|
+
entry: entry,
|
|
196
|
+
resolveFrom: resolveFrom,
|
|
197
|
+
node: node,
|
|
198
|
+
arguments: custody.processArguments(request.runtimeDirectory),
|
|
199
|
+
onDiagnostic: request.onDiagnostic,
|
|
200
|
+
);
|
|
201
|
+
return NmsNodeTestPeerHost._(
|
|
202
|
+
transport: transport,
|
|
203
|
+
custody: custody,
|
|
204
|
+
runtimeDirectory: request.runtimeDirectory,
|
|
205
|
+
entry: entry,
|
|
206
|
+
node: node,
|
|
207
|
+
resolveFrom: resolveFrom,
|
|
208
|
+
onDiagnostic: request.onDiagnostic,
|
|
209
|
+
);
|
|
210
|
+
} catch (_) {
|
|
211
|
+
await custody.dispose();
|
|
212
|
+
rethrow;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
@override
|
|
217
|
+
NmsTestPeerHostKind get kind => NmsTestPeerHostKind.node;
|
|
218
|
+
|
|
219
|
+
@override
|
|
220
|
+
PeerOfficeConnection get connection => _transport;
|
|
221
|
+
|
|
222
|
+
@override
|
|
223
|
+
NomosKernelCallTransport get kernelCalls => _transport;
|
|
224
|
+
|
|
225
|
+
@override
|
|
226
|
+
Directory get custodyDirectory => custody.directory;
|
|
227
|
+
|
|
228
|
+
@override
|
|
229
|
+
String workspaceGitDirectory(String workspace) =>
|
|
230
|
+
'${custody.directory.path}/custody/holon-$workspace.git';
|
|
231
|
+
|
|
232
|
+
@override
|
|
233
|
+
Future<void> provisionWorkspace(String workspace) =>
|
|
234
|
+
custody.provisionWorkspace(workspace);
|
|
235
|
+
|
|
236
|
+
@override
|
|
237
|
+
Future<void> awaitReady(DartReception reception) => reception.ready;
|
|
238
|
+
|
|
239
|
+
@override
|
|
240
|
+
Future<protocol.LawPackageInspection> inspectLawPackage(
|
|
241
|
+
Uint8List lawUsdz,
|
|
242
|
+
String label,
|
|
243
|
+
) async {
|
|
244
|
+
final response = await _transport.invokeRuntime(
|
|
245
|
+
protocol.RuntimeRequest(
|
|
246
|
+
protocolVersion: 2,
|
|
247
|
+
requestId: Int64(_transport.nextRuntimeRequestId()),
|
|
248
|
+
inspectLawPackage: protocol.InspectLawPackageRequest(
|
|
249
|
+
lawUsdz: lawUsdz,
|
|
250
|
+
label: label,
|
|
251
|
+
),
|
|
252
|
+
),
|
|
253
|
+
);
|
|
254
|
+
if (response.hasFailure()) {
|
|
255
|
+
throw StateError('${response.failure.code}: ${response.failure.message}');
|
|
256
|
+
}
|
|
257
|
+
if (!response.hasLawPackageInspection()) {
|
|
258
|
+
throw StateError(
|
|
259
|
+
'Peer Office returned ${response.whichOutcome().name} while inspecting $label',
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
return response.lawPackageInspection;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
@override
|
|
266
|
+
Future<bool> trustFoundingRoot(
|
|
267
|
+
List<int> publicKey, {
|
|
268
|
+
required Future<void> Function() beforeReplacingConnection,
|
|
269
|
+
}) async {
|
|
270
|
+
await beforeReplacingConnection();
|
|
271
|
+
await restart(trustRootPublicKey: publicKey);
|
|
272
|
+
return true;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
@override
|
|
276
|
+
Future<void> restart({
|
|
277
|
+
required List<int> trustRootPublicKey,
|
|
278
|
+
Future<void> Function()? arrangeStoppedCustody,
|
|
279
|
+
}) async {
|
|
280
|
+
await _transport.dispose();
|
|
281
|
+
await arrangeStoppedCustody?.call();
|
|
282
|
+
_transport = await NodeRunnerTransport.start(
|
|
283
|
+
entry: _entry,
|
|
284
|
+
resolveFrom: _resolveFrom,
|
|
285
|
+
node: _node,
|
|
286
|
+
arguments: custody.processArguments(
|
|
287
|
+
runtimeDirectory,
|
|
288
|
+
trustRootPublicKey: base64Encode(trustRootPublicKey),
|
|
289
|
+
),
|
|
290
|
+
onDiagnostic: _onDiagnostic,
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
@override
|
|
295
|
+
Future<void> stop() => _transport.dispose();
|
|
296
|
+
|
|
297
|
+
@override
|
|
298
|
+
Future<void> dispose() async {
|
|
299
|
+
try {
|
|
300
|
+
await stop();
|
|
301
|
+
} finally {
|
|
302
|
+
await custody.dispose();
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
@@ -4,6 +4,7 @@ import 'package:nomos_types/nomos_kernel_protocol.dart' as nomos_protocol;
|
|
|
4
4
|
import 'package:nomos_types/nomos_types.dart'
|
|
5
5
|
show NomosErrorInfo, NomosUnavailable, nomosErrorFromInfo;
|
|
6
6
|
|
|
7
|
+
import 'nomos_trace.dart' show NomosTrace;
|
|
7
8
|
import 'replica_readiness.dart' show NmsReplicaWake;
|
|
8
9
|
|
|
9
10
|
final class NmsTypedWatchHandle<T> {
|
|
@@ -30,11 +31,13 @@ final class NmsTypedWatchRegistry {
|
|
|
30
31
|
required T Function(nomos_protocol.WatchChanged) decode,
|
|
31
32
|
required Future<void> Function(String subscriptionId) start,
|
|
32
33
|
required Future<void> Function(String subscriptionId) stop,
|
|
34
|
+
String label = '',
|
|
33
35
|
}) {
|
|
34
36
|
if (_closed) throw StateError('Reception watch registry is closed');
|
|
35
37
|
final id = '$session:watch:${++_sequence}';
|
|
36
38
|
late final _TypedWatch<T> watch;
|
|
37
39
|
watch = _TypedWatch<T>(
|
|
40
|
+
traceFields: NomosTrace.enabled ? 'sub=$id q=$label' : '',
|
|
38
41
|
synced: _synced,
|
|
39
42
|
decode: decode,
|
|
40
43
|
start: () {
|
|
@@ -51,7 +54,11 @@ final class NmsTypedWatchRegistry {
|
|
|
51
54
|
return stop(id);
|
|
52
55
|
},
|
|
53
56
|
);
|
|
54
|
-
return NmsTypedWatchHandle<T>(
|
|
57
|
+
return NmsTypedWatchHandle<T>(
|
|
58
|
+
id: id,
|
|
59
|
+
stream: NomosTrace.timedListener(
|
|
60
|
+
watch.controller.stream, 'sub=$id q=$label stage=deliver'),
|
|
61
|
+
);
|
|
55
62
|
}
|
|
56
63
|
|
|
57
64
|
void deliver(nomos_protocol.WatchChanged changed) {
|
|
@@ -82,6 +89,7 @@ abstract interface class _RegisteredWatch {
|
|
|
82
89
|
/// exists, and order cancellation after registration even when it is in flight.
|
|
83
90
|
/// A single-subscription controller buffers the initial response and pauses.
|
|
84
91
|
final class _TypedWatch<T> implements _RegisteredWatch {
|
|
92
|
+
final String _traceFields;
|
|
85
93
|
final Stream<String>? _synced;
|
|
86
94
|
final T Function(nomos_protocol.WatchChanged) _decode;
|
|
87
95
|
final Future<void> Function() _start;
|
|
@@ -91,11 +99,13 @@ final class _TypedWatch<T> implements _RegisteredWatch {
|
|
|
91
99
|
Future<void>? _stopping;
|
|
92
100
|
|
|
93
101
|
_TypedWatch({
|
|
102
|
+
required String traceFields,
|
|
94
103
|
required Stream<String>? synced,
|
|
95
104
|
required T Function(nomos_protocol.WatchChanged) decode,
|
|
96
105
|
required Future<void> Function() start,
|
|
97
106
|
required Future<void> Function() stop,
|
|
98
|
-
}) :
|
|
107
|
+
}) : _traceFields = traceFields,
|
|
108
|
+
_synced = synced,
|
|
99
109
|
_decode = decode,
|
|
100
110
|
_start = start,
|
|
101
111
|
_stop = stop {
|
|
@@ -147,7 +157,20 @@ final class _TypedWatch<T> implements _RegisteredWatch {
|
|
|
147
157
|
return;
|
|
148
158
|
}
|
|
149
159
|
try {
|
|
150
|
-
|
|
160
|
+
if (NomosTrace.enabled) {
|
|
161
|
+
// The emission reached its watch; its listener runs later (watch.handler).
|
|
162
|
+
final started = NomosTrace.now();
|
|
163
|
+
final value = _decode(changed);
|
|
164
|
+
NomosTrace.emit(
|
|
165
|
+
'dart',
|
|
166
|
+
'watch.deliver',
|
|
167
|
+
'$_traceFields delta=${changed.hasApplicationDelta()} '
|
|
168
|
+
'bytes=${changed.applicationResult.value.length + changed.resultUsda.length + changed.applicationDelta.changedRows.value.length} '
|
|
169
|
+
'decodeUs=${NomosTrace.now() - started}');
|
|
170
|
+
controller.add(value);
|
|
171
|
+
} else {
|
|
172
|
+
controller.add(_decode(changed));
|
|
173
|
+
}
|
|
151
174
|
} catch (error, stackTrace) {
|
|
152
175
|
controller.addError(error, stackTrace);
|
|
153
176
|
}
|