@githolon/testing 0.34.1 → 0.36.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.34.1",
3
+ "version": "0.36.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",
@@ -44,7 +44,7 @@ function osEntropyBuffer(n) { const bytes = new Uint8Array(n * 8); for (let o =
44
44
  function stringifyBig(o) { return JSON.stringify(o, (_k, v) => (typeof v === "bigint" ? `@@B:${v}@@` : v)).replace(/"@@B:(\d+)@@"/g, "$1"); }
45
45
  const log = (evt, fields = {}) => console.log(JSON.stringify({ evt, t: Date.now(), ...fields }));
46
46
 
47
- function call(ex, mode, fields, STDERR) {
47
+ export function call(ex, mode, fields, STDERR) {
48
48
  const req = enc.encode(JSON.stringify({ mode, ...fields }));
49
49
  const reqPtr = ex.git_holon_alloc(req.length);
50
50
  try {
@@ -1411,9 +1411,31 @@ export function author(eng, ws, domain, directiveId, payload, controllerHash, op
1411
1411
  capPorts.principal_verified = !!opts.principalVerified;
1412
1412
  }
1413
1413
  const envelope = { payload: { domain, directiveId, payload }, captured_ports: capPorts, policy_version: 1, policy_domain: "Nomos", policy_gas: 0, policy_memory: 0 };
1414
+ // ATTESTED READS (slice B stage 1 — attested_reads_design.md §4.1): the PRE-FETCHED foreign
1415
+ // envelopes injected on the author request (`[{queryId, result, attestation}]`). The wasm author
1416
+ // door serves a plan's `read(q, args, {from})` from these AFTER verifying each (never mid-plan
1417
+ // I/O); unconsumed entries are dropped (COMMIT-ONLY-CONSUMED). Absent ⇒ byte-identical envelope
1418
+ // (the era rule). The DO→DO/HTTPS pre-fetch that POPULATES this is stage 2 — this is the seam.
1419
+ if (opts.attestations !== undefined) envelope.attestations = opts.attestations;
1414
1420
  writeWork(eng, `payload-${seq}.json`, enc.encode(JSON.stringify(payload)));
1415
1421
  writeWork(eng, `envelope-${seq}.json`, enc.encode(stringifyBig(envelope)));
1416
1422
  const genesis = !controllerHash;
1423
+ // THE COLOCATION SHORT-CIRCUIT (stage 2): when the declared foreign SOURCE is already MOUNTED in
1424
+ // THIS engine (the container pool colocates workspaces; the engine plane is multi-workspace — the
1425
+ // birth-effect precedent), a missing attested read never leaves the kernel: the gate's typed
1426
+ // `attested-read-unreachable` refusal names {queryId, from, args}; we serve + sign it via the SAME
1427
+ // `attestedRead` op (secret injected per call from `opts.attestorSecrets[from]` — operator config
1428
+ // the host passes through blindly), inject the envelope, and retry. Zero HTTP, zero host decision.
1429
+ // The envelope is BYTE-IDENTICAL to one fetched over ?attest=1 (same signed bytes, same verify arm).
1430
+ // A CARRIED attestation always wins — the refusal only fires for a read no injected entry answers,
1431
+ // so nothing is ever silently replaced. Non-colocated / secret-less sources fall through to the
1432
+ // caller with the kernel's own instruction (the client-carried ?attest=1 dance).
1433
+ const attestorSecretFor = (from) => {
1434
+ const s = opts.attestorSecrets;
1435
+ if (!s) return null;
1436
+ if (typeof s === "string") return s;
1437
+ return typeof s[from] === "string" ? s[from] : null;
1438
+ };
1417
1439
  // THE DEFERRED-PROJECTION ACK (#47, sharding slice 7): the read-projection
1418
1440
  // catch-up (incl. the derive/combine materialize) moves OFF the author ack path
1419
1441
  // by default — every read op self-heals to head, and the post-ack warm lane
@@ -1430,7 +1452,28 @@ export function author(eng, ws, domain, directiveId, payload, controllerHash, op
1430
1452
  // DRY-RUN (offer-kernel): `offer` with the commit withheld — run the plan + full gate, get the verdict,
1431
1453
  // commit nothing. This is the home of the old `evolve_dryrun` verb (see `evolveDryRun`). Never defers
1432
1454
  // projection (no commit to flush).
1433
- const v = JSON.parse(call(eng.ex, "offer", { repoArg: repoArgOf(ws), workspace: ws, domain, directiveId, payloadFile: `/work/payload-${seq}.json`, envelopeFile: `/work/envelope-${seq}.json`, seq, actor: opts.actor ?? "", ...(opts.authToken ? { authToken: opts.authToken } : {}), domainFile: genesis ? "/work/domain.package.usda" : "", domainHash: genesis ? "" : controllerHash, branch: BRANCH, ...(opts.authorSecret ? { authorSecret: opts.authorSecret } : {}), ...(opts.dryRun ? { dryRun: true } : (defer ? { deferProjection: true } : {})) }, eng.STDERR));
1455
+ const offerOnce = () =>
1456
+ JSON.parse(call(eng.ex, "offer", { repoArg: repoArgOf(ws), workspace: ws, domain, directiveId, payloadFile: `/work/payload-${seq}.json`, envelopeFile: `/work/envelope-${seq}.json`, seq, actor: opts.actor ?? "", ...(opts.authToken ? { authToken: opts.authToken } : {}), domainFile: genesis ? "/work/domain.package.usda" : "", domainHash: genesis ? "" : controllerHash, branch: BRANCH, ...(opts.authorSecret ? { authorSecret: opts.authorSecret } : {}), ...(opts.dryRun ? { dryRun: true } : (defer ? { deferProjection: true } : {})) }, eng.STDERR));
1457
+ let v = offerOnce();
1458
+ // COLOCATED foreign reads: bounded loop (a plan may declare several) — each pass answers exactly the
1459
+ // read the kernel named, so it terminates in ≤ #declared-foreign-reads passes (cap 8, defensive).
1460
+ for (let pass = 0; pass < 8 && v.outcome === "refused"; pass++) {
1461
+ const need = parseAttestedReadNeed(v.verdict?.reason ?? v.error);
1462
+ if (!need || !eng.mounted.has(need.from)) break;
1463
+ const secret = attestorSecretFor(need.from);
1464
+ if (!secret) break;
1465
+ const served = attestedRead(eng, need.from, {
1466
+ queryId: need.queryId, paramsJson: JSON.stringify(need.args), attestorSecret: secret,
1467
+ nowMs: opts.attestNowMs ?? Date.now(),
1468
+ // K3 (§1.5.3): colocated custody is in-memory — always ask for the carried enrollment
1469
+ // proof; the kernel produces-or-omits under its own bounds (the adapter decides nothing).
1470
+ includeRosterProof: true,
1471
+ });
1472
+ if (!served || served.ok !== true) break; // the kernel's original instruction stands for the caller
1473
+ envelope.attestations = [...(envelope.attestations ?? []), { queryId: need.queryId, result: served.rows, attestation: served.attestation }];
1474
+ writeWork(eng, `envelope-${seq}.json`, enc.encode(stringifyBig(envelope)));
1475
+ v = offerOnce();
1476
+ }
1434
1477
  // KEEP `born`: the wasm offer ran the G3 birth offer-effect and surfaced the child heads; thread them
1435
1478
  // through the normalized shape so the relay (container author case → worker out.born) sees them.
1436
1479
  const res = v.outcome === "admitted" ? { ok: true, head: v.head, intentOut: v.intentOut, ...(v.born ? { born: v.born } : {}) }
@@ -1861,6 +1904,36 @@ export function describe(eng, ws) {
1861
1904
  // (base64) — the parts a `recordShare` payload carries; the subject's home gate verifies them (§3 gate-arm).
1862
1905
  export const attestationSign = (eng, { secret, asserterWorkspace, object, relation, subject, attestedAt }) =>
1863
1906
  JSON.parse(call(eng.ex, "query", { queryBytes: b64Json({ op: "attestationSign", secret, asserterWorkspace, object, relation, subject, attestedAt }) }, eng.STDERR));
1907
+
1908
+ // THE ATTESTED READ (slice B stage 2 — attested_reads_design.md §4.1, the source side). Serve + SIGN a
1909
+ // DECLARED query read over this workspace's own certified fold: the KERNEL derives the rows, pins
1910
+ // {genesis, head, fold_root, signer epoch, time} and signs with the PER-CALL-INJECTED read-attestor
1911
+ // secret (the `attestationSign` pattern — never persisted). The adapter/host transports the secret and
1912
+ // relays the returned `{rows, attestation, leafCount}` OPAQUE — it never constructs, inspects, or
1913
+ // modifies the envelope (the bailiff line). `nowMs` = the source clock at serve, injected by the caller.
1914
+ export const attestedRead = (eng, ws, { queryId, paramsJson, attestorSecret, nowMs, includeRosterProof }) =>
1915
+ JSON.parse(call(eng.ex, "query", {
1916
+ repoArg: repoArgOf(ws), workspace: ws, branch: BRANCH,
1917
+ queryBytes: b64Json({ op: "attestedRead", queryId, paramsJson: paramsJson || "{}", attestorSecret, nowMs: nowMs ?? Date.now(), sourceWorkspace: ws, ...(includeRosterProof ? { includeRosterProof: true } : {}) }),
1918
+ }, eng.STDERR));
1919
+
1920
+ // THE KERNEL-INSTRUCTED MISSING-EVIDENCE PARSE (stage 2 §4.1): the one gate's typed
1921
+ // `attested-read-unreachable` refusal NAMES the exact fetch — queryId + source + args. This parses it so
1922
+ // a caller (the colocation short-circuit below; the web client's pre-fetch convenience) can fulfil the
1923
+ // instruction and retry. Returns { queryId, from, args } or null. The kernel instructs; nobody guesses.
1924
+ export function parseAttestedReadNeed(error) {
1925
+ const m = /attested-read-unreachable: read\('([^']+)', \{from: '([^']+)'\}\) args=(.*?) has no injected attestation/
1926
+ .exec(String(error || ""));
1927
+ if (!m) return null;
1928
+ // The refusal may surface through a JSON-quoting layer (the engine's Threw detail) — the args
1929
+ // blob's quotes then arrive backslash-escaped. Unescape a level at a time until it parses.
1930
+ let blob = m[3];
1931
+ for (let i = 0; i < 4; i++) {
1932
+ try { return { queryId: m[1], from: m[2], args: JSON.parse(blob) }; }
1933
+ catch { blob = blob.replace(/\\(["\\])/g, "$1"); }
1934
+ }
1935
+ return null;
1936
+ }
1864
1937
  // THE BIRTH-CERT SIGNER (VA root-of-trust) — the SINGLE SOURCE OF TRUTH for the cert byte format. The
1865
1938
  // signer's client produces a SIGNED birth cert IN-wasm (`cert_sign` reuses `BirthCert::signed_bytes`/`sign`,
1866
1939
  // the SIBLING of `attestation_sign`) so no client re-implements the layout (drift-proof). The device
@@ -2746,3 +2819,48 @@ export function warmEngine(eng) {
2746
2819
  // not a top-level adapter verb. Still post-ack (the adapter calls warmEngine off the write critical path).
2747
2820
  return JSON.parse(call(eng.ex, "query", { repoArg: "/work", workspace: "root", queryBytes: b64Json({ op: "warm" }), branch: BRANCH }, eng.STDERR));
2748
2821
  }
2822
+
2823
+ /**
2824
+ * ATTESTED READS §5.2 (stage 3) — THE BATCH-SEAL DRIVER. The adapter's ONLY roles here are
2825
+ * SCHEDULING (when to ask) and TRANSPORT (carrying the kernel's own snapshot back as an offer)
2826
+ * — the debounced-admission-alarm precedent: the kernel decides WHAT a seal is (the
2827
+ * `attestationOpenBatch` snapshot + `shouldSeal` verdict + the gate's Merkle-root demand);
2828
+ * this function decides nothing. Call it on a timer / after serves / on unmount
2829
+ * (`{unmounting: true}` forces pending batches to seal — a batch never dies silently).
2830
+ * A failed seal offer LEAVES the accumulator intact (retry next tick, never a silent drop);
2831
+ * only an ADMITTED seal clears the sealed snapshot (`attestationBatchClear`).
2832
+ *
2833
+ * `domain` names the installed law that exports `recordAttestationBatch` (the framework
2834
+ * directive rides the composing law); `controllerHash` is that law's content hash. The offer
2835
+ * is authored AS `workspace:read-attestor` (the enrolled signer — §1.2's "one key, two
2836
+ * duties"); on a warranted workspace pass `opts.authorOpts` with the signing material.
2837
+ */
2838
+ export function sealAttestationBatches(eng, ws, domain, controllerHash, opts = {}) {
2839
+ const snap = JSON.parse(call(eng.ex, "query", {
2840
+ repoArg: repoArgOf(ws), workspace: ws, branch: BRANCH,
2841
+ queryBytes: b64Json({ op: "attestationOpenBatch", repoArg: repoArgOf(ws), nowMs: opts.nowMs ?? Date.now(), unmounting: !!opts.unmounting }),
2842
+ }, eng.STDERR));
2843
+ const outcomes = [];
2844
+ for (const b of snap.batches ?? []) {
2845
+ if (b.leafCount === 0) continue;
2846
+ if (!(b.shouldSeal || opts.force)) continue;
2847
+ const payload = {
2848
+ batchId: b.batchId, batchRoot: b.batchRoot, leafCount: b.leafCount, leaves: b.leaves,
2849
+ firstAt: b.firstAt, lastAt: b.lastAt, headAtSeal: b.headAtSeal,
2850
+ foldRootAtSeal: b.foldRootAtSeal, signerKeyHash: b.signerKeyHash,
2851
+ };
2852
+ const r = author(eng, ws, domain, "recordAttestationBatch", payload, controllerHash, {
2853
+ actor: "workspace:read-attestor", ...(opts.authorOpts ?? {}),
2854
+ });
2855
+ if (!r.ok) {
2856
+ outcomes.push({ batchId: b.batchId, ok: false, error: r.error }); // retained — retries next call
2857
+ continue;
2858
+ }
2859
+ JSON.parse(call(eng.ex, "query", {
2860
+ repoArg: repoArgOf(ws), workspace: ws, branch: BRANCH,
2861
+ queryBytes: b64Json({ op: "attestationBatchClear", batchId: b.batchId, leafCount: b.leafCount }),
2862
+ }, eng.STDERR));
2863
+ outcomes.push({ batchId: b.batchId, ok: true, leafCount: b.leafCount, intent: r.intentOut });
2864
+ }
2865
+ return outcomes;
2866
+ }