@herbertgao/pi-extensions 2026.9.7 → 2026.9.9

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 (158) hide show
  1. package/README.md +8 -4
  2. package/THIRD_PARTY_NOTICES.md +52 -0
  3. package/node_modules/@herbertgao/pi-bark/package.json +2 -2
  4. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +1 -1
  5. package/node_modules/@herbertgao/pi-cc-extensions/README.md +1 -1
  6. package/node_modules/@herbertgao/pi-cc-extensions/package.json +4 -4
  7. package/node_modules/@herbertgao/resume-from/package.json +2 -2
  8. package/node_modules/@narumitw/pi-btw/README.md +6 -1
  9. package/node_modules/@narumitw/pi-btw/dist/index.ts +209 -23
  10. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +4 -4
  11. package/node_modules/@narumitw/pi-btw/package.json +1 -1
  12. package/node_modules/@narumitw/pi-btw/src/btw.ts +1 -12
  13. package/node_modules/@narumitw/pi-btw/src/menu.ts +244 -15
  14. package/node_modules/@narumitw/pi-btw/src/settings.ts +5 -0
  15. package/node_modules/grok-mermaid/CHANGELOG.md +46 -0
  16. package/node_modules/grok-mermaid/LICENSE +205 -0
  17. package/node_modules/grok-mermaid/README.md +191 -0
  18. package/node_modules/grok-mermaid/dist/ansi.d.ts +16 -0
  19. package/node_modules/grok-mermaid/dist/ansi.d.ts.map +1 -0
  20. package/node_modules/grok-mermaid/dist/ansi.js +23 -0
  21. package/node_modules/grok-mermaid/dist/ansi.js.map +1 -0
  22. package/node_modules/grok-mermaid/dist/canvas.d.ts +87 -0
  23. package/node_modules/grok-mermaid/dist/canvas.d.ts.map +1 -0
  24. package/node_modules/grok-mermaid/dist/canvas.js +366 -0
  25. package/node_modules/grok-mermaid/dist/canvas.js.map +1 -0
  26. package/node_modules/grok-mermaid/dist/graph.d.ts +74 -0
  27. package/node_modules/grok-mermaid/dist/graph.d.ts.map +1 -0
  28. package/node_modules/grok-mermaid/dist/graph.js +91 -0
  29. package/node_modules/grok-mermaid/dist/graph.js.map +1 -0
  30. package/node_modules/grok-mermaid/dist/index.d.ts +32 -0
  31. package/node_modules/grok-mermaid/dist/index.d.ts.map +1 -0
  32. package/node_modules/grok-mermaid/dist/index.js +100 -0
  33. package/node_modules/grok-mermaid/dist/index.js.map +1 -0
  34. package/node_modules/grok-mermaid/dist/labels.d.ts +62 -0
  35. package/node_modules/grok-mermaid/dist/labels.d.ts.map +1 -0
  36. package/node_modules/grok-mermaid/dist/labels.js +324 -0
  37. package/node_modules/grok-mermaid/dist/labels.js.map +1 -0
  38. package/node_modules/grok-mermaid/dist/layout-seq.d.ts +12 -0
  39. package/node_modules/grok-mermaid/dist/layout-seq.d.ts.map +1 -0
  40. package/node_modules/grok-mermaid/dist/layout-seq.js +194 -0
  41. package/node_modules/grok-mermaid/dist/layout-seq.js.map +1 -0
  42. package/node_modules/grok-mermaid/dist/layout.d.ts +87 -0
  43. package/node_modules/grok-mermaid/dist/layout.d.ts.map +1 -0
  44. package/node_modules/grok-mermaid/dist/layout.js +881 -0
  45. package/node_modules/grok-mermaid/dist/layout.js.map +1 -0
  46. package/node_modules/grok-mermaid/dist/parse.d.ts +83 -0
  47. package/node_modules/grok-mermaid/dist/parse.d.ts.map +1 -0
  48. package/node_modules/grok-mermaid/dist/parse.js +1151 -0
  49. package/node_modules/grok-mermaid/dist/parse.js.map +1 -0
  50. package/node_modules/grok-mermaid/dist/source-box.d.ts +18 -0
  51. package/node_modules/grok-mermaid/dist/source-box.d.ts.map +1 -0
  52. package/node_modules/grok-mermaid/dist/source-box.js +78 -0
  53. package/node_modules/grok-mermaid/dist/source-box.js.map +1 -0
  54. package/node_modules/grok-mermaid/dist/types.d.ts +42 -0
  55. package/node_modules/grok-mermaid/dist/types.d.ts.map +1 -0
  56. package/node_modules/grok-mermaid/dist/types.js +1 -0
  57. package/node_modules/grok-mermaid/dist/types.js.map +1 -0
  58. package/node_modules/grok-mermaid/dist/width-data.d.ts +2 -0
  59. package/node_modules/grok-mermaid/dist/width-data.d.ts.map +1 -0
  60. package/node_modules/grok-mermaid/dist/width-data.js +994 -0
  61. package/node_modules/grok-mermaid/dist/width-data.js.map +1 -0
  62. package/node_modules/grok-mermaid/dist/width.d.ts +18 -0
  63. package/node_modules/grok-mermaid/dist/width.d.ts.map +1 -0
  64. package/node_modules/grok-mermaid/dist/width.js +76 -0
  65. package/node_modules/grok-mermaid/dist/width.js.map +1 -0
  66. package/node_modules/grok-mermaid/package.json +49 -0
  67. package/node_modules/grok-mermaid/src/ansi.ts +34 -0
  68. package/node_modules/grok-mermaid/src/canvas.ts +373 -0
  69. package/node_modules/grok-mermaid/src/graph.ts +142 -0
  70. package/node_modules/grok-mermaid/src/index.ts +104 -0
  71. package/node_modules/grok-mermaid/src/labels.ts +326 -0
  72. package/node_modules/grok-mermaid/src/layout-seq.ts +203 -0
  73. package/node_modules/grok-mermaid/src/layout.ts +1015 -0
  74. package/node_modules/grok-mermaid/src/parse.ts +1189 -0
  75. package/node_modules/grok-mermaid/src/source-box.ts +89 -0
  76. package/node_modules/grok-mermaid/src/types.ts +43 -0
  77. package/node_modules/grok-mermaid/src/width-data.ts +993 -0
  78. package/node_modules/grok-mermaid/src/width.ts +74 -0
  79. package/node_modules/pi-jev-auto-mode/CHANGELOG.md +110 -0
  80. package/node_modules/pi-jev-auto-mode/LICENSE +21 -0
  81. package/node_modules/pi-jev-auto-mode/README.md +285 -0
  82. package/node_modules/pi-jev-auto-mode/SECURITY.md +32 -0
  83. package/node_modules/pi-jev-auto-mode/docs/calibration.md +154 -0
  84. package/node_modules/pi-jev-auto-mode/docs/design.md +164 -0
  85. package/node_modules/pi-jev-auto-mode/docs/security.md +124 -0
  86. package/node_modules/pi-jev-auto-mode/index.ts +1 -0
  87. package/node_modules/pi-jev-auto-mode/package.json +72 -0
  88. package/node_modules/pi-jev-auto-mode/src/call.ts +180 -0
  89. package/node_modules/pi-jev-auto-mode/src/decide.ts +86 -0
  90. package/node_modules/pi-jev-auto-mode/src/extension.ts +862 -0
  91. package/node_modules/pi-jev-auto-mode/src/intent.ts +71 -0
  92. package/node_modules/pi-jev-auto-mode/src/jev/availability.ts +53 -0
  93. package/node_modules/pi-jev-auto-mode/src/jev/criteria.ts +19 -0
  94. package/node_modules/pi-jev-auto-mode/src/jev/decide.ts +187 -0
  95. package/node_modules/pi-jev-auto-mode/src/jev/engine.ts +165 -0
  96. package/node_modules/pi-jev-auto-mode/src/jev/index.ts +20 -0
  97. package/node_modules/pi-jev-auto-mode/src/jev/questions.ts +273 -0
  98. package/node_modules/pi-jev-auto-mode/src/jev/response.ts +64 -0
  99. package/node_modules/pi-jev-auto-mode/src/jev/state.ts +20 -0
  100. package/node_modules/pi-jev-auto-mode/src/jev/transport.ts +117 -0
  101. package/node_modules/pi-jev-auto-mode/src/jev/types.ts +46 -0
  102. package/node_modules/pi-jev-auto-mode/src/policy.ts +558 -0
  103. package/node_modules/pi-jev-auto-mode/src/records.ts +118 -0
  104. package/node_modules/pi-jev-auto-mode/src/settings.ts +327 -0
  105. package/node_modules/pi-jev-auto-mode/src/ui.ts +233 -0
  106. package/node_modules/pi-lens/CHANGELOG.md +45 -0
  107. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +21 -9
  108. package/node_modules/pi-lens/dist/clients/cascade-format.js +24 -0
  109. package/node_modules/pi-lens/dist/clients/extension-log.js +48 -1
  110. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +11 -12
  111. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +3 -0
  112. package/node_modules/pi-lens/dist/clients/lsp/index.js +41 -7
  113. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +9 -2
  114. package/node_modules/pi-lens/dist/clients/path-utils.js +51 -9
  115. package/node_modules/pi-lens/dist/clients/persistent-reverify.js +271 -0
  116. package/node_modules/pi-lens/dist/clients/pipeline.js +15 -1
  117. package/node_modules/pi-lens/dist/clients/runtime-turn.js +124 -7
  118. package/node_modules/pi-lens/dist/clients/skills-resolver.js +38 -2
  119. package/node_modules/pi-lens/dist/clients/widget-state.js +152 -6
  120. package/node_modules/pi-lens/dist/index.js +729 -344
  121. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +14 -2
  122. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +84 -17
  123. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +32 -5
  124. package/node_modules/pi-lens/package.json +1 -1
  125. package/node_modules/pi-typesafe/LICENSE +21 -0
  126. package/node_modules/pi-typesafe/README.md +153 -0
  127. package/node_modules/pi-typesafe/dist/ask.d.ts +32 -0
  128. package/node_modules/pi-typesafe/dist/ask.js +25 -0
  129. package/node_modules/pi-typesafe/dist/auth.d.ts +56 -0
  130. package/node_modules/pi-typesafe/dist/auth.js +114 -0
  131. package/node_modules/pi-typesafe/dist/batch.d.ts +74 -0
  132. package/node_modules/pi-typesafe/dist/batch.js +116 -0
  133. package/node_modules/pi-typesafe/dist/calibrate.d.ts +106 -0
  134. package/node_modules/pi-typesafe/dist/calibrate.js +157 -0
  135. package/node_modules/pi-typesafe/dist/client.d.ts +69 -0
  136. package/node_modules/pi-typesafe/dist/client.js +188 -0
  137. package/node_modules/pi-typesafe/dist/credentials.d.ts +46 -0
  138. package/node_modules/pi-typesafe/dist/credentials.js +112 -0
  139. package/node_modules/pi-typesafe/dist/errors.d.ts +9 -0
  140. package/node_modules/pi-typesafe/dist/errors.js +31 -0
  141. package/node_modules/pi-typesafe/dist/extension.d.ts +3 -0
  142. package/node_modules/pi-typesafe/dist/extension.js +222 -0
  143. package/node_modules/pi-typesafe/dist/index.d.ts +18 -0
  144. package/node_modules/pi-typesafe/dist/index.js +9 -0
  145. package/node_modules/pi-typesafe/dist/key-prompt.d.ts +3 -0
  146. package/node_modules/pi-typesafe/dist/key-prompt.js +43 -0
  147. package/node_modules/pi-typesafe/dist/login.d.ts +27 -0
  148. package/node_modules/pi-typesafe/dist/login.js +38 -0
  149. package/node_modules/pi-typesafe/dist/schema.d.ts +43 -0
  150. package/node_modules/pi-typesafe/dist/schema.js +152 -0
  151. package/node_modules/pi-typesafe/dist/ui.d.ts +3 -0
  152. package/node_modules/pi-typesafe/dist/ui.js +3 -0
  153. package/node_modules/pi-typesafe/dist/usage.d.ts +73 -0
  154. package/node_modules/pi-typesafe/dist/usage.js +182 -0
  155. package/node_modules/pi-typesafe/examples/decision-extension.ts +41 -0
  156. package/node_modules/pi-typesafe/extensions/index.js +2 -0
  157. package/node_modules/pi-typesafe/package.json +89 -0
  158. package/package.json +16 -8
@@ -2041,16 +2041,17 @@ function adoptCanonicalCasing(held, canonical) {
2041
2041
  function normalizeFilePath(filePath) {
2042
2042
  const normalized = filePath.replace(/\\/g, "/");
2043
2043
  if (process.platform !== "win32" && !isWindowsPath(normalized)) {
2044
+ const folded = normalized === "" || normalized.startsWith("//") ? normalized : path2.posix.normalize(normalized);
2044
2045
  try {
2045
- const canonical = realpathSync.native(normalized);
2046
- if (canonical === normalized)
2047
- return normalized;
2048
- const adopted = adoptCanonicalCasing(normalized, canonical);
2049
- if (adopted === normalized)
2050
- return normalized;
2051
- return realpathSync.native(adopted) === canonical ? adopted : normalized;
2046
+ const canonical = realpathSync.native(folded);
2047
+ if (canonical === folded)
2048
+ return folded;
2049
+ const adopted = adoptCanonicalCasing(folded, canonical);
2050
+ if (adopted === folded)
2051
+ return folded;
2052
+ return realpathSync.native(adopted) === canonical ? adopted : folded;
2052
2053
  } catch {
2053
- return normalized;
2054
+ return folded;
2054
2055
  }
2055
2056
  }
2056
2057
  try {
@@ -3744,6 +3745,9 @@ function inCaptureWindow(fn) {
3744
3745
  function isCaptureSeam(prop) {
3745
3746
  return prop === "on" || typeof prop === "string" && prop.startsWith("register");
3746
3747
  }
3748
+ function isCallableSchema(value) {
3749
+ return typeof value === "function" && typeof value.toJsonSchema === "function" && typeof value.assert === "function";
3750
+ }
3747
3751
  function wrapFunctionsInPlace(value) {
3748
3752
  if (typeof value === "function") {
3749
3753
  return inCaptureWindow(value);
@@ -3754,7 +3758,7 @@ function wrapFunctionsInPlace(value) {
3754
3758
  const obj = value;
3755
3759
  for (const key4 of Object.keys(obj)) {
3756
3760
  const propValue = obj[key4];
3757
- if (typeof propValue === "function") {
3761
+ if (typeof propValue === "function" && !isCallableSchema(propValue)) {
3758
3762
  assignWrapped(obj, key4, propValue);
3759
3763
  }
3760
3764
  }
@@ -6413,7 +6417,7 @@ var require_parallel = __commonJS({
6413
6417
  var require_cpu = __commonJS({
6414
6418
  "node_modules/pidusage/lib/helpers/cpu.js"(exports, module) {
6415
6419
  var os37 = __require("os");
6416
- var fs134 = __require("fs");
6420
+ var fs135 = __require("fs");
6417
6421
  var exec = __require("child_process").exec;
6418
6422
  var parallel = require_parallel();
6419
6423
  function updateCpu(cpu, next) {
@@ -6440,7 +6444,7 @@ var require_cpu = __commonJS({
6440
6444
  }
6441
6445
  module.exports = updateCpu;
6442
6446
  function getRealUptime(next) {
6443
- fs134.readFile("/proc/uptime", "utf8", function(err, uptime) {
6447
+ fs135.readFile("/proc/uptime", "utf8", function(err, uptime) {
6444
6448
  if (err || uptime === void 0) {
6445
6449
  if (!process.env.PIDUSAGE_SILENT) {
6446
6450
  console.warn("[pidusage] We couldn't find uptime from /proc/uptime, using os.uptime() value");
@@ -6546,7 +6550,7 @@ var require_safe_buffer = __commonJS({
6546
6550
  // node_modules/pidusage/lib/procfile.js
6547
6551
  var require_procfile = __commonJS({
6548
6552
  "node_modules/pidusage/lib/procfile.js"(exports, module) {
6549
- var fs134 = __require("fs");
6553
+ var fs135 = __require("fs");
6550
6554
  var path211 = __require("path");
6551
6555
  var updateCpu = require_cpu();
6552
6556
  var parallel = require_parallel();
@@ -6560,11 +6564,11 @@ var require_procfile = __commonJS({
6560
6564
  if (history2.fd) {
6561
6565
  return cb(null, history2.fd);
6562
6566
  }
6563
- fs134.open(path212, "r", cb);
6567
+ fs135.open(path212, "r", cb);
6564
6568
  }
6565
6569
  function close(history2) {
6566
6570
  if (history2.fd) {
6567
- fs134.close(history2.fd, noop);
6571
+ fs135.close(history2.fd, noop);
6568
6572
  }
6569
6573
  }
6570
6574
  function readUntilEnd(fd, buf, cb) {
@@ -6574,7 +6578,7 @@ var require_procfile = __commonJS({
6574
6578
  buf = Buffer2.alloc(SIZE);
6575
6579
  firstRead = true;
6576
6580
  }
6577
- fs134.read(fd, buf, 0, SIZE, 0, function(err, bytesRead, buffer) {
6581
+ fs135.read(fd, buf, 0, SIZE, 0, function(err, bytesRead, buffer) {
6578
6582
  if (err) {
6579
6583
  cb(err);
6580
6584
  return;
@@ -6857,7 +6861,7 @@ var require_gwmi = __commonJS({
6857
6861
  var require_stats = __commonJS({
6858
6862
  "node_modules/pidusage/lib/stats.js"(exports, module) {
6859
6863
  "use strict";
6860
- var fs134 = __require("fs");
6864
+ var fs135 = __require("fs");
6861
6865
  var os37 = __require("os");
6862
6866
  var spawn3 = __require("child_process").spawn;
6863
6867
  var requireMap = {
@@ -6881,7 +6885,7 @@ var require_stats = __commonJS({
6881
6885
  win: "wmic"
6882
6886
  };
6883
6887
  var platform = os37.platform();
6884
- if (fs134.existsSync("/etc/alpine-release")) {
6888
+ if (fs135.existsSync("/etc/alpine-release")) {
6885
6889
  platform = "alpine";
6886
6890
  }
6887
6891
  if (platform.match(/^win/)) {
@@ -15822,8 +15826,8 @@ var init_path_keyed_map = __esm({
15822
15826
  * already-resident path is an update, and must not drop an unrelated file
15823
15827
  * to make room for a key that needs none.
15824
15828
  */
15825
- constructor(normalize3, maxEntries) {
15826
- this.normalize = normalize3;
15829
+ constructor(normalize2, maxEntries) {
15830
+ this.normalize = normalize2;
15827
15831
  this.store = maxEntries === void 0 ? /* @__PURE__ */ new Map() : new BoundedFifoMap(maxEntries);
15828
15832
  }
15829
15833
  get size() {
@@ -18256,7 +18260,7 @@ function createGenerationSource(name) {
18256
18260
  }
18257
18261
  function createGenerationMap(name, options = {}) {
18258
18262
  declare(name);
18259
- const normalize3 = options.normalizeKey ?? ((key4) => key4);
18263
+ const normalize2 = options.normalizeKey ?? ((key4) => key4);
18260
18264
  const maxKeys = Math.max(1, options.maxKeys ?? DEFAULT_MAX_KEYS);
18261
18265
  const stamps = new BoundedFifoMap(maxKeys);
18262
18266
  let nextTicket = 0;
@@ -18272,25 +18276,25 @@ function createGenerationMap(name, options = {}) {
18272
18276
  return {
18273
18277
  name,
18274
18278
  current(key4) {
18275
- return read(normalize3(key4));
18279
+ return read(normalize2(key4));
18276
18280
  },
18277
18281
  bump(key4) {
18278
18282
  invalidations += 1;
18279
- return issue(normalize3(key4));
18283
+ return issue(normalize2(key4));
18280
18284
  },
18281
18285
  capture(key4) {
18282
- const normalized = normalize3(key4);
18286
+ const normalized = normalize2(key4);
18283
18287
  const stamp2 = read(normalized) || issue(normalized);
18284
18288
  const capturedInvalidations = invalidations;
18285
18289
  return makeHandle(`${name}[${normalized}]`, stamp2, () => {
18286
18290
  if (invalidations === capturedInvalidations)
18287
18291
  return stamp2;
18288
- return read(normalize3(key4));
18292
+ return read(normalize2(key4));
18289
18293
  });
18290
18294
  },
18291
18295
  forget(key4) {
18292
18296
  invalidations += 1;
18293
- stamps.delete(normalize3(key4));
18297
+ stamps.delete(normalize2(key4));
18294
18298
  },
18295
18299
  clear() {
18296
18300
  invalidations += 1;
@@ -31632,13 +31636,13 @@ async function findAncestorFile(startDir, ...relativeSegments) {
31632
31636
  return findAncestorFileAmong(startDir, [relativeSegments]);
31633
31637
  }
31634
31638
  async function findAncestorFileAmong(startDir, candidateSegmentLists) {
31635
- const fs134 = await import("node:fs/promises");
31639
+ const fs135 = await import("node:fs/promises");
31636
31640
  let currentDir = path50.resolve(startDir);
31637
31641
  while (!isAtOrAboveHomeDir(currentDir)) {
31638
31642
  for (const segments of candidateSegmentLists) {
31639
31643
  const candidate = path50.join(currentDir, ...segments);
31640
31644
  try {
31641
- await fs134.access(candidate);
31645
+ await fs135.access(candidate);
31642
31646
  return candidate;
31643
31647
  } catch {
31644
31648
  }
@@ -31692,13 +31696,13 @@ async function typescriptVersionForTsc(tscPath) {
31692
31696
  return void 0;
31693
31697
  }
31694
31698
  async function findTsserverPath(root, allowInstall) {
31695
- const fs134 = await import("node:fs/promises");
31699
+ const fs135 = await import("node:fs/promises");
31696
31700
  const ancestorHit = await findAncestorFile(root, "node_modules", "typescript", "lib", "tsserver.js");
31697
31701
  if (ancestorHit)
31698
31702
  return ancestorHit;
31699
31703
  const cwdCandidate = path50.join(process.cwd(), "node_modules", "typescript", "lib", "tsserver.js");
31700
31704
  try {
31701
- await fs134.access(cwdCandidate);
31705
+ await fs135.access(cwdCandidate);
31702
31706
  return cwdCandidate;
31703
31707
  } catch {
31704
31708
  }
@@ -31708,7 +31712,7 @@ async function findTsserverPath(root, allowInstall) {
31708
31712
  for (const dir of typescriptDirsForTsc(tscPath)) {
31709
31713
  const candidate = path50.join(dir, "lib", "tsserver.js");
31710
31714
  try {
31711
- await fs134.access(candidate);
31715
+ await fs135.access(candidate);
31712
31716
  return candidate;
31713
31717
  } catch {
31714
31718
  }
@@ -32203,7 +32207,7 @@ var init_server = __esm({
32203
32207
  root: TypeScriptRoot,
32204
32208
  rootMarkers: TypeScriptRoot.rootMarkers,
32205
32209
  async spawn(root, options) {
32206
- const fs134 = await import("node:fs/promises");
32210
+ const fs135 = await import("node:fs/promises");
32207
32211
  const nativeLsp = await findNativeTypeScriptLsp(root);
32208
32212
  if (nativeLsp) {
32209
32213
  const env2 = await getToolEnvironment();
@@ -32233,7 +32237,7 @@ var init_server = __esm({
32233
32237
  if (!tsserverPath) {
32234
32238
  const localCandidate = path50.join(path50.dirname(lspPath), "..", "typescript", "lib", "tsserver.js");
32235
32239
  try {
32236
- await fs134.access(localCandidate);
32240
+ await fs135.access(localCandidate);
32237
32241
  tsserverPath = localCandidate;
32238
32242
  } catch {
32239
32243
  }
@@ -57915,7 +57919,7 @@ var require_main = __commonJS({
57915
57919
  ril_1.default.install();
57916
57920
  var path211 = __importStar(__require("path"));
57917
57921
  var os37 = __importStar(__require("os"));
57918
- var fs134 = __importStar(__require("fs"));
57922
+ var fs135 = __importStar(__require("fs"));
57919
57923
  var crypto_1 = __require("crypto");
57920
57924
  var net_1 = __require("net");
57921
57925
  var api_1 = require_api();
@@ -58056,7 +58060,7 @@ var require_main = __commonJS({
58056
58060
  }
58057
58061
  let randomLength = 32;
58058
58062
  const fixedLength = "/lsp-.sock".length;
58059
- const tmpDir = fs134.realpathSync(XDG_RUNTIME_DIR ?? os37.tmpdir());
58063
+ const tmpDir = fs135.realpathSync(XDG_RUNTIME_DIR ?? os37.tmpdir());
58060
58064
  const limit = safeIpcPathLengths.get(process.platform);
58061
58065
  if (limit !== void 0) {
58062
58066
  randomLength = Math.min(limit - tmpDir.length - fixedLength, randomLength);
@@ -66137,6 +66141,7 @@ var init_lsp = __esm({
66137
66141
  let auxUnconfirmedServerIds;
66138
66142
  let lateDeliveryServerIds;
66139
66143
  let tsserverSyncEligible = false;
66144
+ const isWarmupTouch = source === "lsp_sweep_warmup";
66140
66145
  let tsserverSyncConfirmed;
66141
66146
  let diagnosticsUnsupportedServerIds = [];
66142
66147
  if (diagnosticsMode !== "none") {
@@ -66209,7 +66214,7 @@ var init_lsp = __esm({
66209
66214
  timeoutFor = () => callerCap ?? modeFloor;
66210
66215
  }
66211
66216
  const timeoutMs = Math.max(0, ...spawned.filter((e) => !deferredResyncServerIds.has(e.info.id)).map((e) => timeoutFor(e.client.serverId)));
66212
- if (!notifyWriteTimedOut && options.collectDiagnostics === true && clientScope === "primary" && spawned.length === 1 && spawned[0].client.serverId === "typescript" && getStrategy(spawned[0].client.serverId, spawned[0].client.getLaunchVariant?.()).silentOnClean === true) {
66217
+ if (!notifyWriteTimedOut && (options.collectDiagnostics === true || isWarmupTouch) && clientScope === "primary" && spawned.length === 1 && spawned[0].client.serverId === "typescript" && getStrategy(spawned[0].client.serverId, spawned[0].client.getLaunchVariant?.()).silentOnClean === true) {
66213
66218
  try {
66214
66219
  const snapshots = await this.getCapabilitySnapshots(filePath);
66215
66220
  tsserverSyncEligible = classifyCascadeWaitTier(this, filePath, snapshots) === "tier3-silent";
@@ -66283,7 +66288,6 @@ var init_lsp = __esm({
66283
66288
  const serverTimeout = perServerWaitTimeouts[entryIndex];
66284
66289
  const baseline = diagnosticBaselines.get(entry.client);
66285
66290
  const pullOnly = classifyServerWaitTier(entry.client.serverId, pressureSnapshots.find((snapshot) => snapshot.serverId === entry.client.serverId)) === "pull-capable";
66286
- const isWarmupTouch = source === "lsp_sweep_warmup";
66287
66291
  const waitForDiagnostics = () => !notifySkippedServerIds.has(entry.info.id) && Number.isFinite(baseline) ? entry.client.waitForDiagnostics(filePath, serverTimeout, {
66288
66292
  minVersion: baseline,
66289
66293
  ...pullOnly && { pullOnly: true },
@@ -66712,7 +66716,17 @@ var init_lsp = __esm({
66712
66716
  if (diagnosticsTimedOut && // #1549: primary-scoped, like the aggregate gate. `spawned.length === 1`
66713
66717
  // below means the one server IS the primary, so this is the same condition
66714
66718
  // written in the vocabulary the rest of the merge now uses.
66715
- !primaryNotifyWriteTimedOut && !tsserverSyncEligible && clientScope === "primary" && spawned.length === 1 && getStrategy(spawned[0].client.serverId, spawned[0].client.getLaunchVariant?.()).silentOnClean === true) {
66719
+ !primaryNotifyWriteTimedOut && // #3187: the warm-up is sync-ELIGIBLE now, and eligibility alone must
66720
+ // not cost it this fallback. For a collecting touch the exclusion is
66721
+ // #707's contract (the end-of-wait retry above is typescript's second
66722
+ // chance, and failing it stays inconclusive); the warm-up collects
66723
+ // nothing, so that retry's `collected !== undefined` precondition can
66724
+ // never hold for it and the exclusion would leave a server whose
66725
+ // `typescript.tsserverRequest` is unavailable stranded inconclusive —
66726
+ // a #744 warm-up failure, its retry, and a SKIPPED sweep group, where
66727
+ // today it is certified clean. Reaching here at all means the race
66728
+ // produced no answer, so this decides nothing the sync already decided.
66729
+ (!tsserverSyncEligible || isWarmupTouch) && clientScope === "primary" && spawned.length === 1 && getStrategy(spawned[0].client.serverId, spawned[0].client.getLaunchVariant?.()).silentOnClean === true) {
66716
66730
  try {
66717
66731
  const snapshots = await this.getCapabilitySnapshots(filePath);
66718
66732
  if (classifyCascadeWaitTier(this, filePath, snapshots) === "tier3-silent") {
@@ -68057,6 +68071,13 @@ var init_lsp = __esm({
68057
68071
  });
68058
68072
  const warmupAttempt = this.touchFile(representativeFile, content, {
68059
68073
  diagnostics: "document",
68074
+ // #3187: stays FALSE. This touch wants a verdict, not findings: its
68075
+ // `clientScope: "primary"` evidence does not speak for the auxiliaries
68076
+ // the sweep's own `clientScope: "all"` touch waits on, so collecting
68077
+ // here would prime this file's `lastKnownDiagnostics` from a narrower
68078
+ // scope than the result that follows (and an empty confirm would delete
68079
+ // a previously confirmed record). `touchFile`'s sync-confirm gates key
68080
+ // off `source` instead, so the verdict still arrives at ~grace+RTT.
68060
68081
  collectDiagnostics: false,
68061
68082
  clientScope: "primary",
68062
68083
  source: "lsp_sweep_warmup",
@@ -71656,8 +71677,39 @@ var init_blocker_freshness = __esm({
71656
71677
  }
71657
71678
  });
71658
71679
 
71659
- // dist/clients/disposition-logger.js
71680
+ // dist/clients/finding-identity.js
71681
+ import { createHash as createHash14 } from "node:crypto";
71660
71682
  import * as path101 from "node:path";
71683
+ function normalizeMessage(message) {
71684
+ return message.replace(/\s+/g, " ").trim().toLowerCase();
71685
+ }
71686
+ function hashText(value, length = FINDING_ID_HASH_LENGTH) {
71687
+ return createHash14("sha256").update(value).digest("hex").slice(0, length);
71688
+ }
71689
+ function relativeFile(filePath, cwd) {
71690
+ const canonicalCwd = normalizeMapKey(cwd);
71691
+ const canonicalFile = normalizeMapKey(filePath);
71692
+ const rel = path101.relative(canonicalCwd, canonicalFile).replace(/\\/g, "/");
71693
+ return rel && !rel.startsWith("..") ? rel : canonicalFile;
71694
+ }
71695
+ function stableFindingId(prefix, args) {
71696
+ const joined = [
71697
+ relativeFile(args.filePath, args.cwd),
71698
+ ...args.parts.map((part) => part === void 0 || part === null ? "" : String(part))
71699
+ ].join("|");
71700
+ return `${prefix}${hashText(joined)}`;
71701
+ }
71702
+ var FINDING_ID_HASH_LENGTH;
71703
+ var init_finding_identity = __esm({
71704
+ "dist/clients/finding-identity.js"() {
71705
+ "use strict";
71706
+ init_path_utils();
71707
+ FINDING_ID_HASH_LENGTH = 12;
71708
+ }
71709
+ });
71710
+
71711
+ // dist/clients/disposition-logger.js
71712
+ import * as path102 from "node:path";
71661
71713
  function logDispositionEvent(entry) {
71662
71714
  if (isTestMode()) {
71663
71715
  return;
@@ -71672,8 +71724,8 @@ var init_disposition_logger = __esm({
71672
71724
  init_probe_home_state();
71673
71725
  init_ndjson_logger();
71674
71726
  DISPOSITION_LOG_DIR = getGlobalPiLensLogDir();
71675
- DISPOSITION_LOG_FILE = path101.join(DISPOSITION_LOG_DIR, "dispositions.log");
71676
- DISPOSITION_LOG_BACKUP_FILE = path101.join(DISPOSITION_LOG_DIR, "dispositions.log.1");
71727
+ DISPOSITION_LOG_FILE = path102.join(DISPOSITION_LOG_DIR, "dispositions.log");
71728
+ DISPOSITION_LOG_BACKUP_FILE = path102.join(DISPOSITION_LOG_DIR, "dispositions.log.1");
71677
71729
  MAX_LOG_BYTES2 = Math.max(128 * 1024, Number.parseInt(process.env.PI_LENS_DISPOSITION_LOG_MAX_BYTES ?? "1048576", 10) || 1048576);
71678
71730
  writer7 = createNdjsonLogger({
71679
71731
  filePath: DISPOSITION_LOG_FILE,
@@ -71684,7 +71736,7 @@ var init_disposition_logger = __esm({
71684
71736
  });
71685
71737
 
71686
71738
  // dist/clients/bus-events-logger.js
71687
- import * as path102 from "node:path";
71739
+ import * as path103 from "node:path";
71688
71740
  function bumpRollupCount(event, outcome) {
71689
71741
  const existing = eventRollupCounts.get(event) ?? {
71690
71742
  emitted: 0,
@@ -71727,7 +71779,7 @@ var init_bus_events_logger = __esm({
71727
71779
  init_ndjson_logger();
71728
71780
  init_log_cleanup();
71729
71781
  init_latency_logger();
71730
- BUS_EVENTS_LOG_FILE = path102.join(getGlobalPiLensLogDir(), "bus-events.log");
71782
+ BUS_EVENTS_LOG_FILE = path103.join(getGlobalPiLensLogDir(), "bus-events.log");
71731
71783
  writer8 = createNdjsonLogger({
71732
71784
  filePath: BUS_EVENTS_LOG_FILE,
71733
71785
  maxBytes: getMaxLogSizeMB() * 1024 * 1024
@@ -71738,9 +71790,9 @@ var init_bus_events_logger = __esm({
71738
71790
 
71739
71791
  // dist/clients/recent-touches.js
71740
71792
  import * as fs81 from "node:fs";
71741
- import * as path103 from "node:path";
71793
+ import * as path104 from "node:path";
71742
71794
  function recentTouchesPath(cwd) {
71743
- return path103.join(getProjectDataDir(cwd), "recent-touches.json");
71795
+ return path104.join(getProjectDataDir(cwd), "recent-touches.json");
71744
71796
  }
71745
71797
  function isRecentTouchesEnabled() {
71746
71798
  if (_enabledCache2 === void 0) {
@@ -72294,37 +72346,6 @@ var init_disposition_publish = __esm({
72294
72346
  }
72295
72347
  });
72296
72348
 
72297
- // dist/clients/finding-identity.js
72298
- import { createHash as createHash14 } from "node:crypto";
72299
- import * as path104 from "node:path";
72300
- function normalizeMessage(message) {
72301
- return message.replace(/\s+/g, " ").trim().toLowerCase();
72302
- }
72303
- function hashText(value, length = FINDING_ID_HASH_LENGTH) {
72304
- return createHash14("sha256").update(value).digest("hex").slice(0, length);
72305
- }
72306
- function relativeFile(filePath, cwd) {
72307
- const canonicalCwd = normalizeMapKey(cwd);
72308
- const canonicalFile = normalizeMapKey(filePath);
72309
- const rel = path104.relative(canonicalCwd, canonicalFile).replace(/\\/g, "/");
72310
- return rel && !rel.startsWith("..") ? rel : canonicalFile;
72311
- }
72312
- function stableFindingId(prefix, args) {
72313
- const joined = [
72314
- relativeFile(args.filePath, args.cwd),
72315
- ...args.parts.map((part) => part === void 0 || part === null ? "" : String(part))
72316
- ].join("|");
72317
- return `${prefix}${hashText(joined)}`;
72318
- }
72319
- var FINDING_ID_HASH_LENGTH;
72320
- var init_finding_identity = __esm({
72321
- "dist/clients/finding-identity.js"() {
72322
- "use strict";
72323
- init_path_utils();
72324
- FINDING_ID_HASH_LENGTH = 12;
72325
- }
72326
- });
72327
-
72328
72349
  // dist/clients/diagnostic-dispositions.js
72329
72350
  import * as fs82 from "node:fs";
72330
72351
  import * as path105 from "node:path";
@@ -72629,6 +72650,8 @@ function fileMapKey(filePath) {
72629
72650
  return normalizeEphemeralMapKey(filePath);
72630
72651
  }
72631
72652
  function isBlocking(d) {
72653
+ if (d.disposition)
72654
+ return false;
72632
72655
  if (d.stale)
72633
72656
  return false;
72634
72657
  if (d.semantic === "blocking")
@@ -72680,20 +72703,24 @@ function reconcileWidgetDisposition(cwd, target, _disposition, content) {
72680
72703
  }
72681
72704
  }
72682
72705
  const active = new Set(applyDispositions(current, cwd, target.filePath, source));
72683
- const normalized = current.map((diagnostic) => {
72706
+ const normalized = current.flatMap((diagnostic) => {
72684
72707
  const { strict, weak } = anchorsForDiagnostic(cwd, target.filePath, diagnostic, source);
72685
72708
  const entry = getDisposition(cwd, strict) ?? getDisposition(cwd, weak);
72686
72709
  if (!active.has(diagnostic) && (entry?.disposition === "false-positive" || entry?.disposition === "suppress")) {
72687
- return { ...diagnostic, disposition: entry.disposition, flagged: false };
72710
+ return [
72711
+ { ...diagnostic, disposition: entry.disposition, flagged: false }
72712
+ ];
72688
72713
  }
72714
+ if (diagnostic.suppressedRetained)
72715
+ return [];
72689
72716
  const { disposition: _disposition2, flagged: _flagged, ...baseDiagnostic } = diagnostic;
72690
- return entry?.disposition === "flagged" ? { ...baseDiagnostic, flagged: true } : baseDiagnostic;
72717
+ return entry?.disposition === "flagged" ? [{ ...baseDiagnostic, flagged: true }] : [baseDiagnostic];
72691
72718
  });
72692
72719
  const rec = getOrCreate(target.filePath);
72693
72720
  const writeIndex = admitWidgetDiagnosticsWrite(target.filePath);
72694
72721
  if (!diagnosticsWriteGuard.shouldWrite(fileMapKey(target.filePath), writeIndex))
72695
72722
  return;
72696
- commitDiagnostics(rec, target.filePath, normalized, Date.now());
72723
+ commitDiagnostics(rec, target.filePath, normalized, Date.now(), fileMapKey(target.filePath), false);
72697
72724
  }
72698
72725
  function admitWidgetDiagnosticsWrite(filePath, writeIndex) {
72699
72726
  const key4 = fileMapKey(filePath);
@@ -72858,7 +72885,35 @@ function normalizeDiagnostics(filePath, diagnostics, observedTs) {
72858
72885
  };
72859
72886
  });
72860
72887
  }
72861
- function commitDiagnostics(rec, filePath, normalized, observedAt, key4 = fileMapKey(filePath)) {
72888
+ function retentionIdentity(d) {
72889
+ return JSON.stringify([
72890
+ d.tool ?? "",
72891
+ d.rule ?? "",
72892
+ normalizeMessage(d.message)
72893
+ ]);
72894
+ }
72895
+ function retainSuppressedRows(previous, incoming, filePath) {
72896
+ const tagged = previous.filter((d) => d.disposition !== void 0);
72897
+ if (tagged.length === 0)
72898
+ return incoming;
72899
+ const reported = new Set(incoming.map(retentionIdentity));
72900
+ const retained = tagged.filter((d) => !reported.has(retentionIdentity(d)));
72901
+ if (retained.length > MAX_RETAINED_SUPPRESSED_PER_FILE) {
72902
+ retained.sort((a, b) => (b.observedAt ?? 0) - (a.observedAt ?? 0));
72903
+ recordDegradationOnce({
72904
+ kind: "widget-suppressed-retention-capped",
72905
+ subject: filePath,
72906
+ reason: `${retained.length - MAX_RETAINED_SUPPRESSED_PER_FILE} suppressed row(s) beyond the ${MAX_RETAINED_SUPPRESSED_PER_FILE}-row per-file retention cap are no longer counted by the footer chip`
72907
+ });
72908
+ retained.length = MAX_RETAINED_SUPPRESSED_PER_FILE;
72909
+ }
72910
+ return [
72911
+ ...incoming,
72912
+ ...retained.map((d) => d.suppressedRetained ? d : { ...d, suppressedRetained: true })
72913
+ ];
72914
+ }
72915
+ function commitDiagnostics(rec, filePath, incoming, observedAt, key4 = fileMapKey(filePath), retainSuppressed = true) {
72916
+ const normalized = retainSuppressed ? retainSuppressedRows(rec.allDiagnostics, incoming, filePath) : incoming;
72862
72917
  rec.diagnosticCounts = countDiagnostics(normalized);
72863
72918
  rec.diagnostics = capStoredDiagnostics(normalized);
72864
72919
  rec.allDiagnostics = normalized;
@@ -73149,6 +73204,16 @@ function getFileDiagnosticSummaries() {
73149
73204
  };
73150
73205
  });
73151
73206
  }
73207
+ function countRetainedSuppressedRows(filePath) {
73208
+ const rec = files.get(fileMapKey(filePath));
73209
+ if (!rec)
73210
+ return 0;
73211
+ let n = 0;
73212
+ for (const d of rec.allDiagnostics)
73213
+ if (d.suppressedRetained)
73214
+ n += 1;
73215
+ return n;
73216
+ }
73152
73217
  function getFileDiagnostics(filePath) {
73153
73218
  const rec = files.get(fileMapKey(filePath));
73154
73219
  if (!rec)
@@ -73459,7 +73524,7 @@ function dedupeByBasename(recs) {
73459
73524
  }
73460
73525
  return [...seen.values()].sort((a, b) => b.touchedAt - a.touchedAt);
73461
73526
  }
73462
- var files, sessionLanguages, requestRenderFn, diagnosticsWriteGuard, runnerWriteGuard, MAX_STORED_DIAGNOSTICS_PER_FILE, MAX_INACTIVE_FILE_RECORDS, ACTIVE_FILE_IDLE_MS, MAX_LSP_SERVER_RECORDS, lspServers, nextInactivePruneSize, renderedDependencyDriftFiles, WIDGET_STATE_VERSION, staleReconcileTimer, STALE_RECONCILE_DEBOUNCE_MS, HORIZONTAL_MIN_WIDTH;
73527
+ var files, sessionLanguages, requestRenderFn, diagnosticsWriteGuard, runnerWriteGuard, MAX_STORED_DIAGNOSTICS_PER_FILE, MAX_RETAINED_SUPPRESSED_PER_FILE, MAX_INACTIVE_FILE_RECORDS, ACTIVE_FILE_IDLE_MS, MAX_LSP_SERVER_RECORDS, lspServers, nextInactivePruneSize, renderedDependencyDriftFiles, WIDGET_STATE_VERSION, staleReconcileTimer, STALE_RECONCILE_DEBOUNCE_MS, HORIZONTAL_MIN_WIDTH;
73463
73528
  var init_widget_state = __esm({
73464
73529
  "dist/clients/widget-state.js"() {
73465
73530
  "use strict";
@@ -73470,6 +73535,7 @@ var init_widget_state = __esm({
73470
73535
  init_tui_fit();
73471
73536
  init_write_ordering_guard();
73472
73537
  init_blocker_freshness();
73538
+ init_finding_identity();
73473
73539
  init_freshness();
73474
73540
  init_diagnostic_line_freshness();
73475
73541
  init_stale_marker();
@@ -73481,6 +73547,7 @@ var init_widget_state = __esm({
73481
73547
  diagnosticsWriteGuard = new WriteOrderingGuard();
73482
73548
  runnerWriteGuard = new WriteOrderingGuard();
73483
73549
  MAX_STORED_DIAGNOSTICS_PER_FILE = 12;
73550
+ MAX_RETAINED_SUPPRESSED_PER_FILE = 12;
73484
73551
  MAX_INACTIVE_FILE_RECORDS = 1024;
73485
73552
  ACTIVE_FILE_IDLE_MS = 30 * 6e4;
73486
73553
  MAX_LSP_SERVER_RECORDS = 128;
@@ -76024,6 +76091,194 @@ var init_finding_policy = __esm({
76024
76091
  }
76025
76092
  });
76026
76093
 
76094
+ // dist/clients/finding-delivery-gate.js
76095
+ function markUnreconciledFindings(findings) {
76096
+ return findings.map((finding) => ({ ...finding, stale: true }));
76097
+ }
76098
+ function formatCacheAgeLabel(scannedAt, nowMs = Date.now()) {
76099
+ if (scannedAt === void 0 || scannedAt === null || scannedAt === "") {
76100
+ return "scan age unknown";
76101
+ }
76102
+ const scannedAtMs = typeof scannedAt === "number" ? scannedAt : Date.parse(scannedAt);
76103
+ if (!Number.isFinite(scannedAtMs))
76104
+ return "scan age unknown";
76105
+ const ageMs = Math.max(0, nowMs - scannedAtMs);
76106
+ const ageMinutes = Math.round(ageMs / MS_PER_MINUTE);
76107
+ if (ageMinutes < 1)
76108
+ return "scanned <1m ago";
76109
+ if (ageMinutes < 60)
76110
+ return `scanned ${ageMinutes}m ago`;
76111
+ const ageHours = Math.floor(ageMinutes / 60);
76112
+ const remMinutes = ageMinutes % 60;
76113
+ return remMinutes === 0 ? `scanned ${ageHours}h ago` : `scanned ${ageHours}h ${remMinutes}m ago`;
76114
+ }
76115
+ function gated(file, description, gates, evidence, extra = {}) {
76116
+ return { mode: "gated", file, description, gates, evidence, ...extra };
76117
+ }
76118
+ function labeled(file, description, reason, ageSource, evidence = [], extra = {}) {
76119
+ return {
76120
+ mode: "labeled",
76121
+ file,
76122
+ description,
76123
+ reason,
76124
+ ageSource,
76125
+ evidence,
76126
+ ...extra
76127
+ };
76128
+ }
76129
+ var MS_PER_MINUTE, RUNTIME_TURN_FILE, LENS_DIAGNOSTICS_FILE, DELIVERY_SURFACES;
76130
+ var init_finding_delivery_gate = __esm({
76131
+ "dist/clients/finding-delivery-gate.js"() {
76132
+ "use strict";
76133
+ MS_PER_MINUTE = 6e4;
76134
+ RUNTIME_TURN_FILE = "clients/runtime-turn.ts";
76135
+ LENS_DIAGNOSTICS_FILE = "tools/lens-diagnostics.ts";
76136
+ DELIVERY_SURFACES = {
76137
+ "runtime-turn:secrets-gitleaks": gated(RUNTIME_TURN_FILE, "Turn-end \u{1F534} secrets blocker, gitleaks cache.", ["gateFindingsByPathFreshness"], ['store: "gitleaks"']),
76138
+ "runtime-turn:secrets-trivy": gated(RUNTIME_TURN_FILE, "Turn-end \u{1F534} secrets blocker, trivy secrets cache.", ["gateFindingsByPathFreshness"], ['store: "trivy-secrets"']),
76139
+ "runtime-turn:govulncheck-advisory": gated(RUNTIME_TURN_FILE, "Turn-end \u{1F6E1}\uFE0F Go CVE advisory (call-site line only).", ["gateFindingsByPathFreshness"], ['store: "govulncheck"']),
76140
+ // Two tagged seams (the stale and live render branches below the same
76141
+ // `sweepInlineBlockerFreshness` call) legitimately share one evidence
76142
+ // occurrence — evidenceMin: 2 tells the exclusive-assignment check both
76143
+ // are expected claimants, not a rogue seam contesting the real one.
76144
+ "runtime-turn:unresolved-inline-blocker": gated(
76145
+ RUNTIME_TURN_FILE,
76146
+ "Turn-end re-surfaced unresolved inline blockers.",
76147
+ ["sweepInlineBlockerFreshness"],
76148
+ // #1790: the call now passes `additionalEntries` (widget-store rows) as a
76149
+ // third argument, so the literal is the call's opening rather than the
76150
+ // whole single-line invocation.
76151
+ ["sweepInlineBlockerFreshness(runtime, cwd, {"],
76152
+ { evidenceMin: 2 }
76153
+ ),
76154
+ // Same two gate calls as the live secrets tier — this tier renders their
76155
+ // `.stale` arm, so it has no OWN gate call to point at.
76156
+ "runtime-turn:stale-secrets-tier": gated(RUNTIME_TURN_FILE, "Turn-end \u{1F511} demoted-secrets tier (drifted since scan).", ["gateFindingsByPathFreshness"], ['store: "gitleaks"', 'store: "trivy-secrets"']),
76157
+ // The evidence below is the literal header FRAGMENT including the
76158
+ // interpolation — proves the label is actually rendered, not merely
76159
+ // computed and discarded (review round F1's "deleted the age
76160
+ // interpolations" attack).
76161
+ "runtime-turn:trivy-critical-blocker": labeled(RUNTIME_TURN_FILE, "Turn-end \u{1F534} CRITICAL dependency CVE blocker (trivy).", "Package-pinned finding with no cited file:line to stat \u2014 freshness has nothing to check drift against. The session_start cache can be arbitrarily old, so its age is stated instead. Also routes through `filterFindingsByDisposition` (#1625) first \u2014 a suppressed/false-positive finding never reaches this render, so the age label and the disposition filter compose rather than substitute for each other.", "TrivyResult.scannedAt", ["CRITICAL dependency CVEs (trivy, ${trivyAgeLabel}"]),
76162
+ "runtime-turn:trivy-cve-advisory": labeled(RUNTIME_TURN_FILE, "Turn-end \u{1F6E1}\uFE0F non-critical dependency CVE advisory (trivy).", "Same package-pinned shape and #1625 disposition composition as the CRITICAL blocker above.", "TrivyResult.scannedAt", ["Dependency CVEs (trivy, ${trivyAgeLabel}"]),
76163
+ "runtime-turn:trivy-license-advisory": labeled(RUNTIME_TURN_FILE, "Turn-end \u{1F4DC} dependency license-risk advisory (trivy).", "Same package-pinned shape as the CRITICAL blocker above.", "TrivyResult.scannedAt", ["Dependency license risk (trivy, ${trivyAgeLabel}"]),
76164
+ "runtime-turn:dead-code-advisory": labeled(RUNTIME_TURN_FILE, "Turn-end dead-code delta advisory (knip/vulture/etc).", "The delta shown is computed from THIS turn's own analyze() call, diffed against the previous cache \u2014 never a replay of a stale cache.", "live"),
76165
+ "runtime-turn:knip-blocker": labeled(RUNTIME_TURN_FILE, "Turn-end \u{1F534} Knip unresolved-imports/deps blocker.", "Same shape as the dead-code advisory: `await knipClient.analyze(cwd, ...)` runs fresh THIS turn, diffed against the previous cache and filtered to files edited this turn \u2014 never a replay of a stale cache.", "live"),
76166
+ "runtime-turn:knip-advisory": labeled(RUNTIME_TURN_FILE, "Turn-end \u26A0\uFE0F Knip newly-unused-exports advisory.", "Same live-this-turn shape as the Knip blocker above.", "live"),
76167
+ "runtime-turn:actionable-warnings-advisory": labeled(RUNTIME_TURN_FILE, "Turn-end actionable-warnings advisory (ast-grep etc).", "`peekActionableWarnings` reads `_actionableWarningsThisTurn` \u2014 recorded fresh from this turn's own dispatch, never a cross-turn cache.", "live"),
76168
+ "runtime-turn:code-quality-warnings-advisory": labeled(RUNTIME_TURN_FILE, "Turn-end code-quality-warnings advisory.", "`peekCodeQualityWarnings` reads `_codeQualityWarningsThisTurn` \u2014 the same per-turn accumulator shape as actionable-warnings above.", "live"),
76169
+ "runtime-turn:disposition-suppressed-notice": labeled(RUNTIME_TURN_FILE, "Turn-end running disposition-suppressed-count notice (#1616).", "A live running total accumulated from this turn's own gate/disposition calls above (`dispositionSuppressedTotal`) \u2014 telemetry ABOUT the other gated surfaces, not a cache of its own.", "live"),
76170
+ // #2001/#2002 collect-later: findings an auxiliary LSP published AFTER its
76171
+ // aux-grace window expired, probed from the client cache at the next
76172
+ // turn_end. Gated on the mark timestamp: a cited file deleted or edited
76173
+ // since the mark drops its findings (never delivered before, and the
76174
+ // drifting edit already re-touched the file — a fresh pending pair
76175
+ // supersedes this one), with both drop arms counted in the
76176
+ // `late_auxiliary_findings` latency record rather than silenced.
76177
+ //
76178
+ // #3102: freshness is only half the gate. The survivors also take the shared
76179
+ // `clients/dispatch/finding-policy.ts` stack (inline `pi-lens-ignore` →
76180
+ // stored dispositions → `.pi-lens.json` rule policy) the per-edit dispatcher,
76181
+ // `mode=full` and the `source=lsp` probe lane apply — without it a finding
76182
+ // the agent marked `false-positive` re-reported on every drain. Per #3088
76183
+ // round-2 F4, the evidence is the CALL TEXT of that filter, which exists
76184
+ // nowhere else in this file: a bare `applyFindingPolicy` would also be
76185
+ // satisfied by an import line or an unrelated caller.
76186
+ "runtime-turn:late-auxiliary-findings": gated(RUNTIME_TURN_FILE, "Turn-end late-auxiliary LSP findings (collect-later probe of aux client caches whose grace window expired).", ["gateFindingsByPathFreshness", "applyFindingPolicy"], ['store: "late-auxiliary-findings"', "applyFindingPolicy(gate.live, {"]),
76187
+ "runtime-turn:late-runner-findings": gated(RUNTIME_TURN_FILE, "Turn-end CLI runner findings collected after the post-write path.", ["gateFindingsByPathFreshness"], ['store: "late-runner-findings"'], { evidenceMin: 2 }),
76188
+ "runtime-turn:cascade-blocker": labeled(RUNTIME_TURN_FILE, "Turn-end \u{1F9EA} cascade neighbor blocker.", "Cascade results settle synchronously this turn where possible (`settleCascadeRuns`), but an unsettled compute can carry over to a later turn (bounded by a carry cap) \u2014 a carried-over result renders with an explicit `(carried N turns \xB7 scanned Xm ago)` label (#3167, #3168 F3), so the agent can tell it from a fresh observation.", "live", ["cascadeCarrySuffix("], { evidenceMin: 1 }),
76189
+ "runtime-turn:cascade-coverage-advisory": labeled(RUNTIME_TURN_FILE, "Turn-end cascade-coverage-gap advisories (graph/binding/budget).", "Explains what the cascade check could NOT confirm this turn \u2014 not a finding with a cited path, an absence-of-coverage disclosure computed from this turn's own indeterminate-run list. An advisory computed from a carried indeterminate run is labeled `(carried N turns \xB7 <age>)` (#3167, #3168 F4 \u2014 dropped on mixed carried/fresh buckets). The age half reads `scan age unknown` on this surface today: no indeterminate-run producer stamps `observedAt` (only the resolved-found plumb does, `clients/cascade-format.ts`), and one unstamped carried run collapses the whole bucket's age rather than claiming the stamped runs' (#3168 F13). The carry COUNT is always real.", "live", ["withCarryLabel("], { evidenceMin: 3 }),
76190
+ // #3102: the cold-neighbour cascade run is BUILT here, in the quiet-window
76191
+ // reconcile (`onResolvedFound` in index.ts), a turn earlier than the
76192
+ // `runtime-turn:cascade-blocker` render that finally carries it — so it is
76193
+ // its own delivery lane, and it was the one that rendered raw LSP findings
76194
+ // with no policy filter at all. The evidence is the CALL TEXT of the shared
76195
+ // filter (#3088 round-2 F4): a bare `applyFindingPolicy` would also be
76196
+ // satisfied by the import line.
76197
+ "cascade-format:resolved-found-run": gated("clients/cascade-format.ts", "Cold-neighbour ERROR diagnostics formatted into the turn-end cascade run by `buildResolvedFoundCascadeRun` (quiet-window reconcile of a cascade touch that skipped its in-lane wait).", ["applyFindingPolicy"], ["applyFindingPolicy(retained, {"]),
76198
+ // #3157: the IN-LANE cascade — a different delivery lane from the
76199
+ // quiet-window run above, and the one #3102's sweep cleared WRONGLY. Its four
76200
+ // display sites (passive cold snapshot, fresh touch, touch-error fallback,
76201
+ // degraded fallback) assemble `CascadeNeighborResult.diagnostics` and reach
76202
+ // the agent through `formatCascadeNeighborDiagnostics` without ever entering
76203
+ // the dispatcher's `applyOutputFilters` pipeline: the sweep's file-level
76204
+ // verdict ("the per-edit dispatch path, which already filters in
76205
+ // dispatcher.ts") was true only of the per-edit RUNNER output in
76206
+ // `runners/lsp.ts`, which is a different call site in a different file.
76207
+ //
76208
+ // Evidence is the CALL TEXT (#3088 round-2 F4), counted: `cascadeDisplay(` is
76209
+ // the one seam all four sites route through, so `evidenceMin: 4` is the count
76210
+ // of display sites in the file and dropping ANY of them back to a raw
76211
+ // `convertLspDiagnostics` reds this suite. Identity-stubbing the callee
76212
+ // instead is caught behaviourally — it reds twelve cases in
76213
+ // `tests/clients/inlane-cascade-finding-policy.test.ts`, which is a stronger
76214
+ // signal than the R2 proximity heuristic. Residual, stated: a FIFTH display
76215
+ // site that open-codes the conversion keeps the count at 4 and is caught by
76216
+ // review, not by this row.
76217
+ "dispatch-integration:in-lane-cascade": gated("clients/dispatch/integration.ts", "In-lane per-edit cascade neighbour ERROR diagnostics, rendered into the turn-end cascade block by `computeCascadeForFile`.", ["applyCascadeDisplayPolicy"], ["cascadeDisplay("], { evidenceMin: 4 }),
76218
+ "runtime-turn:call-graph-advisory": labeled(RUNTIME_TURN_FILE, "Turn-end \u{1F4CA} call-graph impact advisory.", "`runtime.callGraph` is a session-lifetime in-memory structure; this round does not verify or gate ITS OWN freshness policy (how/when it incorporates edits made earlier in the session).", "live", [], {
76219
+ status: "partial",
76220
+ partialReason: "call-graph freshness/invalidation policy is out of this PR's scope \u2014 tracked as a follow-up, not silently assumed fresh."
76221
+ }),
76222
+ // #3088 round 2 F4: this row used to name `applyDispositions`/`applyRulePolicy`
76223
+ // with whole-file evidence literals. Once #3088 folded mode=full's stack onto
76224
+ // `applyFindingPolicy`, those two literals survived only in UNRELATED
76225
+ // mode=delta helpers in the same file, so deleting the entire policy stack out
76226
+ // of `applyInlineSuppressionsToSummaries` left this gate — and the ratchet —
76227
+ // green while five behaviour cases redded. The evidence is now the call text
76228
+ // of the merge's own filter, which exists nowhere else in the file.
76229
+ "lens-diagnostics:mode-full": gated(LENS_DIAGNOSTICS_FILE, "`lens_diagnostics mode=full` report.", ["applyFindingPolicy", "reconcileProjectDiagnosticsSnapshot"], [
76230
+ "applyFindingPolicy(summary.diagnostics, {",
76231
+ "reconcileProjectDiagnosticsSnapshot("
76232
+ ]),
76233
+ "lens-diagnostics:mode-all": gated(LENS_DIAGNOSTICS_FILE, "`lens_diagnostics mode=all` report (widget-state read).", ["reconcileStaleWidgetFiles", "reconcileStaleWidgetDependencyBlockers"], ["reconcileStaleWidgetFiles(", "reconcileStaleWidgetDependencyBlockers("]),
76234
+ // #1634 review round F3: this was the most important gap — the tool's
76235
+ // DEFAULT mode rendered cached `file:line` findings with zero freshness
76236
+ // check. `applyDeltaFreshnessGate` (this file's sibling in
76237
+ // tools/lens-diagnostics.ts) now routes the actionable/quality-warnings
76238
+ // caches through the shared gate using each report's own `generatedAt`.
76239
+ // Round R3: mode=delta has a THIRD arm — the persisted project-diagnostics
76240
+ // delta report, rendered by `appendProjectDiagnosticsDeltaLines` — which
76241
+ // carried the identical unfixed shape. It now gates the same way, against
76242
+ // its own `generatedAt`.
76243
+ "lens-diagnostics:mode-delta": gated(LENS_DIAGNOSTICS_FILE, "`lens_diagnostics mode=delta` report (the tool's DEFAULT mode) \u2014 re-serves the actionable-warnings/code-quality-warnings caches AND the persisted project-diagnostics delta report.", ["gateFindingsByPathFreshness"], [
76244
+ 'store: "lens-diagnostics-delta"',
76245
+ "applyDeltaFreshnessGate(",
76246
+ 'store: "lens-diagnostics-delta-project"'
76247
+ ]),
76248
+ "widget-state:footer": gated("clients/widget-state.ts", "TUI footer blocking/error/warning tally.", ["reconcileStaleWidgetFiles", "isBlocking"], ["isBlocking(diagnostic)", "reconcileStaleWidgetFiles()"]),
76249
+ "agent-nudge:context-message": labeled("clients/agent-nudge.ts", "Post-edit file-touch nudge injected via `context`.", "Not a scanner finding store: no severity, no cited line, no cache \u2014 the accumulator is drained and cleared at the moment of injection, so the content IS the current state by construction.", "live"),
76250
+ "test-runner-delivery:custom-entry": labeled("clients/test-runner-delivery.ts", "Post-agent test-runner failures in a non-context custom entry.", "The cache remains authoritative for pull diagnostics and the commit guard; this surface appends only after provenance validation and an idle recheck.", "live"),
76251
+ "project-diagnostics:persisted-snapshot": gated(LENS_DIAGNOSTICS_FILE, "Cross-session persisted project-diagnostics snapshot read.", ["reconcileProjectDiagnosticsSnapshot"], ["reconcileProjectDiagnosticsSnapshot("]),
76252
+ // ── #2028: the remaining agent-facing surfaces ──────────────────────────
76253
+ // Registered after #2028's root-cause review found the 🔴 STOP block
76254
+ // rendering stale/deleted-file blockers because its surface was never in
76255
+ // this registry — the exact "gated or labeled, never neither" gap this
76256
+ // module exists to close.
76257
+ "tool-call:stop-blocker": gated("clients/pipeline.ts", "Per-edit \u{1F534} STOP blocker output appended to the write/edit tool result.", ["dropFindingsForMissingPaths"], ['store: "stop-blocker"']),
76258
+ // The freshness stack itself (own-file mtime + reverse-dependency mtimes
76259
+ // via isEntryFresh, plus the #1095 content binding) lives inside
76260
+ // clients/lsp/workspace-diagnostics-cache.ts; the two evidence calls are
76261
+ // the tool's literal entry points into that gated path.
76262
+ "lsp-diagnostics:tool-output": gated("tools/lsp-diagnostics.ts", "`lsp_diagnostics` batch/directory sweep results. Cache hits replay through the shared workspace-diagnostics cache: createWorkspaceDiagnosticsCacheContext is the tool's entry, and its lookup() applies the #671/#672 freshness stack (own-file mtime + reverse-dependency mtimes via isEntryFresh) plus the #1095 content binding. #3088: every route out of this tool \u2014 fresh, cache replay, single file, directory \u2014 also passes applyProbeFindingPolicy, the stored-disposition / .pi-lens.json rule-policy / inline pi-lens-ignore stack the per-edit dispatch path and mode=full apply.", [
76263
+ "createWorkspaceDiagnosticsCacheContext",
76264
+ "isEntryFresh",
76265
+ "cacheCtx.lookup",
76266
+ "applyProbeFindingPolicy"
76267
+ ], [
76268
+ "createWorkspaceDiagnosticsCacheContext(resolvedCwd)",
76269
+ "cacheCtx.lookup(file, scopeKey)",
76270
+ "applyProbeFindingPolicy("
76271
+ ]),
76272
+ "git-guard:commit-blocked": labeled("clients/git-guard.ts", "Git-guard commit/push \u{1F534} COMMIT BLOCKED verdict (--lens-guard).", "Synchronous preflight rejection returned inline with the failed git command \u2014 no stored state is delivered, so nothing can go stale between detection and delivery.", "live"),
76273
+ "shared-checkout-guard:worktree-mutation-blocked": labeled("clients/shared-checkout-guard.ts", "Shared-checkout \u{1F534} WORKING-TREE CHANGE BLOCKED verdict (--lens-checkout-guard).", "Synchronous preflight rejection returned inline with the failed git command. Both inputs are read at decision time \u2014 the instance registry and `git status` \u2014 so no stored finding is replayed and nothing can go stale between detection and delivery.", "live"),
76274
+ "read-guard-tool-lines:preflight-errors": labeled("clients/read-guard-tool-lines.ts", "Read-guard hashline BLOCKED / RE-READ REQUIRED / PARTIAL APPLY / ALREADY APPLIED preflight errors (#2402).", "Computed fresh per edit attempt and returned as that attempt's rejection \u2014 no cached findings are replayed, so there is no staleness window. The PARTIAL APPLY and ALREADY APPLIED variants describe the same attempt's commit outcome; applied-edit recognition reads RuntimeCoordinator's session-scoped record, which resets with the session.", "live"),
76275
+ "mutating-tool:adapter-preflight-errors": labeled("clients/mutating-tool.ts", "Shape-adapter BLOCKED preflight errors for hashline edit inputs the adapter recognized but could not resolve to a range (#2423).", "Computed fresh from the tool input on each attempt and returned as that attempt's rejection, so there is no staleness window. The adapters read only the call's own arguments; nothing cached is replayed.", "live"),
76276
+ "tool-call:duplicate-export-blocker": labeled("clients/runtime-tool-call.ts", "Duplicate-export STOP rejection returned inline with the failed edit.", "Synchronous preflight rejection computed per edit attempt; no stored state.", "live"),
76277
+ "agent-behavior:thrashing-notice": labeled("clients/agent-behavior-client.ts", "In-result thrashing/blind-write detection notice.", "Ephemeral notice computed at detection time inside the same tool result \u2014 no persistence and no cache, so nothing can be stale.", "live")
76278
+ };
76279
+ }
76280
+ });
76281
+
76027
76282
  // dist/clients/cascade-format.js
76028
76283
  import * as fs88 from "node:fs";
76029
76284
  function formatCascadeNeighborDiagnostics(cwd, neighbors, options = {}) {
@@ -76114,7 +76369,11 @@ suppressed by disposition: ${suppressed} finding(s) (marked false-positive or wo
76114
76369
  formatted
76115
76370
  },
76116
76371
  neighborCount: 1,
76117
- diagnosticCount: diagnostics.length
76372
+ diagnosticCount: diagnostics.length,
76373
+ // #3168 F3: the #1444 publish stamp — the run's own observation time, so
76374
+ // the carried-render age label states the real age instead of claiming
76375
+ // no stamp exists.
76376
+ ...neighbor.publishedAt !== void 0 ? { observedAt: neighbor.publishedAt } : {}
76118
76377
  };
76119
76378
  }
76120
76379
  function applyCascadeDisplayPolicy(errors, options) {
@@ -76170,6 +76429,12 @@ function readNeighborContent(filePath) {
76170
76429
  return void 0;
76171
76430
  }
76172
76431
  }
76432
+ function cascadeCarrySuffix(carriedTurns, observedAt) {
76433
+ if (carriedTurns === void 0 || carriedTurns < 1)
76434
+ return void 0;
76435
+ const noun = carriedTurns === 1 ? "turn" : "turns";
76436
+ return `(carried ${carriedTurns} ${noun} \xB7 ${formatCacheAgeLabel(observedAt)})`;
76437
+ }
76173
76438
  var init_cascade_format = __esm({
76174
76439
  "dist/clients/cascade-format.js"() {
76175
76440
  "use strict";
@@ -76177,6 +76442,7 @@ var init_cascade_format = __esm({
76177
76442
  init_finding_policy();
76178
76443
  init_file_role();
76179
76444
  init_latency_logger();
76445
+ init_finding_delivery_gate();
76180
76446
  init_lsp_diagnostics();
76181
76447
  init_runner_context();
76182
76448
  }
@@ -76457,7 +76723,8 @@ function registerCascadeTierReconcileTask(getLspService, options = {}) {
76457
76723
  options.onResolvedFound?.({
76458
76724
  filePath: o.filePath,
76459
76725
  serverId: o.serverId,
76460
- diagnostics: o.diagnostics
76726
+ diagnostics: o.diagnostics,
76727
+ ...o.publishedAt !== void 0 ? { publishedAt: o.publishedAt } : {}
76461
76728
  });
76462
76729
  } else if (o.outcome === "resolved-clean" && o.publishedAt != null) {
76463
76730
  options.onResolvedClean?.({
@@ -95750,7 +96017,7 @@ __export(lens_map_exports, {
95750
96017
  parseUntrackedIgnoredOutput: () => parseUntrackedIgnoredOutput,
95751
96018
  renderMapHtml: () => renderMapHtml
95752
96019
  });
95753
- import * as fs132 from "node:fs";
96020
+ import * as fs133 from "node:fs";
95754
96021
  import * as path209 from "node:path";
95755
96022
  function aggregateGraphToFiles(graph, options) {
95756
96023
  const maxNodes = Math.max(1, options?.maxNodes ?? DEFAULT_MAX_MAP_NODES);
@@ -96701,9 +96968,9 @@ async function generateLensMap(cwd) {
96701
96968
  };
96702
96969
  const html = renderMapHtml(payload);
96703
96970
  const outDir = path209.join(getProjectDataDir(cwd), "reports");
96704
- fs132.mkdirSync(outDir, { recursive: true });
96971
+ fs133.mkdirSync(outDir, { recursive: true });
96705
96972
  const filePath = path209.join(outDir, "lens-map.html");
96706
- fs132.writeFileSync(filePath, html, "utf-8");
96973
+ fs133.writeFileSync(filePath, html, "utf-8");
96707
96974
  return {
96708
96975
  filePath,
96709
96976
  fileCount: aggregated.nodes.length,
@@ -96747,7 +97014,7 @@ __export(performance_report_exports, {
96747
97014
  resolveLogByteBudget: () => resolveLogByteBudget,
96748
97015
  summarizePhaseLatency: () => summarizePhaseLatency
96749
97016
  });
96750
- import * as fs133 from "node:fs";
97017
+ import * as fs134 from "node:fs";
96751
97018
  import * as readline from "node:readline";
96752
97019
  function resolveLogByteBudget() {
96753
97020
  return getMaxLogSizeMB() * 1024 * 1024;
@@ -96879,7 +97146,7 @@ async function readPhaseLogTail(filePath, windowBytes, maxSamples, processId, se
96879
97146
  });
96880
97147
  let handle;
96881
97148
  try {
96882
- handle = await fs133.promises.open(filePath, "r");
97149
+ handle = await fs134.promises.open(filePath, "r");
96883
97150
  const stat4 = await handle.stat();
96884
97151
  if (!stat4.isFile()) {
96885
97152
  const notAFile = new Error(`EISDIR: illegal operation on a directory, read '${filePath}'`);
@@ -99321,8 +99588,11 @@ function resolveSkillPaths(importMetaUrl) {
99321
99588
  });
99322
99589
  if (health.status !== "healthy") {
99323
99590
  const entryDir = path85.dirname(fileURLToPath6(importMetaUrl));
99324
- const reason = `${describeBundledResourceHealth(health, skillsDir, `no SKILL.md (or loadable .md) found under ${skillsDir}`)} (entry loaded from ${entryDir})`;
99325
- reportBundledResourceDirHealth(SKILLS_DIR_MISSING_KIND, skillsDir, health, "skills", reason, `pi-lens: registers zero skills \u2014 ${reason}.`);
99591
+ const healthDescription = describeBundledResourceHealth(health, skillsDir, `no SKILL.md (or loadable .md) found under ${skillsDir}`);
99592
+ const notifyReason = `${healthDescription} (entry loaded from ${entryDir})`;
99593
+ const classification = health.status === "unreadable" ? `cannot read (${health.fsErrorCode})` : health.status === "empty" ? "no SKILL.md found" : "no such directory";
99594
+ const ledgerReason = `${classification} (entry loaded from ${entryDir})`;
99595
+ reportBundledResourceDirHealth(SKILLS_DIR_MISSING_KIND, skillsDir, health, "skills", ledgerReason, `pi-lens: registers zero skills \u2014 ${notifyReason}.`);
99326
99596
  }
99327
99597
  return [skillsDir];
99328
99598
  }
@@ -104075,7 +104345,19 @@ function mergeActionableWarningsReports(args) {
104075
104345
  continue;
104076
104346
  }
104077
104347
  mergedFiles++;
104078
- const merged = incumbent ? {
104348
+ const merged = incumbent ? incumbent.reVerified || incumbent.reVerifyIncomplete ? (
104349
+ // #3170: the runtime just RE-OBSERVED this file (the bounded
104350
+ // persistent-reverify pass) — the fresh observation supersedes
104351
+ // the carried entry instead of unioning with it: a converged
104352
+ // finding would otherwise survive its own supersession via the
104353
+ // id-union in `mergeWarnings`.
104354
+ {
104355
+ ...incumbent,
104356
+ fileSeq: incumbent.fileSeq ?? entry.fileSeq,
104357
+ generatedAt: olderStamp(incumbent.generatedAt ?? newerReport?.generatedAt, entry.generatedAt ?? olderReport?.generatedAt),
104358
+ warnings: incumbent.warnings
104359
+ }
104360
+ ) : {
104079
104361
  ...incumbent,
104080
104362
  fileSeq: incumbent.fileSeq ?? entry.fileSeq,
104081
104363
  generatedAt: olderStamp(incumbent.generatedAt ?? newerReport?.generatedAt, entry.generatedAt ?? olderReport?.generatedAt),
@@ -104400,7 +104682,7 @@ function formatActionableWarningsAdvisory(report, cwd, host = "pi") {
104400
104682
  if (report.summary.unsuppressed === 0)
104401
104683
  return void 0;
104402
104684
  const files2 = report.files.filter((file) => file.warnings.some((warning) => !warning.suppressed));
104403
- const fileList = files2.slice(0, 5).map((file) => ` ${file.displayPath}: ${file.warnings.filter((warning) => !warning.suppressed).length}`).join("\n");
104685
+ const fileList = files2.slice(0, 5).map((file) => ` ${file.displayPath}: ${file.warnings.filter((warning) => !warning.suppressed).length}${file.reVerifyIncomplete ? " (re-verify incomplete)" : ""}`).join("\n");
104404
104686
  const more = files2.length > 5 ? `
104405
104687
  ... and ${files2.length - 5} more file(s)` : "";
104406
104688
  const safe2 = report.summary.autoFixEligible > 0 ? ` ${report.summary.autoFixEligible} appear to have conservative preferred quickfixes.` : "";
@@ -105660,6 +105942,8 @@ async function runFormatPhase(filePath, getFormatService2, dbg2, signal, budgetM
105660
105942
  };
105661
105943
  }
105662
105944
  function buildEnrichedBlockerOutput(blockers, fileContent = "") {
105945
+ if (blockers.length === 0)
105946
+ return "";
105663
105947
  const fileLines = fileContent ? fileContent.split("\n") : [];
105664
105948
  const MAX_SNIPPET = 120;
105665
105949
  let out = `
@@ -105898,7 +106182,8 @@ async function runPipeline(ctx, deps) {
105898
106182
  const rest = dispatchResult.output.slice(dispatchResult.blockerOutput.length);
105899
106183
  if (rest)
105900
106184
  gatedOut += rest;
105901
- output += `
106185
+ if (gatedOut)
106186
+ output += `
105902
106187
 
105903
106188
  ${gatedOut}`;
105904
106189
  } else {
@@ -113944,7 +114229,7 @@ function wrapSessionEventHandlerWithResult(eventName, handler, options) {
113944
114229
  init_session_lifecycle();
113945
114230
 
113946
114231
  // dist/clients/runtime-turn.js
113947
- import * as fs123 from "node:fs";
114232
+ import * as fs124 from "node:fs";
113948
114233
  import * as path187 from "node:path";
113949
114234
  init_cascade_budget();
113950
114235
  init_cascade_logger();
@@ -113952,196 +114237,7 @@ init_path_utils();
113952
114237
  init_string_utils();
113953
114238
  init_runner_context();
113954
114239
  init_file_utils();
113955
-
113956
- // dist/clients/finding-delivery-gate.js
113957
- var MS_PER_MINUTE = 6e4;
113958
- function markUnreconciledFindings(findings) {
113959
- return findings.map((finding) => ({ ...finding, stale: true }));
113960
- }
113961
- function formatCacheAgeLabel(scannedAt, nowMs = Date.now()) {
113962
- if (scannedAt === void 0 || scannedAt === null || scannedAt === "") {
113963
- return "scan age unknown";
113964
- }
113965
- const scannedAtMs = typeof scannedAt === "number" ? scannedAt : Date.parse(scannedAt);
113966
- if (!Number.isFinite(scannedAtMs))
113967
- return "scan age unknown";
113968
- const ageMs = Math.max(0, nowMs - scannedAtMs);
113969
- const ageMinutes = Math.round(ageMs / MS_PER_MINUTE);
113970
- if (ageMinutes < 1)
113971
- return "scanned <1m ago";
113972
- if (ageMinutes < 60)
113973
- return `scanned ${ageMinutes}m ago`;
113974
- const ageHours = Math.floor(ageMinutes / 60);
113975
- const remMinutes = ageMinutes % 60;
113976
- return remMinutes === 0 ? `scanned ${ageHours}h ago` : `scanned ${ageHours}h ${remMinutes}m ago`;
113977
- }
113978
- function gated(file, description, gates, evidence, extra = {}) {
113979
- return { mode: "gated", file, description, gates, evidence, ...extra };
113980
- }
113981
- function labeled(file, description, reason, ageSource, evidence = [], extra = {}) {
113982
- return {
113983
- mode: "labeled",
113984
- file,
113985
- description,
113986
- reason,
113987
- ageSource,
113988
- evidence,
113989
- ...extra
113990
- };
113991
- }
113992
- var RUNTIME_TURN_FILE = "clients/runtime-turn.ts";
113993
- var LENS_DIAGNOSTICS_FILE = "tools/lens-diagnostics.ts";
113994
- var DELIVERY_SURFACES = {
113995
- "runtime-turn:secrets-gitleaks": gated(RUNTIME_TURN_FILE, "Turn-end \u{1F534} secrets blocker, gitleaks cache.", ["gateFindingsByPathFreshness"], ['store: "gitleaks"']),
113996
- "runtime-turn:secrets-trivy": gated(RUNTIME_TURN_FILE, "Turn-end \u{1F534} secrets blocker, trivy secrets cache.", ["gateFindingsByPathFreshness"], ['store: "trivy-secrets"']),
113997
- "runtime-turn:govulncheck-advisory": gated(RUNTIME_TURN_FILE, "Turn-end \u{1F6E1}\uFE0F Go CVE advisory (call-site line only).", ["gateFindingsByPathFreshness"], ['store: "govulncheck"']),
113998
- // Two tagged seams (the stale and live render branches below the same
113999
- // `sweepInlineBlockerFreshness` call) legitimately share one evidence
114000
- // occurrence — evidenceMin: 2 tells the exclusive-assignment check both
114001
- // are expected claimants, not a rogue seam contesting the real one.
114002
- "runtime-turn:unresolved-inline-blocker": gated(
114003
- RUNTIME_TURN_FILE,
114004
- "Turn-end re-surfaced unresolved inline blockers.",
114005
- ["sweepInlineBlockerFreshness"],
114006
- // #1790: the call now passes `additionalEntries` (widget-store rows) as a
114007
- // third argument, so the literal is the call's opening rather than the
114008
- // whole single-line invocation.
114009
- ["sweepInlineBlockerFreshness(runtime, cwd, {"],
114010
- { evidenceMin: 2 }
114011
- ),
114012
- // Same two gate calls as the live secrets tier — this tier renders their
114013
- // `.stale` arm, so it has no OWN gate call to point at.
114014
- "runtime-turn:stale-secrets-tier": gated(RUNTIME_TURN_FILE, "Turn-end \u{1F511} demoted-secrets tier (drifted since scan).", ["gateFindingsByPathFreshness"], ['store: "gitleaks"', 'store: "trivy-secrets"']),
114015
- // The evidence below is the literal header FRAGMENT including the
114016
- // interpolation — proves the label is actually rendered, not merely
114017
- // computed and discarded (review round F1's "deleted the age
114018
- // interpolations" attack).
114019
- "runtime-turn:trivy-critical-blocker": labeled(RUNTIME_TURN_FILE, "Turn-end \u{1F534} CRITICAL dependency CVE blocker (trivy).", "Package-pinned finding with no cited file:line to stat \u2014 freshness has nothing to check drift against. The session_start cache can be arbitrarily old, so its age is stated instead. Also routes through `filterFindingsByDisposition` (#1625) first \u2014 a suppressed/false-positive finding never reaches this render, so the age label and the disposition filter compose rather than substitute for each other.", "TrivyResult.scannedAt", ["CRITICAL dependency CVEs (trivy, ${trivyAgeLabel}"]),
114020
- "runtime-turn:trivy-cve-advisory": labeled(RUNTIME_TURN_FILE, "Turn-end \u{1F6E1}\uFE0F non-critical dependency CVE advisory (trivy).", "Same package-pinned shape and #1625 disposition composition as the CRITICAL blocker above.", "TrivyResult.scannedAt", ["Dependency CVEs (trivy, ${trivyAgeLabel}"]),
114021
- "runtime-turn:trivy-license-advisory": labeled(RUNTIME_TURN_FILE, "Turn-end \u{1F4DC} dependency license-risk advisory (trivy).", "Same package-pinned shape as the CRITICAL blocker above.", "TrivyResult.scannedAt", ["Dependency license risk (trivy, ${trivyAgeLabel}"]),
114022
- "runtime-turn:dead-code-advisory": labeled(RUNTIME_TURN_FILE, "Turn-end dead-code delta advisory (knip/vulture/etc).", "The delta shown is computed from THIS turn's own analyze() call, diffed against the previous cache \u2014 never a replay of a stale cache.", "live"),
114023
- "runtime-turn:knip-blocker": labeled(RUNTIME_TURN_FILE, "Turn-end \u{1F534} Knip unresolved-imports/deps blocker.", "Same shape as the dead-code advisory: `await knipClient.analyze(cwd, ...)` runs fresh THIS turn, diffed against the previous cache and filtered to files edited this turn \u2014 never a replay of a stale cache.", "live"),
114024
- "runtime-turn:knip-advisory": labeled(RUNTIME_TURN_FILE, "Turn-end \u26A0\uFE0F Knip newly-unused-exports advisory.", "Same live-this-turn shape as the Knip blocker above.", "live"),
114025
- "runtime-turn:actionable-warnings-advisory": labeled(RUNTIME_TURN_FILE, "Turn-end actionable-warnings advisory (ast-grep etc).", "`peekActionableWarnings` reads `_actionableWarningsThisTurn` \u2014 recorded fresh from this turn's own dispatch, never a cross-turn cache.", "live"),
114026
- "runtime-turn:code-quality-warnings-advisory": labeled(RUNTIME_TURN_FILE, "Turn-end code-quality-warnings advisory.", "`peekCodeQualityWarnings` reads `_codeQualityWarningsThisTurn` \u2014 the same per-turn accumulator shape as actionable-warnings above.", "live"),
114027
- "runtime-turn:disposition-suppressed-notice": labeled(RUNTIME_TURN_FILE, "Turn-end running disposition-suppressed-count notice (#1616).", "A live running total accumulated from this turn's own gate/disposition calls above (`dispositionSuppressedTotal`) \u2014 telemetry ABOUT the other gated surfaces, not a cache of its own.", "live"),
114028
- // #2001/#2002 collect-later: findings an auxiliary LSP published AFTER its
114029
- // aux-grace window expired, probed from the client cache at the next
114030
- // turn_end. Gated on the mark timestamp: a cited file deleted or edited
114031
- // since the mark drops its findings (never delivered before, and the
114032
- // drifting edit already re-touched the file — a fresh pending pair
114033
- // supersedes this one), with both drop arms counted in the
114034
- // `late_auxiliary_findings` latency record rather than silenced.
114035
- //
114036
- // #3102: freshness is only half the gate. The survivors also take the shared
114037
- // `clients/dispatch/finding-policy.ts` stack (inline `pi-lens-ignore` →
114038
- // stored dispositions → `.pi-lens.json` rule policy) the per-edit dispatcher,
114039
- // `mode=full` and the `source=lsp` probe lane apply — without it a finding
114040
- // the agent marked `false-positive` re-reported on every drain. Per #3088
114041
- // round-2 F4, the evidence is the CALL TEXT of that filter, which exists
114042
- // nowhere else in this file: a bare `applyFindingPolicy` would also be
114043
- // satisfied by an import line or an unrelated caller.
114044
- "runtime-turn:late-auxiliary-findings": gated(RUNTIME_TURN_FILE, "Turn-end late-auxiliary LSP findings (collect-later probe of aux client caches whose grace window expired).", ["gateFindingsByPathFreshness", "applyFindingPolicy"], ['store: "late-auxiliary-findings"', "applyFindingPolicy(gate.live, {"]),
114045
- "runtime-turn:late-runner-findings": gated(RUNTIME_TURN_FILE, "Turn-end CLI runner findings collected after the post-write path.", ["gateFindingsByPathFreshness"], ['store: "late-runner-findings"'], { evidenceMin: 2 }),
114046
- "runtime-turn:cascade-blocker": labeled(RUNTIME_TURN_FILE, "Turn-end \u{1F9EA} cascade neighbor blocker.", "Cascade results settle synchronously this turn where possible (`settleCascadeRuns`), but an unsettled compute can carry over to a later turn (bounded by a carry cap) \u2014 this round does not freshness-gate that carry-over window.", "live", [], {
114047
- status: "partial",
114048
- partialReason: "A carried-over cascade result (run.carriedTurns > 0) is rendered without an age label. Follow-up: surface carriedTurns as an explicit label when > 0, or route through formatCacheAgeLabel using the run's own timestamp."
114049
- }),
114050
- "runtime-turn:cascade-coverage-advisory": labeled(RUNTIME_TURN_FILE, "Turn-end cascade-coverage-gap advisories (graph/binding/budget).", "Explains what the cascade check could NOT confirm this turn \u2014 not a finding with a cited path, an absence-of-coverage disclosure computed from this turn's own indeterminate-run list.", "live", [], {
114051
- status: "partial",
114052
- partialReason: "Same cascade carry-over caveat as runtime-turn:cascade-blocker."
114053
- }),
114054
- // #3102: the cold-neighbour cascade run is BUILT here, in the quiet-window
114055
- // reconcile (`onResolvedFound` in index.ts), a turn earlier than the
114056
- // `runtime-turn:cascade-blocker` render that finally carries it — so it is
114057
- // its own delivery lane, and it was the one that rendered raw LSP findings
114058
- // with no policy filter at all. The evidence is the CALL TEXT of the shared
114059
- // filter (#3088 round-2 F4): a bare `applyFindingPolicy` would also be
114060
- // satisfied by the import line.
114061
- "cascade-format:resolved-found-run": gated("clients/cascade-format.ts", "Cold-neighbour ERROR diagnostics formatted into the turn-end cascade run by `buildResolvedFoundCascadeRun` (quiet-window reconcile of a cascade touch that skipped its in-lane wait).", ["applyFindingPolicy"], ["applyFindingPolicy(retained, {"]),
114062
- // #3157: the IN-LANE cascade — a different delivery lane from the
114063
- // quiet-window run above, and the one #3102's sweep cleared WRONGLY. Its four
114064
- // display sites (passive cold snapshot, fresh touch, touch-error fallback,
114065
- // degraded fallback) assemble `CascadeNeighborResult.diagnostics` and reach
114066
- // the agent through `formatCascadeNeighborDiagnostics` without ever entering
114067
- // the dispatcher's `applyOutputFilters` pipeline: the sweep's file-level
114068
- // verdict ("the per-edit dispatch path, which already filters in
114069
- // dispatcher.ts") was true only of the per-edit RUNNER output in
114070
- // `runners/lsp.ts`, which is a different call site in a different file.
114071
- //
114072
- // Evidence is the CALL TEXT (#3088 round-2 F4), counted: `cascadeDisplay(` is
114073
- // the one seam all four sites route through, so `evidenceMin: 4` is the count
114074
- // of display sites in the file and dropping ANY of them back to a raw
114075
- // `convertLspDiagnostics` reds this suite. Identity-stubbing the callee
114076
- // instead is caught behaviourally — it reds twelve cases in
114077
- // `tests/clients/inlane-cascade-finding-policy.test.ts`, which is a stronger
114078
- // signal than the R2 proximity heuristic. Residual, stated: a FIFTH display
114079
- // site that open-codes the conversion keeps the count at 4 and is caught by
114080
- // review, not by this row.
114081
- "dispatch-integration:in-lane-cascade": gated("clients/dispatch/integration.ts", "In-lane per-edit cascade neighbour ERROR diagnostics, rendered into the turn-end cascade block by `computeCascadeForFile`.", ["applyCascadeDisplayPolicy"], ["cascadeDisplay("], { evidenceMin: 4 }),
114082
- "runtime-turn:call-graph-advisory": labeled(RUNTIME_TURN_FILE, "Turn-end \u{1F4CA} call-graph impact advisory.", "`runtime.callGraph` is a session-lifetime in-memory structure; this round does not verify or gate ITS OWN freshness policy (how/when it incorporates edits made earlier in the session).", "live", [], {
114083
- status: "partial",
114084
- partialReason: "call-graph freshness/invalidation policy is out of this PR's scope \u2014 tracked as a follow-up, not silently assumed fresh."
114085
- }),
114086
- // #3088 round 2 F4: this row used to name `applyDispositions`/`applyRulePolicy`
114087
- // with whole-file evidence literals. Once #3088 folded mode=full's stack onto
114088
- // `applyFindingPolicy`, those two literals survived only in UNRELATED
114089
- // mode=delta helpers in the same file, so deleting the entire policy stack out
114090
- // of `applyInlineSuppressionsToSummaries` left this gate — and the ratchet —
114091
- // green while five behaviour cases redded. The evidence is now the call text
114092
- // of the merge's own filter, which exists nowhere else in the file.
114093
- "lens-diagnostics:mode-full": gated(LENS_DIAGNOSTICS_FILE, "`lens_diagnostics mode=full` report.", ["applyFindingPolicy", "reconcileProjectDiagnosticsSnapshot"], [
114094
- "applyFindingPolicy(summary.diagnostics, {",
114095
- "reconcileProjectDiagnosticsSnapshot("
114096
- ]),
114097
- "lens-diagnostics:mode-all": gated(LENS_DIAGNOSTICS_FILE, "`lens_diagnostics mode=all` report (widget-state read).", ["reconcileStaleWidgetFiles", "reconcileStaleWidgetDependencyBlockers"], ["reconcileStaleWidgetFiles(", "reconcileStaleWidgetDependencyBlockers("]),
114098
- // #1634 review round F3: this was the most important gap — the tool's
114099
- // DEFAULT mode rendered cached `file:line` findings with zero freshness
114100
- // check. `applyDeltaFreshnessGate` (this file's sibling in
114101
- // tools/lens-diagnostics.ts) now routes the actionable/quality-warnings
114102
- // caches through the shared gate using each report's own `generatedAt`.
114103
- // Round R3: mode=delta has a THIRD arm — the persisted project-diagnostics
114104
- // delta report, rendered by `appendProjectDiagnosticsDeltaLines` — which
114105
- // carried the identical unfixed shape. It now gates the same way, against
114106
- // its own `generatedAt`.
114107
- "lens-diagnostics:mode-delta": gated(LENS_DIAGNOSTICS_FILE, "`lens_diagnostics mode=delta` report (the tool's DEFAULT mode) \u2014 re-serves the actionable-warnings/code-quality-warnings caches AND the persisted project-diagnostics delta report.", ["gateFindingsByPathFreshness"], [
114108
- 'store: "lens-diagnostics-delta"',
114109
- "applyDeltaFreshnessGate(",
114110
- 'store: "lens-diagnostics-delta-project"'
114111
- ]),
114112
- "widget-state:footer": gated("clients/widget-state.ts", "TUI footer blocking/error/warning tally.", ["reconcileStaleWidgetFiles", "isBlocking"], ["isBlocking(diagnostic)", "reconcileStaleWidgetFiles()"]),
114113
- "agent-nudge:context-message": labeled("clients/agent-nudge.ts", "Post-edit file-touch nudge injected via `context`.", "Not a scanner finding store: no severity, no cited line, no cache \u2014 the accumulator is drained and cleared at the moment of injection, so the content IS the current state by construction.", "live"),
114114
- "test-runner-delivery:custom-entry": labeled("clients/test-runner-delivery.ts", "Post-agent test-runner failures in a non-context custom entry.", "The cache remains authoritative for pull diagnostics and the commit guard; this surface appends only after provenance validation and an idle recheck.", "live"),
114115
- "project-diagnostics:persisted-snapshot": gated(LENS_DIAGNOSTICS_FILE, "Cross-session persisted project-diagnostics snapshot read.", ["reconcileProjectDiagnosticsSnapshot"], ["reconcileProjectDiagnosticsSnapshot("]),
114116
- // ── #2028: the remaining agent-facing surfaces ──────────────────────────
114117
- // Registered after #2028's root-cause review found the 🔴 STOP block
114118
- // rendering stale/deleted-file blockers because its surface was never in
114119
- // this registry — the exact "gated or labeled, never neither" gap this
114120
- // module exists to close.
114121
- "tool-call:stop-blocker": gated("clients/pipeline.ts", "Per-edit \u{1F534} STOP blocker output appended to the write/edit tool result.", ["dropFindingsForMissingPaths"], ['store: "stop-blocker"']),
114122
- // The freshness stack itself (own-file mtime + reverse-dependency mtimes
114123
- // via isEntryFresh, plus the #1095 content binding) lives inside
114124
- // clients/lsp/workspace-diagnostics-cache.ts; the two evidence calls are
114125
- // the tool's literal entry points into that gated path.
114126
- "lsp-diagnostics:tool-output": gated("tools/lsp-diagnostics.ts", "`lsp_diagnostics` batch/directory sweep results. Cache hits replay through the shared workspace-diagnostics cache: createWorkspaceDiagnosticsCacheContext is the tool's entry, and its lookup() applies the #671/#672 freshness stack (own-file mtime + reverse-dependency mtimes via isEntryFresh) plus the #1095 content binding. #3088: every route out of this tool \u2014 fresh, cache replay, single file, directory \u2014 also passes applyProbeFindingPolicy, the stored-disposition / .pi-lens.json rule-policy / inline pi-lens-ignore stack the per-edit dispatch path and mode=full apply.", [
114127
- "createWorkspaceDiagnosticsCacheContext",
114128
- "isEntryFresh",
114129
- "cacheCtx.lookup",
114130
- "applyProbeFindingPolicy"
114131
- ], [
114132
- "createWorkspaceDiagnosticsCacheContext(resolvedCwd)",
114133
- "cacheCtx.lookup(file, scopeKey)",
114134
- "applyProbeFindingPolicy("
114135
- ]),
114136
- "git-guard:commit-blocked": labeled("clients/git-guard.ts", "Git-guard commit/push \u{1F534} COMMIT BLOCKED verdict (--lens-guard).", "Synchronous preflight rejection returned inline with the failed git command \u2014 no stored state is delivered, so nothing can go stale between detection and delivery.", "live"),
114137
- "shared-checkout-guard:worktree-mutation-blocked": labeled("clients/shared-checkout-guard.ts", "Shared-checkout \u{1F534} WORKING-TREE CHANGE BLOCKED verdict (--lens-checkout-guard).", "Synchronous preflight rejection returned inline with the failed git command. Both inputs are read at decision time \u2014 the instance registry and `git status` \u2014 so no stored finding is replayed and nothing can go stale between detection and delivery.", "live"),
114138
- "read-guard-tool-lines:preflight-errors": labeled("clients/read-guard-tool-lines.ts", "Read-guard hashline BLOCKED / RE-READ REQUIRED / PARTIAL APPLY / ALREADY APPLIED preflight errors (#2402).", "Computed fresh per edit attempt and returned as that attempt's rejection \u2014 no cached findings are replayed, so there is no staleness window. The PARTIAL APPLY and ALREADY APPLIED variants describe the same attempt's commit outcome; applied-edit recognition reads RuntimeCoordinator's session-scoped record, which resets with the session.", "live"),
114139
- "mutating-tool:adapter-preflight-errors": labeled("clients/mutating-tool.ts", "Shape-adapter BLOCKED preflight errors for hashline edit inputs the adapter recognized but could not resolve to a range (#2423).", "Computed fresh from the tool input on each attempt and returned as that attempt's rejection, so there is no staleness window. The adapters read only the call's own arguments; nothing cached is replayed.", "live"),
114140
- "tool-call:duplicate-export-blocker": labeled("clients/runtime-tool-call.ts", "Duplicate-export STOP rejection returned inline with the failed edit.", "Synchronous preflight rejection computed per edit attempt; no stored state.", "live"),
114141
- "agent-behavior:thrashing-notice": labeled("clients/agent-behavior-client.ts", "In-result thrashing/blind-write detection notice.", "Ephemeral notice computed at detection time inside the same tool result \u2014 no persistence and no cache, so nothing can be stale.", "live")
114142
- };
114143
-
114144
- // dist/clients/runtime-turn.js
114240
+ init_finding_delivery_gate();
114145
114241
  init_workspace_sweep_hold();
114146
114242
  init_collateral_test_role();
114147
114243
  init_gitleaks_client();
@@ -114566,6 +114662,176 @@ init_lsp();
114566
114662
  init_pending_aux_coverage();
114567
114663
  init_lsp_diagnostics();
114568
114664
  init_auxiliary_lsp();
114665
+
114666
+ // dist/clients/persistent-reverify.js
114667
+ import * as fs123 from "node:fs";
114668
+ init_deadline_utils();
114669
+ init_latency_logger();
114670
+ init_diagnostic_binding();
114671
+ init_auxiliary_lsp();
114672
+ var MAX_REVERIFY_FILES = 4;
114673
+ var REVERIFY_BUDGET_MS = 3e3;
114674
+ var TOUCH_SOURCE = "persistent_reverify";
114675
+ function selectPersistentReverifyFiles(report, nowMs = Date.now()) {
114676
+ const candidates = [];
114677
+ let skippedChanged = 0;
114678
+ for (const entry of report.files) {
114679
+ if (entry.origin !== "deferred")
114680
+ continue;
114681
+ if (entry.warnings.some((w) => findAuxiliaryProfileForSource(w.source ?? "") !== void 0)) {
114682
+ continue;
114683
+ }
114684
+ const stamp2 = entry.generatedAt ? Date.parse(entry.generatedAt) : NaN;
114685
+ if (!Number.isFinite(stamp2) || stamp2 > nowMs)
114686
+ continue;
114687
+ let mtimeMs;
114688
+ try {
114689
+ mtimeMs = fs123.statSync(entry.filePath).mtimeMs;
114690
+ } catch {
114691
+ continue;
114692
+ }
114693
+ if (mtimeMs > stamp2) {
114694
+ skippedChanged += 1;
114695
+ continue;
114696
+ }
114697
+ if (candidates.length < MAX_REVERIFY_FILES)
114698
+ candidates.push(entry);
114699
+ }
114700
+ return { candidates, skippedChanged };
114701
+ }
114702
+ async function runPersistentReverify(args) {
114703
+ const started = Date.now();
114704
+ const deadlineAt = started + (args.budgetMs ?? REVERIFY_BUDGET_MS);
114705
+ const nowMs = args.nowMs ?? started;
114706
+ const { candidates, skippedChanged } = selectPersistentReverifyFiles(args.report, nowMs);
114707
+ const outcomes = [];
114708
+ const replacementFiles = [];
114709
+ let touched = 0;
114710
+ const buildArgs = {
114711
+ cwd: args.cwd,
114712
+ sessionId: args.report.sessionId,
114713
+ turnIndex: 0,
114714
+ files: [],
114715
+ modifiedRangesByFile: /* @__PURE__ */ new Map(),
114716
+ dispatchWarnings: [],
114717
+ includeLspCodeActions: true,
114718
+ deltaOnly: false,
114719
+ ...args.signal !== void 0 ? { signal: args.signal } : {},
114720
+ lspBudgetMs: REVERIFY_BUDGET_MS,
114721
+ dbg: () => {
114722
+ }
114723
+ };
114724
+ let processed = 0;
114725
+ for (const entry of candidates) {
114726
+ if (args.signal?.aborted || Date.now() >= deadlineAt)
114727
+ break;
114728
+ processed += 1;
114729
+ let content;
114730
+ try {
114731
+ content = fs123.readFileSync(entry.filePath, "utf-8");
114732
+ } catch {
114733
+ outcomes.push({
114734
+ filePath: entry.filePath,
114735
+ displayPath: entry.displayPath,
114736
+ outcome: "unconfirmed",
114737
+ dropped: 0,
114738
+ kept: entry.warnings.length
114739
+ });
114740
+ replacementFiles.push({ ...entry, reVerifyIncomplete: true });
114741
+ continue;
114742
+ }
114743
+ let touchedResult;
114744
+ try {
114745
+ touchedResult = await bounded(args.lspService.touchFile(entry.filePath, content, {
114746
+ diagnostics: "document",
114747
+ collectDiagnostics: true,
114748
+ source: TOUCH_SOURCE,
114749
+ clientScope: "primary"
114750
+ }), {
114751
+ ms: Math.max(250, deadlineAt - Date.now()),
114752
+ signal: args.signal,
114753
+ hook: "turn_end",
114754
+ label: "persistent_reverify_touch"
114755
+ });
114756
+ } catch {
114757
+ touchedResult = void 0;
114758
+ }
114759
+ if (touchedResult === void 0 || touchedResult.inconclusive === true || touchedResult.skipReason !== void 0 || (touchedResult.diagnosticsUnsupportedServerIds ?? []).length > 0 || !touchCompletedConfirmationPolicy(touchedResult)) {
114760
+ outcomes.push({
114761
+ filePath: entry.filePath,
114762
+ displayPath: entry.displayPath,
114763
+ outcome: "unconfirmed",
114764
+ dropped: 0,
114765
+ kept: entry.warnings.length
114766
+ });
114767
+ replacementFiles.push({ ...entry, reVerifyIncomplete: true });
114768
+ continue;
114769
+ }
114770
+ touched += 1;
114771
+ const freshRecords = await bounded(enrichFileFromLsp(args.cwd, buildArgs, { filePath: entry.filePath, cached: touchedResult.diags }, {
114772
+ lspService: args.lspService,
114773
+ pullTimeoutMs: 2e3,
114774
+ deadlineAt,
114775
+ ...args.signal !== void 0 ? { signal: args.signal } : {},
114776
+ site: { hook: "turn_end", label: "persistent_reverify" }
114777
+ }), {
114778
+ ms: Math.max(250, deadlineAt - Date.now()),
114779
+ signal: args.signal,
114780
+ hook: "turn_end",
114781
+ label: "persistent_reverify_enrich"
114782
+ }) ?? [];
114783
+ let dropped = 0;
114784
+ let kept = 0;
114785
+ for (const warning of entry.warnings) {
114786
+ const stillThere = freshRecords.some((record6) => record6.rule === warning.rule && record6.message === warning.message);
114787
+ if (stillThere)
114788
+ kept += 1;
114789
+ else
114790
+ dropped += 1;
114791
+ }
114792
+ const outcome = freshRecords.length === 0 ? "clean" : dropped === 0 ? "reconfirmed" : "mixed";
114793
+ outcomes.push({
114794
+ filePath: entry.filePath,
114795
+ displayPath: entry.displayPath,
114796
+ outcome,
114797
+ dropped,
114798
+ kept
114799
+ });
114800
+ replacementFiles.push({
114801
+ ...entry,
114802
+ warnings: freshRecords,
114803
+ reVerified: true,
114804
+ generatedAt: new Date(nowMs).toISOString()
114805
+ });
114806
+ }
114807
+ logLatency({
114808
+ type: "phase",
114809
+ toolName: "turn_end",
114810
+ filePath: args.cwd,
114811
+ phase: "persistent_reverify",
114812
+ durationMs: Date.now() - started,
114813
+ metadata: {
114814
+ candidates: candidates.length,
114815
+ touched,
114816
+ clean: outcomes.filter((o) => o.outcome === "clean").length,
114817
+ reconfirmed: outcomes.filter((o) => o.outcome === "reconfirmed").length,
114818
+ mixed: outcomes.filter((o) => o.outcome === "mixed").length,
114819
+ unconfirmed: outcomes.filter((o) => o.outcome === "unconfirmed").length,
114820
+ skippedChanged,
114821
+ skippedBudget: candidates.length - processed
114822
+ }
114823
+ });
114824
+ return {
114825
+ outcomes,
114826
+ replacementFiles,
114827
+ candidates: candidates.length,
114828
+ touched,
114829
+ skippedChanged
114830
+ };
114831
+ }
114832
+
114833
+ // dist/clients/runtime-turn.js
114834
+ init_cascade_format();
114569
114835
  init_finding_policy();
114570
114836
  init_file_role();
114571
114837
  init_pending_runner_findings();
@@ -115069,6 +115335,17 @@ ${summary}`);
115069
115335
  return true;
115070
115336
  });
115071
115337
  const cascadeResults = cascadeRuns.flatMap((r) => r.result ? [r.result] : []);
115338
+ let carriedRunsRendered = 0;
115339
+ let labeledAdvisories = 0;
115340
+ const carriedMetaByResult = /* @__PURE__ */ new Map();
115341
+ for (const r of cascadeRuns) {
115342
+ if (r.result && (r.carriedTurns ?? 0) > 0 && r.carriedTurns !== void 0) {
115343
+ carriedMetaByResult.set(r.result, {
115344
+ carriedTurns: r.carriedTurns,
115345
+ observedAt: r.observedAt
115346
+ });
115347
+ }
115348
+ }
115072
115349
  const cascadeLogFilePath = cascadeResults[0]?.filePath ?? files2[0] ?? cwd;
115073
115350
  if (cascadeResults.length > 0) {
115074
115351
  const seen = /* @__PURE__ */ new Map();
@@ -115092,7 +115369,12 @@ ${summary}`);
115092
115369
  const pk = normalizeMapKey(result.filePath);
115093
115370
  const ownsAny = result.neighbors.some((n) => neighborOwner.get(normalizeMapKey(n.filePath)) === pk);
115094
115371
  if (ownsAny && result.formatted) {
115095
- parts.push(result.formatted);
115372
+ const carryMeta = carriedMetaByResult.get(result);
115373
+ const carrySuffix = cascadeCarrySuffix(carryMeta?.carriedTurns, carryMeta?.observedAt);
115374
+ parts.push(carrySuffix ? `${result.formatted}
115375
+ ${carrySuffix}` : result.formatted);
115376
+ if (carrySuffix)
115377
+ carriedRunsRendered += 1;
115096
115378
  injectedNeighborCount += result.neighbors.length;
115097
115379
  injectedDiagnosticCount += result.neighbors.reduce((s, n) => s + n.diagnostics.length, 0);
115098
115380
  } else if (!ownsAny && result.formatted) {
@@ -115181,19 +115463,35 @@ ${lines.join("\n")}`;
115181
115463
  const graphRuns = indeterminateRuns.filter((r) => r.indeterminate?.reason !== "lsp_binding_rejected" && r.indeterminate?.reason !== "excluded_by_role" && r.indeterminate?.reason !== "budget_truncated" && r.indeterminate?.budget === void 0);
115182
115464
  const bindingRuns = indeterminateRuns.filter((r) => r.indeterminate?.reason === "lsp_binding_rejected" && r.indeterminate?.budget === void 0);
115183
115465
  const budgetRuns = indeterminateRuns.filter((r) => r.indeterminate?.reason === "budget_truncated" || r.indeterminate?.budget !== void 0);
115184
- const graphAdvisory = buildAdvisory(graphRuns, {
115466
+ const withCarryLabel = (advisory, runs) => {
115467
+ if (advisory === void 0)
115468
+ return void 0;
115469
+ const carried = runs.filter((r) => (r.carriedTurns ?? 0) > 0);
115470
+ if (carried.length === 0 || carried.length !== runs.length) {
115471
+ return advisory;
115472
+ }
115473
+ const stamps = carried.map((r) => r.observedAt);
115474
+ const observedAt = stamps.every((s) => s !== void 0) ? Math.min(...stamps) : void 0;
115475
+ const suffix = cascadeCarrySuffix(carried[0]?.carriedTurns, observedAt);
115476
+ if (!suffix)
115477
+ return advisory;
115478
+ labeledAdvisories += 1;
115479
+ return `${advisory}
115480
+ ${suffix}`;
115481
+ };
115482
+ const graphAdvisory = withCarryLabel(buildAdvisory(graphRuns, {
115185
115483
  lead: (fileCount2, reasons) => `Cascade could not compute downstream impact for ${fileCount2} edited file(s) this turn \u2014 the review graph was unavailable (${reasons}), so their dependents were not cascade-checked and a clean cascade result does not cover them.`,
115186
115484
  fallbackDetail: (r) => r.indeterminate?.reason === "missing_node" ? "changed file not in the review graph" : "review graph unavailable"
115187
- });
115485
+ }), graphRuns);
115188
115486
  if (graphAdvisory)
115189
115487
  advisoryParts.push(graphAdvisory);
115190
- const bindingAdvisory = buildAdvisory(bindingRuns, {
115488
+ const bindingAdvisory = withCarryLabel(buildAdvisory(bindingRuns, {
115191
115489
  lead: (fileCount2, reasons) => `Cascade identified dependents for ${fileCount2} edited file(s) this turn, but their diagnostics could not be freshly confirmed (${reasons}) and were withheld \u2014 a clean cascade result does not cover them.`,
115192
115490
  fallbackDetail: () => "cascade diagnostics withheld (binding rejected)"
115193
- });
115491
+ }), bindingRuns);
115194
115492
  if (bindingAdvisory)
115195
115493
  advisoryParts.push(bindingAdvisory);
115196
- const budgetAdvisory = buildAdvisory(budgetRuns, {
115494
+ const budgetAdvisory = withCarryLabel(buildAdvisory(budgetRuns, {
115197
115495
  lead: (fileCount2, reasons) => `Cascade checked the selected neighbors for ${fileCount2} edited file(s) this turn, but some eligible dependents were not checked because the cascade budget was exhausted (${reasons}); a clean cascade result does not cover them.`,
115198
115496
  fallbackDetail: (r) => {
115199
115497
  const budget = r.indeterminate?.budget;
@@ -115202,7 +115500,7 @@ ${lines.join("\n")}`;
115202
115500
  const detail = `cascade budget checked ${budget.selectedCount} of ${budget.eligibleCount} eligible dependents (${budget.truncatedCount} omitted)`;
115203
115501
  return budget.transitiveTruncated ? `${detail}; transitive expansion was capped before all eligible dependents were enumerated` : detail;
115204
115502
  }
115205
- });
115503
+ }), budgetRuns);
115206
115504
  if (budgetAdvisory)
115207
115505
  advisoryParts.push(budgetAdvisory);
115208
115506
  const fileCount = new Set(indeterminateRuns.map((r) => normalizeMapKey(r.filePath))).size;
@@ -115228,6 +115526,13 @@ ${lines.join("\n")}`;
115228
115526
  }
115229
115527
  });
115230
115528
  }
115529
+ if (carriedRunsRendered > 0 || labeledAdvisories > 0) {
115530
+ logCascade({
115531
+ phase: "cascade_carry_rendered",
115532
+ filePath: cascadeLogFilePath,
115533
+ metadata: { carriedRunsRendered, labeledAdvisories }
115534
+ });
115535
+ }
115231
115536
  const cascadeSkipped = {
115232
115537
  blockers: 0,
115233
115538
  non_code: 0,
@@ -115761,7 +116066,7 @@ The cached line numbers are no longer trustworthy, so they are withheld. Re-run
115761
116066
  continue;
115762
116067
  }
115763
116068
  const config = RUNNERS[carried.runner];
115764
- if (!config || isExcludedTestTarget(testFile, cwd) || !fs123.existsSync(testFile)) {
116069
+ if (!config || isExcludedTestTarget(testFile, cwd) || !fs124.existsSync(testFile)) {
115765
116070
  resolvedDeferralKeys.add(carriedKey);
115766
116071
  deadDeferred++;
115767
116072
  continue;
@@ -115836,7 +116141,7 @@ The cached line numbers are no longer trustworthy, so they are withheld. Re-run
115836
116141
  dbg2(`turn_end: ${display} \u2192 test target excluded (integration/e2e), skipping spawn (${path187.relative(cwd, target.testFile)})`);
115837
116142
  continue;
115838
116143
  }
115839
- if (!fs123.existsSync(target.testFile)) {
116144
+ if (!fs124.existsSync(target.testFile)) {
115840
116145
  missingTargetFiles++;
115841
116146
  dbg2(`turn_end: ${display} \u2192 test file missing, skipping spawn (${path187.relative(cwd, target.testFile)})`);
115842
116147
  continue;
@@ -116238,7 +116543,25 @@ ${impactLines.join("\n")}`);
116238
116543
  fileSeqByPath.set(filePath, getFileSeq.call(runtime2, filePath));
116239
116544
  }
116240
116545
  }
116546
+ let reverify;
116241
116547
  if (getFlag("lens-actionable-warnings")) {
116548
+ const persistedReport = cacheManager2.readCache("actionable-warnings", cwd, Number.MAX_SAFE_INTEGER)?.data;
116549
+ if (persistedReport?.files?.some((entry) => entry.origin === "deferred")) {
116550
+ const reverifyLspService = getLSPService();
116551
+ if (reverifyLspService) {
116552
+ reverify = await bounded(runPersistentReverify({
116553
+ report: persistedReport,
116554
+ cwd,
116555
+ lspService: reverifyLspService,
116556
+ signal: getAmbientAbortSignal()
116557
+ }), {
116558
+ ms: HOOK_WALL_BUDGET_MS.turn_end,
116559
+ signal: getAmbientAbortSignal(),
116560
+ hook: "turn_end",
116561
+ label: "persistent_reverify"
116562
+ });
116563
+ }
116564
+ }
116242
116565
  try {
116243
116566
  const report = await buildActionableWarningsReport({
116244
116567
  cwd,
@@ -116281,6 +116604,19 @@ ${impactLines.join("\n")}`);
116281
116604
  }
116282
116605
  }
116283
116606
  });
116607
+ if (reverify?.replacementFiles.length) {
116608
+ const replacementByPath = new Map(reverify.replacementFiles.map((file) => [
116609
+ normalizeMapKey(file.filePath),
116610
+ file
116611
+ ]));
116612
+ const files3 = (report.files ?? []).map((file) => replacementByPath.get(normalizeMapKey(file.filePath)) ?? file);
116613
+ for (const replacement of reverify.replacementFiles) {
116614
+ if (!files3.some((file) => normalizeMapKey(file.filePath) === normalizeMapKey(replacement.filePath))) {
116615
+ files3.push(replacement);
116616
+ }
116617
+ }
116618
+ report.files = files3;
116619
+ }
116284
116620
  const publishResult = publishActionableWarningsReport(cacheManager2, cwd, report, {
116285
116621
  origin: "in-band",
116286
116622
  getFileSeq: getFileSeq ? (filePath) => getFileSeq.call(runtime2, filePath) : void 0,
@@ -116484,7 +116820,7 @@ ${lines.join("\n")}`);
116484
116820
  if (!lateAuxContentRead) {
116485
116821
  lateAuxContentRead = true;
116486
116822
  try {
116487
- lateAuxContentValue = fs123.readFileSync(lateAuxPath, "utf-8");
116823
+ lateAuxContentValue = fs124.readFileSync(lateAuxPath, "utf-8");
116488
116824
  } catch {
116489
116825
  lateAuxContentValue = void 0;
116490
116826
  }
@@ -116965,13 +117301,14 @@ function createActivateToolsTool(pi, lazyTools, options = {}) {
116965
117301
 
116966
117302
  // dist/tools/lens-diagnostics.js
116967
117303
  init_bounded_cache();
116968
- import { promises as fs129 } from "node:fs";
117304
+ import { promises as fs130 } from "node:fs";
116969
117305
  import * as fsSync from "node:fs";
116970
117306
  import * as path200 from "node:path";
116971
117307
  init_diagnostic_dispositions();
116972
117308
  init_blocker_freshness();
116973
117309
  init_freshness();
116974
117310
  init_finding_policy();
117311
+ init_finding_delivery_gate();
116975
117312
  init_rule_id_normalize();
116976
117313
  init_rule_policy();
116977
117314
  init_deadline_utils();
@@ -117048,7 +117385,7 @@ init_gitleaks_client();
117048
117385
  init_govulncheck_client();
117049
117386
  init_project_trust();
117050
117387
  init_trivy_client();
117051
- import * as fs125 from "node:fs";
117388
+ import * as fs126 from "node:fs";
117052
117389
  import * as os35 from "node:os";
117053
117390
  import * as path192 from "node:path";
117054
117391
 
@@ -117154,7 +117491,7 @@ function opengrepResultToProjectDiagnostics(cwd, result) {
117154
117491
 
117155
117492
  // dist/clients/project-diagnostics/runner-adapters/runner-findings.js
117156
117493
  init_test_runner_client();
117157
- import * as fs124 from "node:fs";
117494
+ import * as fs125 from "node:fs";
117158
117495
  import * as path191 from "node:path";
117159
117496
  function failureMessage(failure) {
117160
117497
  const firstLine2 = failure.message.split("\n")[0]?.slice(0, 300) ?? "";
@@ -117221,7 +117558,7 @@ function testRunnerFindingsToProjectDiagnostics(cache2, cwd, runtime2) {
117221
117558
  return [];
117222
117559
  const validation = cwd ? validateAdvisoryProvenance(cache2, cwd, runtime2) : { status: "unknown", reasons: ["validation-context-missing"] };
117223
117560
  const root = cwd ?? process.cwd();
117224
- const missingKeys = new Set((cache2.provenance?.files ?? []).filter((file) => !fs124.existsSync(path191.resolve(root, file.path))).map((file) => advisoryPathKey(file.path, root)));
117561
+ const missingKeys = new Set((cache2.provenance?.files ?? []).filter((file) => !fs125.existsSync(path191.resolve(root, file.path))).map((file) => advisoryPathKey(file.path, root)));
117225
117562
  const historical = cache2.superseded === true || cache2.stale === true || validation.status !== "current";
117226
117563
  return cache2.results.filter((result) => !missingKeys.has(advisoryPathKey(result.file, root))).flatMap((result) => testResultToProjectDiagnostics(result, historical)).map((diagnostic) => historical ? {
117227
117564
  ...diagnostic,
@@ -117253,10 +117590,10 @@ async function fetchFreshProjectDiagnostics(cacheManager2, cwd, clients, signal,
117253
117590
  let analysisRootValidation;
117254
117591
  if (options.analysisRoot !== void 0) {
117255
117592
  try {
117256
- if (!fs125.statSync(requestedRoot).isDirectory()) {
117593
+ if (!fs126.statSync(requestedRoot).isDirectory()) {
117257
117594
  throw new Error("is not a directory");
117258
117595
  }
117259
- analysisRoot = fs125.realpathSync(requestedRoot);
117596
+ analysisRoot = fs126.realpathSync(requestedRoot);
117260
117597
  } catch (error) {
117261
117598
  const detail = error instanceof Error ? error.message : String(error);
117262
117599
  const reason = `the explicit analysis root ${requestedRoot} is unavailable or ${detail}`;
@@ -117407,7 +117744,7 @@ async function fetchFreshProjectDiagnostics(cacheManager2, cwd, clients, signal,
117407
117744
  markCold("jscpd", reasonFromAvailabilityVerdict("jscpd", clients.jscpdClient.getAvailabilityVerdict?.()));
117408
117745
  return;
117409
117746
  }
117410
- const isTsProject = fs125.existsSync(path192.join(analysisRoot, "tsconfig.json"));
117747
+ const isTsProject = fs126.existsSync(path192.join(analysisRoot, "tsconfig.json"));
117411
117748
  const scannerKey = isTsProject ? "jscpd-ts" : "jscpd";
117412
117749
  const startMs = Date.now();
117413
117750
  const result = await clients.jscpdClient.scan(analysisRoot, void 0, void 0, isTsProject);
@@ -117820,7 +118157,7 @@ init_source_filter();
117820
118157
  init_tree_sitter_logger();
117821
118158
  init_tree_sitter_query_loader();
117822
118159
  init_tree_sitter_shared();
117823
- import * as fs126 from "node:fs";
118160
+ import * as fs127 from "node:fs";
117824
118161
  import * as path194 from "node:path";
117825
118162
  init_diagnostic_binding();
117826
118163
  init_integration();
@@ -117961,7 +118298,7 @@ async function scanFileMajorRules(cwd, files2, signal) {
117961
118298
  let astGrepLang;
117962
118299
  if (sgModule && canHandle(filePath)) {
117963
118300
  try {
117964
- if (fs126.statSync(filePath).size <= AST_GREP_MAX_FILE_BYTES) {
118301
+ if (fs127.statSync(filePath).size <= AST_GREP_MAX_FILE_BYTES) {
117965
118302
  astGrepLang = getLang(filePath, sgModule);
117966
118303
  }
117967
118304
  } catch {
@@ -117974,7 +118311,7 @@ async function scanFileMajorRules(cwd, files2, signal) {
117974
118311
  let content;
117975
118312
  let contentBytes;
117976
118313
  try {
117977
- const buf = fs126.readFileSync(filePath);
118314
+ const buf = fs127.readFileSync(filePath);
117978
118315
  contentBytes = buf.length;
117979
118316
  content = buf.toString("utf-8");
117980
118317
  } catch {
@@ -118237,7 +118574,7 @@ var turnEndQueue = new TurnEndQueue();
118237
118574
  // dist/clients/module-report.js
118238
118575
  init_file_kinds();
118239
118576
  init_latency_logger();
118240
- import * as fs127 from "node:fs";
118577
+ import * as fs128 from "node:fs";
118241
118578
  import * as path195 from "node:path";
118242
118579
 
118243
118580
  // dist/clients/middle-man-analysis.js
@@ -119383,7 +119720,7 @@ async function moduleReport(file, cwd, options) {
119383
119720
  const normalizedPath = normalizeMapKey(absPath);
119384
119721
  let content;
119385
119722
  try {
119386
- content = fs127.readFileSync(absPath, "utf-8");
119723
+ content = fs128.readFileSync(absPath, "utf-8");
119387
119724
  } catch {
119388
119725
  return unavailableReport(toDisplayPath(absPath, cwd));
119389
119726
  }
@@ -119708,7 +120045,7 @@ async function readSymbol(file, symbolName, cwd, options = {}) {
119708
120045
  };
119709
120046
  let content;
119710
120047
  try {
119711
- content = fs127.readFileSync(absPath, "utf-8");
120048
+ content = fs128.readFileSync(absPath, "utf-8");
119712
120049
  } catch {
119713
120050
  log2(false);
119714
120051
  return { found: false, path: absPath, name: symbolName };
@@ -119884,7 +120221,7 @@ async function readEnclosing(file, line, cwd, options) {
119884
120221
  };
119885
120222
  let content;
119886
120223
  try {
119887
- content = fs127.readFileSync(absPath, "utf-8");
120224
+ content = fs128.readFileSync(absPath, "utf-8");
119888
120225
  } catch {
119889
120226
  log2(false);
119890
120227
  return { found: false, path: absPath, line: targetLine };
@@ -120926,8 +121263,9 @@ init_wait_policy();
120926
121263
  init_tsserver_sync();
120927
121264
  init_lsp_diagnostics();
120928
121265
  init_inferred_project();
121266
+ init_path_utils();
120929
121267
  init_widget_state();
120930
- import * as fs128 from "node:fs";
121268
+ import * as fs129 from "node:fs";
120931
121269
  import * as path199 from "node:path";
120932
121270
  init_warm_attach();
120933
121271
  init_language_registry();
@@ -120981,7 +121319,7 @@ async function collectFiles(dir, extensions, maxFiles, isIgnored = () => false)
120981
121319
  return;
120982
121320
  let entries;
120983
121321
  try {
120984
- entries = await fs128.promises.readdir(current, { withFileTypes: true });
121322
+ entries = await fs129.promises.readdir(current, { withFileTypes: true });
120985
121323
  } catch {
120986
121324
  return;
120987
121325
  }
@@ -121051,7 +121389,7 @@ function createLspDiagnosticsTool(nextWriteIndex, onConfirmedNoBlockers, getServ
121051
121389
  details: { mode: "batch", filesChecked: 0 }
121052
121390
  };
121053
121391
  }
121054
- const absPaths = rawPaths.map((entry) => path199.normalize(path199.isAbsolute(entry) ? entry : path199.resolve(cwd, entry)));
121392
+ const absPaths = rawPaths.map((entry) => normalizeMapKey(path199.resolve(cwd, entry)));
121055
121393
  return runBatchFileDiagnostics(absPaths, severity, lspService, {
121056
121394
  concurrency,
121057
121395
  waitMs,
@@ -121076,10 +121414,10 @@ function createLspDiagnosticsTool(nextWriteIndex, onConfirmedNoBlockers, getServ
121076
121414
  details: {}
121077
121415
  };
121078
121416
  }
121079
- const absPath = path199.isAbsolute(rawPath) ? rawPath : path199.resolve(cwd, rawPath);
121417
+ const absPath = normalizeMapKey(path199.resolve(cwd, rawPath));
121080
121418
  let stat4;
121081
121419
  try {
121082
- stat4 = fs128.statSync(absPath);
121420
+ stat4 = fs129.statSync(absPath);
121083
121421
  } catch {
121084
121422
  return {
121085
121423
  content: [
@@ -121110,7 +121448,7 @@ async function collectDiagnosticsForFile(absPath, lspService, scanRoot, waitMs,
121110
121448
  let content;
121111
121449
  let touched;
121112
121450
  try {
121113
- content = fs128.readFileSync(absPath, "utf-8");
121451
+ content = fs129.readFileSync(absPath, "utf-8");
121114
121452
  if (isWarmAttached()) {
121115
121453
  const attachedBudgetMs = waitMs ?? 15e3;
121116
121454
  const attached = await tryWarmAttachedDiagnostics(absPath, content, attachedBudgetMs, "sweep");
@@ -121265,7 +121603,8 @@ function applyProbeFindingPolicy(file, diagnostics, cwd, content, severity) {
121265
121603
  durationMs: Date.now() - started,
121266
121604
  metadata: {
121267
121605
  suppressed,
121268
- total: inScopeBefore
121606
+ total: inScopeBefore,
121607
+ retainedSuppressed: countRetainedSuppressedRows(file)
121269
121608
  }
121270
121609
  });
121271
121610
  }
@@ -121283,7 +121622,7 @@ function replayContentForStrictMarks(file, cwd) {
121283
121622
  if (!hasStrictDispositionMarks(cwd))
121284
121623
  return void 0;
121285
121624
  try {
121286
- return fs128.readFileSync(file, "utf-8");
121625
+ return fs129.readFileSync(file, "utf-8");
121287
121626
  } catch {
121288
121627
  return void 0;
121289
121628
  }
@@ -121309,7 +121648,7 @@ async function collectFileDiagnosticResult(file, severity, lspService, waitMs, n
121309
121648
  const writeIndex = nextWriteIndex?.();
121310
121649
  let stat4;
121311
121650
  try {
121312
- stat4 = fs128.statSync(file);
121651
+ stat4 = fs129.statSync(file);
121313
121652
  if (!stat4.isFile()) {
121314
121653
  return { file, diagnostics: [], error: `${file}: not a file` };
121315
121654
  }
@@ -121957,7 +122296,7 @@ async function findFullScanBindingMismatches(results) {
121957
122296
  result.boundToCurrentDisk !== true && result.contentHash !== void 0
121958
122297
  ) {
121959
122298
  try {
121960
- const content = await fs129.readFile(result.filePath, "utf-8");
122299
+ const content = await fs130.readFile(result.filePath, "utf-8");
121961
122300
  if (hashDiagnosticContent(content) !== result.contentHash) {
121962
122301
  mismatched.add(result);
121963
122302
  }
@@ -122232,7 +122571,7 @@ function formatProjectDeltaDiagnostic(diagnostic, stale) {
122232
122571
  const where = stale ? STALE_LINE_MARKER : `L${diagnostic.line ?? "?"}`;
122233
122572
  return ` ${marker} ${where} ${rule} ${diagnostic.message}`;
122234
122573
  }
122235
- function appendProjectDiagnosticsDeltaLines(lines, cwd, report, severity, includeFile) {
122574
+ function appendProjectDiagnosticsDeltaLines(groups2, cwd, report, severity, includeFile) {
122236
122575
  const scoped = (report?.diagnostics ?? []).filter((diagnostic) => includeFile(diagnostic.filePath) && matchesSeverity(projectDiagnosticToWidget(diagnostic, diagnostic.filePath), severity));
122237
122576
  const gated2 = gateFindingsByPathFreshness({
122238
122577
  store: "lens-diagnostics-delta-project",
@@ -122252,11 +122591,9 @@ function appendProjectDiagnosticsDeltaLines(lines, cwd, report, severity, includ
122252
122591
  byFile.set(filePath, bucket);
122253
122592
  }
122254
122593
  for (const [filePath, fileDiagnostics] of byFile) {
122255
- const rel = path200.relative(cwd, filePath);
122256
- if (!lines.includes(rel))
122257
- lines.push(rel);
122594
+ const group = getDeltaFileGroup(groups2, cwd, filePath);
122258
122595
  for (const diagnostic of fileDiagnostics) {
122259
- lines.push(formatProjectDeltaDiagnostic(diagnostic, staleSet.has(diagnostic)));
122596
+ group.projectLines.push(formatProjectDeltaDiagnostic(diagnostic, staleSet.has(diagnostic)));
122260
122597
  }
122261
122598
  }
122262
122599
  return diagnostics.length;
@@ -122355,7 +122692,12 @@ function applyDeltaFreshnessGate(files2, cwd, generatedAt) {
122355
122692
  }
122356
122693
  for (const f of gated2.stale) {
122357
122694
  const arr = byFile.get(f.filePath) ?? [];
122358
- arr.push({ ...f.warning, stale: true, line: void 0 });
122695
+ arr.push({
122696
+ ...f.warning,
122697
+ stale: true,
122698
+ line: void 0,
122699
+ staleAsOf: effectiveAt
122700
+ });
122359
122701
  byFile.set(f.filePath, arr);
122360
122702
  }
122361
122703
  return files2.map((file) => ({
@@ -122363,9 +122705,25 @@ function applyDeltaFreshnessGate(files2, cwd, generatedAt) {
122363
122705
  warnings: byFile.get(file.filePath) ?? []
122364
122706
  })).filter((file) => file.warnings.length > 0);
122365
122707
  }
122708
+ function getDeltaFileGroup(groups2, cwd, filePath) {
122709
+ const rel = path200.relative(cwd, filePath);
122710
+ let group = groups2.get(rel);
122711
+ if (!group) {
122712
+ group = {
122713
+ rel,
122714
+ actionableLines: [],
122715
+ qualityLines: [],
122716
+ projectLines: [],
122717
+ incomplete: false
122718
+ };
122719
+ groups2.set(rel, group);
122720
+ }
122721
+ return group;
122722
+ }
122366
122723
  function formatDeltaMode(cacheManager2, cwd, severity, pathsScope) {
122367
122724
  const actionableEntry = cacheManager2.readCache("actionable-warnings", cwd);
122368
122725
  const qualityEntry = cacheManager2.readCache("code-quality-warnings", cwd);
122726
+ const reverifyIncompletePaths = new Set((actionableEntry?.data?.files ?? []).filter((file) => file.reVerifyIncomplete).map((file) => normalizeMapKey(file.filePath)));
122369
122727
  const actionable = actionableEntry?.data;
122370
122728
  const quality = qualityEntry?.data;
122371
122729
  const policyMap = loadProjectRulePolicyMap(cwd);
@@ -122389,29 +122747,54 @@ function formatDeltaMode(cacheManager2, cwd, severity, pathsScope) {
122389
122747
  ...file,
122390
122748
  warnings: matchingWarnings(file.warnings)
122391
122749
  })).filter((file) => file.warnings.length > 0);
122392
- const lines = [];
122750
+ const groups2 = /* @__PURE__ */ new Map();
122393
122751
  if (filteredActionableFiles.length > 0) {
122394
122752
  for (const file of filteredActionableFiles) {
122395
- const rel = path200.relative(cwd, file.filePath);
122396
- lines.push(`${rel}`);
122753
+ const group = getDeltaFileGroup(groups2, cwd, file.filePath);
122397
122754
  for (const w of file.warnings) {
122398
122755
  const where = w.stale ? STALE_LINE_MARKER : `L${w.line ?? "?"}`;
122399
- lines.push(` \u26A0 ${where} ${w.rule ?? w.code ?? w.tool} ${w.message}`);
122756
+ group.actionableLines.push(` \u26A0 ${where} ${w.rule ?? w.code ?? w.tool} ${w.message}`);
122757
+ }
122758
+ const key4 = normalizeMapKey(file.filePath);
122759
+ if (reverifyIncompletePaths.has(key4))
122760
+ group.incomplete = true;
122761
+ if (!group.staleRow) {
122762
+ const staleWarning = file.warnings.find((w) => w.stale);
122763
+ if (staleWarning)
122764
+ group.staleRow = { staleAsOf: staleWarning.staleAsOf };
122400
122765
  }
122401
122766
  }
122402
122767
  }
122403
122768
  if (filteredQualityFiles.length > 0) {
122404
122769
  for (const file of filteredQualityFiles) {
122405
- const rel = path200.relative(cwd, file.filePath);
122406
- if (!lines.includes(rel))
122407
- lines.push(rel);
122770
+ const group = getDeltaFileGroup(groups2, cwd, file.filePath);
122408
122771
  for (const w of file.warnings) {
122409
122772
  const where = w.stale ? STALE_LINE_MARKER : `L${w.line ?? "?"}`;
122410
- lines.push(` \u2139 ${where} ${w.rule ?? w.code ?? w.tool} ${w.message}`);
122773
+ group.qualityLines.push(` \u2139 ${where} ${w.rule ?? w.code ?? w.tool} ${w.message}`);
122774
+ }
122775
+ const key4 = normalizeMapKey(file.filePath);
122776
+ if (reverifyIncompletePaths.has(key4))
122777
+ group.incomplete = true;
122778
+ if (!group.staleRow) {
122779
+ const staleWarning = file.warnings.find((w) => w.stale);
122780
+ if (staleWarning)
122781
+ group.staleRow = { staleAsOf: staleWarning.staleAsOf };
122411
122782
  }
122412
122783
  }
122413
122784
  }
122414
- const projectDeltaCount = appendProjectDiagnosticsDeltaLines(lines, cwd, projectDelta, severity, includeFile);
122785
+ const projectDeltaCount = appendProjectDiagnosticsDeltaLines(groups2, cwd, projectDelta, severity, includeFile);
122786
+ const lines = [];
122787
+ for (const group of groups2.values()) {
122788
+ lines.push(group.rel);
122789
+ lines.push(...group.actionableLines);
122790
+ lines.push(...group.qualityLines);
122791
+ lines.push(...group.projectLines);
122792
+ if (group.staleRow) {
122793
+ lines.push(` (${formatCacheAgeLabel(group.staleRow.staleAsOf)})`);
122794
+ }
122795
+ if (group.incomplete)
122796
+ lines.push(" (re-verify incomplete)");
122797
+ }
122415
122798
  const selectedActionableFiles = filteredActionableFiles;
122416
122799
  const selectedQualityFiles = filteredQualityFiles;
122417
122800
  const aw = selectedActionableFiles.reduce((count, file) => count + file.warnings.length, 0);
@@ -122811,7 +123194,7 @@ async function applyInlineSuppressionsToSummaries(summaries, cwd, policyMap) {
122811
123194
  return summary;
122812
123195
  let content;
122813
123196
  try {
122814
- content = await fs129.readFile(summary.filePath, "utf8");
123197
+ content = await fs130.readFile(summary.filePath, "utf8");
122815
123198
  } catch {
122816
123199
  const policyKept2 = applyRulePolicy(summary.diagnostics, policyMap);
122817
123200
  return policyKept2.length === summary.diagnostics.length ? summary : summarizeDiagnostics(summary.filePath, policyKept2, summary.hasFinalSnapshot);
@@ -123407,7 +123790,7 @@ Summary (${visibleSummaries.length} files diagnosed this session):`,
123407
123790
  }
123408
123791
 
123409
123792
  // dist/tools/lens-diagnostic-mark.js
123410
- import { promises as fs130 } from "node:fs";
123793
+ import { promises as fs131 } from "node:fs";
123411
123794
  import * as path202 from "node:path";
123412
123795
  init_diagnostic_dispositions();
123413
123796
 
@@ -123462,8 +123845,9 @@ function insertSuppressComment(content, filePath, line, rule) {
123462
123845
  }
123463
123846
 
123464
123847
  // dist/tools/lens-diagnostic-mark.js
123465
- init_widget_state();
123848
+ init_path_utils();
123466
123849
  init_tool_config();
123850
+ init_widget_state();
123467
123851
  var DISPOSITIONS = [
123468
123852
  "false-positive",
123469
123853
  "suppress",
@@ -123477,7 +123861,7 @@ function isBlank(text) {
123477
123861
  function widgetCrossCheck(absPath, tool, rule, message, callerLine) {
123478
123862
  let diagnostics;
123479
123863
  try {
123480
- diagnostics = getFileDiagnostics(absPath);
123864
+ diagnostics = getFileDiagnostics(normalizeMapKey(absPath));
123481
123865
  } catch {
123482
123866
  return void 0;
123483
123867
  }
@@ -123612,7 +123996,7 @@ function createLensDiagnosticMarkTool(getCwd, getIdentity) {
123612
123996
  const absPath = path202.isAbsolute(filePathArg) ? filePathArg : path202.resolve(cwd, filePathArg);
123613
123997
  let content;
123614
123998
  try {
123615
- content = await fs130.readFile(absPath, "utf-8");
123999
+ content = await fs131.readFile(absPath, "utf-8");
123616
124000
  } catch (err) {
123617
124001
  return {
123618
124002
  content: [
@@ -123682,7 +124066,7 @@ function createLensDiagnosticMarkTool(getCwd, getIdentity) {
123682
124066
  details: {}
123683
124067
  };
123684
124068
  }
123685
- await fs130.writeFile(absPath, updated, "utf-8");
124069
+ await fs131.writeFile(absPath, updated, "utf-8");
123686
124070
  }
123687
124071
  const anchor = markDisposition(cwd, {
123688
124072
  cwd,
@@ -128166,7 +128550,7 @@ init_env_utils();
128166
128550
  init_package_root();
128167
128551
  init_path_utils();
128168
128552
  init_git_identity();
128169
- import * as fs131 from "node:fs";
128553
+ import * as fs132 from "node:fs";
128170
128554
  import * as path208 from "node:path";
128171
128555
  import { fileURLToPath as fileURLToPath11 } from "node:url";
128172
128556
  function resolveOwnedCommit(root) {
@@ -128180,7 +128564,7 @@ function resolveOwnedCommit(root) {
128180
128564
  }
128181
128565
  function readPackageVersion(root) {
128182
128566
  try {
128183
- const raw = fs131.readFileSync(path208.join(root, "package.json"), "utf-8");
128567
+ const raw = fs132.readFileSync(path208.join(root, "package.json"), "utf-8");
128184
128568
  const pkg = JSON.parse(raw);
128185
128569
  return pkg.version ?? "unknown";
128186
128570
  } catch {
@@ -128189,7 +128573,7 @@ function readPackageVersion(root) {
128189
128573
  }
128190
128574
  function statEntryMtime(entryImportMetaUrl) {
128191
128575
  try {
128192
- return fs131.statSync(fileURLToPath11(entryImportMetaUrl)).mtime.toISOString();
128576
+ return fs132.statSync(fileURLToPath11(entryImportMetaUrl)).mtime.toISOString();
128193
128577
  } catch {
128194
128578
  return "unknown";
128195
128579
  }
@@ -129855,10 +130239,11 @@ ${degradationLines.join("\n")}` : "";
129855
130239
  });
129856
130240
  }
129857
130241
  registerCascadeTierReconcileTask(() => getLSPService(), {
129858
- onResolvedFound: ({ filePath, diagnostics }) => {
130242
+ onResolvedFound: ({ filePath, diagnostics, publishedAt }) => {
129859
130243
  const run = buildResolvedFoundCascadeRun(runtime.projectRoot, {
129860
130244
  filePath,
129861
- diagnostics
130245
+ diagnostics,
130246
+ publishedAt
129862
130247
  });
129863
130248
  if (run)
129864
130249
  runtime.appendCascadeRun(run);