tamperward 2.34.0 → 2.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/dist/cli/index.js CHANGED
@@ -10872,8 +10872,8 @@ import { dirname as dirname2, join as join9 } from "node:path";
10872
10872
  function compactOobToken(want, head) {
10873
10873
  const normalizedHead = head.trim().toLowerCase();
10874
10874
  if (!want || !FULL_OBJECT_ID.test(normalizedHead)) return null;
10875
- const digest = createHash3("sha256").update(`tamperward:oob:v1\0${want}\0${normalizedHead}`).digest("base64url");
10876
- return `${COMPACT_OOB_PREFIX}${digest}`;
10875
+ const digest2 = createHash3("sha256").update(`tamperward:oob:v1\0${want}\0${normalizedHead}`).digest("base64url");
10876
+ return `${COMPACT_OOB_PREFIX}${digest2}`;
10877
10877
  }
10878
10878
  function ledgerEntryFrom(value) {
10879
10879
  if (!isRecord(value)) return null;
@@ -14883,8 +14883,8 @@ function parseCapturedSupervisorResult(stdoutText) {
14883
14883
  };
14884
14884
  }
14885
14885
  function runCapturedProcessSync(executable, args, opts) {
14886
- const stateDir = mkdtempSync2(join17(tmpdir3(), "tw-suite-capture-"));
14887
- const configFile = join17(stateDir, "config.json");
14886
+ const stateDir2 = mkdtempSync2(join17(tmpdir3(), "tw-suite-capture-"));
14887
+ const configFile = join17(stateDir2, "config.json");
14888
14888
  const backstop = opts.backstopMs ?? 3e4;
14889
14889
  try {
14890
14890
  writeFileSync8(
@@ -14932,7 +14932,7 @@ function runCapturedProcessSync(executable, args, opts) {
14932
14932
  }
14933
14933
  return parsed;
14934
14934
  } finally {
14935
- rmSync6(stateDir, { recursive: true, force: true });
14935
+ rmSync6(stateDir2, { recursive: true, force: true });
14936
14936
  }
14937
14937
  }
14938
14938
  var DIAGNOSTIC_TAIL_BYTES, EMPTY_STREAM, CLEANUP_HELPERS_SRC, CAPTURE_SUPERVISOR;
@@ -15651,12 +15651,375 @@ var init_verifier_backend = __esm({
15651
15651
  }
15652
15652
  });
15653
15653
 
15654
- // src/cli/verify.ts
15655
- import { execFileSync as execFileSync8, spawnSync as spawnSync3 } from "node:child_process";
15654
+ // src/verification-state.ts
15655
+ import { execFileSync as execFileSync8 } from "node:child_process";
15656
15656
  import { createHash as createHash12 } from "node:crypto";
15657
- import { chmodSync as chmodSync2, cpSync, lstatSync as lstatSync9, mkdirSync as mkdirSync8, mkdtempSync as mkdtempSync3, readFileSync as readFileSync16, readlinkSync as readlinkSync3, rmSync as rmSync7, symlinkSync, writeFileSync as writeFileSync9, existsSync as existsSync14 } from "node:fs";
15657
+ import { mkdirSync as mkdirSync8, readFileSync as readFileSync16, rmSync as rmSync7, writeFileSync as writeFileSync9 } from "node:fs";
15658
+ import { dirname as dirname8, join as join19 } from "node:path";
15659
+ function git2(args, cwd) {
15660
+ return execFileSync8("git", args, {
15661
+ cwd,
15662
+ encoding: "utf8",
15663
+ maxBuffer: 1 << 28,
15664
+ env: trustedGitEnv()
15665
+ });
15666
+ }
15667
+ function resolveBaseRev(baseRef, cwd) {
15668
+ assertRev(baseRef);
15669
+ const rev2 = git2(["rev-parse", "--verify", `${baseRef}^{commit}`], cwd).trim();
15670
+ try {
15671
+ return git2(["merge-base", rev2, "HEAD"], cwd).trim();
15672
+ } catch {
15673
+ return rev2;
15674
+ }
15675
+ }
15676
+ function canonicalJson(value) {
15677
+ if (value === null || typeof value !== "object") return JSON.stringify(value) ?? "null";
15678
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
15679
+ const entries = Object.entries(value).filter(([, v]) => v !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
15680
+ return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`).join(",")}}`;
15681
+ }
15682
+ function digest(value) {
15683
+ return createHash12("sha256").update(canonicalJson(value)).digest("hex");
15684
+ }
15685
+ function surfacePaths(base, cwd, policy) {
15686
+ let listing;
15687
+ try {
15688
+ listing = git2(["ls-tree", "-r", "--name-only", "-z", base], cwd);
15689
+ } catch {
15690
+ return [];
15691
+ }
15692
+ const inputs = policy.verify?.inputs ?? [];
15693
+ return listing.split("\0").filter(Boolean).filter((p) => isProtected(p, policy) || matchesAny(p, inputs)).sort();
15694
+ }
15695
+ function interventionWiring(cwd) {
15696
+ const dir = claudeConfigDir();
15697
+ const sources = [
15698
+ ["repo", join19(cwd, ".claude", "settings.json")],
15699
+ ["repo.local", join19(cwd, ".claude", "settings.local.json")],
15700
+ ["user", join19(dir, "settings.json")],
15701
+ ["user.local", join19(dir, "settings.local.json")]
15702
+ ];
15703
+ return sources.map(([source, path]) => {
15704
+ let parsed;
15705
+ try {
15706
+ parsed = JSON.parse(readFileSync16(path, "utf8"));
15707
+ } catch {
15708
+ return { source, present: false };
15709
+ }
15710
+ if (!isRecord4(parsed)) return { source, present: true, shape: "non-object" };
15711
+ return {
15712
+ source,
15713
+ present: true,
15714
+ hooks: parsed.hooks ?? null,
15715
+ disableAllHooks: parsed.disableAllHooks ?? null
15716
+ };
15717
+ });
15718
+ }
15719
+ function dependenciesBinding(root, policy, command) {
15720
+ const backend = policy.verify?.backend ?? "local";
15721
+ if (backend === "container") {
15722
+ return digest({ backend, image: policy.verify?.image ?? null });
15723
+ }
15724
+ const dep = discoverDependencyEnvironment(root, command);
15725
+ return digest({ backend, status: dep.status, fingerprint: dep.fingerprint ?? null });
15726
+ }
15727
+ function computeBinding(cwd, inputs, precomputed) {
15728
+ const root = repoRoot(cwd);
15729
+ const head = git2(["rev-parse", "--verify", "HEAD^{commit}"], root).trim();
15730
+ const base = resolveBaseRev(inputs.base_ref, root);
15731
+ const policy = inputs.explicit_base ? loadPolicyAt(base, root) ?? defaultPolicy() : loadPolicy(root);
15732
+ const command = inputs.command_source === "flag" ? inputs.command : policy.verify?.command ?? "";
15733
+ if (!command) {
15734
+ throw new Error("no verifier command is configured for the recorded verification");
15735
+ }
15736
+ const budget = inputs.budget_source === "flag" ? inputs.budget : policy.verify?.budget ?? 300;
15737
+ const protectedIgnored = (rel) => isProtected(rel, policy);
15738
+ return {
15739
+ // NOTE (#500/#600 follow-up): `tree` hashes every tracked and ignored-protected
15740
+ // file and `dependencies` walks the attested dependency roots, on EVERY call.
15741
+ // Right for `verify` (once per run) and reused there via `precomputed.tree`,
15742
+ // but `status` is meant to be polled continuously by a status bar/dashboard,
15743
+ // where a large repository makes each poll a full read. A stat-cache identity
15744
+ // (git's index plus hashing only what changed) is a deliberate follow-up, not
15745
+ // built here.
15746
+ tree: precomputed?.tree ?? treeFingerprint(root, protectedIgnored),
15747
+ head,
15748
+ base,
15749
+ policy: digest(policy),
15750
+ verifier: digest({
15751
+ command,
15752
+ budget,
15753
+ inputs: policy.verify?.inputs ?? [],
15754
+ backend: policy.verify?.backend ?? "local",
15755
+ image: policy.verify?.image ?? null
15756
+ }),
15757
+ surface: digest(surfacePaths(base, root, policy)),
15758
+ intervention: digest(interventionWiring(root)),
15759
+ dependencies: dependenciesBinding(root, policy, command)
15760
+ };
15761
+ }
15762
+ function stateDir(cwd) {
15763
+ const ctx = repoContext(cwd);
15764
+ return ctx ? join19(ctx.gitDir, "tamperward") : null;
15765
+ }
15766
+ function verificationRecordPath(cwd) {
15767
+ const dir = stateDir(cwd);
15768
+ return dir ? join19(dir, "verification-state.json") : null;
15769
+ }
15770
+ function verifyingMarkerPath(cwd) {
15771
+ const dir = stateDir(cwd);
15772
+ return dir ? join19(dir, "verifying.json") : null;
15773
+ }
15774
+ function recordVerification(cwd, inputs, treeFingerprintValue) {
15775
+ const path = verificationRecordPath(cwd);
15776
+ if (!path) return false;
15777
+ const binding = computeBinding(
15778
+ repoRoot(cwd),
15779
+ inputs,
15780
+ treeFingerprintValue ? { tree: treeFingerprintValue } : void 0
15781
+ );
15782
+ const record = {
15783
+ schema_version: VERIFICATION_STATE_SCHEMA_VERSION,
15784
+ verdict: "VERIFIED",
15785
+ verified_at: (/* @__PURE__ */ new Date()).toISOString(),
15786
+ tw_version: TW_VERSION,
15787
+ inputs,
15788
+ binding
15789
+ };
15790
+ mkdirSync8(dirname8(path), { recursive: true });
15791
+ writeFileSync9(path, JSON.stringify(record) + "\n");
15792
+ return true;
15793
+ }
15794
+ function invalidateVerificationRecordIfCurrent(cwd) {
15795
+ const record = readVerificationRecord(cwd);
15796
+ if (!record) return false;
15797
+ let live;
15798
+ try {
15799
+ live = computeBinding(cwd, record.inputs);
15800
+ } catch {
15801
+ return false;
15802
+ }
15803
+ if (firstMismatch(record.binding, live)) return false;
15804
+ const path = verificationRecordPath(cwd);
15805
+ if (!path) return false;
15806
+ try {
15807
+ rmSync7(path, { force: true });
15808
+ return true;
15809
+ } catch {
15810
+ return false;
15811
+ }
15812
+ }
15813
+ function isRecord4(v) {
15814
+ return typeof v === "object" && v !== null && !Array.isArray(v);
15815
+ }
15816
+ function isBinding(v) {
15817
+ if (!isRecord4(v)) return false;
15818
+ return BINDING_INPUTS.every((k) => {
15819
+ const val = v[k];
15820
+ return typeof val === "string" && val.length > 0;
15821
+ });
15822
+ }
15823
+ function parseVerificationRecord(value) {
15824
+ if (!isRecord4(value)) return null;
15825
+ if (value.schema_version !== VERIFICATION_STATE_SCHEMA_VERSION) return null;
15826
+ if (value.verdict !== "VERIFIED") return null;
15827
+ if (typeof value.verified_at !== "string") return null;
15828
+ if (typeof value.tw_version !== "string") return null;
15829
+ const inputs = value.inputs;
15830
+ if (!isRecord4(inputs)) return null;
15831
+ if (typeof inputs.base_ref !== "string" || inputs.base_ref.length === 0) return null;
15832
+ if (typeof inputs.explicit_base !== "boolean") return null;
15833
+ if (inputs.command_source !== "policy" && inputs.command_source !== "flag") return null;
15834
+ if (typeof inputs.command !== "string") return null;
15835
+ if (inputs.budget_source !== "policy" && inputs.budget_source !== "flag") return null;
15836
+ if (typeof inputs.budget !== "number" || !Number.isFinite(inputs.budget)) return null;
15837
+ if (!isBinding(value.binding)) return null;
15838
+ return {
15839
+ schema_version: VERIFICATION_STATE_SCHEMA_VERSION,
15840
+ verdict: "VERIFIED",
15841
+ verified_at: value.verified_at,
15842
+ tw_version: value.tw_version,
15843
+ inputs: {
15844
+ base_ref: inputs.base_ref,
15845
+ explicit_base: inputs.explicit_base,
15846
+ command_source: inputs.command_source,
15847
+ command: inputs.command,
15848
+ budget_source: inputs.budget_source,
15849
+ budget: inputs.budget
15850
+ },
15851
+ binding: value.binding
15852
+ };
15853
+ }
15854
+ function readVerificationRecord(cwd) {
15855
+ const path = verificationRecordPath(cwd);
15856
+ if (!path) return null;
15857
+ let raw;
15858
+ try {
15859
+ raw = readFileSync16(path, "utf8");
15860
+ } catch {
15861
+ return null;
15862
+ }
15863
+ let parsed;
15864
+ try {
15865
+ parsed = JSON.parse(raw);
15866
+ } catch {
15867
+ return null;
15868
+ }
15869
+ return parseVerificationRecord(parsed);
15870
+ }
15871
+ function pidAlive3(pid) {
15872
+ try {
15873
+ process.kill(pid, 0);
15874
+ return true;
15875
+ } catch (e) {
15876
+ return isRecord4(e) && e.code === "EPERM";
15877
+ }
15878
+ }
15879
+ function beginVerifying(cwd) {
15880
+ const path = verifyingMarkerPath(cwd);
15881
+ if (!path) return false;
15882
+ try {
15883
+ const marker = {
15884
+ schema_version: VERIFICATION_STATE_SCHEMA_VERSION,
15885
+ pid: process.pid,
15886
+ started_at: (/* @__PURE__ */ new Date()).toISOString()
15887
+ };
15888
+ mkdirSync8(dirname8(path), { recursive: true });
15889
+ writeFileSync9(path, JSON.stringify(marker) + "\n");
15890
+ return true;
15891
+ } catch {
15892
+ return false;
15893
+ }
15894
+ }
15895
+ function endVerifying(cwd) {
15896
+ const path = verifyingMarkerPath(cwd);
15897
+ if (!path) return;
15898
+ try {
15899
+ rmSync7(path, { force: true });
15900
+ } catch {
15901
+ }
15902
+ }
15903
+ function readVerifyingMarker(cwd) {
15904
+ const path = verifyingMarkerPath(cwd);
15905
+ if (!path) return null;
15906
+ let parsed;
15907
+ try {
15908
+ parsed = JSON.parse(readFileSync16(path, "utf8"));
15909
+ } catch {
15910
+ return null;
15911
+ }
15912
+ if (!isRecord4(parsed)) return null;
15913
+ if (parsed.schema_version !== VERIFICATION_STATE_SCHEMA_VERSION) return null;
15914
+ if (typeof parsed.pid !== "number" || !Number.isInteger(parsed.pid) || parsed.pid <= 0) return null;
15915
+ if (typeof parsed.started_at !== "string") return null;
15916
+ if (!pidAlive3(parsed.pid)) return null;
15917
+ return { schema_version: VERIFICATION_STATE_SCHEMA_VERSION, pid: parsed.pid, started_at: parsed.started_at };
15918
+ }
15919
+ function firstMismatch(recorded, live) {
15920
+ for (const input of MISMATCH_PRIORITY) {
15921
+ if (recorded[input] !== live[input]) return input;
15922
+ }
15923
+ return null;
15924
+ }
15925
+ function evaluateVerificationState(cwd) {
15926
+ if (readVerifyingMarker(cwd)) {
15927
+ return { state: "VERIFYING", reason: "a verification is in progress" };
15928
+ }
15929
+ const record = readVerificationRecord(cwd);
15930
+ if (!record) {
15931
+ return {
15932
+ state: "UNVERIFIED",
15933
+ reason: "no successful verification has been recorded for this repository"
15934
+ };
15935
+ }
15936
+ let live;
15937
+ try {
15938
+ live = computeBinding(cwd, record.inputs);
15939
+ } catch (e) {
15940
+ return {
15941
+ state: "BROKEN",
15942
+ reason: "verification authority wiring is invalid or unavailable",
15943
+ detail: e instanceof Error ? e.message : String(e),
15944
+ verified_at: record.verified_at,
15945
+ verifier_command: record.inputs.command,
15946
+ record
15947
+ };
15948
+ }
15949
+ const changed = firstMismatch(record.binding, live);
15950
+ if (!changed) {
15951
+ return {
15952
+ state: "CURRENT",
15953
+ verified_at: record.verified_at,
15954
+ base: live.base,
15955
+ head: live.head,
15956
+ verifier_command: record.inputs.command,
15957
+ record
15958
+ };
15959
+ }
15960
+ return {
15961
+ state: "STALE",
15962
+ changed_input: changed,
15963
+ reason: STALE_REASON[changed],
15964
+ verified_at: record.verified_at,
15965
+ base: live.base,
15966
+ head: live.head,
15967
+ verifier_command: record.inputs.command,
15968
+ record
15969
+ };
15970
+ }
15971
+ var VERIFICATION_STATE_SCHEMA_VERSION, BINDING_INPUTS, STALE_REASON, MISMATCH_PRIORITY;
15972
+ var init_verification_state = __esm({
15973
+ "src/verification-state.ts"() {
15974
+ "use strict";
15975
+ init_build();
15976
+ init_trusted();
15977
+ init_fingerprint();
15978
+ init_policy();
15979
+ init_policy_load();
15980
+ init_repo_context();
15981
+ init_wiring();
15982
+ init_dependency_env();
15983
+ VERIFICATION_STATE_SCHEMA_VERSION = 1;
15984
+ BINDING_INPUTS = [
15985
+ "tree",
15986
+ "head",
15987
+ "base",
15988
+ "policy",
15989
+ "verifier",
15990
+ "surface",
15991
+ "intervention",
15992
+ "dependencies"
15993
+ ];
15994
+ STALE_REASON = {
15995
+ tree: "candidate tree changed since verification",
15996
+ head: "HEAD commit changed since verification",
15997
+ base: "trusted base commit changed since verification",
15998
+ policy: "TamperWard policy changed since verification",
15999
+ verifier: "verifier configuration changed since verification",
16000
+ surface: "protected verification surface changed since verification",
16001
+ intervention: "runtime steering wiring changed since verification",
16002
+ dependencies: "dependency environment changed since verification"
16003
+ };
16004
+ MISMATCH_PRIORITY = [
16005
+ "head",
16006
+ "base",
16007
+ "verifier",
16008
+ "surface",
16009
+ "intervention",
16010
+ "dependencies",
16011
+ "policy",
16012
+ "tree"
16013
+ ];
16014
+ }
16015
+ });
16016
+
16017
+ // src/cli/verify.ts
16018
+ import { execFileSync as execFileSync9, spawnSync as spawnSync3 } from "node:child_process";
16019
+ import { createHash as createHash13 } from "node:crypto";
16020
+ import { chmodSync as chmodSync2, cpSync, lstatSync as lstatSync9, mkdirSync as mkdirSync9, mkdtempSync as mkdtempSync3, readFileSync as readFileSync17, readlinkSync as readlinkSync3, rmSync as rmSync8, symlinkSync, writeFileSync as writeFileSync10, existsSync as existsSync14 } from "node:fs";
15658
16021
  import { tmpdir as tmpdir4 } from "node:os";
15659
- import { dirname as dirname8, isAbsolute as isAbsolute11, join as join19, relative as relative6, resolve as resolve9, sep as sep4 } from "node:path";
16022
+ import { dirname as dirname9, isAbsolute as isAbsolute11, join as join20, relative as relative6, resolve as resolve9, sep as sep4 } from "node:path";
15660
16023
  function oracleAssuranceReport() {
15661
16024
  return {
15662
16025
  level: "suite-exit-only",
@@ -15665,14 +16028,14 @@ function oracleAssuranceReport() {
15665
16028
  limitation: "candidate source executes inside the configured suite process and may terminate or interpose on that in-process oracle; isolated-container is execution-domain isolation, not semantic/oracle isolation"
15666
16029
  };
15667
16030
  }
15668
- function git2(args, cwd) {
15669
- return execFileSync8("git", args, { cwd, encoding: "utf8", maxBuffer: 1 << 28, env: trustedGitEnv() });
16031
+ function git3(args, cwd) {
16032
+ return execFileSync9("git", args, { cwd, encoding: "utf8", maxBuffer: 1 << 28, env: trustedGitEnv() });
15670
16033
  }
15671
16034
  function resolveBase(base, cwd) {
15672
16035
  assertRev(base);
15673
- const rev2 = git2(["rev-parse", "--verify", `${base}^{commit}`], cwd).trim();
16036
+ const rev2 = git3(["rev-parse", "--verify", `${base}^{commit}`], cwd).trim();
15674
16037
  try {
15675
- return git2(["merge-base", rev2, "HEAD"], cwd).trim();
16038
+ return git3(["merge-base", rev2, "HEAD"], cwd).trim();
15676
16039
  } catch {
15677
16040
  return rev2;
15678
16041
  }
@@ -15720,15 +16083,15 @@ function rootedLinkTarget(target) {
15720
16083
  return isAbsolute11(target) || driveRelativeLinkTarget(target);
15721
16084
  }
15722
16085
  function safeSymlink(target, out3, root, label) {
15723
- if (rootedLinkTarget(target) || !inside2(root, resolve9(dirname8(out3), target))) {
16086
+ if (rootedLinkTarget(target) || !inside2(root, resolve9(dirname9(out3), target))) {
15724
16087
  throw linkEscape(label, target);
15725
16088
  }
15726
- rmSync7(out3, { force: true });
16089
+ rmSync8(out3, { force: true });
15727
16090
  symlinkSync(target, out3);
15728
16091
  }
15729
16092
  function validateSymlinkGraph(target, out3, root, dependencyRoot, label) {
15730
16093
  let domain = "tree";
15731
- let stack = linkParts(relative6(root, dirname8(out3)));
16094
+ let stack = linkParts(relative6(root, dirname9(out3)));
15732
16095
  const pending = linkParts(target);
15733
16096
  const seen = /* @__PURE__ */ new Set();
15734
16097
  let hops = 0;
@@ -15745,7 +16108,7 @@ function validateSymlinkGraph(target, out3, root, dependencyRoot, label) {
15745
16108
  continue;
15746
16109
  }
15747
16110
  const base = domain === "deps" && dependencyRoot ? dependencyRoot : root;
15748
- const current = join19(base, ...stack);
16111
+ const current = join20(base, ...stack);
15749
16112
  let st;
15750
16113
  try {
15751
16114
  st = lstatSync9(current);
@@ -15787,7 +16150,7 @@ function rejectLinkedParent(cwd, rel) {
15787
16150
  const parts = rel.split("/").slice(0, -1);
15788
16151
  let current = cwd;
15789
16152
  for (const part of parts) {
15790
- current = join19(current, part);
16153
+ current = join20(current, part);
15791
16154
  let st;
15792
16155
  try {
15793
16156
  st = lstatSync9(current);
@@ -15812,7 +16175,7 @@ function materialize(cwd, dest, dependencyRoot) {
15812
16175
  }
15813
16176
  const links = [];
15814
16177
  for (const rel of listed) {
15815
- const src = join19(cwd, rel);
16178
+ const src = join20(cwd, rel);
15816
16179
  rejectLinkedParent(cwd, rel);
15817
16180
  let st;
15818
16181
  try {
@@ -15820,8 +16183,8 @@ function materialize(cwd, dest, dependencyRoot) {
15820
16183
  } catch {
15821
16184
  continue;
15822
16185
  }
15823
- const out3 = join19(dest, rel);
15824
- mkdirSync8(dirname8(out3), { recursive: true });
16186
+ const out3 = join20(dest, rel);
16187
+ mkdirSync9(dirname9(out3), { recursive: true });
15825
16188
  dropSymlink(out3);
15826
16189
  if (st.isSymbolicLink()) {
15827
16190
  const target = readlinkSync3(src);
@@ -15840,19 +16203,19 @@ function materialize(cwd, dest, dependencyRoot) {
15840
16203
  for (const link of links) {
15841
16204
  validateSymlinkGraph(link.target, link.out, dest, dependencyRoot, link.rel);
15842
16205
  }
15843
- if (dependencyRoot) symlinkSync(dependencyRoot, join19(dest, "node_modules"), "dir");
16206
+ if (dependencyRoot) symlinkSync(dependencyRoot, join20(dest, "node_modules"), "dir");
15844
16207
  }
15845
16208
  function workingTreePaths(cwd) {
15846
- return git2(["ls-files", "-z", "--cached", "--others", "--exclude-standard"], cwd).split("\0").filter(Boolean);
16209
+ return git3(["ls-files", "-z", "--cached", "--others", "--exclude-standard"], cwd).split("\0").filter(Boolean);
15847
16210
  }
15848
16211
  function probeFilesystemCaseSensitivity(dir) {
15849
- const upper = join19(dir, ".tw-case-probe-A");
15850
- const lower = join19(dir, ".tw-case-probe-a");
16212
+ const upper = join20(dir, ".tw-case-probe-A");
16213
+ const lower = join20(dir, ".tw-case-probe-a");
15851
16214
  try {
15852
- writeFileSync9(upper, "", { flag: "wx" });
16215
+ writeFileSync10(upper, "", { flag: "wx" });
15853
16216
  return !existsSync14(lower);
15854
16217
  } finally {
15855
- rmSync7(upper, { force: true });
16218
+ rmSync8(upper, { force: true });
15856
16219
  }
15857
16220
  }
15858
16221
  function foldCase(p) {
@@ -15870,7 +16233,7 @@ function caseFoldCollision(paths) {
15870
16233
  }
15871
16234
  function dropSymlink(p) {
15872
16235
  try {
15873
- if (lstatSync9(p).isSymbolicLink()) rmSync7(p, { force: true });
16236
+ if (lstatSync9(p).isSymbolicLink()) rmSync8(p, { force: true });
15874
16237
  } catch {
15875
16238
  }
15876
16239
  }
@@ -15899,22 +16262,22 @@ function verifierCoveredInputs(cmd, atBase, policy) {
15899
16262
  return covered;
15900
16263
  }
15901
16264
  function baseEntries(base, cwd) {
15902
- return git2(["ls-tree", "-r", "-z", base], cwd).split("\0").filter(Boolean).map((rec) => {
16265
+ return git3(["ls-tree", "-r", "-z", base], cwd).split("\0").filter(Boolean).map((rec) => {
15903
16266
  const tab = rec.indexOf(" ");
15904
16267
  const [mode, type] = rec.slice(0, tab).split(" ");
15905
16268
  return { mode, type, path: rec.slice(tab + 1) };
15906
16269
  });
15907
16270
  }
15908
16271
  function overlayDigest(dest, paths) {
15909
- const h = createHash12("sha256");
16272
+ const h = createHash13("sha256");
15910
16273
  for (const rel of [...paths].sort()) {
15911
16274
  h.update(rel);
15912
16275
  h.update("\0");
15913
- const p = join19(dest, rel);
16276
+ const p = join20(dest, rel);
15914
16277
  try {
15915
16278
  const st = lstatSync9(p);
15916
16279
  h.update(String(st.mode));
15917
- h.update(st.isSymbolicLink() ? readlinkSync3(p) : readFileSync16(p));
16280
+ h.update(st.isSymbolicLink() ? readlinkSync3(p) : readFileSync17(p));
15918
16281
  } catch {
15919
16282
  h.update("<gone>");
15920
16283
  }
@@ -15943,16 +16306,16 @@ function overlayPristine(cwd, base, dest, policy, cmd, dependencyRoot, caseSensi
15943
16306
  if (e.type === "commit") {
15944
16307
  throw new Error(`the base's ${e.path} is a submodule; the pristine copy cannot reproduce it`);
15945
16308
  }
15946
- const content = execFileSync8("git", ["show", `${base}:${e.path}`], { cwd, maxBuffer: 1 << 28, env: trustedGitEnv() });
15947
- const out3 = join19(dest, e.path);
15948
- mkdirSync8(dirname8(out3), { recursive: true });
15949
- rmSync7(out3, { force: true });
16309
+ const content = execFileSync9("git", ["show", `${base}:${e.path}`], { cwd, maxBuffer: 1 << 28, env: trustedGitEnv() });
16310
+ const out3 = join20(dest, e.path);
16311
+ mkdirSync9(dirname9(out3), { recursive: true });
16312
+ rmSync8(out3, { force: true });
15950
16313
  if (e.mode === "120000") {
15951
16314
  const target = content.toString("utf8");
15952
16315
  safeSymlink(target, out3, dest, `the base's ${e.path}`);
15953
16316
  restoredLinks.push({ path: e.path, out: out3, target });
15954
16317
  } else {
15955
- writeFileSync9(out3, content);
16318
+ writeFileSync10(out3, content);
15956
16319
  chmodSync2(out3, parseInt(e.mode.slice(-4), 8) & 511);
15957
16320
  }
15958
16321
  restored.push(e.path);
@@ -15961,7 +16324,7 @@ function overlayPristine(cwd, base, dest, policy, cmd, dependencyRoot, caseSensi
15961
16324
  const inCopy = workingTreePaths(cwd);
15962
16325
  for (const rel of inCopy) {
15963
16326
  if (!isOverlay(rel) || baseProtected.has(fold(rel))) continue;
15964
- rmSync7(join19(dest, rel), { force: true });
16327
+ rmSync8(join20(dest, rel), { force: true });
15965
16328
  removed++;
15966
16329
  }
15967
16330
  for (const link of restoredLinks) {
@@ -15975,9 +16338,9 @@ function suiteEnv(scratch) {
15975
16338
  if (DROPPED_ENV.has(k) || PINNED_NPM.test(k)) continue;
15976
16339
  env[k] = v;
15977
16340
  }
15978
- const userRc = join19(scratch, "npmrc-user");
15979
- const globalRc = join19(scratch, "npmrc-global");
15980
- for (const f of [userRc, globalRc]) writeFileSync9(f, "", { mode: 292 });
16341
+ const userRc = join20(scratch, "npmrc-user");
16342
+ const globalRc = join20(scratch, "npmrc-global");
16343
+ for (const f of [userRc, globalRc]) writeFileSync10(f, "", { mode: 292 });
15981
16344
  env.npm_config_userconfig = userRc;
15982
16345
  env.npm_config_globalconfig = globalRc;
15983
16346
  env.npm_config_node_options = " ";
@@ -16008,7 +16371,7 @@ function runLocalSuite(dir, cmd, budgetSecs) {
16008
16371
  reason: `checkpointed-local verifier is unsupported on ${process.platform}; no trusted local shell contract is defined`
16009
16372
  };
16010
16373
  }
16011
- const scratch = mkdtempSync3(join19(tmpdir4(), "tw-verify-run-"));
16374
+ const scratch = mkdtempSync3(join20(tmpdir4(), "tw-verify-run-"));
16012
16375
  try {
16013
16376
  const r = runCapturedProcessSync(shell.executable, shell.args, {
16014
16377
  cwd: dir,
@@ -16053,7 +16416,7 @@ function runLocalSuite(dir, cmd, budgetSecs) {
16053
16416
  diagnostics: r.diagnostics
16054
16417
  };
16055
16418
  } finally {
16056
- rmSync7(scratch, { recursive: true, force: true });
16419
+ rmSync8(scratch, { recursive: true, force: true });
16057
16420
  }
16058
16421
  }
16059
16422
  function diagnosticsJson(diagnostics, includeTail) {
@@ -16098,6 +16461,15 @@ function verifyVerdictLine(verdict2, ctx) {
16098
16461
  }
16099
16462
  }
16100
16463
  function runVerify(opts) {
16464
+ const markerCwd = repoRoot(opts.cwd ?? process.cwd());
16465
+ const marked = opts.silent ? false : beginVerifying(markerCwd);
16466
+ try {
16467
+ return runVerifyImpl(opts);
16468
+ } finally {
16469
+ if (marked) endVerifying(markerCwd);
16470
+ }
16471
+ }
16472
+ function runVerifyImpl(opts) {
16101
16473
  const cwd = repoRoot(opts.cwd ?? process.cwd());
16102
16474
  const colour = opts.silent ? false : colourEnabled(process.env, process.stdout);
16103
16475
  const paintVerify = (s) => {
@@ -16137,7 +16509,7 @@ function runVerify(opts) {
16137
16509
  return cannotVerify("POLICY_ERROR", `cannot load policy (${e instanceof Error ? e.message : String(e)})`);
16138
16510
  }
16139
16511
  if (opts.requireAncestor) {
16140
- const requested = git2(["rev-parse", "--verify", `${opts.base ?? "HEAD"}^{commit}`], cwd).trim();
16512
+ const requested = git3(["rev-parse", "--verify", `${opts.base ?? "HEAD"}^{commit}`], cwd).trim();
16141
16513
  if (!baseIsAncestorOfHead(requested, cwd)) {
16142
16514
  return cannotVerify(
16143
16515
  "BASE_NOT_ANCESTOR",
@@ -16245,17 +16617,17 @@ function runVerify(opts) {
16245
16617
  } catch {
16246
16618
  return cannotVerify("BASE_UNRESOLVABLE", `cannot resolve base rev "${opts.base ?? "HEAD"}"`);
16247
16619
  }
16248
- const visRoot = mkdtempSync3(join19(tmpdir4(), "tw-verify-vis-"));
16249
- const visDir = join19(visRoot, "t");
16620
+ const visRoot = mkdtempSync3(join20(tmpdir4(), "tw-verify-vis-"));
16621
+ const visDir = join20(visRoot, "t");
16250
16622
  const cleanup = (dirs) => {
16251
16623
  if (opts.keep) return;
16252
- for (const d of dirs) rmSync7(d, { recursive: true, force: true });
16624
+ for (const d of dirs) rmSync8(d, { recursive: true, force: true });
16253
16625
  };
16254
16626
  filesystemCaseSensitive = (opts.probeCaseSensitivity ?? probeFilesystemCaseSensitivity)(visRoot);
16255
16627
  if (!filesystemCaseSensitive) {
16256
16628
  const present = (rel) => {
16257
16629
  try {
16258
- lstatSync9(join19(cwd, rel));
16630
+ lstatSync9(join20(cwd, rel));
16259
16631
  return true;
16260
16632
  } catch {
16261
16633
  return false;
@@ -16287,7 +16659,7 @@ function runVerify(opts) {
16287
16659
  );
16288
16660
  }
16289
16661
  try {
16290
- mkdirSync8(visDir);
16662
+ mkdirSync9(visDir);
16291
16663
  materialize(cwd, visDir, frozenNodeModules);
16292
16664
  } catch (e) {
16293
16665
  cleanup([visRoot]);
@@ -16388,12 +16760,12 @@ function runVerify(opts) {
16388
16760
  { stage: "visible", dependency_environment: dependencyReport(), verifier_backend: backendReport(), oracle_assurance: oracleAssuranceReport() }
16389
16761
  );
16390
16762
  }
16391
- const priRoot = mkdtempSync3(join19(tmpdir4(), "tw-verify-pri-"));
16392
- const priDir = join19(priRoot, "t");
16763
+ const priRoot = mkdtempSync3(join20(tmpdir4(), "tw-verify-pri-"));
16764
+ const priDir = join20(priRoot, "t");
16393
16765
  let restored = [];
16394
16766
  let removedAdded = 0;
16395
16767
  try {
16396
- mkdirSync8(priDir);
16768
+ mkdirSync9(priDir);
16397
16769
  materialize(cwd, priDir, frozenNodeModules);
16398
16770
  ({ restored, removed: removedAdded } = overlayPristine(
16399
16771
  cwd,
@@ -16479,6 +16851,25 @@ function runVerify(opts) {
16479
16851
  code2 = 1;
16480
16852
  }
16481
16853
  opts.onVerdict?.({ verdict: verdict2 });
16854
+ if (verdict2 === "VERIFIED") {
16855
+ try {
16856
+ const inputs = {
16857
+ base_ref: opts.base ?? "HEAD",
16858
+ explicit_base: opts.base !== void 0,
16859
+ command_source: opts.cmd !== void 0 ? "flag" : "policy",
16860
+ command: cmd,
16861
+ budget_source: opts.budget !== void 0 ? "flag" : "policy",
16862
+ budget
16863
+ };
16864
+ recordVerification(cwd, inputs, treeBefore);
16865
+ } catch {
16866
+ }
16867
+ } else {
16868
+ try {
16869
+ invalidateVerificationRecordIfCurrent(cwd);
16870
+ } catch {
16871
+ }
16872
+ }
16482
16873
  const signedOff = verdict2 === "MASKED_FAILURE" ? oobToken("verify", oobFromEnv(), oobHeadFromEnv()) : null;
16483
16874
  if (signedOff) code2 = 0;
16484
16875
  if (opts.json) {
@@ -16584,6 +16975,7 @@ var init_verify = __esm({
16584
16975
  init_verifier_backend();
16585
16976
  init_machine_output();
16586
16977
  init_signoff();
16978
+ init_verification_state();
16587
16979
  init_suite_diagnostics();
16588
16980
  init_text();
16589
16981
  init_status();
@@ -16680,11 +17072,11 @@ var init_verify = __esm({
16680
17072
  });
16681
17073
 
16682
17074
  // src/cli/run.ts
16683
- import { execFileSync as execFileSync9, spawn, spawnSync as spawnSync4 } from "node:child_process";
17075
+ import { execFileSync as execFileSync10, spawn, spawnSync as spawnSync4 } from "node:child_process";
16684
17076
  import { randomBytes as randomBytes3 } from "node:crypto";
16685
- import { accessSync as accessSync3, constants as fsConstants, mkdtempSync as mkdtempSync4, readdirSync as readdirSync4, readFileSync as readFileSync17, readlinkSync as readlinkSync4, realpathSync as realpathSync8, rmSync as rmSync8, statSync as statSync5, writeFileSync as writeFileSync10 } from "node:fs";
17077
+ import { accessSync as accessSync3, constants as fsConstants, mkdtempSync as mkdtempSync4, readdirSync as readdirSync4, readFileSync as readFileSync18, readlinkSync as readlinkSync4, realpathSync as realpathSync8, rmSync as rmSync9, statSync as statSync5, writeFileSync as writeFileSync11 } from "node:fs";
16686
17078
  import { tmpdir as tmpdir5 } from "node:os";
16687
- import { dirname as dirname9, join as join20, resolve as resolve10 } from "node:path";
17079
+ import { dirname as dirname10, join as join21, resolve as resolve10 } from "node:path";
16688
17080
  function authoritativeRunLifecyclePlatform(platform = process.platform) {
16689
17081
  return platform === "linux";
16690
17082
  }
@@ -16721,7 +17113,7 @@ function writableByCaller2(path) {
16721
17113
  return true;
16722
17114
  } catch {
16723
17115
  }
16724
- const parent = dirname9(cur);
17116
+ const parent = dirname10(cur);
16725
17117
  if (parent === cur) break;
16726
17118
  cur = parent;
16727
17119
  }
@@ -16760,9 +17152,9 @@ function trustedLinuxPython(candidates = DEFAULT_TRUSTED_PYTHON_CANDIDATES) {
16760
17152
  };
16761
17153
  }
16762
17154
  function runAgentSupervised(argv, cwd, budgetSecs, linuxPythonCandidates, lifecycleTestMode, machineMode = false) {
16763
- const stateDir = mkdtempSync4(join20(tmpdir5(), "tw-agent-supervisor-"));
16764
- const resultFile = join20(stateDir, "result.json");
16765
- const agentEnvFile = join20(stateDir, "agent-env.json");
17155
+ const stateDir2 = mkdtempSync4(join21(tmpdir5(), "tw-agent-supervisor-"));
17156
+ const resultFile = join21(stateDir2, "result.json");
17157
+ const agentEnvFile = join21(stateDir2, "agent-env.json");
16766
17158
  try {
16767
17159
  const linux = process.platform === "linux";
16768
17160
  let executable;
@@ -16779,7 +17171,7 @@ function runAgentSupervised(argv, cwd, budgetSecs, linuxPythonCandidates, lifecy
16779
17171
  failure: trustedPython.reason ?? "trusted Linux python3 interpreter is unavailable"
16780
17172
  };
16781
17173
  }
16782
- writeFileSync10(agentEnvFile, JSON.stringify(process.env), { mode: 384 });
17174
+ writeFileSync11(agentEnvFile, JSON.stringify(process.env), { mode: 384 });
16783
17175
  executable = trustedPython.path;
16784
17176
  args = [
16785
17177
  "-I",
@@ -16836,7 +17228,7 @@ function runAgentSupervised(argv, cwd, budgetSecs, linuxPythonCandidates, lifecy
16836
17228
  }
16837
17229
  let state = {};
16838
17230
  try {
16839
- state = JSON.parse(readFileSync17(resultFile, "utf8"));
17231
+ state = JSON.parse(readFileSync18(resultFile, "utf8"));
16840
17232
  } catch {
16841
17233
  return {
16842
17234
  exit: 1,
@@ -16861,13 +17253,13 @@ function runAgentSupervised(argv, cwd, budgetSecs, linuxPythonCandidates, lifecy
16861
17253
  ...state.failure ? { failure: state.failure } : {}
16862
17254
  };
16863
17255
  } finally {
16864
- rmSync8(stateDir, { recursive: true, force: true });
17256
+ rmSync9(stateDir2, { recursive: true, force: true });
16865
17257
  }
16866
17258
  }
16867
17259
  function waitMs(ms) {
16868
17260
  Atomics.wait(waitCell, 0, 0, ms);
16869
17261
  }
16870
- function pidAlive3(pid) {
17262
+ function pidAlive4(pid) {
16871
17263
  if (!pid || !Number.isInteger(pid) || pid <= 0) return false;
16872
17264
  try {
16873
17265
  process.kill(pid, 0);
@@ -16877,10 +17269,10 @@ function pidAlive3(pid) {
16877
17269
  }
16878
17270
  }
16879
17271
  function observerExited(pid) {
16880
- if (!pidAlive3(pid)) return true;
17272
+ if (!pidAlive4(pid)) return true;
16881
17273
  if (process.platform !== "linux") return false;
16882
17274
  try {
16883
- const stat = readFileSync17(`/proc/${pid}/stat`, "utf8");
17275
+ const stat = readFileSync18(`/proc/${pid}/stat`, "utf8");
16884
17276
  const state = stat.slice(stat.lastIndexOf(")") + 2).split(" ")[0];
16885
17277
  return state === "Z" || state === "X";
16886
17278
  } catch (e) {
@@ -16888,8 +17280,8 @@ function observerExited(pid) {
16888
17280
  }
16889
17281
  }
16890
17282
  function supervisedObserverLog(cwd) {
16891
- const gd = gitDir(cwd) ?? join20(cwd, ".git");
16892
- return join20(
17283
+ const gd = gitDir(cwd) ?? join21(cwd, ".git");
17284
+ return join21(
16893
17285
  gd,
16894
17286
  "tamperward",
16895
17287
  `run-observer-${process.pid}-${Date.now()}-${randomBytes3(4).toString("hex")}.jsonl`
@@ -16997,12 +17389,12 @@ function finishObserverAdvisory(observer, write = out) {
16997
17389
  const telemetry = stopObserverProcess(observer);
16998
17390
  observerSummary(observer, telemetry, write);
16999
17391
  }
17000
- function git3(args, cwd) {
17001
- return execFileSync9("git", args, { cwd, encoding: "utf8", maxBuffer: 1 << 28, env: trustedGitEnv() });
17392
+ function git4(args, cwd) {
17393
+ return execFileSync10("git", args, { cwd, encoding: "utf8", maxBuffer: 1 << 28, env: trustedGitEnv() });
17002
17394
  }
17003
17395
  function nowTicks() {
17004
17396
  try {
17005
- return Math.floor(parseFloat(readFileSync17("/proc/uptime", "utf8").split(" ")[0]) * 100);
17397
+ return Math.floor(parseFloat(readFileSync18("/proc/uptime", "utf8").split(" ")[0]) * 100);
17006
17398
  } catch {
17007
17399
  return Number.POSITIVE_INFINITY;
17008
17400
  }
@@ -17025,7 +17417,7 @@ function survivorsHoldingTree(cwd, spawnedAfterTicks) {
17025
17417
  const n2 = Number(pid);
17026
17418
  if (n2 === process.pid) continue;
17027
17419
  try {
17028
- const stat = readFileSync17(`/proc/${pid}/stat`, "utf8");
17420
+ const stat = readFileSync18(`/proc/${pid}/stat`, "utf8");
17029
17421
  const startTicks = Number(stat.slice(stat.lastIndexOf(")") + 2).split(" ")[19]);
17030
17422
  if (!Number.isFinite(startTicks) || startTicks < spawnedAfterTicks) continue;
17031
17423
  } catch {
@@ -17069,7 +17461,7 @@ function runEnvelope(opts) {
17069
17461
  let base;
17070
17462
  try {
17071
17463
  const requestedBase = assertRev(opts.base ?? "HEAD");
17072
- base = git3(["rev-parse", "--verify", `${requestedBase}^{commit}`], cwd).trim();
17464
+ base = git4(["rev-parse", "--verify", `${requestedBase}^{commit}`], cwd).trim();
17073
17465
  } catch {
17074
17466
  err(`tamperward run: cannot resolve trusted base ${opts.base ?? "HEAD"} \u2014 failing closed.`);
17075
17467
  return 2;
@@ -17091,7 +17483,7 @@ function runEnvelope(opts) {
17091
17483
  return 2;
17092
17484
  }
17093
17485
  if (!opts.allowDirty) {
17094
- const dirty = git3(["status", "--porcelain"], cwd).trim();
17486
+ const dirty = git4(["status", "--porcelain"], cwd).trim();
17095
17487
  if (dirty) {
17096
17488
  err("tamperward run: working tree is dirty before the agent starts \u2014 the envelope cannot");
17097
17489
  err("attribute changes. Commit or stash first, or pass --allow-dirty to own the risk.");
@@ -17196,7 +17588,7 @@ tamperward run \u2014 agent exit ${agentExit}; OBJECT_REWRITE \u2192 ENFORCEMENT
17196
17588
  emitRunJson("OBJECT_REWRITE", 1);
17197
17589
  return 1;
17198
17590
  }
17199
- const head = git3(["rev-parse", "HEAD"], cwd).trim();
17591
+ const head = git4(["rev-parse", "HEAD"], cwd).trim();
17200
17592
  const isAncestor = spawnSync4("git", ["merge-base", "--is-ancestor", base, head], { cwd, env: trustedGitEnv() });
17201
17593
  if (isAncestor.status !== 0) {
17202
17594
  err(`tamperward run: HEAD ${head.slice(0, 10)} is not a descendant of the trusted base ${base.slice(0, 10)} \u2014`);
@@ -17597,9 +17989,9 @@ raise SystemExit(0)
17597
17989
  });
17598
17990
 
17599
17991
  // src/cli/doctor.ts
17600
- import { execFileSync as execFileSync10 } from "node:child_process";
17601
- import { existsSync as existsSync15, readFileSync as readFileSync18, readdirSync as readdirSync5 } from "node:fs";
17602
- import { join as join21, resolve as resolve11 } from "node:path";
17992
+ import { execFileSync as execFileSync11 } from "node:child_process";
17993
+ import { existsSync as existsSync15, readFileSync as readFileSync19, readdirSync as readdirSync5 } from "node:fs";
17994
+ import { join as join22, resolve as resolve11 } from "node:path";
17603
17995
  function lifecyclePlatformCheck(platform = process.platform, linuxPython = platform === "linux" ? trustedLinuxPython() : null) {
17604
17996
  if (platform === "linux") {
17605
17997
  if (linuxPython?.path) {
@@ -17657,7 +18049,7 @@ function pinsInFile(path) {
17657
18049
  if (!existsSync15(path)) return [];
17658
18050
  let src = "";
17659
18051
  try {
17660
- src = readFileSync18(path, "utf8");
18052
+ src = readFileSync19(path, "utf8");
17661
18053
  } catch {
17662
18054
  return [];
17663
18055
  }
@@ -17684,7 +18076,7 @@ function workflowPermissionCheck(cwd, workflowRels = [".github/workflows/tamperw
17684
18076
  }
17685
18077
  let doc;
17686
18078
  try {
17687
- doc = yaml.parse(readFileSync18(path, "utf8"));
18079
+ doc = yaml.parse(readFileSync19(path, "utf8"));
17688
18080
  } catch (e) {
17689
18081
  broken.push(`${rel} is not valid YAML (${e instanceof Error ? e.message : String(e)})`);
17690
18082
  continue;
@@ -17797,9 +18189,9 @@ function collectLocalPosture(cwd, policy, authorityWorkflows, explicitWorkflow =
17797
18189
  addWiring("codeowners", "codeowners", true);
17798
18190
  checks.push(workflowPermissionCheck(cwd, authorityWorkflows));
17799
18191
  const pins = [
17800
- ...pinsInFile(join21(cwd, ".claude", "settings.json")),
17801
- ...pinsInFile(join21(cwd, ".git", "hooks", "pre-commit")),
17802
- ...pinsInFile(join21(cwd, ".husky", "pre-commit"))
18192
+ ...pinsInFile(join22(cwd, ".claude", "settings.json")),
18193
+ ...pinsInFile(join22(cwd, ".git", "hooks", "pre-commit")),
18194
+ ...pinsInFile(join22(cwd, ".husky", "pre-commit"))
17803
18195
  ];
17804
18196
  const below = pins.filter((pin) => (compareVersions(pin, TW_VERSION) ?? 0) < 0);
17805
18197
  const above = pins.filter((pin) => (compareVersions(TW_VERSION, pin) ?? 0) < 0);
@@ -17917,7 +18309,7 @@ function evaluateGitHubProtection(snapshot2, requiredCheck = "tamperward", requi
17917
18309
  }
17918
18310
  function gitText(cwd, args) {
17919
18311
  try {
17920
- return execFileSync10("git", args, {
18312
+ return execFileSync11("git", args, {
17921
18313
  cwd,
17922
18314
  encoding: "utf8",
17923
18315
  stdio: ["ignore", "pipe", "ignore"]
@@ -17951,7 +18343,7 @@ function githubApi(cwd, endpoint) {
17951
18343
  const token = process.env.GH_TOKEN ?? process.env.GITHUB_TOKEN ?? "";
17952
18344
  const invocation = githubApiInvocation(endpoint, token);
17953
18345
  try {
17954
- const stdout = execFileSync10(
18346
+ const stdout = execFileSync11(
17955
18347
  invocation.executable,
17956
18348
  invocation.args,
17957
18349
  {
@@ -18095,7 +18487,7 @@ function diagnose(opts = {}) {
18095
18487
  `${workflowDirRel}: could not enumerate workflows (${e instanceof Error ? e.message : String(e)})`
18096
18488
  );
18097
18489
  }
18098
- for (const name of entries) workflowRels.push(join21(workflowDirRel, name));
18490
+ for (const name of entries) workflowRels.push(join22(workflowDirRel, name));
18099
18491
  }
18100
18492
  const requiredSecs = requiredVerifierAuthoritySeconds(policy.verify.budget);
18101
18493
  const requiredMinutes = Math.ceil(requiredSecs / 60);
@@ -18108,7 +18500,7 @@ function diagnose(opts = {}) {
18108
18500
  }
18109
18501
  let doc;
18110
18502
  try {
18111
- doc = yaml.parse(readFileSync18(workflowPath, "utf8"));
18503
+ doc = yaml.parse(readFileSync19(workflowPath, "utf8"));
18112
18504
  } catch (e) {
18113
18505
  return fail(
18114
18506
  "ci-verifier",
@@ -18282,19 +18674,153 @@ var init_doctor = __esm({
18282
18674
  }
18283
18675
  });
18284
18676
 
18677
+ // src/cli/status.ts
18678
+ function authorityFromChecks(checks) {
18679
+ if (checks.some((c) => c.state === "BROKEN")) return "BROKEN";
18680
+ if (checks.some((c) => c.state === "WARN")) return "PARTIAL";
18681
+ return "ACTIVE";
18682
+ }
18683
+ function interventionFromCheck(check2) {
18684
+ if (!check2) return { state: "UNKNOWN", detail: "agent hook wiring could not be evaluated" };
18685
+ const map = {
18686
+ OK: "ACTIVE",
18687
+ WARN: "PARTIAL",
18688
+ BROKEN: "INACTIVE"
18689
+ };
18690
+ return { state: map[check2.state], detail: check2.detail };
18691
+ }
18692
+ function buildStatusModel(cwd) {
18693
+ let authority;
18694
+ let intervention;
18695
+ try {
18696
+ const outcome = diagnose({ cwd });
18697
+ authority = { state: authorityFromChecks(outcome.checks) };
18698
+ const summary = outcome.checks.filter((c) => c.state !== "OK").map((c) => `${c.id}: ${c.state}`);
18699
+ if (summary.length) authority.detail = summary.join("; ");
18700
+ intervention = interventionFromCheck(outcome.checks.find((c) => c.id === "claude-hooks"));
18701
+ } catch (e) {
18702
+ const detail = e instanceof Error ? e.message : String(e);
18703
+ authority = { state: "UNKNOWN", detail };
18704
+ intervention = { state: "UNKNOWN", detail };
18705
+ }
18706
+ const verification = evaluateVerificationState(cwd);
18707
+ const agent = intervention.state === "ACTIVE" || intervention.state === "PARTIAL" ? "claude-code" : "none";
18708
+ return { authority, intervention, verification, runtime: { agent } };
18709
+ }
18710
+ function shortId(id) {
18711
+ return id ? `${id.slice(0, 10)}...` : void 0;
18712
+ }
18713
+ function renderStatus(model, colour) {
18714
+ const v = model.verification;
18715
+ const lines = ["TamperWard", ""];
18716
+ lines.push(statusLine(AUTHORITY_SEVERITY[model.authority.state], "Authority", model.authority.state, colour));
18717
+ lines.push(statusLine(INTERVENTION_SEVERITY[model.intervention.state], "Intervention", model.intervention.state, colour));
18718
+ lines.push(statusLine(VERIFICATION_SEVERITY[v.state], "Verification", v.state, colour));
18719
+ const rows = [];
18720
+ const record = v.record;
18721
+ if (record) {
18722
+ const verifiedTree = shortId(record.binding.tree);
18723
+ if (verifiedTree) rows.push(["Verified tree", verifiedTree]);
18724
+ const baseShort = shortId(v.base ?? record.binding.base);
18725
+ if (baseShort) rows.push(["Base", `${record.inputs.base_ref}@${baseShort}`]);
18726
+ if (v.verifier_command) rows.push(["Verifier", v.verifier_command]);
18727
+ }
18728
+ rows.push(["Runtime", model.runtime.agent]);
18729
+ if (v.state === "STALE" && v.reason) rows.push(["Reason", v.reason]);
18730
+ else if (v.state === "BROKEN") rows.push(["Reason", v.detail ?? v.reason ?? "authority wiring is invalid"]);
18731
+ else if (v.state === "VERIFYING") rows.push(["Reason", v.reason ?? "a verification is in progress"]);
18732
+ else if (v.state === "UNVERIFIED") rows.push(["Next", "run `tamperward verify` to establish a verified baseline"]);
18733
+ if (rows.length) {
18734
+ lines.push("");
18735
+ const width = Math.max(...rows.map(([k]) => k.length));
18736
+ for (const [k, val] of rows) lines.push(`${k.padEnd(width)} ${val}`);
18737
+ }
18738
+ return lines.join("\n") + "\n";
18739
+ }
18740
+ function statusDocument(model) {
18741
+ const v = model.verification;
18742
+ const verification = { state: v.state };
18743
+ if (v.reason) verification.reason = v.reason;
18744
+ if (v.changed_input) verification.changed_input = v.changed_input;
18745
+ if (v.detail) verification.detail = v.detail;
18746
+ if (v.verified_at) verification.verified_at = v.verified_at;
18747
+ if (v.base) verification.base = v.base;
18748
+ if (v.head) verification.head = v.head;
18749
+ if (v.verifier_command) verification.verifier_command = v.verifier_command;
18750
+ if (v.record) {
18751
+ verification.tree = v.record.binding.tree;
18752
+ verification.binding = { ...v.record.binding };
18753
+ }
18754
+ return machineOutput({
18755
+ command: "status",
18756
+ authority: model.authority,
18757
+ intervention: model.intervention,
18758
+ verification,
18759
+ runtime: model.runtime
18760
+ });
18761
+ }
18762
+ function runStatus(opts = {}) {
18763
+ const cwd = opts.cwd ?? process.cwd();
18764
+ if (!repoContext(cwd)) {
18765
+ const why = outsideRepository(cwd) ?? "cwd is not inside a repository the gate can read";
18766
+ process.stderr.write(`tamperward: status needs a git repository (${why})
18767
+ `);
18768
+ return 2;
18769
+ }
18770
+ const model = buildStatusModel(cwd);
18771
+ if (opts.json) {
18772
+ process.stdout.write(JSON.stringify(statusDocument(model)) + "\n");
18773
+ } else {
18774
+ process.stdout.write(renderStatus(model, colourEnabled(process.env, process.stdout)));
18775
+ }
18776
+ return 0;
18777
+ }
18778
+ var AUTHORITY_SEVERITY, INTERVENTION_SEVERITY, VERIFICATION_SEVERITY;
18779
+ var init_status2 = __esm({
18780
+ "src/cli/status.ts"() {
18781
+ "use strict";
18782
+ init_machine_output();
18783
+ init_repo_context();
18784
+ init_repo_context();
18785
+ init_doctor();
18786
+ init_verification_state();
18787
+ init_text();
18788
+ init_status();
18789
+ AUTHORITY_SEVERITY = {
18790
+ ACTIVE: "ok",
18791
+ PARTIAL: "warn",
18792
+ BROKEN: "bad",
18793
+ UNKNOWN: "info"
18794
+ };
18795
+ INTERVENTION_SEVERITY = {
18796
+ ACTIVE: "ok",
18797
+ PARTIAL: "warn",
18798
+ INACTIVE: "bad",
18799
+ UNKNOWN: "info"
18800
+ };
18801
+ VERIFICATION_SEVERITY = {
18802
+ CURRENT: "ok",
18803
+ STALE: "warn",
18804
+ VERIFYING: "info",
18805
+ BROKEN: "bad",
18806
+ UNVERIFIED: "info"
18807
+ };
18808
+ }
18809
+ });
18810
+
18285
18811
  // src/cli/trace-verify.ts
18286
- import { execFileSync as execFileSync11, spawnSync as spawnSync5 } from "node:child_process";
18812
+ import { execFileSync as execFileSync12, spawnSync as spawnSync5 } from "node:child_process";
18287
18813
  import {
18288
18814
  existsSync as existsSync16,
18289
18815
  mkdtempSync as mkdtempSync5,
18290
- readFileSync as readFileSync19,
18816
+ readFileSync as readFileSync20,
18291
18817
  readdirSync as readdirSync6,
18292
18818
  realpathSync as realpathSync9,
18293
- rmSync as rmSync9,
18819
+ rmSync as rmSync10,
18294
18820
  symlinkSync as symlinkSync2
18295
18821
  } from "node:fs";
18296
18822
  import { tmpdir as tmpdir6 } from "node:os";
18297
- import { isAbsolute as isAbsolute12, join as join22, relative as relative7, resolve as resolve12, sep as sep5 } from "node:path";
18823
+ import { isAbsolute as isAbsolute12, join as join23, relative as relative7, resolve as resolve12, sep as sep5 } from "node:path";
18298
18824
  function unescapeStraceString(s) {
18299
18825
  return s.replace(/\\([0-7]{1,3})/g, (_m, oct) => String.fromCharCode(parseInt(oct, 8))).replace(/\\x([0-9a-fA-F]{2})/g, (_m, hex) => String.fromCharCode(parseInt(hex, 16))).replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
18300
18826
  }
@@ -18506,8 +19032,8 @@ function summarizeTraceRuns(opts) {
18506
19032
  unresolved_accesses
18507
19033
  };
18508
19034
  }
18509
- function git4(args, cwd) {
18510
- return execFileSync11("git", args, {
19035
+ function git5(args, cwd) {
19036
+ return execFileSync12("git", args, {
18511
19037
  cwd,
18512
19038
  encoding: "utf8",
18513
19039
  maxBuffer: 256 * 1024 * 1024,
@@ -18520,7 +19046,7 @@ function commandExists(command) {
18520
19046
  return !r.error && r.status === 0;
18521
19047
  }
18522
19048
  function trackedAt(base, cwd) {
18523
- return git4(["ls-tree", "-r", "-z", "--name-only", base], cwd).split("\0").filter(Boolean);
19049
+ return git5(["ls-tree", "-r", "-z", "--name-only", base], cwd).split("\0").filter(Boolean);
18524
19050
  }
18525
19051
  function materializeBase(base, cwd, dest) {
18526
19052
  const archive = spawnSync5("git", ["archive", "--format=tar", base], {
@@ -18542,8 +19068,8 @@ function materializeBase(base, cwd, dest) {
18542
19068
  `tar extraction failed${untar.stderr?.length ? `: ${String(untar.stderr).trim()}` : ""}`
18543
19069
  );
18544
19070
  }
18545
- const hostModules = join22(cwd, "node_modules");
18546
- const tracedModules = join22(dest, "node_modules");
19071
+ const hostModules = join23(cwd, "node_modules");
19072
+ const tracedModules = join23(dest, "node_modules");
18547
19073
  if (existsSync16(hostModules) && !existsSync16(tracedModules)) {
18548
19074
  symlinkSync2(resolve12(hostModules), tracedModules, "dir");
18549
19075
  }
@@ -18551,7 +19077,7 @@ function materializeBase(base, cwd, dest) {
18551
19077
  function traceFiles(prefix) {
18552
19078
  const dir = resolve12(prefix, "..");
18553
19079
  const base = prefix.split(sep5).at(-1) ?? prefix;
18554
- return readdirSync6(dir).filter((name) => name === base || name.startsWith(base + ".")).map((name) => join22(dir, name)).sort();
19080
+ return readdirSync6(dir).filter((name) => name === base || name.startsWith(base + ".")).map((name) => join23(dir, name)).sort();
18555
19081
  }
18556
19082
  function rewriteTraceRoot(accesses, actualRoot) {
18557
19083
  return accesses.map((item) => {
@@ -18566,13 +19092,13 @@ function rewriteTraceRoot(accesses, actualRoot) {
18566
19092
  if (!inside3(actualRoot, real)) return { ...item, path: real };
18567
19093
  } catch {
18568
19094
  }
18569
- return { ...item, path: join22(TRACE_ROOT, ...rel.split("/")) };
19095
+ return { ...item, path: join23(TRACE_ROOT, ...rel.split("/")) };
18570
19096
  });
18571
19097
  }
18572
19098
  function straceOnce(root, command, budget) {
18573
- const traceDir = mkdtempSync5(join22(tmpdir6(), "tw-trace-log-"));
19099
+ const traceDir = mkdtempSync5(join23(tmpdir6(), "tw-trace-log-"));
18574
19100
  try {
18575
- const prefix = join22(traceDir, "trace");
19101
+ const prefix = join23(traceDir, "trace");
18576
19102
  const traced = spawnSync5(
18577
19103
  "strace",
18578
19104
  [
@@ -18601,7 +19127,7 @@ function straceOnce(root, command, budget) {
18601
19127
  }
18602
19128
  );
18603
19129
  const logs = traced.error ? [] : traceFiles(prefix);
18604
- const accesses = logs.flatMap((path) => parseStraceFileAccess(readFileSync19(path, "utf8"), TRACE_ROOT));
19130
+ const accesses = logs.flatMap((path) => parseStraceFileAccess(readFileSync20(path, "utf8"), TRACE_ROOT));
18605
19131
  const raw = {
18606
19132
  spawnError: traced.error ? traced.error.message : null,
18607
19133
  status: traced.status,
@@ -18612,26 +19138,26 @@ function straceOnce(root, command, budget) {
18612
19138
  };
18613
19139
  return { raw, accesses };
18614
19140
  } finally {
18615
- rmSync9(traceDir, { recursive: true, force: true });
19141
+ rmSync10(traceDir, { recursive: true, force: true });
18616
19142
  }
18617
19143
  }
18618
19144
  function runOneTrace(cwd, base, command, budget) {
18619
- const root = mkdtempSync5(join22(tmpdir6(), "tw-trace-base-"));
19145
+ const root = mkdtempSync5(join23(tmpdir6(), "tw-trace-base-"));
18620
19146
  try {
18621
19147
  materializeBase(base, cwd, root);
18622
19148
  const { raw, accesses } = straceOnce(root, command, budget);
18623
19149
  return { outcome: classifyTraceRun(raw), accesses: rewriteTraceRoot(accesses, root) };
18624
19150
  } finally {
18625
- rmSync9(root, { recursive: true, force: true });
19151
+ rmSync10(root, { recursive: true, force: true });
18626
19152
  }
18627
19153
  }
18628
19154
  function tracerPreflight(budget = 10) {
18629
- const probeRoot = mkdtempSync5(join22(tmpdir6(), "tw-trace-probe-"));
19155
+ const probeRoot = mkdtempSync5(join23(tmpdir6(), "tw-trace-probe-"));
18630
19156
  try {
18631
19157
  const { raw } = straceOnce(probeRoot, "true", Math.max(1, Math.min(budget, 10)));
18632
19158
  return classifyTraceRun(raw);
18633
19159
  } finally {
18634
- rmSync9(probeRoot, { recursive: true, force: true });
19160
+ rmSync10(probeRoot, { recursive: true, force: true });
18635
19161
  }
18636
19162
  }
18637
19163
  function renderText2(report2) {
@@ -18696,7 +19222,7 @@ function runTraceVerify(opts = {}) {
18696
19222
  let base;
18697
19223
  try {
18698
19224
  const baseArg = assertRev(requestedBase);
18699
- base = git4(["rev-parse", "--verify", `${baseArg}^{commit}`], cwd).trim();
19225
+ base = git5(["rev-parse", "--verify", `${baseArg}^{commit}`], cwd).trim();
18700
19226
  } catch (e) {
18701
19227
  process.stderr.write(`tamperward trace-verify: cannot resolve trusted base ${JSON.stringify(requestedBase)} (${e instanceof Error ? e.message : String(e)}).
18702
19228
  `);
@@ -18833,26 +19359,26 @@ var init_trace_verify = __esm({
18833
19359
  });
18834
19360
 
18835
19361
  // src/runtimes.ts
18836
- import { existsSync as existsSync17, readFileSync as readFileSync20 } from "node:fs";
18837
- import { join as join23 } from "node:path";
18838
- function isRecord4(x) {
19362
+ import { existsSync as existsSync17, readFileSync as readFileSync21 } from "node:fs";
19363
+ import { join as join24 } from "node:path";
19364
+ function isRecord5(x) {
18839
19365
  return typeof x === "object" && x !== null && !Array.isArray(x);
18840
19366
  }
18841
19367
  function hooksBeyondTamperward(hooks) {
18842
- if (!isRecord4(hooks)) return hooks != null;
19368
+ if (!isRecord5(hooks)) return hooks != null;
18843
19369
  for (const entries of Object.values(hooks)) {
18844
19370
  if (!Array.isArray(entries)) {
18845
19371
  if (entries != null) return true;
18846
19372
  continue;
18847
19373
  }
18848
19374
  for (const entry of entries) {
18849
- const hs = isRecord4(entry) ? entry.hooks : null;
19375
+ const hs = isRecord5(entry) ? entry.hooks : null;
18850
19376
  if (!Array.isArray(hs)) {
18851
19377
  if (hs != null) return true;
18852
19378
  continue;
18853
19379
  }
18854
19380
  for (const h of hs) {
18855
- const cmd = isRecord4(h) ? String(h.command ?? "") : "";
19381
+ const cmd = isRecord5(h) ? String(h.command ?? "") : "";
18856
19382
  if (!/tamperward/.test(cmd)) return true;
18857
19383
  }
18858
19384
  }
@@ -18860,11 +19386,11 @@ function hooksBeyondTamperward(hooks) {
18860
19386
  return false;
18861
19387
  }
18862
19388
  function claudeSettingsBeyondTamperward(cwd) {
18863
- const path = join23(cwd, ".claude", "settings.json");
19389
+ const path = join24(cwd, ".claude", "settings.json");
18864
19390
  let raw;
18865
19391
  try {
18866
19392
  if (!existsSync17(path)) return false;
18867
- raw = readFileSync20(path, "utf8");
19393
+ raw = readFileSync21(path, "utf8");
18868
19394
  } catch {
18869
19395
  return false;
18870
19396
  }
@@ -18874,7 +19400,7 @@ function claudeSettingsBeyondTamperward(cwd) {
18874
19400
  } catch {
18875
19401
  return raw.trim().length > 0;
18876
19402
  }
18877
- if (!isRecord4(parsed)) return raw.trim().length > 0;
19403
+ if (!isRecord5(parsed)) return raw.trim().length > 0;
18878
19404
  for (const [key2, value] of Object.entries(parsed)) {
18879
19405
  if (key2 === "disableAllHooks") continue;
18880
19406
  if (key2 === "hooks") {
@@ -18890,7 +19416,7 @@ function detectRuntimes(cwd) {
18890
19416
  for (const rt of KNOWN_RUNTIMES) {
18891
19417
  const marker = rt.markers.find((m) => {
18892
19418
  try {
18893
- return existsSync17(join23(cwd, m));
19419
+ return existsSync17(join24(cwd, m));
18894
19420
  } catch {
18895
19421
  return false;
18896
19422
  }
@@ -18970,10 +19496,10 @@ var init_runtimes = __esm({
18970
19496
  });
18971
19497
 
18972
19498
  // src/cli/onboard.ts
18973
- import { execFileSync as execFileSync12 } from "node:child_process";
18974
- import { lstatSync as lstatSync10, mkdtempSync as mkdtempSync6, readFileSync as readFileSync21, realpathSync as realpathSync10, rmSync as rmSync10 } from "node:fs";
19499
+ import { execFileSync as execFileSync13 } from "node:child_process";
19500
+ import { lstatSync as lstatSync10, mkdtempSync as mkdtempSync6, readFileSync as readFileSync22, realpathSync as realpathSync10, rmSync as rmSync11 } from "node:fs";
18975
19501
  import { tmpdir as tmpdir7 } from "node:os";
18976
- import { join as join24, relative as relative8, resolve as resolve13, sep as sep6 } from "node:path";
19502
+ import { join as join25, relative as relative8, resolve as resolve13, sep as sep6 } from "node:path";
18977
19503
  import { createInterface } from "node:readline";
18978
19504
  import { parseDocument } from "yaml";
18979
19505
  function readlineAsker(input, output) {
@@ -19017,9 +19543,9 @@ function platformLabel(platform) {
19017
19543
  if (platform === "linux") return "Linux";
19018
19544
  return platform;
19019
19545
  }
19020
- function git5(cwd, args) {
19546
+ function git6(cwd, args) {
19021
19547
  try {
19022
- return execFileSync12("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
19548
+ return execFileSync13("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
19023
19549
  } catch {
19024
19550
  return null;
19025
19551
  }
@@ -19038,13 +19564,13 @@ function explainVerdict(v, code2) {
19038
19564
  }
19039
19565
  }
19040
19566
  function writeVerifyCommand(cwd, command) {
19041
- const path = join24(cwd, POLICY_FILE);
19567
+ const path = join25(cwd, POLICY_FILE);
19042
19568
  const kind = writeTargetKind(path);
19043
19569
  if (kind === "symlink" || kind === "irregular") {
19044
19570
  return `${POLICY_FILE} is not a regular file; refusing to follow or replace a symlink or special file`;
19045
19571
  }
19046
19572
  const mode = existingMode(path, 420);
19047
- const original = kind === "file" ? readFileSync21(path, "utf8") : null;
19573
+ const original = kind === "file" ? readFileSync22(path, "utf8") : null;
19048
19574
  let next;
19049
19575
  try {
19050
19576
  const doc = parseDocument(original && original.trim() ? original : "version: 1\n");
@@ -19061,7 +19587,7 @@ function writeVerifyCommand(cwd, command) {
19061
19587
  if (loaded2.verify?.command !== command) throw new Error("verify.command did not round-trip");
19062
19588
  return null;
19063
19589
  } catch (e) {
19064
- if (original === null) rmSync10(path, { force: true });
19590
+ if (original === null) rmSync11(path, { force: true });
19065
19591
  else atomicReplaceFile(path, original, mode);
19066
19592
  return `the updated ${POLICY_FILE} does not load (${errorMessage(e)}); restored the previous file`;
19067
19593
  }
@@ -19096,7 +19622,7 @@ async function runOnboard(opts, io = {}) {
19096
19622
  rawErr("or run `tamperward init` for the deterministic wiring step only.");
19097
19623
  return 2;
19098
19624
  }
19099
- const rootText = git5(requestedCwd, ["rev-parse", "--show-toplevel"])?.trim() ?? "";
19625
+ const rootText = git6(requestedCwd, ["rev-parse", "--show-toplevel"])?.trim() ?? "";
19100
19626
  if (!rootText) {
19101
19627
  fail(requestedCwd + " is not inside a Git repository. Run `git init` first.");
19102
19628
  return 2;
@@ -19162,8 +19688,8 @@ async function runOnboard(opts, io = {}) {
19162
19688
  } else {
19163
19689
  status("LIMITED", platformLabel(platform) + ": check works here; local verify and `run` are unavailable. Use the container verifier for final verification.", "warn");
19164
19690
  }
19165
- const head = git5(cwd, ["rev-parse", "--verify", "--quiet", "HEAD^{commit}"])?.trim() ?? null;
19166
- const treeStatus = git5(cwd, ["status", "--porcelain", "--untracked-files=all"]) ?? "";
19691
+ const head = git6(cwd, ["rev-parse", "--verify", "--quiet", "HEAD^{commit}"])?.trim() ?? null;
19692
+ const treeStatus = git6(cwd, ["status", "--porcelain", "--untracked-files=all"]) ?? "";
19167
19693
  const dirty = treeStatus.split("\n").filter(Boolean).map((line2) => line2.slice(3).split(" -> ").at(-1) ?? "");
19168
19694
  if (!head) {
19169
19695
  const count = dirty.length;
@@ -19411,7 +19937,7 @@ function postureOf(outcome) {
19411
19937
  return "READY";
19412
19938
  }
19413
19939
  function runDemo(cwd, head, runners, out3) {
19414
- const files = (git5(cwd, ["ls-tree", "-r", "-z", head]) ?? "").split("\0").filter(Boolean).flatMap((row) => {
19940
+ const files = (git6(cwd, ["ls-tree", "-r", "-z", head]) ?? "").split("\0").filter(Boolean).flatMap((row) => {
19415
19941
  const tab = row.indexOf(" ");
19416
19942
  if (tab < 0) return [];
19417
19943
  const mode = row.slice(0, tab).split(" ")[0];
@@ -19420,7 +19946,7 @@ function runDemo(cwd, head, runners, out3) {
19420
19946
  });
19421
19947
  let target = null;
19422
19948
  for (const path of files) {
19423
- const before2 = git5(cwd, ["show", `${head}:${path}`]);
19949
+ const before2 = git6(cwd, ["show", `${head}:${path}`]);
19424
19950
  if (before2 === null || !JS_TEST_BLOCK.test(before2)) continue;
19425
19951
  target = { path, before: before2, after: before2.replace(JS_TEST_BLOCK, "$1$2.skip$3") };
19426
19952
  break;
@@ -19431,13 +19957,13 @@ function runDemo(cwd, head, runners, out3) {
19431
19957
  }
19432
19958
  const before = treeFingerprint(cwd);
19433
19959
  out3(`working tree fingerprint before: ${before.slice(0, 16)}`);
19434
- const wt = mkdtempSync6(join24(tmpdir7(), "tw-onboard-demo-"));
19960
+ const wt = mkdtempSync6(join25(tmpdir7(), "tw-onboard-demo-"));
19435
19961
  try {
19436
- if (git5(cwd, ["worktree", "add", "--detach", wt, head]) === null) {
19962
+ if (git6(cwd, ["worktree", "add", "--detach", wt, head]) === null) {
19437
19963
  out3("Demo skipped: could not create a temporary worktree (git worktree add failed).");
19438
19964
  return;
19439
19965
  }
19440
- const targetPath = join24(wt, target.path);
19966
+ const targetPath = join25(wt, target.path);
19441
19967
  const targetStat = lstatSync10(targetPath, { throwIfNoEntry: false });
19442
19968
  if (!targetStat || !targetStat.isFile() || targetStat.isSymbolicLink()) {
19443
19969
  out3("Demo skipped: the selected test is not a regular file in the disposable worktree.");
@@ -19466,9 +19992,9 @@ function runDemo(cwd, head, runners, out3) {
19466
19992
  out3("");
19467
19993
  out3(`check exited ${code2}: ${code2 === 1 ? "a blocking finding, which is what the hook, pre-commit and CI would all report" : "no blocking finding was reported"}.`);
19468
19994
  } finally {
19469
- git5(cwd, ["worktree", "remove", "--force", wt]);
19470
- git5(cwd, ["worktree", "prune"]);
19471
- rmSync10(wt, { recursive: true, force: true });
19995
+ git6(cwd, ["worktree", "remove", "--force", wt]);
19996
+ git6(cwd, ["worktree", "prune"]);
19997
+ rmSync11(wt, { recursive: true, force: true });
19472
19998
  }
19473
19999
  const after = treeFingerprint(cwd);
19474
20000
  out3(`working tree fingerprint after: ${after.slice(0, 16)}`);
@@ -19655,9 +20181,9 @@ var init_capture = __esm({
19655
20181
  });
19656
20182
 
19657
20183
  // src/research/manifest.ts
19658
- import { createHash as createHash13 } from "node:crypto";
19659
- import { readFileSync as readFileSync22 } from "node:fs";
19660
- import { dirname as dirname10, resolve as resolve15 } from "node:path";
20184
+ import { createHash as createHash14 } from "node:crypto";
20185
+ import { readFileSync as readFileSync23 } from "node:fs";
20186
+ import { dirname as dirname11, resolve as resolve15 } from "node:path";
19661
20187
  function taskFrom(raw, index, manifestDir) {
19662
20188
  const where = `task #${index + 1}`;
19663
20189
  if (!isRecord(raw)) throw new ResearchError(`task manifest: ${where} is not an object`);
@@ -19690,7 +20216,7 @@ function readManifest(path) {
19690
20216
  const abs = resolve15(path);
19691
20217
  let bytes;
19692
20218
  try {
19693
- bytes = readFileSync22(abs);
20219
+ bytes = readFileSync23(abs);
19694
20220
  } catch (e) {
19695
20221
  throw new ResearchError(`cannot read task manifest ${abs}: ${e instanceof Error ? e.message : String(e)}`);
19696
20222
  }
@@ -19707,13 +20233,13 @@ function readManifest(path) {
19707
20233
  if (!Array.isArray(raw.tasks) || raw.tasks.length === 0) {
19708
20234
  throw new ResearchError(`task manifest ${abs}: no tasks (tasks must be a non-empty array)`);
19709
20235
  }
19710
- const tasks = raw.tasks.map((t, i) => taskFrom(t, i, dirname10(abs)));
20236
+ const tasks = raw.tasks.map((t, i) => taskFrom(t, i, dirname11(abs)));
19711
20237
  const seen = /* @__PURE__ */ new Set();
19712
20238
  for (const t of tasks) {
19713
20239
  if (seen.has(t.id)) throw new ResearchError(`task manifest ${abs}: duplicate task id "${t.id}"`);
19714
20240
  seen.add(t.id);
19715
20241
  }
19716
- return { path: abs, sha256: createHash13("sha256").update(bytes).digest("hex"), tasks };
20242
+ return { path: abs, sha256: createHash14("sha256").update(bytes).digest("hex"), tasks };
19717
20243
  }
19718
20244
  var MANIFEST_VERSION;
19719
20245
  var init_manifest = __esm({
@@ -19987,22 +20513,22 @@ var init_record = __esm({
19987
20513
 
19988
20514
  // src/research/run.ts
19989
20515
  import { randomUUID as randomUUID2 } from "node:crypto";
19990
- import { execFileSync as execFileSync13 } from "node:child_process";
19991
- import { closeSync as closeSync3, existsSync as existsSync18, linkSync, mkdirSync as mkdirSync9, openSync as openSync3, readFileSync as readFileSync23, renameSync as renameSync4, rmSync as rmSync11, unlinkSync, writeFileSync as writeFileSync11, writeSync } from "node:fs";
20516
+ import { execFileSync as execFileSync14 } from "node:child_process";
20517
+ import { closeSync as closeSync3, existsSync as existsSync18, linkSync, mkdirSync as mkdirSync10, openSync as openSync3, readFileSync as readFileSync24, renameSync as renameSync4, rmSync as rmSync12, unlinkSync, writeFileSync as writeFileSync12, writeSync } from "node:fs";
19992
20518
  import { hostname } from "node:os";
19993
- import { join as join25, resolve as resolve16 } from "node:path";
19994
- function git6(args, cwd) {
19995
- return execFileSync13("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
20519
+ import { join as join26, resolve as resolve16 } from "node:path";
20520
+ function git7(args, cwd) {
20521
+ return execFileSync14("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
19996
20522
  }
19997
20523
  function pairRecordPath(ledger, task, pair) {
19998
- return join25(ledger, "pairs", `${task}--${pair}.json`);
20524
+ return join26(ledger, "pairs", `${task}--${pair}.json`);
19999
20525
  }
20000
20526
  function researchLockPath(ledger) {
20001
- return join25(ledger, "run.lock");
20527
+ return join26(ledger, "run.lock");
20002
20528
  }
20003
20529
  function readLockOwner(path) {
20004
20530
  try {
20005
- const parsed = JSON.parse(readFileSync23(path, "utf8"));
20531
+ const parsed = JSON.parse(readFileSync24(path, "utf8"));
20006
20532
  if (!isRecord(parsed) || typeof parsed.pid !== "number" || !Number.isInteger(parsed.pid) || parsed.pid <= 0) return null;
20007
20533
  if (typeof parsed.host !== "string" || typeof parsed.started_at !== "string" || typeof parsed.token !== "string") return null;
20008
20534
  return { pid: parsed.pid, host: parsed.host, started_at: parsed.started_at, token: parsed.token };
@@ -20023,7 +20549,7 @@ function lockHolder(path) {
20023
20549
  return owner ? `pid ${owner.pid} on ${owner.host}, started ${owner.started_at}` : "an unreadable lock file";
20024
20550
  }
20025
20551
  function acquireResearchLock(ledger, breakStaleLock = false) {
20026
- mkdirSync9(ledger, { recursive: true });
20552
+ mkdirSync10(ledger, { recursive: true });
20027
20553
  const path = researchLockPath(ledger);
20028
20554
  if (breakStaleLock && existsSync18(path)) {
20029
20555
  const owner2 = readLockOwner(path);
@@ -20087,7 +20613,7 @@ function acquireResearchLock(ledger, breakStaleLock = false) {
20087
20613
  if (released) return;
20088
20614
  released = true;
20089
20615
  try {
20090
- if (readFileSync23(path, "utf8") === text) unlinkSync(path);
20616
+ if (readFileSync24(path, "utf8") === text) unlinkSync(path);
20091
20617
  } catch {
20092
20618
  }
20093
20619
  }
@@ -20096,7 +20622,7 @@ function acquireResearchLock(ledger, breakStaleLock = false) {
20096
20622
  function resumableRecord(path, expected) {
20097
20623
  let raw;
20098
20624
  try {
20099
- raw = JSON.parse(readFileSync23(path, "utf8"));
20625
+ raw = JSON.parse(readFileSync24(path, "utf8"));
20100
20626
  } catch (e) {
20101
20627
  throw new ResearchError(`ledger record ${path} is malformed (${errorMessage(e).split("\n")[0]}); remove it to regenerate the pair, or use a new --out`);
20102
20628
  }
@@ -20133,28 +20659,28 @@ function resumableRecord(path, expected) {
20133
20659
  }
20134
20660
  function writeRecordAtomically(path, text) {
20135
20661
  const tmp = `${path}.${process.pid}.tmp`;
20136
- writeFileSync11(tmp, text);
20662
+ writeFileSync12(tmp, text);
20137
20663
  renameSync4(tmp, path);
20138
20664
  }
20139
20665
  function cloneArgs(repo, ws3) {
20140
20666
  return ["-c", "protocol.ext.allow=never", "clone", "-q", "--no-hardlinks", "--", repo, ws3];
20141
20667
  }
20142
20668
  function freshWorkspace(ledger, task, pair, arm, sourceBase) {
20143
- const ws3 = join25(ledger, "workspaces", `${task.id}--${pair}--${arm}`);
20144
- rmSync11(ws3, { recursive: true, force: true });
20145
- mkdirSync9(join25(ledger, "workspaces"), { recursive: true });
20669
+ const ws3 = join26(ledger, "workspaces", `${task.id}--${pair}--${arm}`);
20670
+ rmSync12(ws3, { recursive: true, force: true });
20671
+ mkdirSync10(join26(ledger, "workspaces"), { recursive: true });
20146
20672
  try {
20147
- execFileSync13("git", cloneArgs(task.repo, ws3), { stdio: ["ignore", "ignore", "pipe"], encoding: "utf8" });
20673
+ execFileSync14("git", cloneArgs(task.repo, ws3), { stdio: ["ignore", "ignore", "pipe"], encoding: "utf8" });
20148
20674
  } catch (e) {
20149
20675
  throw new ResearchError(`task "${task.id}": cannot clone ${task.repo}: ${errorMessage(e).split("\n")[0]}`);
20150
20676
  }
20151
- git6(["config", "user.name", "tamperward-research"], ws3);
20152
- git6(["config", "user.email", "research@tamperward.invalid"], ws3);
20677
+ git7(["config", "user.name", "tamperward-research"], ws3);
20678
+ git7(["config", "user.email", "research@tamperward.invalid"], ws3);
20153
20679
  const requestedBase = sourceBase ?? task.base;
20154
20680
  const candidates = sourceBase !== void 0 ? [sourceBase] : task.base === "HEAD" ? ["HEAD"] : [task.base, `origin/${task.base}`];
20155
20681
  for (const rev2 of candidates) {
20156
20682
  try {
20157
- git6(["checkout", "-q", "--detach", rev2], ws3);
20683
+ git7(["checkout", "-q", "--detach", rev2], ws3);
20158
20684
  return ws3;
20159
20685
  } catch {
20160
20686
  }
@@ -20308,15 +20834,15 @@ function observeOutcome(ws3, base, head, task, trusted) {
20308
20834
  }
20309
20835
  function runTrajectory(ledger, task, pair, arm, adapter, opts, sourceBase) {
20310
20836
  const ws3 = freshWorkspace(ledger, task, pair, arm, sourceBase);
20311
- const prep = { id: task.id, prompt: task.prompt, cwd: ws3, base: git6(["rev-parse", "HEAD"], ws3), arm, model: opts.model };
20837
+ const prep = { id: task.id, prompt: task.prompt, cwd: ws3, base: git7(["rev-parse", "HEAD"], ws3), arm, model: opts.model };
20312
20838
  if (arm === "gated" && adapter.prepareGated) {
20313
20839
  captureStdout(() => adapter.prepareGated?.(prep));
20314
- if (git6(["status", "--porcelain"], ws3)) {
20315
- git6(["add", "-A"], ws3);
20316
- git6(["commit", "-qm", "tamperward research: wire enforcement (gated arm)"], ws3);
20840
+ if (git7(["status", "--porcelain"], ws3)) {
20841
+ git7(["add", "-A"], ws3);
20842
+ git7(["commit", "-qm", "tamperward research: wire enforcement (gated arm)"], ws3);
20317
20843
  }
20318
20844
  }
20319
- const base = git6(["rev-parse", "HEAD"], ws3);
20845
+ const base = git7(["rev-parse", "HEAD"], ws3);
20320
20846
  const launch2 = adapter.launch({ ...prep, base });
20321
20847
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
20322
20848
  let agent;
@@ -20329,7 +20855,7 @@ function runTrajectory(ledger, task, pair, arm, adapter, opts, sourceBase) {
20329
20855
  agent = runUngated(ws3, launch2.argv, launch2.env, opts.agentBudget);
20330
20856
  }
20331
20857
  const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
20332
- const head = git6(["rev-parse", "HEAD"], ws3);
20858
+ const head = git7(["rev-parse", "HEAD"], ws3);
20333
20859
  const trustedSurface = trustedProtectedOnly(base, ws3);
20334
20860
  const fingerprintBefore = treeFingerprint(ws3, trustedSurface.protectedOnly);
20335
20861
  const observed = observeOutcome(ws3, base, head, task, trustedSurface.trusted);
@@ -20391,7 +20917,7 @@ function runResearch(opts) {
20391
20917
  }
20392
20918
  try {
20393
20919
  const pairs = opts.pairs ?? 1;
20394
- mkdirSync9(join25(ledger, "pairs"), { recursive: true });
20920
+ mkdirSync10(join26(ledger, "pairs"), { recursive: true });
20395
20921
  for (const task of tasks) {
20396
20922
  const existingRecords = /* @__PURE__ */ new Map();
20397
20923
  let sourceBase = null;
@@ -20518,10 +21044,10 @@ var init_run2 = __esm({
20518
21044
  });
20519
21045
 
20520
21046
  // src/research/summarize.ts
20521
- import { readdirSync as readdirSync7, readFileSync as readFileSync24 } from "node:fs";
20522
- import { join as join26, resolve as resolve17 } from "node:path";
21047
+ import { readdirSync as readdirSync7, readFileSync as readFileSync25 } from "node:fs";
21048
+ import { join as join27, resolve as resolve17 } from "node:path";
20523
21049
  function readLedger2(dir) {
20524
- const pairsDir = join26(resolve17(dir), "pairs");
21050
+ const pairsDir = join27(resolve17(dir), "pairs");
20525
21051
  let names;
20526
21052
  try {
20527
21053
  names = readdirSync7(pairsDir).filter((n2) => n2.endsWith(".json")).sort();
@@ -20530,10 +21056,10 @@ function readLedger2(dir) {
20530
21056
  }
20531
21057
  if (names.length === 0) throw new ResearchError(`ledger ${pairsDir} holds no pair records`);
20532
21058
  return names.map((n2) => {
20533
- const path = join26(pairsDir, n2);
21059
+ const path = join27(pairsDir, n2);
20534
21060
  let raw;
20535
21061
  try {
20536
- raw = JSON.parse(readFileSync24(path, "utf8"));
21062
+ raw = JSON.parse(readFileSync25(path, "utf8"));
20537
21063
  } catch (e) {
20538
21064
  throw new ResearchError(`ledger record ${path} is not valid JSON: ${errorMessage(e)}`);
20539
21065
  }
@@ -20664,9 +21190,9 @@ var init_summarize = __esm({
20664
21190
  });
20665
21191
 
20666
21192
  // src/research/init.ts
20667
- import { createHash as createHash14 } from "node:crypto";
20668
- import { existsSync as existsSync19, mkdirSync as mkdirSync10, readFileSync as readFileSync25, writeFileSync as writeFileSync12 } from "node:fs";
20669
- import { basename as basename5, dirname as dirname11, resolve as resolve18 } from "node:path";
21193
+ import { createHash as createHash15 } from "node:crypto";
21194
+ import { existsSync as existsSync19, mkdirSync as mkdirSync11, readFileSync as readFileSync26, writeFileSync as writeFileSync13 } from "node:fs";
21195
+ import { basename as basename5, dirname as dirname12, resolve as resolve18 } from "node:path";
20670
21196
  function idFrom(repo) {
20671
21197
  const clean = repo.replace(/[\\/]$/, "").split(/[\\/]/).pop() || "task";
20672
21198
  return (basename5(clean, ".git").replace(/[^A-Za-z0-9._-]+/g, "-") || "task").slice(0, 80);
@@ -20694,8 +21220,8 @@ function createResearchManifest(opts) {
20694
21220
  }]
20695
21221
  };
20696
21222
  const text = JSON.stringify(doc, null, 2) + "\n";
20697
- mkdirSync10(dirname11(out3), { recursive: true });
20698
- writeFileSync12(out3, text, { flag: "wx", mode: 384 });
21223
+ mkdirSync11(dirname12(out3), { recursive: true });
21224
+ writeFileSync13(out3, text, { flag: "wx", mode: 384 });
20699
21225
  const parsed = readManifest(out3);
20700
21226
  if (parsed.tasks.length !== 1 || parsed.tasks[0].id !== id) throw new ResearchError(`research init wrote an invalid manifest ${out3}`);
20701
21227
  return out3;
@@ -20717,7 +21243,7 @@ function runResearchInit(opts) {
20717
21243
  }
20718
21244
  }
20719
21245
  function requireHash(path) {
20720
- return createHash14("sha256").update(readFileSync25(path)).digest("hex");
21246
+ return createHash15("sha256").update(readFileSync26(path)).digest("hex");
20721
21247
  }
20722
21248
  var init_init2 = __esm({
20723
21249
  "src/research/init.ts"() {
@@ -20782,10 +21308,10 @@ var init_report2 = __esm({
20782
21308
  });
20783
21309
 
20784
21310
  // src/research/bundle.ts
20785
- import { createHash as createHash15 } from "node:crypto";
21311
+ import { createHash as createHash16 } from "node:crypto";
20786
21312
  import { gzipSync, gunzipSync } from "node:zlib";
20787
- import { existsSync as existsSync20, mkdirSync as mkdirSync11, readdirSync as readdirSync8, readFileSync as readFileSync26, statSync as statSync6, writeFileSync as writeFileSync13 } from "node:fs";
20788
- import { dirname as dirname12, join as join27, resolve as resolve19 } from "node:path";
21313
+ import { existsSync as existsSync20, mkdirSync as mkdirSync12, readdirSync as readdirSync8, readFileSync as readFileSync27, statSync as statSync6, writeFileSync as writeFileSync14 } from "node:fs";
21314
+ import { dirname as dirname13, join as join28, resolve as resolve19 } from "node:path";
20789
21315
  function octal(value, width) {
20790
21316
  return value.toString(8).padStart(width - 1, "0") + "\0";
20791
21317
  }
@@ -20831,14 +21357,14 @@ function parseTar(bytes) {
20831
21357
  }
20832
21358
  function walkLedger(ledger) {
20833
21359
  const entries = [];
20834
- const pairs = join27(ledger, "pairs");
21360
+ const pairs = join28(ledger, "pairs");
20835
21361
  if (!existsSync20(pairs) || !statSync6(pairs).isDirectory()) throw new ResearchError(`ledger ${ledger} has no pairs directory`);
20836
21362
  const names = readdirSync8(pairs).filter((n2) => n2.endsWith(".json")).sort();
20837
21363
  if (names.length === 0) throw new ResearchError(`ledger ${ledger} holds no pair records`);
20838
21364
  for (const name of names) {
20839
- const path = join27(pairs, name);
21365
+ const path = join28(pairs, name);
20840
21366
  if (!statSync6(path).isFile()) continue;
20841
- entries.push({ name: `ledger/pairs/${name}`, bytes: readFileSync26(path) });
21367
+ entries.push({ name: `ledger/pairs/${name}`, bytes: readFileSync27(path) });
20842
21368
  }
20843
21369
  return entries;
20844
21370
  }
@@ -20848,7 +21374,7 @@ function jsonBytes(value) {
20848
21374
  function validateResearchBundle(path) {
20849
21375
  let files;
20850
21376
  try {
20851
- files = parseTar(gunzipSync(readFileSync26(resolve19(path))));
21377
+ files = parseTar(gunzipSync(readFileSync27(resolve19(path))));
20852
21378
  } catch (e) {
20853
21379
  throw new ResearchError(`cannot read research bundle ${path}: ${e instanceof Error ? e.message : String(e)}`);
20854
21380
  }
@@ -20871,7 +21397,7 @@ function validateResearchBundle(path) {
20871
21397
  if (p.manifest_included !== Boolean(manifest) || p.prompts_included !== Boolean(manifest)) {
20872
21398
  throw new ResearchError("research bundle manifest inclusion metadata is inconsistent");
20873
21399
  }
20874
- if (manifest && createHash15("sha256").update(manifest).digest("hex") !== p.manifest_sha256) {
21400
+ if (manifest && createHash16("sha256").update(manifest).digest("hex") !== p.manifest_sha256) {
20875
21401
  throw new ResearchError("research bundle manifest does not match its recorded sha256");
20876
21402
  }
20877
21403
  const parsedSummary = summary;
@@ -20902,8 +21428,8 @@ function createResearchBundle(opts) {
20902
21428
  const summary = summarizeLedger(ledger);
20903
21429
  const entries = walkLedger(ledger);
20904
21430
  if (opts.manifest) {
20905
- const manifest = readFileSync26(resolve19(opts.manifest));
20906
- const sha3 = createHash15("sha256").update(manifest).digest("hex");
21431
+ const manifest = readFileSync27(resolve19(opts.manifest));
21432
+ const sha3 = createHash16("sha256").update(manifest).digest("hex");
20907
21433
  if (sha3 !== summary.manifest_sha256) throw new ResearchError(`manifest sha256 ${sha3} does not match ledger ${summary.manifest_sha256}`);
20908
21434
  entries.push({ name: "manifest.json", bytes: manifest });
20909
21435
  }
@@ -20925,8 +21451,8 @@ function createResearchBundle(opts) {
20925
21451
  entries.push({ name: "report.txt", bytes: Buffer.from(renderResearchReport(summary), "utf8") });
20926
21452
  entries.push({ name: "provenance.json", bytes: jsonBytes(provenance) });
20927
21453
  const out3 = resolve19(opts.out);
20928
- mkdirSync11(dirname12(out3), { recursive: true });
20929
- writeFileSync13(out3, gzipSync(makeTar(entries)), { flag: "wx", mode: 384 });
21454
+ mkdirSync12(dirname13(out3), { recursive: true });
21455
+ writeFileSync14(out3, gzipSync(makeTar(entries)), { flag: "wx", mode: 384 });
20930
21456
  return out3;
20931
21457
  }
20932
21458
  function runResearchBundle(opts) {
@@ -21145,6 +21671,15 @@ function parseDoctor(args) {
21145
21671
  }
21146
21672
  return o;
21147
21673
  }
21674
+ function parseStatus(args) {
21675
+ const o = {};
21676
+ for (let i = 0; i < args.length; i++) {
21677
+ const a = args[i];
21678
+ if (a === "--json") o.json = true;
21679
+ else if (a === "--cwd") o.cwd = args[++i];
21680
+ }
21681
+ return o;
21682
+ }
21148
21683
  function parseStats(args) {
21149
21684
  const o = {};
21150
21685
  for (let i = 0; i < args.length; i++) {
@@ -21353,6 +21888,12 @@ function validateCliArgs(cmd, args) {
21353
21888
  }
21354
21889
  }).error;
21355
21890
  }
21891
+ if (cmd === "status") {
21892
+ return validateFlatArgs(args, {
21893
+ flags: ["--json"],
21894
+ values: { "--cwd": "string" }
21895
+ }).error;
21896
+ }
21356
21897
  if (cmd === "onboard") {
21357
21898
  const parsed = validateFlatArgs(args, {
21358
21899
  flags: ["--yes", "--skip-demo", "--demo", "--no-github"],
@@ -21598,6 +22139,16 @@ Formats:
21598
22139
  every tool the gate must see.
21599
22140
  --force-workflow replaces a workflow it
21600
22141
  did not write, or one you have edited.
22142
+ tamperward status [--json] [--cwd D] verification posture in three distinct
22143
+ lanes \u2014 Authority (repository/final
22144
+ adjudication), Intervention (runtime
22145
+ steering), and Verification: whether the
22146
+ last successful verify still applies to
22147
+ the EXACT current state (CURRENT / STALE /
22148
+ VERIFYING / BROKEN / UNVERIFIED). --json
22149
+ emits the versioned status document for
22150
+ editors, CI and dashboards. Local CURRENT
22151
+ is posture, never repository/CI authority.
21601
22152
  tamperward doctor [--base R] report installation + authority posture
21602
22153
  [--workflow F] [--cwd D] and validate the CI verifier's outer-time
21603
22154
  [--json] envelope against the trusted policy.
@@ -21650,6 +22201,8 @@ function main(argv) {
21650
22201
  return runDoctor(parseDoctor(rest));
21651
22202
  case "stats":
21652
22203
  return runStats(parseStats(rest));
22204
+ case "status":
22205
+ return runStatus(parseStatus(rest));
21653
22206
  case "onboard":
21654
22207
  return runOnboard(parseOnboard(rest));
21655
22208
  case "watch":
@@ -21705,6 +22258,7 @@ var init_main = __esm({
21705
22258
  init_allow();
21706
22259
  init_init();
21707
22260
  init_doctor();
22261
+ init_status2();
21708
22262
  init_verify();
21709
22263
  init_trace_verify();
21710
22264
  init_run();
@@ -21719,7 +22273,7 @@ var init_main = __esm({
21719
22273
  // src/cli/index.ts
21720
22274
  init_hook_client();
21721
22275
  init_exit();
21722
- import { readFileSync as readFileSync27 } from "node:fs";
22276
+ import { readFileSync as readFileSync28 } from "node:fs";
21723
22277
  function loadMain() {
21724
22278
  return Promise.resolve().then(() => (init_main(), main_exports));
21725
22279
  }
@@ -21732,7 +22286,7 @@ async function launch(argv) {
21732
22286
  if (kind && hookServiceEnabled()) {
21733
22287
  let raw = null;
21734
22288
  try {
21735
- raw = readFileSync27(0, "utf8");
22289
+ raw = readFileSync28(0, "utf8");
21736
22290
  } catch {
21737
22291
  raw = null;
21738
22292
  }