tamperward 2.15.2 → 2.16.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.
Files changed (3) hide show
  1. package/README.md +12 -0
  2. package/dist/cli/index.js +485 -109
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -369,6 +369,18 @@ restored `"test": "sh scripts/test.sh"` will happily call a script nothing
369
369
  restored. It bounds the class rather than closing it — see
370
370
  [the threat model](./docs/THREAT-MODEL-pristine-run.md).
371
371
 
372
+ From **2.16.0**, verifier suite output is diagnostic evidence instead of discarded
373
+ noise. Both visible and pristine stages continuously drain stdout/stderr through a
374
+ trusted supervisor, retain only the final **16 KiB per stream**, and count the total
375
+ bytes observed. Structured output exposes this under
376
+ `visible.diagnostics` / `pristine.diagnostics` with
377
+ `captured_bytes`, `retained_bytes`, `truncated`, and a bounded `tail` on a
378
+ failed stage. Default human output remains quiet on success. On failure, retained
379
+ diagnostics are rendered with every line prefixed and terminal/control characters
380
+ escaped, so candidate output cannot become ANSI control traffic or a GitHub
381
+ `::command::`. These bytes are evidence produced by candidate code; they never own
382
+ the verifier verdict.
383
+
372
384
  The four primitives:
373
385
 
374
386
  ```bash
package/dist/cli/index.js CHANGED
@@ -7085,13 +7085,14 @@ function watchTree(dir, cb, onState, onError) {
7085
7085
  }
7086
7086
  }
7087
7087
  const watchers = /* @__PURE__ */ new Map();
7088
+ let closed = false;
7088
7089
  onState("fallback", 0);
7089
7090
  const addDir = (rel) => {
7090
- if (watchers.has(rel) || SKIP2.test(rel + "/")) return;
7091
+ if (closed || watchers.has(rel) || SKIP2.test(rel + "/")) return;
7091
7092
  let w;
7092
7093
  try {
7093
7094
  w = watch(rel ? join11(dir, rel) : dir, (kind, fname) => {
7094
- if (!fname) return;
7095
+ if (closed || !fname) return;
7095
7096
  const child = rel ? `${rel}/${String(fname)}` : String(fname);
7096
7097
  cb(kind, child);
7097
7098
  try {
@@ -7129,9 +7130,14 @@ function watchTree(dir, cb, onState, onError) {
7129
7130
  }
7130
7131
  };
7131
7132
  walk2("");
7132
- return { close: () => {
7133
- for (const w of watchers.values()) w.close();
7134
- } };
7133
+ return {
7134
+ close: () => {
7135
+ if (closed) return;
7136
+ closed = true;
7137
+ for (const w of watchers.values()) w.close();
7138
+ watchers.clear();
7139
+ }
7140
+ };
7135
7141
  }
7136
7142
  function startWatcher(dir, log, policy) {
7137
7143
  try {
@@ -7215,8 +7221,11 @@ function startWatcher(dir, log, policy) {
7215
7221
  },
7216
7222
  (detail) => degrade(detail)
7217
7223
  );
7224
+ let closed = false;
7218
7225
  return {
7219
7226
  close: () => {
7227
+ if (closed) return;
7228
+ closed = true;
7220
7229
  tree.close();
7221
7230
  health.state = "stopped";
7222
7231
  health.stopped_at = (/* @__PURE__ */ new Date()).toISOString();
@@ -9152,11 +9161,11 @@ function runDoctor(opts = {}) {
9152
9161
  }
9153
9162
 
9154
9163
  // src/cli/verify.ts
9155
- import { execFileSync as execFileSync9, spawnSync as spawnSync2 } from "node:child_process";
9164
+ import { execFileSync as execFileSync9, spawnSync as spawnSync3 } from "node:child_process";
9156
9165
  import { createHash as createHash10 } from "node:crypto";
9157
- import { chmodSync as chmodSync2, cpSync, lstatSync as lstatSync5, mkdirSync as mkdirSync6, mkdtempSync as mkdtempSync2, readFileSync as readFileSync15, readlinkSync as readlinkSync3, rmSync as rmSync3, symlinkSync, writeFileSync as writeFileSync7 } from "node:fs";
9158
- import { tmpdir as tmpdir2 } from "node:os";
9159
- import { dirname as dirname7, isAbsolute as isAbsolute10, join as join16, relative as relative5, resolve as resolve7, sep as sep2 } from "node:path";
9166
+ import { chmodSync as chmodSync2, cpSync, lstatSync as lstatSync5, mkdirSync as mkdirSync6, mkdtempSync as mkdtempSync3, readFileSync as readFileSync16, readlinkSync as readlinkSync3, rmSync as rmSync4, symlinkSync, writeFileSync as writeFileSync8 } from "node:fs";
9167
+ import { tmpdir as tmpdir3 } from "node:os";
9168
+ import { dirname as dirname7, isAbsolute as isAbsolute10, join as join17, relative as relative5, resolve as resolve7, sep as sep2 } from "node:path";
9160
9169
 
9161
9170
  // src/fingerprint.ts
9162
9171
  import { execFileSync as execFileSync8 } from "node:child_process";
@@ -9539,9 +9548,321 @@ function dependencyEnvironmentSummary(descriptor) {
9539
9548
 
9540
9549
  // src/verifier-backend.ts
9541
9550
  import { createHash as createHash9, randomUUID } from "node:crypto";
9551
+ import { spawnSync as spawnSync2 } from "node:child_process";
9552
+ import { accessSync, constants as constants2, readFileSync as readFileSync15, realpathSync as realpathSync4, statSync as statSync4 } from "node:fs";
9553
+ import { delimiter as delimiter2, dirname as dirname6, join as join16, resolve as resolve6 } from "node:path";
9554
+
9555
+ // src/suite-diagnostics.ts
9542
9556
  import { spawnSync } from "node:child_process";
9543
- import { accessSync, constants as constants2, readFileSync as readFileSync14, realpathSync as realpathSync4, statSync as statSync4 } from "node:fs";
9544
- import { delimiter as delimiter2, dirname as dirname6, join as join15, resolve as resolve6 } from "node:path";
9557
+ import { mkdtempSync as mkdtempSync2, rmSync as rmSync3, writeFileSync as writeFileSync7 } from "node:fs";
9558
+ import { tmpdir as tmpdir2 } from "node:os";
9559
+ import { join as join15 } from "node:path";
9560
+ var DIAGNOSTIC_TAIL_BYTES = 16 * 1024;
9561
+ var EMPTY_STREAM = {
9562
+ captured_bytes: 0,
9563
+ retained_bytes: 0,
9564
+ truncated: false,
9565
+ tail: ""
9566
+ };
9567
+ function emptySuiteDiagnostics() {
9568
+ return {
9569
+ stdout: { ...EMPTY_STREAM },
9570
+ stderr: { ...EMPTY_STREAM }
9571
+ };
9572
+ }
9573
+ function decodeUtf8Tail(tail) {
9574
+ let start = 0;
9575
+ while (start < tail.length && (tail[start] & 192) === 128) start++;
9576
+ return tail.subarray(start).toString("utf8");
9577
+ }
9578
+ function streamFromRaw(raw) {
9579
+ let tail = Buffer.alloc(0);
9580
+ try {
9581
+ tail = Buffer.from(raw?.tail_b64 ?? "", "base64");
9582
+ } catch {
9583
+ tail = Buffer.alloc(0);
9584
+ }
9585
+ const captured = Number.isFinite(raw?.captured_bytes) ? Math.max(0, Number(raw?.captured_bytes)) : tail.length;
9586
+ return {
9587
+ captured_bytes: captured,
9588
+ retained_bytes: tail.length,
9589
+ truncated: captured > tail.length,
9590
+ tail: decodeUtf8Tail(tail)
9591
+ };
9592
+ }
9593
+ function scrubDiagnosticText(input) {
9594
+ let out2 = "";
9595
+ for (const ch of input) {
9596
+ const cp = ch.codePointAt(0);
9597
+ if (ch === "\n" || ch === " ") {
9598
+ out2 += ch;
9599
+ } else if (cp === 13) {
9600
+ out2 += "\\r";
9601
+ } else if (cp < 32 || cp >= 127 && cp <= 159) {
9602
+ out2 += cp <= 255 ? `\\x${cp.toString(16).padStart(2, "0")}` : `\\u{${cp.toString(16)}}`;
9603
+ } else {
9604
+ out2 += ch;
9605
+ }
9606
+ }
9607
+ return out2;
9608
+ }
9609
+ function diagnosticLines(stage, diagnostics) {
9610
+ const lines = [];
9611
+ for (const streamName of ["stdout", "stderr"]) {
9612
+ const stream = diagnostics[streamName];
9613
+ if (stream.captured_bytes === 0) continue;
9614
+ const suffix = stream.truncated ? ` \u2014 tail ${stream.retained_bytes}/${stream.captured_bytes} bytes retained` : ` \u2014 ${stream.captured_bytes} bytes`;
9615
+ lines.push(`${stage} suite ${streamName}${suffix}:`);
9616
+ const scrubbed = scrubDiagnosticText(stream.tail);
9617
+ const body = scrubbed.split("\n");
9618
+ if (body.at(-1) === "") body.pop();
9619
+ for (const line of body) lines.push(` | ${line}`);
9620
+ }
9621
+ return lines;
9622
+ }
9623
+ var CAPTURE_SUPERVISOR = String.raw`
9624
+ const cp = require('node:child_process');
9625
+ const fs = require('node:fs');
9626
+
9627
+ const [configFile] = process.argv.slice(1);
9628
+ const cfg = JSON.parse(fs.readFileSync(configFile, 'utf8'));
9629
+ const CAP = Number(cfg.captureBytes);
9630
+
9631
+ const fresh = () => ({ total: 0, tail: Buffer.alloc(0) });
9632
+ const stdout = fresh();
9633
+ const stderr = fresh();
9634
+
9635
+ // Linux ordinary-exit hardening (#371): process-group ownership alone misses a
9636
+ // descendant that calls setsid(). Track the real descendant tree while the
9637
+ // suite is alive so a reparented session escape is still known when the main
9638
+ // child exits. This supplements, rather than replaces, group termination.
9639
+ const trackedDescendants = new Set();
9640
+
9641
+ function linuxDescendants(rootPid) {
9642
+ if (process.platform !== 'linux' || !rootPid) return [];
9643
+ const byParent = new Map();
9644
+ let names = [];
9645
+ try { names = fs.readdirSync('/proc').filter((x) => /^\d+$/.test(x)); } catch { return []; }
9646
+ for (const name of names) {
9647
+ const pid = Number(name);
9648
+ try {
9649
+ const stat = fs.readFileSync('/proc/' + name + '/stat', 'utf8');
9650
+ const fields = stat.slice(stat.lastIndexOf(')') + 2).split(' ');
9651
+ const ppid = Number(fields[1]);
9652
+ if (!Number.isFinite(ppid)) continue;
9653
+ const kids = byParent.get(ppid) || [];
9654
+ kids.push(pid);
9655
+ byParent.set(ppid, kids);
9656
+ } catch {}
9657
+ }
9658
+ const out = [];
9659
+ const seen = new Set([rootPid]);
9660
+ const stack = [rootPid];
9661
+ while (stack.length) {
9662
+ const parent = stack.pop();
9663
+ for (const pid of (byParent.get(parent) || [])) {
9664
+ if (seen.has(pid)) continue;
9665
+ seen.add(pid);
9666
+ out.push(pid);
9667
+ stack.push(pid);
9668
+ }
9669
+ }
9670
+ return out;
9671
+ }
9672
+
9673
+ function trackDescendants() {
9674
+ if (!child || !child.pid || process.platform !== 'linux') return;
9675
+ for (const pid of linuxDescendants(child.pid)) trackedDescendants.add(pid);
9676
+ }
9677
+
9678
+ function append(state, chunk) {
9679
+ const b = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
9680
+ state.total += b.length;
9681
+ if (b.length >= CAP) {
9682
+ state.tail = Buffer.from(b.subarray(b.length - CAP));
9683
+ return;
9684
+ }
9685
+ if (state.tail.length + b.length <= CAP) {
9686
+ state.tail = Buffer.concat([state.tail, b]);
9687
+ return;
9688
+ }
9689
+ const keep = CAP - b.length;
9690
+ state.tail = Buffer.concat([state.tail.subarray(state.tail.length - keep), b]);
9691
+ }
9692
+
9693
+ function diag(state) {
9694
+ return {
9695
+ captured_bytes: state.total,
9696
+ tail_b64: state.tail.toString('base64'),
9697
+ };
9698
+ }
9699
+
9700
+ let child;
9701
+ let done = false;
9702
+ let timedOut = false;
9703
+ let exitInfo = { exit: null, signal: null };
9704
+ let descendantTracker = null;
9705
+
9706
+ function killOwned() {
9707
+ if (!child || !child.pid) return;
9708
+ trackDescendants();
9709
+ if (cfg.detached && process.platform !== 'win32') {
9710
+ try { process.kill(-child.pid, 'SIGKILL'); } catch {}
9711
+ }
9712
+ try { child.kill('SIGKILL'); } catch {}
9713
+ if (process.platform === 'linux') {
9714
+ // Deepest-first is friendlier to short-lived process trees. PIDs are still
9715
+ // rechecked by kill(2); races with natural exit are harmless.
9716
+ for (const pid of Array.from(trackedDescendants).reverse()) {
9717
+ try { process.kill(pid, 'SIGKILL'); } catch {}
9718
+ }
9719
+ }
9720
+ }
9721
+
9722
+ function finish(extra) {
9723
+ if (done) return;
9724
+ done = true;
9725
+ if (descendantTracker) clearInterval(descendantTracker);
9726
+ try {
9727
+ // stdout is reserved for the trusted supervisor result. Candidate suite
9728
+ // stdout/stderr are separate pipes and are never inherited here.
9729
+ process.stdout.write(JSON.stringify({
9730
+ ...exitInfo,
9731
+ timedOut,
9732
+ ...extra,
9733
+ stdout: diag(stdout),
9734
+ stderr: diag(stderr),
9735
+ }));
9736
+ } catch {}
9737
+ process.exit(0);
9738
+ }
9739
+
9740
+ try {
9741
+ child = cp.spawn(cfg.executable, cfg.args, {
9742
+ cwd: cfg.cwd,
9743
+ env: process.env,
9744
+ detached: Boolean(cfg.detached) && process.platform !== 'win32',
9745
+ stdio: ['ignore', 'pipe', 'pipe'],
9746
+ });
9747
+ } catch (e) {
9748
+ finish({ error: String(e) });
9749
+ }
9750
+
9751
+ if (child) {
9752
+ if (process.platform === 'linux') {
9753
+ trackDescendants();
9754
+ descendantTracker = setInterval(trackDescendants, 5);
9755
+ descendantTracker.unref();
9756
+ }
9757
+ child.stdout.on('data', (b) => append(stdout, b));
9758
+ child.stderr.on('data', (b) => append(stderr, b));
9759
+
9760
+ const timer = setTimeout(() => {
9761
+ timedOut = true;
9762
+ killOwned();
9763
+ // Backstop if a platform fails to emit close after the forced kill.
9764
+ setTimeout(() => finish({}), 1000).unref();
9765
+ }, Number(cfg.timeoutMs));
9766
+ timer.unref();
9767
+
9768
+ child.on('error', (e) => {
9769
+ clearTimeout(timer);
9770
+ killOwned();
9771
+ finish({ error: String(e) });
9772
+ });
9773
+
9774
+ child.on('exit', (code, signal) => {
9775
+ exitInfo = {
9776
+ exit: code == null ? null : Number(code),
9777
+ signal: signal == null ? null : String(signal),
9778
+ };
9779
+ if (cfg.killGroupOnFinish) killOwned();
9780
+ });
9781
+
9782
+ // close fires after the stdio pipes close, so the retained tails include all
9783
+ // output that was drained before owned descendants were terminated.
9784
+ child.on('close', (code, signal) => {
9785
+ clearTimeout(timer);
9786
+ if (exitInfo.exit === null && code != null) exitInfo.exit = Number(code);
9787
+ if (exitInfo.signal === null && signal != null) exitInfo.signal = String(signal);
9788
+ finish({});
9789
+ });
9790
+ }
9791
+ `;
9792
+ function parseCapturedSupervisorResult(stdoutText) {
9793
+ let raw;
9794
+ try {
9795
+ raw = JSON.parse(stdoutText);
9796
+ } catch {
9797
+ return null;
9798
+ }
9799
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
9800
+ const diagnostics = {
9801
+ stdout: streamFromRaw(raw.stdout),
9802
+ stderr: streamFromRaw(raw.stderr)
9803
+ };
9804
+ return {
9805
+ exit: typeof raw.exit === "number" ? raw.exit : null,
9806
+ signal: typeof raw.signal === "string" ? raw.signal : null,
9807
+ timedOut: Boolean(raw.timedOut),
9808
+ ...typeof raw.error === "string" && raw.error ? { error: raw.error } : {},
9809
+ diagnostics
9810
+ };
9811
+ }
9812
+ function runCapturedProcessSync(executable, args, opts) {
9813
+ const stateDir = mkdtempSync2(join15(tmpdir2(), "tw-suite-capture-"));
9814
+ const configFile = join15(stateDir, "config.json");
9815
+ const backstop = opts.backstopMs ?? 3e4;
9816
+ try {
9817
+ writeFileSync7(
9818
+ configFile,
9819
+ JSON.stringify({
9820
+ executable,
9821
+ args,
9822
+ cwd: opts.cwd,
9823
+ timeoutMs: opts.timeoutMs,
9824
+ detached: Boolean(opts.detached),
9825
+ killGroupOnFinish: Boolean(opts.killGroupOnFinish),
9826
+ captureBytes: DIAGNOSTIC_TAIL_BYTES
9827
+ }),
9828
+ { mode: 384 }
9829
+ );
9830
+ const supervisor = spawnSync(
9831
+ process.execPath,
9832
+ ["-e", CAPTURE_SUPERVISOR, configFile],
9833
+ {
9834
+ cwd: opts.cwd,
9835
+ env: opts.env,
9836
+ encoding: "utf8",
9837
+ stdio: ["ignore", "pipe", "ignore"],
9838
+ // The trusted result is at most two 16 KiB tails plus small metadata.
9839
+ // A candidate that somehow writes into the supervisor's stdout through
9840
+ // same-UID /proc can only overflow/corrupt this bounded channel and make
9841
+ // parsing fail closed; its suite streams are never inherited here.
9842
+ maxBuffer: 256 * 1024,
9843
+ timeout: opts.timeoutMs + backstop,
9844
+ killSignal: "SIGKILL"
9845
+ }
9846
+ );
9847
+ const parsed = parseCapturedSupervisorResult(supervisor.stdout ?? "");
9848
+ const diagnostics = parsed?.diagnostics ?? emptySuiteDiagnostics();
9849
+ if (!parsed) {
9850
+ const supervisorTimedOut = Boolean(supervisor.error) && supervisor.error.code === "ETIMEDOUT";
9851
+ return {
9852
+ exit: null,
9853
+ signal: supervisor.signal ? String(supervisor.signal) : null,
9854
+ timedOut: supervisorTimedOut,
9855
+ error: supervisor.error?.message ?? "suite capture supervisor did not produce a result",
9856
+ diagnostics
9857
+ };
9858
+ }
9859
+ return parsed;
9860
+ } finally {
9861
+ rmSync3(stateDir, { recursive: true, force: true });
9862
+ }
9863
+ }
9864
+
9865
+ // src/verifier-backend.ts
9545
9866
  var DEFAULT_CONTAINER_RESOURCE_LIMITS = Object.freeze({
9546
9867
  memoryBytes: 2 * 1024 * 1024 * 1024,
9547
9868
  // Docker interprets memory-swap == memory as no additional swap allowance.
@@ -9584,7 +9905,7 @@ function replaceableByCaller(path) {
9584
9905
  function findExecutable(name) {
9585
9906
  for (const raw of (process.env.PATH ?? "").split(delimiter2)) {
9586
9907
  if (!raw) continue;
9587
- const candidate = join15(raw, name);
9908
+ const candidate = join16(raw, name);
9588
9909
  try {
9589
9910
  accessSync(candidate, constants2.X_OK);
9590
9911
  return realpathSync4(candidate);
@@ -9594,7 +9915,7 @@ function findExecutable(name) {
9594
9915
  return null;
9595
9916
  }
9596
9917
  function fileSha256(path) {
9597
- return createHash9("sha256").update(readFileSync14(path)).digest("hex");
9918
+ return createHash9("sha256").update(readFileSync15(path)).digest("hex");
9598
9919
  }
9599
9920
  function engineClientEnv() {
9600
9921
  return {
@@ -9609,7 +9930,7 @@ function dockerArgs(host, args) {
9609
9930
  }
9610
9931
  function dockerAvailable(enginePath, host) {
9611
9932
  try {
9612
- const r = spawnSync(enginePath, dockerArgs(host, ["version"]), {
9933
+ const r = spawnSync2(enginePath, dockerArgs(host, ["version"]), {
9613
9934
  stdio: "ignore",
9614
9935
  timeout: 5e3,
9615
9936
  killSignal: "SIGKILL",
@@ -9637,7 +9958,7 @@ function declaredImageVolumePaths(metadata) {
9637
9958
  }
9638
9959
  function inspectImageMetadata(enginePath, host, image) {
9639
9960
  try {
9640
- const r = spawnSync(enginePath, dockerArgs(host, ["image", "inspect", image]), {
9961
+ const r = spawnSync2(enginePath, dockerArgs(host, ["image", "inspect", image]), {
9641
9962
  encoding: "utf8",
9642
9963
  stdio: ["ignore", "pipe", "ignore"],
9643
9964
  timeout: 1e4,
@@ -9812,7 +10133,7 @@ function verifierBackendSummary(backend) {
9812
10133
  function cleanupContainer(backend, name) {
9813
10134
  if (!backend.enginePath || !backend.daemonHost || !engineStable(backend)) return;
9814
10135
  try {
9815
- spawnSync(backend.enginePath, dockerArgs(backend.daemonHost, ["rm", "-f", "-v", name]), {
10136
+ spawnSync2(backend.enginePath, dockerArgs(backend.daemonHost, ["rm", "-f", "-v", name]), {
9816
10137
  stdio: "ignore",
9817
10138
  timeout: 1e4,
9818
10139
  killSignal: "SIGKILL",
@@ -9923,29 +10244,45 @@ function runContainerStage(backend, dir, command, budgetSecs) {
9923
10244
  resources: backend.resources ?? DEFAULT_CONTAINER_RESOURCE_LIMITS
9924
10245
  });
9925
10246
  try {
9926
- const r = spawnSync(
10247
+ const r = runCapturedProcessSync(
9927
10248
  backend.enginePath,
9928
10249
  dockerArgs(backend.daemonHost, args),
9929
10250
  {
9930
- stdio: "ignore",
9931
- timeout: budgetSecs * 1e3,
9932
- killSignal: "SIGKILL",
9933
- env: engineClientEnv()
10251
+ cwd: dirname6(backend.enginePath),
10252
+ env: engineClientEnv(),
10253
+ timeoutMs: budgetSecs * 1e3,
10254
+ backstopMs: 3e4
9934
10255
  }
9935
10256
  );
9936
10257
  const secs = Math.round((Date.now() - t0) / 1e3);
10258
+ if (r.timedOut) {
10259
+ cleanupContainer(backend, name);
10260
+ return {
10261
+ exit: null,
10262
+ secs,
10263
+ failure: "budget",
10264
+ reason: "verifier stage exceeded its budget",
10265
+ diagnostics: r.diagnostics
10266
+ };
10267
+ }
9937
10268
  if (r.error) {
9938
10269
  cleanupContainer(backend, name);
9939
- const code2 = r.error.code;
9940
- return code2 === "ETIMEDOUT" ? { exit: null, secs, failure: "budget", reason: "verifier stage exceeded its budget" } : { exit: null, secs, failure: "backend", reason: `Docker client failed: ${r.error.message}` };
10270
+ return {
10271
+ exit: null,
10272
+ secs,
10273
+ failure: "backend",
10274
+ reason: `Docker client failed: ${r.error}`,
10275
+ diagnostics: r.diagnostics
10276
+ };
9941
10277
  }
9942
- if (r.status === null) {
10278
+ if (r.exit === null) {
9943
10279
  cleanupContainer(backend, name);
9944
10280
  return {
9945
10281
  exit: null,
9946
10282
  secs,
9947
10283
  failure: "backend",
9948
- reason: `Docker client did not return a status${r.signal ? ` (signal ${r.signal})` : ""}`
10284
+ reason: `Docker client did not return a status${r.signal ? ` (signal ${r.signal})` : ""}`,
10285
+ diagnostics: r.diagnostics
9949
10286
  };
9950
10287
  }
9951
10288
  if (!engineStable(backend)) {
@@ -9953,10 +10290,11 @@ function runContainerStage(backend, dir, command, budgetSecs) {
9953
10290
  exit: null,
9954
10291
  secs,
9955
10292
  failure: "backend",
9956
- reason: "Docker engine identity changed before verifier result inspection"
10293
+ reason: "Docker engine identity changed before verifier result inspection",
10294
+ diagnostics: r.diagnostics
9957
10295
  };
9958
10296
  }
9959
- const inspected = spawnSync(
10297
+ const inspected = spawnSync2(
9960
10298
  backend.enginePath,
9961
10299
  dockerArgs(backend.daemonHost, ["inspect", "--format", "{{json .State}}", name]),
9962
10300
  {
@@ -9976,7 +10314,10 @@ function runContainerStage(backend, dir, command, budgetSecs) {
9976
10314
  }
9977
10315
  }
9978
10316
  cleanupContainer(backend, name);
9979
- return containerStateResult(state, secs);
10317
+ return {
10318
+ ...containerStateResult(state, secs),
10319
+ diagnostics: r.diagnostics
10320
+ };
9980
10321
  } catch (e) {
9981
10322
  cleanupContainer(backend, name);
9982
10323
  return {
@@ -10081,7 +10422,7 @@ function resolveBase(base, cwd) {
10081
10422
  }
10082
10423
  }
10083
10424
  function baseIsAncestorOfHead(base, cwd) {
10084
- const r = spawnSync2("git", ["merge-base", "--is-ancestor", base, "HEAD"], { cwd, env: trustedGitEnv() });
10425
+ const r = spawnSync3("git", ["merge-base", "--is-ancestor", base, "HEAD"], { cwd, env: trustedGitEnv() });
10085
10426
  return r.status === 0;
10086
10427
  }
10087
10428
  function inside2(root, path) {
@@ -10105,7 +10446,7 @@ function safeSymlink(target, out2, root, label) {
10105
10446
  if (rootedLinkTarget(target) || !inside2(root, resolve7(dirname7(out2), target))) {
10106
10447
  throw linkEscape(label, target);
10107
10448
  }
10108
- rmSync3(out2, { force: true });
10449
+ rmSync4(out2, { force: true });
10109
10450
  symlinkSync(target, out2);
10110
10451
  }
10111
10452
  function validateSymlinkGraph(target, out2, root, dependencyRoot, label) {
@@ -10128,7 +10469,7 @@ function validateSymlinkGraph(target, out2, root, dependencyRoot, label) {
10128
10469
  continue;
10129
10470
  }
10130
10471
  const base = domain === "tree" ? root : dependencyRoot;
10131
- const current = join16(base, ...stack);
10472
+ const current = join17(base, ...stack);
10132
10473
  let st;
10133
10474
  try {
10134
10475
  st = lstatSync5(current);
@@ -10164,7 +10505,7 @@ function rejectLinkedParent(cwd, rel) {
10164
10505
  const parts = rel.split("/").slice(0, -1);
10165
10506
  let current = cwd;
10166
10507
  for (const part of parts) {
10167
- current = join16(current, part);
10508
+ current = join17(current, part);
10168
10509
  let st;
10169
10510
  try {
10170
10511
  st = lstatSync5(current);
@@ -10180,7 +10521,7 @@ function materialize(cwd, dest, dependencyRoot) {
10180
10521
  const listed = git2(["ls-files", "-z", "--cached", "--others", "--exclude-standard"], cwd).split("\0").filter(Boolean);
10181
10522
  const links = [];
10182
10523
  for (const rel of listed) {
10183
- const src = join16(cwd, rel);
10524
+ const src = join17(cwd, rel);
10184
10525
  rejectLinkedParent(cwd, rel);
10185
10526
  let st;
10186
10527
  try {
@@ -10188,7 +10529,7 @@ function materialize(cwd, dest, dependencyRoot) {
10188
10529
  } catch {
10189
10530
  continue;
10190
10531
  }
10191
- const out2 = join16(dest, rel);
10532
+ const out2 = join17(dest, rel);
10192
10533
  mkdirSync6(dirname7(out2), { recursive: true });
10193
10534
  dropSymlink(out2);
10194
10535
  if (st.isSymbolicLink()) {
@@ -10205,11 +10546,11 @@ function materialize(cwd, dest, dependencyRoot) {
10205
10546
  for (const link of links) {
10206
10547
  validateSymlinkGraph(link.target, link.out, dest, dependencyRoot, link.rel);
10207
10548
  }
10208
- if (dependencyRoot) symlinkSync(dependencyRoot, join16(dest, "node_modules"), "dir");
10549
+ if (dependencyRoot) symlinkSync(dependencyRoot, join17(dest, "node_modules"), "dir");
10209
10550
  }
10210
10551
  function dropSymlink(p) {
10211
10552
  try {
10212
- if (lstatSync5(p).isSymbolicLink()) rmSync3(p, { force: true });
10553
+ if (lstatSync5(p).isSymbolicLink()) rmSync4(p, { force: true });
10213
10554
  } catch {
10214
10555
  }
10215
10556
  }
@@ -10238,11 +10579,11 @@ function overlayDigest(dest, paths) {
10238
10579
  for (const rel of [...paths].sort()) {
10239
10580
  h.update(rel);
10240
10581
  h.update("\0");
10241
- const p = join16(dest, rel);
10582
+ const p = join17(dest, rel);
10242
10583
  try {
10243
10584
  const st = lstatSync5(p);
10244
10585
  h.update(String(st.mode));
10245
- h.update(st.isSymbolicLink() ? readlinkSync3(p) : readFileSync15(p));
10586
+ h.update(st.isSymbolicLink() ? readlinkSync3(p) : readFileSync16(p));
10246
10587
  } catch {
10247
10588
  h.update("<gone>");
10248
10589
  }
@@ -10266,15 +10607,15 @@ function overlayPristine(cwd, base, dest, policy, cmd, dependencyRoot) {
10266
10607
  throw new Error(`the base's ${e.path} is a submodule; the pristine copy cannot reproduce it`);
10267
10608
  }
10268
10609
  const content = execFileSync9("git", ["show", `${base}:${e.path}`], { cwd, maxBuffer: 1 << 28, env: trustedGitEnv() });
10269
- const out2 = join16(dest, e.path);
10610
+ const out2 = join17(dest, e.path);
10270
10611
  mkdirSync6(dirname7(out2), { recursive: true });
10271
- rmSync3(out2, { force: true });
10612
+ rmSync4(out2, { force: true });
10272
10613
  if (e.mode === "120000") {
10273
10614
  const target = content.toString("utf8");
10274
10615
  safeSymlink(target, out2, dest, `the base's ${e.path}`);
10275
10616
  restoredLinks.push({ path: e.path, out: out2, target });
10276
10617
  } else {
10277
- writeFileSync7(out2, content);
10618
+ writeFileSync8(out2, content);
10278
10619
  chmodSync2(out2, parseInt(e.mode.slice(-4), 8) & 511);
10279
10620
  }
10280
10621
  restored.push(e.path);
@@ -10283,7 +10624,7 @@ function overlayPristine(cwd, base, dest, policy, cmd, dependencyRoot) {
10283
10624
  const inCopy = git2(["ls-files", "-z", "--cached", "--others", "--exclude-standard"], cwd).split("\0").filter(Boolean);
10284
10625
  for (const rel of inCopy) {
10285
10626
  if (!isOverlay(rel) || baseProtected.has(rel)) continue;
10286
- rmSync3(join16(dest, rel), { force: true });
10627
+ rmSync4(join17(dest, rel), { force: true });
10287
10628
  removed++;
10288
10629
  }
10289
10630
  for (const link of restoredLinks) {
@@ -10291,27 +10632,6 @@ function overlayPristine(cwd, base, dest, policy, cmd, dependencyRoot) {
10291
10632
  }
10292
10633
  return { restored, removed };
10293
10634
  }
10294
- var SUPERVISOR = `
10295
- const cp = require('node:child_process');
10296
- const fs = require('node:fs');
10297
- const [cmd, budgetMs, outFile] = process.argv.slice(1);
10298
- const child = cp.spawn('sh', ['-c', cmd], { stdio: 'ignore', detached: process.platform !== 'win32' });
10299
- const killGroup = () => {
10300
- try { process.kill(-child.pid, 'SIGKILL'); } catch {}
10301
- try { child.kill('SIGKILL'); } catch {}
10302
- };
10303
- let done = false;
10304
- const finish = (r) => {
10305
- if (done) return;
10306
- done = true;
10307
- killGroup();
10308
- fs.writeFileSync(outFile, JSON.stringify(r));
10309
- process.exit(0);
10310
- };
10311
- const timer = setTimeout(() => finish({ timedOut: true }), Number(budgetMs));
10312
- child.on('error', (e) => { clearTimeout(timer); finish({ error: String(e) }); });
10313
- child.on('exit', (code, signal) => { clearTimeout(timer); finish({ exit: code, signal }); });
10314
- `;
10315
10635
  var DROPPED_ENV = /* @__PURE__ */ new Set([
10316
10636
  "NODE_OPTIONS",
10317
10637
  "NODE_PATH",
@@ -10328,9 +10648,9 @@ function suiteEnv(scratch) {
10328
10648
  if (DROPPED_ENV.has(k) || PINNED_NPM.test(k)) continue;
10329
10649
  env[k] = v;
10330
10650
  }
10331
- const userRc = join16(scratch, "npmrc-user");
10332
- const globalRc = join16(scratch, "npmrc-global");
10333
- for (const f of [userRc, globalRc]) writeFileSync7(f, "", { mode: 292 });
10651
+ const userRc = join17(scratch, "npmrc-user");
10652
+ const globalRc = join17(scratch, "npmrc-global");
10653
+ for (const f of [userRc, globalRc]) writeFileSync8(f, "", { mode: 292 });
10334
10654
  env.npm_config_userconfig = userRc;
10335
10655
  env.npm_config_globalconfig = globalRc;
10336
10656
  env.npm_config_node_options = " ";
@@ -10339,31 +10659,75 @@ function suiteEnv(scratch) {
10339
10659
  }
10340
10660
  function runLocalSuite(dir, cmd, budgetSecs) {
10341
10661
  const t0 = Date.now();
10342
- const outDir = mkdtempSync2(join16(tmpdir2(), "tw-verify-run-"));
10343
- const outFile = join16(outDir, "result.json");
10662
+ const scratch = mkdtempSync3(join17(tmpdir3(), "tw-verify-run-"));
10344
10663
  try {
10345
- spawnSync2(process.execPath, ["-e", SUPERVISOR, cmd, String(budgetSecs * 1e3), outFile], {
10664
+ const r = runCapturedProcessSync("sh", ["-c", cmd], {
10346
10665
  cwd: dir,
10347
- env: suiteEnv(outDir),
10348
- stdio: "ignore",
10349
- timeout: budgetSecs * 1e3 + 3e4,
10350
- // backstop only; the supervisor enforces the budget
10351
- killSignal: "SIGKILL"
10666
+ env: suiteEnv(scratch),
10667
+ timeoutMs: budgetSecs * 1e3,
10668
+ detached: process.platform !== "win32",
10669
+ killGroupOnFinish: true,
10670
+ backstopMs: 3e4
10352
10671
  });
10353
10672
  const secs = Math.round((Date.now() - t0) / 1e3);
10354
- let r;
10355
- try {
10356
- r = JSON.parse(readFileSync15(outFile, "utf8"));
10357
- } catch {
10358
- return { exit: null, secs };
10673
+ if (r.timedOut) {
10674
+ return {
10675
+ exit: null,
10676
+ secs,
10677
+ failure: "budget",
10678
+ reason: "verifier stage exceeded its budget",
10679
+ diagnostics: r.diagnostics
10680
+ };
10359
10681
  }
10360
- if (r.timedOut || r.error) return { exit: null, secs };
10361
- if (r.exit === null || r.exit === void 0) return { exit: null, secs };
10362
- return { exit: r.exit, secs };
10682
+ if (r.error) {
10683
+ return {
10684
+ exit: null,
10685
+ secs,
10686
+ failure: "backend",
10687
+ reason: r.error,
10688
+ diagnostics: r.diagnostics
10689
+ };
10690
+ }
10691
+ if (r.exit === null) {
10692
+ return {
10693
+ exit: null,
10694
+ secs,
10695
+ failure: "backend",
10696
+ reason: `verifier suite did not return an exit code${r.signal ? ` (signal ${r.signal})` : ""}`,
10697
+ diagnostics: r.diagnostics
10698
+ };
10699
+ }
10700
+ return { exit: r.exit, secs, diagnostics: r.diagnostics };
10363
10701
  } finally {
10364
- rmSync3(outDir, { recursive: true, force: true });
10702
+ rmSync4(scratch, { recursive: true, force: true });
10365
10703
  }
10366
10704
  }
10705
+ function diagnosticsJson(diagnostics, includeTail) {
10706
+ if (!diagnostics) return void 0;
10707
+ const stream = (d) => ({
10708
+ captured_bytes: d.captured_bytes,
10709
+ retained_bytes: d.retained_bytes,
10710
+ truncated: d.truncated,
10711
+ ...includeTail && d.captured_bytes > 0 ? { tail: d.tail } : {}
10712
+ });
10713
+ return {
10714
+ stdout: stream(diagnostics.stdout),
10715
+ stderr: stream(diagnostics.stderr)
10716
+ };
10717
+ }
10718
+ function stageJson(result) {
10719
+ const includeTail = result.exit !== 0 || result.failure !== void 0;
10720
+ return {
10721
+ exit: result.exit,
10722
+ secs: result.secs,
10723
+ ...result.reason ? { reason: result.reason } : {},
10724
+ ...result.diagnostics ? { diagnostics: diagnosticsJson(result.diagnostics, includeTail) } : {}
10725
+ };
10726
+ }
10727
+ function renderStageDiagnostics(out2, stage, result) {
10728
+ if (!result.diagnostics) return;
10729
+ for (const line of diagnosticLines(stage, result.diagnostics)) out2(line);
10730
+ }
10367
10731
  function runVerify(opts) {
10368
10732
  const cwd = opts.cwd ?? process.cwd();
10369
10733
  const out2 = (s) => void process.stdout.write(s + "\n");
@@ -10445,11 +10809,11 @@ function runVerify(opts) {
10445
10809
  out2(`verify: cannot resolve base rev "${opts.base ?? "HEAD"}" \u2014 failing closed`);
10446
10810
  return 2;
10447
10811
  }
10448
- const visRoot = mkdtempSync2(join16(tmpdir2(), "tw-verify-vis-"));
10449
- const visDir = join16(visRoot, "t");
10812
+ const visRoot = mkdtempSync3(join17(tmpdir3(), "tw-verify-vis-"));
10813
+ const visDir = join17(visRoot, "t");
10450
10814
  const cleanup = (dirs) => {
10451
10815
  if (opts.keep) return;
10452
- for (const d of dirs) rmSync3(d, { recursive: true, force: true });
10816
+ for (const d of dirs) rmSync4(d, { recursive: true, force: true });
10453
10817
  };
10454
10818
  const protectedIgnored = (rel) => isProtected(rel, policy);
10455
10819
  const treeBefore = treeFingerprint(cwd, protectedIgnored);
@@ -10479,14 +10843,16 @@ function runVerify(opts) {
10479
10843
  stage: "visible",
10480
10844
  ...visible.resource ? { resource: visible.resource } : {},
10481
10845
  detail: visible.reason,
10846
+ diagnostics: diagnosticsJson(visible.diagnostics, true),
10482
10847
  verifier_backend: backendReport(),
10483
10848
  oracle_assurance: oracleAssuranceReport()
10484
10849
  }));
10485
10850
  } else {
10486
10851
  out2(
10487
- exhausted ? "verify: isolated verifier resource envelope was exhausted during the visible stage \u2014 failing closed" : "verify: isolated verifier backend failed while running the visible stage \u2014 failing closed"
10852
+ exhausted ? "verify: isolated verifier resource envelope was exhausted during the visible stage \u2014 failing closed" : "verify: verifier execution backend failed while running the visible stage \u2014 failing closed"
10488
10853
  );
10489
10854
  if (visible.reason) out2("verify: " + visible.reason);
10855
+ renderStageDiagnostics(out2, "visible", visible);
10490
10856
  }
10491
10857
  return 2;
10492
10858
  }
@@ -10503,8 +10869,8 @@ function runVerify(opts) {
10503
10869
  out2((dependencyAfterVisible.reason ?? "dependency identity changed") + ". Failing closed, not open.");
10504
10870
  return 2;
10505
10871
  }
10506
- const priRoot = mkdtempSync2(join16(tmpdir2(), "tw-verify-pri-"));
10507
- const priDir = join16(priRoot, "t");
10872
+ const priRoot = mkdtempSync3(join17(tmpdir3(), "tw-verify-pri-"));
10873
+ const priDir = join17(priRoot, "t");
10508
10874
  let restored = [];
10509
10875
  let removedAdded = 0;
10510
10876
  try {
@@ -10535,14 +10901,16 @@ function runVerify(opts) {
10535
10901
  stage: "pristine",
10536
10902
  ...pristine.resource ? { resource: pristine.resource } : {},
10537
10903
  detail: pristine.reason,
10904
+ diagnostics: diagnosticsJson(pristine.diagnostics, true),
10538
10905
  verifier_backend: backendReport(),
10539
10906
  oracle_assurance: oracleAssuranceReport()
10540
10907
  }));
10541
10908
  } else {
10542
10909
  out2(
10543
- exhausted ? "verify: isolated verifier resource envelope was exhausted during the pristine stage \u2014 failing closed" : "verify: isolated verifier backend failed while running the pristine stage \u2014 failing closed"
10910
+ exhausted ? "verify: isolated verifier resource envelope was exhausted during the pristine stage \u2014 failing closed" : "verify: verifier execution backend failed while running the pristine stage \u2014 failing closed"
10544
10911
  );
10545
10912
  if (pristine.reason) out2("verify: " + pristine.reason);
10913
+ renderStageDiagnostics(out2, "pristine", pristine);
10546
10914
  }
10547
10915
  return 2;
10548
10916
  }
@@ -10584,8 +10952,8 @@ function runVerify(opts) {
10584
10952
  base,
10585
10953
  command: cmd,
10586
10954
  budget_secs: budget,
10587
- visible: { exit: visible.exit, secs: visible.secs },
10588
- pristine: { exit: pristine.exit, secs: pristine.secs },
10955
+ visible: stageJson(visible),
10956
+ pristine: stageJson(pristine),
10589
10957
  protected_restored: restored.length,
10590
10958
  added_protected_removed: removedAdded,
10591
10959
  verifier_backend: backendReport(),
@@ -10610,6 +10978,14 @@ function runVerify(opts) {
10610
10978
  out2(
10611
10979
  dependencyEnvironment ? `dependency environment: ${dependencyEnvironmentSummary(dependencyEnvironment)}` + (opts.allowDepDrift && dependencyEnvironment.status === "unattestable" ? " (operator override)" : "") : `dependency environment: verifier-owned by ${verifierBackend.image ?? "isolated image"}`
10612
10980
  );
10981
+ if (verdict2 !== "VERIFIED") {
10982
+ if (visible.exit !== 0 || visible.failure !== void 0) {
10983
+ renderStageDiagnostics(out2, "visible", visible);
10984
+ }
10985
+ if (pristine.exit !== 0 || pristine.failure !== void 0) {
10986
+ renderStageDiagnostics(out2, "pristine", pristine);
10987
+ }
10988
+ }
10613
10989
  if (signedOff)
10614
10990
  out2(
10615
10991
  `masked failure cleared by out-of-band approval (tamperward:allow:${signedOff}): a reviewer accepted that the original suite no longer applies to this change. Exit 0.`
@@ -10654,11 +11030,11 @@ function parseVerify(args) {
10654
11030
  }
10655
11031
 
10656
11032
  // src/cli/run.ts
10657
- import { execFileSync as execFileSync10, spawn, spawnSync as spawnSync3 } from "node:child_process";
11033
+ import { execFileSync as execFileSync10, spawn, spawnSync as spawnSync4 } from "node:child_process";
10658
11034
  import { randomBytes } from "node:crypto";
10659
- import { mkdtempSync as mkdtempSync3, readdirSync as readdirSync5, readFileSync as readFileSync16, readlinkSync as readlinkSync4, realpathSync as realpathSync6, rmSync as rmSync4 } from "node:fs";
10660
- import { tmpdir as tmpdir3 } from "node:os";
10661
- import { join as join17, resolve as resolve8 } from "node:path";
11035
+ import { mkdtempSync as mkdtempSync4, readdirSync as readdirSync5, readFileSync as readFileSync17, readlinkSync as readlinkSync4, realpathSync as realpathSync6, rmSync as rmSync5 } from "node:fs";
11036
+ import { tmpdir as tmpdir4 } from "node:os";
11037
+ import { join as join18, resolve as resolve8 } from "node:path";
10662
11038
  var out = (s) => process.stdout.write(s + "\n");
10663
11039
  var err2 = (s) => process.stderr.write(s + "\n");
10664
11040
  var AGENT_SUPERVISOR = String.raw`
@@ -10778,7 +11154,7 @@ function linuxDescendantPids(rootPid) {
10778
11154
  for (const name of names) {
10779
11155
  const pid = Number(name);
10780
11156
  try {
10781
- const stat = readFileSync16(`/proc/${name}/stat`, "utf8");
11157
+ const stat = readFileSync17(`/proc/${name}/stat`, "utf8");
10782
11158
  const fields = stat.slice(stat.lastIndexOf(")") + 2).split(" ");
10783
11159
  const ppid = Number(fields[1]);
10784
11160
  if (!Number.isFinite(ppid)) continue;
@@ -10806,7 +11182,7 @@ function killAgentTree(pid) {
10806
11182
  if (!Number.isInteger(pid) || pid <= 1) return;
10807
11183
  if (process.platform === "win32") {
10808
11184
  try {
10809
- spawnSync3("taskkill", ["/pid", String(pid), "/t", "/f"], { stdio: "ignore", timeout: 5e3 });
11185
+ spawnSync4("taskkill", ["/pid", String(pid), "/t", "/f"], { stdio: "ignore", timeout: 5e3 });
10810
11186
  } catch {
10811
11187
  }
10812
11188
  return;
@@ -10828,11 +11204,11 @@ function killAgentTree(pid) {
10828
11204
  }
10829
11205
  }
10830
11206
  function runAgentWithBudget(argv2, cwd, budgetSecs) {
10831
- const stateDir = mkdtempSync3(join17(tmpdir3(), "tw-agent-supervisor-"));
10832
- const resultFile = join17(stateDir, "result.json");
11207
+ const stateDir = mkdtempSync4(join18(tmpdir4(), "tw-agent-supervisor-"));
11208
+ const resultFile = join18(stateDir, "result.json");
10833
11209
  let supervisorTimedOut = false;
10834
11210
  try {
10835
- const supervisor = spawnSync3(
11211
+ const supervisor = spawnSync4(
10836
11212
  process.execPath,
10837
11213
  ["-e", AGENT_SUPERVISOR, resultFile, String(budgetSecs * 1e3), ...argv2],
10838
11214
  {
@@ -10847,7 +11223,7 @@ function runAgentWithBudget(argv2, cwd, budgetSecs) {
10847
11223
  supervisorTimedOut = Boolean(supervisor.error) && supervisor.error.code === "ETIMEDOUT";
10848
11224
  let state = {};
10849
11225
  try {
10850
- state = JSON.parse(readFileSync16(resultFile, "utf8"));
11226
+ state = JSON.parse(readFileSync17(resultFile, "utf8"));
10851
11227
  } catch {
10852
11228
  }
10853
11229
  if (supervisorTimedOut || state.timedOut) {
@@ -10874,7 +11250,7 @@ function runAgentWithBudget(argv2, cwd, budgetSecs) {
10874
11250
  failure: state.failure ?? "agent supervisor did not produce a final status"
10875
11251
  };
10876
11252
  } finally {
10877
- rmSync4(stateDir, { recursive: true, force: true });
11253
+ rmSync5(stateDir, { recursive: true, force: true });
10878
11254
  }
10879
11255
  }
10880
11256
  var waitCell = new Int32Array(new SharedArrayBuffer(4));
@@ -10891,8 +11267,8 @@ function pidAlive2(pid) {
10891
11267
  }
10892
11268
  }
10893
11269
  function supervisedObserverLog(cwd) {
10894
- const gd = gitDir(cwd) ?? join17(cwd, ".git");
10895
- return join17(
11270
+ const gd = gitDir(cwd) ?? join18(cwd, ".git");
11271
+ return join18(
10896
11272
  gd,
10897
11273
  "tamperward",
10898
11274
  `run-observer-${process.pid}-${Date.now()}-${randomBytes(4).toString("hex")}.jsonl`
@@ -11009,7 +11385,7 @@ function git3(args, cwd) {
11009
11385
  }
11010
11386
  function nowTicks() {
11011
11387
  try {
11012
- return Math.floor(parseFloat(readFileSync16("/proc/uptime", "utf8").split(" ")[0]) * 100);
11388
+ return Math.floor(parseFloat(readFileSync17("/proc/uptime", "utf8").split(" ")[0]) * 100);
11013
11389
  } catch {
11014
11390
  return Number.POSITIVE_INFINITY;
11015
11391
  }
@@ -11032,7 +11408,7 @@ function survivorsHoldingTree(cwd, spawnedAfterTicks) {
11032
11408
  const n = Number(pid);
11033
11409
  if (n === process.pid) continue;
11034
11410
  try {
11035
- const stat = readFileSync16(`/proc/${pid}/stat`, "utf8");
11411
+ const stat = readFileSync17(`/proc/${pid}/stat`, "utf8");
11036
11412
  const startTicks = Number(stat.slice(stat.lastIndexOf(")") + 2).split(" ")[19]);
11037
11413
  if (!Number.isFinite(startTicks) || startTicks < spawnedAfterTicks) continue;
11038
11414
  } catch {
@@ -11137,7 +11513,7 @@ function runEnvelope(opts) {
11137
11513
  const rewritesBefore = objectRewriteState(cwd);
11138
11514
  const spawnTicks = nowTicks();
11139
11515
  const agentRun = opts.agentBudget === void 0 ? (() => {
11140
- const agent = spawnSync3(opts.argv[0], opts.argv.slice(1), { cwd, stdio: "inherit" });
11516
+ const agent = spawnSync4(opts.argv[0], opts.argv.slice(1), { cwd, stdio: "inherit" });
11141
11517
  return {
11142
11518
  exit: agent.status ?? 1,
11143
11519
  timedOut: false,
@@ -11177,7 +11553,7 @@ tamperward run \u2014 agent exit ${agentExit}; DEPENDENCY_DRIFT \u2192 ENFORCEME
11177
11553
  err2("(--allow-dep-drift: proceeding anyway, on the operator's judgement.)");
11178
11554
  }
11179
11555
  const head = git3(["rev-parse", "HEAD"], cwd).trim();
11180
- const isAncestor = spawnSync3("git", ["merge-base", "--is-ancestor", base, head], { cwd, env: trustedGitEnv() });
11556
+ const isAncestor = spawnSync4("git", ["merge-base", "--is-ancestor", base, head], { cwd, env: trustedGitEnv() });
11181
11557
  if (isAncestor.status !== 0) {
11182
11558
  err2(`tamperward run: HEAD ${head.slice(0, 10)} is not a descendant of the trusted base ${base.slice(0, 10)} \u2014`);
11183
11559
  err2("the agent rewrote history out from under the anchor. That is the finding.");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tamperward",
3
- "version": "2.15.2",
3
+ "version": "2.16.0",
4
4
  "description": "The deterministic agent-integrity gate. One ruleset, evaluated on the actual diff/commands as a verdict, enforced everywhere a change can be made.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "hexrift",