tamperward 2.16.0 → 2.16.1

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
@@ -325,8 +325,14 @@ Public rulesets can be read anonymously; set `GH_TOKEN` or `GITHUB_TOKEN` when
325
325
  authentication is required.
326
326
 
327
327
  A real deployment needs a verify command configured — the generated CI verify step
328
- **fails closed (exit 2) without one** rather than passing quietly. In
329
- `.tamperward.yml`:
328
+ **fails closed (exit 2) without one** rather than passing quietly. Since 2.16.1,
329
+ `tamperward init` ends with a separate **VERIFICATION SETUP** status: it says either
330
+ `verification configured — <command>` or prominently reports
331
+ `INCOMPLETE: verification not configured — CI will fail closed`. It can suggest a
332
+ single high-confidence command such as `npm test`, `pytest`, `tox`, `cargo test`
333
+ or `go test ./...`; multiple candidates are listed without choosing one. Suggestions
334
+ are advisory only — init never writes an inferred verifier command because that command
335
+ is part of the trust anchor. In `.tamperward.yml`:
330
336
 
331
337
  ```yaml
332
338
  verify:
package/dist/cli/index.js CHANGED
@@ -8173,6 +8173,78 @@ function hooksShapeError(hooks) {
8173
8173
  }
8174
8174
  return null;
8175
8175
  }
8176
+ function verifierCandidates(cwd) {
8177
+ const candidates = [];
8178
+ const add = (command) => {
8179
+ if (!candidates.includes(command)) candidates.push(command);
8180
+ };
8181
+ try {
8182
+ const pkg = JSON.parse(readFileSync11(join12(cwd, "package.json"), "utf8"));
8183
+ const test = pkg.scripts?.test;
8184
+ if (typeof test === "string" && test.trim() !== "" && !/error:\s*no test specified/i.test(test)) add("npm test");
8185
+ } catch {
8186
+ }
8187
+ try {
8188
+ const pyproject = readFileSync11(join12(cwd, "pyproject.toml"), "utf8");
8189
+ if (/^\s*\[tool\.pytest(?:\.ini_options)?\]\s*$/m.test(pyproject)) add("pytest");
8190
+ } catch {
8191
+ }
8192
+ if (existsSync9(join12(cwd, "pytest.ini"))) add("pytest");
8193
+ try {
8194
+ const setupCfg = readFileSync11(join12(cwd, "setup.cfg"), "utf8");
8195
+ if (/^\s*\[tool:pytest\]\s*$/m.test(setupCfg)) add("pytest");
8196
+ } catch {
8197
+ }
8198
+ try {
8199
+ const tox = readFileSync11(join12(cwd, "tox.ini"), "utf8");
8200
+ if (/^\s*\[tox\]\s*$/m.test(tox)) add("tox");
8201
+ } catch {
8202
+ }
8203
+ try {
8204
+ const cargo = readFileSync11(join12(cwd, "Cargo.toml"), "utf8");
8205
+ if (/^\s*\[(?:package|workspace)\]\s*(?:#.*)?$/m.test(cargo)) add("cargo test");
8206
+ } catch {
8207
+ }
8208
+ try {
8209
+ const goMod = readFileSync11(join12(cwd, "go.mod"), "utf8");
8210
+ if (/^\s*module\s+\S+\s*(?:\/\/.*)?$/m.test(goMod)) add("go test ./...");
8211
+ } catch {
8212
+ }
8213
+ return candidates;
8214
+ }
8215
+ function verifierSetupMessage(cwd) {
8216
+ try {
8217
+ const policy = loadPolicy(cwd);
8218
+ const command = policy.verify?.command?.trim();
8219
+ if (command) return `verification configured \u2014 ${command}`;
8220
+ } catch (e) {
8221
+ return `INCOMPLETE: verification not configured \u2014 policy cannot currently be loaded; CI will fail closed until the policy is fixed (${errText3(e)}).`;
8222
+ }
8223
+ const candidates = verifierCandidates(cwd);
8224
+ const lines = [
8225
+ "INCOMPLETE: verification not configured \u2014 CI will fail closed until .tamperward.yml names a trusted suite command."
8226
+ ];
8227
+ if (candidates.length === 1) {
8228
+ lines.push(`Suggested verifier command: ${candidates[0]}`);
8229
+ lines.push("Review it, then add:");
8230
+ lines.push("verify:");
8231
+ lines.push(` command: "${candidates[0]}"`);
8232
+ lines.push(" budget: 300");
8233
+ } else if (candidates.length > 1) {
8234
+ lines.push(`Detected verifier candidates: ${candidates.join(", ")}`);
8235
+ lines.push("Choose the suite that defines merge authority, then add for example:");
8236
+ lines.push("verify:");
8237
+ lines.push(' command: "<reviewed suite command>"');
8238
+ lines.push(" budget: 300");
8239
+ } else {
8240
+ lines.push("Add the reviewed suite command to .tamperward.yml, for example:");
8241
+ lines.push("verify:");
8242
+ lines.push(' command: "<your test command>"');
8243
+ lines.push(" budget: 300");
8244
+ }
8245
+ lines.push("TamperWard does not write an inferred verifier command automatically because it is part of the trust anchor.");
8246
+ return lines.join("\n");
8247
+ }
8176
8248
  function planPolicy(cwd) {
8177
8249
  const path = join12(cwd, POLICY_FILE);
8178
8250
  if (existsSync9(path)) {
@@ -8563,6 +8635,10 @@ WARNING (${a.item}): ${a.warning}
8563
8635
  }
8564
8636
  const errors = plan.filter((a) => a.status === "error");
8565
8637
  const changed = opts.dryRun ? plan.filter((a) => a.apply).length : applied;
8638
+ w.write(`
8639
+ VERIFICATION SETUP
8640
+ ${verifierSetupMessage(cwd)}
8641
+ `);
8566
8642
  if (errors.length) {
8567
8643
  w.write(`
8568
8644
  tamperward init: ${errors.length} item(s) need your attention above; the rest ${opts.dryRun ? "are planned" : "were applied"}.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tamperward",
3
- "version": "2.16.0",
3
+ "version": "2.16.1",
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",