@githolon/dsl 0.102.1 → 0.102.3

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 CHANGED
@@ -1,3 +1,12 @@
1
+ ## 0.64.1
2
+
3
+ - Resolve a compiled OpenUSD application as one ordered package set in generated offline and connected
4
+ scenarios, keeping reference-only control law off user replicas while payload law births their workspaces.
5
+ - Pin source and published test runs to the exact coordinated kernel release, and make restart, reconnect and
6
+ clean reinstall use the same hidden multi-workspace custody lifecycle.
7
+ - Seat every business workspace founder on its private transport relation at genesis so public business
8
+ records can coexist with framework-private identity state and still reopen on a fresh device.
9
+
1
10
  ## 0.64.0
2
11
 
3
12
  - Generate one self-documenting application object: intent-shaped business actions at the root, typed
@@ -58,8 +58,14 @@ class NomosTestCloud {
58
58
  bool _online = true;
59
59
  bool _stopped = false;
60
60
 
61
- NomosTestCloud._(this._server, this._proxy, this._upstream, this._client,
62
- this._onDiagnostic, this.testKRootPublic);
61
+ NomosTestCloud._(
62
+ this._server,
63
+ this._proxy,
64
+ this._upstream,
65
+ this._client,
66
+ this._onDiagnostic,
67
+ this.testKRootPublic,
68
+ );
63
69
 
64
70
  /// The base URL a cloud-BOUND session connects to (`connect(cloud: cloud.origin)`). Points at the PARTITION
65
71
  /// PROXY, so [setOnline] deterministically severs/heals the whole plane's link to custody.
@@ -71,7 +77,8 @@ class NomosTestCloud {
71
77
  void setOnline(bool online) {
72
78
  _online = online;
73
79
  _log(
74
- 'partition: ${online ? "ONLINE (link up)" : "OFFLINE (link severed)"}');
80
+ 'partition: ${online ? "ONLINE (link up)" : "OFFLINE (link severed)"}',
81
+ );
75
82
  }
76
83
 
77
84
  bool get online => _online;
@@ -80,8 +87,9 @@ class NomosTestCloud {
80
87
  /// live cloud state DIRECTLY (bypassing the partition proxy) — a test assertion about custody must see the
81
88
  /// truth even while the app's link is partitioned OFFLINE.
82
89
  Future<RemoteWorkspaceState> remoteHead(String workspace) async {
83
- final body =
84
- await _getJson(_upstream.replace(path: '/v2/workspaces/$workspace'));
90
+ final body = await _getJson(
91
+ _upstream.replace(path: '/v2/workspaces/$workspace'),
92
+ );
85
93
  final ledger =
86
94
  (body['ledgerState'] as Map?)?.cast<String, Object?>() ?? const {};
87
95
  return RemoteWorkspaceState(
@@ -110,9 +118,14 @@ class NomosTestCloud {
110
118
  /// (the old lane — the harness POST-creating the platform — refuses 409 `unborn` by design).
111
119
  String? platform,
112
120
 
113
- /// Optional platform law to deploy at boot (`--deploy build/<name>.deploy.json`) — the dev server
114
- /// installs it on [platform] before serving. The harness may also deploy law itself post-boot.
121
+ /// Optional single platform law to deploy at boot. Prefer [deployJsonPaths] for a composed application.
115
122
  String? deployJsonPath,
123
+
124
+ /// Ordered application package set installed by the warranted bootstrap operator before serving.
125
+ /// Reference-only/control packages go first and remain active on the parent; the home payload package
126
+ /// goes last and is retained as the explicit law used to birth a receiving group. A payload package is
127
+ /// not made current on the parent merely to make it available to a child.
128
+ List<String> deployJsonPaths = const [],
116
129
  Duration readyTimeout = const Duration(seconds: 30),
117
130
  void Function(String line)? onDiagnostic,
118
131
  }) async {
@@ -124,19 +137,31 @@ class NomosTestCloud {
124
137
  if (ephemeral) '--ephemeral',
125
138
  if (dataDir != null) ...['--data', dataDir],
126
139
  if (platform != null) ...['--platform', platform],
127
- if (deployJsonPath != null) ...['--deploy', deployJsonPath],
140
+ for (final deploy in [
141
+ ...deployJsonPaths,
142
+ if (deployJsonPath != null && !deployJsonPaths.contains(deployJsonPath))
143
+ deployJsonPath,
144
+ ]) ...['--deploy', deploy],
128
145
  ];
129
146
  void log(String m) => onDiagnostic?.call('[nomos-test-cloud] $m');
130
147
  log('spawning: $node ${args.join(' ')}');
131
148
  final proc = await Process.start(node, args, mode: ProcessStartMode.normal);
149
+ final serverTail = <String>[];
150
+ void remember(String stream, String line) {
151
+ serverTail.add('$stream: $line');
152
+ if (serverTail.length > 24) serverTail.removeAt(0);
153
+ log('$stream: $line');
154
+ }
132
155
  proc.stdout
133
156
  .transform(utf8.decoder)
134
157
  .transform(const LineSplitter())
135
- .listen((l) => log('server: $l'));
158
+ .listen((l) => remember('server', l));
136
159
  proc.stderr
137
160
  .transform(utf8.decoder)
138
161
  .transform(const LineSplitter())
139
- .listen((l) => log('server!: $l'));
162
+ .listen((l) => remember('server!', l));
163
+ int? prematureExitCode;
164
+ unawaited(proc.exitCode.then((code) => prematureExitCode = code));
140
165
 
141
166
  final upstream = Uri.parse('http://127.0.0.1:$chosenPort');
142
167
  final client = HttpClient();
@@ -147,6 +172,7 @@ class NomosTestCloud {
147
172
  var ready = false;
148
173
  String? testKRootPublic;
149
174
  while (DateTime.now().isBefore(deadline)) {
175
+ if (prematureExitCode != null) break;
150
176
  try {
151
177
  final req = await client.getUrl(upstream.replace(path: '/health'));
152
178
  final resp = await req.close();
@@ -160,20 +186,35 @@ class NomosTestCloud {
160
186
  break;
161
187
  }
162
188
  }
163
- } catch (_) {/* not up yet */}
189
+ } catch (_) {
190
+ /* not up yet */
191
+ }
164
192
  await Future<void>.delayed(const Duration(milliseconds: 200));
165
193
  }
166
194
  if (!ready) {
167
- proc.kill(ProcessSignal.sigkill);
195
+ if (prematureExitCode == null) proc.kill(ProcessSignal.sigkill);
196
+ final exited = prematureExitCode;
197
+ final tail = serverTail.isEmpty ? '' : '\nserver tail:\n${serverTail.join('\n')}';
168
198
  throw NomosTestCloudException(
169
- 'the dev cloud did not answer /health${platform != null ? ' with platformReady:true' : ''} within ${readyTimeout.inSeconds}s (serverEntry: $serverEntry)');
199
+ exited == null
200
+ ? 'the dev cloud did not answer /health${platform != null ? ' with platformReady:true' : ''} within ${readyTimeout.inSeconds}s (serverEntry: $serverEntry)$tail'
201
+ : 'the dev cloud exited with code $exited before /health${platform != null ? ' reported platformReady:true' : ' became ready'} (serverEntry: $serverEntry)$tail',
202
+ );
170
203
  }
171
- log('dev cloud healthy at $upstream${platform != null ? ' (platform ceremony complete)' : ''}');
204
+ log(
205
+ 'dev cloud healthy at $upstream${platform != null ? ' (platform ceremony complete)' : ''}',
206
+ );
172
207
 
173
208
  // The partition proxy — forwards to the dev cloud when ONLINE, fails fast when OFFLINE.
174
209
  final proxy = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);
175
210
  final cloud = NomosTestCloud._(
176
- proc, proxy, upstream, client, onDiagnostic, testKRootPublic);
211
+ proc,
212
+ proxy,
213
+ upstream,
214
+ client,
215
+ onDiagnostic,
216
+ testKRootPublic,
217
+ );
177
218
  proxy.listen(cloud._handleProxy);
178
219
  log('partition proxy at ${cloud.origin} → $upstream');
179
220
  return cloud;
@@ -192,8 +233,9 @@ class NomosTestCloud {
192
233
  }
193
234
  try {
194
235
  final target = _upstream.replace(
195
- path: req.uri.path,
196
- query: req.uri.query.isEmpty ? null : req.uri.query);
236
+ path: req.uri.path,
237
+ query: req.uri.query.isEmpty ? null : req.uri.query,
238
+ );
197
239
  final fwd = await _client.openUrl(req.method, target);
198
240
  req.headers.forEach((name, values) {
199
241
  if (name.toLowerCase() != 'host')
@@ -220,7 +262,8 @@ class NomosTestCloud {
220
262
  final text = await resp.transform(utf8.decoder).join();
221
263
  if (resp.statusCode != 200) {
222
264
  throw NomosTestCloudException(
223
- 'GET $url → ${resp.statusCode}: ${text.substring(0, text.length.clamp(0, 200))}');
265
+ 'GET $url → ${resp.statusCode}: ${text.substring(0, text.length.clamp(0, 200))}',
266
+ );
224
267
  }
225
268
  return (jsonDecode(text) as Map).cast<String, Object?>();
226
269
  }
@@ -1,7 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ // <define:__NOMOS_KERNEL_RELEASE__>
4
+ var define_NOMOS_KERNEL_RELEASE_default = { production: { hash: "bc52f7eb630bc5a9a03b4c62b9b7ab58c4b6f25e687ecd51c72f9251763593fc", version: "kernel-bc52f7eb", features: ["compoundKeys", "capturedReads", "invariantGate", "surfacedMerge", "fieldProvenance", "nullSemantics", "attestedReads", "conflictSnapshots", "ownedAudience"], protocol: 1 }, testMint: { hash: "17d8f91b661dc8c3a6dea72d699f9f5759ab5e6b4e3e3e5e90afceb3a7034154", version: "kernel-17d8f91b", features: ["compoundKeys", "capturedReads", "invariantGate", "surfacedMerge", "fieldProvenance", "nullSemantics", "attestedReads", "conflictSnapshots", "ownedAudience"], protocol: 1 } };
5
+
3
6
  // runner/node-entry.ts
4
7
  import { createInterface } from "node:readline";
8
+ import { createHash } from "node:crypto";
5
9
  import { readFileSync } from "node:fs";
6
10
  import { homedir } from "node:os";
7
11
  import { join } from "node:path";
@@ -2597,12 +2601,20 @@ function parseBulkReadForbidden(status, bodyText) {
2597
2601
  // src/kernel_provider_wasm.ts
2598
2602
  var enc2 = new TextEncoder();
2599
2603
  var dec2 = new TextDecoder();
2600
- var snapB64 = (bytes) => {
2604
+ var nmsInternalSnapshotToBase64 = (bytes) => {
2605
+ if (typeof Buffer !== "undefined") {
2606
+ return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString("base64");
2607
+ }
2601
2608
  let s = "";
2602
- for (let i = 0; i < bytes.length; i += 32768) s += String.fromCharCode.apply(null, bytes.subarray(i, i + 32768));
2609
+ for (let i = 0; i < bytes.length; i += 32768) {
2610
+ s += String.fromCharCode.apply(null, bytes.subarray(i, i + 32768));
2611
+ }
2603
2612
  return btoa(s);
2604
2613
  };
2605
- var snapUnb64 = (s) => Uint8Array.from(atob(s), (c) => c.charCodeAt(0));
2614
+ var nmsInternalSnapshotFromBase64 = (s) => {
2615
+ if (typeof Buffer !== "undefined") return Buffer.from(s, "base64");
2616
+ return Uint8Array.from(atob(s), (c) => c.charCodeAt(0));
2617
+ };
2606
2618
  var unpack = (p) => {
2607
2619
  const v = typeof p === "bigint" ? p : BigInt(p);
2608
2620
  return { ptr: Number(v >> 32n), len: Number(v & 0xffffffffn) };
@@ -2647,7 +2659,7 @@ function wasmModuleFor(base, fetchImpl) {
2647
2659
  function parseSnapshot(bytes) {
2648
2660
  try {
2649
2661
  const o = JSON.parse(dec2.decode(bytes));
2650
- if (o && o.v === 2 && typeof o.tree === "string") return { treeBytes: snapUnb64(o.tree), ckpt: o.ckpt ?? null, frontier: o.frontier ?? null, syncedBase: o.syncedBase ?? null };
2662
+ if (o && o.v === 2 && typeof o.tree === "string") return { treeBytes: nmsInternalSnapshotFromBase64(o.tree), ckpt: o.ckpt ?? null, frontier: o.frontier ?? null, syncedBase: o.syncedBase ?? null };
2651
2663
  } catch {
2652
2664
  }
2653
2665
  return { treeBytes: bytes, ckpt: null, frontier: null, syncedBase: null };
@@ -2656,7 +2668,7 @@ function buildSnapshot(treeBytes, ckpt, syncedBase) {
2656
2668
  if ((!ckpt || !ckpt.blob && !ckpt.frontier) && !syncedBase) return treeBytes;
2657
2669
  return enc2.encode(JSON.stringify({
2658
2670
  v: 2,
2659
- tree: snapB64(treeBytes),
2671
+ tree: nmsInternalSnapshotToBase64(treeBytes),
2660
2672
  ckpt: ckpt && ckpt.blob ? ckpt.blob : null,
2661
2673
  frontier: ckpt ? ckpt.frontier ?? null : null,
2662
2674
  cursor: ckpt ? ckpt.cursor ?? null : null,
@@ -2896,7 +2908,7 @@ function createWasmEnginePlane({ span } = {}) {
2896
2908
  }
2897
2909
  const buf = new Uint8Array(await r.arrayBuffer());
2898
2910
  if (!buf.length) return { applied: false };
2899
- view.applyPack(snapB64(buf));
2911
+ view.applyPack(nmsInternalSnapshotToBase64(buf));
2900
2912
  return { applied: true };
2901
2913
  } finally {
2902
2914
  if (end) end();
@@ -9027,6 +9039,15 @@ function offlineOpenRealm() {
9027
9039
  const restorePath = process.env["NOMOS_OFFLINE_RESTORE"];
9028
9040
  const wasmName = process.env["NOMOS_OFFLINE_WASM"] || "holon.wasm";
9029
9041
  const wasm = readFileSync(join(runtimeDir, wasmName));
9042
+ if (typeof define_NOMOS_KERNEL_RELEASE_default !== "undefined") {
9043
+ const expected = wasmName === "holon-testmint.wasm" ? define_NOMOS_KERNEL_RELEASE_default.testMint.hash : define_NOMOS_KERNEL_RELEASE_default.production.hash;
9044
+ const actual = createHash("sha256").update(wasm).digest("hex");
9045
+ if (actual !== expected) {
9046
+ throw new Error(
9047
+ `offline runtime/kernel skew: ${wasmName} is ${actual.slice(0, 12)}\u2026, but this SDK requires ${expected.slice(0, 12)}\u2026; the installer must acquire the release-pinned runtime before boot`
9048
+ );
9049
+ }
9050
+ }
9030
9051
  const pkgs = readFileSync(join(runtimeDir, "packages.json"));
9031
9052
  let manifests = null;
9032
9053
  try {
@@ -5,15 +5,19 @@ var __export = (target, all) => {
5
5
  __defProp(target, name, { get: all[name], enumerable: true });
6
6
  };
7
7
 
8
+ // <define:__NOMOS_KERNEL_RELEASE__>
9
+ var define_NOMOS_KERNEL_RELEASE_default = { production: { hash: "bc52f7eb630bc5a9a03b4c62b9b7ab58c4b6f25e687ecd51c72f9251763593fc", version: "kernel-bc52f7eb", features: ["compoundKeys", "capturedReads", "invariantGate", "surfacedMerge", "fieldProvenance", "nullSemantics", "attestedReads", "conflictSnapshots", "ownedAudience"], protocol: 1 }, testMint: { hash: "17d8f91b661dc8c3a6dea72d699f9f5759ab5e6b4e3e3e5e90afceb3a7034154", version: "kernel-17d8f91b", features: ["compoundKeys", "capturedReads", "invariantGate", "surfacedMerge", "fieldProvenance", "nullSemantics", "attestedReads", "conflictSnapshots", "ownedAudience"], protocol: 1 } };
10
+
8
11
  // runner/offline-install.ts
9
- import { readFileSync, writeFileSync as writeFileSync2, existsSync } from "node:fs";
12
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync } from "node:fs";
10
13
  import { homedir } from "node:os";
11
14
  import { join as join2, dirname } from "node:path";
12
15
  import { randomBytes, webcrypto as wc } from "node:crypto";
13
16
  import { fileURLToPath } from "node:url";
14
17
 
15
18
  // runner/runtime-refresh.ts
16
- import { statSync, renameSync, writeFileSync, mkdirSync } from "node:fs";
19
+ import { statSync, renameSync, writeFileSync, mkdirSync, readFileSync } from "node:fs";
20
+ import { createHash } from "node:crypto";
17
21
  import { join } from "node:path";
18
22
  var RUNTIME_FRESH_MS = 15 * 60 * 1e3;
19
23
  var _refreshedDirs = /* @__PURE__ */ new Set();
@@ -32,12 +36,29 @@ function atomicWrite(target, data) {
32
36
  writeFileSync(tmp, data);
33
37
  renameSync(tmp, target);
34
38
  }
35
- async function refreshRuntimeDir(cloud, dir, { fetchImpl = globalThis.fetch, log: log2, mode } = {}) {
39
+ var sha256 = (bytes) => createHash("sha256").update(bytes).digest("hex");
40
+ function runtimeReleaseMismatch(dir, expected) {
41
+ try {
42
+ const production = sha256(readFileSync(join(dir, "holon.wasm")));
43
+ if (production !== expected.production) {
44
+ return `production kernel ${production.slice(0, 12)}\u2026; SDK requires ${expected.production.slice(0, 12)}\u2026`;
45
+ }
46
+ const testMint = sha256(readFileSync(join(dir, "holon-testmint.wasm")));
47
+ if (testMint !== expected.testMint) {
48
+ return `test-mint kernel ${testMint.slice(0, 12)}\u2026; SDK requires ${expected.testMint.slice(0, 12)}\u2026`;
49
+ }
50
+ return void 0;
51
+ } catch (error) {
52
+ return `runtime cache is incomplete (${error.message})`;
53
+ }
54
+ }
55
+ async function refreshRuntimeDir(cloud, dir, { fetchImpl = globalThis.fetch, log: log2, mode, expected } = {}) {
36
56
  const m = String(mode ?? process.env["NOMOS_RUNTIME_REFRESH"] ?? "auto").toLowerCase();
37
57
  if (m === "never") return false;
58
+ const coherent = expected === void 0 || runtimeReleaseMismatch(dir, expected) === void 0;
38
59
  if (m !== "always") {
39
- if (_refreshedDirs.has(dir)) return false;
40
- if (runtimeIsWarm(dir)) return false;
60
+ if (coherent && _refreshedDirs.has(dir)) return false;
61
+ if (coherent && runtimeIsWarm(dir)) return false;
41
62
  }
42
63
  try {
43
64
  const r = await fetchImpl(`${cloud}/v1/runtime/holon.wasm`);
@@ -46,6 +67,15 @@ async function refreshRuntimeDir(cloud, dir, { fetchImpl = globalThis.fetch, log
46
67
  const testmintResp = await fetchImpl(`${cloud}/v1/runtime/holon-testmint.wasm`);
47
68
  if (!testmintResp.ok) throw new Error(`test-mint runtime wasm \u2192 ${testmintResp.status}`);
48
69
  const testmintBytes = new Uint8Array(await testmintResp.arrayBuffer());
70
+ if (expected !== void 0) {
71
+ const productionHash = sha256(bytes);
72
+ const testMintHash = sha256(testmintBytes);
73
+ if (productionHash !== expected.production || testMintHash !== expected.testMint) {
74
+ throw new Error(
75
+ `cloud runtime does not match this SDK release (production ${productionHash.slice(0, 12)}\u2026/${expected.production.slice(0, 12)}\u2026, test-mint ${testMintHash.slice(0, 12)}\u2026/${expected.testMint.slice(0, 12)}\u2026)`
76
+ );
77
+ }
78
+ }
49
79
  const pkgsResp = await fetchImpl(`${cloud}/v1/runtime/packages`);
50
80
  if (!pkgsResp.ok) throw new Error(`runtime packages \u2192 ${pkgsResp.status}`);
51
81
  const pkgText = await pkgsResp.text();
@@ -56,7 +86,10 @@ async function refreshRuntimeDir(cloud, dir, { fetchImpl = globalThis.fetch, log
56
86
  _refreshedDirs.add(dir);
57
87
  if (typeof log2 === "function") log2(`offline-install: refreshed runtime cache from the cloud \u2192 ${dir}`);
58
88
  return true;
59
- } catch {
89
+ } catch (error) {
90
+ if (typeof log2 === "function" && expected !== void 0) {
91
+ log2(`offline-install: release-pinned runtime refresh failed: ${error.message}`);
92
+ }
60
93
  return false;
61
94
  }
62
95
  }
@@ -1863,11 +1896,16 @@ async function pushPack(remote, headers = {}, { updates = [], pack = null } = {}
1863
1896
  });
1864
1897
  cmds.push(FLUSH);
1865
1898
  const body = concatBytes([...cmds, pack && pack.length ? pack : EMPTY_PACK]);
1866
- const r = await fetch(`${remote}/git-receive-pack`, {
1867
- method: "POST",
1868
- headers: { "content-type": "application/x-git-receive-pack-request", ...headers },
1869
- body
1870
- });
1899
+ let r;
1900
+ try {
1901
+ r = await fetch(`${remote}/git-receive-pack`, {
1902
+ method: "POST",
1903
+ headers: { "content-type": "application/x-git-receive-pack-request", ...headers },
1904
+ body
1905
+ });
1906
+ } catch (error) {
1907
+ throw new Error(`receive-pack transport failed (${body.byteLength} bytes): ${String(error)}`, { cause: error });
1908
+ }
1871
1909
  const txt = dec2.decode(new Uint8Array(await r.arrayBuffer()));
1872
1910
  const unpacked = /unpack ok/.test(txt);
1873
1911
  const everyRefOk = updates.every((u) => new RegExp(`ok ${u.ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`).test(txt));
@@ -3322,7 +3360,12 @@ async function custodyFetch(eng2, ws, ledger, { remoteRef = "refs/heads/main", l
3322
3360
  async function custodyPush(eng2, ws, ledger, { localRef = null, remoteRef, del = false, pointer = false, newOid = null }) {
3323
3361
  const headers = ledger.headers || {};
3324
3362
  const remoteRefFull = fullRef(remoteRef);
3325
- const remote = await lsRefs(ledger.remote, headers, { service: "git-receive-pack", prefix: remoteRefFull });
3363
+ let remote;
3364
+ try {
3365
+ remote = await lsRefs(ledger.remote, headers, { service: "git-receive-pack", prefix: remoteRefFull });
3366
+ } catch (error) {
3367
+ throw new Error(`custody receive-pack advertisement failed for ${remoteRefFull}: ${String(error)}`, { cause: error });
3368
+ }
3326
3369
  const oldOid = remote[remoteRefFull] || ZERO_OID;
3327
3370
  if (del) {
3328
3371
  if (oldOid === ZERO_OID) return;
@@ -3336,14 +3379,21 @@ async function custodyPush(eng2, ws, ledger, { localRef = null, remoteRef, del =
3336
3379
  pack = oldOid !== ZERO_OID ? packDelta(eng2, ws, oldOid, tgt) : null;
3337
3380
  if (!pack) pack = packDelta(eng2, ws, "", tgt);
3338
3381
  }
3339
- return pushVia(ledger, () => pushPack(ledger.remote, headers, { updates: [{ ref: remoteRefFull, oldOid, newOid: tgt }], pack }));
3382
+ const packBytes = pack?.byteLength ?? 0;
3383
+ try {
3384
+ return await pushVia(ledger, () => pushPack(ledger.remote, headers, { updates: [{ ref: remoteRefFull, oldOid, newOid: tgt }], pack }));
3385
+ } catch (error) {
3386
+ throw new Error(`custody receive-pack push failed for ${remoteRefFull} (${packBytes} pack bytes): ${String(error)}`, { cause: error });
3387
+ }
3340
3388
  }
3341
3389
  async function commitAndPush(eng2, ws, ledger) {
3342
3390
  try {
3343
3391
  await custodyPush(eng2, ws, ledger, { remoteRef: "refs/heads/main" });
3344
3392
  } catch (e) {
3345
3393
  unmount(eng2, ws);
3346
- throw new Error(`ledger push failed \u2014 write NOT durable, rolled back to ledger: ${e}`);
3394
+ const cause = e instanceof Error ? e.cause : void 0;
3395
+ const detail = cause === void 0 ? String(e) : `${e}; cause: ${String(cause)}`;
3396
+ throw new Error(`ledger push failed \u2014 write NOT durable, rolled back to ledger: ${detail}`, { cause: e });
3347
3397
  }
3348
3398
  }
3349
3399
  async function pushShape(eng2, ws, ledger) {
@@ -4627,6 +4677,7 @@ function sealAttestationBatches(eng2, ws, domain, controllerHash, opts = {}) {
4627
4677
 
4628
4678
  // runner/offline-install.ts
4629
4679
  var HERE = dirname(fileURLToPath(import.meta.url));
4680
+ var EXPECTED_RUNTIME = typeof define_NOMOS_KERNEL_RELEASE_default === "undefined" ? void 0 : { production: define_NOMOS_KERNEL_RELEASE_default.production.hash, testMint: define_NOMOS_KERNEL_RELEASE_default.testMint.hash };
4630
4681
  var engineModule = process.env["NOMOS_ADAPTER_ENGINE"] ? await import(process.env["NOMOS_ADAPTER_ENGINE"]) : engine_exports;
4631
4682
  var treeModule = process.env["NOMOS_ADAPTER_TREE"] ? await import(process.env["NOMOS_ADAPTER_TREE"]) : { serializeTree };
4632
4683
  var { createEngine: createEngine2, mountWorkspace: mountWorkspace2, author: author2, installPayload: installPayload2, authorKeygen: authorKeygen2, refreshProjection: refreshProjection2, currentLaw: currentLaw2, exportCheckpoint: exportCheckpoint2 } = engineModule;
@@ -4654,14 +4705,25 @@ if (!outPath) die("no --out snapshot path given");
4654
4705
  var CLOUD = (process.env["NOMOS_CLOUD"] || "https://nomos.captainapp.co.uk").replace(/\/+$/, "");
4655
4706
  var STALE_RUNTIME_HINT = "your cached kernel runtime may be stale \u2014 rm -rf ~/.holon/runtime and re-run (it re-fetches from the cloud)";
4656
4707
  var looksLikeStaleRuntime = (msg) => /\bbbox param\b|missing.*param|non-numeric|unknown (op|query op)|unsupported op|unexpected (field|key)|no such op/i.test(msg);
4657
- await refreshRuntimeDir(CLOUD, runtimeDir, { log: (m) => process.stderr.write(m + "\n") });
4708
+ await refreshRuntimeDir(CLOUD, runtimeDir, {
4709
+ log: (m) => process.stderr.write(m + "\n"),
4710
+ ...EXPECTED_RUNTIME !== void 0 ? { expected: EXPECTED_RUNTIME } : {}
4711
+ });
4712
+ if (EXPECTED_RUNTIME !== void 0) {
4713
+ const mismatch = runtimeReleaseMismatch(runtimeDir, EXPECTED_RUNTIME);
4714
+ if (mismatch !== void 0) {
4715
+ die(
4716
+ `runtime release coherence failed: ${mismatch}. The SDK will not run generated law against a different kernel; reconnect once so it can fetch this release's runtime, or set NOMOS_OFFLINE_RUNTIME to the exact candidate bundle.`
4717
+ );
4718
+ }
4719
+ }
4658
4720
  var wasmFile = join2(runtimeDir, "holon-testmint.wasm");
4659
4721
  var pkgsFile = join2(runtimeDir, "packages.json");
4660
4722
  if (!existsSync(wasmFile) || !existsSync(pkgsFile)) {
4661
4723
  die(`test runtime cache missing at ${runtimeDir} (holon-testmint.wasm + packages.json). Warm it once with a \`githolon\` run (it fetches + caches the runtime), then re-run offline.`);
4662
4724
  }
4663
- var wasm = readFileSync(wasmFile);
4664
- var pkgs = JSON.parse(readFileSync(pkgsFile, "utf8"));
4725
+ var wasm = readFileSync2(wasmFile);
4726
+ var pkgs = JSON.parse(readFileSync2(pkgsFile, "utf8"));
4665
4727
  var wasmModule = await WebAssembly.compile(wasm);
4666
4728
  var replica = BigInt(`0x${randomBytes(8).toString("hex")}`) & (1n << 63n) - 1n;
4667
4729
  var eng = await createEngine2({ wasmModule, bootstrapPkg: pkgs.bootstrap, nomosPkg: pkgs.nomos, replica, installedBy: "nomos-test-harness" });
@@ -4688,7 +4750,7 @@ var lawHashes = [];
4688
4750
  var expectedDomainLaw = /* @__PURE__ */ new Map();
4689
4751
  for (const p of deployPaths) {
4690
4752
  if (!existsSync(p)) die(`deploy JSON not found: ${p} \u2014 run \`npx githolon compile\` first`);
4691
- const deploy2 = JSON.parse(readFileSync(p, "utf8"));
4753
+ const deploy2 = JSON.parse(readFileSync2(p, "utf8"));
4692
4754
  const usda = deploy2.packageUsda;
4693
4755
  if (typeof usda !== "string" || !usda.startsWith("#usda")) die(`${p} has no #usda packageUsda`);
4694
4756
  const lawHash = await sha256hex2(usda);