tamperward 2.39.11 → 2.39.12

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 +124 -63
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -788,9 +788,23 @@ function readStateFile(abs) {
788
788
  if (e.kind === "absent") return null;
789
789
  if (e.kind === "file") return textOf2(e) ?? "";
790
790
  throw new StateFileError(
791
- `${abs} ${notARegularFile(e)}; the gate reads its session state only as a regular file \u2014 remove what stands there so the state can be re-established`
791
+ `${abs} ${notARegularFile(e)}; the gate reads its state files only as regular files \u2014 remove what stands there so the state can be re-established`
792
792
  );
793
793
  }
794
+ function stateDirectory(gitDir3) {
795
+ const dir = join3(gitDir3, "tamperward");
796
+ const st = lstatSync2(dir, { throwIfNoEntry: false });
797
+ if (st === void 0 || st.isDirectory()) return dir;
798
+ if (st.isSymbolicLink()) {
799
+ let target = "";
800
+ try {
801
+ target = readlinkSync(dir);
802
+ } catch {
803
+ }
804
+ throw new StateFileError(`${dir} is a symbolic link to ${escapeControl(target)}${STATE_DIR_REFUSED}`);
805
+ }
806
+ throw new StateFileError(`${dir} is a ${st.isFile() ? "regular file" : irregularName(st)}${STATE_DIR_REFUSED}`);
807
+ }
794
808
  function multiplyLinked(st) {
795
809
  return st.nlink === 1 ? null : `has ${st.nlink} names \u2014 a hard link is the same file under another name, the session marker's or the policy's, say`;
796
810
  }
@@ -861,7 +875,7 @@ function unjudgeableProtected(cwd, policy, changes) {
861
875
  }
862
876
  return out3;
863
877
  }
864
- var READ_CAP, NO_META, O_READ, StateFileError, O_APPEND_REGULAR, APPEND_REFUSED;
878
+ var READ_CAP, NO_META, O_READ, StateFileError, STATE_DIR_REFUSED, O_APPEND_REGULAR, APPEND_REFUSED;
865
879
  var init_disk = __esm({
866
880
  "src/disk.ts"() {
867
881
  "use strict";
@@ -872,6 +886,7 @@ var init_disk = __esm({
872
886
  O_READ = constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0) | (constants.O_NONBLOCK ?? 0);
873
887
  StateFileError = class extends Error {
874
888
  };
889
+ STATE_DIR_REFUSED = "; the gate keeps its state only in a directory of its own under the git directory \u2014 replace what stands there with a directory, or remove it";
875
890
  O_APPEND_REGULAR = constants.O_WRONLY | constants.O_APPEND | constants.O_CREAT | (constants.O_NOFOLLOW ?? 0) | (constants.O_NONBLOCK ?? 0);
876
891
  APPEND_REFUSED = "; the gate appends its records only to a regular file with one name \u2014 remove what stands there so the record can be kept";
877
892
  }
@@ -11614,7 +11629,13 @@ function writeBaseline(p, sha3) {
11614
11629
  function baselinePath(cwd, sessionId) {
11615
11630
  const gd = gitDir(cwd);
11616
11631
  if (!gd) return null;
11617
- return join10(gd, "tamperward", `session-${sessionId.replace(UNSAFE, "")}`);
11632
+ const name = `session-${sessionId.replace(UNSAFE, "")}`;
11633
+ try {
11634
+ return join10(stateDirectory(gd), name);
11635
+ } catch (e) {
11636
+ if (e instanceof StateFileError) throw new BaselineWriteError(join10(gd, "tamperward", name), e);
11637
+ throw e;
11638
+ }
11618
11639
  }
11619
11640
  function turnBaseline(cwd, sessionId) {
11620
11641
  if (!sessionId) return null;
@@ -11681,7 +11702,7 @@ function statePath(cwd, sessionId, kind) {
11681
11702
  if (!sessionId) return null;
11682
11703
  const gd = gitDir2(cwd);
11683
11704
  if (!gd) return null;
11684
- const dir = join11(gd, "tamperward");
11705
+ const dir = stateDirectory(gd);
11685
11706
  return join11(dir, `${kind}-${sessionId.replace(UNSAFE2, "")}.json`);
11686
11707
  }
11687
11708
  function ptreePath(cwd, sessionId) {
@@ -11867,7 +11888,7 @@ function snap(abs) {
11867
11888
  }
11868
11889
  function defaultEventLog(cwd) {
11869
11890
  if (process.env.TAMPERWARD_FSEVENTS) return process.env.TAMPERWARD_FSEVENTS;
11870
- return join12(gitDir(cwd) ?? join12(cwd, ".git"), "tamperward", "fsevents.jsonl");
11891
+ return join12(stateDirectory(gitDir(cwd) ?? join12(cwd, ".git")), "fsevents.jsonl");
11871
11892
  }
11872
11893
  function watcherHealthPath(log) {
11873
11894
  return `${log}.health.json`;
@@ -12425,19 +12446,19 @@ function configuredAuditPath(cwd, env = process.env) {
12425
12446
  if (!value) return null;
12426
12447
  if (value === "auto") {
12427
12448
  const ctx = repoContext(cwd);
12428
- return ctx ? join13(ctx.gitDir, "tamperward", "audit.jsonl") : null;
12449
+ return ctx ? join13(stateDirectory(ctx.gitDir), "audit.jsonl") : null;
12429
12450
  }
12430
12451
  return isAbsolute8(value) ? value : resolve4(cwd, value);
12431
12452
  }
12432
12453
  function defaultAuditPath(cwd) {
12433
12454
  const ctx = repoContext(cwd);
12434
- return ctx ? join13(ctx.gitDir, "tamperward", "audit.jsonl") : null;
12455
+ return ctx ? join13(stateDirectory(ctx.gitDir), "audit.jsonl") : null;
12435
12456
  }
12436
12457
  function recordAuditFindings(findings, context) {
12437
12458
  if (findings.length === 0) return;
12438
- const path = configuredAuditPath(context.cwd);
12439
- if (!path) return;
12440
12459
  try {
12460
+ const path = configuredAuditPath(context.cwd);
12461
+ if (!path) return;
12441
12462
  mkdirSync5(dirname4(path), { recursive: true });
12442
12463
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
12443
12464
  const session = sessionHash(context.sessionId);
@@ -15950,7 +15971,7 @@ var init_verifier_backend = __esm({
15950
15971
  // src/verification-state.ts
15951
15972
  import { execFileSync as execFileSync8 } from "node:child_process";
15952
15973
  import { createHash as createHash12 } from "node:crypto";
15953
- import { mkdirSync as mkdirSync8, readFileSync as readFileSync11, rmSync as rmSync7, writeFileSync as writeFileSync8 } from "node:fs";
15974
+ import { mkdirSync as mkdirSync8, readFileSync as readFileSync11, rmSync as rmSync7 } from "node:fs";
15954
15975
  import { dirname as dirname8, join as join19 } from "node:path";
15955
15976
  function git2(args, cwd) {
15956
15977
  return execFileSync8("git", args, {
@@ -16067,7 +16088,7 @@ function computeBinding(cwd, inputs, precomputed) {
16067
16088
  }
16068
16089
  function stateDir(cwd) {
16069
16090
  const ctx = repoContext(cwd);
16070
- return ctx ? join19(ctx.gitDir, "tamperward") : null;
16091
+ return ctx ? stateDirectory(ctx.gitDir) : null;
16071
16092
  }
16072
16093
  function verificationRecordPath(cwd) {
16073
16094
  const dir = stateDir(cwd);
@@ -16094,11 +16115,17 @@ function recordVerification(cwd, inputs, treeFingerprintValue) {
16094
16115
  binding
16095
16116
  };
16096
16117
  mkdirSync8(dirname8(path), { recursive: true });
16097
- writeFileSync8(path, JSON.stringify(record) + "\n");
16118
+ atomicReplaceFile(path, JSON.stringify(record) + "\n", 438);
16098
16119
  return true;
16099
16120
  }
16100
16121
  function invalidateVerificationRecordIfCurrent(cwd) {
16101
- const record = readVerificationRecord(cwd);
16122
+ let record;
16123
+ try {
16124
+ record = readVerificationRecord(cwd);
16125
+ } catch (e) {
16126
+ if (e instanceof StateFileError) return false;
16127
+ throw e;
16128
+ }
16102
16129
  if (!record) return false;
16103
16130
  let live;
16104
16131
  try {
@@ -16160,12 +16187,8 @@ function parseVerificationRecord(value) {
16160
16187
  function readVerificationRecord(cwd) {
16161
16188
  const path = verificationRecordPath(cwd);
16162
16189
  if (!path) return null;
16163
- let raw;
16164
- try {
16165
- raw = readFileSync11(path, "utf8");
16166
- } catch {
16167
- return null;
16168
- }
16190
+ const raw = readStateFile(path);
16191
+ if (raw === null) return null;
16169
16192
  let parsed;
16170
16193
  try {
16171
16194
  parsed = JSON.parse(raw);
@@ -16183,25 +16206,25 @@ function pidAlive3(pid) {
16183
16206
  }
16184
16207
  }
16185
16208
  function beginVerifying(cwd) {
16186
- const path = verifyingMarkerPath(cwd);
16187
- if (!path) return false;
16188
16209
  try {
16210
+ const path = verifyingMarkerPath(cwd);
16211
+ if (!path) return false;
16189
16212
  const marker = {
16190
16213
  schema_version: VERIFICATION_STATE_SCHEMA_VERSION,
16191
16214
  pid: process.pid,
16192
16215
  started_at: (/* @__PURE__ */ new Date()).toISOString()
16193
16216
  };
16194
16217
  mkdirSync8(dirname8(path), { recursive: true });
16195
- writeFileSync8(path, JSON.stringify(marker) + "\n");
16218
+ atomicReplaceFile(path, JSON.stringify(marker) + "\n", 438);
16196
16219
  return true;
16197
16220
  } catch {
16198
16221
  return false;
16199
16222
  }
16200
16223
  }
16201
16224
  function endVerifying(cwd) {
16202
- const path = verifyingMarkerPath(cwd);
16203
- if (!path) return;
16204
16225
  try {
16226
+ const path = verifyingMarkerPath(cwd);
16227
+ if (!path) return;
16205
16228
  rmSync7(path, { force: true });
16206
16229
  } catch {
16207
16230
  }
@@ -16209,9 +16232,11 @@ function endVerifying(cwd) {
16209
16232
  function readVerifyingMarker(cwd) {
16210
16233
  const path = verifyingMarkerPath(cwd);
16211
16234
  if (!path) return null;
16235
+ const raw = readStateFile(path);
16236
+ if (raw === null) return null;
16212
16237
  let parsed;
16213
16238
  try {
16214
- parsed = JSON.parse(readFileSync11(path, "utf8"));
16239
+ parsed = JSON.parse(raw);
16215
16240
  } catch {
16216
16241
  return null;
16217
16242
  }
@@ -16238,10 +16263,22 @@ function environmentInputDivergences(recorded, live) {
16238
16263
  return ENVIRONMENT_INPUTS.filter((input) => recorded[input] !== live[input]);
16239
16264
  }
16240
16265
  function evaluateVerificationState(cwd) {
16241
- if (readVerifyingMarker(cwd)) {
16266
+ let marker;
16267
+ let record;
16268
+ try {
16269
+ marker = readVerifyingMarker(cwd);
16270
+ record = marker ? null : readVerificationRecord(cwd);
16271
+ } catch (e) {
16272
+ if (!(e instanceof StateFileError)) throw e;
16273
+ return {
16274
+ state: "UNVERIFIED",
16275
+ reason: "the verification store cannot be read: something other than a regular file stands at a record path",
16276
+ detail: e.message
16277
+ };
16278
+ }
16279
+ if (marker) {
16242
16280
  return { state: "VERIFYING", reason: "a verification is in progress" };
16243
16281
  }
16244
- const record = readVerificationRecord(cwd);
16245
16282
  if (!record) {
16246
16283
  return {
16247
16284
  state: "UNVERIFIED",
@@ -16296,6 +16333,8 @@ var init_verification_state = __esm({
16296
16333
  init_repo_context();
16297
16334
  init_wiring();
16298
16335
  init_dependency_env();
16336
+ init_disk();
16337
+ init_safe_write();
16299
16338
  VERIFICATION_STATE_SCHEMA_VERSION = 1;
16300
16339
  BINDING_INPUTS = [
16301
16340
  "tree",
@@ -16345,7 +16384,7 @@ var init_verification_state = __esm({
16345
16384
 
16346
16385
  // src/verification-receipt.ts
16347
16386
  import { createHash as createHash13 } from "node:crypto";
16348
- import { mkdirSync as mkdirSync9, rmSync as rmSync8, writeFileSync as writeFileSync9 } from "node:fs";
16387
+ import { mkdirSync as mkdirSync9, rmSync as rmSync8 } from "node:fs";
16349
16388
  import { dirname as dirname9, join as join20 } from "node:path";
16350
16389
  function isRecord5(v) {
16351
16390
  return typeof v === "object" && v !== null && !Array.isArray(v);
@@ -16385,23 +16424,23 @@ function receiptFromRecord(record) {
16385
16424
  }
16386
16425
  function receiptPath(cwd) {
16387
16426
  const ctx = repoContext(cwd);
16388
- return ctx ? join20(ctx.gitDir, "tamperward", "verification-receipt.json") : null;
16427
+ return ctx ? join20(stateDirectory(ctx.gitDir), "verification-receipt.json") : null;
16389
16428
  }
16390
16429
  function storeReceipt(cwd, receipt) {
16391
- const path = receiptPath(cwd);
16392
- if (!path) return false;
16393
16430
  try {
16431
+ const path = receiptPath(cwd);
16432
+ if (!path) return false;
16394
16433
  mkdirSync9(dirname9(path), { recursive: true });
16395
- writeFileSync9(path, JSON.stringify(receipt) + "\n");
16434
+ atomicReplaceFile(path, JSON.stringify(receipt) + "\n", 438);
16396
16435
  return true;
16397
16436
  } catch {
16398
16437
  return false;
16399
16438
  }
16400
16439
  }
16401
16440
  function removeStoredReceipt(cwd) {
16402
- const path = receiptPath(cwd);
16403
- if (!path) return;
16404
16441
  try {
16442
+ const path = receiptPath(cwd);
16443
+ if (!path) return;
16405
16444
  rmSync8(path, { force: true });
16406
16445
  } catch {
16407
16446
  }
@@ -16563,6 +16602,8 @@ var RECEIPT_SCHEMA_VERSION, RECEIPT_STAGE_RESULTS, RECEIPT_INTEGRITY_RESULTS;
16563
16602
  var init_verification_receipt = __esm({
16564
16603
  "src/verification-receipt.ts"() {
16565
16604
  "use strict";
16605
+ init_disk();
16606
+ init_safe_write();
16566
16607
  init_repo_context();
16567
16608
  init_verification_state();
16568
16609
  init_machine_output();
@@ -16575,7 +16616,7 @@ var init_verification_receipt = __esm({
16575
16616
  // src/cli/verify.ts
16576
16617
  import { execFileSync as execFileSync9, spawnSync as spawnSync3 } from "node:child_process";
16577
16618
  import { createHash as createHash14 } from "node:crypto";
16578
- import { chmodSync as chmodSync2, cpSync, lstatSync as lstatSync10, mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync3, readFileSync as readFileSync12, readlinkSync as readlinkSync3, rmSync as rmSync9, symlinkSync, writeFileSync as writeFileSync10, existsSync as existsSync10 } from "node:fs";
16619
+ import { chmodSync as chmodSync2, cpSync, lstatSync as lstatSync10, mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync3, readFileSync as readFileSync12, readlinkSync as readlinkSync3, rmSync as rmSync9, symlinkSync, writeFileSync as writeFileSync8, existsSync as existsSync10 } from "node:fs";
16579
16620
  import { tmpdir as tmpdir4 } from "node:os";
16580
16621
  import { dirname as dirname10, isAbsolute as isAbsolute11, join as join21, relative as relative6, resolve as resolve9, sep as sep4 } from "node:path";
16581
16622
  function oracleAssuranceReport() {
@@ -16770,7 +16811,7 @@ function probeFilesystemCaseSensitivity(dir) {
16770
16811
  const upper = join21(dir, ".tw-case-probe-A");
16771
16812
  const lower = join21(dir, ".tw-case-probe-a");
16772
16813
  try {
16773
- writeFileSync10(upper, "", { flag: "wx" });
16814
+ writeFileSync8(upper, "", { flag: "wx" });
16774
16815
  return !existsSync10(lower);
16775
16816
  } finally {
16776
16817
  rmSync9(upper, { force: true });
@@ -16873,7 +16914,7 @@ function overlayPristine(cwd, base, dest, policy, cmd, dependencyRoot, caseSensi
16873
16914
  safeSymlink(target, out3, dest, `the base's ${e.path}`);
16874
16915
  restoredLinks.push({ path: e.path, out: out3, target });
16875
16916
  } else {
16876
- writeFileSync10(out3, content);
16917
+ writeFileSync8(out3, content);
16877
16918
  chmodSync2(out3, parseInt(e.mode.slice(-4), 8) & 511);
16878
16919
  }
16879
16920
  restored.push(e.path);
@@ -16898,7 +16939,7 @@ function suiteEnv(scratch) {
16898
16939
  }
16899
16940
  const userRc = join21(scratch, "npmrc-user");
16900
16941
  const globalRc = join21(scratch, "npmrc-global");
16901
- for (const f of [userRc, globalRc]) writeFileSync10(f, "", { mode: 292 });
16942
+ for (const f of [userRc, globalRc]) writeFileSync8(f, "", { mode: 292 });
16902
16943
  env.npm_config_userconfig = userRc;
16903
16944
  env.npm_config_globalconfig = globalRc;
16904
16945
  env.npm_config_node_options = " ";
@@ -17649,7 +17690,7 @@ var init_verify = __esm({
17649
17690
  // src/cli/run.ts
17650
17691
  import { execFileSync as execFileSync10, spawn, spawnSync as spawnSync4 } from "node:child_process";
17651
17692
  import { randomBytes as randomBytes3 } from "node:crypto";
17652
- import { accessSync as accessSync3, constants as fsConstants, mkdtempSync as mkdtempSync4, readdirSync as readdirSync4, readFileSync as readFileSync13, readlinkSync as readlinkSync4, realpathSync as realpathSync8, rmSync as rmSync10, statSync as statSync5, writeFileSync as writeFileSync11 } from "node:fs";
17693
+ import { accessSync as accessSync3, constants as fsConstants, mkdtempSync as mkdtempSync4, readdirSync as readdirSync4, readFileSync as readFileSync13, readlinkSync as readlinkSync4, realpathSync as realpathSync8, rmSync as rmSync10, statSync as statSync5, writeFileSync as writeFileSync9 } from "node:fs";
17653
17694
  import { tmpdir as tmpdir5 } from "node:os";
17654
17695
  import { dirname as dirname11, join as join22, resolve as resolve10 } from "node:path";
17655
17696
  function authoritativeRunLifecyclePlatform(platform = process.platform) {
@@ -17746,7 +17787,7 @@ function runAgentSupervised(argv, cwd, budgetSecs, linuxPythonCandidates, lifecy
17746
17787
  failure: trustedPython.reason ?? "trusted Linux python3 interpreter is unavailable"
17747
17788
  };
17748
17789
  }
17749
- writeFileSync11(agentEnvFile, JSON.stringify(process.env), { mode: 384 });
17790
+ writeFileSync9(agentEnvFile, JSON.stringify(process.env), { mode: 384 });
17750
17791
  executable = trustedPython.path;
17751
17792
  args = [
17752
17793
  "-I",
@@ -17857,8 +17898,7 @@ function observerExited(pid) {
17857
17898
  function supervisedObserverLog(cwd) {
17858
17899
  const gd = gitDir(cwd) ?? join22(cwd, ".git");
17859
17900
  return join22(
17860
- gd,
17861
- "tamperward",
17901
+ stateDirectory(gd),
17862
17902
  `run-observer-${process.pid}-${Date.now()}-${randomBytes3(4).toString("hex")}.jsonl`
17863
17903
  );
17864
17904
  }
@@ -19304,7 +19344,10 @@ function renderStatus(model, colour) {
19304
19344
  if (v.state === "STALE" && v.reason) rows.push(["Reason", v.reason]);
19305
19345
  else if (v.state === "BROKEN") rows.push(["Reason", v.detail ?? v.reason ?? "authority wiring is invalid"]);
19306
19346
  else if (v.state === "VERIFYING") rows.push(["Reason", v.reason ?? "a verification is in progress"]);
19307
- else if (v.state === "UNVERIFIED") rows.push(["Next", "run `tamperward verify` to establish a verified baseline"]);
19347
+ else if (v.state === "UNVERIFIED" && v.detail) {
19348
+ rows.push(["Reason", v.detail]);
19349
+ rows.push(["Next", "remove what stands at that path, then run `tamperward verify`"]);
19350
+ } else if (v.state === "UNVERIFIED") rows.push(["Next", "run `tamperward verify` to establish a verified baseline"]);
19308
19351
  if (rows.length) {
19309
19352
  lines.push("");
19310
19353
  const width = Math.max(...rows.map(([k]) => k.length));
@@ -21039,7 +21082,7 @@ var init_adapter3 = __esm({
21039
21082
 
21040
21083
  // src/adapters/copilot-sdk/changes.ts
21041
21084
  import { execFileSync as execFileSync13 } from "node:child_process";
21042
- import { mkdtempSync as mkdtempSync6, writeFileSync as writeFileSync12, readFileSync as readFileSync17, rmSync as rmSync12, existsSync as existsSync14, realpathSync as realpathSync10 } from "node:fs";
21085
+ import { mkdtempSync as mkdtempSync6, writeFileSync as writeFileSync10, readFileSync as readFileSync17, rmSync as rmSync12, existsSync as existsSync14, realpathSync as realpathSync10 } from "node:fs";
21043
21086
  import { tmpdir as tmpdir7 } from "node:os";
21044
21087
  import { basename as basename5, dirname as dirname12, isAbsolute as isAbsolute16, relative as relative11, resolve as resolve15, join as join26 } from "node:path";
21045
21088
  function asStr4(v) {
@@ -21116,9 +21159,9 @@ function reconstructAfterViaGit(rawDiff, display2, before, isCreate) {
21116
21159
  const dir = mkdtempSync6(join26(tmpdir7(), "hf-sdk-apply-"));
21117
21160
  try {
21118
21161
  const targetPath = join26(dir, TARGET);
21119
- if (op !== "create") writeFileSync12(targetPath, before ?? "");
21162
+ if (op !== "create") writeFileSync10(targetPath, before ?? "");
21120
21163
  const patchPath = join26(dir, "change.patch");
21121
- writeFileSync12(patchPath, patch);
21164
+ writeFileSync10(patchPath, patch);
21122
21165
  const git8 = (extra) => execFileSync13("git", ["apply", ...extra, patchPath], {
21123
21166
  cwd: dir,
21124
21167
  encoding: "utf8",
@@ -21886,17 +21929,19 @@ var init_runtime_qualification = __esm({
21886
21929
 
21887
21930
  // src/cli/runtime.ts
21888
21931
  import { execFileSync as execFileSync14 } from "node:child_process";
21889
- import { existsSync as existsSync15, mkdirSync as mkdirSync11, readFileSync as readFileSync18, writeFileSync as writeFileSync13 } from "node:fs";
21932
+ import { existsSync as existsSync15, mkdirSync as mkdirSync11, readFileSync as readFileSync18 } from "node:fs";
21890
21933
  import { join as join27, dirname as dirname13 } from "node:path";
21891
21934
  function storePath(cwd) {
21892
21935
  const dir = gitDir(cwd);
21893
- return dir ? join27(dir, "tamperward", "runtime-qualification.json") : null;
21936
+ return dir ? join27(stateDirectory(dir), "runtime-qualification.json") : null;
21894
21937
  }
21895
21938
  function readStore(cwd) {
21896
21939
  const path = storePath(cwd);
21897
- if (!path || !existsSync15(path)) return null;
21940
+ if (!path) return null;
21941
+ const raw = readStateFile(path);
21942
+ if (raw === null) return null;
21898
21943
  try {
21899
- const parsed = JSON.parse(readFileSync18(path, "utf8"));
21944
+ const parsed = JSON.parse(raw);
21900
21945
  if (parsed && typeof parsed === "object" && parsed.records && typeof parsed.records === "object") return parsed;
21901
21946
  } catch {
21902
21947
  return null;
@@ -22027,7 +22072,7 @@ function writeRecord(cwd, id, report2) {
22027
22072
  store.updated_at = report2.timestamp;
22028
22073
  try {
22029
22074
  mkdirSync11(dirname13(path), { recursive: true });
22030
- writeFileSync13(path, JSON.stringify(store, null, 2) + "\n", { mode: 384 });
22075
+ atomicReplaceFile(path, JSON.stringify(store, null, 2) + "\n", 384);
22031
22076
  return true;
22032
22077
  } catch {
22033
22078
  return false;
@@ -22207,7 +22252,13 @@ function emit2(report2, opts, cwd) {
22207
22252
  renderText3(report2, cwd);
22208
22253
  }
22209
22254
  function storedQualification(adapter, cwd, opts = {}) {
22210
- const raw = readStore(cwd)?.records[adapter.name];
22255
+ let raw;
22256
+ try {
22257
+ raw = readStore(cwd)?.records[adapter.name];
22258
+ } catch (e) {
22259
+ if (e instanceof StateFileError) return { state: "rejected", reason: e.message };
22260
+ throw e;
22261
+ }
22211
22262
  if (raw === void 0) return { state: "none" };
22212
22263
  const validated = validateStoredReport(raw);
22213
22264
  if ("reason" in validated) return { state: "rejected", reason: validated.reason };
@@ -22246,10 +22297,18 @@ function runRuntime(sub, opts) {
22246
22297
  }
22247
22298
  const { binding, assessments } = buildQualification(target, opts, cwd);
22248
22299
  const report2 = reportFrom("verify", binding, assessments);
22249
- const wrote = writeRecord(cwd, target.name, report2);
22300
+ let wrote = false;
22301
+ let refused;
22302
+ try {
22303
+ wrote = writeRecord(cwd, target.name, report2);
22304
+ } catch (e) {
22305
+ if (!(e instanceof StateFileError)) throw e;
22306
+ refused = e.message;
22307
+ }
22250
22308
  if (!wrote) {
22251
- const dest = storePath(cwd);
22252
- const reason = `could not persist the qualification to ${dest ?? "the git-local store (no repository found)"}; nothing was recorded, so \`tamperward runtime status\` will report UNQUALIFIED`;
22309
+ const dir = gitDir(cwd);
22310
+ const dest = dir ? join27(dir, "tamperward", "runtime-qualification.json") : null;
22311
+ const reason = `could not persist the qualification to ${dest ?? "the git-local store (no repository found)"}${refused ? ` (${refused})` : ""}; nothing was recorded, so \`tamperward runtime status\` will report UNQUALIFIED`;
22253
22312
  const failure = {
22254
22313
  ...report2,
22255
22314
  recorded: false,
@@ -22319,6 +22378,8 @@ var HONESTY_NOTE, CAP_ID_SET, STATE_SEVERITY, AGGREGATE_SEVERITY, RUNTIME_SUBCOM
22319
22378
  var init_runtime = __esm({
22320
22379
  "src/cli/runtime.ts"() {
22321
22380
  "use strict";
22381
+ init_disk();
22382
+ init_safe_write();
22322
22383
  init_build();
22323
22384
  init_wiring();
22324
22385
  init_machine_output();
@@ -23527,7 +23588,7 @@ var init_record = __esm({
23527
23588
  // src/research/run.ts
23528
23589
  import { randomUUID as randomUUID2 } from "node:crypto";
23529
23590
  import { execFileSync as execFileSync16 } from "node:child_process";
23530
- import { closeSync as closeSync5, existsSync as existsSync16, linkSync, mkdirSync as mkdirSync12, openSync as openSync5, readFileSync as readFileSync21, renameSync as renameSync4, rmSync as rmSync14, unlinkSync, writeFileSync as writeFileSync14, writeSync as writeSync2 } from "node:fs";
23591
+ import { closeSync as closeSync5, existsSync as existsSync16, linkSync, mkdirSync as mkdirSync12, openSync as openSync5, readFileSync as readFileSync21, renameSync as renameSync4, rmSync as rmSync14, unlinkSync, writeFileSync as writeFileSync11, writeSync as writeSync2 } from "node:fs";
23531
23592
  import { hostname } from "node:os";
23532
23593
  import { join as join29, resolve as resolve19 } from "node:path";
23533
23594
  function git7(args, cwd) {
@@ -23672,7 +23733,7 @@ function resumableRecord(path, expected) {
23672
23733
  }
23673
23734
  function writeRecordAtomically(path, text) {
23674
23735
  const tmp = `${path}.${process.pid}.tmp`;
23675
- writeFileSync14(tmp, text);
23736
+ writeFileSync11(tmp, text);
23676
23737
  renameSync4(tmp, path);
23677
23738
  }
23678
23739
  function cloneArgs(repo, ws3) {
@@ -24273,7 +24334,7 @@ var init_summarize = __esm({
24273
24334
 
24274
24335
  // src/research/init.ts
24275
24336
  import { createHash as createHash17 } from "node:crypto";
24276
- import { existsSync as existsSync17, mkdirSync as mkdirSync13, readFileSync as readFileSync23, writeFileSync as writeFileSync15 } from "node:fs";
24337
+ import { existsSync as existsSync17, mkdirSync as mkdirSync13, readFileSync as readFileSync23, writeFileSync as writeFileSync12 } from "node:fs";
24277
24338
  import { basename as basename6, dirname as dirname15, resolve as resolve21 } from "node:path";
24278
24339
  function idFrom(repo) {
24279
24340
  const clean = repo.replace(/[\\/]$/, "").split(/[\\/]/).pop() || "task";
@@ -24303,7 +24364,7 @@ function createResearchManifest(opts) {
24303
24364
  };
24304
24365
  const text = JSON.stringify(doc, null, 2) + "\n";
24305
24366
  mkdirSync13(dirname15(out3), { recursive: true });
24306
- writeFileSync15(out3, text, { flag: "wx", mode: 384 });
24367
+ writeFileSync12(out3, text, { flag: "wx", mode: 384 });
24307
24368
  const parsed = readManifest(out3);
24308
24369
  if (parsed.tasks.length !== 1 || parsed.tasks[0].id !== id) throw new ResearchError(`research init wrote an invalid manifest ${out3}`);
24309
24370
  return out3;
@@ -24393,7 +24454,7 @@ var init_report2 = __esm({
24393
24454
  import { createHash as createHash18 } from "node:crypto";
24394
24455
  import { isDeepStrictEqual } from "node:util";
24395
24456
  import { gzipSync, gunzipSync } from "node:zlib";
24396
- import { mkdirSync as mkdirSync14, readFileSync as readFileSync24, writeFileSync as writeFileSync16 } from "node:fs";
24457
+ import { mkdirSync as mkdirSync14, readFileSync as readFileSync24, writeFileSync as writeFileSync13 } from "node:fs";
24397
24458
  import { dirname as dirname16, resolve as resolve22 } from "node:path";
24398
24459
  function octal(value, width) {
24399
24460
  return value.toString(8).padStart(width - 1, "0") + "\0";
@@ -24593,7 +24654,7 @@ function createResearchBundle(opts) {
24593
24654
  entries.push({ name: "provenance.json", bytes: jsonBytes(provenance) });
24594
24655
  const out3 = resolve22(opts.out);
24595
24656
  mkdirSync14(dirname16(out3), { recursive: true });
24596
- writeFileSync16(out3, gzipSync(makeTar(entries)), { flag: "wx", mode: 384 });
24657
+ writeFileSync13(out3, gzipSync(makeTar(entries)), { flag: "wx", mode: 384 });
24597
24658
  return out3;
24598
24659
  }
24599
24660
  function runResearchBundle(opts) {
@@ -24740,7 +24801,7 @@ var init_research = __esm({
24740
24801
  });
24741
24802
 
24742
24803
  // src/cli/receipt.ts
24743
- import { appendFileSync as appendFileSync3, readFileSync as readFileSync25, writeFileSync as writeFileSync17 } from "node:fs";
24804
+ import { appendFileSync as appendFileSync3, readFileSync as readFileSync25, writeFileSync as writeFileSync14 } from "node:fs";
24744
24805
  function parseReceipt(args) {
24745
24806
  const [sub, ...rest] = args;
24746
24807
  const exportOpts = {};
@@ -24842,7 +24903,7 @@ function runReceiptExport(opts = {}) {
24842
24903
  const serialized = JSON.stringify(receipt) + "\n";
24843
24904
  if (opts.out) {
24844
24905
  try {
24845
- writeFileSync17(opts.out, serialized);
24906
+ writeFileSync14(opts.out, serialized);
24846
24907
  } catch (e) {
24847
24908
  process.stderr.write(`tamperward: could not write receipt to ${opts.out} (${e instanceof Error ? e.message : String(e)})
24848
24909
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tamperward",
3
- "version": "2.39.11",
3
+ "version": "2.39.12",
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",