@codacy/verity-cli 0.33.3-experimental.f081164 → 0.34.0-experimental.007b74f

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/bin/verity.js +49 -22
  2. package/package.json +1 -1
package/bin/verity.js CHANGED
@@ -27021,7 +27021,7 @@ function channelSilence(input) {
27021
27021
  // src/lib/cli-version.ts
27022
27022
  function cliVersion() {
27023
27023
  try {
27024
- return true ? "0.33.3-experimental.f081164" : "dev";
27024
+ return true ? "0.34.0-experimental.007b74f" : "dev";
27025
27025
  } catch {
27026
27026
  return "dev";
27027
27027
  }
@@ -34334,11 +34334,11 @@ STEP 3 \u2014 Properties: 2 to 4, in this order of priority:
34334
34334
  4. conservation \u2014 money, stock and counts add up: parts sum to the total, nothing credited twice or lost, no row skipped;
34335
34335
  5. progress \u2014 no deadlock, nothing stuck forever, retries end;
34336
34336
  6. crash consistency \u2014 AT MOST ONE, listed LAST: "after a crash between X and Y and the recovery the code relies on, \u2026", or, where nothing recovers, only when the crash loses money or records for good.
34337
- A property must be something the code PROMISES. An operation whose job is to apply an effect on every call (a withdrawal, a charge, a send) promises "no double effect" only where the code dedups it (an idempotency key, a unique insert, a conditional update). When a comment says duplicates are by design (consumers dedupe on an id), do not claim "no double effect". An upsert, a set-to-value or a delete applied twice is not a double effect.
34337
+ A property must be something the code PROMISES. An operation whose job is to apply an effect on every call (a withdrawal, a charge, a send) promises "no double effect" where the code dedups it (an idempotency key, a unique insert, a conditional update), or where the SAME input \u2014 one that carries its own event, message, delivery or request id \u2014 can be delivered or retried twice; there a missing dedup is the bug. Two calls whose input carries no such identity are two operations, not a double effect. When a comment says duplicates are by design (consumers dedupe on an id), do not claim "no double effect". An upsert, a set-to-value or a delete applied twice is not a double effect.
34338
34338
  A counterexample must run the real function: a trace made only of environment moves (another writer, a clock) says nothing about this code. A promise about the END result (nothing lost, nothing charged twice, totals add up) is checked on states where every invocation has finished or crashed \u2014 guard the \`inv\` with the program counters, as exemplar 2's second property does; a violation that exists only while work is still pending is not a bug.
34339
34339
  A property is about an OUTCOME someone would call a bug \u2014 money, stock or records wrong, access granted, work lost or done twice, a job stuck forever \u2014 never about the mechanism's internals (a key's format, two timestamps being distinct, a lease's exact boundary tick). A comment that says HOW the code works ("make each attempt's key unique") is not the promise; the promise is what that mechanism is for.
34340
- A comment that states a SAFE design choice ("a spent token presented again ends every session", "consumers dedupe, so publishing twice is harmless") is the design: never claim the opposite. A comment never makes an UNSAFE outcome safe: granting access on an error, losing or double-counting money, stock or records is a bug whatever the comment says ("don't block people when the permission service is flaky" describes a fail-open bug).
34341
- Every property says what it rests on, in \`source\`: either \`L<n>: <words copied from that line>\` \u2014 a line of the numbered source above (a comment, docstring, check, error message or name) that states the promise \u2014 or one rule: \`rule:conservation\` (money, stock or counts add up), \`rule:once\` (no double effect, only where the code dedups, as above), \`rule:no-lost-update\`, \`rule:fail-closed\` (an error never grants access or reports success), \`rule:progress\` (nothing stuck forever). A property that neither a line nor one of these rules supports is not a promise: do not state it. A property whose \`source\` is empty, or cites words its line does not contain, sends the model back.
34340
+ A comment that states a SAFE design choice ("a spent token presented again ends every session", "consumers dedupe, so publishing twice is harmless") is the design: never claim the opposite. A comment never makes an UNSAFE outcome safe: granting access on an error, losing or double-counting money, stock or records is a bug whatever the comment says ("don't block people when the permission service is flaky" describes a fail-open bug). Fail-open means an ACCESS decision: running WITHOUT an optional check when the check itself is unavailable, and saying so (a warning, a log line), is a stated degradation, not a fail-open \u2014 never claim it must fail closed.
34341
+ Every property says what it rests on, in \`source\`: either \`L<n>: <words copied from that line>\` \u2014 a line of the numbered source above (a comment, docstring, check, error message or name) that states the promise \u2014 or one rule: \`rule:conservation\` (money, stock or counts add up), \`rule:once\` (no double effect: where the code dedups, or where the SAME input can arrive twice \u2014 an input that carries its own identity, such as an event, message, delivery or request id, redelivered or retried \u2014 even when the code has no dedup at all; a missing dedup is the bug, not a reason to drop the property. Two calls whose input carries no such identity are two operations: reserving a seat twice or creating a record twice from two requests is what the caller asked for, not a double effect), \`rule:no-lost-update\`, \`rule:fail-closed\` (an error in an access decision never grants access; an operation that failed is never reported as done), \`rule:progress\` (nothing stuck forever). A property that neither a line nor one of these rules supports is not a promise: do not state it. A property whose \`source\` is empty, or cites words its line does not contain, sends the model back.
34342
34342
  When the input lists where the function is called, a caller's guard bounds its inputs: never build a counterexample from an input no caller can pass (an empty list a caller refuses).
34343
34343
  Property ids name the promise in snake_case (refund_total_within_capture) \u2014 never P1. \`desc\` states the promise in the code's own terms and names the move the trace needs ("two concurrent calls \u2026", "after the charge succeeded and the call timed out \u2026").
34344
34344
  A PURE computation (no effect sites) is checked with \`Verity.checkData\` on small concrete inputs, including the edge cases (exemplar 3).
@@ -34720,7 +34720,8 @@ Rules:
34720
34720
  5. Write the harness as ONE file and run it in the SAME shell call that writes it (a heredoc followed by the run). TypeScript: node --experimental-strip-types harness.ts (detect runtimes only if that fails: node --version, python3 --version); if strip-types is unavailable, remove type annotations ONLY on lines that are not part of the function under test's logic, and say so. Python: python3 harness.py.
34721
34721
  6. Nothing you run can reach the network. Do not try.
34722
34722
  7. Keep it under about eight shell calls. Output tokens are time.
34723
- 8. Answer with the JSON object only: verdict (reproduced | diverged | unreplayable), reason (2-4 sentences: what you ran, what the real code did, and how that compares to the trace's final state), harness_files (every file you wrote: path and its line count \u2014 do NOT paste contents, your shell calls already carry them), executed_output (the last relevant stdout, at most 2000 characters).
34723
+ 8. Answer with the JSON object only: verdict (reproduced | diverged | unreplayable), reason (2-4 sentences: what you ran, what the real code did, and how that compares to the trace's final state), harness_files (every file you wrote: path and its line count \u2014 do NOT paste contents, your shell calls already carry them), executed_output (the last relevant stdout, at most 2000 characters), impact (one word, below).
34724
+ 9. impact \u2014 AFTER the verdict, and never a reason to change it: what the violation you ran costs a user of this code, the most serious one the run showed. money: a balance, charge, refund, price or stock is wrong. access: someone gets access they should not have, or an auth check is bypassed. data: a user's records are lost, corrupted or wrongly duplicated. external: an effect outside the system happens twice or never (a charge, an email, a shipment, a webhook). count: a counter, metric or statistic is off. cache: a cached or derived value is stale until it refreshes. internal: the program's own bookkeeping is off, with no effect a user would see. none: the verdict is not reproduced.
34724
34725
 
34725
34726
  "reproduced" means the real code, driven by the trace, reached the property violation the model describes. "diverged" means it did not \u2014 the model is wrong about the code, which is a useful answer. "unreplayable" means you could not run the real function faithfully; say why.`;
34726
34727
  var REPLAY_SCHEMA = {
@@ -34738,9 +34739,10 @@ var REPLAY_SCHEMA = {
34738
34739
  required: ["path", "lines"]
34739
34740
  }
34740
34741
  },
34741
- executed_output: { type: "string" }
34742
+ executed_output: { type: "string" },
34743
+ impact: { type: "string", enum: ["money", "access", "data", "external", "count", "cache", "internal", "none"] }
34742
34744
  },
34743
- required: ["verdict", "reason", "harness_files", "executed_output"]
34745
+ required: ["verdict", "reason", "harness_files", "executed_output", "impact"]
34744
34746
  };
34745
34747
  function renderBriefInput(brief) {
34746
34748
  const p = brief.property;
@@ -34767,6 +34769,8 @@ function renderBriefInput(brief) {
34767
34769
  }
34768
34770
  return parts2.join("\n");
34769
34771
  }
34772
+ var IMPACTS = ["money", "access", "data", "external", "count", "cache", "internal", "none"];
34773
+ var isImpact = (v) => typeof v === "string" && IMPACTS.includes(v);
34770
34774
  function stringsIn(value, out2 = [], depth = 0) {
34771
34775
  if (depth > 8) return out2;
34772
34776
  if (typeof value === "string") out2.push(value);
@@ -34808,7 +34812,8 @@ function parseReplayOutput(output) {
34808
34812
  verdict,
34809
34813
  reason: typeof o.reason === "string" ? o.reason.slice(0, 2e3) : "",
34810
34814
  harness_files: Array.isArray(o.harness_files) ? o.harness_files.filter((f) => typeof f.path === "string").map((f) => ({ path: String(f.path).slice(0, 200), lines: Number.isInteger(f.lines) ? f.lines : 0 })) : [],
34811
- executed_output: typeof o.executed_output === "string" ? o.executed_output.slice(0, 2e3) : ""
34815
+ executed_output: typeof o.executed_output === "string" ? o.executed_output.slice(0, 2e3) : "",
34816
+ impact: isImpact(o.impact) ? o.impact : "none"
34812
34817
  };
34813
34818
  }
34814
34819
  } catch {
@@ -35009,6 +35014,7 @@ async function runReplayInContainer(brief, opts) {
35009
35014
  ...judged,
35010
35015
  executedCalls,
35011
35016
  shellCalls,
35017
+ ...judged.verdict === "reproduced" && parsed.claim ? { impact: parsed.claim.impact } : {},
35012
35018
  executedOutput: parsed.claim?.executed_output ?? "",
35013
35019
  harnessFiles: parsed.claim?.harness_files ?? [],
35014
35020
  usage,
@@ -35067,6 +35073,7 @@ async function runReplayViaServer(brief, auth2, budgetMs = 24e4) {
35067
35073
  verdict: verdict === "reproduced" || verdict === "diverged" || verdict === "unreplayable" ? verdict : "unverified",
35068
35074
  reason: typeof d.reason === "string" ? d.reason : "",
35069
35075
  downgrade: typeof d.downgrade === "string" ? d.downgrade : null,
35076
+ ...isImpact(d.impact) ? { impact: d.impact } : {},
35070
35077
  executedCalls: Number(d.executed_calls ?? 0),
35071
35078
  shellCalls: Number(d.shell_calls ?? 0),
35072
35079
  verbatim: { covered: Number(v.covered ?? 0), total: Number(v.total ?? 0), ratio: Number(v.ratio ?? 0), basis: v.basis === "symbol" || v.basis === "file" ? v.basis : "none" },
@@ -36021,18 +36028,33 @@ function writeResult(root, report) {
36021
36028
  function isCrashTrace(trace) {
36022
36029
  return trace.some((s) => /\b(crash\w*|kill\w*|restart\w*|interrupt\w*|power|abort\w*|process (dies|exit\w*)|lost (reply|response))\b/i.test(s));
36023
36030
  }
36031
+ var SEVERITY_BY_IMPACT = {
36032
+ money: "high",
36033
+ access: "high",
36034
+ data: "high",
36035
+ external: "high",
36036
+ count: "medium",
36037
+ cache: "medium",
36038
+ internal: "medium",
36039
+ // A reproduced finding always has a real code; 'none' here is a malformed answer, so it blocks.
36040
+ none: "high"
36041
+ };
36042
+ function formalSeverity(f) {
36043
+ return f.impact && SEVERITY_BY_IMPACT[f.impact] || "high";
36044
+ }
36024
36045
  function mergeFormalFindings(response, outcome, pushedFiles) {
36025
- if (!outcome) return { blocking: 0 };
36046
+ if (!outcome) return { blocking: 0, warning: 0 };
36026
36047
  const norm2 = (p) => p.replace(/\\/g, "/");
36027
36048
  const pushed = new Set(pushedFiles.map(norm2));
36028
- const blocking = outcome.findings.filter((f) => f.verdict === "reproduced" && !f.waived && pushed.has(norm2(f.file)));
36029
- if (blocking.length === 0) return { blocking: 0 };
36049
+ const reproduced = outcome.findings.filter((f) => f.verdict === "reproduced" && !f.waived && pushed.has(norm2(f.file)));
36050
+ if (reproduced.length === 0) return { blocking: 0, warning: 0 };
36051
+ const blocking = reproduced.filter((f) => formalSeverity(f) === "high");
36030
36052
  const existing = Array.isArray(response.findings) ? response.findings : [];
36031
36053
  response.findings = [
36032
36054
  ...existing,
36033
- ...blocking.map((f) => ({
36055
+ ...reproduced.map((f) => ({
36034
36056
  pattern_id: "formal-counterexample",
36035
- severity: "high",
36057
+ severity: formalSeverity(f),
36036
36058
  scope: "change",
36037
36059
  file: f.file,
36038
36060
  line: outcome.target?.lines?.from ?? null,
@@ -36040,14 +36062,15 @@ function mergeFormalFindings(response, outcome, pushedFiles) {
36040
36062
  // as a title it read backwards (e2e, 2026-09-26). A trace that needs a
36041
36063
  // crash between two writes is labelled: reproducing it means injecting
36042
36064
  // the crash, so it is a crash-consistency finding, not a race.
36043
- title: `Formal: ${terminalText(f.symbol, 128)} breaks "${terminalText(f.desc, 280)}"${isCrashTrace(f.trace) ? " after a crash (crash consistency)" : ""} \u2014 reproduced on the real code`,
36065
+ title: `Formal: ${terminalText(f.symbol, 128)} breaks "${terminalText(f.desc, 280)}"${isCrashTrace(f.trace) ? " after a crash (crash consistency)" : ""} \u2014 reproduced on the real code${f.impact && f.impact !== "none" ? ` (impact: ${f.impact})` : ""}`,
36044
36066
  description: terminalText(f.reason ?? "", 2e3),
36045
36067
  fix: { description: `Trace: ${terminalText(f.trace.join(" \u2192 ") || "(initial state)", 2e3)}. Replay: ${terminalText(f.reason ?? "reproduced in the container", 2e3)}` },
36046
36068
  source: "formal"
36047
36069
  }))
36048
36070
  ];
36049
- if (response.gate_decision !== "FAIL") response.gate_decision = "FAIL";
36050
- return { blocking: blocking.length };
36071
+ if (blocking.length > 0) response.gate_decision = "FAIL";
36072
+ else if (response.gate_decision === "PASS" || response.gate_decision == null) response.gate_decision = "WARN";
36073
+ return { blocking: blocking.length, warning: reproduced.length - blocking.length };
36051
36074
  }
36052
36075
 
36053
36076
  // src/lib/prove/scalpel.ts
@@ -36356,7 +36379,9 @@ async function runFormalPhase(input) {
36356
36379
  symbol: f.symbol,
36357
36380
  verdict: "reproduced",
36358
36381
  reason: f.waived ? `waived ${f.waived.at.slice(0, 10)}: ${f.waived.reason} (standing since ${f.at.slice(0, 10)}; the file has not changed)` : `${f.reason ?? "reproduced on the real code"} (standing since ${f.at.slice(0, 10)}; the file has not changed)`,
36359
- ...f.waived ? { waived: true } : {}
36382
+ ...f.waived ? { waived: true } : {},
36383
+ // A hand-edited file cannot invent a code: unknown values read as absent (HIGH).
36384
+ ...isImpact(f.impact) ? { impact: f.impact } : {}
36360
36385
  });
36361
36386
  }
36362
36387
  }
@@ -36629,7 +36654,7 @@ async function runFormalPhase(input) {
36629
36654
  continue;
36630
36655
  }
36631
36656
  const verdict = r.verdict === "reproduced" ? "reproduced" : r.verdict === "diverged" ? "diverged" : r.verdict === "unreplayable" ? "model-only" : "unverified";
36632
- findings.push({ ...baseOf(p), verdict, reason: r.reason || r.downgrade || void 0 });
36657
+ findings.push({ ...baseOf(p), verdict, reason: r.reason || r.downgrade || void 0, ...verdict === "reproduced" && r.impact ? { impact: r.impact } : {} });
36633
36658
  }
36634
36659
  const reproduced = findings.filter((f) => f.verdict === "reproduced");
36635
36660
  if (reproduced.length > 0 && !cancelled()) {
@@ -36637,7 +36662,7 @@ async function runFormalPhase(input) {
36637
36662
  const others = readPersistedFindings(root).filter((f) => f.file !== top.file);
36638
36663
  writePersistedFindings(root, [
36639
36664
  ...others,
36640
- ...reproduced.map((f) => ({ file: f.file, symbol: f.symbol, sha256: sha2562, property: f.property, desc: f.desc, kind: f.kind, trace: f.trace, model: modelName, reason: f.reason, ...lines ? { lines } : {}, at: now }))
36665
+ ...reproduced.map((f) => ({ file: f.file, symbol: f.symbol, sha256: sha2562, property: f.property, desc: f.desc, kind: f.kind, trace: f.trace, model: modelName, reason: f.reason, ...f.impact ? { impact: f.impact } : {}, ...lines ? { lines } : {}, at: now }))
36641
36666
  ]);
36642
36667
  }
36643
36668
  const clean2 = findings.every((f) => f.verdict === "diverged");
@@ -37375,6 +37400,8 @@ async function runGuard(opts, globals) {
37375
37400
  rounds: formalOutcome.model?.rounds ?? null,
37376
37401
  properties: formalOutcome.properties?.map((p) => `${p.id}:${p.result}`) ?? [],
37377
37402
  findings: formalOutcome.findings.map((f) => `${f.property}:${f.verdict}`),
37403
+ // Decision 82: the replay's impact code per reproduced finding; count/cache/internal warn, the rest block.
37404
+ impacts: formalOutcome.findings.filter((f) => f.impact).map((f) => `${f.property}:${f.impact}`),
37378
37405
  // Decision 81: what each property rests on (a rule, or "line"), what was
37379
37406
  // sent back or dropped as ungrounded, and which counterexamples lack their
37380
37407
  // property's trigger (shadow). Ids and rule names only, never code.
@@ -39777,7 +39804,7 @@ function registerInitCommand(program2) {
39777
39804
  ...telemetryChoice ? { telemetry: telemetryChoice } : {},
39778
39805
  init: {
39779
39806
  completed_at: (/* @__PURE__ */ new Date()).toISOString(),
39780
- cli_version: true ? "0.33.3-experimental.f081164" : "dev"
39807
+ cli_version: true ? "0.34.0-experimental.007b74f" : "dev"
39781
39808
  }
39782
39809
  });
39783
39810
  } catch (err2) {
@@ -41250,8 +41277,8 @@ function registerLeanCommand(program2) {
41250
41277
  }
41251
41278
 
41252
41279
  // src/cli.ts
41253
- program.name("verity").description("CLI for Verity quality gate service").version("0.33.3-experimental.f081164").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async (_thisCommand, actionCommand) => {
41254
- installStderrLog(actionCommand.name(), process.argv.slice(2), "0.33.3-experimental.f081164");
41280
+ program.name("verity").description("CLI for Verity quality gate service").version("0.34.0-experimental.007b74f").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async (_thisCommand, actionCommand) => {
41281
+ installStderrLog(actionCommand.name(), process.argv.slice(2), "0.34.0-experimental.007b74f");
41255
41282
  setUserNamedServiceUrl(program.opts().serviceUrl);
41256
41283
  try {
41257
41284
  await foldLegacyLocalCredential();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codacy/verity-cli",
3
- "version": "0.33.3-experimental.f081164",
3
+ "version": "0.34.0-experimental.007b74f",
4
4
  "description": "CLI for Verity quality gate service",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://verity.md",