@githolon/testing 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@githolon/testing",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "type": "module",
5
5
  "description": "@githolon/testing — law TDD for Nomos domains: boot the REAL engine plane in-process (the exact machinery every cloud DO, heavy container and web client runs), dispatch directives, assert rows, assert TYPED REFUSALS, fork for what-ifs — fully offline inside vitest.",
6
6
  "license": "SEE LICENSE IN LICENSE.md",
@@ -12,7 +12,7 @@
12
12
  // from the container instance base, ledgers ARRIVE on each op (containers have no
13
13
  // Workers bindings — the control-plane DO mints tokens and sends them), runs in Node.
14
14
  //
15
- // CONTROL PLANE STAYS IN THE DO: ownership secrets, manifest overlays, the DLQ store,
15
+ // CONTROL PLANE STAYS IN THE DO: ownership secrets, the DLQ store,
16
16
  // quotas/rates, alarms. The engine RETURNS data (e.g. dead-letter entries with intent
17
17
  // bytes) and the host persists. Runtime-neutral: workerd + Node 22 (globalThis.crypto,
18
18
  // fetch, TextEncoder are the only ambient dependencies).
@@ -55,9 +55,9 @@ function call(ex, mode, fields, STDERR) {
55
55
  } finally { ex.git_holon_dealloc(reqPtr, req.length); }
56
56
  }
57
57
 
58
- function seedManifests(m, strings) {
59
- m.set("domain_manifests.json", new File(enc.encode(strings.read)));
60
- m.set("identity-manifests.json", new File(enc.encode(strings.identity)));
58
+ function seedManifests(_m, _strings) {
59
+ // Runtime manifests are committed inside USDA packages and derived by the holon
60
+ // from active installed law. Hosts must not seed admission/projection sidecars.
61
61
  }
62
62
 
63
63
  // (#39 BAILIFF: no host-chosen `authorityScope` literal. It was a dead, hardcoded "workspace/root" stamped
@@ -68,13 +68,14 @@ export const installPayload = (hash, usda, installedBy, dispositions) => ({ doma
68
68
 
69
69
  /**
70
70
  * Boot the resident wasm + /work tree. `wasmModule` is a PRECOMPILED WebAssembly.Module
71
- * (the host compiles its way); `manifestStrings` = {read, identity} (baked overlays —
72
- * the HOST owns overlay storage and recomputes); `replica` = the host's unique 63-bit id.
71
+ * (the host compiles its way); `replica` = the host's unique 63-bit id.
72
+ *
73
+ * Runtime read/admission metadata is sovereign holon state: committed USDA packages
74
+ * are folded by the githolon, and the host never stages manifest sidecars.
73
75
  */
74
- export async function createEngine({ wasmModule, bootstrapPkg, nomosPkg, manifestStrings, replica, installedBy = "nomos-cloud" }) {
76
+ export async function createEngine({ wasmModule, bootstrapPkg, nomosPkg, replica, installedBy = "nomos-cloud" }) {
75
77
  const hashes = { bootstrap: await sha256hex(bootstrapPkg), nomos: await sha256hex(nomosPkg) };
76
78
  const root = new Map();
77
- seedManifests(root, manifestStrings);
78
79
  root.set("domain.package.usda", new File(enc.encode(bootstrapPkg)));
79
80
  root.set("ws", new Directory(new Map()));
80
81
  const preopen = new PreopenDirectory("/work", root);
@@ -87,39 +88,19 @@ export async function createEngine({ wasmModule, bootstrapPkg, nomosPkg, manifes
87
88
  for (const n of ["memory", "git_holon_alloc", "git_holon_dealloc", "git_holon_call"]) if (!inst.exports[n]) throw new Error(`missing export ${n}`);
88
89
  const eng = {
89
90
  ex: inst.exports, preopen, STDERR, seq: 0, replica, hashes,
90
- bootstrapPkg, nomosPkg, manifestStrings, installedBy,
91
+ bootstrapPkg, nomosPkg, installedBy,
91
92
  fs: makeGitFs(preopen, "/work", { File, Directory }),
92
93
  mounted: new Map(), // ws -> { restored, remoteMain, restoreError }
93
94
  mainIntents: new Map(), // ws -> { head, ids:Set, oids:Set } (admission idempotence)
94
- knownDomainsCache: null,
95
95
  };
96
96
  return eng;
97
97
  }
98
98
 
99
- /** Swap the manifest strings (a deploy staged a new overlay) + restage every mount. */
100
- export function setManifests(eng, strings) {
101
- eng.manifestStrings = strings;
102
- eng.knownDomainsCache = null;
103
- eng.directiveRoutesCache = null;
104
- eng.scopedReadsCache = null;
105
- eng.placementDirectivesCache = null;
106
- eng.tenantTypesCache = null;
107
- eng.globalAggregatesCache = null;
108
- const root = eng.preopen.dir.contents;
109
- seedManifests(root, strings);
110
- const wsRoot = root.get("ws");
111
- for (const ws of eng.mounted.keys()) {
112
- const wsDir = wsRoot.contents.get(ws);
113
- if (wsDir) seedManifests(wsDir.contents, strings);
114
- }
115
- }
116
-
117
- /** The domains DEPLOYED to this workspace = the effective identity map's keys. */
118
- export function knownDomains(eng) {
119
- if (eng.knownDomainsCache) return eng.knownDomainsCache;
120
- eng.knownDomainsCache = new Set(Object.keys(JSON.parse(eng.manifestStrings.identity)));
121
- return eng.knownDomainsCache;
122
- }
99
+ // There is NO host→holon manifest channel. The wasm derives every read/identity
100
+ // manifest from the FOLDED ledger (installed_read_manifest_bytes_from_state) and
101
+ // refuses a package lacking nomosReadProjection. A host cannot provide a manifest:
102
+ // the op does not exist (Read Confinement — the kernel's transition function, not a
103
+ // host convention). `setManifests` is gone, not no-op'd, so the lie is unrepresentable.
123
104
 
124
105
  // ── TRANSPARENT SHARDING: the WRONG-HOME GATE (the edge bailiff — sharding slice 2) ──
125
106
  //
@@ -135,18 +116,10 @@ export function knownDomains(eng) {
135
116
 
136
117
  /** directive routes off the effective identity manifests: `${domain} ${directiveId}` → route. */
137
118
  export function directiveRoutes(eng) {
138
- if (eng.directiveRoutesCache) return eng.directiveRoutesCache;
139
- const routes = new Map();
140
- let identity = {};
141
- try { identity = JSON.parse(eng.manifestStrings.identity); } catch {}
142
- for (const [domain, manifestJson] of Object.entries(identity)) {
143
- try {
144
- const m = typeof manifestJson === "string" ? JSON.parse(manifestJson) : manifestJson;
145
- for (const r of (m && m.routes) || []) routes.set(`${domain} ${r.directive}`, r);
146
- } catch {}
147
- }
148
- eng.directiveRoutesCache = routes;
149
- return routes;
119
+ void eng;
120
+ // Host-side manifest taxonomy is retired. Shard routing needs a holon-owned
121
+ // taxonomy API before it can be re-enabled without leaking law into the host.
122
+ return new Map();
150
123
  }
151
124
 
152
125
  /** The 48-bit route tag of a home key (must agree byte-for-byte with the client mint). */
@@ -246,39 +219,15 @@ const FRAMEWORK_AGG_TYPES = new Set([
246
219
  * the suffix re-derivation, deep-verify's recount): control-plane rows (PolicyBundle,
247
220
  * DomainInstallation) and framework law-state never skew the capacity metric. */
248
221
  function tenantTypes(eng) {
249
- if (eng.tenantTypesCache) return eng.tenantTypesCache;
250
- let readManifest = {};
251
- try { readManifest = JSON.parse(eng.manifestStrings.read); } catch {}
252
- // §5.4 (slice 4): GLOBAL reference data is replicated custody on every shard —
253
- // its rows arrive by the reverse lane (replicateIntents — a host leg the §5.2
254
- // capture never sees), so counting them as tenant load would let the three
255
- // row-count lanes drift (capture vs suffix vs deep-verify recount). Excluded
256
- // from the capacity metric on EVERY lane, by the one shared definition here.
257
- const globals = globalAggregates(eng);
258
- const out = new Set(Object.keys(readManifest.aggregateFieldKinds || {}).filter((t) => !FRAMEWORK_AGG_TYPES.has(t) && !globals.has(t)));
259
- eng.tenantTypesCache = out;
260
- return out;
222
+ void eng;
223
+ return new Set();
261
224
  }
262
225
 
263
226
  /** The derived PLACEMENT directives off the effective identity manifests:
264
227
  * packed type `site` ⇒ `birthSite` keyed by payload field `siteId` (the law's naming). */
265
228
  export function placementDirectives(eng) {
266
- if (eng.placementDirectivesCache) return eng.placementDirectivesCache;
267
- const out = new Map(); // directiveId -> { axis, keyField }
268
- const pascal = (s) => s.replace(/[_\s-]+(\w)/g, (_m, c) => c.toUpperCase()).replace(/^./, (c) => c.toUpperCase());
269
- let identity = {};
270
- try { identity = JSON.parse(eng.manifestStrings.identity); } catch {}
271
- for (const manifestJson of Object.values(identity)) {
272
- let m;
273
- try { m = typeof manifestJson === "string" ? JSON.parse(manifestJson) : manifestJson; } catch { continue; }
274
- for (const t of (m && m.workspaceTypes) || []) {
275
- if (!t || t.mode !== "packed") continue;
276
- const p = pascal(String(t.id));
277
- out.set(`birth${p}`, { axis: t.id, keyField: `${p.length ? p[0].toLowerCase() + p.slice(1) : p}Id` });
278
- }
279
- }
280
- eng.placementDirectivesCache = out;
281
- return out;
229
+ void eng;
230
+ return new Map();
282
231
  }
283
232
 
284
233
  /** The ESTATE-SCOPED maintained reads off the effective identity manifests.
@@ -287,31 +236,8 @@ export function placementDirectives(eng) {
287
236
  * predicate-aware in the kernel); the SUFFIX re-derivation evaluates it host-side
288
237
  * ({@link evalCanonicalPred}) so both lanes agree byte-for-byte. */
289
238
  export function scopedReads(eng) {
290
- if (eng.scopedReadsCache) return eng.scopedReadsCache;
291
- const out = [];
292
- let identity = {};
293
- try { identity = JSON.parse(eng.manifestStrings.identity); } catch {}
294
- for (const manifestJson of Object.values(identity)) {
295
- let m;
296
- try { m = typeof manifestJson === "string" ? JSON.parse(manifestJson) : manifestJson; } catch { continue; }
297
- for (const c of (m && m.counts) || []) {
298
- 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 } : {}) });
299
- }
300
- for (const s of (m && m.sums) || []) {
301
- 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 } : {}) });
302
- }
303
- // SLICE 8 — scoped EXTREMUMS: per-shard min/max ride the same delta lane
304
- // (events carry post-intent ABSOLUTES; the gate extremizes-on-monotone and
305
- // requires declared re-derivation on retractions). The oracle is the
306
- // projection's long-maintained extrema lane (#47's `extremum` RPC).
307
- for (const [key, kind] of [["mins", "min"], ["maxes", "max"]]) {
308
- for (const e of (m && m[key]) || []) {
309
- if (e && typeof e.scope === "string") out.push({ id: e.id, kind, of: e.of, by: e.by ?? null, field: e.valueField, ...(e.where ? { where: e.where } : {}) });
310
- }
311
- }
312
- }
313
- eng.scopedReadsCache = out;
314
- return out;
239
+ void eng;
240
+ return [];
315
241
  }
316
242
 
317
243
  /**
@@ -340,19 +266,8 @@ export function evalCanonicalPred(pred, get) {
340
266
  * DOWNWARD into every shard chain by re-admission (the reverse delta lane).
341
267
  */
342
268
  export function globalAggregates(eng) {
343
- if (eng.globalAggregatesCache) return eng.globalAggregatesCache;
344
- const out = new Set();
345
- let identity = {};
346
- try { identity = JSON.parse(eng.manifestStrings.identity); } catch {}
347
- for (const manifestJson of Object.values(identity)) {
348
- let m;
349
- try { m = typeof manifestJson === "string" ? JSON.parse(manifestJson) : manifestJson; } catch { continue; }
350
- for (const t of (m && m.workspaceTypes) || []) {
351
- for (const g of (t && t.globals) || []) if (typeof g === "string") out.add(g);
352
- }
353
- }
354
- eng.globalAggregatesCache = out;
355
- return out;
269
+ void eng;
270
+ return new Set();
356
271
  }
357
272
 
358
273
  /** Parse an intent blob's sealed events: [{aggregate, ops: Map(field -> {Str|Int})}]. */
@@ -1066,12 +981,6 @@ export async function admitIntentOffers(eng, ws, ledger, offers, { refuseDomains
1066
981
  rec.rejected.push({ oid, error: `session lane refuses ${dom ? `'${dom}'` : "non-authored"} intents — law deploys go through POST /v1/workspaces/${ws}/domains with the workspace secret` });
1067
982
  continue;
1068
983
  }
1069
- if (!knownDomains(eng).has(dom)) {
1070
- 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`;
1071
- rec.rejected.push({ oid, error, deadLettered: true });
1072
- deadLetters.push({ intentId, oid, session: cid, error, blob, domain: dom, directiveId: dirId });
1073
- continue;
1074
- }
1075
984
  if (receiptFence && dirId && Array.isArray(receiptFence[dom]) && receiptFence[dom].includes(dirId)) {
1076
985
  const error = `'${dom}/${dirId}' is a capability RECEIPT — receipts are executor/owner writes, never open-session writes. Author it via POST /v1/workspaces/${ws}/author (owner Bearer), or — if this parked receipt is sanctioned — the owner unjams it: POST /v1/workspaces/${ws}/dead-letters/retry`;
1077
986
  rec.rejected.push({ oid, error, deadLettered: true });
@@ -1214,7 +1123,6 @@ export async function deepVerifyShard(eng, shardWs, shardLedger, coordinatorRows
1214
1123
 
1215
1124
  function stageWorkspaceDir(eng, ws) {
1216
1125
  const wsContents = new Map();
1217
- seedManifests(wsContents, eng.manifestStrings);
1218
1126
  eng.preopen.dir.contents.get("ws").contents.set(ws, new Directory(wsContents));
1219
1127
  }
1220
1128
 
@@ -1704,7 +1612,7 @@ async function mainIntentIds(eng, ws) {
1704
1612
  * engine has no durable store), obvious attacks drop. Admitted intents merge to main,
1705
1613
  * durably push, the branch is consumed.
1706
1614
  */
1707
- export async function admitAll(eng, ws, ledger, { refuseDomains = [], receiptFence = null, shardRouting = null, sessionRefs = null } = {}) {
1615
+ export async function admitAll(eng, ws, ledger, { refuseDomains = [], receiptFence = null, shardRouting = null, sessionRefs = null, defer = false } = {}) {
1708
1616
  const refuse = new Set(refuseDomains);
1709
1617
  const routes = shardRouting ? directiveRoutes(eng) : null;
1710
1618
  const gitdir = gitdirOf(ws);
@@ -1823,14 +1731,6 @@ export async function admitAll(eng, ws, ledger, { refuseDomains = [], receiptFen
1823
1731
  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` });
1824
1732
  continue;
1825
1733
  }
1826
- // LAW-FENCE part 2: the domain must be DEPLOYED to this workspace. An
1827
- // undeployed domain's write is the canonical LEGITIMATE JAM — dead-letter.
1828
- if (!knownDomains(eng).has(dom)) {
1829
- 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`;
1830
- rejected.push({ oid: oid.slice(0, 10), error, deadLettered: true });
1831
- deadLetters.push({ intentId, oid: oid.slice(0, 10), session: cid, error, blob, domain: dom, directiveId: dirId });
1832
- continue;
1833
- }
1834
1734
  // THE RECEIPT FENCE (capability_marketplace.md slice 1.5 — the ratified
1835
1735
  // authz condition): a capability RECEIPT (complete/fail/block/deadLetter)
1836
1736
  // is an EXECUTOR/OWNER write — the open session lane never folds one, so
@@ -1984,8 +1884,17 @@ export async function admitAll(eng, ws, ledger, { refuseDomains = [], receiptFen
1984
1884
  // the ws fs); we then SKIP the branch deletes so every offered intent re-admits next cycle (idempotent by
1985
1885
  // id). On success, delete the consumed session branches (best-effort cleanup; a stale branch is a harmless
1986
1886
  // re-fetch). Single-client workspaces were already one push/cycle; this collapses collaborative bursts.
1887
+ // THE BAILIFF SEAM: when the HOST asked us to `defer` (the open session admit lane on a
1888
+ // non-coordinator workspace), we DO NOT push or delete consumed branches inline — the fold
1889
+ // is already on resident main (truth), and the host backs it up to custody off the critical
1890
+ // path (engine.backupToCustody, debounced + coalesced). This is sound because the SESSION
1891
+ // BRANCHES are themselves durable in custody (the durable inbox) and admit is idempotent by
1892
+ // intent id: a crash before backup just re-derives the SAME sealed main from the same offers.
1893
+ // Coordinator/sharded admits (any §5.2/§5.4/placement/split follow-up) stay durable-before-ack.
1894
+ const hasCoordination = !!(summaryCtx || placements.length || splits.length || globalsOut.length);
1895
+ const deferDurability = defer && !hasCoordination;
1987
1896
  let _sealed = true;
1988
- if (totalAdmitted) {
1897
+ if (totalAdmitted && !deferDurability) {
1989
1898
  const _sealDone = _span(`seal (batch)`, "cseal", 1, { sessions: consumed.length, admitted: totalAdmitted });
1990
1899
  const _s = Date.now();
1991
1900
  try {
@@ -1998,7 +1907,7 @@ export async function admitAll(eng, ws, ledger, { refuseDomains = [], receiptFen
1998
1907
  _sealDone();
1999
1908
  }
2000
1909
  }
2001
- if (_sealed) {
1910
+ if (_sealed && !deferDurability) {
2002
1911
  for (const { ref } of consumed) {
2003
1912
  try { await pushVia(ledger, () => git.push({ fs: eng.fs, http, gitdir, url: ledger.remote, ref: BRANCH, remoteRef: ref, delete: true, headers: ledger.headers })); } catch {}
2004
1913
  }
@@ -2017,9 +1926,33 @@ export async function admitAll(eng, ws, ledger, { refuseDomains = [], receiptFen
2017
1926
  ...(placements.length ? { placements } : {}),
2018
1927
  ...(splits.length ? { splits } : {}),
2019
1928
  ...(globalsOut.length ? { globals: globalsOut } : {}),
1929
+ // The host's backup lane consumes these: `deferred` ⇒ resident main carries totalAdmitted
1930
+ // un-backed-up commits and `consumed` are the session branches to delete AFTER the push lands.
1931
+ ...(deferDurability && totalAdmitted ? { deferred: true, consumed, admitted: totalAdmitted } : {}),
2020
1932
  };
2021
1933
  }
2022
1934
 
1935
+ // THE BAILIFF'S BACKUP — push resident main to custody when quiet, NEVER on a write's critical
1936
+ // path and NEVER destructive. Unlike commitAndPush (durability-before-ack: unmounts on failure),
1937
+ // a failed backup leaves the resident holon mounted and the session branches intact, so the host
1938
+ // just retries on the next quiet tick. Append-only main means an off-lock push of an older head
1939
+ // concurrent with a fresh fold is always a sound fast-forward subset (the newer head backs up next).
1940
+ export async function backupToCustody(eng, ws, ledger, { consumed = [] } = {}) {
1941
+ if (!eng.mounted.has(ws)) return { ok: false, error: "not mounted" };
1942
+ const gitdir = gitdirOf(ws);
1943
+ let head = null;
1944
+ try { head = await git.resolveRef({ fs: eng.fs, gitdir, ref: BRANCH }); } catch { return { ok: false, error: "no local main" }; }
1945
+ try {
1946
+ await pushVia(ledger, () => git.push({ fs: eng.fs, http, gitdir, url: ledger.remote, ref: BRANCH, remoteRef: BRANCH, force: false, headers: ledger.headers }));
1947
+ } catch (e) {
1948
+ return { ok: false, error: String(e).slice(0, 200), head };
1949
+ }
1950
+ for (const { ref } of consumed) {
1951
+ try { await pushVia(ledger, () => git.push({ fs: eng.fs, http, gitdir, url: ledger.remote, ref: BRANCH, remoteRef: ref, delete: true, headers: ledger.headers })); } catch {}
1952
+ }
1953
+ return { ok: true, head };
1954
+ }
1955
+
2023
1956
  /**
2024
1957
  * VERIFY AN UPLOADED LEDGER — "the githolon executes and thus validates it". Fresh-mounts
2025
1958
  * the workspace from custody (never a cached mount — the bytes under judgment are the
@@ -2095,32 +2028,8 @@ export async function deploy(eng, ws, ledger, usda, domainHash, dispositions) {
2095
2028
  // adjudicates the stable-id diff against. Absent for the common (non-destructive)
2096
2029
  // upgrade; the gate's typed refusal names the remedy when one is needed.
2097
2030
  const result = author(eng, ws, "nomos", "installDomain", installPayload(domainHash, usda, eng.installedBy, dispositions), eng.hashes.nomos);
2098
- // THE SHARD-IDENTITY ENSURE (#41 slice 3a): a taxonomy-bearing law carries the
2099
- // homing invariant, which judges against the workspace's OWN declared identity
2100
- // (`NomosShardIdentity`, law-state in the shard's own chain). The worker is the
2101
- // bailiff: it records the CUSTODY fact (this workspace's name ⇒ its shard label)
2102
- // through the chain's own gate right after the law lands — attributed, replayable,
2103
- // idempotent (an Ensure; re-deploys re-assert the same state). Coordinator ⇒ label ""
2104
- // (no homing claim — the invariant holds vacuously there).
2105
- try {
2106
- eng.directiveRoutesCache = null; // the just-staged overlay may add routes
2107
- const routed = new Set([...directiveRoutes(eng).keys()].map((k) => k.split(" ")[0]));
2108
- if (routed.size) {
2109
- const m = ws.match(/^(.*)--(s\d+)$/);
2110
- const label = m ? m[2] : "";
2111
- const coordinator = m ? m[1] : ws;
2112
- const existing = qById(eng, ws, "nomos-shard-identity:self");
2113
- const current = existing.length ? existing[0].data : null;
2114
- if (!current || current.label !== label || current.coordinator !== coordinator) {
2115
- for (const dom of routed) {
2116
- try {
2117
- const declared = author(eng, ws, dom, "nomosDeclareShardIdentity", { label, coordinator, declaredAt: new Date().toISOString() }, domainHash);
2118
- if (declared && declared.ok !== false) break; // one declaration IS the workspace's identity
2119
- } catch {} // a routed domain owned by an earlier law: its own deploy declared it
2120
- }
2121
- }
2122
- }
2123
- } catch {}
2031
+ // Shard identity used to be inferred from host-side taxonomy. That is retired:
2032
+ // if sharding needs declaration, the holon must derive and author it itself.
2124
2033
  await commitAndPush(eng, ws, ledger);
2125
2034
  return { head: result.head, installation: qById(eng, ws, `domain-installation:${domainHash}`) };
2126
2035
  }