tamperward 2.29.14 → 2.29.15

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 (2) hide show
  1. package/dist/cli/index.js +48 -2
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -14640,6 +14640,7 @@ function parseCapturedSupervisorResult(stdoutText) {
14640
14640
  signal: typeof raw.signal === "string" ? raw.signal : null,
14641
14641
  timedOut: Boolean(raw.timedOut),
14642
14642
  ...typeof raw.error === "string" && raw.error ? { error: raw.error } : {},
14643
+ ...raw.pipeHeldOpen === true ? { pipeHeldOpen: true } : {},
14643
14644
  diagnostics
14644
14645
  };
14645
14646
  }
@@ -14657,6 +14658,7 @@ function runCapturedProcessSync(executable, args, opts) {
14657
14658
  timeoutMs: opts.timeoutMs,
14658
14659
  detached: Boolean(opts.detached),
14659
14660
  killGroupOnFinish: Boolean(opts.killGroupOnFinish),
14661
+ drainMs: opts.drainMs ?? 1e3,
14660
14662
  captureBytes: DIAGNOSTIC_TAIL_BYTES
14661
14663
  }),
14662
14664
  { mode: 384 }
@@ -14789,6 +14791,7 @@ let done = false;
14789
14791
  let timedOut = false;
14790
14792
  let exitInfo = { exit: null, signal: null };
14791
14793
  let descendantTracker = null;
14794
+ let drainTimer = null;
14792
14795
 
14793
14796
  function killOwned() {
14794
14797
  if (!child || !child.pid) return;
@@ -14810,6 +14813,7 @@ function finish(extra) {
14810
14813
  if (done) return;
14811
14814
  done = true;
14812
14815
  if (descendantTracker) clearInterval(descendantTracker);
14816
+ if (drainTimer) clearTimeout(drainTimer);
14813
14817
  try {
14814
14818
  // stdout is reserved for the trusted supervisor result. Candidate suite
14815
14819
  // stdout/stderr are separate pipes and are never inherited here.
@@ -14864,12 +14868,19 @@ if (child) {
14864
14868
  signal: signal == null ? null : String(signal),
14865
14869
  };
14866
14870
  if (cfg.killGroupOnFinish) killOwned();
14871
+ // A leaked descendant can hold a pipe open so 'close' never fires; finish off
14872
+ // exit after a bounded drain instead of hanging. 'close' still wins if first.
14873
+ if (!drainTimer) {
14874
+ drainTimer = setTimeout(() => finish({ pipeHeldOpen: true }), Number(cfg.drainMs));
14875
+ drainTimer.unref();
14876
+ }
14867
14877
  });
14868
14878
 
14869
14879
  // close fires after the stdio pipes close, so the retained tails include all
14870
14880
  // output that was drained before owned descendants were terminated.
14871
14881
  child.on('close', (code, signal) => {
14872
14882
  clearTimeout(timer);
14883
+ if (drainTimer) clearTimeout(drainTimer);
14873
14884
  if (exitInfo.exit === null && code != null) exitInfo.exit = Number(code);
14874
14885
  if (exitInfo.signal === null && signal != null) exitInfo.signal = String(signal);
14875
14886
  finish({});
@@ -15714,7 +15725,12 @@ function runLocalSuite(dir, cmd, budgetSecs) {
15714
15725
  diagnostics: r.diagnostics
15715
15726
  };
15716
15727
  }
15717
- return { exit: r.exit, secs, diagnostics: r.diagnostics };
15728
+ return {
15729
+ exit: r.exit,
15730
+ secs,
15731
+ ...r.pipeHeldOpen ? { pipeHeldOpen: true } : {},
15732
+ diagnostics: r.diagnostics
15733
+ };
15718
15734
  } finally {
15719
15735
  rmSync7(scratch, { recursive: true, force: true });
15720
15736
  }
@@ -15873,7 +15889,29 @@ function runVerify(opts) {
15873
15889
  }
15874
15890
  const frozenNodeModules = dependencyEnvironment ? dependencyEnvironment.roots.find((root) => root.kind === "node_modules")?.realPath ?? null : null;
15875
15891
  const checkDeps = () => dependencyEnvironment ? checkDependencyEnvironment(cwd, dependencyEnvironment) : { ok: true };
15876
- const runStage = (dir) => isolated ? runContainerStage(verifierBackend, dir, cmd, budget) : runLocalSuite(dir, cmd, budget);
15892
+ const runStage = opts.runStage ?? ((dir) => isolated ? runContainerStage(verifierBackend, dir, cmd, budget) : runLocalSuite(dir, cmd, budget));
15893
+ const notQuiescent = (stage, r) => {
15894
+ const detail = `the ${stage} suite exited (code ${r.exit ?? "unknown"}) but left a process holding stdout open after termination was attempted; the stage is not quiescent, so the run cannot be certified`;
15895
+ opts.onVerdict?.({ verdict: "CANNOT_VERIFY", reason: "VERIFIER_BACKEND_RUNTIME_FAILURE", detail });
15896
+ if (opts.json) {
15897
+ out3(JSON.stringify({
15898
+ schema_version: MACHINE_SCHEMA_VERSION,
15899
+ verdict: "CANNOT_VERIFY",
15900
+ reason: "VERIFIER_BACKEND_RUNTIME_FAILURE",
15901
+ stage,
15902
+ ...caseReport(),
15903
+ detail,
15904
+ diagnostics: diagnosticsJson(r.diagnostics, true),
15905
+ verifier_backend: backendReport(),
15906
+ oracle_assurance: oracleAssuranceReport()
15907
+ }));
15908
+ } else {
15909
+ out3(`verify: the ${stage} suite exited but left a process holding stdout open \u2014 the stage is not quiescent, failing closed`);
15910
+ out3("verify: " + detail);
15911
+ renderStageDiagnostics(out3, stage, r);
15912
+ }
15913
+ return 2;
15914
+ };
15877
15915
  let base;
15878
15916
  try {
15879
15917
  base = resolveBase(opts.base ?? "HEAD", cwd);
@@ -15933,6 +15971,10 @@ function runVerify(opts) {
15933
15971
  );
15934
15972
  }
15935
15973
  const visible = runStage(visDir);
15974
+ if (visible.pipeHeldOpen) {
15975
+ cleanup([visRoot]);
15976
+ return notQuiescent("visible", visible);
15977
+ }
15936
15978
  if (visible.failure === "backend" || visible.failure === "resource") {
15937
15979
  cleanup([visRoot]);
15938
15980
  const exhausted = visible.failure === "resource";
@@ -16002,6 +16044,10 @@ function runVerify(opts) {
16002
16044
  }
16003
16045
  const overlayBefore = overlayDigest(priDir, restored);
16004
16046
  const pristine = runStage(priDir);
16047
+ if (pristine.pipeHeldOpen) {
16048
+ cleanup([visRoot, priRoot]);
16049
+ return notQuiescent("pristine", pristine);
16050
+ }
16005
16051
  if (pristine.failure === "backend" || pristine.failure === "resource") {
16006
16052
  cleanup([visRoot, priRoot]);
16007
16053
  const exhausted = pristine.failure === "resource";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tamperward",
3
- "version": "2.29.14",
3
+ "version": "2.29.15",
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",