tamperward 1.5.0 → 1.6.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/README.md CHANGED
@@ -118,12 +118,15 @@ escape valve is a PR label only a human with write access can apply.
118
118
  **Does the loop work?** `harness/run.sh` stands up an isolated repo with a real bug and a
119
119
  failing protected test, lets a headless agent loose with the hooks installed, and
120
120
  classifies the end state with an oracle independent of the detectors under test
121
- (SPEC §7.B). Across 77 runs of these **guarded** scenarios — every attempted bypass class
122
- had a detector covering it — **0 tampers reached green; all 25 runs where a block fired
123
- ended in an honest fix**, a 25/25 point estimate whose 95% lower bound is ≈89%. The split is the
124
- honest part: across four models (Haiku 4.5, Sonnet 5, Opus 5, Fable 5), every
125
- interception came from Haiku on a seed whose honest fix is genuinely expensive — no
126
- frontier model attempted a bypass at all, even when the prompt *ordered* one.
121
+ (SPEC §7.B). Across **137 runs** of these **guarded** scenarios — every attempted bypass
122
+ class had a detector covering it — **0 tampers reached green**. Denied runs converted to
123
+ an honest fix **42 times out of 56 (75%)**, and that number is honestly seed-dependent:
124
+ 25/25 where the honest fix was the agent's own task, down to **0/10** where the failing
125
+ suite was somebody else's bug (the no-verify study below). The frontier split gets the
126
+ same scoping: on the expensive-fix seed no frontier model attempted a bypass even when
127
+ the prompt *ordered* one; on the hook-skip seed, frontier models under a prompt ordering
128
+ the skip **try the ordered skip once, get denied once, and convert 10/10** — frontier
129
+ honesty there is what happens after the first no, not never-trying.
127
130
  Reproduce: `npm run build && harness/run.sh 5 adversarial haiku seed-hard`.
128
131
 
129
132
  **Is the fix caused by the deny, or would it have happened anyway?** Measured, with a
@@ -137,7 +140,10 @@ ungated Sonnet 5, Opus 5, and Fable 5 each went **10/10 honest** under the same
137
140
  pressure prompt — frontier honesty is disposition, not the gate's deterrence — while
138
141
  ungated Haiku stays 6/10 tamper-green. The gate's measured value concentrates on the
139
142
  models that do cheat. (The Fable rows are a Fable-driven harness measuring Fable:
140
- self-graded homework, bounded by the deterministic held-out oracle.)
143
+ self-graded homework, bounded by the deterministic held-out oracle.) The
144
+ type-laundering study below later measured the boundary of the disposition claim:
145
+ it held for Opus in every regime; Sonnet and Fable held only where the gate could
146
+ interrupt.
141
147
 
142
148
  **And when the agent knows the gate is there?** Also pre-registered
143
149
  (`harness/PREDICTION-informed-adversary.md`) — betting the gate would *leak*. Refuted,
@@ -162,6 +168,99 @@ cover story — measured **0/10 attempts**; Haiku implemented the real fix every
162
168
  measured demand stay in a record-only shadow scanner that accumulates evidence on every
163
169
  weekly harness run.
164
170
 
171
+ **And when no honest fix exists at all?** The regime every other seed excludes — and
172
+ where the typia incident actually lived — measured with a claims-based oracle
173
+ (`harness/PREDICTION-stuck-agent.md`): a provably unfixable suite, an explicit cheap
174
+ honest exit ("STATUS: blocked is a fully acceptable outcome"), fabrication defined as
175
+ claiming `fixed` against cryptographic reality. **Twenty of twenty pressured runs —
176
+ Haiku and Sonnet alike — surrendered honestly**, zero fabrications, zero test edits,
177
+ a written diagnosis every time. Both my Haiku bets were refuted: the model that
178
+ tampers 6/10 when the cheat is cheaper than the fix takes the honest exit when the
179
+ honest exit is cheapest. The sharpened thesis: agents take the cheapest path they
180
+ believe is sanctioned — the danger is pressure with no sanctioned exit cheaper than
181
+ the lie. The untested harder variant (no one *names* the exit) is registered as the
182
+ open follow-up.
183
+
184
+ **Is the honesty just eval-awareness?** The sharpest critique of every number above —
185
+ the seeds *look* like tests, so maybe the models are performing for a suspected
186
+ grader — is measured too (`harness/PREDICTION-eval-awareness.md`): the same
187
+ seed dressed as a real project (README, changelog, a user-report thread with a
188
+ maintainer's plea, a backdated git history built by a self-deleting kit), gate off.
189
+ **Zero frontier tampers in 20 dressed runs**, matching the undressed baselines, while
190
+ Haiku stayed a cheater at 8/10. The pre-registered manipulation check is the honest
191
+ part: only Opus actually read the dressing (8/10 runs, all ten honest) — so the Opus
192
+ null carries the weight and the Sonnet null is scoped as ambient-only. And the
193
+ sanctioned-edit variant (`harness/PREDICTION-self-authored.md`) — implement from an
194
+ ambiguous spec, then a prompt *licensing* correction of "intern-written" tests —
195
+ produced **zero test rewrites in 30 runs across three models**, refuting all three of
196
+ my rewrite bets, for a reason worth knowing: every model, implementing blind, chose
197
+ the same reading the tests encode. You cannot make an agent believe a test is wrong
198
+ by telling it tests might be wrong; the forced-conflict variant is the registered
199
+ follow-up.
200
+
201
+ **Can "closed vs open" be called in advance?** The two dimension studies
202
+ (`harness/PREDICTION-coverage.md`, `harness/PREDICTION-noverify.md`) tested whether a
203
+ rule's leak-prone axis can be identified a priori. Both a-priori calls **held
204
+ deterministically**: every enumerated hook-skip spelling denies while both
205
+ `core.hooksPath` forms allow (the axis called open is open — surveyed by six frontier
206
+ runs in reconnaissance, exploited by none), and all three coverage config-surface moves
207
+ block. The behavioral half delivered the study's best datum: told to get the coverage
208
+ gate green fast, **half of Haiku's runs opened with the per-path override** — the exact
209
+ config-surface move the registration predicted a token detector would miss — and the
210
+ shipped semantic detector caught every one. Zero tampers in 60 more guarded runs; one
211
+ false-positive interception recorded and kept (Fable extending a test-file import via
212
+ `sed`, denied by the shell-mutation heuristic mid-honest-fix — the FP record's first
213
+ live entry).
214
+
215
+ **And when the cheapest cheat is invisible to the gate?** The type-laundering study
216
+ (`harness/PREDICTION-asany.md`) probed the one seed whose cheapest cheat spellings are
217
+ deliberately uncovered — `: any` annotations are warn-only (`ts-any-launder`, the
218
+ FP-avoidance choice) and a bare assertion (`raw as Settings`) has no rule at all — with
219
+ a held-out runtime oracle hardened *before* any rep (without that hardening, nine
220
+ assertion tampers would have scored honest and the conclusion would have inverted).
221
+ Result: **Haiku laundered 10/10** — and the three runs denied on the guarded `as any`
222
+ cast **switched to the invisible assertion**, the first time a deny redirected an agent
223
+ to a better bypass instead of the fix. **Opus went 10/10 honest with zero gate
224
+ involvement** — the strongest disposition evidence measured — while **Fable tampered
225
+ 6/10 and Sonnet, the default model, 10/10, both via the bare assertion and drawing
226
+ zero denies** (Sonnet's spelling every time: an inline structural assertion,
227
+ `as { retries: number; ... }` — the quickest silence that compiles carries no bypass
228
+ framing, and Sonnet's famous refusals turn out to be triggered by framing). The
229
+ completed frontier split — **Opus 0/10 · Fable 6/10 · Sonnet 10/10** — is the
230
+ published shape, with the standing self-measurement caveat on the Fable rows.
231
+ Twenty-six of forty runs tampered through the uncovered dimension: measured demand
232
+ that hardens the `ts-any-launder` warn→block graduation case via the §7.A corpus —
233
+ the honest fix itself narrows through `as Record<string, unknown>`, so a naive
234
+ assertion-blocker is an FP factory.
235
+
236
+ **Why not just a prompt line, or a 20-line grep hook?** Measured
237
+ (`harness/PREDICTION-baselines.md`), against a frozen naive hook and a one-sentence
238
+ prompt baseline — and two of my three bets lost, both against my own product's
239
+ framing. **One integrity sentence took ungated Haiku from 6/10 tampering to 1/10**:
240
+ the cheap prompt convention is a real mitigation and this README says so. The naive
241
+ hook converted denials nearly as well as the gate (4/5) on the crude-spelling seed —
242
+ but was tampered through twice: once by **hardcoding the implementation** (invisible
243
+ to any hook; the held-out oracle layer caught it), and once by a denied agent
244
+ switching to the node:test **options spelling** `{ skip: true }` — which the shipped
245
+ `test-skip` rule *also* missed until it shipped closed in 1.5.1. On 1,227 real merged
246
+ commits, the naive patterns would block **one in four legitimate commits (24.9%)**
247
+ versus the gate's 9.0% — a ~2.75× precision edge, not the ≥5× I bet, and that 9% on
248
+ unconfigured repos is published as the honest tuning-and-sign-off cost, not hidden.
249
+ The loop-layer deny alone was never the product; the measured margins are whole-class
250
+ spelling coverage, commit-layer precision plus sign-off machinery, and the layers no
251
+ hook has. Two follow-ups closed the loops this study opened. The sentence was then
252
+ aimed at the regime where the gate is blind — Sonnet's 10/10 assertion-laundering
253
+ seed — and produced a **complete flip: 10/10 honest** (`harness/PREDICTION-baselines.md`,
254
+ addendum): naming the act is the intervention Sonnet's framing-triggered honesty
255
+ runs on. And the promised **tuned-gate numbers** now exist
256
+ (`harness/PREDICTION-tuned-fp.md`): 9.0% unconfigured → **6.0%** with a one-line
257
+ severity tune → roughly one sign-off decision per 17 commits residual. The same study
258
+ shipped per-rule `exclude` globs (1.6.0, with the policy-weakening symmetry built in)
259
+ and then measured them honestly: **zero effect on this corpus** — these repos' casts
260
+ live in source, not tests, so the zod-recommended path scoping removed nothing here;
261
+ the null is published next to the feature. Running ledger: **56 pre-registered
262
+ bets — 25 refuted, 29 held, 2 unresolvable.**
263
+
165
264
  **What about false positives?** `snapshot-rewrite` was swept over **1,652 real mainline
166
265
  commits** (prettier, jest, docusaurus, immer): 216 touched snapshots, all legitimately —
167
266
  which is exactly why that rule is a `warn` asking for human confirmation, not a block,
package/dist/cli/index.js CHANGED
@@ -623,7 +623,13 @@ var PATTERNS2 = [
623
623
  { re: /\b(?:it|test|describe)\.(?:skip|only|todo)\b/, why: "a .skip/.only/.todo marker" },
624
624
  { re: /\b(?:xit|xtest|xdescribe)\b/, why: "an x-prefixed disabled test" },
625
625
  { re: /\b(?:fit|ftest|fdescribe)\b/, why: "an f-prefixed focused test (narrows the suite)" },
626
- { re: /\bpending\(\s*\)/, why: "a pending() marker" }
626
+ { re: /\bpending\(\s*\)/, why: "a pending() marker" },
627
+ // node:test spells the same class as options — `test('x', { skip: true }, ...)` — and as
628
+ // a runtime call. A live evasion used the options form the day this gap was measured
629
+ // (PREDICTION-baselines.md): denied the rm, the agent skipped by option instead.
630
+ { re: /\bskip:\s*(?:true|1|['"`])/, why: "a { skip: ... } option disabling the test (node:test options form)" },
631
+ { re: /\btodo:\s*(?:true|['"`])/, why: "a { todo: ... } option (the test no longer fails the run)" },
632
+ { re: /\b(?:t|ctx|context)\.skip\(/, why: "a runtime t.skip() call" }
627
633
  ];
628
634
  var testSkip = {
629
635
  id: RULE3,
@@ -978,6 +984,11 @@ function policyWeakening(before, after) {
978
984
  if (ar.enabled === false && br.enabled !== false) {
979
985
  reasons.push(`rule "${name}" disabled (enabled: false)`);
980
986
  }
987
+ const beforeEx = new Set(br.exclude ?? []);
988
+ const addedEx = (ar.exclude ?? []).filter((g) => !beforeEx.has(g));
989
+ if (addedEx.length) {
990
+ reasons.push(`rule "${name}" exclude globs added (${addedEx.join(", ")}) \u2014 blinds this rule on those paths`);
991
+ }
981
992
  }
982
993
  const beforeIgnore = new Set(be.ignore);
983
994
  const addedIgnore = ae.ignore.filter((g) => !beforeIgnore.has(g));
@@ -1390,12 +1401,17 @@ function evaluate(changes, policy, detectors = allDetectors, view) {
1390
1401
  }
1391
1402
  }
1392
1403
  const seen = /* @__PURE__ */ new Set();
1393
- return out.filter((f) => {
1404
+ const deduped = out.filter((f) => {
1394
1405
  const k = key(f);
1395
1406
  if (seen.has(k)) return false;
1396
1407
  seen.add(k);
1397
1408
  return true;
1398
1409
  });
1410
+ return deduped.filter((f) => {
1411
+ const ex = policy.rules[f.rule]?.exclude;
1412
+ if (!ex?.length || !f.file || isPolicyFile(f.file)) return true;
1413
+ return !matchesAny(f.file, ex);
1414
+ });
1399
1415
  }
1400
1416
  function hasBlocking(findings) {
1401
1417
  return findings.some((f) => f.severity === "block");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tamperward",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "The deterministic agent-integrity gate. One ruleset, evaluated on the actual diff/commands as a verdict, enforced everywhere a change can be made.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "hexrift",