tamperward 1.5.1 → 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
@@ -248,8 +248,18 @@ versus the gate's 9.0% — a ~2.75× precision edge, not the ≥5× I bet, and t
248
248
  unconfigured repos is published as the honest tuning-and-sign-off cost, not hidden.
249
249
  The loop-layer deny alone was never the product; the measured margins are whole-class
250
250
  spelling coverage, commit-layer precision plus sign-off machinery, and the layers no
251
- hook has. Running ledger: **52 pre-registered bets 23 refuted, 27 held, 2
252
- unresolvable.**
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.**
253
263
 
254
264
  **What about false positives?** `snapshot-rewrite` was swept over **1,652 real mainline
255
265
  commits** (prettier, jest, docusaurus, immer): 216 touched snapshots, all legitimately —
package/dist/cli/index.js CHANGED
@@ -984,6 +984,11 @@ function policyWeakening(before, after) {
984
984
  if (ar.enabled === false && br.enabled !== false) {
985
985
  reasons.push(`rule "${name}" disabled (enabled: false)`);
986
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
+ }
987
992
  }
988
993
  const beforeIgnore = new Set(be.ignore);
989
994
  const addedIgnore = ae.ignore.filter((g) => !beforeIgnore.has(g));
@@ -1396,12 +1401,17 @@ function evaluate(changes, policy, detectors = allDetectors, view) {
1396
1401
  }
1397
1402
  }
1398
1403
  const seen = /* @__PURE__ */ new Set();
1399
- return out.filter((f) => {
1404
+ const deduped = out.filter((f) => {
1400
1405
  const k = key(f);
1401
1406
  if (seen.has(k)) return false;
1402
1407
  seen.add(k);
1403
1408
  return true;
1404
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
+ });
1405
1415
  }
1406
1416
  function hasBlocking(findings) {
1407
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.1",
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",