@codacy/verity-cli 0.28.1-experimental.39013ce → 0.28.1-experimental.4410c26

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 +196 -42
  2. package/package.json +1 -1
package/bin/verity.js CHANGED
@@ -10386,10 +10386,9 @@ function projectPath(relativePath) {
10386
10386
  return (0, import_node_path.join)(repoRoot(), relativePath);
10387
10387
  }
10388
10388
  var MAX_DELTA_BYTES = 194560;
10389
- var MAX_FILES = 20;
10389
+ var MAX_FILES = 40;
10390
10390
  var MAX_FILE_BYTES = 51200;
10391
10391
  var DEBOUNCE_SECONDS = 30;
10392
- var MAX_ITERATIONS = 2;
10393
10392
  var MAX_SPEC_FILES = 10;
10394
10393
  var MAX_SPEC_FILE_BYTES = 10240;
10395
10394
  var MAX_TOTAL_SPEC_BYTES = 30720;
@@ -15543,6 +15542,34 @@ ${addedLines}`,
15543
15542
  }
15544
15543
  return { diffs, has_baseline: true };
15545
15544
  }
15545
+ function absorbIntoBaseline(paths, sessionId) {
15546
+ const baseline = readBaseline(sessionId);
15547
+ if (!baseline || paths.length === 0) return 0;
15548
+ const dir = sessionDir(sessionKey(baseline.session_id));
15549
+ let adopted = 0;
15550
+ const dirty = new Set(baseline.dirty_paths);
15551
+ for (const p of paths) {
15552
+ try {
15553
+ const content = safeReadForMirror(projectPath(p));
15554
+ if (content === null) continue;
15555
+ const dest = mirrorPath(dir, p);
15556
+ (0, import_node_fs13.mkdirSync)((0, import_node_path14.dirname)(dest), { recursive: true });
15557
+ (0, import_node_fs13.writeFileSync)(dest, content);
15558
+ dirty.add(p);
15559
+ adopted++;
15560
+ } catch {
15561
+ }
15562
+ }
15563
+ if (adopted === 0) return 0;
15564
+ try {
15565
+ const updated = { ...baseline, dirty_paths: [...dirty] };
15566
+ (0, import_node_fs13.writeFileSync)(manifestPath(dir), JSON.stringify(updated));
15567
+ preImageCache.delete(baseline);
15568
+ } catch {
15569
+ return 0;
15570
+ }
15571
+ return adopted;
15572
+ }
15546
15573
  function changedSinceBaseline(repoRelPath, baseline) {
15547
15574
  const pre = preImage(repoRelPath, baseline);
15548
15575
  let current;
@@ -16419,40 +16446,40 @@ function narrowToRecent(files, sessionId) {
16419
16446
  });
16420
16447
  return recent.length > 0 ? recent : files;
16421
16448
  }
16422
- function readIteration(currentCommit, _contentHash) {
16423
- if (!(0, import_node_fs15.existsSync)(ITERATION_FILE)) return 1;
16449
+ function readIterationState(currentCommit) {
16450
+ if (!(0, import_node_fs15.existsSync)(ITERATION_FILE)) return { iteration: 1, fingerprint: null };
16424
16451
  try {
16425
16452
  const stored = (0, import_node_fs15.readFileSync)(ITERATION_FILE, "utf-8").trim();
16426
16453
  const parts = stored.split(":");
16427
16454
  const iter = parseInt(parts[0], 10);
16428
16455
  const storedCommit = parts[1] ?? "";
16429
16456
  const storedTimestamp = parseInt(parts[2] ?? "0", 10);
16430
- if (isNaN(iter)) return 1;
16431
- if (storedCommit !== currentCommit) return 1;
16457
+ const fingerprint = parts.slice(3).join(":") || null;
16458
+ if (isNaN(iter)) return { iteration: 1, fingerprint: null };
16459
+ if (storedCommit !== currentCommit) return { iteration: 1, fingerprint: null };
16432
16460
  if (storedTimestamp > 0) {
16433
16461
  const elapsed = Math.floor(Date.now() / 1e3) - storedTimestamp;
16434
- if (elapsed > 600) return 1;
16462
+ if (elapsed > 600) return { iteration: 1, fingerprint: null };
16435
16463
  }
16436
- return iter;
16464
+ return { iteration: iter, fingerprint };
16437
16465
  } catch {
16438
- return 1;
16466
+ return { iteration: 1, fingerprint: null };
16439
16467
  }
16440
16468
  }
16441
- function checkMaxIterations(currentCommit, maxIterations = MAX_ITERATIONS, contentHash) {
16442
- const iteration = readIteration(currentCommit, contentHash);
16443
- if (iteration > maxIterations) {
16444
- writeIteration(1, currentCommit, contentHash);
16445
- return {
16446
- skip: `Max Verity iterations (${maxIterations}) reached \u2014 accepting to prevent infinite loop. Human review required before deploying.`,
16447
- iteration
16448
- };
16449
- }
16450
- return { skip: null, iteration };
16469
+ function findingsFingerprint(findings) {
16470
+ const keys = findings.map((f) => `${String(f.pattern_id ?? "?")}|${String(f.file ?? "?")}`).filter((k) => k !== "?|?");
16471
+ return [...new Set(keys)].sort().join(",");
16472
+ }
16473
+ function isSameProblem(previous, current) {
16474
+ if (!previous || !current) return false;
16475
+ const prev = new Set(previous.split(","));
16476
+ return current.split(",").some((k) => prev.has(k));
16451
16477
  }
16452
- function writeIteration(iteration, commit, _contentHash) {
16478
+ function writeIteration(iteration, commit, _contentHash, fingerprint) {
16453
16479
  (0, import_node_fs15.mkdirSync)(VERITY_DIR, { recursive: true });
16454
16480
  const ts = Math.floor(Date.now() / 1e3);
16455
- (0, import_node_fs15.writeFileSync)(ITERATION_FILE, `${iteration}:${commit}:${ts}`);
16481
+ const fp = fingerprint ? `:${fingerprint}` : "";
16482
+ (0, import_node_fs15.writeFileSync)(ITERATION_FILE, `${iteration}:${commit}:${ts}${fp}`);
16456
16483
  }
16457
16484
 
16458
16485
  // src/lib/static-analysis.ts
@@ -16701,7 +16728,7 @@ function resolveTaskContext(opts) {
16701
16728
  // src/lib/cli-version.ts
16702
16729
  function cliVersion() {
16703
16730
  try {
16704
- return true ? "0.28.1-experimental.39013ce" : "dev";
16731
+ return true ? "0.28.1-experimental.4410c26" : "dev";
16705
16732
  } catch {
16706
16733
  return "dev";
16707
16734
  }
@@ -17375,6 +17402,7 @@ function buildAgentContext(input) {
17375
17402
  }
17376
17403
  for (const p of input.pendingItems ?? []) {
17377
17404
  if (lines.length >= MAX_AGENT_ITEMS) break;
17405
+ if (p.pattern_id === "intent-misalignment") continue;
17378
17406
  const text = p.description ?? p.title ?? p.reason;
17379
17407
  if (!text) continue;
17380
17408
  lines.push(renderItem("", text, p.pattern_id, p.file, p.line));
@@ -17638,9 +17666,11 @@ var MAX_SUMMARY_BYTES = 4096;
17638
17666
  var HOME = process.env.HOME ?? "";
17639
17667
  async function extractActionSummary(transcriptPath) {
17640
17668
  try {
17641
- const lines = readTurnLines(transcriptPath);
17642
- if (!lines || lines.length === 0) return null;
17643
- return buildSummary(lines);
17669
+ const read = readTurnLines(transcriptPath);
17670
+ if (!read || read.lines.length === 0) return null;
17671
+ const summary = buildSummary(read.lines);
17672
+ if (summary) summary.transcript_windowed = read.window;
17673
+ return summary;
17644
17674
  } catch {
17645
17675
  return null;
17646
17676
  }
@@ -17654,9 +17684,11 @@ function readTurnLines(transcriptPath) {
17654
17684
  }
17655
17685
  if (size === 0) return null;
17656
17686
  let raw;
17687
+ let windowed = false;
17657
17688
  if (size <= SMALL_FILE_BYTES) {
17658
17689
  raw = (0, import_node_fs22.readFileSync)(transcriptPath, "utf-8");
17659
17690
  } else {
17691
+ windowed = true;
17660
17692
  const buf = Buffer.alloc(Math.min(MAX_READ_BYTES, size));
17661
17693
  const fd = require("node:fs").openSync(transcriptPath, "r");
17662
17694
  try {
@@ -17674,17 +17706,22 @@ function readTurnLines(transcriptPath) {
17674
17706
  const allLines = raw.split("\n").filter((l) => l.trim().length > 0);
17675
17707
  if (allLines.length === 0) return null;
17676
17708
  let turnStart = 0;
17709
+ let boundaryFound = false;
17677
17710
  for (let i = allLines.length - 1; i >= 0; i--) {
17678
17711
  try {
17679
17712
  const parsed = JSON.parse(allLines[i]);
17680
17713
  if (parsed.type === "user" && isRealUserMessage(parsed)) {
17681
17714
  turnStart = i;
17715
+ boundaryFound = true;
17682
17716
  break;
17683
17717
  }
17684
17718
  } catch {
17685
17719
  }
17686
17720
  }
17687
- return allLines.slice(turnStart);
17721
+ return {
17722
+ lines: allLines.slice(turnStart),
17723
+ window: !windowed ? "whole" : boundaryFound ? "windowed" : "orphaned"
17724
+ };
17688
17725
  }
17689
17726
  function isRealUserMessage(parsed) {
17690
17727
  const message = parsed.message;
@@ -18281,7 +18318,7 @@ async function passAndExit(reason, skip, kindOverride) {
18281
18318
  if (unaccounted.length > 0) {
18282
18319
  logEvent("coverage_unaccounted", { where: "passAndExit", skip, count: unaccounted.length });
18283
18320
  }
18284
- const AGENT_SILENT_SKIPS = /* @__PURE__ */ new Set(["iteration-cap"]);
18321
+ const AGENT_SILENT_SKIPS = /* @__PURE__ */ new Set([]);
18285
18322
  const agentNote = AGENT_SILENT_SKIPS.has(skip) ? null : note;
18286
18323
  printJsonCompact(
18287
18324
  buildHookOutput(
@@ -18382,13 +18419,21 @@ async function runAnalyze(opts, globals) {
18382
18419
  const conversation = await readAndClearConversationBuffer(baselineSessionId);
18383
18420
  const specs = discoverSpecs();
18384
18421
  const plans = discoverPlans();
18422
+ const agentAuthoredCodeThisTurn = !!(actionSummary && (actionSummary.files_edited.length > 0 || actionSummary.files_created.length > 0));
18423
+ const turnAuthoredCode = agentAuthoredCodeThisTurn || !!baseline && allForReview.some((f) => changedSinceBaseline(f, baseline));
18424
+ const canSeeTurnAuthorship = !!actionSummary || !!baseline;
18385
18425
  const latestPrompt = conversation?.prompts?.[conversation.prompts.length - 1]?.prompt ?? "";
18386
18426
  if (/^\s*\/verity-/i.test(latestPrompt)) {
18427
+ const setupAuthored = [
18428
+ ...actionSummary?.files_edited ?? [],
18429
+ ...actionSummary?.files_created ?? []
18430
+ ];
18431
+ if (setupAuthored.length > 0) {
18432
+ const adopted = absorbIntoBaseline(setupAuthored, baselineSessionId);
18433
+ logEvent("baseline_absorbed", { skip: "verity-command", offered: setupAuthored.length, adopted });
18434
+ }
18387
18435
  await passAndExit("Verity command \u2014 skipping analysis", "verity-command");
18388
18436
  }
18389
- const agentAuthoredCodeThisTurn = !!(actionSummary && (actionSummary.files_edited.length > 0 || actionSummary.files_created.length > 0));
18390
- const turnAuthoredCode = agentAuthoredCodeThisTurn || !!baseline && allForReview.some((f) => changedSinceBaseline(f, baseline));
18391
- const canSeeTurnAuthorship = !!actionSummary || !!baseline;
18392
18437
  if (shouldSkipForBareAck({ prompt: latestPrompt, turnAuthoredCode, canSeeTurnAuthorship })) {
18393
18438
  await passAndExit("Bare acknowledgment \u2014 skipping analysis", "bare-acknowledgment");
18394
18439
  }
@@ -18438,7 +18483,11 @@ async function runAnalyze(opts, globals) {
18438
18483
  );
18439
18484
  }
18440
18485
  if (analysisMode === "skip") {
18441
- await passAndExit("Skip mode \u2014 no code work to analyze", "skip-mode");
18486
+ await passAndExit(
18487
+ "Skip mode \u2014 no code work to analyze",
18488
+ "skip-mode",
18489
+ turnAuthoredCode ? "capacity" : void 0
18490
+ );
18442
18491
  }
18443
18492
  let staticResults = {
18444
18493
  tool: "@codacy/analysis-cli",
@@ -18531,19 +18580,13 @@ async function runAnalyze(opts, globals) {
18531
18580
  snapshotResult = generateSnapshotDiffs(codeDelta.files);
18532
18581
  }
18533
18582
  currentCommit = getCurrentCommit();
18534
- const maxIterations = parseInt(opts.maxIterations, 10);
18535
- const iterResult = checkMaxIterations(currentCommit, maxIterations, contentHash ?? void 0);
18536
- if (iterResult.skip) await passAndExit(iterResult.skip, "iteration-cap");
18537
- iteration = iterResult.iteration;
18583
+ iteration = readIterationState(currentCommit).iteration;
18538
18584
  }
18539
18585
  }
18540
18586
  if (analysisMode === "plan") {
18541
18587
  recordAnalysisStart();
18542
18588
  currentCommit = getCurrentCommit();
18543
- const maxIterations = parseInt(opts.maxIterations, 10);
18544
- const iterResult = checkMaxIterations(currentCommit, maxIterations);
18545
- if (iterResult.skip) await passAndExit(iterResult.skip, "iteration-cap");
18546
- iteration = iterResult.iteration;
18589
+ iteration = readIterationState(currentCommit).iteration;
18547
18590
  }
18548
18591
  const contextFiles = gatherContextFiles(contextFilePaths, codeDelta.files);
18549
18592
  for (const f of codeDelta.files) {
@@ -18716,7 +18759,30 @@ async function runAnalyze(opts, globals) {
18716
18759
  hasUserPrompt: (conversation?.prompts?.length ?? 0) > 0,
18717
18760
  isTTY: process.stdout.isTTY === true
18718
18761
  });
18762
+ const excludedByReason = {};
18763
+ for (const e of codeDelta.excluded ?? []) {
18764
+ excludedByReason[e.reason] = (excludedByReason[e.reason] ?? 0) + 1;
18765
+ }
18766
+ const coverageTelemetry = {
18767
+ // git's whole answer, before ANY narrowing. The number that has never been sent.
18768
+ changed_all: allChanged.length,
18769
+ analyzable: analyzable.length,
18770
+ reviewable: reviewable.length,
18771
+ security: securityFiles.length,
18772
+ // after the allowlist, before authorship scoping and the caps
18773
+ for_review: allForReview.length,
18774
+ // what actually reaches the reviewer
18775
+ sent: codeDelta.files.length,
18776
+ // the two silent narrowings, counted separately so they can be told apart
18777
+ capped_out: actionSummary?.capped_out?.length ?? 0,
18778
+ excluded: (codeDelta.excluded ?? []).length,
18779
+ excluded_by_reason: excludedByReason,
18780
+ // was the transcript itself truncated? The 256 KB window means "this turn"
18781
+ // can quietly mean "the last 256 KB of it".
18782
+ transcript_windowed: actionSummary?.transcript_windowed ?? null
18783
+ };
18719
18784
  const requestBody = {
18785
+ coverage_telemetry: coverageTelemetry,
18720
18786
  static_results: staticResults,
18721
18787
  code_delta: codeDelta,
18722
18788
  changed_files: allForReview,
@@ -18802,6 +18868,54 @@ async function runAnalyze(opts, globals) {
18802
18868
  // replaced a population floor with ≈35% power that was sub-integer for
18803
18869
  // three-quarters of the fleet.
18804
18870
  conservation: foldConservation,
18871
+ // ⚠ VRT-52 — RECORDED, NOT APPLIED. The number nobody has.
18872
+ //
18873
+ // The whole "task-scoped delta" design space rests on an assumption that
18874
+ // has been observed exactly ONCE: that delta files routinely belong to
18875
+ // earlier work. Three designs were built on it and all three were killed
18876
+ // adversarially — two by measurement — so before another is attempted,
18877
+ // measure the base rate.
18878
+ //
18879
+ // `authored_under_earlier_goal` counts delta paths whose LAST authorship
18880
+ // event precedes the seq of the goal now in force. Both numbers come from
18881
+ // the same append-only counter (`nextSeq`), so the comparison is exact.
18882
+ //
18883
+ // Keyed on the GOAL, deliberately, not on the task id. The task classifier
18884
+ // reported `is_new_task` on two consecutive turns of one task 25 seconds
18885
+ // apart, so a task-keyed number would measure its unreliability rather
18886
+ // than the phenomenon. And this only became meaningful once `recordGoal`
18887
+ // stopped letting a bare "ok" supersede the goal — before that the seq
18888
+ // advanced every turn and this would have degenerated to "not edited this
18889
+ // turn", which is the exact mistake that sank one of the three designs.
18890
+ //
18891
+ // Changes no payload the reviewer sees, no narrowing, no verdict.
18892
+ vrt52: (() => {
18893
+ const goalSeq = memory?.projection.goal?.seq;
18894
+ if (goalSeq === void 0 || !memorySession) return { known: false };
18895
+ const lastSeq2 = new Map(
18896
+ foldDossier(memorySession.d).authored_all.map((a) => [a.path, a.last_seq])
18897
+ );
18898
+ let earlier = 0;
18899
+ let unknown = 0;
18900
+ for (const f of codeDelta.files) {
18901
+ const seen = lastSeq2.get(f.path);
18902
+ if (seen === void 0) unknown++;
18903
+ else if (seen < goalSeq) earlier++;
18904
+ }
18905
+ return {
18906
+ known: true,
18907
+ goal_seq: goalSeq,
18908
+ delta: codeDelta.files.length,
18909
+ // Files this delta carries that were last written under an EARLIER
18910
+ // instruction. If this stays near zero, VRT-52's code half is
18911
+ // unnecessary and should be closed saying so.
18912
+ authored_under_earlier_goal: earlier,
18913
+ // Delta files the dossier has no authorship record for at all —
18914
+ // pre-existing tree state, or an authorship channel the fold cannot
18915
+ // see. Reported separately so a blind spot is never counted as a zero.
18916
+ no_authorship_record: unknown
18917
+ };
18918
+ })(),
18805
18919
  // P2 — RECORDED, NOT APPLIED. What this run WOULD have reviewed if it
18806
18920
  // narrowed to the within-session increment: what changed since the last
18807
18921
  // VERDICT rather than since task start.
@@ -18836,6 +18950,14 @@ async function runAnalyze(opts, globals) {
18836
18950
  const latest = conversation.prompts[conversation.prompts.length - 1];
18837
18951
  const goalPrompt = resolveGoalPrompt(conversation.prompts) ?? { entry: latest, turnsBack: 0 };
18838
18952
  intentContext.user_prompt = goalPrompt.entry.prompt;
18953
+ if (isContinuationPrompt(intentContext.user_prompt)) {
18954
+ const carried = memory?.projection.goal?.text;
18955
+ if (carried && !isContinuationPrompt(carried)) {
18956
+ intentContext.continuation_prompt = latest.prompt;
18957
+ intentContext.user_prompt = carried;
18958
+ logEvent("goal_from_dossier", { chars: carried.length });
18959
+ }
18960
+ }
18839
18961
  if (goalPrompt.turnsBack > 0) {
18840
18962
  intentContext.continuation_prompt = latest.prompt;
18841
18963
  logEvent("goal_walked_back", { turns_back: goalPrompt.turnsBack });
@@ -19162,9 +19284,41 @@ async function runAnalyze(opts, globals) {
19162
19284
  reverify_by: response.reverify_by
19163
19285
  });
19164
19286
  const grantNudge = grantWarning ? ` \u26A0\uFE0F ${grantWarning}` : "";
19165
- switch (decision) {
19287
+ let capReleased = false;
19288
+ let effectiveDecision = decision;
19289
+ if (decision === "FAIL") {
19290
+ const blocking = (response.findings ?? []).filter((f) => {
19291
+ const sev = String(f.severity ?? "").toLowerCase();
19292
+ return sev === "critical" || sev === "high";
19293
+ });
19294
+ const fingerprint = findingsFingerprint(blocking);
19295
+ const prior = readIterationState(currentCommit);
19296
+ const sameProblem = isSameProblem(prior.fingerprint, fingerprint);
19297
+ const nextIteration = sameProblem ? prior.iteration + 1 : 1;
19298
+ const maxIterations = parseInt(opts.maxIterations, 10);
19299
+ writeIteration(nextIteration, currentCommit, contentHash ?? void 0, fingerprint);
19300
+ iteration = nextIteration;
19301
+ if (nextIteration > maxIterations) {
19302
+ capReleased = true;
19303
+ effectiveDecision = "WARN";
19304
+ logEvent("iteration_cap_released", { iteration: nextIteration, fingerprint });
19305
+ }
19306
+ }
19307
+ if (capReleased) {
19308
+ const findings = response.findings ?? [];
19309
+ const lines = findings.slice(0, 5).map((f) => ` [${String(f.severity ?? "?").toUpperCase()}] ${String(f.title ?? f.message ?? "")} (${String(f.file ?? "?")}:${String(f.line ?? "?")})`);
19310
+ emitVerdict({
19311
+ proposed: "WARN",
19312
+ changed: skipCoverageChanged,
19313
+ coverage: reviewCoverage,
19314
+ userSummary: `Verity: WARN \u2014 self-healing limit (${opts.maxIterations}) reached on the same finding. NO LONGER BLOCKING, but ${findings.length} finding(s) remain OPEN and were NOT fixed. Human review required before deploying.
19315
+ ${lines.join("\n")}`,
19316
+ agentContext: null,
19317
+ silenced: true
19318
+ });
19319
+ }
19320
+ switch (effectiveDecision) {
19166
19321
  case "FAIL": {
19167
- writeIteration(iteration + 1, currentCommit, contentHash ?? void 0);
19168
19322
  const assessment = response.assessment;
19169
19323
  const narrative = assessment?.narrative ?? "";
19170
19324
  const findings = response.findings ?? [];
@@ -21094,7 +21248,7 @@ function registerTelemetryCommands(program2) {
21094
21248
  }
21095
21249
 
21096
21250
  // src/cli.ts
21097
- program.name("verity").description("CLI for Verity quality gate service").version("0.28.1-experimental.39013ce").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async () => {
21251
+ program.name("verity").description("CLI for Verity quality gate service").version("0.28.1-experimental.4410c26").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async () => {
21098
21252
  try {
21099
21253
  await foldLegacyLocalCredential();
21100
21254
  } catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codacy/verity-cli",
3
- "version": "0.28.1-experimental.39013ce",
3
+ "version": "0.28.1-experimental.4410c26",
4
4
  "description": "CLI for Verity quality gate service",
5
5
  "homepage": "https://verity.md",
6
6
  "bugs": {