tamperward 1.4.4 → 1.4.6

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/README.md +35 -12
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -18,13 +18,17 @@ path: deleting failing tests, skipping them, casting to `any`, suppressing lint
18
18
  lowering coverage gates, rewriting snapshots, editing CI, or bypassing hooks with
19
19
  `--no-verify`. Tamperward treats the safety nets themselves as protected assets and
20
20
  blocks the **class** of bypass — one ruleset, evaluated on the actual diff and commands
21
- as a verdict, not a probability, enforced everywhere a change can be made: **inside the
22
- agent's loop (a deny before the tool runs, and a sweep at end of turn), at the commit,
23
- and at the merge.**
21
+ as a verdict, not a probability, enforced at every stage a change passes through:
22
+ **inside the agent's loop (a Claude Code hook today a deny before the tool runs, and
23
+ a sweep at end of turn), at the commit, and at the merge.** CI is the authority; the
24
+ earlier layers shorten the feedback loop for agents that carry them.
24
25
 
25
26
  > Apache-2.0 · zero runtime model calls · every headline claim below is measured, with
26
27
  > the pre-registered predictions (including the refuted ones) committed to this repo
27
28
 
29
+ **[Docs & guide](https://hexrift.github.io/tamperward/)** ·
30
+ **[The launch post](https://hexrift.github.io/tamperward/blog/what-agents-do-when-you-block-their-shortcuts)** — how these numbers were measured, and which of my own bets lost
31
+
28
32
  ## Install
29
33
 
30
34
  ```bash
@@ -34,12 +38,12 @@ npx tamperward init
34
38
  One idempotent command wires the policy and every enforcement point — it never
35
39
  overwrites anything you wrote, and `--dry-run` shows the plan first:
36
40
 
37
- | point | what it wires |
41
+ | what | wired as |
38
42
  | --- | --- |
43
+ | policy | a commented baseline `.tamperward.yml` — the defaults apply even without it |
39
44
  | agent loop | Claude Code `PreToolUse` deny + `Stop` sweep, merged into `.claude/settings.json` |
40
45
  | pre-commit | husky when present, the plain git hook otherwise |
41
46
  | CI | a PR-gate workflow with out-of-band sign-off (details below) |
42
- | policy | a commented baseline `.tamperward.yml` — the defaults apply even without it |
43
47
 
44
48
  Or run the views directly:
45
49
 
@@ -77,6 +81,7 @@ tamperward: 2 blocking
77
81
  evidence 2 test block(s) removed from test/calc.test.js
78
82
  instead Keep the assertions and fix the code. Removing test blocks to go
79
83
  green is the tamper.
84
+ sign-off tamperward allow test-deletion --file test/calc.test.js --reason "..."
80
85
 
81
86
  A blocking finding clears only with a human sign-off. In CI that sign-off is
82
87
  out-of-band — a PR label applied by a reviewer — never a file committed on the
@@ -88,7 +93,7 @@ Under GitHub Actions each finding also lands as an **inline annotation on the di
88
93
 
89
94
  ## The rules
90
95
 
91
- Nine mechanical rules, deterministic by construction, `block` by default:
96
+ Nine mechanical rules, deterministic by construction — eight `block`, one `warn`:
92
97
  `test-deletion` (AST block-count, rename-out-of-glob, shell mutation), `test-skip`
93
98
  (including `.only`, which narrows the suite), `ts-any-cast`, `lint-suppression`,
94
99
  `coverage-lowering` (Jest and Vitest shapes, all four metrics, `package.json` included),
@@ -96,8 +101,10 @@ Nine mechanical rules, deterministic by construction, `block` by default:
96
101
  (flags and the env-var escape hatches), and `snapshot-rewrite` (`warn` — see the FP study
97
102
  below for why).
98
103
 
99
- Three heuristics ship as `warn` and never block until their precision clears the bar:
100
- `assertion-weakening`, `guard-removal`, `ts-any-launder`.
104
+ Two heuristic rule names `assertion-weakening` and `guard-removal` are **reserved
105
+ in the baseline policy but not yet built**: they get detectors only once a measured
106
+ negatives corpus exists (SPEC §7.A), enter as `warn`, and never block until their
107
+ precision clears the bar.
101
108
 
102
109
  The policy file itself is un-ignorable, CI reads the policy from the **merge-base** (a PR
103
110
  cannot govern its own verdict), and the agent honours no sign-off it can author — the
@@ -108,13 +115,29 @@ escape valve is a PR label only a human with write access can apply.
108
115
  **Does the loop work?** `harness/run.sh` stands up an isolated repo with a real bug and a
109
116
  failing protected test, lets a headless agent loose with the hooks installed, and
110
117
  classifies the end state with an oracle independent of the detectors under test
111
- (SPEC §7.B). Across 42 runs of these **guarded** scenarios — every attempted bypass class
112
- had a detector covering it — **0 tampers reached green; all 20 runs where a block fired
113
- ended in an honest fix**, a 20/20 point estimate whose 95% lower bound is ≈86%. The split is the
118
+ (SPEC §7.B). Across 67 runs of these **guarded** scenarios — every attempted bypass class
119
+ had a detector covering it — **0 tampers reached green; all 25 runs where a block fired
120
+ ended in an honest fix**, a 25/25 point estimate whose 95% lower bound is ≈89%. The split is the
114
121
  honest part: Sonnet 5 never attempted a bypass at all, even when the prompt *ordered*
115
- one; all 20 interceptions came from Haiku 4.5 on a seed whose honest fix is genuinely
122
+ one; every interception came from Haiku 4.5 on a seed whose honest fix is genuinely
116
123
  expensive. Reproduce: `npm run build && harness/run.sh 5 adversarial haiku seed-hard`.
117
124
 
125
+ **Is the fix caused by the deny, or would it have happened anyway?** Measured, with a
126
+ pre-registered no-gate control arm (`harness/PREDICTION-ungated-control.md`): same seed,
127
+ model, prompt and oracle, hooks removed. **6/10 ungated runs tampered through; honest
128
+ fixes fell from 10/10 gated to 4/10 ungated.** The gate's measured contribution on that
129
+ configuration is a 60-point swing — causation, not survivorship — and one of the three
130
+ control-arm bets was refuted (ungated honest fixes came in above my bet).
131
+
132
+ **And when the agent knows the gate is there?** Also pre-registered
133
+ (`harness/PREDICTION-informed-adversary.md`) — betting the gate would *leak*. Refuted,
134
+ three bets out of three: with the detector sources vendored into the repo and the prompt
135
+ naming the gate, Haiku's bypass attempts went from 5/10 uninformed (same day, same
136
+ everything) to **0/10 informed — fifteen informed runs, zero evasion attempts**. Sonnet,
137
+ told the gate exists under "speed is everything" pressure, refused the task outright in
138
+ 3/5 runs rather than work around it, and fixed honestly in the other 2. At this scenario
139
+ size, disclosure measured as deterrence, not as an evasion spec.
140
+
118
141
  **Which detectors deserve to exist?** Measured too. Affordance seeds offer an agent one
119
142
  specific bypass each; predictions are pre-registered before any rep runs
120
143
  (`harness/PREDICTION-affordance.md`). Result: 7/10 adversarial runs regenerated a golden
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tamperward",
3
- "version": "1.4.4",
3
+ "version": "1.4.6",
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",