@githolon/testing 0.3.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.
@@ -0,0 +1,1370 @@
1
+ // AUTO-SYNCED from cloud/holon-host/src/engine.mjs by testing/build.mjs — DO NOT EDIT HERE.
2
+ // Nomos Cloud — THE ENGINE PLANE (one machinery, two hosts).
3
+ //
4
+ // Everything that touches a resident holon wasm instance + its /work tree lives HERE,
5
+ // extracted from the HolonDO so the 12GiB CONTAINER tier runs the IDENTICAL logic the
6
+ // edge DO runs (the old container worker was a drifting fork — killed). The hosts differ
7
+ // only in what they inject:
8
+ //
9
+ // * the EDGE host (worker.mjs HolonDO): wasm via the wrangler CompiledWasm import,
10
+ // replica from the DO id, ledgers minted from env.ARTIFACTS, runs in workerd;
11
+ // * the HEAVY host (container/holon-worker.mjs): wasm compiled from disk, replica
12
+ // from the container instance base, ledgers ARRIVE on each op (containers have no
13
+ // Workers bindings — the control-plane DO mints tokens and sends them), runs in Node.
14
+ //
15
+ // CONTROL PLANE STAYS IN THE DO: ownership secrets, manifest overlays, the DLQ store,
16
+ // quotas/rates, alarms. The engine RETURNS data (e.g. dead-letter entries with intent
17
+ // bytes) and the host persists. Runtime-neutral: workerd + Node 22 (globalThis.crypto,
18
+ // fetch, TextEncoder are the only ambient dependencies).
19
+ import { WASI, File, Directory, OpenFile, ConsoleStdout, PreopenDirectory } from "@bjorn3/browser_wasi_shim";
20
+ import git from "isomorphic-git";
21
+ import http from "isomorphic-git/http/web";
22
+ import { makeGitFs } from "./git-fs.mjs";
23
+ import { serializeTree } from "./tree.mjs";
24
+
25
+ const enc = new TextEncoder(), dec = new TextDecoder();
26
+ const BRANCH = "main";
27
+ export const repoArgOf = (ws) => `/work/ws/${ws}`;
28
+ export const gitdirOf = (ws) => `/work/ws/${ws}/nomos.git`;
29
+
30
+ const unpack = (p) => { const v = typeof p === "bigint" ? p : BigInt(p); return { ptr: Number(v >> 32n), len: Number(v & 0xffffffffn) }; };
31
+ export async function sha256hex(t) { const b = await crypto.subtle.digest("SHA-256", enc.encode(t)); return [...new Uint8Array(b)].map((x) => x.toString(16).padStart(2, "0")).join(""); }
32
+ function osEntropyBuffer(n) { const bytes = new Uint8Array(n * 8); for (let o = 0; o < bytes.length; o += 65536) crypto.getRandomValues(bytes.subarray(o, Math.min(o + 65536, bytes.length))); const out = new Array(n), T = 2 ** 53; for (let i = 0; i < n; i++) { let z = 0n; for (let b = 7; b >= 0; b--) z = (z << 8n) | BigInt(bytes[i * 8 + b]); out[i] = Number(z >> 11n) / T; } return out; }
33
+ // Emit BigInt values as RAW JSON integers (a 63-bit replica must not round through a JS Number).
34
+ function stringifyBig(o) { return JSON.stringify(o, (_k, v) => (typeof v === "bigint" ? `@@B:${v}@@` : v)).replace(/"@@B:(\d+)@@"/g, "$1"); }
35
+ const log = (evt, fields = {}) => console.log(JSON.stringify({ evt, t: Date.now(), ...fields }));
36
+
37
+ function call(ex, mode, fields, STDERR) {
38
+ const req = enc.encode(JSON.stringify({ mode, ...fields }));
39
+ const reqPtr = ex.git_holon_alloc(req.length);
40
+ try {
41
+ new Uint8Array(ex.memory.buffer, reqPtr, req.length).set(req);
42
+ const { ptr, len } = unpack(ex.git_holon_call(reqPtr, req.length));
43
+ try {
44
+ const e = JSON.parse(dec.decode(new Uint8Array(ex.memory.buffer, ptr, len).slice()));
45
+ if (!e.ok) throw new Error((e.error || "holon error") + (STDERR.length ? ` | ${STDERR.slice(-4).join(" / ")}` : ""));
46
+ return e.result;
47
+ } finally { ex.git_holon_dealloc(ptr, len); }
48
+ } finally { ex.git_holon_dealloc(reqPtr, req.length); }
49
+ }
50
+
51
+ function seedManifests(m, strings) {
52
+ m.set("domain_manifests.json", new File(enc.encode(strings.read)));
53
+ m.set("identity-manifests.json", new File(enc.encode(strings.identity)));
54
+ }
55
+
56
+ export const installPayload = (hash, usda, installedBy) => ({ domainHash: hash, packageUsda: usda, installedBy, authorityScope: "workspace/root", dependencies: [], finalizers: [] });
57
+
58
+ /**
59
+ * Boot the resident wasm + /work tree. `wasmModule` is a PRECOMPILED WebAssembly.Module
60
+ * (the host compiles its way); `manifestStrings` = {read, identity} (baked ∪ overlays —
61
+ * the HOST owns overlay storage and recomputes); `replica` = the host's unique 63-bit id.
62
+ */
63
+ export async function createEngine({ wasmModule, bootstrapPkg, nomosPkg, manifestStrings, replica, installedBy = "nomos-cloud" }) {
64
+ const hashes = { bootstrap: await sha256hex(bootstrapPkg), nomos: await sha256hex(nomosPkg) };
65
+ const root = new Map();
66
+ seedManifests(root, manifestStrings);
67
+ root.set("domain.package.usda", new File(enc.encode(bootstrapPkg)));
68
+ root.set("ws", new Directory(new Map()));
69
+ const preopen = new PreopenDirectory("/work", root);
70
+ const STDERR = [];
71
+ const fds = [new OpenFile(new File([])), ConsoleStdout.lineBuffered(() => {}), ConsoleStdout.lineBuffered((l) => STDERR.push(l)), preopen];
72
+ const wasi = new WASI(["wasm_git_holon", "reactor"], [], fds, { debug: false });
73
+ const inst = await WebAssembly.instantiate(wasmModule, { wasi_snapshot_preview1: wasi.wasiImport });
74
+ const code = wasi.start(inst);
75
+ if (code !== 0) throw new Error(`reactor init exited ${code}; stderr=${STDERR.join("\n")}`);
76
+ for (const n of ["memory", "git_holon_alloc", "git_holon_dealloc", "git_holon_call"]) if (!inst.exports[n]) throw new Error(`missing export ${n}`);
77
+ const eng = {
78
+ ex: inst.exports, preopen, STDERR, seq: 0, replica, hashes,
79
+ bootstrapPkg, nomosPkg, manifestStrings, installedBy,
80
+ fs: makeGitFs(preopen, "/work", { File, Directory }),
81
+ mounted: new Map(), // ws -> { restored, remoteMain, restoreError }
82
+ mainIntents: new Map(), // ws -> { head, ids:Set, oids:Set } (admission idempotence)
83
+ knownDomainsCache: null,
84
+ };
85
+ return eng;
86
+ }
87
+
88
+ /** Swap the manifest strings (a deploy staged a new overlay) + restage every mount. */
89
+ export function setManifests(eng, strings) {
90
+ eng.manifestStrings = strings;
91
+ eng.knownDomainsCache = null;
92
+ eng.directiveRoutesCache = null;
93
+ eng.scopedReadsCache = null;
94
+ eng.placementDirectivesCache = null;
95
+ eng.tenantTypesCache = null;
96
+ eng.globalAggregatesCache = null;
97
+ const root = eng.preopen.dir.contents;
98
+ seedManifests(root, strings);
99
+ const wsRoot = root.get("ws");
100
+ for (const ws of eng.mounted.keys()) {
101
+ const wsDir = wsRoot.contents.get(ws);
102
+ if (wsDir) seedManifests(wsDir.contents, strings);
103
+ }
104
+ }
105
+
106
+ /** The domains DEPLOYED to this workspace = the effective identity map's keys. */
107
+ export function knownDomains(eng) {
108
+ if (eng.knownDomainsCache) return eng.knownDomainsCache;
109
+ eng.knownDomainsCache = new Set(Object.keys(JSON.parse(eng.manifestStrings.identity)));
110
+ return eng.knownDomainsCache;
111
+ }
112
+
113
+ // ── TRANSPARENT SHARDING: the WRONG-HOME GATE (the edge bailiff — sharding slice 2) ──
114
+ //
115
+ // The LAW lives in two places the bailiff only READS: the identity manifests'
116
+ // hash-bearing `routes` table (which payload field carries a directive's home, derived
117
+ // at compile from the plan itself), and the coordinator chain's `NomosShardAssignment`
118
+ // rows (the shard map as law-state). The worker DECIDES NOTHING — exactly the quota-law
119
+ // posture: it compares the intent's home against the map and answers with a TYPED
120
+ // refusal naming the correct workspace. Since slice 3a (#41) the CHAIN GATE ITSELF also
121
+ // enforces wrong-home (the law's derived `nomosWrongHome:<directive>` workspace
122
+ // invariants, judged against the shard's own NomosShardIdentity law-state at author,
123
+ // admit AND verify_chain) — this bailiff is RETAINED as defense-in-depth in front of it.
124
+
125
+ /** directive routes off the effective identity manifests: `${domain} ${directiveId}` → route. */
126
+ export function directiveRoutes(eng) {
127
+ if (eng.directiveRoutesCache) return eng.directiveRoutesCache;
128
+ const routes = new Map();
129
+ let identity = {};
130
+ try { identity = JSON.parse(eng.manifestStrings.identity); } catch {}
131
+ for (const [domain, manifestJson] of Object.entries(identity)) {
132
+ try {
133
+ const m = typeof manifestJson === "string" ? JSON.parse(manifestJson) : manifestJson;
134
+ for (const r of (m && m.routes) || []) routes.set(`${domain} ${r.directive}`, r);
135
+ } catch {}
136
+ }
137
+ eng.directiveRoutesCache = routes;
138
+ return routes;
139
+ }
140
+
141
+ /** The 48-bit route tag of a home key (must agree byte-for-byte with the client mint). */
142
+ const routeTagHexOfHomeKey = async (homeKey) => (await sha256hex(`nomos-route:${homeKey}`)).slice(0, 12);
143
+ /** The tag slot of a kernel-minted id (the UUIDv7 leading 12 hex chars). */
144
+ function routeTagHexOfMintedId(id) {
145
+ const i = id.indexOf("_");
146
+ if (i <= 0) return undefined;
147
+ const body = id.slice(i + 1).replace(/-/g, "").toLowerCase();
148
+ return /^[0-9a-f]{32}$/.test(body) ? body.slice(0, 12) : undefined;
149
+ }
150
+
151
+ /**
152
+ * Judge ONE routed intent against the shard map. `shardRouting` is HOST-INJECTED:
153
+ * `{ selfLabel, coordinatorWs, assignments() }` — `selfLabel` is this workspace's shard
154
+ * label (`s<k>`) or null when it IS the coordinator; `assignments()` resolves the
155
+ * ACTIVE `NomosShardAssignment` rows from the coordinator's law-state. Returns null to
156
+ * pass, or `{ error, wrongHome?, deadLettered? }`:
157
+ * * assigned elsewhere → the TYPED `wrong-home` refusal naming the correct workspace
158
+ * (never dead-lettered: a routed client self-heals it);
159
+ * * no placement at all → a DEAD-LETTERED jam with the placement remedy (legitimate
160
+ * work, never lost — the DLQ discipline).
161
+ */
162
+ export async function wrongHomeVerdict(shardRouting, route, payload) {
163
+ const value = payload ? payload[route.key] : undefined;
164
+ if (typeof value !== "string" || !value) return null; // shape problems are the law's verdict, not the bailiff's
165
+ const rows = (await shardRouting.assignments()) || [];
166
+ let assignment = null;
167
+ if (route.via === "axis") {
168
+ assignment = rows.find((a) => a.homeKey === value) || null;
169
+ } else {
170
+ const tag = routeTagHexOfMintedId(value);
171
+ if (tag !== undefined) {
172
+ for (const a of rows) {
173
+ if ((await routeTagHexOfHomeKey(a.homeKey)) === tag) { assignment = a; break; }
174
+ }
175
+ }
176
+ }
177
+ if (!assignment) {
178
+ return {
179
+ error: `no shard placement for this write's home ('${route.key}': ${value.slice(0, 80)}) — author the derived placement intent (birth<Type>) on the coordinator '${shardRouting.coordinatorWs}' first, then POST /dead-letters/retry`,
180
+ deadLettered: true,
181
+ };
182
+ }
183
+ // §6 — HOME-MOVING (slice 5): a home mid-handoff accepts NO writes anywhere — the
184
+ // typed refusal PARKS the work (dead-lettered on both sides, never lost); a routed
185
+ // client retries it onto the post-split home once the handoff seals.
186
+ const movingTo = typeof assignment.status === "string" && assignment.status.startsWith("moving:")
187
+ ? assignment.status.slice("moving:".length)
188
+ : null;
189
+ if (movingTo) {
190
+ return {
191
+ error: `home-moving: home '${value.slice(0, 80)}' is migrating ${assignment.shard} -> ${movingTo} (shard map v${assignment.mapVersion ?? 1}) — parked; a routed client retries automatically after the handoff seals`,
192
+ homeMoving: { homeKey: value, from: assignment.shard, target: movingTo, workspace: `${shardRouting.coordinatorWs}--${movingTo}` },
193
+ deadLettered: true,
194
+ };
195
+ }
196
+ if (shardRouting.selfLabel === assignment.shard) return null;
197
+ const correct = `${shardRouting.coordinatorWs}--${assignment.shard}`;
198
+ return {
199
+ error: `wrong-home: this write's home is assigned to '${correct}' (shard map v${assignment.mapVersion ?? 1}) — the client's shard map is stale; a routed client re-routes and retries automatically`,
200
+ wrongHome: { workspace: correct, ...(assignment.mapVersion !== undefined ? { mapVersion: assignment.mapVersion } : {}) },
201
+ };
202
+ }
203
+
204
+ // ── §5.2 — THE DELTA LANE (sharding slice 3): the shard-side EMITTER machinery ──
205
+ //
206
+ // After a shard's edge admission folds a batch, the HOST authors one coalesced
207
+ // `nomosPropagateSummary` Order into the COORDINATOR's chain: per touched
208
+ // (scoped read, group) the ABSOLUTE subtotal + the covered range's per-intent
209
+ // event deltas as content-addressed evidence. The coordinator's ONE gate
210
+ // adjudicates it (frontier contiguity + held-vs-claimed recomputation — the law
211
+ // in workspace_sharding.ts); the engine here only DERIVES the evidence, with the
212
+ // shard's own projection as the oracle (maintained tallies read before/after each
213
+ // admitted intent — O(touched), never the world).
214
+
215
+ /** The estate-total bucket separator (must agree with the law's `summaryBucket`). */
216
+ const SUMMARY_SEP = "\u001f";
217
+ const summaryKey = (readId, group) => `${readId}${SUMMARY_SEP}${group}`;
218
+ const splitSummaryKey = (key) => { const i = key.indexOf(SUMMARY_SEP); return [key.slice(0, i), key.slice(i + 1)]; };
219
+ /** The synthetic per-shard row-count read (capacity headroom — the split policy's input). */
220
+ export const NOMOS_SHARD_ROWS_READ = "nomosShardRows";
221
+ // FRAMEWORK aggregates are EXCLUDED from the row-count read (and from deep-verify's
222
+ // recount): identity/receipt rows enter chains through the deploy + receipt legs
223
+ // (never via session admission), so counting them would let the lanes drift. The
224
+ // load metric is TENANT rows — which is what capacity headroom means anyway.
225
+ const FRAMEWORK_AGG_TYPES = new Set([
226
+ "NomosShardAssignment", "NomosShardIdentity", "NomosHomeReceipt", "NomosShardRegistry",
227
+ "NomosShardPolicy", "NomosSummarySubtotal", "NomosSummaryFrontier", "NomosDeepVerify",
228
+ "NomosCheckpointSeal",
229
+ // CONTROL-PLANE law-state (every chain carries them via installDomain) — never tenant data:
230
+ "PolicyBundle", "DomainInstallation",
231
+ ]);
232
+
233
+ /** TENANT aggregate types = the read manifest's declared field-kind types minus the
234
+ * framework set — the ONE definition all three row-count lanes share (the capture,
235
+ * the suffix re-derivation, deep-verify's recount): control-plane rows (PolicyBundle,
236
+ * DomainInstallation) and framework law-state never skew the capacity metric. */
237
+ function tenantTypes(eng) {
238
+ if (eng.tenantTypesCache) return eng.tenantTypesCache;
239
+ let readManifest = {};
240
+ try { readManifest = JSON.parse(eng.manifestStrings.read); } catch {}
241
+ // §5.4 (slice 4): GLOBAL reference data is replicated custody on every shard —
242
+ // its rows arrive by the reverse lane (replicateIntents — a host leg the §5.2
243
+ // capture never sees), so counting them as tenant load would let the three
244
+ // row-count lanes drift (capture vs suffix vs deep-verify recount). Excluded
245
+ // from the capacity metric on EVERY lane, by the one shared definition here.
246
+ const globals = globalAggregates(eng);
247
+ const out = new Set(Object.keys(readManifest.aggregateFieldKinds || {}).filter((t) => !FRAMEWORK_AGG_TYPES.has(t) && !globals.has(t)));
248
+ eng.tenantTypesCache = out;
249
+ return out;
250
+ }
251
+
252
+ /** The derived PLACEMENT directives off the effective identity manifests:
253
+ * packed type `site` ⇒ `birthSite` keyed by payload field `siteId` (the law's naming). */
254
+ export function placementDirectives(eng) {
255
+ if (eng.placementDirectivesCache) return eng.placementDirectivesCache;
256
+ const out = new Map(); // directiveId -> { axis, keyField }
257
+ const pascal = (s) => s.replace(/[_\s-]+(\w)/g, (_m, c) => c.toUpperCase()).replace(/^./, (c) => c.toUpperCase());
258
+ let identity = {};
259
+ try { identity = JSON.parse(eng.manifestStrings.identity); } catch {}
260
+ for (const manifestJson of Object.values(identity)) {
261
+ let m;
262
+ try { m = typeof manifestJson === "string" ? JSON.parse(manifestJson) : manifestJson; } catch { continue; }
263
+ for (const t of (m && m.workspaceTypes) || []) {
264
+ if (!t || t.mode !== "packed") continue;
265
+ const p = pascal(String(t.id));
266
+ out.set(`birth${p}`, { axis: t.id, keyField: `${p.length ? p[0].toLowerCase() + p.slice(1) : p}Id` });
267
+ }
268
+ }
269
+ eng.placementDirectivesCache = out;
270
+ return out;
271
+ }
272
+
273
+ /** The ESTATE-SCOPED maintained reads off the effective identity manifests.
274
+ * SLICE 4: `where` (the canonical predicate, hash-bearing in the manifest) rides
275
+ * along — the LIVE capture's oracle is the projection's maintained tally (already
276
+ * predicate-aware in the kernel); the SUFFIX re-derivation evaluates it host-side
277
+ * ({@link evalCanonicalPred}) so both lanes agree byte-for-byte. */
278
+ export function scopedReads(eng) {
279
+ if (eng.scopedReadsCache) return eng.scopedReadsCache;
280
+ const out = [];
281
+ let identity = {};
282
+ try { identity = JSON.parse(eng.manifestStrings.identity); } catch {}
283
+ for (const manifestJson of Object.values(identity)) {
284
+ let m;
285
+ try { m = typeof manifestJson === "string" ? JSON.parse(manifestJson) : manifestJson; } catch { continue; }
286
+ for (const c of (m && m.counts) || []) {
287
+ if (c && typeof c.scope === "string") out.push({ id: c.id, kind: "count", of: c.of, by: c.by ?? null, ...(c.where ? { where: c.where } : {}) });
288
+ }
289
+ for (const s of (m && m.sums) || []) {
290
+ if (s && typeof s.scope === "string") out.push({ id: s.id, kind: "sum", of: s.of, by: s.by ?? null, field: s.sumField, ...(s.where ? { where: s.where } : {}) });
291
+ }
292
+ }
293
+ eng.scopedReadsCache = out;
294
+ return out;
295
+ }
296
+
297
+ /**
298
+ * Evaluate a canonical count/sum predicate (`predicate.ts` CanonicalPred — eq/ne
299
+ * clauses + and/or trees) over a host-folded field map. EXACTLY the kernel's
300
+ * declared semantics: values canonicalize to strings (int fields compare their
301
+ * decimal form); `eq` over an ABSENT field is FALSE, `ne` over an absent field is
302
+ * TRUE (unset ≠ any literal — the §7-D symmetry rule). Used ONLY by the suffix
303
+ * re-derivation fold; the live capture's oracle is the maintained tally itself.
304
+ */
305
+ export function evalCanonicalPred(pred, get) {
306
+ if (!pred || typeof pred !== "object") return true;
307
+ if (pred.kind === "and") return (pred.clauses || []).every((c) => evalCanonicalPred(c, get));
308
+ if (pred.kind === "or") return (pred.clauses || []).some((c) => evalCanonicalPred(c, get));
309
+ const raw = get(pred.field);
310
+ const present = raw !== undefined && raw !== null;
311
+ const canonical = present ? String(raw) : undefined;
312
+ if (pred.kind === "eq") return present && canonical === pred.value;
313
+ if (pred.kind === "ne") return !present || canonical !== pred.value;
314
+ return true; // an unknown clause kind never silently filters — fail-open here is fail-CLOSED at the gate (values mismatch loudly)
315
+ }
316
+
317
+ /**
318
+ * THE GLOBAL (reference-data) AGGREGATES (§5.4, slice 4): the identity manifests'
319
+ * `workspaceTypes[].globals` — aggregates homed on the COORDINATOR and replicated
320
+ * DOWNWARD into every shard chain by re-admission (the reverse delta lane).
321
+ */
322
+ export function globalAggregates(eng) {
323
+ if (eng.globalAggregatesCache) return eng.globalAggregatesCache;
324
+ const out = new Set();
325
+ let identity = {};
326
+ try { identity = JSON.parse(eng.manifestStrings.identity); } catch {}
327
+ for (const manifestJson of Object.values(identity)) {
328
+ let m;
329
+ try { m = typeof manifestJson === "string" ? JSON.parse(manifestJson) : manifestJson; } catch { continue; }
330
+ for (const t of (m && m.workspaceTypes) || []) {
331
+ for (const g of (t && t.globals) || []) if (typeof g === "string") out.add(g);
332
+ }
333
+ }
334
+ eng.globalAggregatesCache = out;
335
+ return out;
336
+ }
337
+
338
+ /** Parse an intent blob's sealed events: [{aggregate, ops: Map(field -> {Str|Int})}]. */
339
+ function parseIntentEvents(blob) {
340
+ let doc;
341
+ try { doc = JSON.parse(dec.decode(blob)); } catch { return []; }
342
+ const events = Array.isArray(doc && doc.events) ? doc.events : [];
343
+ return events.map((ev) => {
344
+ const sets = new Map();
345
+ for (const op of ev.ops || []) {
346
+ const v = op && op.op && op.op.Set;
347
+ if (v === undefined) continue;
348
+ if (typeof v.Str === "string") sets.set(op.field, v.Str);
349
+ else if (typeof v.Int === "number") sets.set(op.field, v.Int);
350
+ }
351
+ return { aggregate: String(ev.aggregate || ""), sets };
352
+ });
353
+ }
354
+
355
+ const groupOf = (read, data) => {
356
+ if (read.by === null) return "";
357
+ const v = data ? data[read.by] : undefined;
358
+ return v === undefined || v === null ? undefined : String(v);
359
+ };
360
+ const tallyOf = (eng, ws, read, group) =>
361
+ read.kind === "count" ? count(eng, ws, read.id, group).count : sum(eng, ws, read.id, group).sum;
362
+
363
+ /**
364
+ * Begin one admitted intent's summary capture: snapshot the touched aggregates +
365
+ * the maintained tallies of every CANDIDATE (read, group) BEFORE the fold. The
366
+ * candidates come from the intent's own sealed ops (new group values ride the
367
+ * Set ops) plus the pre-state rows (old group values) — bounded by the intent.
368
+ */
369
+ function beginSummaryCapture(eng, ws, ctx, blob) {
370
+ const events = parseIntentEvents(blob);
371
+ if (events.length === 0) return null;
372
+ const pre = new Map(); // aggregateId -> {type, data} | null
373
+ const candidates = new Set(); // summaryKey(readId, group)
374
+ for (const ev of events) {
375
+ if (!pre.has(ev.aggregate)) {
376
+ const rows = qById(eng, ws, ev.aggregate);
377
+ pre.set(ev.aggregate, rows.length ? { type: rows[0].type, data: rows[0].data || {} } : null);
378
+ }
379
+ const before = pre.get(ev.aggregate);
380
+ const evType = ev.sets.get("__type") ?? (before ? before.type : undefined);
381
+ for (const read of ctx.reads) {
382
+ if (read.of !== evType) continue;
383
+ const oldGroup = before ? groupOf(read, before.data) : undefined;
384
+ if (oldGroup !== undefined) candidates.add(summaryKey(read.id, oldGroup));
385
+ if (read.by === null) candidates.add(summaryKey(read.id, ""));
386
+ else if (ev.sets.has(read.by)) candidates.add(summaryKey(read.id, String(ev.sets.get(read.by))));
387
+ }
388
+ }
389
+ // Tally every candidate BEFORE the fold; record the batch-start value once per group.
390
+ const preTallies = new Map();
391
+ for (const key of candidates) {
392
+ const [readId, group] = splitSummaryKey(key);
393
+ const read = ctx.reads.find((r) => r.id === readId);
394
+ const v = tallyOf(eng, ws, read, group);
395
+ preTallies.set(key, v);
396
+ if (!ctx.prevs.has(key)) ctx.prevs.set(key, { read, group, value: v });
397
+ }
398
+ return { events, pre, candidates, preTallies };
399
+ }
400
+
401
+ /** Finish the capture AFTER the fold: re-tally, diff, append the evidence event. */
402
+ function finishSummaryCapture(eng, ws, ctx, cap, oid, intentId) {
403
+ const deltas = [];
404
+ for (const key of cap.candidates) {
405
+ const [readId, group] = splitSummaryKey(key);
406
+ const read = ctx.reads.find((r) => r.id === readId);
407
+ const after = tallyOf(eng, ws, read, group);
408
+ const before = cap.preTallies.get(key) ?? 0;
409
+ if (after !== before) deltas.push({ readId, group, delta: after - before });
410
+ ctx.values.set(key, { read, group, value: after });
411
+ }
412
+ // The synthetic row-count read: existence flips, with the projection as oracle.
413
+ // The ABSOLUTE rides relative to the coordinator-held value (the HOST closes it
414
+ // at emit time — a shard never enumerates its own whole projection per batch).
415
+ let rowsDelta = 0;
416
+ for (const [aggregateId, before] of cap.pre) {
417
+ const rows = qById(eng, ws, aggregateId);
418
+ const type = rows.length ? rows[0].type : before ? before.type : undefined;
419
+ if (type === undefined || !tenantTypes(eng).has(type)) continue; // tenant rows only
420
+ const existsAfter = rows.length > 0;
421
+ if (!before && existsAfter) rowsDelta += 1;
422
+ else if (before && !existsAfter) rowsDelta -= 1;
423
+ }
424
+ if (rowsDelta !== 0) {
425
+ deltas.push({ readId: NOMOS_SHARD_ROWS_READ, group: "", delta: rowsDelta });
426
+ ctx.rowsDelta += rowsDelta;
427
+ }
428
+ if (deltas.length > 0) ctx.events.push({ oid, intentId: intentId || "", deltas });
429
+ }
430
+
431
+ /**
432
+ * The coalesced delta of one admission batch (null when nothing summarizable moved).
433
+ * `rowsDelta` rides separately: the HOST closes the `nomosShardRows` absolute against
434
+ * the coordinator-held value at emit time (held + Σ deltas — the gate re-checks both
435
+ * halves). The other subtotals' `prev` is the batch-start maintained tally, which
436
+ * equals the coordinator-held value exactly when the frontier is contiguous — and the
437
+ * gate refuses otherwise (the suffix re-derivation lane is the remedy).
438
+ */
439
+ function summaryOfCapture(ctx, fromOid, toOid) {
440
+ if (ctx.events.length === 0 || !toOid || fromOid === toOid) return null;
441
+ const subtotals = [];
442
+ for (const [key, cur] of ctx.values) {
443
+ const prev = ctx.prevs.get(key);
444
+ const prevValue = prev ? prev.value : 0;
445
+ if (cur.value === prevValue) continue; // untouched-in-net — carries nothing
446
+ subtotals.push({
447
+ readId: cur.read.id,
448
+ group: cur.group,
449
+ kind: cur.read.kind,
450
+ prev: prevValue,
451
+ value: cur.value,
452
+ });
453
+ }
454
+ if (subtotals.length === 0 && ctx.rowsDelta === 0) return null;
455
+ return {
456
+ fromOid: fromOid || "",
457
+ toOid,
458
+ subtotals: subtotals.sort((a, b) => (summaryKey(a.readId, a.group) < summaryKey(b.readId, b.group) ? -1 : 1)),
459
+ events: ctx.events,
460
+ rowsDelta: ctx.rowsDelta,
461
+ };
462
+ }
463
+
464
+ /**
465
+ * THE SUFFIX RE-DERIVATION (the frontier-gap self-heal + /summaries/resync): derive
466
+ * the delta payload for (fromOid → main head] from THE CHAIN ITSELF — a host-side
467
+ * pure fold of the scoped reads' group/sum fields over every commit since genesis
468
+ * (Lww in chain order; scoped reads are predicate-free by the compile gate). Used
469
+ * when the coordinator's recorded frontier does not match a live capture (a lost
470
+ * emission, DO eviction, an operator resync) — O(chain), explicit, logged.
471
+ */
472
+ export async function deriveSummarySuffix(eng, ws, fromOid) {
473
+ const reads = scopedReads(eng);
474
+ const gitdir = gitdirOf(ws);
475
+ const head = await git.resolveRef({ fs: eng.fs, gitdir, ref: BRANCH }).catch(() => null);
476
+ if (!head) return { ok: false, error: "no local main to derive from" };
477
+ const commits = await git.log({ fs: eng.fs, gitdir, ref: BRANCH });
478
+ commits.reverse(); // genesis → head
479
+ const state = new Map(); // aggregateId -> { type, fields: Map }
480
+ const running = new Map(); // summaryKey -> value
481
+ const bump = (key, d) => running.set(key, (running.get(key) ?? 0) + d);
482
+ let prevs = null; // snapshot at fromOid
483
+ let events = [];
484
+ let inSuffix = fromOid === "" || fromOid === undefined;
485
+ if (inSuffix) prevs = new Map();
486
+ let sawFrom = inSuffix;
487
+ for (const c of commits) {
488
+ let blob = null;
489
+ try { blob = (await git.readBlob({ fs: eng.fs, gitdir, oid: c.oid, filepath: "intent.json" })).blob; } catch {}
490
+ const deltas = [];
491
+ let intentId = "";
492
+ if (blob) {
493
+ let doc = null;
494
+ try { doc = JSON.parse(dec.decode(blob)); } catch {}
495
+ // A STRIKE-BEARING chain (a §6 seal retracted moved history): the incremental
496
+ // event fold below cannot express the retraction (only the kernel's
497
+ // strike-correct reprojection can). Answer `strikeBearing` — the caller closes
498
+ // against the coordinator-HELD values with the PROJECTION as the oracle
499
+ // (summaryCloseAgainstHeld), exactly the posture the live capture already uses.
500
+ if (doc && Array.isArray(doc.strikes) && doc.strikes.length > 0) {
501
+ return { ok: true, strikeBearing: true, head };
502
+ }
503
+ intentId = (doc && doc.id) || "";
504
+ for (const ev of parseIntentEvents(blob)) {
505
+ const cur = state.get(ev.aggregate) ?? { type: undefined, fields: new Map(), exists: false };
506
+ const newType = ev.sets.get("__type");
507
+ if (newType !== undefined) cur.type = newType;
508
+ const tallyBefore = new Map();
509
+ for (const read of reads) {
510
+ if (read.of !== cur.type && read.of !== newType) continue;
511
+ tallyBefore.set(read.id, contribution(read, cur));
512
+ }
513
+ const existedBefore = cur.exists;
514
+ for (const [f, v] of ev.sets) cur.fields.set(f, v);
515
+ cur.exists = true;
516
+ state.set(ev.aggregate, cur);
517
+ const effType = cur.type ?? newType;
518
+ if (!existedBefore && effType !== undefined && tenantTypes(eng).has(effType)) {
519
+ bump(summaryKey(NOMOS_SHARD_ROWS_READ, ""), 1);
520
+ deltas.push({ readId: NOMOS_SHARD_ROWS_READ, group: "", delta: 1 });
521
+ }
522
+ for (const read of reads) {
523
+ if (read.of !== cur.type) continue;
524
+ const before = tallyBefore.get(read.id) ?? null;
525
+ const after = contribution(read, cur);
526
+ for (const d of contributionDiff(read, before, after)) {
527
+ bump(summaryKey(d.readId, d.group), d.delta);
528
+ deltas.push(d);
529
+ }
530
+ }
531
+ }
532
+ }
533
+ if (inSuffix && deltas.length) {
534
+ // Coalesce same-(read,group) deltas within one intent.
535
+ const byKey = new Map();
536
+ for (const d of deltas) byKey.set(summaryKey(d.readId, d.group), (byKey.get(summaryKey(d.readId, d.group)) ?? 0) + d.delta);
537
+ const merged = [...byKey.entries()].filter(([, d]) => d !== 0).map(([k, d]) => {
538
+ const [readId, group] = splitSummaryKey(k);
539
+ return { readId, group, delta: d };
540
+ });
541
+ if (merged.length) events.push({ oid: c.oid, intentId, deltas: merged });
542
+ }
543
+ if (!inSuffix && c.oid === fromOid) { inSuffix = true; sawFrom = true; prevs = new Map(running); }
544
+ }
545
+ if (!sawFrom) return { ok: false, error: `fromOid ${String(fromOid).slice(0, 12)} is not on this shard's main — cannot derive the suffix` };
546
+ const subtotals = [];
547
+ const keys = new Set([...running.keys(), ...prevs.keys()]);
548
+ for (const key of keys) {
549
+ const value = running.get(key) ?? 0;
550
+ const prev = prevs.get(key) ?? 0;
551
+ if (value === prev) continue;
552
+ const [readId, group] = splitSummaryKey(key);
553
+ const read = reads.find((r) => r.id === readId);
554
+ subtotals.push({ readId, group, kind: readId === NOMOS_SHARD_ROWS_READ ? "rows" : read ? read.kind : "count", prev, value });
555
+ }
556
+ if (subtotals.length === 0) return { ok: true, upToDate: true, head };
557
+ return {
558
+ ok: true,
559
+ head,
560
+ payload: {
561
+ fromOid: fromOid || "",
562
+ toOid: head,
563
+ subtotals: subtotals.sort((a, b) => (summaryKey(a.readId, a.group) < summaryKey(b.readId, b.group) ? -1 : 1)),
564
+ events,
565
+ },
566
+ };
567
+ }
568
+
569
+ /** One read's contribution shape over a host-folded aggregate (suffix derivation only).
570
+ * SLICE 4: predicate-aware — a `where`-bearing scoped read contributes only while the
571
+ * folded fields satisfy the canonical predicate (same semantics as the kernel's
572
+ * maintained tally, so the suffix lane and the live capture agree byte-for-byte). */
573
+ function contribution(read, cur) {
574
+ if (!cur || !cur.exists || cur.type !== read.of) return null;
575
+ if (read.where && !evalCanonicalPred(read.where, (f) => cur.fields.get(f))) return null;
576
+ const group = read.by === null ? "" : cur.fields.has(read.by) ? String(cur.fields.get(read.by)) : undefined;
577
+ if (group === undefined) return null;
578
+ if (read.kind === "count") return { group, value: 1 };
579
+ const raw = cur.fields.get(read.field);
580
+ return { group, value: typeof raw === "number" ? raw : 0 };
581
+ }
582
+ function contributionDiff(read, before, after) {
583
+ const out = [];
584
+ if (before && after && before.group === after.group) {
585
+ const d = after.value - before.value;
586
+ if (d !== 0) out.push({ readId: read.id, group: after.group, delta: d });
587
+ return out;
588
+ }
589
+ if (before) out.push({ readId: read.id, group: before.group, delta: -before.value });
590
+ if (after) out.push({ readId: read.id, group: after.group, delta: after.value });
591
+ return out.filter((d) => d.delta !== 0);
592
+ }
593
+
594
+ // ── §6 — THE SHARD LIFECYCLE (slice 5): migration = re-admission, never trust ──
595
+ //
596
+ // The host legs of a split: the TARGET pulls the moving homes' intents off the
597
+ // SOURCE chain and re-admits EACH through its own gate (`apply_intent` — the same
598
+ // primitive the upload birth and edge admission run: plan re-run, byte-compared,
599
+ // invariants judged); the SOURCE then seals the move with ONE law intent that
600
+ // STRIKES the migrated history out of its fold (append-then-strike — nothing is
601
+ // erased; the kernel's strike-correct reprojection sheds the rows so tallies,
602
+ // deep-verify recount and the capacity load all stay honest) and tombstones the
603
+ // receipts as redirects. Subtotals close/open through the SAME §5.2 summary gate.
604
+
605
+ /** The total projected TENANT row count — the explicit O(rows) audit walk
606
+ * (deep-verify's recount and the strike-bearing summary close share it). */
607
+ export function countTenantRows(eng, ws) {
608
+ let total = 0;
609
+ for (const type of tenantTypes(eng)) {
610
+ total += query(eng, ws, "aggregatesByType", JSON.stringify({ __type: type })).length;
611
+ }
612
+ return total;
613
+ }
614
+
615
+ /**
616
+ * Walk a mounted workspace's main (genesis → head) and collect every NON-STRUCK
617
+ * intent homed on one of `homeKeys` — judged by the SAME hash-bearing `routes`
618
+ * table the wrong-home gate reads (`via:"axis"`: the payload field carries the
619
+ * home key; `via:"id"`: the minted id's route tag). Framework intents (receipts,
620
+ * identity, seals, installs) carry no route and never match.
621
+ */
622
+ export async function collectHomedIntents(eng, ws, homeKeys) {
623
+ const routes = directiveRoutes(eng);
624
+ const tagOf = new Map(); // route tag hex -> homeKey
625
+ for (const hk of homeKeys) tagOf.set(await routeTagHexOfHomeKey(hk), hk);
626
+ const keySet = new Set(homeKeys);
627
+ const gitdir = gitdirOf(ws);
628
+ const commits = await git.log({ fs: eng.fs, gitdir, ref: BRANCH });
629
+ commits.reverse(); // genesis → head
630
+ const docs = [];
631
+ const strikeParity = new Map(); // intentId -> strike count
632
+ for (const c of commits) {
633
+ let doc = null;
634
+ try {
635
+ const { blob } = await git.readBlob({ fs: eng.fs, gitdir, oid: c.oid, filepath: "intent.json" });
636
+ doc = JSON.parse(dec.decode(blob));
637
+ docs.push({ oid: c.oid, blob, doc });
638
+ } catch { continue; }
639
+ for (const target of (doc && Array.isArray(doc.strikes) ? doc.strikes : [])) {
640
+ strikeParity.set(target, (strikeParity.get(target) ?? 0) + 1);
641
+ }
642
+ }
643
+ const out = [];
644
+ for (const { oid, blob, doc } of docs) {
645
+ const id = doc && typeof doc.id === "string" ? doc.id : null;
646
+ if (!id || (strikeParity.get(id) ?? 0) % 2 === 1) continue; // struck out of the fold
647
+ const dom = doc.payload && typeof doc.payload.domain === "string" ? doc.payload.domain : null;
648
+ const dirId = doc.payload && typeof doc.payload.directiveId === "string" ? doc.payload.directiveId : null;
649
+ const route = dom && dirId ? routes.get(`${dom} ${dirId}`) : null;
650
+ if (!route) continue;
651
+ const value = doc.payload.payload ? doc.payload.payload[route.key] : undefined;
652
+ if (typeof value !== "string" || !value) continue;
653
+ let homeKey = null;
654
+ if (route.via === "axis") homeKey = keySet.has(value) ? value : null;
655
+ else {
656
+ const tag = routeTagHexOfMintedId(value);
657
+ homeKey = tag !== undefined ? tagOf.get(tag) ?? null : null;
658
+ }
659
+ if (homeKey !== null) out.push({ oid, id, homeKey, blob });
660
+ }
661
+ return out;
662
+ }
663
+
664
+ /**
665
+ * MIGRATE the moving homes' intents from `sourceWs`'s chain into THIS workspace
666
+ * (`ws`, the target) — re-admission through the target's OWN gate, oldest first,
667
+ * idempotent by intent id (already-sealed intents are skipped, never double-folded).
668
+ * A LAW refusal jams the move VISIBLY (`refused` returned; the seal never runs over
669
+ * an incomplete migration — fail-closed). Both workspaces must be mounted.
670
+ */
671
+ export async function migrateHomes(eng, ws, ledger, sourceWs, homeKeys, { onlyIds = null } = {}) {
672
+ const wanted = onlyIds ? new Set(onlyIds) : null;
673
+ const homed = await collectHomedIntents(eng, sourceWs, homeKeys);
674
+ const main = await mainIntentIds(eng, ws);
675
+ const migrated = [], skipped = [], refused = [];
676
+ for (const e of homed) {
677
+ if (wanted && !wanted.has(e.id)) continue;
678
+ if (main.ids.has(e.id)) { skipped.push(e.id); continue; }
679
+ const res = applyIntentBytes(eng, ws, e.blob);
680
+ if (res.ok) {
681
+ main.ids.add(e.id); main.oids.add(res.head); main.head = res.head;
682
+ migrated.push(e.id);
683
+ } else {
684
+ refused.push({ intentId: e.id, oid: e.oid.slice(0, 10), error: String(res.error || "").slice(0, 300) });
685
+ }
686
+ }
687
+ if (migrated.length) await commitAndPush(eng, ws, ledger);
688
+ log("migrate-homes", { ws, from: sourceWs, migrated: migrated.length, skipped: skipped.length, refused: refused.length });
689
+ return { migrated, skipped, refused };
690
+ }
691
+
692
+ /**
693
+ * SEAL the move on the SOURCE shard's own chain: verify the migration's
694
+ * COMPLETENESS against this chain itself (every live homed intent must be in
695
+ * `migratedIds` — anything missing is returned and the seal DOES NOT run), then
696
+ * author `nomosSealHome` (chunked: receipts tombstoned, migrated intents struck)
697
+ * through the one gate and push durably.
698
+ */
699
+ export async function sealHomes(eng, ws, ledger, { domain, lawHash, homeKeys, target, migratedIds, sealedAt }) {
700
+ const have = new Set(migratedIds);
701
+ const homed = await collectHomedIntents(eng, ws, homeKeys);
702
+ const missing = homed.filter((e) => !have.has(e.id)).map((e) => e.id);
703
+ if (missing.length) return { ok: false, missing };
704
+ const ids = homed.map((e) => e.id);
705
+ const CHUNK = 1024;
706
+ let head = null, struck = 0;
707
+ for (let i = 0; i < Math.max(1, Math.ceil(ids.length / CHUNK)); i++) {
708
+ const intentIds = ids.slice(i * CHUNK, (i + 1) * CHUNK);
709
+ const res = author(eng, ws, domain, "nomosSealHome", { homeKeys: [...homeKeys], target, intentIds, sealedAt }, lawHash, { rngDraws: 8 });
710
+ if (res && res.ok === false) return { ok: false, error: String(res.error || "the law refused the seal").slice(0, 400) };
711
+ head = res.head;
712
+ struck += intentIds.length;
713
+ }
714
+ await commitAndPush(eng, ws, ledger);
715
+ log("seal-homes", { ws, target, homes: homeKeys.length, struck, head });
716
+ return { ok: true, head, struck };
717
+ }
718
+
719
+ /**
720
+ * CLOSE a shard's subtotals against the coordinator-HELD values with THIS shard's
721
+ * projection as the oracle — the strike-bearing summary lane (a §6 seal just
722
+ * re-shaped the fold; the kernel's strike-correct reprojection is the only honest
723
+ * recomputation). `heldRows` = the coordinator's committed `NomosSummarySubtotal`
724
+ * rows for this shard (+ its recorded frontier). Returns the `nomosPropagateSummary`
725
+ * body — prev = held, value = the live maintained tally, evidence = one event at the
726
+ * chain head carrying the diffs (deep-verify recomputes from the same oracle).
727
+ */
728
+ export async function summaryCloseAgainstHeld(eng, ws, heldRows, heldFrontier) {
729
+ const head = await git.resolveRef({ fs: eng.fs, gitdir: gitdirOf(ws), ref: BRANCH }).catch(() => null);
730
+ if (!head) return { ok: false, error: "no local main to close against" };
731
+ if (head === heldFrontier) return { ok: true, upToDate: true, head };
732
+ const subtotals = [];
733
+ const deltas = [];
734
+ for (const row of heldRows) {
735
+ const d = row.data || row;
736
+ if (typeof d.readId !== "string") continue;
737
+ const group = typeof d.group === "string" ? d.group : "";
738
+ const held = typeof d.value === "number" ? d.value : 0;
739
+ let now;
740
+ if (d.readId === NOMOS_SHARD_ROWS_READ) now = countTenantRows(eng, ws);
741
+ else if (d.kind === "sum") now = sum(eng, ws, d.readId, group).sum;
742
+ else now = count(eng, ws, d.readId, group).count;
743
+ if (now === held) continue;
744
+ subtotals.push({ readId: d.readId, group, kind: d.kind === "sum" || d.kind === "rows" ? d.kind : "count", prev: held, value: now });
745
+ deltas.push({ readId: d.readId, group, delta: now - held });
746
+ }
747
+ // Head moved but no tally did (e.g. a seal over an empty home): the frontier must
748
+ // still advance or the watermark jams forever — the caller amends it with the
749
+ // declared `nomosAmendSummaryFrontier` primitive (no claims, nothing to recompute).
750
+ if (subtotals.length === 0) return { ok: true, frontierOnly: true, head };
751
+ return {
752
+ ok: true,
753
+ head,
754
+ payload: {
755
+ fromOid: heldFrontier || "",
756
+ toOid: head,
757
+ subtotals: subtotals.sort((a, b) => (summaryKey(a.readId, a.group) < summaryKey(b.readId, b.group) ? -1 : 1)),
758
+ events: [{ oid: head, intentId: "", deltas }],
759
+ },
760
+ };
761
+ }
762
+
763
+ // ── §5.4 — THE REVERSE LANE (slice 4): global reference data, coordinator → shards ──
764
+ //
765
+ // Aggregates marked `.global(...)` home on the COORDINATOR (the homing walk pins
766
+ // them); their committed intents REPLICATE DOWNWARD into every shard chain by
767
+ // RE-ADMISSION of the SAME sealed bytes (`apply_intent` — plan re-run under the
768
+ // shard's identical law, byte-compared; provenance preserved: author = original
769
+ // author, committer = the shard's kernel). Idempotent by intent id; a shard born
770
+ // later catches up via {@link collectGlobalIntents} at the auto-birth leg.
771
+
772
+ /**
773
+ * Walk a mounted workspace's main (genesis → head) and collect every NON-STRUCK
774
+ * intent whose sealed events touch a GLOBAL aggregate type — the catch-up set a
775
+ * fresh shard replays at birth, and the audit surface for the reverse lane.
776
+ */
777
+ export async function collectGlobalIntents(eng, ws) {
778
+ const globals = globalAggregates(eng);
779
+ if (globals.size === 0) return [];
780
+ const gitdir = gitdirOf(ws);
781
+ const commits = await git.log({ fs: eng.fs, gitdir, ref: BRANCH });
782
+ commits.reverse(); // genesis → head
783
+ const typeOf = new Map(); // aggregateId -> __type (set once at creation — types never change)
784
+ const strikeParity = new Map();
785
+ const docs = [];
786
+ for (const c of commits) {
787
+ let blob = null, doc = null;
788
+ try {
789
+ blob = (await git.readBlob({ fs: eng.fs, gitdir, oid: c.oid, filepath: "intent.json" })).blob;
790
+ doc = JSON.parse(dec.decode(blob));
791
+ } catch { continue; }
792
+ docs.push({ oid: c.oid, blob, doc });
793
+ for (const target of (doc && Array.isArray(doc.strikes) ? doc.strikes : [])) {
794
+ strikeParity.set(target, (strikeParity.get(target) ?? 0) + 1);
795
+ }
796
+ }
797
+ const out = [];
798
+ for (const { oid, blob, doc } of docs) {
799
+ const id = doc && typeof doc.id === "string" ? doc.id : null;
800
+ let touches = false;
801
+ for (const ev of parseIntentEvents(blob)) {
802
+ const newType = ev.sets.get("__type");
803
+ if (newType !== undefined && !typeOf.has(ev.aggregate)) typeOf.set(ev.aggregate, newType);
804
+ const t = newType ?? typeOf.get(ev.aggregate);
805
+ if (t !== undefined && globals.has(t)) touches = true;
806
+ }
807
+ if (!touches || !id || (strikeParity.get(id) ?? 0) % 2 === 1) continue;
808
+ out.push({ oid, id, blob });
809
+ }
810
+ return out;
811
+ }
812
+
813
+ /**
814
+ * Re-admit carried GLOBAL intents on THIS workspace's chain (a shard, the reverse
815
+ * lane's receiving leg) — oldest first, idempotent by intent id, durably pushed.
816
+ * A law refusal jams VISIBLY (`refused` returned) — the shard's gate is never
817
+ * bypassed; the coordinator's law ≡ the shard's law by construction, so a refusal
818
+ * here means the laws diverged (redeploy the law, then re-offer).
819
+ */
820
+ export async function replicateIntents(eng, ws, ledger, intents, { push = true } = {}) {
821
+ const main = await mainIntentIds(eng, ws);
822
+ const replicated = [], skipped = [], refused = [];
823
+ for (const e of intents) {
824
+ if (e.id && main.ids.has(e.id)) { skipped.push(e.id); continue; }
825
+ const res = applyIntentBytes(eng, ws, e.blob);
826
+ if (res.ok) {
827
+ if (e.id) main.ids.add(e.id);
828
+ main.oids.add(res.head); main.head = res.head;
829
+ replicated.push(e.id || res.head);
830
+ } else {
831
+ refused.push({ intentId: e.id || "", error: String(res.error || "").slice(0, 300) });
832
+ }
833
+ }
834
+ // push=false is the OFFLINE proof lane only (bench harness — no ledger remote);
835
+ // every host leg keeps durability-before-ack.
836
+ if (replicated.length && push) await commitAndPush(eng, ws, ledger);
837
+ log("replicate-globals", { ws, replicated: replicated.length, skipped: skipped.length, refused: refused.length });
838
+ return { replicated, skipped, refused };
839
+ }
840
+
841
+ /**
842
+ * §5.5 (slice 4) — the CONTENT HASH of the coordinator's committed subtotal state:
843
+ * sha256 over the canonical (row-id-sorted) projection of every
844
+ * `NomosSummarySubtotal` row. The ONE definition every checkpoint party shares —
845
+ * the sealing host claims it, an auditor (deep-verify anchoring, the e2e, any
846
+ * peer that mounts the chain) recomputes it from law-state and byte-compares.
847
+ */
848
+ export async function checkpointStateHash(eng, ws) {
849
+ const rows = query(eng, ws, "aggregatesByType", JSON.stringify({ __type: "NomosSummarySubtotal" }));
850
+ const canon = rows
851
+ .map((r) => {
852
+ const d = r.data || {};
853
+ return { id: r.id, readId: d.readId, group: d.group, shard: d.shard, kind: d.kind, value: d.value, frontier: d.frontier };
854
+ })
855
+ .sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
856
+ return sha256hex(JSON.stringify(canon));
857
+ }
858
+
859
+ /**
860
+ * Author one intent on a workspace's chain through ITS OWN gate and push durably —
861
+ * the generic Order leg (`nomosPropagateSummary` into the coordinator, the
862
+ * assignment receipt into a shard, the registry row at auto-birth). A LAW refusal
863
+ * (the gate's typed verdict — frontier-gap, wrong-home, …) returns `{ok:false,
864
+ * error}`; the caller decides the remedy (re-derive, log, jam visibly).
865
+ */
866
+ export async function authorOn(eng, ws, ledger, domain, directiveId, payload, lawHash, opts = {}) {
867
+ await mountWorkspace(eng, ws, ledger);
868
+ let res;
869
+ try {
870
+ res = author(eng, ws, domain, directiveId, payload, lawHash, opts);
871
+ } catch (e) {
872
+ return { ok: false, error: String((e && e.message) || e).slice(0, 500) };
873
+ }
874
+ if (res && res.ok === false) return { ok: false, error: String(res.error || "the law refused this intent").slice(0, 500) };
875
+ await commitAndPush(eng, ws, ledger);
876
+ return { ok: true, head: res.head };
877
+ }
878
+
879
+ /**
880
+ * THE DEEP-VERIFY OP (§5.5 — replay-on-suspicion, made operational): replay-verify
881
+ * the SHARD's whole chain from genesis (the same wasm `verify_chain` the upload
882
+ * birth runs), then recompute every coordinator-held subtotal for that shard from
883
+ * the shard's OWN maintained tallies and byte-compare. `coordinatorRows` is the
884
+ * coordinator's law-state (`NomosSummarySubtotal` rows for this shard) — the HOST
885
+ * resolves and passes them (the engine has no cross-workspace reach). Auditors pay
886
+ * for verification; ordinary reads stay O(1).
887
+ */
888
+ export async function deepVerifyShard(eng, shardWs, shardLedger, coordinatorRows) {
889
+ const replay = await verifyChain(eng, shardWs, shardLedger);
890
+ if (!replay.valid) {
891
+ return { verdict: `invalid:${replay.check || "replay"}`, replay, checks: [] };
892
+ }
893
+ await mountWorkspace(eng, shardWs, shardLedger);
894
+ const checks = [];
895
+ let allMatch = true;
896
+ for (const row of coordinatorRows) {
897
+ const d = row.data || row;
898
+ if (typeof d.readId !== "string") continue;
899
+ let recomputed;
900
+ if (d.readId === NOMOS_SHARD_ROWS_READ) {
901
+ // Total projected rows: enumerate the manifest's aggregate types (an explicit
902
+ // O(rows) audit walk — never an implicit read cost).
903
+ recomputed = countTenantRows(eng, shardWs);
904
+ } else if (d.kind === "sum") {
905
+ recomputed = sum(eng, shardWs, d.readId, d.group ?? "").sum;
906
+ } else {
907
+ recomputed = count(eng, shardWs, d.readId, d.group ?? "").count;
908
+ }
909
+ const match = recomputed === d.value;
910
+ if (!match) allMatch = false;
911
+ checks.push({ readId: d.readId, group: d.group ?? "", coordinator: d.value, shard: recomputed, match });
912
+ }
913
+ const head = await git.resolveRef({ fs: eng.fs, gitdir: gitdirOf(shardWs), ref: BRANCH }).catch(() => null);
914
+ return {
915
+ verdict: allMatch ? "verified" : "mismatch",
916
+ head,
917
+ intents: replay.intents,
918
+ plansRerun: replay.plansRerun,
919
+ checks,
920
+ };
921
+ }
922
+
923
+ /** Mount a workspace, restoring its repo from the Artifacts ledger ({remote, headers}). */
924
+ export async function mountWorkspace(eng, ws, ledger) {
925
+ if (eng.mounted.has(ws)) return eng.mounted.get(ws);
926
+ const wsContents = new Map();
927
+ seedManifests(wsContents, eng.manifestStrings);
928
+ eng.preopen.dir.contents.get("ws").contents.set(ws, new Directory(wsContents));
929
+ const gitdir = gitdirOf(ws);
930
+ let restored = false, remoteMain = null, restoreError = null;
931
+ try {
932
+ const info = await git.getRemoteInfo({ http, url: ledger.remote, headers: ledger.headers });
933
+ remoteMain = (info.refs && info.refs.heads && info.refs.heads.main) || null;
934
+ if (remoteMain) {
935
+ await git.init({ fs: eng.fs, gitdir, bare: true, defaultBranch: "main" });
936
+ await git.addRemote({ fs: eng.fs, gitdir, remote: "origin", url: ledger.remote, force: true });
937
+ await git.fetch({ fs: eng.fs, http, gitdir, remote: "origin", ref: "main", singleBranch: true, headers: ledger.headers });
938
+ await git.writeRef({ fs: eng.fs, gitdir, ref: "refs/heads/main", value: remoteMain, force: true });
939
+ await git.writeRef({ fs: eng.fs, gitdir, ref: "HEAD", value: "ref: refs/heads/main", force: true, symbolic: true });
940
+ restored = true;
941
+ }
942
+ } catch (e) { restoreError = String((e && e.stack) || e).split("\n").slice(0, 5).join(" | "); }
943
+ const m = { restored, remoteMain, restoreError };
944
+ eng.mounted.set(ws, m);
945
+ return m;
946
+ }
947
+
948
+ /** Drop a workspace's in-memory state so the next access re-restores from the ledger. */
949
+ export function unmount(eng, ws) {
950
+ eng.mounted.delete(ws);
951
+ eng.mainIntents.delete(ws);
952
+ const wsRoot = eng.preopen.dir.contents.get("ws");
953
+ if (wsRoot) wsRoot.contents.delete(ws);
954
+ }
955
+
956
+ function writeWork(eng, name, bytes) { eng.preopen.dir.contents.set(name, new File(bytes)); }
957
+
958
+ export function author(eng, ws, domain, directiveId, payload, controllerHash, opts = {}) {
959
+ const seq = eng.seq++;
960
+ // rngDraws: the captured-entropy budget this intent's plan may consume (each minted id
961
+ // costs 19 draws — see NOMOS_API_SHIM). 64 is the per-write default (3 mints of slack);
962
+ // a BATCH-creating directive needs 19×creates. COMMIT-ONLY-CONSUMED: only the consumed
963
+ // prefix is sealed onto the intent, so a generous buffer is request weight, not ledger weight.
964
+ const envelope = { payload: { domain, directiveId, payload }, captured_ports: { clock: { physical: Date.now(), logical: 0, replica: eng.replica }, rng: osEntropyBuffer(opts.rngDraws ?? 64) }, policy_version: 1, policy_domain: "Nomos", policy_gas: 0, policy_memory: 0 };
965
+ writeWork(eng, `payload-${seq}.json`, enc.encode(JSON.stringify(payload)));
966
+ writeWork(eng, `envelope-${seq}.json`, enc.encode(stringifyBig(envelope)));
967
+ const genesis = !controllerHash;
968
+ // THE DEFERRED-PROJECTION ACK (#47, sharding slice 7): the read-projection
969
+ // catch-up (incl. the derive/combine materialize) moves OFF the author ack path
970
+ // by default — every read op self-heals to head, and the post-ack warm lane
971
+ // (`warmEngine`, which every host already calls) flushes it eagerly. Pass
972
+ // `opts.deferProjection: false` to keep the pre-#47 synchronous refresh.
973
+ const defer = opts.deferProjection !== false;
974
+ const res = JSON.parse(call(eng.ex, "author", { repoArg: repoArgOf(ws), workspace: ws, domain, directiveId, payloadFile: `/work/payload-${seq}.json`, envelopeFile: `/work/envelope-${seq}.json`, seq, actor: "", domainFile: genesis ? "/work/domain.package.usda" : "", domainHash: genesis ? "" : controllerHash, branch: BRANCH, ...(defer ? { deferProjection: true } : {}) }, eng.STDERR));
975
+ if (defer && res.ok) (eng.pendingProjection ??= new Set()).add(ws);
976
+ return res;
977
+ }
978
+
979
+ /** Catch one workspace's read projection up to head NOW (the deferred-ack flush). */
980
+ export function refreshProjection(eng, ws) {
981
+ return JSON.parse(call(eng.ex, "refresh_projection", { repoArg: repoArgOf(ws), workspace: ws, branch: BRANCH }, eng.STDERR));
982
+ }
983
+
984
+ /** Flush every deferred projection refresh (rides the post-ack warm lane). */
985
+ export function flushDeferredProjections(eng) {
986
+ for (const ws of eng.pendingProjection ?? []) {
987
+ try { refreshProjection(eng, ws); } catch {}
988
+ }
989
+ if (eng.pendingProjection) eng.pendingProjection.clear();
990
+ }
991
+
992
+ // DURABILITY-BEFORE-ACK: only acked once the write LANDED in the ledger; a failed push
993
+ // unmounts (drops local state) so the next access re-restores from custody.
994
+ export async function commitAndPush(eng, ws, ledger) {
995
+ try {
996
+ await git.push({ fs: eng.fs, http, gitdir: gitdirOf(ws), url: ledger.remote, ref: "main", remoteRef: "main", force: false, headers: ledger.headers });
997
+ } catch (e) {
998
+ unmount(eng, ws);
999
+ throw new Error(`ledger push failed — write NOT durable, rolled back to ledger: ${e}`);
1000
+ }
1001
+ }
1002
+
1003
+ export const qById = (eng, ws, id) => JSON.parse(call(eng.ex, "query_by_id", { repoArg: repoArgOf(ws), workspace: ws, aggregateId: id, branch: BRANCH }, eng.STDERR));
1004
+ export const query = (eng, ws, queryId, paramsJson) => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryId, paramsJson, branch: BRANCH }, eng.STDERR));
1005
+ export const count = (eng, ws, countId, groupKey) => JSON.parse(call(eng.ex, "count", { repoArg: repoArgOf(ws), workspace: ws, countId, groupKey, branch: BRANCH }, eng.STDERR));
1006
+ export const sum = (eng, ws, sumId, groupKey) => JSON.parse(call(eng.ex, "sum", { repoArg: repoArgOf(ws), workspace: ws, sumId, groupKey, branch: BRANCH }, eng.STDERR));
1007
+ /** The maintained O(1) min/max read (#47 — the slice-4 extremum hand-back made an RPC).
1008
+ * `kind` = "min"|"max"; an absent group answers `value: null` (never a fabricated 0). */
1009
+ export const extremum = (eng, ws, extremumId, kind, groupKey) => JSON.parse(call(eng.ex, "extremum", { repoArg: repoArgOf(ws), workspace: ws, extremumId, kind, groupKey: groupKey ?? "", branch: BRANCH }, eng.STDERR));
1010
+ export const nomosActive = (eng, ws) => qById(eng, ws, `domain-installation:${eng.hashes.nomos}`).length > 0;
1011
+
1012
+ /** Re-admit ONE carried intent (bytes) on local main — the DLQ-retry primitive. */
1013
+ export function applyIntentBytes(eng, ws, bytes) {
1014
+ const name = `intent-in-${eng.seq++}.json`;
1015
+ writeWork(eng, name, bytes);
1016
+ return JSON.parse(call(eng.ex, "apply_intent", { repoArg: repoArgOf(ws), workspace: ws, intentFile: `/work/${name}`, branch: BRANCH }, eng.STDERR));
1017
+ }
1018
+
1019
+ // Main's recent intent ids + oids, cached by head (admission idempotence + walk boundary).
1020
+ async function mainIntentIds(eng, ws) {
1021
+ const gitdir = gitdirOf(ws);
1022
+ const head = await git.resolveRef({ fs: eng.fs, gitdir, ref: BRANCH }).catch(() => null);
1023
+ if (!head) return { head: null, ids: new Set(), oids: new Set() };
1024
+ const cached = eng.mainIntents.get(ws);
1025
+ if (cached && cached.head === head) return cached;
1026
+ const ids = new Set(), oids = new Set();
1027
+ for (const c of await git.log({ fs: eng.fs, gitdir, ref: BRANCH, depth: 500 })) {
1028
+ oids.add(c.oid);
1029
+ try {
1030
+ const { blob } = await git.readBlob({ fs: eng.fs, gitdir, oid: c.oid, filepath: "intent.json" });
1031
+ const id = JSON.parse(dec.decode(blob)).id;
1032
+ if (typeof id === "string" && id) ids.add(id);
1033
+ } catch {}
1034
+ }
1035
+ const m = { head, ids, oids };
1036
+ eng.mainIntents.set(ws, m);
1037
+ return m;
1038
+ }
1039
+
1040
+ /**
1041
+ * EDGE ADMISSION (the one machinery): judge every session/<cid> ref. Structural lane
1042
+ * refusals (control-plane intents, undeployed domains) and domain rejections route per
1043
+ * Jack's law: legitimate work DEAD-LETTERS (returned to the host to persist — the
1044
+ * engine has no durable store), obvious attacks drop. Admitted intents merge to main,
1045
+ * durably push, the branch is consumed.
1046
+ */
1047
+ export async function admitAll(eng, ws, ledger, { refuseDomains = [], shardRouting = null } = {}) {
1048
+ const refuse = new Set(refuseDomains);
1049
+ const routes = shardRouting ? directiveRoutes(eng) : null;
1050
+ const gitdir = gitdirOf(ws);
1051
+ const info = await git.getRemoteInfo({ http, url: ledger.remote, headers: ledger.headers });
1052
+ const sessions = (info.refs && info.refs.heads && info.refs.heads.session) || {};
1053
+ const results = [], deadLetters = [], placements = [], splits = [], globalsOut = [];
1054
+ // §5.4 (slice 4): on the COORDINATOR of a taxonomy that declares `.global(...)`
1055
+ // reference data, every admitted intent touching a global aggregate is surfaced
1056
+ // to the HOST, which replicates the SAME sealed bytes into each shard chain.
1057
+ const globalsDeclared = shardRouting && !shardRouting.selfLabel ? globalAggregates(eng) : null;
1058
+ // ── §5.2 SUMMARY CAPTURE (slice 3): on a SHARD (selfLabel) whose law declares
1059
+ // estate-scoped reads, derive each admitted intent's per-(read, group) deltas with
1060
+ // the shard's own projection as the oracle (tallies sandwiched around the fold —
1061
+ // O(touched)); the HOST authors the coalesced `nomosPropagateSummary` Order into
1062
+ // the coordinator afterwards. The coordinator's gate re-verifies everything.
1063
+ const summaryCtx = shardRouting && shardRouting.selfLabel && scopedReads(eng).length > 0
1064
+ ? { reads: scopedReads(eng), prevs: new Map(), values: new Map(), events: [], rowsDelta: 0 }
1065
+ : null;
1066
+ const headBefore = summaryCtx ? await git.resolveRef({ fs: eng.fs, gitdir, ref: BRANCH }).catch(() => null) : null;
1067
+ for (const [cid, head] of Object.entries(sessions)) {
1068
+ const ref = `refs/heads/session/${cid}`;
1069
+ try {
1070
+ await git.addRemote({ fs: eng.fs, gitdir, remote: "origin", url: ledger.remote, force: true });
1071
+ await git.fetch({ fs: eng.fs, http, gitdir, remote: "origin", ref, singleBranch: true, headers: ledger.headers });
1072
+ const main = await mainIntentIds(eng, ws);
1073
+ const chain = [];
1074
+ for (const c of await git.log({ fs: eng.fs, gitdir, ref: head, depth: 100 })) {
1075
+ if (main.oids.has(c.oid)) break;
1076
+ chain.push(c.oid);
1077
+ }
1078
+ chain.reverse(); // oldest → newest
1079
+ const admitted = [], rejected = [], skipped = [];
1080
+ for (const oid of chain) {
1081
+ try {
1082
+ const { blob } = await git.readBlob({ fs: eng.fs, gitdir, oid, filepath: "intent.json" });
1083
+ let dom = null, intentId = null, dirId = null;
1084
+ try { const doc = JSON.parse(dec.decode(blob)); intentId = typeof doc.id === "string" ? doc.id : null; dom = doc && doc.payload && typeof doc.payload.domain === "string" ? doc.payload.domain : null; dirId = doc && doc.payload && typeof doc.payload.directiveId === "string" ? doc.payload.directiveId : null; } catch {}
1085
+ // WRITE-AUTHORITY: law (control-plane) intents never ride the session lane —
1086
+ // nor do HOST-FENCED domains (e.g. `workspaces` on root: governance directives
1087
+ // enter ONLY through the admin-gated lane, never open session pushes).
1088
+ if (!dom || dom === "nomos" || dom === "bootstrap" || refuse.has(dom)) {
1089
+ rejected.push({ oid: oid.slice(0, 10), error: `session lane refuses ${dom ? `'${dom}'` : "non-authored"} intents — law deploys go through POST /v1/workspaces/${ws}/domains with the workspace secret` });
1090
+ continue;
1091
+ }
1092
+ // LAW-FENCE part 2: the domain must be DEPLOYED to this workspace. An
1093
+ // undeployed domain's write is the canonical LEGITIMATE JAM — dead-letter.
1094
+ if (!knownDomains(eng).has(dom)) {
1095
+ const error = `domain '${dom}' is not deployed to this workspace — deploy its law (POST /v1/workspaces/${ws}/domains with the workspace secret), then POST /v1/workspaces/${ws}/dead-letters/retry`;
1096
+ rejected.push({ oid: oid.slice(0, 10), error, deadLettered: true });
1097
+ deadLetters.push({ intentId, oid: oid.slice(0, 10), session: cid, error, blob, domain: dom, directiveId: dirId });
1098
+ continue;
1099
+ }
1100
+ // THE WRONG-HOME GATE (sharding slice 2): a routed directive's home must be
1101
+ // assigned to THIS workspace by the coordinator's law-state shard map.
1102
+ // Misrouted → the typed `wrong-home` refusal naming the correct workspace
1103
+ // (self-healing, never queued); unplaced → a dead-lettered placement jam.
1104
+ if (routes && dom && dirId) {
1105
+ // THE SEAL PEN IS THE BAILIFF'S (slice 5): `nomosSealHome` (strikes the
1106
+ // migrated history out of a source's fold) and `nomosSealHandoff` (flips
1107
+ // the map) are HOST-orchestrated legs of the §6 move — a session-lane
1108
+ // offer of either is an attack on the move lane, refused and DROPPED.
1109
+ if (dirId === "nomosSealHome" || dirId === "nomosSealHandoff") {
1110
+ rejected.push({ oid: oid.slice(0, 10), error: `session lane refuses '${dirId}' — the §6 handoff seals are authored by the host after migration verifies (begin a move with nomosSplitShard, or POST /v1/workspaces/<coordinator>/split)` });
1111
+ continue;
1112
+ }
1113
+ const route = routes.get(`${dom} ${dirId}`);
1114
+ if (route) {
1115
+ let payloadObj = null;
1116
+ try { payloadObj = JSON.parse(dec.decode(blob)).payload?.payload ?? null; } catch {}
1117
+ const verdict = await wrongHomeVerdict(shardRouting, route, payloadObj);
1118
+ if (verdict) {
1119
+ rejected.push({ oid: oid.slice(0, 10), error: verdict.error, ...(verdict.wrongHome ? { wrongHome: verdict.wrongHome } : {}), ...(verdict.homeMoving ? { homeMoving: verdict.homeMoving } : {}), ...(verdict.deadLettered ? { deadLettered: true } : {}) });
1120
+ if (verdict.deadLettered) deadLetters.push({ intentId, oid: oid.slice(0, 10), session: cid, error: verdict.error, blob, domain: dom, directiveId: dirId });
1121
+ continue;
1122
+ }
1123
+ }
1124
+ // THE RECEIPT LEG'S BAILIFF (slice 3 — §3): a session-lane assignment
1125
+ // receipt is VERIFIED against the coordinator's shard map before it can
1126
+ // fold (the client authors it optimistically; the map is the law).
1127
+ if (dirId === "nomosReceiveAssignment" && shardRouting) {
1128
+ let p = null;
1129
+ try { p = JSON.parse(dec.decode(blob)).payload?.payload ?? null; } catch {}
1130
+ const homeKey = p && typeof p.homeKey === "string" ? p.homeKey : null;
1131
+ const rows = homeKey ? (await shardRouting.assignments()) || [] : [];
1132
+ const a = homeKey ? rows.find((x) => x.homeKey === homeKey) : null;
1133
+ if (!a) {
1134
+ const error = `assignment receipt for home '${String(homeKey).slice(0, 80)}' has NO placement on the coordinator '${shardRouting.coordinatorWs}' — author the derived placement intent (birth<Type>) first, then POST /dead-letters/retry`;
1135
+ rejected.push({ oid: oid.slice(0, 10), error, deadLettered: true });
1136
+ deadLetters.push({ intentId, oid: oid.slice(0, 10), session: cid, error, blob, domain: dom, directiveId: dirId });
1137
+ continue;
1138
+ }
1139
+ if (a.shard !== shardRouting.selfLabel || (p && p.shard !== shardRouting.selfLabel)) {
1140
+ const correct = `${shardRouting.coordinatorWs}--${a.shard}`;
1141
+ rejected.push({
1142
+ oid: oid.slice(0, 10),
1143
+ error: `wrong-home: the coordinator's shard map assigns home '${homeKey.slice(0, 80)}' to '${correct}' (not this workspace) — a routed client re-routes and retries automatically`,
1144
+ wrongHome: { workspace: correct, ...(a.mapVersion !== undefined ? { mapVersion: a.mapVersion } : {}) },
1145
+ });
1146
+ continue;
1147
+ }
1148
+ }
1149
+ }
1150
+ // IDEMPOTENCE: an intent already sealed on main is acknowledged, not re-folded.
1151
+ if (intentId && main.ids.has(intentId)) {
1152
+ skipped.push({ oid: oid.slice(0, 10), intent: intentId.slice(0, 12), alreadyOnMain: true });
1153
+ continue;
1154
+ }
1155
+ const cap = summaryCtx ? beginSummaryCapture(eng, ws, summaryCtx, blob) : null;
1156
+ const res = applyIntentBytes(eng, ws, blob);
1157
+ if (res.ok) {
1158
+ main.ids.add(intentId); main.oids.add(res.head); main.head = res.head;
1159
+ admitted.push({ oid: oid.slice(0, 10), head: res.head });
1160
+ if (cap) finishSummaryCapture(eng, ws, summaryCtx, cap, res.head, intentId);
1161
+ // AN ADMITTED PLACEMENT (birth<Type> on the coordinator): surfaced to the
1162
+ // HOST so it can author the assignment RECEIPT into the shard's own chain
1163
+ // (sharding §3 — the receipt leg).
1164
+ if (shardRouting && dirId) {
1165
+ const placement = placementDirectives(eng).get(dirId);
1166
+ if (placement) {
1167
+ let p = null;
1168
+ try { p = JSON.parse(dec.decode(blob)).payload?.payload ?? null; } catch {}
1169
+ const homeKey = p ? p[placement.keyField] : undefined;
1170
+ if (typeof homeKey === "string" && typeof p?.shard === "string") {
1171
+ placements.push({ directive: dirId, homeKey, shard: p.shard, mapVersion: 1 });
1172
+ }
1173
+ }
1174
+ // AN ADMITTED SPLIT (nomosSplitShard on the coordinator, slice 5):
1175
+ // surfaced to the HOST so it can run the §6 migration legs (re-admit
1176
+ // on the target, seal the source, close/open the subtotals, flip the
1177
+ // map) — a session-authored split COMPLETES like a host-authored one.
1178
+ if (dirId === "nomosSplitShard" && !shardRouting.selfLabel) {
1179
+ let p = null;
1180
+ try { p = JSON.parse(dec.decode(blob)).payload?.payload ?? null; } catch {}
1181
+ if (p && typeof p.fromShard === "string" && typeof p.toShard === "string" && Array.isArray(p.homeKeys)) {
1182
+ splits.push({ fromShard: p.fromShard, toShard: p.toShard, homeKeys: p.homeKeys.filter((k) => typeof k === "string") });
1183
+ }
1184
+ }
1185
+ }
1186
+ // AN ADMITTED GLOBAL WRITE (§5.4, slice 4 — coordinator only): the sealed
1187
+ // bytes are surfaced to the HOST, which re-admits them into every shard
1188
+ // chain (the reverse lane — replicated reference data). O(events) per
1189
+ // admitted intent, and only when the law declares `.global(...)`.
1190
+ if (globalsDeclared && globalsDeclared.size > 0) {
1191
+ let touchesGlobal = false;
1192
+ for (const ev of parseIntentEvents(blob)) {
1193
+ const rows = qById(eng, ws, ev.aggregate);
1194
+ const t = ev.sets.get("__type") ?? (rows.length ? rows[0].type : undefined);
1195
+ if (t !== undefined && globalsDeclared.has(t)) { touchesGlobal = true; break; }
1196
+ }
1197
+ if (touchesGlobal) globalsOut.push({ intentId: intentId || "", oid: oid.slice(0, 10), blob });
1198
+ }
1199
+ } else {
1200
+ // THE CHAIN GATE'S OWN WRONG-HOME VERDICT (#41): the law's homing invariant
1201
+ // refuses with `wrong-home:<label>` — surface it as the SAME typed refusal
1202
+ // the edge bailiff answers (self-healing, never queued). The bailiff above
1203
+ // remains defense-in-depth; this arm is the gate catching what it can't.
1204
+ const wh = String(res.error || "").match(/wrong-home:(s\d+)/);
1205
+ if (wh) {
1206
+ const coordinator = (shardRouting && shardRouting.coordinatorWs) || (ws.match(/^(.*)--s\d+$/) || [])[1] || ws;
1207
+ rejected.push({
1208
+ oid: oid.slice(0, 10),
1209
+ error: `wrong-home: this write's home is assigned to '${coordinator}--${wh[1]}' — the chain gate's homing invariant refused it; a routed client re-routes and retries automatically`,
1210
+ wrongHome: { workspace: `${coordinator}--${wh[1]}` },
1211
+ });
1212
+ continue;
1213
+ }
1214
+ const error = `${String(res.error || "").slice(0, 300)} — dead-lettered; fix the law (or the payload), then POST /v1/workspaces/${ws}/dead-letters/retry`;
1215
+ rejected.push({ oid: oid.slice(0, 10), error, deadLettered: true });
1216
+ deadLetters.push({ intentId, oid: oid.slice(0, 10), session: cid, error, blob, domain: dom, directiveId: dirId });
1217
+ }
1218
+ } catch (e) {
1219
+ rejected.push({ oid: oid.slice(0, 10), error: String(e).slice(0, 200) });
1220
+ }
1221
+ }
1222
+ if (admitted.length) await commitAndPush(eng, ws, ledger);
1223
+ try { await git.push({ fs: eng.fs, http, gitdir, url: ledger.remote, ref: BRANCH, remoteRef: ref, delete: true, headers: ledger.headers }); } catch {}
1224
+ results.push({ session: cid, admitted, rejected, ...(skipped.length ? { skipped } : {}) });
1225
+ } catch (e) {
1226
+ results.push({ session: cid, error: String(e).slice(0, 200) });
1227
+ }
1228
+ }
1229
+ const newMain = await git.resolveRef({ fs: eng.fs, gitdir, ref: BRANCH }).catch(() => null);
1230
+ log("admit", { ws, sessions: results.length, main: newMain });
1231
+ // The coalesced §5.2 delta of this batch (null when no scoped read moved): the HOST
1232
+ // closes it against the coordinator's recorded frontier and authors the Order.
1233
+ const summary = summaryCtx ? summaryOfCapture(summaryCtx, headBefore, newMain) : null;
1234
+ return {
1235
+ sessions: results, main: newMain, deadLetters,
1236
+ ...(summary ? { summary } : {}),
1237
+ ...(placements.length ? { placements } : {}),
1238
+ ...(splits.length ? { splits } : {}),
1239
+ ...(globalsOut.length ? { globals: globalsOut } : {}),
1240
+ };
1241
+ }
1242
+
1243
+ /**
1244
+ * VERIFY AN UPLOADED LEDGER — "the githolon executes and thus validates it". Fresh-mounts
1245
+ * the workspace from custody (never a cached mount — the bytes under judgment are the
1246
+ * ledger's, not memory's) and asks the wasm to replay-verify the WHOLE main chain from
1247
+ * genesis (contiguity → per-intent re-admission with the engine re-run; byte-preserving,
1248
+ * read-only — see wasm_git_holon.rs `verify_chain`). Returns the wasm's verdict object:
1249
+ * `{valid:true, head, intents, plansRerun, controllerHash, installedDomains}` or
1250
+ * `{valid:false, check, error, atIndex?, atCommit?}`. The HOST decides what a refusal
1251
+ * does (archive + refuse to serve); this op never mutates the ledger.
1252
+ */
1253
+ export async function verifyChain(eng, ws, ledger) {
1254
+ unmount(eng, ws);
1255
+ const m = await mountWorkspace(eng, ws, ledger);
1256
+ if (!m.restored) {
1257
+ return { valid: false, check: "mount", error: m.restoreError || "no refs/heads/main on the ledger — nothing to verify (push refs/heads/main to deliver a chain first)" };
1258
+ }
1259
+ try {
1260
+ return JSON.parse(call(eng.ex, "verify_chain", { repoArg: repoArgOf(ws), workspace: ws, branch: BRANCH }, eng.STDERR));
1261
+ } finally {
1262
+ // The verified (or refused) chain must never linger as a servable mount: serving
1263
+ // re-restores from custody AFTER the host blesses the verdict.
1264
+ unmount(eng, ws);
1265
+ }
1266
+ }
1267
+
1268
+ /**
1269
+ * Verify the CURRENTLY MOUNTED in-memory chain — no ledger remount. The
1270
+ * LOCAL-FIRST assert: a holon minted offline (`githolon ledger init`) proves its
1271
+ * own validity with the SAME wasm gate the cloud runs before it is ever
1272
+ * pushed. (The cloud lane keeps verifyChain's remount hygiene — it must verify
1273
+ * the LEDGER's bytes, never a hot mount.)
1274
+ */
1275
+ export function verifyChainLocal(eng, ws) {
1276
+ return JSON.parse(call(eng.ex, "verify_chain", { repoArg: repoArgOf(ws), workspace: ws, branch: BRANCH }, eng.STDERR));
1277
+ }
1278
+
1279
+ /**
1280
+ * ARCHIVE A REFUSED UPLOAD: move custody's `refs/heads/main` to
1281
+ * `refs/heads/refused/<label>` (retirement is a move, never an erasure — the refused
1282
+ * bytes stay inspectable; inside refs/heads/ because that is the namespace custody
1283
+ * accepts writes/deletes on) and delete `main` so the workspace reads as unborn again.
1284
+ * Mounts fresh; leaves no mount.
1285
+ */
1286
+ export async function archiveRefusedMain(eng, ws, ledger, label) {
1287
+ unmount(eng, ws);
1288
+ const m = await mountWorkspace(eng, ws, ledger);
1289
+ if (!m.restored) { unmount(eng, ws); return { archived: false, error: "no main to archive" }; }
1290
+ const gitdir = gitdirOf(ws);
1291
+ try {
1292
+ await git.push({ fs: eng.fs, http, gitdir, url: ledger.remote, ref: BRANCH, remoteRef: `refs/heads/refused/${label}`, force: true, headers: ledger.headers });
1293
+ await git.push({ fs: eng.fs, http, gitdir, url: ledger.remote, ref: BRANCH, remoteRef: `refs/heads/${BRANCH}`, delete: true, headers: ledger.headers });
1294
+ return { archived: true, ref: `refs/heads/refused/${label}` };
1295
+ } finally {
1296
+ unmount(eng, ws);
1297
+ }
1298
+ }
1299
+
1300
+ /** Genesis boot: bootstrap → the nomos controller; idempotent when already Active. */
1301
+ export async function boot(eng, ws, ledger) {
1302
+ const m = await mountWorkspace(eng, ws, ledger);
1303
+ if (nomosActive(eng, ws)) {
1304
+ return { status: "already-initialized", controllerHash: eng.hashes.nomos, rows: qById(eng, ws, `domain-installation:${eng.hashes.nomos}`), mount: m };
1305
+ }
1306
+ const result = author(eng, ws, "bootstrap", "installDomain", installPayload(eng.hashes.nomos, eng.nomosPkg, eng.installedBy), "");
1307
+ await commitAndPush(eng, ws, ledger);
1308
+ return { status: "created", head: result.head, controllerHash: eng.hashes.nomos, rows: qById(eng, ws, `domain-installation:${eng.hashes.nomos}`), mount: m };
1309
+ }
1310
+
1311
+ /** Install a tenant package under the nomos controller (validation is the HOST's job). */
1312
+ export async function deploy(eng, ws, ledger, usda, domainHash) {
1313
+ const result = author(eng, ws, "nomos", "installDomain", installPayload(domainHash, usda, eng.installedBy), eng.hashes.nomos);
1314
+ // THE SHARD-IDENTITY ENSURE (#41 — slice 3a): a taxonomy-bearing law carries the
1315
+ // homing invariant, which judges against the workspace's OWN declared identity
1316
+ // (`NomosShardIdentity`, law-state in the shard's own chain). The worker is the
1317
+ // bailiff: it records the CUSTODY fact (this workspace's name ⇒ its shard label)
1318
+ // through the chain's own gate right after the law lands — attributed, replayable,
1319
+ // idempotent (an Ensure; re-deploys re-assert the same state). Coordinator ⇒ label ""
1320
+ // (no homing claim — the invariant holds vacuously there).
1321
+ try {
1322
+ eng.directiveRoutesCache = null; // the just-staged overlay may add routes
1323
+ const routed = new Set([...directiveRoutes(eng).keys()].map((k) => k.split(" ")[0]));
1324
+ if (routed.size) {
1325
+ const m = ws.match(/^(.*)--(s\d+)$/);
1326
+ const label = m ? m[2] : "";
1327
+ const coordinator = m ? m[1] : ws;
1328
+ const existing = qById(eng, ws, "nomos-shard-identity:self");
1329
+ const current = existing.length ? existing[0].data : null;
1330
+ if (!current || current.label !== label || current.coordinator !== coordinator) {
1331
+ for (const dom of routed) {
1332
+ try {
1333
+ const declared = author(eng, ws, dom, "nomosDeclareShardIdentity", { label, coordinator, declaredAt: new Date().toISOString() }, domainHash);
1334
+ if (declared && declared.ok !== false) break; // one declaration IS the workspace's identity
1335
+ } catch {} // a routed domain owned by an earlier law: its own deploy declared it
1336
+ }
1337
+ }
1338
+ }
1339
+ } catch {}
1340
+ await commitAndPush(eng, ws, ledger);
1341
+ return { head: result.head, installation: qById(eng, ws, `domain-installation:${domainHash}`) };
1342
+ }
1343
+
1344
+ export function snapshot(eng, ws) {
1345
+ const wsDir = eng.preopen.dir.contents.get("ws").contents.get(ws);
1346
+ return serializeTree(wsDir);
1347
+ }
1348
+
1349
+ export function stats(eng) {
1350
+ return {
1351
+ mounted: eng.mounted.size,
1352
+ wasmLinearMemMB: +(eng.ex.memory.buffer.byteLength / 1048576).toFixed(1),
1353
+ };
1354
+ }
1355
+
1356
+ /** Per-branch projection observables (cursor, projectCalls, sqliteBytes — see #37). */
1357
+ export const projectionStats = (eng) => JSON.parse(call(eng.ex, "projection_stats", {}, eng.STDERR));
1358
+
1359
+ /**
1360
+ * THE POST-ACK WARM LANE (#34): replenish the wasm engine's pristine-sandbox pool
1361
+ * for every law dispatched since the last call. Call AFTER the response is acked —
1362
+ * DO: `ctx.waitUntil(Promise.resolve().then(() => warmEngine(eng)))`; container:
1363
+ * `setImmediate(() => warmEngine(eng))` — so the NEXT write finds its sandbox
1364
+ * already booted (take-use-discard; a sandbox never executes two plans). Cheap
1365
+ * no-op when the pool is already full or nothing was dispatched.
1366
+ */
1367
+ export function warmEngine(eng) {
1368
+ flushDeferredProjections(eng); // the deferred-ack projection flush (#47) rides the same lane
1369
+ return JSON.parse(call(eng.ex, "engine_warm", {}, eng.STDERR));
1370
+ }