@githolon/dsl 0.100.5 → 0.101.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/dart/CHANGELOG.md +12 -0
- package/dart/lib/src/dev_cloud.dart +46 -15
- package/dart/lib/src/test_harness.dart +220 -71
- package/dart/lib/src/webview_bridge.dart +640 -126
- package/dart/pubspec.lock +1 -1
- package/dart/pubspec.yaml +2 -2
- package/dart/test/acquire_session_credentials_test.dart +1 -1
- package/dart/test/adv_realm_interference_test.dart +1 -1
- package/dart/test/bundle_version_pairing_test.dart +11 -22
- package/dart/test/cluster_attested_test.dart +1 -1
- package/dart/test/cluster_births_test.dart +1 -1
- package/dart/test/cluster_device_keyed_acceptance_test.dart +1 -1
- package/dart/test/cluster_local_autosync_test.dart +1 -1
- package/dart/test/cluster_scale_test.dart +1 -1
- package/dart/test/co2_product_gauntlet_test.dart +228 -0
- package/dart/test/dlq_session_surface_test.dart +1 -1
- package/dart/test/errors_test.dart +63 -12
- package/dart/test/first_app_gauntlet_test.dart +132 -87
- package/dart/test/local_sync_inert_test.dart +10 -5
- package/dart/test/parent_signer_recovery_test.dart +1 -1
- package/dart/test/sealing_client_test.dart +1 -1
- package/dart/test/session_bound_test.dart +1 -1
- package/dart/test/test_harness_test.dart +2 -2
- package/dart/test/vo_read_typing_test.dart +1 -1
- package/dart/test/watch_after_offer_stall_test.dart +1 -1
- package/dart/test/webview_bridge_test.dart +274 -0
- package/dart_types/CHANGELOG.md +6 -0
- package/dart_types/lib/src/errors.dart +136 -30
- package/dart_types/lib/src/surfaced.dart +48 -2
- package/dart_types/lib/src/wire.dart +3 -1
- package/dart_types/pubspec.yaml +1 -1
- package/dart_types/test/surfaced_test.dart +19 -0
- package/package.json +1 -1
- package/src/activity.ts +43 -4
- package/src/aggregate.ts +132 -22
- package/src/attested_core.ts +1 -1
- package/src/authoring.ts +119 -6
- package/src/birth_meter.ts +4 -4
- package/src/birth_meter_ensure.ts +165 -29
- package/src/births.ts +53 -22
- package/src/build_package.ts +96 -20
- package/src/business_birth.ts +79 -16
- package/src/codegen_dart.ts +267 -37
- package/src/codegen_dart_client.ts +930 -87
- package/src/codegen_openapi.ts +28 -5
- package/src/codegen_proof.ts +107 -11
- package/src/codegen_ts.ts +961 -135
- package/src/compile_package_main.ts +172 -53
- package/src/conflicts.ts +252 -0
- package/src/describe_model.ts +448 -0
- package/src/directive.ts +5 -38
- package/src/drivers.ts +12 -4
- package/src/engine_entry.ts +4 -0
- package/src/evolve_preview.ts +72 -11
- package/src/fields.ts +201 -55
- package/src/framework/application_home.ts +103 -0
- package/src/framework/application_home_domain.ts +2 -0
- package/src/framework/attested_receipts.ts +2 -2
- package/src/framework/auth_provider_law.ts +51 -24
- package/src/framework/business_corrections.ts +170 -0
- package/src/framework/disclosure.ts +1 -1
- package/src/framework/domain_lifecycle.ts +250 -16
- package/src/framework/encryption_identity.ts +1 -1
- package/src/framework/encryption_scope.ts +21 -13
- package/src/framework/era_certification.ts +1 -1
- package/src/framework/framework_aggregates.ts +2 -0
- package/src/framework/genesis.ts +1 -1
- package/src/framework/home.ts +22 -9
- package/src/framework/identity.ts +5 -5
- package/src/framework/impure_capability.ts +2 -2
- package/src/framework/owned_custody.ts +561 -0
- package/src/framework/platform_admin.ts +1 -1
- package/src/framework/relations.ts +9 -1
- package/src/framework/repair.ts +1 -1
- package/src/framework/signing_identity.ts +2 -2
- package/src/framework/source_roster.ts +1 -1
- package/src/framework/workspaces.ts +17 -10
- package/src/generated_client_names.ts +39 -0
- package/src/index.ts +20 -15
- package/src/kernel_capabilities.ts +25 -1
- package/src/law_check.ts +4 -1
- package/src/manifest.ts +356 -63
- package/src/read_projection_manifest.ts +8 -0
- package/src/read_projection_namespace.ts +4 -0
- package/src/relation.ts +1 -1
- package/src/shape_compat.ts +61 -5
- package/src/stable_ids.ts +38 -6
- package/src/stable_ids_types.ts +30 -0
- package/src/surfaced_lint.ts +0 -0
- package/src/usd.ts +62 -11
- package/src/usd_layers.ts +54 -3
- package/src/value_object.ts +23 -19
- package/src/wire.ts +1 -0
- package/src/wire_encode.ts +48 -4
- package/src/workspace_routing.ts +57 -0
- package/src/workspace_sharding.ts +9 -9
- package/src/certified_read.ts +0 -313
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
// by CI, not by a tenant.
|
|
6
6
|
//
|
|
7
7
|
// Structure: ONE test, numbered legs, fail-fast — a failure names its leg, so CI output reads as
|
|
8
|
-
// a checklist.
|
|
9
|
-
//
|
|
8
|
+
// a checklist. Its first act uses the one-business-birth CO2 example: the same tenant source and
|
|
9
|
+
// generated client a real Flutter app consumes, never a harness-only birth recipe.
|
|
10
10
|
//
|
|
11
11
|
// Prereqs (fixtures compiled + a warm ~/.holon/runtime): locally the test SKIPS with the recipe
|
|
12
12
|
// when they are missing; in CI the job sets NOMOS_GAUNTLET_REQUIRED=1 so a missing prereq is a
|
|
@@ -14,24 +14,20 @@
|
|
|
14
14
|
@TestOn('vm')
|
|
15
15
|
library;
|
|
16
16
|
|
|
17
|
-
import 'dart:convert';
|
|
18
17
|
import 'dart:io';
|
|
19
18
|
|
|
20
|
-
import 'package:crypto/crypto.dart' show sha256;
|
|
21
|
-
|
|
22
19
|
import 'package:nomos_client/nomos_headless.dart';
|
|
23
20
|
import 'package:test/test.dart';
|
|
24
21
|
|
|
25
|
-
import '../../../
|
|
26
|
-
import '../../../bench/scale/fixtures/estate-birth/build/dart/src/generated/estate.client.dart'
|
|
27
|
-
show EstateClient, SeedEstateWorkspacePayload;
|
|
22
|
+
import '../../../examples/co2-birth/build/dart/src/generated/co2.client.dart';
|
|
28
23
|
|
|
29
24
|
String get _root => '${Directory.current.path}/../..';
|
|
30
25
|
String get _repoEntry => '$_root/cloud/web-client/runner/node-entry.ts';
|
|
31
|
-
String get _homeDeploy =>
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
String get _catDeploy =>
|
|
26
|
+
String get _homeDeploy => '$_root/examples/co2-birth/build/co2.deploy.json';
|
|
27
|
+
String get _docsDeploy =>
|
|
28
|
+
'$_root/bench/scale/fixtures/surfaced/build/surfaced.deploy.json';
|
|
29
|
+
String get _catDeploy =>
|
|
30
|
+
'$_root/bench/scale/fixtures/catalogue-src/build/cat.deploy.json';
|
|
35
31
|
String get _readerDeploy =>
|
|
36
32
|
'$_root/bench/scale/fixtures/catalogue-reader/build/estate.deploy.json';
|
|
37
33
|
String get _runtimeDir =>
|
|
@@ -42,9 +38,10 @@ const _owner = 'user:gauntlet-owner';
|
|
|
42
38
|
const _keyed = 'catalogue';
|
|
43
39
|
|
|
44
40
|
String? _missingPrereq() {
|
|
45
|
-
if (!File(_repoEntry).existsSync())
|
|
41
|
+
if (!File(_repoEntry).existsSync())
|
|
42
|
+
return 'in-repo runner not found at $_repoEntry';
|
|
46
43
|
final fixtures = {
|
|
47
|
-
_homeDeploy: '
|
|
44
|
+
_homeDeploy: 'examples/co2-birth',
|
|
48
45
|
_docsDeploy: 'bench/scale/fixtures/surfaced',
|
|
49
46
|
_catDeploy: 'bench/scale/fixtures/catalogue-src',
|
|
50
47
|
_readerDeploy: 'bench/scale/fixtures/catalogue-reader',
|
|
@@ -54,10 +51,10 @@ String? _missingPrereq() {
|
|
|
54
51
|
return 'missing fixture ${e.key} — compile it: (cd ${e.value} && node ../../../../dsl/compile_package.mjs)';
|
|
55
52
|
}
|
|
56
53
|
}
|
|
57
|
-
if (!File('$_runtimeDir/holon.wasm').existsSync() ||
|
|
54
|
+
if (!File('$_runtimeDir/holon-testmint.wasm').existsSync() ||
|
|
58
55
|
!File('$_runtimeDir/packages.json').existsSync()) {
|
|
59
|
-
return 'warm ~/.holon/runtime first (run any `githolon` command once, or
|
|
60
|
-
'/v1/runtime/holon.wasm + /v1/runtime/packages from the cloud)';
|
|
56
|
+
return 'warm ~/.holon/runtime first (run any `githolon` command once, or fetch '
|
|
57
|
+
'/v1/runtime/holon-testmint.wasm + /v1/runtime/packages from the cloud)';
|
|
61
58
|
}
|
|
62
59
|
return null;
|
|
63
60
|
}
|
|
@@ -74,18 +71,22 @@ Future<T> _leg<T>(String name, Future<T> Function() body) async {
|
|
|
74
71
|
print('✔ GAUNTLET $name (${sw.elapsedMilliseconds}ms)');
|
|
75
72
|
return r;
|
|
76
73
|
} catch (e, st) {
|
|
77
|
-
throw TestFailure(
|
|
74
|
+
throw TestFailure(
|
|
75
|
+
'GAUNTLET $name FAILED after ${sw.elapsedMilliseconds}ms:\n$e\n$st');
|
|
78
76
|
}
|
|
79
77
|
}
|
|
80
78
|
|
|
81
79
|
void main() {
|
|
82
|
-
test(
|
|
83
|
-
'
|
|
80
|
+
test(
|
|
81
|
+
'THE FIRST-FIVE-MINUTES GAUNTLET: harness → blessed shape → local posture → '
|
|
82
|
+
'acquire → reactive reads → attested read → diagnostics → disposal',
|
|
83
|
+
() async {
|
|
84
84
|
final required = Platform.environment['NOMOS_GAUNTLET_REQUIRED'] == '1';
|
|
85
85
|
final missing = _missingPrereq();
|
|
86
86
|
if (missing != null) {
|
|
87
87
|
if (required) {
|
|
88
|
-
fail(
|
|
88
|
+
fail(
|
|
89
|
+
'NOMOS_GAUNTLET_REQUIRED=1 but a prereq is missing — the CI job must provide it: '
|
|
89
90
|
'$missing');
|
|
90
91
|
}
|
|
91
92
|
markTestSkipped(missing);
|
|
@@ -95,7 +96,8 @@ void main() {
|
|
|
95
96
|
final diagnostics = <String>[];
|
|
96
97
|
|
|
97
98
|
// ── LEG 1: harness up — a 4-workspace cluster, one deviceKeyed, phase diagnostics on. ──
|
|
98
|
-
final harness = await _leg(
|
|
99
|
+
final harness = await _leg(
|
|
100
|
+
'leg 1: NomosTestHarness.cluster (4 ws, one deviceKeyed)', () async {
|
|
99
101
|
final h = await NomosTestHarness.cluster(
|
|
100
102
|
workspaces: {
|
|
101
103
|
'home': [_homeDeploy],
|
|
@@ -113,88 +115,119 @@ void main() {
|
|
|
113
115
|
// keyed 3b legs all log enter AND exit lines.
|
|
114
116
|
expect(diagnostics.any((l) => l.contains('offline law install')), isTrue,
|
|
115
117
|
reason: 'phase 1 install diagnostics must appear');
|
|
116
|
-
expect(diagnostics.any((l) => l.
|
|
118
|
+
expect(diagnostics.any((l) => l.startsWith('[offline-install]')), isTrue,
|
|
119
|
+
reason:
|
|
120
|
+
'an in-repo runner must install through its adjacent in-repo helper, never silently fall back to the published CLI');
|
|
121
|
+
expect(
|
|
122
|
+
diagnostics
|
|
123
|
+
.any((l) => l.contains('runner ready') && l.contains('done')),
|
|
124
|
+
isTrue,
|
|
117
125
|
reason: 'the runner-ready phase must log its exit');
|
|
118
126
|
expect(diagnostics.any((l) => l.contains('3b: device keygen')), isTrue,
|
|
119
127
|
reason: 'the deviceKeyed keygen phase must log');
|
|
120
128
|
expect(
|
|
121
|
-
diagnostics.any(
|
|
122
|
-
|
|
129
|
+
diagnostics.any((l) =>
|
|
130
|
+
l.contains("3b: keyed reconnect of '$_keyed'") &&
|
|
131
|
+
l.contains('done')),
|
|
123
132
|
isTrue,
|
|
124
|
-
reason:
|
|
133
|
+
reason:
|
|
134
|
+
'the keyed reconnect phase must log its exit (the reported wedge)');
|
|
125
135
|
expect(h.sessions.toSet(), {'home', 'docs-ws', _keyed, 'reader'});
|
|
126
136
|
return h;
|
|
127
137
|
});
|
|
128
138
|
|
|
129
139
|
try {
|
|
130
|
-
// ── LEG 2: the blessed session-bound shape —
|
|
131
|
-
//
|
|
140
|
+
// ── LEG 2: the blessed session-bound shape — one business birth, then Estate → Site → Asset
|
|
141
|
+
// through the generated Dart client. Zero routing/security strings. ──
|
|
132
142
|
final home = harness.session('home');
|
|
133
|
-
final
|
|
134
|
-
'leg 2:
|
|
143
|
+
final bornEstate = await _leg(
|
|
144
|
+
'leg 2: generated business shape (Estate → Site → Asset)',
|
|
135
145
|
() async {
|
|
136
|
-
final homeClient = await
|
|
146
|
+
final homeClient = await Co2Client.bind(home, actor: _owner);
|
|
137
147
|
expect(homeClient.session!.workspace, home.workspace,
|
|
138
148
|
reason: 'inspectable session context');
|
|
139
|
-
final
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
// era-0: the estate enrols the owner's device at birth (signer-ready) — the harness device.
|
|
146
|
-
ownerKeyHash: sha256.convert(base64Decode(harness.devicePublicKey(home.workspace)!)).toString(),
|
|
149
|
+
final ref = await homeClient.birth(
|
|
150
|
+
NomosBirthEstatePayload(
|
|
151
|
+
reference: 'E-001',
|
|
152
|
+
name: 'Foundry',
|
|
153
|
+
country: 'GB',
|
|
154
|
+
baselineTonnesCo2e: 4200,
|
|
147
155
|
),
|
|
148
156
|
);
|
|
149
|
-
expect(
|
|
150
|
-
|
|
151
|
-
expect(birth.born, hasLength(1));
|
|
152
|
-
expect(birth.birthOutcomes.first['status'], 'born');
|
|
157
|
+
expect(ref.workspace, startsWith('estate-'));
|
|
158
|
+
expect(ref.type, 'Estate');
|
|
153
159
|
|
|
154
|
-
final estateSession = await harness.bridge.follow(
|
|
155
|
-
|
|
160
|
+
final estateSession = await harness.bridge.follow(
|
|
161
|
+
ref,
|
|
162
|
+
authorSecret: harness.deviceSecret(home.workspace),
|
|
163
|
+
);
|
|
164
|
+
expect(estateSession.workspace, ref.workspace);
|
|
165
|
+
|
|
166
|
+
final estateClient = await Co2Client.bind(estateSession, actor: _owner);
|
|
167
|
+
final rows = await estateClient.estateById(ref.id);
|
|
168
|
+
expect(rows, hasLength(1),
|
|
169
|
+
reason: 'the born business root reads back typed');
|
|
170
|
+
expect(rows.single.reference, 'E-001');
|
|
171
|
+
expect(rows.single.name, 'Foundry');
|
|
156
172
|
|
|
157
|
-
final
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
final
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
173
|
+
final siteOutcome = await estateClient.addSite(
|
|
174
|
+
AddSitePayload(estateId: ref.id, name: 'Trafford Depot'),
|
|
175
|
+
);
|
|
176
|
+
final siteRef = siteOutcome.created.single.ref;
|
|
177
|
+
expect(siteRef.type, 'co2.Site');
|
|
178
|
+
final sites = await estateClient.sitesByEstate(estateId: ref.id);
|
|
179
|
+
expect(sites, hasLength(1));
|
|
180
|
+
expect(sites.single.name, 'Trafford Depot');
|
|
181
|
+
|
|
182
|
+
final assetOutcome = await estateClient.addAsset(
|
|
183
|
+
AddAssetPayload(
|
|
184
|
+
siteId: siteRef.id,
|
|
185
|
+
name: 'Main electricity meter',
|
|
186
|
+
operationalStatus: 'commissioned',
|
|
187
|
+
),
|
|
188
|
+
);
|
|
189
|
+
final assetRef = assetOutcome.created.single.ref;
|
|
190
|
+
expect(assetRef.type, 'co2.Asset');
|
|
191
|
+
final assets = await estateClient.assetsBySite(siteId: siteRef.id);
|
|
192
|
+
expect(assets, hasLength(1));
|
|
193
|
+
expect(assets.single.name, 'Main electricity meter');
|
|
194
|
+
expect(assets.single.operationalStatus?.valueOrNull, 'commissioned');
|
|
195
|
+
return (session: estateSession, ref: ref);
|
|
168
196
|
});
|
|
197
|
+
final estate = bornEstate.session;
|
|
169
198
|
|
|
170
199
|
// ── LEG 3: local posture — autoSync on a local-only session returns promptly and never
|
|
171
200
|
// monopolizes the plane; zero network fetch attempts (asserted again at the end). ──
|
|
172
|
-
await _leg(
|
|
201
|
+
await _leg(
|
|
202
|
+
'leg 3: local-only autoSync completes promptly, plane stays responsive',
|
|
173
203
|
() async {
|
|
174
204
|
await home.autoSync().timeout(const Duration(seconds: 10),
|
|
175
205
|
onTimeout: () => throw StateError(
|
|
176
206
|
'autoSync() on a local-only session did not return within 10s (the deadlock class)'));
|
|
177
207
|
// The plane is NOT blocked: a sibling session still answers.
|
|
178
|
-
final listed = await harness.bridge.listWorkspaces().timeout(
|
|
179
|
-
|
|
180
|
-
|
|
208
|
+
final listed = await harness.bridge.listWorkspaces().timeout(
|
|
209
|
+
const Duration(seconds: 10),
|
|
210
|
+
onTimeout: () => throw StateError(
|
|
211
|
+
'listWorkspaces() hung after a local-only autoSync'));
|
|
181
212
|
expect(listed.map((w) => w['session'].toString()).toSet(),
|
|
182
213
|
containsAll({'home', 'docs-ws', _keyed, 'reader'}));
|
|
183
214
|
await home.stopAutoSync().timeout(const Duration(seconds: 10),
|
|
184
|
-
onTimeout: () => throw StateError(
|
|
215
|
+
onTimeout: () => throw StateError(
|
|
216
|
+
'stopAutoSync() hung on a local-only session'));
|
|
185
217
|
});
|
|
186
218
|
|
|
187
219
|
// ── LEG 4: acquireSession — concurrent double-acquire single-flights to ONE handle; the
|
|
188
220
|
// credentials callback is NEVER invoked for a plane-known workspace. ──
|
|
189
|
-
await _leg(
|
|
221
|
+
await _leg(
|
|
222
|
+
'leg 4: acquireSession (concurrent, credential-free for a known workspace)',
|
|
190
223
|
() async {
|
|
191
224
|
final acquired = await Future.wait([
|
|
192
225
|
harness.bridge.acquireSession('reader',
|
|
193
|
-
ifAbsentCredentials: () async =>
|
|
194
|
-
|
|
226
|
+
ifAbsentCredentials: () async => fail(
|
|
227
|
+
'reader is on the plane — credentials must not be requested')),
|
|
195
228
|
harness.bridge.acquireSession('reader',
|
|
196
|
-
ifAbsentCredentials: () async =>
|
|
197
|
-
|
|
229
|
+
ifAbsentCredentials: () async => fail(
|
|
230
|
+
'reader is on the plane — credentials must not be requested')),
|
|
198
231
|
]);
|
|
199
232
|
expect(identical(acquired[0], acquired[1]), isTrue,
|
|
200
233
|
reason: 'a concurrent double-acquire yields ONE handle');
|
|
@@ -204,21 +237,22 @@ void main() {
|
|
|
204
237
|
|
|
205
238
|
// ── LEG 5: reactive reads on a SESSION — watchById / watch / watchPendingConflicts must
|
|
206
239
|
// all deliver a FIRST SNAPSHOT (the 0.48.x dead-delegation class). ──
|
|
207
|
-
await _leg('leg 5: session reactive reads emit their first snapshot',
|
|
208
|
-
|
|
240
|
+
await _leg('leg 5: session reactive reads emit their first snapshot',
|
|
241
|
+
() async {
|
|
242
|
+
final raw = await estate.queryById(bornEstate.ref.id);
|
|
209
243
|
final aggId = ((raw.single as Map)['id'] ?? '').toString();
|
|
210
244
|
expect(aggId, isNotEmpty);
|
|
211
|
-
final byId = await estate
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
onTimeout: () => throw StateError('watchById never emitted its first snapshot'));
|
|
245
|
+
final byId = await estate.watchById(aggId).first.timeout(
|
|
246
|
+
const Duration(seconds: 30),
|
|
247
|
+
onTimeout: () =>
|
|
248
|
+
throw StateError('watchById never emitted its first snapshot'));
|
|
216
249
|
expect(byId, hasLength(1));
|
|
217
250
|
final byQuery = await estate
|
|
218
|
-
.watch('
|
|
251
|
+
.watch('estatesByReference', {'reference': 'E-001'})
|
|
219
252
|
.first
|
|
220
253
|
.timeout(const Duration(seconds: 30),
|
|
221
|
-
onTimeout: () => throw StateError(
|
|
254
|
+
onTimeout: () => throw StateError(
|
|
255
|
+
'watch(query) never emitted its first snapshot'));
|
|
222
256
|
expect(byQuery, hasLength(1));
|
|
223
257
|
// The docs-ws session carries the Surfaced law — its pending-conflicts stream must emit a
|
|
224
258
|
// first snapshot (empty is correct; EMITTING at all is the regression under test).
|
|
@@ -234,10 +268,12 @@ void main() {
|
|
|
234
268
|
|
|
235
269
|
// ── LEG 6: the keyed lane end-to-end — an attested read served + SIGNED with the device
|
|
236
270
|
// key the 3b re-open installed. ──
|
|
237
|
-
await _leg('leg 6: attested read from the deviceKeyed workspace',
|
|
271
|
+
await _leg('leg 6: attested read from the deviceKeyed workspace',
|
|
272
|
+
() async {
|
|
238
273
|
final public = harness.devicePublicKey(_keyed);
|
|
239
274
|
expect(public, isNotNull);
|
|
240
|
-
expect(public, isNotEmpty,
|
|
275
|
+
expect(public, isNotEmpty,
|
|
276
|
+
reason: 'the 3b keygen public half rides the harness');
|
|
241
277
|
final cat = harness.session(_keyed);
|
|
242
278
|
final seeded = await cat.offerCreates(
|
|
243
279
|
domain: 'cat',
|
|
@@ -257,19 +293,23 @@ void main() {
|
|
|
257
293
|
args: {'listingId': 'L-1'},
|
|
258
294
|
);
|
|
259
295
|
final entry = (served['entry'] as Map).cast<String, Object?>();
|
|
260
|
-
final attestation =
|
|
296
|
+
final attestation =
|
|
297
|
+
(entry['attestation'] as Map).cast<String, Object?>();
|
|
261
298
|
expect((attestation['public_key'] ?? '').toString(), public,
|
|
262
299
|
reason: 'the read is signed by the re-opened session device key');
|
|
263
300
|
});
|
|
264
301
|
|
|
265
302
|
// ── LEG 7: harness diagnostics — assertDomainInstalled, resetWorkspaceCustody, and a
|
|
266
303
|
// mid-test seedWorkspaceFromDeployJson round-trip. ──
|
|
267
|
-
await _leg(
|
|
304
|
+
await _leg(
|
|
305
|
+
'leg 7: harness diagnostics (assertDomainInstalled / reset / seed)',
|
|
306
|
+
() async {
|
|
268
307
|
await harness.assertDomainInstalled(
|
|
269
|
-
workspace: estate.workspace, domain:
|
|
308
|
+
workspace: estate.workspace, domain: Co2Client.interface.domain);
|
|
270
309
|
// The negative arm names the active domains.
|
|
271
310
|
await expectLater(
|
|
272
|
-
harness.assertDomainInstalled(
|
|
311
|
+
harness.assertDomainInstalled(
|
|
312
|
+
workspace: 'docs-ws', domain: 'definitely-not-here'),
|
|
273
313
|
throwsA(isA<NomosTestHarnessException>()));
|
|
274
314
|
// Reset a workspace's local custody to its seed snapshot, then prove it still serves.
|
|
275
315
|
await harness.resetWorkspaceCustody('docs-ws');
|
|
@@ -278,23 +318,28 @@ void main() {
|
|
|
278
318
|
.watchPendingConflicts()
|
|
279
319
|
.first
|
|
280
320
|
.timeout(const Duration(seconds: 30),
|
|
281
|
-
onTimeout: () =>
|
|
282
|
-
|
|
283
|
-
expect(postReset, isEmpty,
|
|
321
|
+
onTimeout: () => throw StateError(
|
|
322
|
+
'the reset docs-ws session never served a read'));
|
|
323
|
+
expect(postReset, isEmpty,
|
|
324
|
+
reason: 'the reset workspace is back at its seed');
|
|
284
325
|
// Seed a brand-new workspace mid-test from a deploy.json and use it immediately.
|
|
285
326
|
final late = await harness.seedWorkspaceFromDeployJson(
|
|
286
327
|
workspace: 'late-ws', deployJsonPaths: [_docsDeploy]);
|
|
287
328
|
expect(late.workspace, 'late-ws');
|
|
288
|
-
await harness.assertDomainInstalled(
|
|
329
|
+
await harness.assertDomainInstalled(
|
|
330
|
+
workspace: 'late-ws', domain: 'docs');
|
|
289
331
|
});
|
|
290
332
|
|
|
291
333
|
// Local posture postscript (leg 3's zero-network promise, checked over the WHOLE run): no
|
|
292
334
|
// session on this plane ever attempted a network fetch.
|
|
293
335
|
final offending = diagnostics
|
|
294
|
-
.where((l) =>
|
|
336
|
+
.where((l) =>
|
|
337
|
+
l.contains('intent-offers') ||
|
|
338
|
+
l.contains('refusing network fetch'))
|
|
295
339
|
.toList();
|
|
296
340
|
expect(offending, isEmpty,
|
|
297
|
-
reason:
|
|
341
|
+
reason:
|
|
342
|
+
'local-only sessions must perform NO network work — saw:\n${offending.join('\n')}');
|
|
298
343
|
} finally {
|
|
299
344
|
// ── LEG 8: clean disposal within the deadline (a hung teardown is a failure, not a wedge). ──
|
|
300
345
|
await _leg('leg 8: harness disposal within the deadline', () async {
|
|
@@ -29,7 +29,7 @@ String? _missingPrereq() {
|
|
|
29
29
|
if (!File(_deployJson).existsSync()) {
|
|
30
30
|
return 'compile the fixture: (cd bench/scale/fixtures/estate-birth && node ../../../../dsl/compile_package.mjs)';
|
|
31
31
|
}
|
|
32
|
-
if (!File('$_runtimeDir/holon.wasm').existsSync())
|
|
32
|
+
if (!File('$_runtimeDir/holon-testmint.wasm').existsSync())
|
|
33
33
|
return 'warm ~/.holon/runtime first';
|
|
34
34
|
return null;
|
|
35
35
|
}
|
|
@@ -66,8 +66,10 @@ void main() {
|
|
|
66
66
|
);
|
|
67
67
|
expect(birth.born, hasLength(1));
|
|
68
68
|
|
|
69
|
-
// 3.
|
|
70
|
-
|
|
69
|
+
// 3. Infrastructure assertion: inspect Nomos's private birth record through the raw harness session.
|
|
70
|
+
// Generated tenant clients deliberately do not expose this framework query.
|
|
71
|
+
final before = await session
|
|
72
|
+
.query('birthRecordsByPrincipal', {'principal': _owner});
|
|
71
73
|
expect(before, hasLength(1),
|
|
72
74
|
reason: 'the offer must be readable BEFORE sync');
|
|
73
75
|
final statusBefore = await session.custodyStatus();
|
|
@@ -84,11 +86,14 @@ void main() {
|
|
|
84
86
|
expect(statusAfter.pendingLocalIntents, statusBefore.pendingLocalIntents,
|
|
85
87
|
reason:
|
|
86
88
|
'local-only sync must not consume/move pending local intents');
|
|
87
|
-
final after = await
|
|
89
|
+
final after = await session
|
|
90
|
+
.query('birthRecordsByPrincipal', {'principal': _owner});
|
|
88
91
|
expect(after, hasLength(1),
|
|
89
92
|
reason:
|
|
90
93
|
'local-only sync must be observationally inert — the row must still read');
|
|
91
|
-
|
|
94
|
+
final beforeData = ((before.single as Map)['data'] as Map);
|
|
95
|
+
final afterData = ((after.single as Map)['data'] as Map);
|
|
96
|
+
expect(afterData['principal'], beforeData['principal'],
|
|
92
97
|
reason: 'the SAME row, byte-for-byte semantics');
|
|
93
98
|
} finally {
|
|
94
99
|
await harness.dispose();
|
|
@@ -40,7 +40,7 @@ String? _missingPrereq() {
|
|
|
40
40
|
if (!File(_deploy).existsSync()) {
|
|
41
41
|
return 'compile the fixture: cd bench/scale/fixtures/estate-birth-signed && node ../../../../dsl/compile_package.mjs';
|
|
42
42
|
}
|
|
43
|
-
if (!File('$_runtimeDir/holon.wasm').existsSync()) {
|
|
43
|
+
if (!File('$_runtimeDir/holon-testmint.wasm').existsSync()) {
|
|
44
44
|
return 'warm ~/.holon/runtime first (run any `githolon` command once)';
|
|
45
45
|
}
|
|
46
46
|
return null;
|
|
@@ -34,7 +34,7 @@ String? _missingPrereq() {
|
|
|
34
34
|
if (!File(_deploy).existsSync() || !File(_lockfile).existsSync()) {
|
|
35
35
|
return 'missing encrypted-notes fixture — compile it: (cd cloud/web-client/test/fixtures/encrypted-notes && node ../../../../../dsl/compile_package.mjs)';
|
|
36
36
|
}
|
|
37
|
-
if (!File('$_runtimeDir/holon.wasm').existsSync() ||
|
|
37
|
+
if (!File('$_runtimeDir/holon-testmint.wasm').existsSync() ||
|
|
38
38
|
!File('$_runtimeDir/packages.json').existsSync()) {
|
|
39
39
|
return 'warm ~/.holon/runtime first (run any `githolon` command once)';
|
|
40
40
|
}
|
|
@@ -48,7 +48,7 @@ String? _missingPrereq() {
|
|
|
48
48
|
return 'compile the fixtures: (cd bench/scale/fixtures/estate-birth && node ../../../../dsl/compile_package.mjs); '
|
|
49
49
|
'(cd bench/scale/fixtures/surfaced && node ../../../../dsl/compile_package.mjs)';
|
|
50
50
|
}
|
|
51
|
-
if (!File('$_runtimeDir/holon.wasm').existsSync() ||
|
|
51
|
+
if (!File('$_runtimeDir/holon-testmint.wasm').existsSync() ||
|
|
52
52
|
!File('$_runtimeDir/packages.json').existsSync()) {
|
|
53
53
|
return 'warm ~/.holon/runtime first (run any `githolon` command once)';
|
|
54
54
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// the app UI uses, under `dart test` / `flutter test`, with no network.
|
|
5
5
|
//
|
|
6
6
|
// Prerequisites (skipped-with-reason when absent, so CI stays green either way): `node` on PATH; the
|
|
7
|
-
// in-repo runner; a warm `~/.holon/runtime` cache (holon.wasm + packages.json — one `githolon` run fills it);
|
|
7
|
+
// in-repo runner; a warm `~/.holon/runtime` cache (holon-testmint.wasm + packages.json — one `githolon` run fills it);
|
|
8
8
|
// and the compiled `examples/guestbook/build/guestbook.deploy.json` (`cd examples/guestbook && npx nomos-compile`).
|
|
9
9
|
@TestOn('vm')
|
|
10
10
|
library;
|
|
@@ -31,7 +31,7 @@ String? _missingPrereq() {
|
|
|
31
31
|
if (!File(_guestbookDeployJson).existsSync()) {
|
|
32
32
|
return 'compile the fixture first: cd examples/guestbook && npx nomos-compile';
|
|
33
33
|
}
|
|
34
|
-
if (!File('$_runtimeDir/holon.wasm').existsSync() ||
|
|
34
|
+
if (!File('$_runtimeDir/holon-testmint.wasm').existsSync() ||
|
|
35
35
|
!File('$_runtimeDir/packages.json').existsSync()) {
|
|
36
36
|
return 'warm ~/.holon/runtime first (run any `githolon` command once to cache the runtime)';
|
|
37
37
|
}
|
|
@@ -29,7 +29,7 @@ String? _missingPrereq() {
|
|
|
29
29
|
if (!File(_deployJson).existsSync()) {
|
|
30
30
|
return 'compile the fixture: cd bench/scale/fixtures/vo-read-typing && npx nomos-compile';
|
|
31
31
|
}
|
|
32
|
-
if (!File('$_runtimeDir/holon.wasm').existsSync() ||
|
|
32
|
+
if (!File('$_runtimeDir/holon-testmint.wasm').existsSync() ||
|
|
33
33
|
!File('$_runtimeDir/packages.json').existsSync()) {
|
|
34
34
|
return 'warm ~/.holon/runtime first (run any `githolon` command once)';
|
|
35
35
|
}
|
|
@@ -23,7 +23,7 @@ String? _missingPrereq() {
|
|
|
23
23
|
if (!File(_gbDeploy).existsSync()) {
|
|
24
24
|
return 'missing $_gbDeploy — compile it (cd examples/guestbook && npx nomos-compile)';
|
|
25
25
|
}
|
|
26
|
-
if (!File('$_runtimeDir/holon.wasm').existsSync() ||
|
|
26
|
+
if (!File('$_runtimeDir/holon-testmint.wasm').existsSync() ||
|
|
27
27
|
!File('$_runtimeDir/packages.json').existsSync()) {
|
|
28
28
|
return 'warm ~/.holon/runtime first (run any `githolon` command once)';
|
|
29
29
|
}
|