pi-lens 4.4.2 → 4.4.4

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 (143) hide show
  1. package/CHANGELOG.md +306 -0
  2. package/README.md +13 -1
  3. package/dist/{chunk-VITUCP5A.js → chunk-24AKHFGP.js} +9 -7
  4. package/dist/{chunk-JSQFTSC6.js → chunk-2FN63LPV.js} +11 -11
  5. package/dist/{chunk-CF24OIYI.js → chunk-2KXTHF3M.js} +20 -20
  6. package/dist/{chunk-ESBQHFMJ.js → chunk-3DA2JDOL.js} +543 -349
  7. package/dist/{chunk-RWWYJ623.js → chunk-3KAZU3VR.js} +1 -1
  8. package/dist/chunk-3PTHXJXR.js +28 -0
  9. package/dist/chunk-3QED2P4V.js +31 -0
  10. package/dist/{chunk-N2H3ZPZY.js → chunk-3RKP3FMS.js} +1 -0
  11. package/dist/{chunk-XVF2VVXP.js → chunk-5HSRT26B.js} +2 -2
  12. package/dist/{chunk-XKIZ2HNN.js → chunk-5IUSNGOC.js} +18 -16
  13. package/dist/{chunk-EJIUXPBX.js → chunk-5OFV52AM.js} +1 -1
  14. package/dist/{chunk-AW7R2GWG.js → chunk-5VSF4UUQ.js} +12 -7
  15. package/dist/{chunk-NW7BOILN.js → chunk-5WRPDP2A.js} +18 -18
  16. package/dist/{chunk-7ULNCDCO.js → chunk-6DLWKFEX.js} +35 -34
  17. package/dist/{chunk-WOA4LN2R.js → chunk-7D6L4MTX.js} +2 -2
  18. package/dist/{chunk-GAEGEPOC.js → chunk-7HXE67BN.js} +2 -2
  19. package/dist/chunk-AGSS7277.js +84 -0
  20. package/dist/{chunk-R3PCY3YS.js → chunk-AMERCH6V.js} +13 -13
  21. package/dist/{chunk-WU567RSC.js → chunk-ATKJFDQN.js} +14 -10
  22. package/dist/{chunk-VSSBEUFT.js → chunk-AWSCPCN2.js} +16 -16
  23. package/dist/{chunk-VCHXCQFG.js → chunk-BWD5EPKO.js} +3 -3
  24. package/dist/{chunk-ODBNXWT4.js → chunk-CT4MN5IU.js} +1271 -262
  25. package/dist/{chunk-34WSVGEM.js → chunk-CUHSHSB6.js} +83 -5
  26. package/dist/{chunk-CHOW74WE.js → chunk-CYRKQRPM.js} +7 -10
  27. package/dist/{chunk-Q7DUK4LX.js → chunk-DEL2U6CM.js} +5 -5
  28. package/dist/{chunk-6C7JWPCP.js → chunk-DGUKIYSI.js} +26 -23
  29. package/dist/{chunk-PHDMFAKM.js → chunk-DUO2TU77.js} +2 -2
  30. package/dist/{chunk-MRXKYPCS.js → chunk-EBC36LNN.js} +2 -2
  31. package/dist/chunk-ED36RF6Y.js +17 -0
  32. package/dist/{chunk-76MWC2K6.js → chunk-EUXZEFTV.js} +90 -97
  33. package/dist/{chunk-QOZXJ4NA.js → chunk-FM4RDMKT.js} +11 -8
  34. package/dist/{chunk-66VMUL7Q.js → chunk-GAQ7OQG6.js} +31 -28
  35. package/dist/{chunk-Q6ZWTCLY.js → chunk-GL5LUMZE.js} +23 -24
  36. package/dist/{chunk-BKE7E7TC.js → chunk-GMX5OS7B.js} +9 -9
  37. package/dist/{chunk-ZHFZWTZV.js → chunk-GSLNCYGG.js} +11 -11
  38. package/dist/{chunk-ARJ6PLFH.js → chunk-GSM5UU7A.js} +26 -34
  39. package/dist/{chunk-724UL2TJ.js → chunk-GXFV6MYU.js} +3 -3
  40. package/dist/chunk-HAD7QRQI.js +30 -0
  41. package/dist/{chunk-QOBUHWHX.js → chunk-HRNHZA3N.js} +6 -6
  42. package/dist/{chunk-VWCXW2E7.js → chunk-HWCCCQNX.js} +1 -1
  43. package/dist/{chunk-7DYBLXYA.js → chunk-HYGZEMX6.js} +3 -3
  44. package/dist/chunk-IJYGIDF4.js +186 -0
  45. package/dist/{chunk-4W4VLAPJ.js → chunk-IQWN5JCP.js} +11 -11
  46. package/dist/{chunk-GZYBXISR.js → chunk-IUE7SSKV.js} +9 -9
  47. package/dist/{chunk-IBGIOBEE.js → chunk-IWDLU6K3.js} +68 -5
  48. package/dist/{chunk-XCJN5IJU.js → chunk-IYNJ6YPZ.js} +4 -4
  49. package/dist/{chunk-JAU72XNY.js → chunk-J64T55EI.js} +3 -3
  50. package/dist/{chunk-2BBLFX75.js → chunk-J6MPGQTS.js} +5 -5
  51. package/dist/chunk-JORED7EX.js +44 -0
  52. package/dist/{chunk-6SDEP3AJ.js → chunk-JUKMK5Y4.js} +8 -8
  53. package/dist/{chunk-IBRALRBR.js → chunk-JW3YWBVB.js} +15 -14
  54. package/dist/{chunk-L3BLXZG7.js → chunk-KAPJINKN.js} +30 -69
  55. package/dist/{chunk-2B73ZPOK.js → chunk-KN35K52N.js} +3 -3
  56. package/dist/{chunk-C34ZXZ7O.js → chunk-KUA4OJ5Y.js} +24 -11
  57. package/dist/chunk-KYUB6QDC.js +72 -0
  58. package/dist/{chunk-67WMNWEE.js → chunk-LLJNQKQW.js} +12 -10
  59. package/dist/chunk-LZSEQW6A.js +57 -0
  60. package/dist/{chunk-ZANLILYD.js → chunk-M2IFVFWU.js} +2 -2
  61. package/dist/chunk-M2UD3LPU.js +88 -0
  62. package/dist/{chunk-WOKKRCJ5.js → chunk-MBKQHGA7.js} +15 -14
  63. package/dist/{chunk-P7266HLM.js → chunk-MPNJLASA.js} +2 -2
  64. package/dist/{chunk-6GFRNVYR.js → chunk-MZGQNL3Y.js} +10 -10
  65. package/dist/{chunk-QZVW3AAK.js → chunk-N5FLEO7W.js} +33 -31
  66. package/dist/{chunk-3JNKMP3B.js → chunk-O4NWQT6I.js} +4 -4
  67. package/dist/{chunk-PFNSLWU5.js → chunk-OHKJ7PQF.js} +33 -31
  68. package/dist/{chunk-VRZP76YM.js → chunk-P63GXC37.js} +4 -1
  69. package/dist/{chunk-XTMJ7XDT.js → chunk-POMLXPMY.js} +1 -1
  70. package/dist/{chunk-QQNQ7SJW.js → chunk-QBFBRV2B.js} +10 -4
  71. package/dist/{chunk-3UUPUE3O.js → chunk-R3T7CLQ2.js} +84 -60
  72. package/dist/{chunk-23WOO7TX.js → chunk-R7YOCSNV.js} +10 -10
  73. package/dist/{chunk-2CD5RS6M.js → chunk-RKBXZN3A.js} +23 -2
  74. package/dist/{chunk-OG5UUPN7.js → chunk-RKKOYPRA.js} +14 -14
  75. package/dist/{chunk-52FUZI6K.js → chunk-RMLC67LF.js} +1 -1
  76. package/dist/{chunk-XMSXXAHW.js → chunk-ROYSX2S5.js} +2 -2
  77. package/dist/{chunk-PV6LR5P5.js → chunk-RVK72CZX.js} +9605 -14776
  78. package/dist/{chunk-FLKWCIZH.js → chunk-S2HEXIWI.js} +5 -5
  79. package/dist/{chunk-UZ3TUTRV.js → chunk-S67KYG37.js} +1 -1
  80. package/dist/{chunk-JWA5I3SR.js → chunk-SD4I7ZO4.js} +16 -18
  81. package/dist/{chunk-Z527N6YQ.js → chunk-SKAMFC7A.js} +12 -12
  82. package/dist/{chunk-MQLUAEOC.js → chunk-SRYEJNES.js} +2 -2
  83. package/dist/{chunk-BRGDSBJE.js → chunk-SYE6T2EB.js} +12 -12
  84. package/dist/{chunk-GXW6K4YJ.js → chunk-T57LYLZT.js} +1 -1
  85. package/dist/{chunk-ZXYDQ3TZ.js → chunk-T7FEBKXN.js} +48 -15
  86. package/dist/{chunk-IXXDFY73.js → chunk-TIQ2PKDW.js} +3 -3
  87. package/dist/{chunk-QILIYSJF.js → chunk-TLX72JTG.js} +1 -1
  88. package/dist/{chunk-TYM6OUIT.js → chunk-TOY6QKOH.js} +12 -12
  89. package/dist/{chunk-IZSHAIJ7.js → chunk-TRETHTQO.js} +12 -12
  90. package/dist/{chunk-3VPVG5SU.js → chunk-U44DJXQP.js} +10 -6
  91. package/dist/{chunk-PNQDPDRC.js → chunk-UA7ZXYDR.js} +1 -1
  92. package/dist/{chunk-OXXKQCSV.js → chunk-UBOQLVT2.js} +4 -4
  93. package/dist/{chunk-QGQ5CCCG.js → chunk-UMS2MFPZ.js} +2 -2
  94. package/dist/{chunk-QHJ6DQ2R.js → chunk-US7ECLD7.js} +55 -13
  95. package/dist/{chunk-EETY4LF7.js → chunk-UZ2XCLYS.js} +8 -8
  96. package/dist/chunk-V35JG4JB.js +2667 -0
  97. package/dist/{chunk-N3X6Q4YT.js → chunk-VOOAPROD.js} +3 -3
  98. package/dist/{chunk-SAEFPSB6.js → chunk-W66O5E25.js} +21 -11
  99. package/dist/{chunk-KBG47GHP.js → chunk-WMYE35PX.js} +15 -11
  100. package/dist/{chunk-5NGR67U5.js → chunk-XBJVAEEO.js} +32 -5
  101. package/dist/chunk-XHJBS436.js +4217 -0
  102. package/dist/{chunk-GBPP5P7X.js → chunk-XTBDCR27.js} +4 -4
  103. package/dist/{chunk-4UNXBNE2.js → chunk-XTFDJI3T.js} +11 -11
  104. package/dist/{chunk-UXGETRP4.js → chunk-XYURS6EN.js} +13 -12
  105. package/dist/{chunk-RPLINGVB.js → chunk-Y6GZHWSJ.js} +6 -6
  106. package/dist/{chunk-2ZGIZPDG.js → chunk-YCWYO6TQ.js} +41 -38
  107. package/dist/{chunk-66SXUJCA.js → chunk-YH5MVY2U.js} +10 -8
  108. package/dist/{chunk-AOHZU2BM.js → chunk-YNE3RP52.js} +1 -1
  109. package/dist/{chunk-GOYRYZ2R.js → chunk-ZHH37KZL.js} +72 -20
  110. package/dist/{chunk-ZGDGW2V3.js → chunk-ZIVJBOO3.js} +2 -2
  111. package/dist/{chunk-GHB6AYCP.js → chunk-ZVUL7QYY.js} +29 -28
  112. package/dist/index.js +11559 -6213
  113. package/dist/mcp/analyze-cli.js +10 -9
  114. package/dist/mcp/cli.js +41 -38
  115. package/dist/mcp/server.js +1142 -495
  116. package/dist/mcp/worker.js +61 -57
  117. package/dist/probes/installer.js +19 -19
  118. package/dist/workers/project-snapshot-persist-worker.js +28 -9
  119. package/dist/workers/review-graph-persist-worker.js +1 -1
  120. package/docs/configuration.md +11 -0
  121. package/docs/engineering-principles.md +32 -0
  122. package/docs/lsp-capability-matrix.md +1 -1
  123. package/docs/lsp-idle-eviction.md +15 -26
  124. package/docs/pi-lens-fixer.md +117 -13
  125. package/docs/pi-lens-merge-policy.md +109 -11
  126. package/docs/pi-lens-monitor.md +8 -2
  127. package/docs/pi-lens-retro.md +4 -1
  128. package/docs/pi-lens-reviewer.md +114 -3
  129. package/docs/pi-lens-subagent.md +27 -14
  130. package/docs/release-qa-baseline.md +3 -1
  131. package/docs/servercapabilities.md +9 -19
  132. package/package.json +7 -5
  133. package/scripts/download-grammars.js +40 -7
  134. package/scripts/lib/latency-log-phases.mjs +95 -0
  135. package/scripts/rpc-load-check.mjs +570 -81
  136. package/dist/chunk-3WA4HP4I.js +0 -55
  137. package/dist/chunk-466Z26MG.js +0 -30
  138. package/dist/chunk-5JM2524W.js +0 -84
  139. package/dist/chunk-CO2G5PHH.js +0 -84
  140. package/dist/chunk-LOP6GRPH.js +0 -31
  141. package/dist/chunk-UOPVJQ4N.js +0 -103
  142. package/dist/chunk-XPZPXCBV.js +0 -28
  143. /package/dist/{chunk-FC4RP4I2.js → chunk-6HWVI6MI.js} +0 -0
@@ -1,5 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire as __pilensCreateRequire } from "node:module"; const require = __pilensCreateRequire(import.meta.url);
3
+ import {
4
+ GitleaksClient,
5
+ classifyAndFilterFindings
6
+ } from "../chunk-24AKHFGP.js";
3
7
  import {
4
8
  DEAD_CODE_DELTA_MAX_SHOWN,
5
9
  deadCodeIssueCount,
@@ -7,35 +11,31 @@ import {
7
11
  deadCodeIssues,
8
12
  formatDeadCodeDelta,
9
13
  stableFindingKey
10
- } from "../chunk-OG5UUPN7.js";
14
+ } from "../chunk-RKKOYPRA.js";
11
15
  import {
12
16
  clearFormatterCache
13
- } from "../chunk-ARJ6PLFH.js";
17
+ } from "../chunk-GSM5UU7A.js";
14
18
  import {
15
19
  findNearestProjectRoot,
16
20
  isStartupScanVerdictFresh,
17
21
  resolveStartupScanContext
18
- } from "../chunk-3VPVG5SU.js";
22
+ } from "../chunk-U44DJXQP.js";
19
23
  import {
20
24
  loadCallGraph
21
- } from "../chunk-WOA4LN2R.js";
25
+ } from "../chunk-7D6L4MTX.js";
26
+ import {
27
+ isHardFailureSummary
28
+ } from "../chunk-7U2IDNXN.js";
22
29
  import {
23
30
  RUNNERS,
24
31
  formatRunDurationMs,
25
32
  isExcludedTestTarget,
26
33
  isRunnerErrorResult
27
- } from "../chunk-JWA5I3SR.js";
34
+ } from "../chunk-SD4I7ZO4.js";
28
35
  import {
29
36
  GovulncheckClient
30
- } from "../chunk-GZYBXISR.js";
31
- import {
32
- GitleaksClient,
33
- classifyAndFilterFindings
34
- } from "../chunk-VITUCP5A.js";
35
- import "../chunk-ZGDGW2V3.js";
36
- import {
37
- isHardFailureSummary
38
- } from "../chunk-7U2IDNXN.js";
37
+ } from "../chunk-IUE7SSKV.js";
38
+ import "../chunk-ZIVJBOO3.js";
39
39
  import {
40
40
  resetManagedToolRefreshSession
41
41
  } from "../chunk-UXVNL3D7.js";
@@ -53,7 +53,7 @@ import {
53
53
  peekTurnEndFindings,
54
54
  snapshotAdvisoryProvenance,
55
55
  validateAdvisoryProvenance
56
- } from "../chunk-KBG47GHP.js";
56
+ } from "../chunk-WMYE35PX.js";
57
57
  import {
58
58
  CacheManager,
59
59
  DELTA_UNUSED_PROMOTION_NOTE,
@@ -64,21 +64,27 @@ import {
64
64
  applyFindingPolicy,
65
65
  applyLspFindingPolicy,
66
66
  applyPushedFindingPolicy,
67
+ applyPushedFindingPolicyByFile,
67
68
  applyRulePolicy,
68
69
  canHandle,
69
70
  cascadeCarrySuffix,
70
71
  cascadeSettleWaitMs,
72
+ cascadeStillPresentSince,
73
+ composeCascadeSection,
71
74
  convertLspDiagnostics,
72
75
  createDispatchContext,
73
76
  createMcpHost,
74
77
  demoteInferredProjectDiagnostics,
75
78
  demoteInferredProjectSweepResults,
79
+ dispositionSuppressedLine,
76
80
  drainPendingRunnerFindings,
77
81
  dropStaleRunnerFindings,
78
82
  evaluateAstGrepRules,
79
83
  evaluateRules,
80
84
  filterFindingsByDisposition,
85
+ forgetCascadePendingNeighbor,
81
86
  formatCacheAgeLabel,
87
+ formatCascadeNeighborDiagnostics,
82
88
  formatDiagnostics,
83
89
  getAvailableRunners,
84
90
  getDiagnosticTracker,
@@ -86,65 +92,85 @@ import {
86
92
  getLatencyReports,
87
93
  getToolPlan,
88
94
  inlineBlockerIdentities,
95
+ laneCopyStandsFor,
96
+ listCascadePendingNeighbors,
89
97
  loadProjectRulePolicyMap,
90
98
  loadSg,
91
99
  logCascade,
100
+ markCascadeUnansweredPrimariesDelivered,
92
101
  markUnreconciledFindings,
93
102
  normalizeRuleId,
103
+ owedByContentOnly,
104
+ peekOutstandingCascadeTouches,
94
105
  pendingRunnerFindingsSize,
106
+ readFileContent,
107
+ recordDeliveredCascadeNeighbor,
108
+ renderCascadeResult,
95
109
  requeueRunnerFindings,
96
110
  resetCascadeTierSessionState,
97
111
  resetDispatchBaselines,
98
112
  resetObservedRunnerLatency,
99
113
  resetPendingRunnerFindings,
100
114
  resetPsScriptAnalyzerAvailability,
101
- runProviders
102
- } from "../chunk-ODBNXWT4.js";
115
+ runProviders,
116
+ stampLineHashes
117
+ } from "../chunk-CT4MN5IU.js";
103
118
  import {
104
119
  resetLazyInstallAttempts
105
- } from "../chunk-OXXKQCSV.js";
120
+ } from "../chunk-UBOQLVT2.js";
106
121
  import {
107
122
  isTestRoleCollateral
108
- } from "../chunk-EJIUXPBX.js";
109
- import "../chunk-FC4RP4I2.js";
123
+ } from "../chunk-5OFV52AM.js";
124
+ import {
125
+ findingsResult,
126
+ hasUsableResult,
127
+ reachedNoVerdict
128
+ } from "../chunk-RKBXZN3A.js";
129
+ import "../chunk-6HWVI6MI.js";
130
+ import "../chunk-S67KYG37.js";
131
+ import "../chunk-KN35K52N.js";
132
+ import {
133
+ resetGoAvailability
134
+ } from "../chunk-ROYSX2S5.js";
110
135
  import {
111
136
  TrivyClient
112
- } from "../chunk-66SXUJCA.js";
113
- import "../chunk-VCHXCQFG.js";
114
- import "../chunk-IXXDFY73.js";
137
+ } from "../chunk-YH5MVY2U.js";
115
138
  import {
116
- resetRustAvailability
117
- } from "../chunk-MRXKYPCS.js";
139
+ scannerReportParentDir
140
+ } from "../chunk-ED36RF6Y.js";
141
+ import "../chunk-BWD5EPKO.js";
142
+ import "../chunk-TIQ2PKDW.js";
118
143
  import {
119
- findingsResult,
120
- hasUsableResult
121
- } from "../chunk-2CD5RS6M.js";
122
- import "../chunk-UZ3TUTRV.js";
123
- import "../chunk-EETY4LF7.js";
144
+ resetRustAvailability
145
+ } from "../chunk-EBC36LNN.js";
146
+ import "../chunk-HYGZEMX6.js";
147
+ import "../chunk-UZ2XCLYS.js";
124
148
  import {
125
149
  getSgCommand,
126
150
  resetDispatchAvailabilityState,
127
151
  resolveManagedToolClient
128
- } from "../chunk-GOYRYZ2R.js";
129
- import "../chunk-RPLINGVB.js";
130
- import "../chunk-XCJN5IJU.js";
131
- import "../chunk-76MWC2K6.js";
132
- import "../chunk-GXW6K4YJ.js";
133
- import "../chunk-2B73ZPOK.js";
152
+ } from "../chunk-ZHH37KZL.js";
153
+ import "../chunk-Y6GZHWSJ.js";
154
+ import "../chunk-IYNJ6YPZ.js";
155
+ import "../chunk-EUXZEFTV.js";
156
+ import "../chunk-T57LYLZT.js";
134
157
  import {
135
- resetGoAvailability
136
- } from "../chunk-XMSXXAHW.js";
137
- import "../chunk-7DYBLXYA.js";
158
+ SYMBOL_KIND_NAMES,
159
+ captureReviewGraphStructuralIr,
160
+ formatReviewGraphRevisionDriftNote,
161
+ publishReviewGraphFileIr,
162
+ reviewGraphIrContentHash
163
+ } from "../chunk-XHJBS436.js";
164
+ import {
165
+ buildSymbolId
166
+ } from "../chunk-3MED75HT.js";
138
167
  import {
139
- DEPENDENCY_DRIFT_MAX_DELIVERIES,
140
168
  MAX_LATE_AUX_REARMS,
141
169
  PAST_EOF_STALE_MARKER,
142
- RUNTIME_CONFIG,
170
+ PRIMARY_UNAVAILABLE_CAUSE,
143
171
  ReadGuard,
144
172
  STALE_LINE_MARKER,
145
- SYMBOL_KIND_NAMES,
146
173
  Text,
147
- TreeSitterSymbolExtractor,
148
174
  WriteOrderingGuard,
149
175
  anchorsForDiagnostic,
150
176
  applyAuxiliarySuppressions,
@@ -155,12 +181,12 @@ import {
155
181
  armDeferredLspWork,
156
182
  attemptTsserverSyncDiagnostics,
157
183
  beginScope,
158
- buildScopeKey,
184
+ buildProbeScopeKey,
159
185
  canRearmPendingAuxiliary,
160
- canWriteAnalysisRoot,
161
- captureReviewGraphStructuralIr,
162
186
  classifyDiagnosticTier,
163
187
  clearAllSessions,
188
+ combinePrimaryUnavailableCauses,
189
+ configDisabledAuxiliaryLspServerIds,
164
190
  countRetainedSuppressedRows,
165
191
  createFileTime,
166
192
  createLazyImport,
@@ -170,20 +196,21 @@ import {
170
196
  drainPendingAuxCapEvictedCount,
171
197
  drainPendingAuxiliaryCoverage,
172
198
  drainRenderedDependencyDriftFilePaths,
173
- exceedsLspSyncLimits,
174
199
  findAuxiliaryProfileForSource,
175
- formatReviewGraphRevisionDriftNote,
176
200
  getActiveSessionId,
177
201
  getDisposition,
178
202
  getFileDiagnosticSummaries,
179
203
  getLSPService,
180
204
  getLspBudgetIdleTimeoutMs,
181
205
  getMutationBridge,
206
+ getSuppressedDiagnostics,
182
207
  getWidgetBlockingFilesForSweep,
208
+ goPackageFilesToAnnounce,
183
209
  groupFilesByPrimaryServer,
184
210
  hasAnyDispositionMarks,
185
211
  hasStrictDispositionMarks,
186
212
  hashDiagnosticContent,
213
+ importTargetsToAnnounce,
187
214
  incrementWidgetDependencyDriftDelivery,
188
215
  isAuxBacklogPublished,
189
216
  isBlocking,
@@ -195,7 +222,7 @@ import {
195
222
  newLspMutationCorrelationId,
196
223
  nextOrderTurn,
197
224
  pendingAuxiliaryCoverageSize,
198
- publishReviewGraphFileIr,
225
+ primaryUnavailableCause,
199
226
  rearmPendingAuxiliaryCoverage,
200
227
  reconcileCorrelatedScanDiagnostics,
201
228
  reconcileScanDiagnostics,
@@ -212,52 +239,42 @@ import {
212
239
  resetOpaqueMutationState,
213
240
  resetPendingAuxiliaryCoverage,
214
241
  resetWorkspaceDiagnosticsCacheSession,
215
- resolveImportToFiles,
216
242
  resolveLiveBusEmitter,
217
243
  resyncDocumentOnPastEof,
218
244
  retagAuxiliaryDiagnostics,
219
245
  retireScope,
220
246
  retireWidgetDependencyDriftBlockers,
221
- reviewGraphIrContentHash,
222
247
  runPerServerGroups,
223
248
  scopeCell,
224
249
  setSessionLanguages,
225
250
  shouldShortenLspIdleTimeout,
226
251
  summarizeWorkspaceEdit,
227
- sweepInlineBlockerFreshness,
228
- symbolExtractionGrammar,
229
252
  touchCompletedConfirmationPolicy,
230
253
  touchCoverageGap,
231
254
  tryWarmAttachedDiagnostics,
232
255
  widgetDiagnosticUri,
233
256
  workspaceEditTextPaths,
234
257
  writeOrderToken
235
- } from "../chunk-PV6LR5P5.js";
236
- import {
237
- buildSymbolId
238
- } from "../chunk-3MED75HT.js";
239
- import {
240
- emitBounded,
241
- resetBoundedTelemetry
242
- } from "../chunk-CO2G5PHH.js";
258
+ } from "../chunk-RVK72CZX.js";
243
259
  import {
260
+ ANY_POSITION,
261
+ foldedFindingIdentity,
244
262
  hashText,
245
263
  normalizeMessage,
246
264
  primePersistedMutationAttribution,
247
265
  resetMutationAttribution,
266
+ retainRecordedFindings,
248
267
  stableFindingId
249
- } from "../chunk-IBGIOBEE.js";
250
- import {
251
- resolveGitCheckout,
252
- resolveLinkedWorktreeOwner
253
- } from "../chunk-PNQDPDRC.js";
268
+ } from "../chunk-IWDLU6K3.js";
254
269
  import {
255
270
  mapWithConcurrency
256
271
  } from "../chunk-XIMYZ3JA.js";
257
272
  import {
273
+ canWriteAnalysisRoot,
258
274
  classifyCascadeWaitTier,
259
275
  customServerSpecsOf,
260
276
  describeRootFallback,
277
+ disabledPrimaries,
261
278
  explainServersForFile,
262
279
  getAstGrepRuleFingerprint,
263
280
  getServersForFileWithConfig,
@@ -272,11 +289,17 @@ import {
272
289
  resetLSPCaseSensitivityState,
273
290
  resolveLspCwdForFile,
274
291
  shouldInitializeSessionRoot
275
- } from "../chunk-ESBQHFMJ.js";
292
+ } from "../chunk-3DA2JDOL.js";
293
+ import "../chunk-HWCCCQNX.js";
276
294
  import {
277
- hasAmbientPythonEnvironment
278
- } from "../chunk-34WSVGEM.js";
279
- import "../chunk-VWCXW2E7.js";
295
+ emitBounded,
296
+ hasAmbientPythonEnvironment,
297
+ resetBoundedTelemetry
298
+ } from "../chunk-CUHSHSB6.js";
299
+ import {
300
+ resolveGitCheckout,
301
+ resolveLinkedWorktreeOwner
302
+ } from "../chunk-UA7ZXYDR.js";
280
303
  import {
281
304
  PROJECT_SNAPSHOT_VERSION,
282
305
  getProjectSnapshotLegacyPath,
@@ -291,8 +314,25 @@ import {
291
314
  readJsonCache,
292
315
  readProjectSnapshotMeta,
293
316
  saveRuntimeProjectSnapshot
294
- } from "../chunk-ZXYDQ3TZ.js";
317
+ } from "../chunk-T7FEBKXN.js";
318
+ import {
319
+ SWEEP_IDLE_SAFETY_MARGIN_MS,
320
+ getFullScanWallClockMs,
321
+ isWorkspaceSweepActive,
322
+ runWhenWorkspaceSweepIdle
323
+ } from "../chunk-SRYEJNES.js";
295
324
  import "../chunk-5W3R6LFT.js";
325
+ import {
326
+ DEPENDENCY_DRIFT_MAX_DELIVERIES,
327
+ RUNTIME_CONFIG,
328
+ TreeSitterSymbolExtractor,
329
+ exceedsLspSyncLimits,
330
+ resolveImportToFiles,
331
+ sweepInlineBlockerFreshness,
332
+ symbolExtractionGrammar
333
+ } from "../chunk-V35JG4JB.js";
334
+ import "../chunk-XTBDCR27.js";
335
+ import "../chunk-MPNJLASA.js";
296
336
  import {
297
337
  EXT_TO_LANG,
298
338
  classifyTreeSitterWasmError,
@@ -305,7 +345,7 @@ import {
305
345
  queryLoader,
306
346
  reportBundledResourceDirHealth,
307
347
  resolveTreeSitterLanguage
308
- } from "../chunk-QHJ6DQ2R.js";
348
+ } from "../chunk-US7ECLD7.js";
309
349
  import "../chunk-Z54ZSOBZ.js";
310
350
  import {
311
351
  SCAN_LANGUAGE_PRIORITY,
@@ -313,35 +353,27 @@ import {
313
353
  extensionsForLanguageToken,
314
354
  getUserRuleRoot,
315
355
  resolveLanguage
316
- } from "../chunk-724UL2TJ.js";
356
+ } from "../chunk-GXFV6MYU.js";
317
357
  import {
318
358
  dump,
319
359
  resolvePackagePath
320
- } from "../chunk-QOZXJ4NA.js";
321
- import "../chunk-GBPP5P7X.js";
322
- import "../chunk-P7266HLM.js";
323
- import {
324
- SWEEP_IDLE_SAFETY_MARGIN_MS,
325
- getFullScanWallClockMs,
326
- isWorkspaceSweepActive,
327
- runWhenWorkspaceSweepIdle
328
- } from "../chunk-MQLUAEOC.js";
360
+ } from "../chunk-FM4RDMKT.js";
329
361
  import {
330
362
  HOOK_WALL_BUDGET_MS,
331
363
  bounded,
332
364
  combineAbortSignals,
333
365
  withDeadline
334
- } from "../chunk-52FUZI6K.js";
366
+ } from "../chunk-RMLC67LF.js";
335
367
  import {
336
368
  centralityFromReverseDeps,
337
369
  getWordIndexBuildStatus,
338
370
  logWordIndex,
339
371
  searchWordIndex,
340
372
  triggerBackgroundWordIndexBuild
341
- } from "../chunk-SAEFPSB6.js";
373
+ } from "../chunk-W66O5E25.js";
342
374
  import {
343
375
  getProjectDiagnosticsScannerMaxFiles
344
- } from "../chunk-GAEGEPOC.js";
376
+ } from "../chunk-7HXE67BN.js";
345
377
  import {
346
378
  PathKeyedMap
347
379
  } from "../chunk-Z3CCVDUB.js";
@@ -349,32 +381,37 @@ import {
349
381
  WARM_TURN_END_SCHEMA_VERSION,
350
382
  createWarmIpcLineReader,
351
383
  createWarmIpcRequestQueue,
352
- freshnessFromMtime,
353
384
  ipcPathForCwd,
354
385
  readTurnEndStatus,
386
+ unlinkStaleUnixSocket
387
+ } from "../chunk-KAPJINKN.js";
388
+ import {
389
+ freshnessFromMtime,
390
+ hashContentBytes,
355
391
  resolveRunnerPath,
356
392
  toRunnerDisplayPath
357
- } from "../chunk-L3BLXZG7.js";
393
+ } from "../chunk-AGSS7277.js";
358
394
  import "../chunk-2BEMIWEW.js";
395
+ import "../chunk-IJYGIDF4.js";
359
396
  import {
360
397
  commitDurableStore,
361
398
  resetResolvedPathCache,
362
399
  resetZizmorTokenAvailability
363
- } from "../chunk-66VMUL7Q.js";
400
+ } from "../chunk-GAQ7OQG6.js";
364
401
  import {
365
402
  resolveToolCwd
366
- } from "../chunk-Q7DUK4LX.js";
403
+ } from "../chunk-DEL2U6CM.js";
367
404
  import {
368
405
  _resetPackageManagerCache,
369
406
  findGlobalBinary,
370
407
  pmBinary,
371
408
  resolveNodePackageManager,
372
409
  runScriptArgs
373
- } from "../chunk-AW7R2GWG.js";
410
+ } from "../chunk-5VSF4UUQ.js";
374
411
  import {
375
412
  getIsolatedNpxSpawnOptions,
376
413
  probeToolAsync
377
- } from "../chunk-RWWYJ623.js";
414
+ } from "../chunk-3KAZU3VR.js";
378
415
  import {
379
416
  classifyProbeFailure,
380
417
  createAvailabilityLatch,
@@ -382,20 +419,20 @@ import {
382
419
  logAvailabilityDecision,
383
420
  resetInstallRetryLatches,
384
421
  startHostStallSampler
385
- } from "../chunk-VRZP76YM.js";
422
+ } from "../chunk-P63GXC37.js";
386
423
  import {
387
424
  canRunStartupHeavyScans,
388
425
  detectProjectLanguageProfile,
389
426
  getDefaultStartupTools
390
- } from "../chunk-QOBUHWHX.js";
391
- import "../chunk-QILIYSJF.js";
427
+ } from "../chunk-HRNHZA3N.js";
428
+ import "../chunk-TLX72JTG.js";
392
429
  import {
393
430
  collectSourceFilesWithBudgetAsync,
394
431
  getSlowFsVerdict,
395
432
  isSlowFs,
396
433
  slowFsDegradationNotice
397
- } from "../chunk-6C7JWPCP.js";
398
- import "../chunk-5NGR67U5.js";
434
+ } from "../chunk-DGUKIYSI.js";
435
+ import "../chunk-XBJVAEEO.js";
399
436
  import {
400
437
  displayProjectDataPath,
401
438
  drainProjectDataDirMigrations,
@@ -422,7 +459,7 @@ import {
422
459
  sweepDeadEphemeralDataDirs,
423
460
  updateHeartbeat,
424
461
  withGenerationLockSync
425
- } from "../chunk-C34ZXZ7O.js";
462
+ } from "../chunk-KUA4OJ5Y.js";
426
463
  import {
427
464
  createSingleFlight
428
465
  } from "../chunk-BW5KFIQC.js";
@@ -432,7 +469,7 @@ import {
432
469
  } from "../chunk-QYF5S6P6.js";
433
470
  import {
434
471
  detectFileKind
435
- } from "../chunk-PHDMFAKM.js";
472
+ } from "../chunk-DUO2TU77.js";
436
473
  import {
437
474
  compareKeys,
438
475
  detectFileRole,
@@ -442,18 +479,18 @@ import {
442
479
  provenanceView,
443
480
  resolvePiLensConfig,
444
481
  summarizeConfigResolution
445
- } from "../chunk-2BBLFX75.js";
482
+ } from "../chunk-J6MPGQTS.js";
446
483
  import {
447
484
  LSP_NAMESPACE_KEY,
448
485
  getPiLensGlobalConfigPath,
449
486
  resetWorkspaceTopology
450
- } from "../chunk-XVF2VVXP.js";
487
+ } from "../chunk-5HSRT26B.js";
451
488
  import {
452
489
  getProjectTrustState,
453
490
  isToolInstallAllowedByTrust,
454
491
  projectTrustDenialReason,
455
492
  setProjectTrustState
456
- } from "../chunk-ZANLILYD.js";
493
+ } from "../chunk-M2IFVFWU.js";
457
494
  import {
458
495
  DEFAULT_MAX_OUTPUT_BYTES,
459
496
  createBoundedOutputSink,
@@ -474,17 +511,17 @@ import {
474
511
  resolveLensToolEnabled,
475
512
  resolveLensToolName,
476
513
  toolRegistryEntryForMcp
477
- } from "../chunk-JAU72XNY.js";
514
+ } from "../chunk-J64T55EI.js";
478
515
  import {
479
516
  createSubsystemLogger,
480
517
  flushExtensionLog,
481
518
  logExtension
482
- } from "../chunk-XTMJ7XDT.js";
519
+ } from "../chunk-POMLXPMY.js";
483
520
  import "../chunk-JMYYOTQG.js";
484
521
  import {
485
522
  claimPhaseOncePerSession,
486
523
  logLatency
487
- } from "../chunk-QQNQ7SJW.js";
524
+ } from "../chunk-QBFBRV2B.js";
488
525
  import {
489
526
  beginTurnContext,
490
527
  createNdjsonLogger,
@@ -493,11 +530,11 @@ import {
493
530
  isTestMode,
494
531
  runLogCleanup,
495
532
  setTurnContextSession
496
- } from "../chunk-AOHZU2BM.js";
533
+ } from "../chunk-YNE3RP52.js";
497
534
  import "../chunk-LJDODBBZ.js";
498
535
  import {
499
536
  FactStore
500
- } from "../chunk-CHOW74WE.js";
537
+ } from "../chunk-CYRKQRPM.js";
501
538
  import {
502
539
  BoundedFifoMap,
503
540
  BoundedLruCache
@@ -515,13 +552,13 @@ import {
515
552
  resolveHostToolPath,
516
553
  toProjectRelativePath,
517
554
  uriToPath
518
- } from "../chunk-N2H3ZPZY.js";
555
+ } from "../chunk-3RKP3FMS.js";
519
556
  import "../chunk-NXL4FFQQ.js";
520
557
 
521
558
  // dist/mcp/server.js
522
559
  import * as fs25 from "node:fs";
523
560
  import * as net from "node:net";
524
- import * as path39 from "node:path";
561
+ import * as path40 from "node:path";
525
562
  import { fileURLToPath as fileURLToPath3 } from "node:url";
526
563
 
527
564
  // dist/tools/render-compact.js
@@ -786,7 +823,7 @@ function compactRenderResult(summarize) {
786
823
 
787
824
  // dist/clients/ast-grep-client.js
788
825
  import * as fs4 from "node:fs";
789
- import * as os2 from "node:os";
826
+ import * as os from "node:os";
790
827
  import * as path4 from "node:path";
791
828
 
792
829
  // dist/clients/ast-grep-rule-manager.js
@@ -927,7 +964,6 @@ var AstGrepRuleManager = class {
927
964
 
928
965
  // dist/clients/sg-runner.js
929
966
  import * as fs3 from "node:fs";
930
- import * as os from "node:os";
931
967
  import * as path3 from "node:path";
932
968
  function buildBashRunArgs(cmd, allArgs) {
933
969
  return ["-c", '"$0" "$@"', cmd, ...allArgs];
@@ -1356,6 +1392,7 @@ ${result.stderr}`)) {
1356
1392
  case "cwd-unresolvable":
1357
1393
  case "permission-denied":
1358
1394
  case "spawn-failed":
1395
+ case "trust-refused":
1359
1396
  case void 0:
1360
1397
  return result.error ? "cli-failure" : void 0;
1361
1398
  }
@@ -1473,7 +1510,7 @@ Original error: ${stderr}`;
1473
1510
  }
1474
1511
  // --- Shared helpers for temp-dir rule scans ---
1475
1512
  prepareTempScan(ruleId, ruleYaml) {
1476
- const sessionDir = fs3.mkdtempSync(path3.join(os.tmpdir(), `pi-lens-temp-${ruleId}-`));
1513
+ const sessionDir = fs3.mkdtempSync(path3.join(scannerReportParentDir(), `pi-lens-temp-${ruleId}-`));
1477
1514
  try {
1478
1515
  const rulesSubdir = path3.join(sessionDir, "rules");
1479
1516
  const configFile = path3.join(sessionDir, ".sgconfig.yml");
@@ -1952,7 +1989,7 @@ var AstGrepClient = class {
1952
1989
  * Dump the parsed tree-sitter AST for a snippet using ast-grep CLI.
1953
1990
  */
1954
1991
  async dumpAst(source, lang, options = {}) {
1955
- const tmpDir = fs4.mkdtempSync(path4.join(os2.tmpdir(), "pi-lens-ast-dump-"));
1992
+ const tmpDir = fs4.mkdtempSync(path4.join(os.tmpdir(), "pi-lens-ast-dump-"));
1956
1993
  const tmpFile = path4.join(tmpDir, `snippet.${lang.replace(/[^a-z0-9_-]/gi, "") || "txt"}`);
1957
1994
  try {
1958
1995
  fs4.writeFileSync(tmpFile, source, "utf-8");
@@ -1982,7 +2019,7 @@ var AstGrepClient = class {
1982
2019
  if (shapeError)
1983
2020
  return { valid: false, error: shapeError };
1984
2021
  const snippet = validationSnippetFor(lang);
1985
- const tmpDir = fs4.mkdtempSync(path4.join(os2.tmpdir(), "pi-lens-sg-validate-"));
2022
+ const tmpDir = fs4.mkdtempSync(path4.join(os.tmpdir(), "pi-lens-sg-validate-"));
1986
2023
  const tmpFile = path4.join(tmpDir, `snippet.${snippet.ext}`);
1987
2024
  try {
1988
2025
  fs4.writeFileSync(tmpFile, snippet.source, "utf-8");
@@ -2029,7 +2066,7 @@ var AstGrepClient = class {
2029
2066
  return { valid: false, error: shapeError };
2030
2067
  const language = extractRuleLanguage(ruleYaml) ?? "typescript";
2031
2068
  const snippet = validationSnippetFor(language);
2032
- const tmpDir = fs4.mkdtempSync(path4.join(os2.tmpdir(), "pi-lens-sg-rule-"));
2069
+ const tmpDir = fs4.mkdtempSync(path4.join(os.tmpdir(), "pi-lens-sg-rule-"));
2033
2070
  try {
2034
2071
  fs4.writeFileSync(path4.join(tmpDir, `snippet.${snippet.ext}`), snippet.source, "utf-8");
2035
2072
  const result = await this.tempScanDetailed(tmpDir, ruleYaml, options);
@@ -2606,6 +2643,7 @@ async function scanFileMajorRules(cwd, files, signal) {
2606
2643
  } finally {
2607
2644
  facts.dropFileFacts(filePath);
2608
2645
  }
2646
+ await new Promise((resolve32) => setImmediate(resolve32));
2609
2647
  }
2610
2648
  if (wasmAborted) {
2611
2649
  astGrep.length = 0;
@@ -2937,29 +2975,29 @@ async function buildBootstrapClients() {
2937
2975
  }
2938
2976
  }
2939
2977
  const [ruffClient, biomeClient, knipClient, todoScanner, jscpdClient, depChecker, testRunnerClient, metricsClient, complexityClient, goClient, govulncheckClient, gitleaksClient, trivyClient, opengrepClient, rustClient, agentBehaviorClient2, deadCodeClients] = await Promise.all([
2940
- load("ruff", async () => new (await import("../chunk-PFNSLWU5.js")).RuffClient()),
2941
- load("biome", async () => new (await import("../chunk-QZVW3AAK.js")).BiomeClient()),
2942
- load("knip", async () => new (await import("../chunk-7ULNCDCO.js")).KnipClient()),
2943
- load("todo", async () => new (await import("../chunk-JSQFTSC6.js")).TodoScanner()),
2944
- load("jscpd", async () => new (await import("../chunk-3UUPUE3O.js")).JscpdClient()),
2945
- load("dependency-checker", async () => new (await import("../chunk-3WA4HP4I.js")).DependencyChecker()),
2946
- load("test-runner", async () => new (await import("../chunk-Q6ZWTCLY.js")).TestRunnerClient()),
2947
- load("metrics", async () => new (await import("../chunk-N3X6Q4YT.js")).MetricsClient()),
2948
- load("complexity", async () => new (await import("../chunk-VSSBEUFT.js")).ComplexityClient()),
2978
+ load("ruff", async () => new (await import("../chunk-OHKJ7PQF.js")).RuffClient()),
2979
+ load("biome", async () => new (await import("../chunk-N5FLEO7W.js")).BiomeClient()),
2980
+ load("knip", async () => new (await import("../chunk-6DLWKFEX.js")).KnipClient()),
2981
+ load("todo", async () => new (await import("../chunk-2FN63LPV.js")).TodoScanner()),
2982
+ load("jscpd", async () => new (await import("../chunk-R3T7CLQ2.js")).JscpdClient()),
2983
+ load("dependency-checker", async () => new (await import("../chunk-LZSEQW6A.js")).DependencyChecker()),
2984
+ load("test-runner", async () => new (await import("../chunk-GL5LUMZE.js")).TestRunnerClient()),
2985
+ load("metrics", async () => new (await import("../chunk-VOOAPROD.js")).MetricsClient()),
2986
+ load("complexity", async () => new (await import("../chunk-AWSCPCN2.js")).ComplexityClient()),
2949
2987
  // The process-wide singleton, NOT a fresh instance (#2455 fix round 4,
2950
2988
  // F2). `handleSessionStart` reads this object for its "Active tools"
2951
2989
  // line while `resetGoAvailability` re-arms the singleton's latch; a
2952
2990
  // second instance here meant the reset never reached the surface a
2953
2991
  // user actually sees.
2954
- load("go", async () => (await import("../chunk-Z527N6YQ.js")).goClient),
2955
- load("govulncheck", async () => new (await import("../chunk-XPZPXCBV.js")).GovulncheckClient()),
2956
- load("gitleaks", async () => new (await import("../chunk-WOKKRCJ5.js")).GitleaksClient()),
2957
- load("trivy", async () => new (await import("../chunk-IBRALRBR.js")).TrivyClient()),
2958
- load("opengrep", async () => new (await import("../chunk-XKIZ2HNN.js")).OpengrepClient()),
2992
+ load("go", async () => (await import("../chunk-SKAMFC7A.js")).goClient),
2993
+ load("govulncheck", async () => new (await import("../chunk-3PTHXJXR.js")).GovulncheckClient()),
2994
+ load("gitleaks", async () => new (await import("../chunk-MBKQHGA7.js")).GitleaksClient()),
2995
+ load("trivy", async () => new (await import("../chunk-JW3YWBVB.js")).TrivyClient()),
2996
+ load("opengrep", async () => new (await import("../chunk-5IUSNGOC.js")).OpengrepClient()),
2959
2997
  // The process-wide singleton, for the same reason as "go" above.
2960
- load("rust", async () => (await import("../chunk-BRGDSBJE.js")).rustClient),
2961
- load("agent-behavior", async () => (await import("../chunk-23WOO7TX.js")).agentBehaviorClient),
2962
- loadList("dead-code", async () => (await import("../chunk-NW7BOILN.js")).getDeadCodeClients())
2998
+ load("rust", async () => (await import("../chunk-SYE6T2EB.js")).rustClient),
2999
+ load("agent-behavior", async () => (await import("../chunk-R7YOCSNV.js")).agentBehaviorClient),
3000
+ loadList("dead-code", async () => (await import("../chunk-5WRPDP2A.js")).getDeadCodeClients())
2963
3001
  ]);
2964
3002
  if (failures.length > 0)
2965
3003
  await logBootstrapFailures(failures);
@@ -3046,7 +3084,7 @@ function resetAnalyzerBootstrapSessionState() {
3046
3084
  }
3047
3085
 
3048
3086
  // dist/clients/formatters-lazy.js
3049
- var lazyFormatters = createLazyImport(() => import("../chunk-GHB6AYCP.js"));
3087
+ var lazyFormatters = createLazyImport(() => import("../chunk-ZVUL7QYY.js"));
3050
3088
  function warmFormatters() {
3051
3089
  return lazyFormatters.get();
3052
3090
  }
@@ -3200,13 +3238,18 @@ function readChangeLogMaxSeq(logPath) {
3200
3238
  }
3201
3239
  var CHANGE_LOG_LOCK = { staleMs: 5e3, waitMs: 500 };
3202
3240
  function appendProjectChangeAllocated(cwd, build) {
3241
+ appendProjectChangesAllocated(cwd, (logMaxSeq) => [build(logMaxSeq)]);
3242
+ }
3243
+ function appendProjectChangesAllocated(cwd, buildAll) {
3203
3244
  const logPath = getProjectChangeLogPath(cwd);
3204
3245
  fs7.mkdirSync(path7.dirname(logPath), { recursive: true });
3205
3246
  readChangeLogMaxSeq(logPath);
3206
3247
  const append = (locked) => {
3207
- const entry = build(readChangeLogMaxSeq(logPath));
3208
- const line = locked ? entry : { ...entry, unlocked: true };
3209
- fs7.appendFileSync(logPath, `${JSON.stringify(line)}
3248
+ const entries = buildAll(readChangeLogMaxSeq(logPath));
3249
+ if (entries.length === 0)
3250
+ return;
3251
+ const payload = entries.map((entry) => JSON.stringify(locked ? entry : { ...entry, unlocked: true })).join("\n");
3252
+ fs7.appendFileSync(logPath, `${payload}
3210
3253
  `, "utf-8");
3211
3254
  };
3212
3255
  const result = withGenerationLockSync(`${logPath}.locks`, CHANGE_LOG_LOCK, () => append(true));
@@ -3227,7 +3270,11 @@ function inlineBlockerSources(blockers) {
3227
3270
  }
3228
3271
  function inlineBlockerLines(blockers, filePath) {
3229
3272
  const own = normalizeEphemeralMapKey(filePath);
3230
- return blockers.filter((d) => normalizeEphemeralMapKey(d.filePath) === own).map((d) => d.line).filter((line) => typeof line === "number");
3273
+ return blockers.filter(
3274
+ // #4509 (N3): a stale entry's line is from older bytes, so it cites
3275
+ // none: the past-EOF sweep would retire the whole record on it.
3276
+ (d) => !d.positionStale && normalizeEphemeralMapKey(d.filePath) === own
3277
+ ).map((d) => d.line).filter((line) => typeof line === "number");
3231
3278
  }
3232
3279
 
3233
3280
  // dist/clients/partial-edit-apply.js
@@ -3413,6 +3460,7 @@ function recordViewIncomplete(cwd, cause) {
3413
3460
  });
3414
3461
  }
3415
3462
  var MAX_RESOLVED_BLOCKER_FILES = 10;
3463
+ var MAX_OWED_ANCHORS = 16;
3416
3464
  var TOOL_CALL_ATTRIBUTION_CAPACITY = 256;
3417
3465
  var TOOL_CALL_ATTRIBUTION_TTL_MS = 5 * 6e4;
3418
3466
  var TurnWarningsBySession = class {
@@ -3462,7 +3510,7 @@ var RuntimeCoordinator = class {
3462
3510
  diagnosticsSurfaced: 0,
3463
3511
  coldSnapshotTouches: 0
3464
3512
  };
3465
- _complexityBaselines = /* @__PURE__ */ new Map();
3513
+ _complexityBaselines = /* @__PURE__ */ new Set();
3466
3514
  _fixedThisTurn = new PathKeyedMap(normalizeMapKey);
3467
3515
  _writtenThisTurn = new PathKeyedMap(normalizeMapKey);
3468
3516
  _autofixDemotedThisTurn = new PathKeyedMap(normalizeMapKey);
@@ -3551,6 +3599,26 @@ var RuntimeCoordinator = class {
3551
3599
  _readWidenings = new BoundedLruCache(TOOL_CALL_ATTRIBUTION_CAPACITY);
3552
3600
  _lspReadWarmState = /* @__PURE__ */ new Map();
3553
3601
  _pendingInlineBlockers = new PathKeyedMap(normalizeMapKey);
3602
+ /**
3603
+ * #4484 round 4: per file, what its last applied inline verdict left owed
3604
+ * (`setAnalysisAnchor`), for a file whose record is gone or has no anchor.
3605
+ * The positions live on the blocker record
3606
+ * (`InlineBlockerRecord.analysisAnchor`). Round 5: this keeps each owing
3607
+ * runner's owed line texts (`owedByContentOnly`, at most
3608
+ * `MAX_OWED_LINE_TEXTS` hashes per runner, 16 KB), not only its id, so
3609
+ * the next write owes it only the lines with those texts, never the whole
3610
+ * file for a skip. Replaced by every applied verdict of the file and
3611
+ * cleared per session.
3612
+ */
3613
+ _owedRunners = new PathKeyedMap(normalizeMapKey);
3614
+ /**
3615
+ * #4484 round 5: for a file with no blocker record, its last verdict's
3616
+ * whole anchor (line hashes too), so the next write still remaps owed
3617
+ * positions exactly (verify probe P23). At most `MAX_OWED_ANCHORS` files,
3618
+ * 400 KB each; an evicted file falls back to `_owedRunners`' owed line
3619
+ * texts, a superset. Cleared per session.
3620
+ */
3621
+ _owedAnchors = new PathKeyedMap(normalizeMapKey, MAX_OWED_ANCHORS);
3554
3622
  /**
3555
3623
  * #3507: one per-path order for BOTH verbs on `_pendingInlineBlockers`,
3556
3624
  * the widget store's guard shape (`widget-state.ts` diagnosticsWriteGuard).
@@ -3639,6 +3707,8 @@ var RuntimeCoordinator = class {
3639
3707
  this._readWidenings.clear();
3640
3708
  this._lspReadWarmState.clear();
3641
3709
  this._pendingInlineBlockers.clear();
3710
+ this._owedRunners.clear();
3711
+ this._owedAnchors.clear();
3642
3712
  this._inlineBlockerWriteOrder.clear();
3643
3713
  this._resolvedBlockerFilesThisTurn.clear();
3644
3714
  this._resolvedBlockerFilesDropped = 0;
@@ -3792,20 +3862,79 @@ var RuntimeCoordinator = class {
3792
3862
  }
3793
3863
  }
3794
3864
  const { projectSeq, fileSeq, key } = logged ?? this.bumpFileSeq(args.filePath);
3865
+ this.recordMutationReceipt({ projectSeq, fileSeq, key }, args.source);
3866
+ return { projectSeq, fileSeq };
3867
+ }
3868
+ /**
3869
+ * #4535: the bulk form of {@link recordProjectMutation}, for a disk change
3870
+ * pi-lens did not author that spans many paths (a publisher's recorded
3871
+ * population). It allocates from the SAME sequence, in ONE locked append:
3872
+ * `appendProjectChangesAllocated` takes the change-log lock once and writes
3873
+ * every line under it, so a 400-path HEAD move costs one lock round-trip
3874
+ * inside a hook budget instead of 400. The lock-failure semantics are the
3875
+ * single writer's: when the lock is unavailable every appended line is
3876
+ * tagged `unlocked` and one `change-log-lock-unavailable` degradation is
3877
+ * recorded for the batch. Receipts are appended per path with the same ring
3878
+ * cap and drop counter as a single mutation.
3879
+ *
3880
+ * Returns the batch's last allocated `projectSeq` (the record's seq after
3881
+ * the batch) and each path's `fileSeq` keyed by the bump's normalized key.
3882
+ * Duplicate paths are the caller's to collapse: each entry bumps.
3883
+ */
3884
+ recordProjectMutations(args) {
3885
+ let logged;
3886
+ if (args.cwd !== void 0 && args.paths.length > 0) {
3887
+ try {
3888
+ const cwd = args.cwd;
3889
+ const paths = args.paths;
3890
+ appendProjectChangesAllocated(cwd, (logMaxSeq) => {
3891
+ const ownSeq = this._projectSeq;
3892
+ const bumps2 = paths.map((filePath) => this.bumpFileSeq(filePath, logMaxSeq));
3893
+ logged = bumps2;
3894
+ if (this._viewMissingThrough > 0) {
3895
+ recordViewIncomplete(cwd, { ownSeq, logMaxSeq });
3896
+ }
3897
+ return bumps2.map((bump, index) => ({
3898
+ seq: bump.projectSeq,
3899
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3900
+ sessionId: this.telemetrySessionId,
3901
+ turnIndex: this.turnIndex,
3902
+ source: args.source,
3903
+ filePath: path8.resolve(paths[index]),
3904
+ fileSeq: bump.fileSeq
3905
+ }));
3906
+ });
3907
+ } catch (err) {
3908
+ args.onAppendError?.(err);
3909
+ }
3910
+ }
3911
+ const bumps = logged ?? args.paths.map((filePath) => this.bumpFileSeq(filePath));
3912
+ const fileSeqs = /* @__PURE__ */ new Map();
3913
+ for (const bump of bumps) {
3914
+ this.recordMutationReceipt({ projectSeq: bump.projectSeq, fileSeq: bump.fileSeq, key: bump.key }, args.source);
3915
+ fileSeqs.set(bump.key, bump.fileSeq);
3916
+ }
3917
+ return { projectSeq: this._projectSeq, fileSeqs };
3918
+ }
3919
+ /**
3920
+ * The receipt ring (§ {@link MutationReceipt}): one entry per bump, oldest
3921
+ * evicted at the cap and counted, never silently incomplete. One writer, so
3922
+ * the single and bulk mutation paths cannot drift apart on attribution.
3923
+ */
3924
+ recordMutationReceipt(bump, source) {
3795
3925
  if (this._mutationReceipts.length >= MAX_MUTATION_RECEIPTS) {
3796
3926
  this._mutationReceipts.shift();
3797
3927
  this._droppedMutationReceipts += 1;
3798
3928
  }
3799
3929
  this._mutationReceipts.push({
3800
- seq: projectSeq,
3930
+ seq: bump.projectSeq,
3801
3931
  // Reuse the bump's normalized key (a realpath syscall: ~200us on
3802
3932
  // Windows, ~1.8us on POSIX since #3098) — never re-derive it here.
3803
- filePath: key,
3804
- source: args.source,
3933
+ filePath: bump.key,
3934
+ source,
3805
3935
  turnIndex: this._turnIndex,
3806
3936
  ts: Date.now()
3807
3937
  });
3808
- return { projectSeq, fileSeq };
3809
3938
  }
3810
3939
  /**
3811
3940
  * Attributed mutations whose seq is strictly after `seq` — the receipt-level
@@ -4274,7 +4403,7 @@ var RuntimeCoordinator = class {
4274
4403
  const existing = this._pendingInlineBlockers.get(key);
4275
4404
  if (existing && !existing.diagnostics)
4276
4405
  return 0;
4277
- const identity = (d) => JSON.stringify([d.tool, d.id, d.line, d.message]);
4406
+ const identity = (d) => JSON.stringify([foldedFindingIdentity(d), d.line, d.column]);
4278
4407
  const known = new Set((existing?.diagnostics ?? []).map(identity));
4279
4408
  const added = blocking.flatMap((d) => known.has(identity(d)) ? [] : [d]);
4280
4409
  if (added.length === 0)
@@ -4310,6 +4439,91 @@ var RuntimeCoordinator = class {
4310
4439
  });
4311
4440
  return added.length;
4312
4441
  }
4442
+ /**
4443
+ * #4484 round 2: the file's recorded blocking diagnostics, which the next
4444
+ * write's pipeline carries forward unless a conclusive observation from
4445
+ * their own source shows them gone. Undefined when nothing is recorded or
4446
+ * the record holds text only.
4447
+ */
4448
+ getInlineBlockerDiagnostics(filePath) {
4449
+ return this._pendingInlineBlockers.get(path8.resolve(filePath))?.diagnostics;
4450
+ }
4451
+ /**
4452
+ * #4484 round 3: the anchor the next write's pipeline aligns to: the line
4453
+ * hashes of the bytes the file's blocker record is about, and the lines
4454
+ * still owed. Round 4: it is the record's own anchor. A file without one
4455
+ * (no record, or a record a collect-later settle started) gets no hashes,
4456
+ * so the pipeline cannot align. Round 5: it gets the owed line texts.
4457
+ */
4458
+ getAnalysisAnchor(filePath) {
4459
+ return this._pendingInlineBlockers.get(filePath)?.analysisAnchor ?? this._owedAnchors.get(filePath) ?? this._owedRunners.get(filePath);
4460
+ }
4461
+ /**
4462
+ * #4484 round 3: store the anchor of an inline verdict that just applied
4463
+ * (`recordInlineBlockers` or `clearInlineBlockers` returned applied), so it
4464
+ * always describes the bytes the record is about. Round 4: it goes on the
4465
+ * record, and lives as long as the record; what is owed goes apart
4466
+ * (`_owedRunners`, by content since round 5), so it outlives a record that
4467
+ * a later retire drops. Round 5: a file with no record keeps its whole
4468
+ * anchor too (`_owedAnchors`, bounded).
4469
+ */
4470
+ setAnalysisAnchor(filePath, anchor) {
4471
+ const existing = this._pendingInlineBlockers.get(filePath);
4472
+ if (existing) {
4473
+ const key = path8.resolve(filePath);
4474
+ const { analysisAnchor: _dropped, ...rest } = existing;
4475
+ this._pendingInlineBlockers.set(key, anchor ? { ...rest, analysisAnchor: anchor } : rest);
4476
+ }
4477
+ const owed = owedByContentOnly(anchor);
4478
+ if (owed)
4479
+ this._owedRunners.set(filePath, owed);
4480
+ else
4481
+ this._owedRunners.delete(filePath);
4482
+ if (owed && anchor?.lineHashes && !existing)
4483
+ this._owedAnchors.set(filePath, anchor);
4484
+ else
4485
+ this._owedAnchors.delete(filePath);
4486
+ }
4487
+ /**
4488
+ * #4484 round 2: a settled collect-later run is the observation that can
4489
+ * retire the blockers its runner left pending while it was in flight. Its
4490
+ * runner's recorded blockers that the run no longer observes leave the
4491
+ * record; blockers from other runners, and from a server the run left
4492
+ * unconfirmed, stay. A blocker is still observed only at its own line, in
4493
+ * range or withheld by the run's edit-range gate (#4484 round 3). The run
4494
+ * must be about the recorded bytes: marked after
4495
+ * they were read (`recordedAtMs`), with the file still holding them
4496
+ * (`recordedHash`). An emptied record is deleted and its file is noted
4497
+ * Resolved for turn end, as a clean write's clear does.
4498
+ *
4499
+ * @returns how many recorded blockers the run retired.
4500
+ */
4501
+ retireUnobservedRunnerBlockers(filePath, run) {
4502
+ const key = path8.resolve(filePath);
4503
+ const existing = this._pendingInlineBlockers.get(key);
4504
+ if (!existing?.diagnostics || run.sha256 === void 0 || existing.recordedHash !== run.sha256 || existing.recordedAtMs === void 0 || run.markedAtMs < existing.recordedAtMs)
4505
+ return 0;
4506
+ const unconfirmed = new Set(run.unconfirmedServerIds);
4507
+ const { kept, retired } = retainRecordedFindings(existing.diagnostics, run.observed, {
4508
+ positionOf: (d) => d.positionStale ? ANY_POSITION : d.line,
4509
+ keepUnobserved: (d) => d.runnerId !== run.runnerId || d.serverId !== void 0 && unconfirmed.has(d.serverId)
4510
+ });
4511
+ if (retired.length === 0)
4512
+ return 0;
4513
+ if (kept.length === 0) {
4514
+ this.noteResolvedBlockerFile(existing, void 0, false);
4515
+ this._pendingInlineBlockers.delete(key);
4516
+ return retired.length;
4517
+ }
4518
+ this._pendingInlineBlockers.set(key, {
4519
+ ...existing,
4520
+ summary: formatDiagnostics(kept, "blocking").trim(),
4521
+ diagnostics: kept,
4522
+ sources: inlineBlockerSources(kept),
4523
+ lines: inlineBlockerLines(kept, existing.filePath)
4524
+ });
4525
+ return retired.length;
4526
+ }
4313
4527
  /**
4314
4528
  * #3218 criterion 2: remember one file whose blocker a clean verdict just
4315
4529
  * retired, for the turn-end "Resolved" line. Called from the two retire
@@ -4347,9 +4561,22 @@ var RuntimeCoordinator = class {
4347
4561
  * counted as `blocker-clear-refused` per file key. A record with no
4348
4562
  * `recordedHash` (#2982: the file was unreadable or over the baseline cap at
4349
4563
  * record time) cannot be matched and is cleared as before.
4564
+ *
4565
+ * #4484: a blocker is pending until a fresh observation no longer holds it,
4566
+ * so a run whose own observation still holds `analysis.blocking` blockers
4567
+ * never clears the record, whatever its authorship or its delta said; it
4568
+ * is refused and counted the same way.
4350
4569
  */
4351
4570
  clearInlineBlockers(filePath, writeIndex, orderTurn = this._writeOrderTurn, analysis) {
4352
4571
  const existing = this._pendingInlineBlockers.get(path8.resolve(filePath));
4572
+ if (existing && (analysis?.blocking ?? 0) > 0) {
4573
+ incrementDegradationCount({
4574
+ kind: "blocker-clear-refused",
4575
+ subject: normalizeMapKey(filePath),
4576
+ reason: "the clearing run still observed a blocker on the file; the record stays until an observation holds none (#4484)"
4577
+ });
4578
+ return false;
4579
+ }
4353
4580
  if (existing && analysis && !analysis.authored && analysis.sha256 !== void 0 && existing.recordedHash === analysis.sha256) {
4354
4581
  incrementDegradationCount({
4355
4582
  kind: "blocker-clear-refused",
@@ -5344,7 +5571,8 @@ function boundDeferredTargets(entries, max, isOwn) {
5344
5571
  }
5345
5572
 
5346
5573
  // dist/clients/test-runner-delivery.js
5347
- var pending = /* @__PURE__ */ new Map();
5574
+ var MAX_PENDING_DELIVERIES = 32;
5575
+ var pending = new BoundedFifoMap(MAX_PENDING_DELIVERIES);
5348
5576
  function resetTestRunnerDelivery() {
5349
5577
  pending.clear();
5350
5578
  }
@@ -5603,8 +5831,8 @@ async function igniteDominantLanguageWarm(cwd, runtime, sessionGeneration, dbg)
5603
5831
  if (!runtime.isCurrentSession(sessionGeneration))
5604
5832
  return;
5605
5833
  const lspService = (await loadLspService()).getLSPService();
5606
- const { collectSourceFilesAsync } = await import("../chunk-4W4VLAPJ.js");
5607
- const { CODE_KINDS, detectFileKind: detectFileKind2 } = await import("../chunk-FLKWCIZH.js");
5834
+ const { collectSourceFilesAsync } = await import("../chunk-IQWN5JCP.js");
5835
+ const { CODE_KINDS, detectFileKind: detectFileKind2 } = await import("../chunk-S2HEXIWI.js");
5608
5836
  const files = await collectSourceFilesAsync(cwd, {
5609
5837
  inspectGeneratedHeaders: false
5610
5838
  });
@@ -5670,7 +5898,7 @@ function scheduleManagedToolRefresh(dbg) {
5670
5898
  const timer = setTimeout(() => {
5671
5899
  void (async () => {
5672
5900
  try {
5673
- const refresh = await import("../chunk-CF24OIYI.js");
5901
+ const refresh = await import("../chunk-2KXTHF3M.js");
5674
5902
  const outcome = await refresh.runManagedToolRefresh();
5675
5903
  if (outcome.skipped) {
5676
5904
  dbg(`session_start tool-refresh: skipped (${outcome.skipped})`);
@@ -5717,7 +5945,7 @@ function scanOneTodoFile(scanner, filePath, items) {
5717
5945
  async function collectTodoBaselineItems(scanner, analysisRoot, stillCurrent) {
5718
5946
  const items = [];
5719
5947
  try {
5720
- const { getSourceFilesAsync } = await import("../chunk-IZSHAIJ7.js");
5948
+ const { getSourceFilesAsync } = await import("../chunk-TRETHTQO.js");
5721
5949
  const files = await getSourceFilesAsync(analysisRoot, true);
5722
5950
  if (!stillCurrent())
5723
5951
  return items;
@@ -5746,7 +5974,7 @@ async function buildOrRefreshWordIndex(args) {
5746
5974
  const snapshot = loadProjectSnapshot(snapshotRoot);
5747
5975
  const snapshotLoadMs = Date.now() - snapshotLoadStartMs;
5748
5976
  if (snapshot?.wordIndex) {
5749
- const { deserializeWordIndex, refreshWordIndexIncrementally, countWordIndexPostingEntries: countWordIndexPostingEntries2, estimateWordIndexResidentBytes: estimateWordIndexResidentBytes2 } = await import("../chunk-WU567RSC.js");
5977
+ const { deserializeWordIndex, refreshWordIndexIncrementally, countWordIndexPostingEntries: countWordIndexPostingEntries2, estimateWordIndexResidentBytes: estimateWordIndexResidentBytes2, notifyWordIndexPublished: notifyWordIndexPublished2 } = await import("../chunk-ATKJFDQN.js");
5750
5978
  const deserializeStartMs = Date.now();
5751
5979
  const index = deserializeWordIndex(snapshot.wordIndex);
5752
5980
  const deserializeMs = Date.now() - deserializeStartMs;
@@ -5771,6 +5999,7 @@ async function buildOrRefreshWordIndex(args) {
5771
5999
  });
5772
6000
  } else {
5773
6001
  runtime.wordIndex = index;
6002
+ notifyWordIndexPublished2(snapshotRoot, index, "session_start");
5774
6003
  let snapshotSaveSyncMs = 0;
5775
6004
  if (!isProjectSnapshotFresh(snapshot, effectiveSeq, latestSeq.unlockedThrough) || result.refreshed > 0 || result.dropped > 0 || snapshot.wordIndex.truncated !== index.truncated) {
5776
6005
  const serializeSaveEnqueueStartMs = Date.now();
@@ -5818,7 +6047,7 @@ async function buildOrRefreshWordIndex(args) {
5818
6047
  }
5819
6048
  }
5820
6049
  }
5821
- const { buildWordIndexAsync, collectWordIndexDocs, countWordIndexPostingEntries, estimateWordIndexResidentBytes } = await import("../chunk-WU567RSC.js");
6050
+ const { buildWordIndexAsync, collectWordIndexDocs, countWordIndexPostingEntries, estimateWordIndexResidentBytes, notifyWordIndexPublished } = await import("../chunk-ATKJFDQN.js");
5822
6051
  const docs = await collectWordIndexDocs(analysisRoot, () => runtime.isCurrentSession(sessionGeneration), rebuildPreflightFiles);
5823
6052
  if (!runtime.isCurrentSession(sessionGeneration))
5824
6053
  return;
@@ -5835,6 +6064,7 @@ async function buildOrRefreshWordIndex(args) {
5835
6064
  if (!runtime.isCurrentSession(sessionGeneration))
5836
6065
  return;
5837
6066
  runtime.wordIndex = rebuiltIndex;
6067
+ notifyWordIndexPublished(snapshotRoot, rebuiltIndex, "session_start");
5838
6068
  saveRuntimeProjectSnapshot({ cwd: snapshotRoot, runtime, dbg });
5839
6069
  dbg(`session_start word-index: rebuilt (absent/stale, seq=${effectiveSeq}) ${runtime.wordIndex.docCount} files, ${runtime.wordIndex.postings.size} tokens (${Date.now() - startMs}ms)`);
5840
6070
  logWordIndex({
@@ -5857,6 +6087,11 @@ async function buildOrRefreshWordIndex(args) {
5857
6087
  reused: 0
5858
6088
  };
5859
6089
  }
6090
+ var STARTUP_TASK_DEFER_MS = {
6091
+ "call-graph": 5e3,
6092
+ "codebase-model": 5200,
6093
+ "ast-grep-exports": 5400
6094
+ };
5860
6095
  function scheduleStartupScans(deps, runtime, sessionGeneration, analysisRoot, snapshotRoot, languageProfile, dbg) {
5861
6096
  void (async () => {
5862
6097
  const resolved = await demandBootstrapDeps(deps, "session-start-scans");
@@ -5878,15 +6113,10 @@ function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analy
5878
6113
  reason: "skipped (disabled by config)"
5879
6114
  });
5880
6115
  }
5881
- const taskDeferMsByName = {
5882
- "call-graph": 5e3,
5883
- "codebase-model": 5200,
5884
- "ast-grep exports": 5400,
5885
- "project index": 5400
5886
- };
5887
6116
  const runTask = (name, task) => {
5888
6117
  const queuedAt = Date.now();
5889
- dbg(`session_start task ${name}: scheduled`);
6118
+ const delay = STARTUP_TASK_DEFER_MS[name] ?? 0;
6119
+ dbg(`session_start task ${name}: scheduled deferMs=${delay}`);
5890
6120
  runtime.markStartupScanInFlight(name, sessionGeneration);
5891
6121
  const completion = new Promise((resolve32) => {
5892
6122
  const fire = () => {
@@ -5903,7 +6133,6 @@ function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analy
5903
6133
  resolve32();
5904
6134
  });
5905
6135
  };
5906
- const delay = taskDeferMsByName[name] ?? 0;
5907
6136
  if (delay > 0) {
5908
6137
  setTimeout(fire, delay).unref?.();
5909
6138
  } else {
@@ -5915,7 +6144,7 @@ function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analy
5915
6144
  const canRunJsTsHeavyScans = canRunStartupHeavyScans(languageProfile, "jsts");
5916
6145
  const scanNames = ["todo", "dead-code"];
5917
6146
  if (canRunJsTsHeavyScans) {
5918
- scanNames.push("knip", "jscpd", "ast-grep exports", "project index");
6147
+ scanNames.push("knip", "jscpd", "ast-grep-exports");
5919
6148
  }
5920
6149
  dbg(`session_start: launching background scans (${scanNames.join(", ")})`);
5921
6150
  void runTask("todo", async () => {
@@ -6183,9 +6412,9 @@ function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analy
6183
6412
  });
6184
6413
  let callGraphIdentity;
6185
6414
  const callGraphTask = runTask("call-graph", async () => {
6186
- const { FactStore: FactStore2 } = await import("../chunk-QGQ5CCCG.js");
6187
- const { buildOrUpdateGraph, extractSymbolsAndRefsFromGraph, getReviewGraphCacheIdentity } = await import("../chunk-2ZGIZPDG.js");
6188
- const { buildCallGraph, saveCallGraph, loadCallGraph: loadCallGraph2 } = await import("../chunk-BKE7E7TC.js");
6415
+ const { FactStore: FactStore2 } = await import("../chunk-UMS2MFPZ.js");
6416
+ const { buildOrUpdateGraph, extractSymbolsAndRefsFromGraph, getReviewGraphCacheIdentity } = await import("../chunk-YCWYO6TQ.js");
6417
+ const { buildCallGraph, saveCallGraph, loadCallGraph: loadCallGraph2 } = await import("../chunk-GMX5OS7B.js");
6189
6418
  if (!runtime.isCurrentSession(sessionGeneration))
6190
6419
  return;
6191
6420
  const startMs = Date.now();
@@ -6226,7 +6455,7 @@ function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analy
6226
6455
  return;
6227
6456
  if (!runtime.callGraph)
6228
6457
  return;
6229
- const { buildCodebaseModel, saveCodebaseModel, DEFAULT_CODEBASE_MODEL_TOKEN_BUDGET } = await import("../chunk-TYM6OUIT.js");
6458
+ const { buildCodebaseModel, saveCodebaseModel, DEFAULT_CODEBASE_MODEL_TOKEN_BUDGET } = await import("../chunk-TOY6QKOH.js");
6230
6459
  if (!callGraphIdentity)
6231
6460
  return;
6232
6461
  const model = buildCodebaseModel(runtime.callGraph, analysisRoot, DEFAULT_CODEBASE_MODEL_TOKEN_BUDGET, callGraphIdentity);
@@ -6258,18 +6487,18 @@ function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analy
6258
6487
  });
6259
6488
  });
6260
6489
  }
6261
- function scheduleDeferredToolProbes(deps, languageProfile, startupDefaults, startupScansWillRun, dbg) {
6490
+ function scheduleDeferredToolProbes(deps, languageProfile, startupDefaults, dbg) {
6262
6491
  void (async () => {
6263
6492
  const resolved = await demandBootstrapDeps(deps, "session-start-tool-probes");
6264
6493
  if (!resolved) {
6265
6494
  dbg("session_start tools: analyzer bootstrap unavailable \u2014 no probes");
6266
6495
  return;
6267
6496
  }
6268
- scheduleDeferredToolProbesWithClients(resolved, languageProfile, startupDefaults, startupScansWillRun, dbg);
6497
+ scheduleDeferredToolProbesWithClients(resolved, languageProfile, startupDefaults, dbg);
6269
6498
  })();
6270
6499
  }
6271
- function scheduleDeferredToolProbesWithClients(deps, languageProfile, startupDefaults, startupScansWillRun, dbg) {
6272
- const { biomeClient, ruffClient, depChecker } = deps;
6500
+ function scheduleDeferredToolProbesWithClients(deps, languageProfile, startupDefaults, dbg) {
6501
+ const { biomeClient, ruffClient } = deps;
6273
6502
  const defaultTools = new Set(startupDefaults);
6274
6503
  const probes = [];
6275
6504
  if (languageProfile.present.jsts && !defaultTools.has("biome")) {
@@ -6278,9 +6507,6 @@ function scheduleDeferredToolProbesWithClients(deps, languageProfile, startupDef
6278
6507
  if (languageProfile.present.python && !defaultTools.has("ruff")) {
6279
6508
  probes.push(["ruff", () => ruffClient.ensureAvailable()]);
6280
6509
  }
6281
- if (startupScansWillRun) {
6282
- probes.push(["madge", () => depChecker.ensureAvailable()]);
6283
- }
6284
6510
  if (probes.length === 0) {
6285
6511
  dbg("session_start tools: no deferred availability probes needed");
6286
6512
  return;
@@ -6299,6 +6525,34 @@ function scheduleDeferredToolProbesWithClients(deps, languageProfile, startupDef
6299
6525
  dbg(`session_start tools (deferred probes complete, ${Date.now() - warmStart}ms): ${summary}`);
6300
6526
  })();
6301
6527
  }
6528
+ function resetSessionScopedState(cwd, sessionStartMs = Date.now()) {
6529
+ resetWorkspaceTopology();
6530
+ resetOpaqueMutationState();
6531
+ resetObservedMutationNet();
6532
+ resetMutationAttribution();
6533
+ primePersistedMutationAttribution(cwd);
6534
+ resetPendingAuxiliaryCoverage();
6535
+ resetSafeSpawnWindowsCommandCache();
6536
+ resetDispatchAvailabilityState();
6537
+ resetObservedRunnerLatency();
6538
+ resetPendingRunnerFindings();
6539
+ resetSpawnTimeoutCooldowns();
6540
+ clearFormatterCache();
6541
+ resetZizmorTokenAvailability();
6542
+ resetGoAvailability();
6543
+ resetRustAvailability();
6544
+ resetPsScriptAnalyzerAvailability();
6545
+ resetInstallRetryLatches();
6546
+ resetLazyInstallAttempts();
6547
+ resetDirectLspCommandAvailability();
6548
+ resetLSPCaseSensitivityState();
6549
+ resetResolvedPathCache();
6550
+ _resetPackageManagerCache();
6551
+ resetManagedToolRefreshSession();
6552
+ resetSmellsSessionState();
6553
+ resetSituationalToolTelemetry();
6554
+ resetWorkspaceDiagnosticsCacheSession(sessionStartMs);
6555
+ }
6302
6556
  function readSubstituteGuidance(isEnabled) {
6303
6557
  const units = [];
6304
6558
  if (isEnabled("symbol_search")) {
@@ -6410,8 +6664,8 @@ async function handleSessionStart(deps) {
6410
6664
  try {
6411
6665
  warmupDbg("warmup: starting background warmup");
6412
6666
  const scanContextStartedAt = Date.now();
6413
- const startupScanModule = await import("../chunk-ZHFZWTZV.js");
6414
- const languageProfileModule = await import("../chunk-R3PCY3YS.js");
6667
+ const startupScanModule = await import("../chunk-GSLNCYGG.js");
6668
+ const languageProfileModule = await import("../chunk-AMERCH6V.js");
6415
6669
  const warmupSnapshotRoot = resolveSnapshotRoot(warmupCwd);
6416
6670
  const cachedSnapshot = loadProjectSnapshot(warmupSnapshotRoot);
6417
6671
  warmupOwnSnapshotRead = cachedSnapshot ? {
@@ -6545,32 +6799,7 @@ async function handleSessionStart(deps) {
6545
6799
  clearAllSessions();
6546
6800
  runtime.complexityBaselines.clear();
6547
6801
  resetDispatchBaselines2(ctxCwd);
6548
- resetWorkspaceTopology();
6549
- resetOpaqueMutationState();
6550
- resetObservedMutationNet();
6551
- resetMutationAttribution();
6552
- primePersistedMutationAttribution(ctxCwd);
6553
- resetPendingAuxiliaryCoverage();
6554
- resetSafeSpawnWindowsCommandCache();
6555
- resetDispatchAvailabilityState();
6556
- resetObservedRunnerLatency();
6557
- resetPendingRunnerFindings();
6558
- resetSpawnTimeoutCooldowns();
6559
- clearFormatterCache();
6560
- resetZizmorTokenAvailability();
6561
- resetGoAvailability();
6562
- resetRustAvailability();
6563
- resetPsScriptAnalyzerAvailability();
6564
- resetInstallRetryLatches();
6565
- resetLazyInstallAttempts();
6566
- resetDirectLspCommandAvailability();
6567
- resetLSPCaseSensitivityState();
6568
- resetResolvedPathCache();
6569
- _resetPackageManagerCache();
6570
- resetManagedToolRefreshSession();
6571
- resetSmellsSessionState();
6572
- resetSituationalToolTelemetry();
6573
- resetWorkspaceDiagnosticsCacheSession(sessionStartMs);
6802
+ resetSessionScopedState(ctxCwd, sessionStartMs);
6574
6803
  deps.bootstrap.peek()?.knipClient.resetSessionState?.();
6575
6804
  resetCascadeTierSessionState();
6576
6805
  runtime.resetForSession(sessionStartMs);
@@ -6957,10 +7186,8 @@ async function handleSessionStart(deps) {
6957
7186
  dbg("session_start: no language defaults selected for pre-install");
6958
7187
  }
6959
7188
  const startupScansEnabled = deps.projectConfig?.startup?.scans?.enabled ?? deps.globalConfig?.startup?.scans?.enabled ?? true;
6960
- const startupScansWillRun = allowBootstrapTasks && startupScan.canWarmCaches && startupScansEnabled;
6961
- const jstsHeavyScansWillRun = startupScansWillRun && canRunStartupHeavyScans(languageProfile, "jsts");
6962
7189
  if (allowBootstrapTasks) {
6963
- scheduleDeferredToolProbes(deps, languageProfile, startupDefaults, jstsHeavyScansWillRun, dbg);
7190
+ scheduleDeferredToolProbes(deps, languageProfile, startupDefaults, dbg);
6964
7191
  scheduleManagedToolRefresh(dbg);
6965
7192
  }
6966
7193
  if (allowBootstrapTasks) {
@@ -7331,6 +7558,7 @@ async function buildActionableWarningsReport(args) {
7331
7558
  const cwd = path15.resolve(args.cwd);
7332
7559
  const records = [...args.dispatchWarnings];
7333
7560
  const lspService = getLSPService();
7561
+ const excludeServerIds = args.getFlag ? configDisabledAuxiliaryLspServerIds(args.getFlag) : void 0;
7334
7562
  const observedAtByPath = /* @__PURE__ */ new Map();
7335
7563
  const buildStartedAt = (/* @__PURE__ */ new Date()).toISOString();
7336
7564
  logActionableWarningsEvent({
@@ -7400,6 +7628,7 @@ async function buildActionableWarningsReport(args) {
7400
7628
  const pullTimeoutMs = boundedNumber(args.lspPullTimeoutMs, ACTIONABLE_WARNINGS_LSP_PULL_TIMEOUT_MS);
7401
7629
  const inBandDeps = {
7402
7630
  lspService,
7631
+ excludeServerIds,
7403
7632
  pullTimeoutMs,
7404
7633
  // #2504 review round 4 (F2). This is the AWAITED hook: the batch
7405
7634
  // budget has to bound what happens INSIDE a file, not only how many
@@ -7447,6 +7676,7 @@ async function buildActionableWarningsReport(args) {
7447
7676
  const loopSignal = combineAbortSignals(args.signal, deferredSignal) ?? deferredSignal;
7448
7677
  const deferredDeps = {
7449
7678
  lspService,
7679
+ excludeServerIds,
7450
7680
  pullTimeoutMs,
7451
7681
  signal: loopSignal,
7452
7682
  // OFF the hook by construction: this loop is the answer to
@@ -7561,7 +7791,7 @@ async function enrichFileFromLsp(cwd, args, target, deps) {
7561
7791
  return out;
7562
7792
  }
7563
7793
  }
7564
- const pulled = await boundedLspCall(() => lspService.getDiagnostics(filePath), deps);
7794
+ const pulled = await boundedLspCall(() => lspService.getDiagnostics(filePath, "full", deps.excludeServerIds), deps);
7565
7795
  if (pulled === void 0) {
7566
7796
  logActionableWarningsEvent({
7567
7797
  event: "lsp_file_skipped",
@@ -8118,8 +8348,8 @@ var MAX_FAILURE_TRACKER_SIZE = 200;
8118
8348
  var recentOldTextFailures = new BoundedFifoMap(MAX_FAILURE_TRACKER_SIZE);
8119
8349
 
8120
8350
  // dist/clients/deferred-runner-blockers.js
8121
- import { createHash as createHash3 } from "node:crypto";
8122
8351
  import * as fs14 from "node:fs";
8352
+ import { resolve as resolve9 } from "node:path";
8123
8353
  function judgeDeferredRunnerFindings(pending2, cwd, options = {}) {
8124
8354
  const findings = pending2.result?.diagnostics ?? [];
8125
8355
  if (findings.length === 0) {
@@ -8160,7 +8390,7 @@ function judgeDeferredRunnerFindings(pending2, cwd, options = {}) {
8160
8390
  };
8161
8391
  }
8162
8392
  function recordDeferredRunnerBlockers(runtime, pending2, survivors, bytes) {
8163
- const blocking = survivors.flatMap((d) => d.semantic === "blocking" ? [d] : []);
8393
+ const blocking = stampLineHashes(withoutLaneCopies(runtime, pending2, survivors).flatMap((d) => d.semantic === "blocking" ? [{ ...d, runnerId: d.runnerId ?? pending2.runnerId }] : []), bytes?.toString("utf-8"), (d) => !d.filePath || resolve9(d.filePath) === resolve9(pending2.filePath));
8164
8394
  if (blocking.length === 0)
8165
8395
  return 0;
8166
8396
  const recorded = runtime.recordDeferredInlineBlockers(pending2.filePath, blocking, {
@@ -8168,7 +8398,7 @@ function recordDeferredRunnerBlockers(runtime, pending2, survivors, bytes) {
8168
8398
  ...bytes ? {
8169
8399
  contentBaseline: {
8170
8400
  size: bytes.byteLength,
8171
- sha256: createHash3("sha256").update(bytes).digest("hex")
8401
+ sha256: hashContentBytes(bytes)
8172
8402
  }
8173
8403
  } : {}
8174
8404
  });
@@ -8176,6 +8406,36 @@ function recordDeferredRunnerBlockers(runtime, pending2, survivors, bytes) {
8176
8406
  runtime.updateGitGuardStatus(false, "");
8177
8407
  return recorded;
8178
8408
  }
8409
+ function retireSettledRunnerBlockers(runtime, pending2, verdict) {
8410
+ const result = pending2.result;
8411
+ if (!result || reachedNoVerdict(result) || result.coveredBy !== void 0 && result.skipReason === "covered-by-primary")
8412
+ return 0;
8413
+ const bytes = readBytes(pending2.filePath);
8414
+ const retired = runtime.retireUnobservedRunnerBlockers(pending2.filePath, {
8415
+ runnerId: pending2.runnerId,
8416
+ // #4484 round 3: what the run's edit-range gate withheld is still
8417
+ // observed, at its own line.
8418
+ observed: [
8419
+ ...withoutLaneCopies(runtime, pending2, verdict.kept),
8420
+ ...result.outOfEditScope ?? []
8421
+ ].flatMap((d) => d.semantic === "blocking" ? [d] : []),
8422
+ markedAtMs: pending2.markedAtMs,
8423
+ sha256: bytes ? hashContentBytes(bytes) : void 0,
8424
+ ...result.unconfirmedServerIds !== void 0 && {
8425
+ unconfirmedServerIds: result.unconfirmedServerIds
8426
+ }
8427
+ });
8428
+ if (retired > 0)
8429
+ runtime.updateGitGuardStatus(false, "");
8430
+ return retired;
8431
+ }
8432
+ function withoutLaneCopies(runtime, pending2, findings) {
8433
+ const lane = pending2.result?.coveredBy;
8434
+ if (lane === void 0)
8435
+ return findings;
8436
+ const standsFor = laneCopyStandsFor((runtime.getInlineBlockerDiagnostics(pending2.filePath) ?? []).flatMap((d) => d.runnerId === lane.runnerId && (lane.serverId === void 0 || d.serverId === lane.serverId) ? [d] : []));
8437
+ return findings.flatMap((d) => standsFor(d) ? [] : [d]);
8438
+ }
8179
8439
  function readBytes(filePath) {
8180
8440
  try {
8181
8441
  return fs14.readFileSync(filePath);
@@ -8837,7 +9097,7 @@ function stillReportedParked(parked, current, keyOf, offeredKeys) {
8837
9097
  }
8838
9098
 
8839
9099
  // dist/clients/knip-scan-roots.js
8840
- import { resolve as resolve13 } from "node:path";
9100
+ import { resolve as resolve14 } from "node:path";
8841
9101
  var MAX_KNIP_ROOTS_PER_TURN = 3;
8842
9102
  function resolveKnipScanRoots(sessionCwd, modifiedFiles) {
8843
9103
  const session = resolveGitCheckout(sessionCwd);
@@ -8847,7 +9107,7 @@ function resolveKnipScanRoots(sessionCwd, modifiedFiles) {
8847
9107
  let sessionOwnsEdit = false;
8848
9108
  const linked = [];
8849
9109
  for (const file of modifiedFiles) {
8850
- const owner = resolveLinkedWorktreeOwner(session, resolve13(sessionCwd, file));
9110
+ const owner = resolveLinkedWorktreeOwner(session, resolve14(sessionCwd, file));
8851
9111
  if (owner === null) {
8852
9112
  sessionOwnsEdit = true;
8853
9113
  } else if (!linked.some((known) => pathsEqual(known.root, owner.root))) {
@@ -9109,7 +9369,7 @@ function selectPersistentReverifyFiles(report, nowMs = Date.now()) {
9109
9369
  } catch {
9110
9370
  continue;
9111
9371
  }
9112
- if (mtimeMs > stamp) {
9372
+ if (freshnessFromMtime({ mtimeMs, referenceMs: stamp, toleranceMs: 0 }).verdict === "stale") {
9113
9373
  skippedChanged += 1;
9114
9374
  continue;
9115
9375
  }
@@ -9500,6 +9760,10 @@ function lateDeadCodeScansOf(scope) {
9500
9760
  function joinedEarlierScan(result, askedAt) {
9501
9761
  return result.scannedAt !== void 0 && Date.parse(result.scannedAt) < askedAt;
9502
9762
  }
9763
+ var KNIP_CARRIED_FILES = "knip-carried-files";
9764
+ function knipCarriedFilesOf(scope) {
9765
+ return scopeCell(scope, KNIP_CARRIED_FILES, () => /* @__PURE__ */ new Map());
9766
+ }
9503
9767
  function withRowForFailureWrite(scan, cacheManager2, cacheKey, cwd, startRow) {
9504
9768
  return scan.then((result) => result.success || startRow?.data.success === true ? { result, row: startRow } : cacheManager2.readCacheAsync(cacheKey, cwd).then((row) => ({ result, row })));
9505
9769
  }
@@ -9843,6 +10107,7 @@ async function handleTurnEnd(deps) {
9843
10107
  let runnerFindingsFailed = 0;
9844
10108
  let runnerFindingsDropped = 0;
9845
10109
  let runnerBlockersRecorded = 0;
10110
+ let runnerBlockersRetired = 0;
9846
10111
  let runnerFindingsDispositionSuppressed = 0;
9847
10112
  const runnerFindingsDeliveredIds = [];
9848
10113
  for (const pending2 of pendingRunnerFindings) {
@@ -9869,6 +10134,7 @@ async function handleTurnEnd(deps) {
9869
10134
  dropStaleRunnerFindings(pending2);
9870
10135
  runnerFindingsDropped += 1;
9871
10136
  }
10137
+ runnerBlockersRetired += retireSettledRunnerBlockers(runtime, pending2, verdict);
9872
10138
  if (verdict.live === 0)
9873
10139
  continue;
9874
10140
  const displayPath = toRunnerDisplayPath(cwd, pending2.filePath);
@@ -9914,6 +10180,7 @@ ${lines.join("\n")}`;
9914
10180
  failed: runnerFindingsFailed,
9915
10181
  dropped: runnerFindingsDropped,
9916
10182
  blockersRecorded: runnerBlockersRecorded,
10183
+ blockersRetired: runnerBlockersRetired,
9917
10184
  dispositionSuppressed: runnerFindingsDispositionSuppressed,
9918
10185
  deliveredIds: runnerFindingsDeliveredIds
9919
10186
  }
@@ -10080,6 +10347,7 @@ ${policy.body}`);
10080
10347
  durationMs: Date.now() - cascadeSettleStart,
10081
10348
  metadata: { settled, timedOut }
10082
10349
  });
10350
+ peekOutstandingCascadeTouches(holdScope);
10083
10351
  const t0 = Date.now();
10084
10352
  const cascadeRuns = (holdScope.guardedWrite("turn-end:cascade-runs", () => runtime.consumeCascadeRuns()) ?? []).filter((run) => {
10085
10353
  const originSeq = run.origin?.projectSeq;
@@ -10132,40 +10400,91 @@ ${policy.body}`);
10132
10400
  }
10133
10401
  }
10134
10402
  const cascadeLogFilePath = cascadeResults[0]?.filePath ?? files[0] ?? cwd;
10135
- if (cascadeResults.length > 0) {
10136
- const seen = /* @__PURE__ */ new Map();
10137
- for (const result of cascadeResults) {
10138
- seen.set(normalizeMapKey(result.filePath), result);
10139
- }
10140
- const neighborOwner = /* @__PURE__ */ new Map();
10141
- for (const result of [...seen.values()].reverse()) {
10142
- const pk = normalizeMapKey(result.filePath);
10143
- for (const n of result.neighbors) {
10144
- const nk = normalizeMapKey(n.filePath);
10145
- if (!neighborOwner.has(nk))
10146
- neighborOwner.set(nk, pk);
10147
- }
10403
+ const cascadeTurn = runtime.turnIndex;
10404
+ const editedKeys = /* @__PURE__ */ new Set();
10405
+ for (const file of files) {
10406
+ const absolute = path24.resolve(cwd, file);
10407
+ editedKeys.add(normalizeMapKey(absolute));
10408
+ forgetCascadePendingNeighbor(absolute);
10409
+ }
10410
+ const seen = /* @__PURE__ */ new Map();
10411
+ for (const result of cascadeResults) {
10412
+ seen.set(normalizeMapKey(result.filePath), result);
10413
+ }
10414
+ const keptResults = new Set(seen.values());
10415
+ const lastObserver = /* @__PURE__ */ new Map();
10416
+ for (const run of [...cascadeRuns].reverse()) {
10417
+ if (run.result && !keptResults.has(run.result))
10418
+ continue;
10419
+ for (const neighborPath of [
10420
+ ...(run.result?.neighbors ?? []).map((n) => n.filePath),
10421
+ ...run.observedNeighborPaths ?? []
10422
+ ]) {
10423
+ const nk = normalizeMapKey(neighborPath);
10424
+ if (!lastObserver.has(nk))
10425
+ lastObserver.set(nk, { run, filePath: neighborPath });
10148
10426
  }
10427
+ }
10428
+ for (const [nk, { run, filePath }] of lastObserver) {
10429
+ const observedClean = run.observedNeighborPaths?.some((p) => normalizeMapKey(p) === nk) && !run.result?.neighbors.some((n) => normalizeMapKey(n.filePath) === nk && n.diagnostics.length > 0);
10430
+ if (observedClean)
10431
+ forgetCascadePendingNeighbor(filePath);
10432
+ }
10433
+ if (cascadeResults.length > 0) {
10149
10434
  const parts = [];
10150
10435
  const sectionRuns = [];
10151
10436
  let injectedNeighborCount = 0;
10152
10437
  let injectedDiagnosticCount = 0;
10153
10438
  let suppressedByOwnership = 0;
10439
+ const sectionObservations = [];
10440
+ const shownUnansweredServerIds = /* @__PURE__ */ new Set();
10441
+ const unansweredLines = [];
10154
10442
  for (const result of seen.values()) {
10155
- const pk = normalizeMapKey(result.filePath);
10156
- const ownsAny = result.neighbors.some((n) => neighborOwner.get(normalizeMapKey(n.filePath)) === pk);
10157
- if (ownsAny && result.formatted) {
10158
- const carryMeta = carriedMetaByResult.get(result);
10159
- const carrySuffix = cascadeCarrySuffix(carryMeta?.carriedTurns, carryMeta?.observedAt);
10160
- parts.push(carrySuffix ? `${result.formatted}
10161
- ${carrySuffix}` : result.formatted);
10162
- if (carrySuffix)
10163
- carriedRunsRendered += 1;
10443
+ const ownedNeighbors = result.neighbors.filter((n) => lastObserver.get(normalizeMapKey(n.filePath))?.run.result === result);
10444
+ const ownsAny = ownedNeighbors.length > 0;
10445
+ let rendered = ownsAny && result.formatted ? renderCascadeResult(result, shownUnansweredServerIds) : void 0;
10446
+ const layout = result.layout;
10447
+ if (rendered && layout) {
10448
+ const since = (n, d) => cascadeStillPresentSince(n.filePath, d, cascadeTurn);
10449
+ const relabel = ownedNeighbors.some((n) => n.diagnostics.some((d) => since(n, d) !== void 0));
10450
+ if (relabel || ownedNeighbors.length < result.neighbors.length) {
10451
+ const block = formatCascadeNeighborDiagnostics(layout.cwd, ownedNeighbors, {
10452
+ noun: layout.noun,
10453
+ includeReason: layout.includeReason,
10454
+ stillPresentSince: since
10455
+ });
10456
+ rendered = {
10457
+ ...rendered,
10458
+ text: composeCascadeSection(block, layout)
10459
+ };
10460
+ }
10461
+ }
10462
+ if (rendered && (rendered.text || rendered.lines.length > 0)) {
10463
+ unansweredLines.push(...rendered.lines);
10464
+ if (rendered.text) {
10465
+ const carryMeta = carriedMetaByResult.get(result);
10466
+ const carrySuffix = cascadeCarrySuffix(carryMeta?.carriedTurns, carryMeta?.observedAt);
10467
+ parts.push(carrySuffix ? `${rendered.text}
10468
+ ${carrySuffix}` : rendered.text);
10469
+ if (carrySuffix)
10470
+ carriedRunsRendered += 1;
10471
+ }
10164
10472
  const sectionRun = runByResult.get(result);
10165
10473
  if (sectionRun)
10166
10474
  sectionRuns.push(sectionRun);
10167
- injectedNeighborCount += result.neighbors.length;
10168
- injectedDiagnosticCount += result.neighbors.reduce((s, n) => s + n.diagnostics.length, 0);
10475
+ for (const n of ownedNeighbors) {
10476
+ if (n.diagnostics.length === 0)
10477
+ continue;
10478
+ if (editedKeys.has(normalizeMapKey(n.filePath)))
10479
+ continue;
10480
+ sectionObservations.push({
10481
+ filePath: n.filePath,
10482
+ diagnostics: n.diagnostics,
10483
+ checkedAt: sectionRun?.checkedAt
10484
+ });
10485
+ }
10486
+ injectedNeighborCount += ownedNeighbors.length;
10487
+ injectedDiagnosticCount += ownedNeighbors.reduce((s, n) => s + n.diagnostics.length, 0);
10169
10488
  } else if (!ownsAny && result.formatted) {
10170
10489
  suppressedByOwnership++;
10171
10490
  }
@@ -10199,11 +10518,20 @@ ${testLines.join("\n")}`;
10199
10518
  parts.push(testSection);
10200
10519
  }
10201
10520
  }
10521
+ if (unansweredLines.length > 0)
10522
+ parts.unshift(unansweredLines.join("\n"));
10202
10523
  if (parts.length > 0) {
10203
10524
  const section = parts.join("\n\n");
10204
10525
  blockerParts.push(section);
10526
+ const shownServerIds = [...shownUnansweredServerIds];
10205
10527
  deliveryHolds.push({
10206
10528
  part: section,
10529
+ onDelivered: () => {
10530
+ markCascadeUnansweredPrimariesDelivered(shownServerIds);
10531
+ for (const o of sectionObservations) {
10532
+ recordDeliveredCascadeNeighbor(o.filePath, o.diagnostics, cascadeTurn, o.checkedAt ?? 0);
10533
+ }
10534
+ },
10207
10535
  onHeld: () => {
10208
10536
  for (const run of sectionRuns)
10209
10537
  runtime.appendCascadeRun(run);
@@ -10232,6 +10560,124 @@ ${testLines.join("\n")}`;
10232
10560
  }
10233
10561
  });
10234
10562
  }
10563
+ const pendingCandidates = listCascadePendingNeighbors().filter((entry) => {
10564
+ const key = normalizeMapKey(entry.filePath);
10565
+ if (lastObserver.has(key) || editedKeys.has(key))
10566
+ return false;
10567
+ return isUnderDir(entry.filePath, cwd);
10568
+ });
10569
+ const { "cascade-pending-neighbors": pendingGate } = gateFindingsByPathFreshness({
10570
+ cwd,
10571
+ quiet: true,
10572
+ sources: {
10573
+ "cascade-pending-neighbors": {
10574
+ findings: pendingCandidates,
10575
+ citedPath: (entry) => entry.filePath,
10576
+ scannedAtOf: (entry) => entry.checkedAt
10577
+ }
10578
+ }
10579
+ });
10580
+ const unresolvedNeighbors = pendingGate.live;
10581
+ const changedNeighbors = pendingGate.stale;
10582
+ const gateKept = /* @__PURE__ */ new Set([...unresolvedNeighbors, ...changedNeighbors]);
10583
+ const deletedNeighbors = [];
10584
+ for (const entry of pendingCandidates) {
10585
+ if (gateKept.has(entry))
10586
+ continue;
10587
+ deletedNeighbors.push(entry);
10588
+ forgetCascadePendingNeighbor(entry.filePath);
10589
+ }
10590
+ const maxShown = RUNTIME_CONFIG.pipeline.cascadeMaxFiles;
10591
+ const pendingAge = (checkedAt) => formatCacheAgeLabel(checkedAt > 0 ? checkedAt : void 0);
10592
+ const pendingBlocks = [];
10593
+ let unresolvedShown = 0;
10594
+ if (unresolvedNeighbors.length > 0) {
10595
+ const lines = unresolvedNeighbors.slice(-maxShown).reverse().map((entry) => ` \u2022 ${toRunnerDisplayPath(cwd, entry.filePath)}: ${entry.errorCount} error(s) unresolved (${pendingAge(entry.checkedAt)})`);
10596
+ unresolvedShown = lines.length;
10597
+ const more = unresolvedNeighbors.length - lines.length;
10598
+ if (more > 0)
10599
+ lines.push(` ... and ${more} more neighbor file(s)`);
10600
+ pendingBlocks.push(`\u{1F4D0} Cascade errors from an earlier turn, not re-checked this turn \u2014 still unresolved until a cascade or an edit shows them gone:
10601
+ ${lines.join("\n")}`);
10602
+ }
10603
+ const changedShown = changedNeighbors.slice(-maxShown).reverse();
10604
+ if (changedShown.length > 0) {
10605
+ const lines = changedShown.map((entry) => ` \u2022 ${toRunnerDisplayPath(cwd, entry.filePath)}: ${entry.errorCount} error(s) changed since checked (${pendingAge(entry.checkedAt)}), not re-checked`);
10606
+ const more = changedNeighbors.length - changedShown.length;
10607
+ if (more > 0)
10608
+ lines.push(` ... and ${more} more neighbor file(s)`);
10609
+ pendingBlocks.push(`\u{1F4D0} Cascade errors from an earlier turn whose file changed outside pi since they were checked \u2014 not re-checked, named this once:
10610
+ ${lines.join("\n")}`);
10611
+ }
10612
+ if (pendingBlocks.length > 0) {
10613
+ const pendingSection = pendingBlocks.join("\n");
10614
+ blockerParts.push(pendingSection);
10615
+ if (changedShown.length > 0) {
10616
+ deliveryHolds.push({
10617
+ part: pendingSection,
10618
+ onDelivered: () => {
10619
+ for (const entry of changedShown) {
10620
+ forgetCascadePendingNeighbor(entry.filePath);
10621
+ }
10622
+ }
10623
+ });
10624
+ }
10625
+ logCascade({
10626
+ phase: "cascade_pending_rendered",
10627
+ filePath: cascadeLogFilePath,
10628
+ metadata: {
10629
+ neighbors: unresolvedNeighbors.length,
10630
+ shown: unresolvedShown,
10631
+ changed: changedShown.length
10632
+ }
10633
+ });
10634
+ }
10635
+ if (changedNeighbors.length + deletedNeighbors.length > 0) {
10636
+ logCascade({
10637
+ phase: "cascade_pending_gated",
10638
+ filePath: cascadeLogFilePath,
10639
+ metadata: {
10640
+ changed: changedNeighbors.length,
10641
+ deleted: deletedNeighbors.length,
10642
+ changedPaths: changedNeighbors.slice(0, 10).map((e) => e.filePath),
10643
+ deletedPaths: deletedNeighbors.slice(0, 10).map((e) => e.filePath)
10644
+ }
10645
+ });
10646
+ }
10647
+ const withCarryLabel = (advisory, runs) => {
10648
+ if (advisory === void 0)
10649
+ return void 0;
10650
+ const carried = runs.filter((r) => (r.carriedTurns ?? 0) > 0);
10651
+ if (carried.length === 0 || carried.length !== runs.length) {
10652
+ return advisory;
10653
+ }
10654
+ const stamps = carried.map((r) => r.observedAt);
10655
+ const observedAt = stamps.every((s) => s !== void 0) ? Math.min(...stamps) : void 0;
10656
+ const suffix = cascadeCarrySuffix(carried[0]?.carriedTurns, observedAt);
10657
+ if (!suffix)
10658
+ return advisory;
10659
+ labeledAdvisories += 1;
10660
+ return `${advisory}
10661
+ ${suffix}`;
10662
+ };
10663
+ const holdAdvisory = (advisory, runs, keep) => {
10664
+ if (advisory === void 0)
10665
+ return;
10666
+ deliveryHolds.push({
10667
+ part: advisory,
10668
+ onHeld: () => {
10669
+ for (const run of runs) {
10670
+ const restored = { ...run, result: void 0 };
10671
+ delete restored.observedNeighborPaths;
10672
+ if (keep !== "indeterminate")
10673
+ delete restored.indeterminate;
10674
+ if (keep !== "unconfirmed")
10675
+ delete restored.unconfirmed;
10676
+ runtime.appendCascadeRun(restored);
10677
+ }
10678
+ }
10679
+ });
10680
+ };
10235
10681
  const indeterminateRuns = cascadeRuns.filter((r) => r.indeterminate);
10236
10682
  if (indeterminateRuns.length > 0) {
10237
10683
  const buildAdvisory = (runs, frame) => {
@@ -10259,47 +10705,20 @@ ${lines.join("\n")}`;
10259
10705
  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);
10260
10706
  const bindingRuns = indeterminateRuns.filter((r) => r.indeterminate?.reason === "lsp_binding_rejected" && r.indeterminate?.budget === void 0);
10261
10707
  const budgetRuns = indeterminateRuns.filter((r) => r.indeterminate?.reason === "budget_truncated" || r.indeterminate?.budget !== void 0);
10262
- const withCarryLabel = (advisory, runs) => {
10263
- if (advisory === void 0)
10264
- return void 0;
10265
- const carried = runs.filter((r) => (r.carriedTurns ?? 0) > 0);
10266
- if (carried.length === 0 || carried.length !== runs.length) {
10267
- return advisory;
10268
- }
10269
- const stamps = carried.map((r) => r.observedAt);
10270
- const observedAt = stamps.every((s) => s !== void 0) ? Math.min(...stamps) : void 0;
10271
- const suffix = cascadeCarrySuffix(carried[0]?.carriedTurns, observedAt);
10272
- if (!suffix)
10273
- return advisory;
10274
- labeledAdvisories += 1;
10275
- return `${advisory}
10276
- ${suffix}`;
10277
- };
10278
- const holdAdvisory = (advisory, runs) => {
10279
- if (advisory === void 0)
10280
- return;
10281
- deliveryHolds.push({
10282
- part: advisory,
10283
- onHeld: () => {
10284
- for (const run of runs)
10285
- runtime.appendCascadeRun({ ...run, result: void 0 });
10286
- }
10287
- });
10288
- };
10289
10708
  const graphAdvisory = withCarryLabel(buildAdvisory(graphRuns, {
10290
10709
  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.`,
10291
10710
  fallbackDetail: (r) => r.indeterminate?.reason === "missing_node" ? "changed file not in the review graph" : "review graph unavailable"
10292
10711
  }), graphRuns);
10293
10712
  if (graphAdvisory)
10294
10713
  advisoryParts.push(graphAdvisory);
10295
- holdAdvisory(graphAdvisory, graphRuns);
10714
+ holdAdvisory(graphAdvisory, graphRuns, "indeterminate");
10296
10715
  const bindingAdvisory = withCarryLabel(buildAdvisory(bindingRuns, {
10297
10716
  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.`,
10298
10717
  fallbackDetail: () => "cascade diagnostics withheld (binding rejected)"
10299
10718
  }), bindingRuns);
10300
10719
  if (bindingAdvisory)
10301
10720
  advisoryParts.push(bindingAdvisory);
10302
- holdAdvisory(bindingAdvisory, bindingRuns);
10721
+ holdAdvisory(bindingAdvisory, bindingRuns, "indeterminate");
10303
10722
  const budgetAdvisory = withCarryLabel(buildAdvisory(budgetRuns, {
10304
10723
  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.`,
10305
10724
  fallbackDetail: (r) => {
@@ -10312,7 +10731,7 @@ ${suffix}`;
10312
10731
  }), budgetRuns);
10313
10732
  if (budgetAdvisory)
10314
10733
  advisoryParts.push(budgetAdvisory);
10315
- holdAdvisory(budgetAdvisory, budgetRuns);
10734
+ holdAdvisory(budgetAdvisory, budgetRuns, "indeterminate");
10316
10735
  const fileCount = new Set(indeterminateRuns.map((r) => normalizeMapKey(r.filePath))).size;
10317
10736
  const byFile = indeterminateRuns.map((r) => ({
10318
10737
  file: toRunnerDisplayPath(cwd, r.filePath),
@@ -10336,6 +10755,57 @@ ${suffix}`;
10336
10755
  }
10337
10756
  });
10338
10757
  }
10758
+ const unconfirmedRuns = cascadeRuns.filter((r) => r.unconfirmed);
10759
+ if (unconfirmedRuns.length > 0) {
10760
+ const runOrder = new Map(cascadeRuns.map((r, i) => [r, i]));
10761
+ const byServer = /* @__PURE__ */ new Map();
10762
+ for (const run of unconfirmedRuns) {
10763
+ for (const group of run.unconfirmed?.servers ?? []) {
10764
+ for (const neighborPath of group.paths) {
10765
+ const nk = normalizeMapKey(neighborPath);
10766
+ if (editedKeys.has(nk))
10767
+ continue;
10768
+ const observer = lastObserver.get(nk)?.run;
10769
+ if (observer && (runOrder.get(observer) ?? -1) > (runOrder.get(run) ?? -1))
10770
+ continue;
10771
+ const bucket = byServer.get(group.serverId) ?? {
10772
+ waitMs: group.waitMs,
10773
+ primaries: /* @__PURE__ */ new Set(),
10774
+ paths: /* @__PURE__ */ new Map()
10775
+ };
10776
+ bucket.waitMs = Math.max(bucket.waitMs, group.waitMs);
10777
+ bucket.primaries.add(toRunnerDisplayPath(cwd, run.filePath));
10778
+ bucket.paths.set(nk, toRunnerDisplayPath(cwd, neighborPath));
10779
+ byServer.set(group.serverId, bucket);
10780
+ }
10781
+ }
10782
+ }
10783
+ const capped = (items, max) => items.length > max ? `${items.slice(0, max).join(", ")} (+${items.length - max} more)` : items.join(", ");
10784
+ const blocks = [];
10785
+ let pathCount = 0;
10786
+ for (const [serverId, bucket] of byServer) {
10787
+ const paths = [...bucket.paths.values()];
10788
+ pathCount += paths.length;
10789
+ const within = bucket.waitMs > 0 ? ` within ${bucket.waitMs} ms` : "";
10790
+ blocks.push(`Cascade sent ${paths.length} dependent(s) of ${capped([...bucket.primaries], 3)} to ${serverId}, but no answer arrived${within}; a clean cascade result does not cover them.
10791
+ \u2022 ${serverId}: ${capped(paths, 5)}`);
10792
+ }
10793
+ if (blocks.length > 0) {
10794
+ const pendingAdvisory = withCarryLabel(blocks.join("\n"), unconfirmedRuns);
10795
+ if (pendingAdvisory)
10796
+ advisoryParts.push(pendingAdvisory);
10797
+ holdAdvisory(pendingAdvisory, unconfirmedRuns, "unconfirmed");
10798
+ logCascade({
10799
+ phase: "cascade_unconfirmed_rendered",
10800
+ filePath: cascadeLogFilePath,
10801
+ metadata: {
10802
+ servers: byServer.size,
10803
+ paths: pathCount,
10804
+ runs: unconfirmedRuns.length
10805
+ }
10806
+ });
10807
+ }
10808
+ }
10339
10809
  if (carriedRunsRendered > 0 || labeledAdvisories > 0) {
10340
10810
  logCascade({
10341
10811
  phase: "cascade_carry_rendered",
@@ -10369,6 +10839,7 @@ ${suffix}`;
10369
10839
  skipped: cascadeSkipped
10370
10840
  }
10371
10841
  });
10842
+ const knipCarried = knipCarriedFilesOf(turnScope);
10372
10843
  const knipScan = resolveKnipScanRoots(cwd, files);
10373
10844
  for (const root of knipScan.overCap) {
10374
10845
  incrementDegradationCount({
@@ -10379,7 +10850,7 @@ ${suffix}`;
10379
10850
  }
10380
10851
  const KNOWN_SLOW_KNIP_GRACE_MS = 1;
10381
10852
  const KNIP_MAX_SHOWN = 5;
10382
- const applyKnipResult = (scanRoot, prevKnip, knipResult, rowNow) => {
10853
+ const applyKnipResult = (scanRoot, prevKnip, knipResult, rowNow, joined) => {
10383
10854
  const knipWouldPoison = wouldPoisonCache(rowNow, knipResult);
10384
10855
  if (knipWouldPoison) {
10385
10856
  dbg(`turn_end: keeping last good knip cache; this run failed: ${knipResult.summary}`);
@@ -10412,7 +10883,7 @@ ${suffix}`;
10412
10883
  if (knipResult.success && knipResult.issues.length > 0) {
10413
10884
  const issueKey = (i) => stableFindingKey(i.type, i.file, i.name, i.package);
10414
10885
  const prevKeys = new Set((prevKnip?.data?.issues ?? []).map(issueKey));
10415
- const modifiedSet = new Set(files.map((f) => resolveRunnerPath(cwd, f)));
10886
+ const modifiedSet = new Set([...files, ...knipCarried.get(scanRoot) ?? []].map((f) => resolveRunnerPath(cwd, f)));
10416
10887
  const newIssues = knipResult.issues.filter((issue) => {
10417
10888
  if (prevKeys.has(issueKey(issue)))
10418
10889
  return false;
@@ -10472,6 +10943,13 @@ ${suffix}`;
10472
10943
  deliveryHolds.push(cutAdvisoryHold("knip", knipAdvisoryLane, report, unusedExportDelta.slice(0, KNIP_MAX_SHOWN), (issue) => reOfferedKeys.has(issueKey(issue))));
10473
10944
  }
10474
10945
  }
10946
+ if (joined) {
10947
+ const carry = new Set(files);
10948
+ knipCarried.set(scanRoot, carry);
10949
+ knipMeta.joined = true;
10950
+ knipMeta.carried = carry.size;
10951
+ } else if (knipResult.success)
10952
+ knipCarried.delete(scanRoot);
10475
10953
  return knipMeta;
10476
10954
  };
10477
10955
  const logKnipRow = (root, startedAt, metadata) => logLatency({
@@ -10538,7 +11016,8 @@ ${suffix}`;
10538
11016
  });
10539
11017
  }
10540
11018
  } else {
10541
- metadata = applyKnipResult(scanRoot, prevKnip, scanned.result, scanned.row);
11019
+ const joined = scanned.result.success && scanned.result.execution !== "cache" && joinedEarlierScan(scanned.result, startedAt);
11020
+ metadata = applyKnipResult(scanRoot, prevKnip, scanned.result, scanned.row, joined);
10542
11021
  }
10543
11022
  }
10544
11023
  budgetSpent = metadata.execution === "deferred" && (metadata.scanFloorMs === void 0 || metadata.aborted === true);
@@ -10909,7 +11388,7 @@ ${suffix}`;
10909
11388
  metadata: madgeStats ?? { skipped: true }
10910
11389
  });
10911
11390
  if (!getFlag("no-tests") && files.length > 0) {
10912
- const seen = /* @__PURE__ */ new Set();
11391
+ const seen2 = /* @__PURE__ */ new Set();
10913
11392
  const targets = [];
10914
11393
  const testRootFor = createTurnEndTestRoots(cwd);
10915
11394
  const candidates = [];
@@ -10976,7 +11455,7 @@ ${suffix}`;
10976
11455
  foreignDeferred++;
10977
11456
  continue;
10978
11457
  }
10979
- if (seen.has(carriedKey)) {
11458
+ if (seen2.has(carriedKey)) {
10980
11459
  resolvedDeferralKeys.add(carriedKey);
10981
11460
  continue;
10982
11461
  }
@@ -11020,7 +11499,7 @@ ${suffix}`;
11020
11499
  continue;
11021
11500
  }
11022
11501
  resolvedDeferralKeys.add(carriedKey);
11023
- seen.add(carriedKey);
11502
+ seen2.add(carriedKey);
11024
11503
  redispatchedDeferred++;
11025
11504
  targets.push({
11026
11505
  testFile,
@@ -11071,8 +11550,8 @@ ${suffix}`;
11071
11550
  candidatesByRoot.set(testRoot, (candidatesByRoot.get(testRoot) ?? 0) + 1);
11072
11551
  const target = testRunnerClient.getTestRunTarget(abs, testRoot, runtime.turnIndex);
11073
11552
  const targetKey = target ? normalizeMapKey(target.testFile) : "";
11074
- if (target && !seen.has(targetKey)) {
11075
- seen.add(targetKey);
11553
+ if (target && !seen2.has(targetKey)) {
11554
+ seen2.add(targetKey);
11076
11555
  if (retiredKeys.has(targetKey)) {
11077
11556
  retiredSkips++;
11078
11557
  dbg(`turn_end: ${display} \u2192 test target retired earlier this session (outran the turn-end batch budget), skipping spawn (${path24.relative(cwd, target.testFile)})`);
@@ -11473,8 +11952,8 @@ ${suffix}`;
11473
11952
  } else {
11474
11953
  const callGraphStart = Date.now();
11475
11954
  try {
11476
- const { impact, formatImpact, parseSymbolKey } = await import("../chunk-BKE7E7TC.js");
11477
- const { callGraphImpactToProjectDiagnostics } = await import("../chunk-6GFRNVYR.js");
11955
+ const { impact, formatImpact, parseSymbolKey } = await import("../chunk-GMX5OS7B.js");
11956
+ const { callGraphImpactToProjectDiagnostics } = await import("../chunk-MZGQNL3Y.js");
11478
11957
  const impactLines = [];
11479
11958
  let callGraphDispositionSuppressed = 0;
11480
11959
  const impactFindings = [];
@@ -11597,6 +12076,8 @@ ${impactLines.join("\n")}`;
11597
12076
  modifiedRangesByFile,
11598
12077
  dispatchWarnings: runtime.peekActionableWarnings(sessionId),
11599
12078
  includeLspCodeActions: !!getFlag("lens-actionable-warning-actions"),
12079
+ // #4323: the cold LSP read leaves a switched-off Opengrep unstarted.
12080
+ getFlag,
11600
12081
  projectSeqStart: runtime.turnStartProjectSeq,
11601
12082
  projectSeqEnd: runtime.projectSeq,
11602
12083
  // #3676: the quick fix credits its writes with this epoch.
@@ -11715,31 +12196,7 @@ ${impactLines.join("\n")}`;
11715
12196
  }
11716
12197
  const t5 = Date.now();
11717
12198
  try {
11718
- const qualityWarningsByFile = /* @__PURE__ */ new Map();
11719
- for (const warning of runtime.peekCodeQualityWarnings(sessionId)) {
11720
- const group = qualityWarningsByFile.get(warning.filePath);
11721
- if (group)
11722
- group.push(warning);
11723
- else
11724
- qualityWarningsByFile.set(warning.filePath, [warning]);
11725
- }
11726
- const qualityWarnings = [];
11727
- let qualityDispositionSuppressed = 0;
11728
- for (const [filePath, group] of qualityWarningsByFile) {
11729
- let content;
11730
- try {
11731
- content = fs17.readFileSync(filePath, "utf-8");
11732
- } catch {
11733
- content = void 0;
11734
- }
11735
- const { kept, suppressed } = applyPushedFindingPolicy(group, {
11736
- cwd,
11737
- filePath,
11738
- content
11739
- });
11740
- qualityWarnings.push(...kept);
11741
- qualityDispositionSuppressed += suppressed;
11742
- }
12199
+ const { kept: qualityWarnings, suppressed: qualityDispositionSuppressed } = applyPushedFindingPolicyByFile(runtime.peekCodeQualityWarnings(sessionId), cwd);
11743
12200
  const qualityReport = buildCodeQualityWarningsReport({
11744
12201
  cwd,
11745
12202
  sessionId: runtime.telemetrySessionId,
@@ -12467,7 +12924,7 @@ var TurnEndQueue = class {
12467
12924
  }
12468
12925
  };
12469
12926
  var turnEndQueue = new TurnEndQueue();
12470
- var pendingTurnEndDeliveries = /* @__PURE__ */ new Map();
12927
+ var pendingTurnEndDeliveries = new BoundedFifoMap(TURN_END_DELIVERY_MAX);
12471
12928
  var inFlightIpcTurnEnds = /* @__PURE__ */ new Map();
12472
12929
  async function runTurnEndNowImpl(cwd, files = [], deferredDelivery = false) {
12473
12930
  const ctx = await getMcpSessionContext();
@@ -12545,12 +13002,6 @@ function prunePendingTurnEndDeliveries(now = Date.now()) {
12545
13002
  pendingTurnEndDeliveries.delete(deliveryId);
12546
13003
  }
12547
13004
  }
12548
- while (pendingTurnEndDeliveries.size > TURN_END_DELIVERY_MAX) {
12549
- const oldest = pendingTurnEndDeliveries.keys().next();
12550
- if (oldest.done)
12551
- break;
12552
- pendingTurnEndDeliveries.delete(oldest.value);
12553
- }
12554
13005
  }
12555
13006
  function pendingForCwd(cwd) {
12556
13007
  for (const [deliveryId, delivery] of pendingTurnEndDeliveries) {
@@ -13167,7 +13618,7 @@ function blastReadArgs(graph, normalizedFile) {
13167
13618
  return { path: normalizedFile, offset: 1, limit };
13168
13619
  }
13169
13620
  async function computeBlastRadius(graph, normalizedPath, projectRoot, maxDepth) {
13170
- const { computeTransitiveImpact } = await import("../chunk-4UNXBNE2.js");
13621
+ const { computeTransitiveImpact } = await import("../chunk-XTFDJI3T.js");
13171
13622
  const result = computeTransitiveImpact(graph, normalizedPath, { maxDepth });
13172
13623
  const byFile = /* @__PURE__ */ new Map();
13173
13624
  for (const hit of result.hits) {
@@ -13784,7 +14235,7 @@ async function moduleReport(file, cwd, options) {
13784
14235
  let callGraphIdentity;
13785
14236
  let revisionDriftNote;
13786
14237
  try {
13787
- const { getCachedReviewGraph, getReviewGraphCacheIdentity, getReviewGraphSizeSkipVerdict, getReviewGraphRevisionDrift, formatReviewGraphRevisionDriftNote: formatReviewGraphRevisionDriftNote2 } = await import("../chunk-2ZGIZPDG.js");
14238
+ const { getCachedReviewGraph, getReviewGraphCacheIdentity, getReviewGraphSizeSkipVerdict, getReviewGraphRevisionDrift, formatReviewGraphRevisionDriftNote: formatReviewGraphRevisionDriftNote2 } = await import("../chunk-YCWYO6TQ.js");
13788
14239
  graph = getCachedReviewGraph(cwd);
13789
14240
  graphFileCap = getReviewGraphSizeSkipVerdict(cwd)?.maxFileCount;
13790
14241
  const drift = graph ? getReviewGraphRevisionDrift(cwd) : void 0;
@@ -14568,7 +15019,7 @@ function roleFor(graph, filePath) {
14568
15019
  return top.length > 0 ? top.map((s) => s.name).join(", ") : void 0;
14569
15020
  }
14570
15021
  async function computeHubs(graph, degrees, cwd, limit, focusTerms) {
14571
- const { computeTransitiveImpact } = await import("../chunk-4UNXBNE2.js");
15022
+ const { computeTransitiveImpact } = await import("../chunk-XTFDJI3T.js");
14572
15023
  const ranked = [...graph.fileNodes.entries()].map(([filePath, fileNodeId]) => ({
14573
15024
  filePath,
14574
15025
  fileNodeId,
@@ -14820,7 +15271,7 @@ async function projectReport(cwd, options) {
14820
15271
  const limit = clampLimit(options?.limit);
14821
15272
  const focusTerms = normalizeFocus2(options?.focus);
14822
15273
  const view = options?.view;
14823
- const { getCachedReviewGraph, getReviewGraphSizeSkipVerdict, getLastReviewGraphBuildAttempt, buildOrUpdateGraph, isGraphBuildInFlight, getReviewGraphRevisionDrift } = await import("../chunk-2ZGIZPDG.js");
15274
+ const { getCachedReviewGraph, getReviewGraphSizeSkipVerdict, getLastReviewGraphBuildAttempt, buildOrUpdateGraph, isGraphBuildInFlight, getReviewGraphRevisionDrift } = await import("../chunk-YCWYO6TQ.js");
14824
15275
  let graph;
14825
15276
  try {
14826
15277
  graph = getCachedReviewGraph(cwd);
@@ -14844,7 +15295,7 @@ async function projectReport(cwd, options) {
14844
15295
  };
14845
15296
  }
14846
15297
  const previousAttempt = getLastReviewGraphBuildAttempt(cwd);
14847
- const { FactStore: FactStore2 } = await import("../chunk-QGQ5CCCG.js");
15298
+ const { FactStore: FactStore2 } = await import("../chunk-UMS2MFPZ.js");
14848
15299
  const trigger = triggerBackgroundGraphBuild(cwd, {
14849
15300
  buildOrUpdateGraph,
14850
15301
  isGraphBuildInFlight,
@@ -14945,7 +15396,7 @@ function renderCompactProjectReport(report) {
14945
15396
  }
14946
15397
 
14947
15398
  // dist/clients/effective-config.js
14948
- import * as os3 from "node:os";
15399
+ import * as os2 from "node:os";
14949
15400
  import * as path28 from "node:path";
14950
15401
  function isEffectiveFileViewError(file) {
14951
15402
  return "error" in file;
@@ -15011,7 +15462,7 @@ function decidedByOrNothing(entry) {
15011
15462
  }
15012
15463
  async function effectiveConfig(options = {}) {
15013
15464
  const cwd = options.cwd ?? process.cwd();
15014
- const homeDir = options.homeDir ?? os3.homedir();
15465
+ const homeDir = options.homeDir ?? os2.homedir();
15015
15466
  const resolveAt = (dir) => resolvePiLensConfig({
15016
15467
  cwd: dir,
15017
15468
  globalDir: getGlobalPiLensDir(),
@@ -15125,13 +15576,19 @@ function recentLatency(limit = 5, fileFilter) {
15125
15576
  }
15126
15577
  return reports.slice(-limit).reverse();
15127
15578
  }
15128
- function projectScan(cwd, maxFiles, includeGenerated) {
15129
- return scanProjectDiagnostics({
15579
+ async function projectScan(cwd, maxFiles, includeGenerated) {
15580
+ const scan = await scanProjectDiagnostics({
15130
15581
  cwd,
15131
15582
  tier: "cheap",
15132
15583
  maxFiles,
15133
15584
  includeGenerated
15134
15585
  });
15586
+ const policy = applyPushedFindingPolicyByFile(scan.diagnostics, cwd);
15587
+ return {
15588
+ ...scan,
15589
+ diagnostics: policy.kept,
15590
+ dispositionSuppressed: policy.suppressed
15591
+ };
15135
15592
  }
15136
15593
  function scanTruncationNotice(snapshot) {
15137
15594
  if (!snapshot.scanTruncated)
@@ -15263,7 +15720,7 @@ async function symbolSearch(query, cwd, limit = 20, options = {}) {
15263
15720
  warmLease.release();
15264
15721
  }
15265
15722
  const hits = results.map(toSymbolSearchHit);
15266
- const { getCachedReviewGraph } = await import("../chunk-2ZGIZPDG.js");
15723
+ const { getCachedReviewGraph } = await import("../chunk-YCWYO6TQ.js");
15267
15724
  const graph = getCachedReviewGraph(cwd);
15268
15725
  if (graph)
15269
15726
  annotateSymbolSearchHitsWithGraph(hits, centrality, graph);
@@ -15460,10 +15917,12 @@ function canonicalLanguage(lang) {
15460
15917
  }
15461
15918
 
15462
15919
  // dist/tools/shared.js
15920
+ import * as path31 from "node:path";
15463
15921
  function resolveAstGrepPaths(paths, cwd) {
15464
15922
  const base = cwd || ".";
15465
- const requested = paths?.length ? paths : [base];
15466
- return requested.map((p) => resolveHostToolPath(p, base));
15923
+ if (!paths?.length)
15924
+ return [path31.resolve(base)];
15925
+ return paths.map((p) => resolveHostToolPath(p, base));
15467
15926
  }
15468
15927
  var LANGUAGES = [
15469
15928
  "bash",
@@ -16413,7 +16872,7 @@ ${hint}` : `${output}${paginationNote}`;
16413
16872
  // dist/tools/lens-diagnostics.js
16414
16873
  import { promises as fs23 } from "node:fs";
16415
16874
  import * as fsSync from "node:fs";
16416
- import * as path37 from "node:path";
16875
+ import * as path38 from "node:path";
16417
16876
 
16418
16877
  // dist/clients/project-diagnostics/extractors.js
16419
16878
  var WARM_TRIGGER = {
@@ -16472,13 +16931,13 @@ function reasonFromAvailabilityVerdict(tool, verdict) {
16472
16931
 
16473
16932
  // dist/clients/project-diagnostics/fresh-fetch.js
16474
16933
  import * as fs21 from "node:fs";
16475
- import * as os4 from "node:os";
16476
- import * as path35 from "node:path";
16934
+ import * as os3 from "node:os";
16935
+ import * as path36 from "node:path";
16477
16936
 
16478
16937
  // dist/clients/project-diagnostics/runner-adapters/jscpd.js
16479
- import * as path31 from "node:path";
16938
+ import * as path32 from "node:path";
16480
16939
  function resolveClonePath(cwd, file) {
16481
- return path31.isAbsolute(file) ? file : path31.resolve(cwd, file);
16940
+ return path32.isAbsolute(file) ? file : path32.resolve(cwd, file);
16482
16941
  }
16483
16942
  function jscpdCloneToProjectDiagnostics(cwd, clone) {
16484
16943
  const a = resolveClonePath(cwd, clone.fileA);
@@ -16491,7 +16950,7 @@ function jscpdCloneToProjectDiagnostics(cwd, clone) {
16491
16950
  tool: "jscpd",
16492
16951
  runner: "jscpd",
16493
16952
  rule: "jscpd:duplicate",
16494
- message: `Duplicate code (${clone.lines} lines) \u2014 also at ${path31.relative(cwd, otherFile)}:${otherLine}`,
16953
+ message: `Duplicate code (${clone.lines} lines) \u2014 also at ${path32.relative(cwd, otherFile)}:${otherLine}`,
16495
16954
  source: "project-scan"
16496
16955
  });
16497
16956
  return [
@@ -16506,16 +16965,16 @@ function jscpdResultToProjectDiagnostics(cwd, result) {
16506
16965
  }
16507
16966
 
16508
16967
  // dist/clients/project-diagnostics/runner-adapters/madge.js
16509
- import * as path32 from "node:path";
16968
+ import * as path33 from "node:path";
16510
16969
  function resolveCyclePath(cwd, file) {
16511
- return path32.isAbsolute(file) ? file : path32.resolve(cwd, file);
16970
+ return path33.isAbsolute(file) ? file : path33.resolve(cwd, file);
16512
16971
  }
16513
16972
  function cycleMembers(dep) {
16514
16973
  return dep.path.length > 0 ? dep.path : [dep.file];
16515
16974
  }
16516
16975
  function circularDepToProjectDiagnostics(cwd, dep) {
16517
16976
  const members = cycleMembers(dep);
16518
- const rendered = `${members.map((f) => path32.basename(f)).join(" \u2192 ")} \u2192 ${path32.basename(members[0] ?? "")}`;
16977
+ const rendered = `${members.map((f) => path33.basename(f)).join(" \u2192 ")} \u2192 ${path33.basename(members[0] ?? "")}`;
16519
16978
  return members.map((file) => ({
16520
16979
  filePath: resolveCyclePath(cwd, file),
16521
16980
  severity: "warning",
@@ -16541,7 +17000,7 @@ function circularDepsToProjectDiagnostics(cwd, circular) {
16541
17000
  }
16542
17001
 
16543
17002
  // dist/clients/project-diagnostics/runner-adapters/opengrep.js
16544
- import * as path33 from "node:path";
17003
+ import * as path34 from "node:path";
16545
17004
  function severityFor(raw) {
16546
17005
  switch (raw.toUpperCase()) {
16547
17006
  case "ERROR":
@@ -16556,7 +17015,7 @@ function opengrepFindingToProjectDiagnostic(cwd, finding) {
16556
17015
  const { severity, blocking } = severityFor(finding.severity);
16557
17016
  const cweSuffix = finding.cwe?.length ? ` (${finding.cwe[0]})` : "";
16558
17017
  return {
16559
- filePath: path33.isAbsolute(finding.path) ? finding.path : path33.resolve(cwd, finding.path),
17018
+ filePath: path34.isAbsolute(finding.path) ? finding.path : path34.resolve(cwd, finding.path),
16560
17019
  line: finding.startLine,
16561
17020
  column: finding.startCol,
16562
17021
  severity,
@@ -16576,7 +17035,7 @@ function opengrepResultToProjectDiagnostics(cwd, result) {
16576
17035
 
16577
17036
  // dist/clients/project-diagnostics/runner-adapters/runner-findings.js
16578
17037
  import * as fs20 from "node:fs";
16579
- import * as path34 from "node:path";
17038
+ import * as path35 from "node:path";
16580
17039
  function failureMessage(failure) {
16581
17040
  const firstLine2 = failure.message.split("\n")[0]?.slice(0, 300) ?? "";
16582
17041
  return firstLine2 ? `${failure.name}: ${firstLine2}` : failure.name;
@@ -16642,7 +17101,7 @@ function testRunnerFindingsToProjectDiagnostics(cache, cwd, runtime) {
16642
17101
  return [];
16643
17102
  const validation = cwd ? validateAdvisoryProvenance(cache, cwd, runtime) : { status: "unknown", reasons: ["validation-context-missing"] };
16644
17103
  const root = cwd ?? process.cwd();
16645
- const missingKeys = new Set((cache.provenance?.files ?? []).filter((file) => !fs20.existsSync(path34.resolve(root, file.path))).map((file) => advisoryPathKey(file.path, root)));
17104
+ const missingKeys = new Set((cache.provenance?.files ?? []).filter((file) => !fs20.existsSync(path35.resolve(root, file.path))).map((file) => advisoryPathKey(file.path, root)));
16646
17105
  const historical = cache.superseded === true || cache.stale === true || validation.status !== "current";
16647
17106
  return cache.results.filter((result) => !missingKeys.has(advisoryPathKey(result.file, root))).flatMap((result) => testResultToProjectDiagnostics(result, historical)).map((diagnostic) => historical ? {
16648
17107
  ...diagnostic,
@@ -16672,7 +17131,7 @@ function laneObservedAtMs(scannedAt, startedAtMs) {
16672
17131
  return parseScannedAtMs(scannedAt) ?? startedAtMs;
16673
17132
  }
16674
17133
  async function fetchFreshProjectDiagnostics(cacheManager2, cwd, clients, signal, options = {}) {
16675
- const requestedRoot = options.analysisRoot === void 0 ? cwd : path35.resolve(cwd, options.analysisRoot);
17134
+ const requestedRoot = options.analysisRoot === void 0 ? cwd : path36.resolve(cwd, options.analysisRoot);
16676
17135
  let analysisRoot;
16677
17136
  let analysisRootValidation;
16678
17137
  if (options.analysisRoot !== void 0) {
@@ -16711,7 +17170,7 @@ async function fetchFreshProjectDiagnostics(cacheManager2, cwd, clients, signal,
16711
17170
  analysisRoot = realpathOrResolve(cwd);
16712
17171
  }
16713
17172
  const unsafeRootReason = "the analysis root resolves at or above the home directory; heavyweight analyzers refuse to walk from there (#747)";
16714
- const homeRoot = realpathOrResolve(options.homeDir ?? os4.homedir());
17173
+ const homeRoot = realpathOrResolve(options.homeDir ?? os3.homedir());
16715
17174
  if (options.analysisRoot !== void 0) {
16716
17175
  const unsafeExplicitRoot = !isSameOrWithin(homeRoot, analysisRoot) || isSameOrWithin(analysisRoot, homeRoot);
16717
17176
  analysisRootValidation = unsafeExplicitRoot ? {
@@ -16839,7 +17298,7 @@ async function fetchFreshProjectDiagnostics(cacheManager2, cwd, clients, signal,
16839
17298
  markCold("jscpd", reasonFromAvailabilityVerdict("jscpd", clients.jscpdClient.getAvailabilityVerdict?.()));
16840
17299
  return;
16841
17300
  }
16842
- const isTsProject = fs21.existsSync(path35.join(analysisRoot, "tsconfig.json"));
17301
+ const isTsProject = fs21.existsSync(path36.join(analysisRoot, "tsconfig.json"));
16843
17302
  const scannerKey = isTsProject ? "jscpd-ts" : "jscpd";
16844
17303
  const startMs = Date.now();
16845
17304
  const result = await clients.jscpdClient.scan(analysisRoot, void 0, void 0, isTsProject);
@@ -17156,7 +17615,7 @@ function makeProgressReporter(onUpdate, label, throttleMs = 250) {
17156
17615
 
17157
17616
  // dist/tools/lsp-diagnostics.js
17158
17617
  import * as fs22 from "node:fs";
17159
- import * as path36 from "node:path";
17618
+ import * as path37 from "node:path";
17160
17619
  var MAX_FILES = 100;
17161
17620
  var MAX_BATCH_FILES = 100;
17162
17621
  var MAX_DIAGNOSTICS = 200;
@@ -17239,11 +17698,11 @@ async function collectFiles(dir, extensions, maxFiles, isIgnored = () => false)
17239
17698
  return;
17240
17699
  if (entry.isSymbolicLink())
17241
17700
  continue;
17242
- const full = path36.join(current, entry.name);
17701
+ const full = path37.join(current, entry.name);
17243
17702
  if (entry.isDirectory()) {
17244
17703
  if (!isExcludedDirName(entry.name) && !isIgnored(full, true))
17245
17704
  await walk(full);
17246
- } else if (entry.isFile() && extensions.includes(path36.extname(full))) {
17705
+ } else if (entry.isFile() && extensions.includes(path37.extname(full))) {
17247
17706
  if (isIgnored(full, false))
17248
17707
  continue;
17249
17708
  files.push(full);
@@ -17253,7 +17712,7 @@ async function collectFiles(dir, extensions, maxFiles, isIgnored = () => false)
17253
17712
  await walk(dir);
17254
17713
  return files;
17255
17714
  }
17256
- function createLspDiagnosticsTool(nextWriteIndex, onConfirmedNoBlockers, getService = getLSPService) {
17715
+ function createLspDiagnosticsTool(nextWriteIndex, onConfirmedNoBlockers, getService = getLSPService, getFlag) {
17257
17716
  return {
17258
17717
  async execute(_toolCallId, params, _signal, onUpdate, ctx) {
17259
17718
  const signal = combineAbortSignals(_signal, ctx.signal);
@@ -17264,6 +17723,7 @@ function createLspDiagnosticsTool(nextWriteIndex, onConfirmedNoBlockers, getServ
17264
17723
  const concurrency = boundedPositiveInt(typedParams.concurrency, DEFAULT_BATCH_CONCURRENCY, 1, MAX_BATCH_CONCURRENCY);
17265
17724
  const waitMs = typeof typedParams.waitMs === "number" && typedParams.waitMs >= 0 ? Math.floor(typedParams.waitMs) : void 0;
17266
17725
  const serverScope = typedParams.serverScope === "primary" ? "primary" : "all";
17726
+ const excludeServerIds = getFlag ? configDisabledAuxiliaryLspServerIds((name) => getFlag(name, cwd)) : void 0;
17267
17727
  const lspService = getService();
17268
17728
  if (!lspService) {
17269
17729
  return {
@@ -17321,7 +17781,8 @@ function createLspDiagnosticsTool(nextWriteIndex, onConfirmedNoBlockers, getServ
17321
17781
  nextWriteIndex,
17322
17782
  serverScope,
17323
17783
  cwd,
17324
- onConfirmedNoBlockers
17784
+ onConfirmedNoBlockers,
17785
+ excludeServerIds
17325
17786
  }, expanded);
17326
17787
  }
17327
17788
  const rawPath = typedParams.path;
@@ -17337,7 +17798,7 @@ function createLspDiagnosticsTool(nextWriteIndex, onConfirmedNoBlockers, getServ
17337
17798
  details: {}
17338
17799
  };
17339
17800
  }
17340
- const absPath = normalizeMapKey(path36.resolve(cwd, rawPath));
17801
+ const absPath = normalizeMapKey(typedParams.pathAlreadyResolved === true ? rawPath : resolveHostToolPath(rawPath, cwd));
17341
17802
  let stat;
17342
17803
  try {
17343
17804
  stat = fs22.statSync(absPath);
@@ -17359,14 +17820,15 @@ function createLspDiagnosticsTool(nextWriteIndex, onConfirmedNoBlockers, getServ
17359
17820
  nextWriteIndex,
17360
17821
  serverScope,
17361
17822
  cwd,
17362
- onConfirmedNoBlockers
17823
+ onConfirmedNoBlockers,
17824
+ excludeServerIds
17363
17825
  });
17364
17826
  }
17365
- return runFileDiagnostics(absPath, severity, lspService, waitMs, nextWriteIndex, serverScope, cwd, onConfirmedNoBlockers);
17827
+ return runFileDiagnostics(absPath, severity, lspService, waitMs, nextWriteIndex, serverScope, cwd, onConfirmedNoBlockers, excludeServerIds);
17366
17828
  }
17367
17829
  };
17368
17830
  }
17369
- async function collectDiagnosticsForFile(absPath, lspService, scanRoot, waitMs, serverScope = "all") {
17831
+ async function collectDiagnosticsForFile(absPath, lspService, scanRoot, waitMs, serverScope = "all", excludeServerIds) {
17370
17832
  let timedOut = false;
17371
17833
  let content;
17372
17834
  let touched;
@@ -17431,6 +17893,7 @@ async function collectDiagnosticsForFile(absPath, lspService, scanRoot, waitMs,
17431
17893
  maxClientWaitMs: waitMs,
17432
17894
  source: "lsp_diagnostics",
17433
17895
  clientScope: serverScope,
17896
+ excludeServerIds,
17434
17897
  // #3405: the caller asked whether THIS file is clean right now, and the
17435
17898
  // content above was just read from disk — so it is the file's saved
17436
17899
  // state. Without the save a save-triggered server answers zero
@@ -17438,10 +17901,11 @@ async function collectDiagnosticsForFile(absPath, lspService, scanRoot, waitMs,
17438
17901
  // as clean.
17439
17902
  saved: true
17440
17903
  });
17441
- timedOut = touched?.inconclusive === true;
17904
+ timedOut = touched?.inconclusive === true && touched.inconclusiveReason !== "primary-unavailable";
17442
17905
  } catch {
17443
17906
  }
17444
- const diagnostics = touched !== void 0 ? touched.diags : await lspService.getDiagnostics(absPath, waitMs !== void 0 ? "document" : "full");
17907
+ const diagnostics = touched !== void 0 ? touched.diags : await lspService.getDiagnostics(absPath, waitMs !== void 0 ? "document" : "full", excludeServerIds);
17908
+ const fallbackHealth = touched === void 0 ? lspService.getDiagnosticsHealth?.(absPath) : void 0;
17445
17909
  const filtered = content !== void 0 ? applyAuxiliarySuppressions(diagnostics, content, {
17446
17910
  fileRole: detectFileRole(absPath, content),
17447
17911
  filePath: absPath,
@@ -17449,11 +17913,18 @@ async function collectDiagnosticsForFile(absPath, lspService, scanRoot, waitMs,
17449
17913
  }) : diagnostics;
17450
17914
  const binding = touched?.binding;
17451
17915
  const confirmedByTouch = touchCompletedConfirmationPolicy(touched);
17916
+ const primaryUnavailable = describePrimaryUnavailable(touched, fallbackHealth);
17917
+ const noClientAnswered = fallbackHealth !== void 0 && String(fallbackHealth.health ?? "").startsWith("no_clients");
17452
17918
  return {
17453
17919
  diagnostics: filtered,
17454
17920
  timedOut,
17455
17921
  skipReason: touched?.skipReason,
17456
17922
  confirmedByTouch,
17923
+ ...noClientAnswered && { noClientAnswered: true },
17924
+ ...primaryUnavailable !== void 0 && {
17925
+ primaryUnavailable: primaryUnavailable.text,
17926
+ primaryUnavailableCause: primaryUnavailable.cause
17927
+ },
17457
17928
  // #1470: only a touch that resolved clients contributes a coverage gap;
17458
17929
  // the getDiagnostics fallback never reports one, which is honest — that
17459
17930
  // path claims no confirmation at all.
@@ -17463,6 +17934,27 @@ async function collectDiagnosticsForFile(absPath, lspService, scanRoot, waitMs,
17463
17934
  binding
17464
17935
  };
17465
17936
  }
17937
+ function describePrimaryUnavailable(touched, fallbackHealth) {
17938
+ let serverIds;
17939
+ let disabledServerIds;
17940
+ let carriedCause;
17941
+ if (touched?.inconclusiveReason === "primary-unavailable") {
17942
+ serverIds = touched.inconclusiveServerIds ?? [];
17943
+ disabledServerIds = touched.disabledPrimaryServerIds ?? [];
17944
+ carriedCause = touched.primaryUnavailableCause;
17945
+ } else if (fallbackHealth?.unavailablePrimaryServerIds?.length) {
17946
+ serverIds = fallbackHealth.unavailablePrimaryServerIds;
17947
+ disabledServerIds = fallbackHealth.disabledPrimaryServerIds ?? [];
17948
+ carriedCause = fallbackHealth.primaryUnavailableCause;
17949
+ } else {
17950
+ return void 0;
17951
+ }
17952
+ const cause = carriedCause ?? primaryUnavailableCause(serverIds, disabledServerIds);
17953
+ return {
17954
+ text: `Primary LSP (${serverIds.join(", ")}): not available \u2014 no language server answered for this file (${PRIMARY_UNAVAILABLE_CAUSE[cause]}). NOT the same as 0 diagnostics; auxiliary scanners do not check the language itself.`,
17955
+ cause
17956
+ };
17957
+ }
17466
17958
  function diagnosticsToFileDiags(file, diagnostics) {
17467
17959
  return diagnostics.map((d) => ({
17468
17960
  file,
@@ -17508,7 +18000,7 @@ async function resolveEmptyResult(file, lspService) {
17508
18000
  function canTrustTouchConfirmation(file, serverScope, confirmedByTouch) {
17509
18001
  return confirmedByTouch && (serverScope === "primary" || primaryServerId(file) !== "typescript");
17510
18002
  }
17511
- function coveredSourcesForCheck(file, serverScope, unconfirmedServerIds) {
18003
+ function coveredSourcesForCheck(file, serverScope, unconfirmedServerIds, excludeServerIds) {
17512
18004
  const unconfirmed = new Set(unconfirmedServerIds);
17513
18005
  const covered = /* @__PURE__ */ new Set();
17514
18006
  let servers = [];
@@ -17523,6 +18015,8 @@ function coveredSourcesForCheck(file, serverScope, unconfirmedServerIds) {
17523
18015
  continue;
17524
18016
  if (unconfirmed.has(server.id))
17525
18017
  continue;
18018
+ if (excludeServerIds?.has(server.id))
18019
+ continue;
17526
18020
  covered.add(auxiliary ? server.id : "lsp");
17527
18021
  }
17528
18022
  return [...covered];
@@ -17534,7 +18028,7 @@ function demoteInferredForFile(file, diagnostics, cwd, lspService) {
17534
18028
  service: lspService
17535
18029
  });
17536
18030
  }
17537
- function applyProbeFindingPolicy(file, diagnostics, cwd, content, severity) {
18031
+ function applyProbeFindingPolicy(file, diagnostics, cwd, content, severity, toolName = "lsp_diagnostics") {
17538
18032
  const started = Date.now();
17539
18033
  try {
17540
18034
  const result = applyLspFindingPolicy(diagnostics, {
@@ -17549,7 +18043,7 @@ function applyProbeFindingPolicy(file, diagnostics, cwd, content, severity) {
17549
18043
  if (suppressed > 0) {
17550
18044
  logLatency({
17551
18045
  type: "phase",
17552
- toolName: "lsp_diagnostics",
18046
+ toolName,
17553
18047
  filePath: file,
17554
18048
  phase: "lsp_probe_disposition_filter",
17555
18049
  durationMs: Date.now() - started,
@@ -17571,15 +18065,11 @@ function applyProbeFindingPolicy(file, diagnostics, cwd, content, severity) {
17571
18065
  }
17572
18066
  }
17573
18067
  function replayContentForStrictMarks(file, cwd) {
17574
- if (!hasStrictDispositionMarks(cwd))
17575
- return void 0;
17576
- try {
17577
- return fs22.readFileSync(file, "utf-8");
17578
- } catch {
17579
- return void 0;
17580
- }
18068
+ return hasStrictDispositionMarks(cwd) ? readFileContent(file) : void 0;
17581
18069
  }
17582
- function reconcileWidgetFromLspResult(file, rawDiags, confirmation, writeIndex, cwd, content, boundMismatch = false, observedAt) {
18070
+ function reconcileWidgetFromLspResult(file, rawDiags, confirmation, writeIndex, cwd, content, boundMismatch = false, observedAt, auxiliariesOnly = false) {
18071
+ if (auxiliariesOnly)
18072
+ return { confirmed: false, blocking: true };
17583
18073
  const confirmed = (rawDiags.length > 0 || confirmation !== "unconfirmed") && !boundMismatch;
17584
18074
  if (!confirmed)
17585
18075
  return { confirmed: false, blocking: true };
@@ -17596,7 +18086,7 @@ function reconcileWidgetFromLspResult(file, rawDiags, confirmation, writeIndex,
17596
18086
  return { confirmed: false, blocking: true };
17597
18087
  }
17598
18088
  }
17599
- async function collectFileDiagnosticResult(file, severity, lspService, waitMs, nextWriteIndex, serverScope = "all", cacheCtx, scopeKey, cwd = process.cwd(), onConfirmedNoBlockers) {
18089
+ async function collectFileDiagnosticResult(file, severity, lspService, waitMs, nextWriteIndex, serverScope = "all", cacheCtx, scopeKey, cwd = process.cwd(), onConfirmedNoBlockers, excludeServerIds) {
17600
18090
  const writeIndex = nextWriteIndex?.();
17601
18091
  const scannedAt = Date.now();
17602
18092
  let stat;
@@ -17640,7 +18130,7 @@ async function collectFileDiagnosticResult(file, severity, lspService, waitMs, n
17640
18130
  };
17641
18131
  }
17642
18132
  }
17643
- const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, diagnosticsUnsupportedServerIds, content: collectedContent, binding, tooLargeReason, skipReason } = await collectDiagnosticsForFile(file, lspService, cwd, waitMs, serverScope);
18133
+ const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, diagnosticsUnsupportedServerIds, content: collectedContent, binding, tooLargeReason, skipReason, primaryUnavailable, primaryUnavailableCause: primaryUnavailableCause2, noClientAnswered } = await collectDiagnosticsForFile(file, lspService, cwd, waitMs, serverScope, excludeServerIds);
17644
18134
  if (tooLargeReason !== void 0) {
17645
18135
  return { file, diagnostics: [], outcome: "too_large", tooLargeReason };
17646
18136
  }
@@ -17649,7 +18139,7 @@ async function collectFileDiagnosticResult(file, severity, lspService, waitMs, n
17649
18139
  let confirmation;
17650
18140
  if (diagnosticsUnsupportedServerIds.length > 0) {
17651
18141
  confirmation = void 0;
17652
- } else if (skipReason !== void 0) {
18142
+ } else if (skipReason !== void 0 || primaryUnavailable !== void 0) {
17653
18143
  confirmation = "unconfirmed";
17654
18144
  } else if (timedOut) {
17655
18145
  if (applySeverityFilter(rawDiags, severity).length === 0) {
@@ -17689,13 +18179,14 @@ async function collectFileDiagnosticResult(file, severity, lspService, waitMs, n
17689
18179
  boundMismatch,
17690
18180
  // #3505 r1 F1: the widget row is observed at the read, as the cache
17691
18181
  // entry recorded below is.
17692
- scannedAt
18182
+ scannedAt,
18183
+ primaryUnavailable !== void 0
17693
18184
  );
17694
18185
  if (verdict.confirmed && !verdict.blocking) {
17695
18186
  onConfirmedNoBlockers?.({
17696
18187
  filePath: file,
17697
18188
  writeIndex,
17698
- coveredSources: coveredSourcesForCheck(file, serverScope, unconfirmedServerIds),
18189
+ coveredSources: coveredSourcesForCheck(file, serverScope, unconfirmedServerIds, excludeServerIds),
17699
18190
  cwd
17700
18191
  });
17701
18192
  }
@@ -17710,6 +18201,11 @@ async function collectFileDiagnosticResult(file, severity, lspService, waitMs, n
17710
18201
  timedOut: confirmation === "unconfirmed" ? timedOut : void 0,
17711
18202
  ...rootFallback && { rootFallback },
17712
18203
  ...skipReason !== void 0 && { skipReason },
18204
+ ...primaryUnavailable !== void 0 && {
18205
+ primaryUnavailable,
18206
+ primaryUnavailableCause: primaryUnavailableCause2
18207
+ },
18208
+ ...noClientAnswered && { noClientAnswered },
17713
18209
  primaryServerId: primaryServerId(file),
17714
18210
  diagnosticsUnsupported: diagnosticsUnsupportedServerIds.length > 0,
17715
18211
  ...policy.suppressed > 0 && {
@@ -17717,10 +18213,14 @@ async function collectFileDiagnosticResult(file, severity, lspService, waitMs, n
17717
18213
  }
17718
18214
  };
17719
18215
  }
17720
- async function runFileDiagnostics(absPath, severity, lspService, waitMs, nextWriteIndex, serverScope = "all", cwd = process.cwd(), onConfirmedNoBlockers) {
18216
+ async function runFileDiagnostics(absPath, severity, lspService, waitMs, nextWriteIndex, serverScope = "all", cwd = process.cwd(), onConfirmedNoBlockers, excludeServerIds) {
17721
18217
  const writeIndex = nextWriteIndex?.();
18218
+ lspService.announceCreatedFiles([
18219
+ ...goPackageFilesToAnnounce([absPath]),
18220
+ ...await importTargetsToAnnounce(cwd, [absPath])
18221
+ ]);
17722
18222
  const observedAt = Date.now();
17723
- const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, diagnosticsUnsupportedServerIds, content: collectedContent, binding, tooLargeReason, skipReason } = await collectDiagnosticsForFile(absPath, lspService, cwd, waitMs, serverScope);
18223
+ const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, diagnosticsUnsupportedServerIds, content: collectedContent, binding, tooLargeReason, skipReason, primaryUnavailable } = await collectDiagnosticsForFile(absPath, lspService, cwd, waitMs, serverScope, excludeServerIds);
17724
18224
  if (tooLargeReason !== void 0) {
17725
18225
  return {
17726
18226
  content: [{ type: "text", text: tooLargeReason }],
@@ -17742,7 +18242,7 @@ async function runFileDiagnostics(absPath, severity, lspService, waitMs, nextWri
17742
18242
  let confirmation;
17743
18243
  if (diagnosticsUnsupportedServerIds.length > 0) {
17744
18244
  confirmation = void 0;
17745
- } else if (skipReason !== void 0) {
18245
+ } else if (skipReason !== void 0 || primaryUnavailable !== void 0) {
17746
18246
  confirmation = "unconfirmed";
17747
18247
  } else if (timedOut) {
17748
18248
  if (applySeverityFilter(rawDiags, severity).length === 0) {
@@ -17776,12 +18276,12 @@ async function runFileDiagnostics(absPath, severity, lspService, waitMs, nextWri
17776
18276
  const truncated = total > MAX_DIAGNOSTICS;
17777
18277
  const limited = truncated ? filtered.slice(0, MAX_DIAGNOSTICS) : filtered;
17778
18278
  const unconfirmed = confirmation === "unconfirmed";
17779
- const verdict = reconcileWidgetFromLspResult(absPath, effectiveRawDiags, confirmation, writeIndex, cwd, collectedContent, boundMismatch, observedAt);
18279
+ const verdict = reconcileWidgetFromLspResult(absPath, effectiveRawDiags, confirmation, writeIndex, cwd, collectedContent, boundMismatch, observedAt, primaryUnavailable !== void 0);
17780
18280
  if (verdict.confirmed && !verdict.blocking) {
17781
18281
  onConfirmedNoBlockers?.({
17782
18282
  filePath: absPath,
17783
18283
  writeIndex,
17784
- coveredSources: coveredSourcesForCheck(absPath, serverScope, unconfirmedServerIds),
18284
+ coveredSources: coveredSourcesForCheck(absPath, serverScope, unconfirmedServerIds, excludeServerIds),
17785
18285
  cwd
17786
18286
  });
17787
18287
  }
@@ -17789,6 +18289,8 @@ async function runFileDiagnostics(absPath, severity, lspService, waitMs, nextWri
17789
18289
  const primaryDiags = limited.filter((d) => d.serverId === primaryId);
17790
18290
  const auxiliaryDiags = limited.filter((d) => d.serverId !== primaryId);
17791
18291
  const primaryLine = (() => {
18292
+ if (primaryUnavailable !== void 0)
18293
+ return primaryUnavailable;
17792
18294
  if (diagnosticsUnsupportedServerIds.length > 0) {
17793
18295
  return `Primary LSP${primaryId ? ` (${primaryId})` : ""}: navigation-only \u2014 diagnostics unsupported (no pull provider or publish evidence).`;
17794
18296
  }
@@ -17808,12 +18310,14 @@ async function runFileDiagnostics(absPath, severity, lspService, waitMs, nextWri
17808
18310
  })();
17809
18311
  const coverageLine = unconfirmedServerIds.length > 0 ? `Auxiliary coverage INCOMPLETE \u2014 ${[...unconfirmedServerIds].join(", ")} did not answer within the wait budget, so ${unconfirmedServerIds.length === 1 ? "its findings are" : "their findings are"} NOT included here. This is not a clean bill of health for ${unconfirmedServerIds.length === 1 ? "that scanner" : "those scanners"}; re-check after the next edit. waitMs can extend an ordinary wait, but it cannot override a session-demoted scanner.` : void 0;
17810
18312
  const suppressedLine = dispositionSuppressedLine(policy.suppressed);
18313
+ const switchedOffServerIds = serverScope === "all" && excludeServerIds && excludeServerIds.size > 0 ? getServersForFileWithConfig(absPath).flatMap((server) => excludeServerIds.has(server.id) ? [server.id] : []) : [];
18314
+ const noAuxiliaryLine = switchedOffServerIds.length > 0 ? `No auxiliary findings. Switched off, not run: ${switchedOffServerIds.join(", ")}.` : "No auxiliary findings.";
17811
18315
  let text;
17812
18316
  if (total === 0) {
17813
18317
  text = [
17814
18318
  primaryLine,
17815
18319
  "",
17816
- unavailable ?? "No auxiliary findings.",
18320
+ unavailable ?? noAuxiliaryLine,
17817
18321
  ...coverageLine ? [coverageLine] : [],
17818
18322
  ...suppressedLine ? [suppressedLine] : []
17819
18323
  ].join("\n");
@@ -17865,6 +18369,7 @@ async function runFileDiagnostics(absPath, severity, lspService, waitMs, nextWri
17865
18369
  timedOut: unconfirmed ? timedOut : void 0,
17866
18370
  navigationOnlyFiles: diagnosticsUnsupportedServerIds.length > 0 ? 1 : void 0,
17867
18371
  ...skipReason !== void 0 && { skipReason },
18372
+ ...primaryUnavailable !== void 0 && { primaryUnavailable },
17868
18373
  ...rootFallback && {
17869
18374
  rootFallbackReason: describeRootFallback(rootFallback)
17870
18375
  },
@@ -17882,6 +18387,8 @@ function tallyConfirmation(results) {
17882
18387
  let clean = 0;
17883
18388
  let unconfirmed = 0;
17884
18389
  let timedOut = 0;
18390
+ let primaryUnavailable = 0;
18391
+ const primaryUnavailableCauses = [];
17885
18392
  const rootFallbackReasons = /* @__PURE__ */ new Set();
17886
18393
  let navigationOnly = 0;
17887
18394
  let tooLarge = 0;
@@ -17900,6 +18407,11 @@ function tallyConfirmation(results) {
17900
18407
  unconfirmed += 1;
17901
18408
  if (result.timedOut)
17902
18409
  timedOut += 1;
18410
+ if (result.primaryUnavailable !== void 0) {
18411
+ primaryUnavailable += 1;
18412
+ if (result.primaryUnavailableCause !== void 0)
18413
+ primaryUnavailableCauses.push(result.primaryUnavailableCause);
18414
+ }
17903
18415
  if (result.rootFallback) {
17904
18416
  rootFallbackReasons.add(describeRootFallback(result.rootFallback));
17905
18417
  }
@@ -17911,6 +18423,10 @@ function tallyConfirmation(results) {
17911
18423
  clean,
17912
18424
  unconfirmed,
17913
18425
  timedOut,
18426
+ primaryUnavailable: {
18427
+ count: primaryUnavailable,
18428
+ cause: combinePrimaryUnavailableCauses(primaryUnavailableCauses)
18429
+ },
17914
18430
  rootFallbackReasons: [...rootFallbackReasons],
17915
18431
  navigationOnly,
17916
18432
  tooLarge
@@ -17923,6 +18439,9 @@ function classifyBatchFileOutcome(result) {
17923
18439
  return "too_large";
17924
18440
  if (result.diagnosticsUnsupported)
17925
18441
  return "unsupported";
18442
+ if (result.noClientAnswered && result.primaryUnavailable !== void 0) {
18443
+ return "unavailable";
18444
+ }
17926
18445
  if (result.timedOut || result.confirmation === "unconfirmed") {
17927
18446
  return "inconclusive";
17928
18447
  }
@@ -17945,7 +18464,24 @@ function inconclusiveBatchResult(file, reason) {
17945
18464
  inconclusiveReason: reason
17946
18465
  };
17947
18466
  }
17948
- function unconfirmedReasonClause(unconfirmed, timedOut, rootFallbackReasons = []) {
18467
+ function batchOutcomeReason(result) {
18468
+ return result.tooLargeReason ?? // #4322: a file only auxiliaries answered for names its language server.
18469
+ result.primaryUnavailable ?? result.inconclusiveReason ?? result.error ?? result.unavailable;
18470
+ }
18471
+ function auxiliaryOnlyFindingsLine(results) {
18472
+ const auxiliaryOnly = results.filter((result) => result.diagnostics.length > 0 && result.primaryUnavailable !== void 0 && result.noClientAnswered === void 0);
18473
+ const count = auxiliaryOnly.length;
18474
+ if (count === 0)
18475
+ return void 0;
18476
+ const cause = combinePrimaryUnavailableCauses(auxiliaryOnly.flatMap((result) => result.primaryUnavailableCause ?? [])) ?? "unstarted";
18477
+ return `${count} file${count === 1 ? "" : "s"} with auxiliary findings only: no language server answered (${PRIMARY_UNAVAILABLE_CAUSE[cause]}), so the language itself was not checked.`;
18478
+ }
18479
+ function unconfirmedReasonClause(unconfirmed, timedOut, rootFallbackReasons = [], primaryUnavailable = { count: 0, cause: void 0 }) {
18480
+ if (primaryUnavailable.count > 0) {
18481
+ const head = `${primaryUnavailable.count} with no language server answer (${PRIMARY_UNAVAILABLE_CAUSE[primaryUnavailable.cause ?? "unstarted"]})`;
18482
+ const rest = unconfirmed - primaryUnavailable.count;
18483
+ return rest > 0 ? `${head}; the other ${rest}: ${unconfirmedReasonClause(rest, timedOut, rootFallbackReasons)}` : `${head}.`;
18484
+ }
17949
18485
  if (rootFallbackReasons.length > 0) {
17950
18486
  return `${rootFallbackReasons.join("; ")} (${unconfirmed} unconfirmed in all).`;
17951
18487
  }
@@ -17961,7 +18497,8 @@ function unconfirmedReasonClause(unconfirmed, timedOut, rootFallbackReasons = []
17961
18497
  async function collectBatchDiagnostics(files, severity, lspService, options) {
17962
18498
  const resolvedCwd = options.cwd ?? process.cwd();
17963
18499
  const cacheCtx = createWorkspaceDiagnosticsCacheContext(resolvedCwd);
17964
- const scopeKey = buildScopeKey(options.serverScope ?? "all");
18500
+ const serverScope = options.serverScope ?? "all";
18501
+ const scopeKey = buildProbeScopeKey(serverScope, options.excludeServerIds);
17965
18502
  const resultsByIndex = /* @__PURE__ */ new Map();
17966
18503
  let completed = 0;
17967
18504
  const pendingIndices = /* @__PURE__ */ new Map();
@@ -17972,6 +18509,11 @@ async function collectBatchDiagnostics(files, severity, lspService, options) {
17972
18509
  else
17973
18510
  pendingIndices.set(file, [index]);
17974
18511
  });
18512
+ lspService.announceCreatedFiles([
18513
+ ...files,
18514
+ ...goPackageFilesToAnnounce(files),
18515
+ ...await importTargetsToAnnounce(resolvedCwd, files.filter((file) => !cacheCtx.servesClean(file, scopeKey)), (file, scope) => cacheCtx.noteImportScope(file, scope))
18516
+ ]);
17975
18517
  const groups = groupFilesByPrimaryServer(files);
17976
18518
  await runPerServerGroups(groups, options.concurrency, async (group) => {
17977
18519
  if (options.signal?.aborted)
@@ -17986,7 +18528,7 @@ async function collectBatchDiagnostics(files, severity, lspService, options) {
17986
18528
  }
17987
18529
  await mapWithConcurrency(group.files, 1, async (file) => {
17988
18530
  const index = pendingIndices.get(file).shift();
17989
- const work = collectFileDiagnosticResult(file, severity, lspService, options.waitMs, options.nextWriteIndex, options.serverScope, cacheCtx, scopeKey, resolvedCwd, options.onConfirmedNoBlockers);
18531
+ const work = collectFileDiagnosticResult(file, severity, lspService, options.waitMs, options.nextWriteIndex, options.serverScope, cacheCtx, scopeKey, resolvedCwd, options.onConfirmedNoBlockers, options.excludeServerIds);
17990
18532
  const bounded2 = withDeadline(work, {
17991
18533
  ms: batchFileDeadlineMs(),
17992
18534
  onTimeout: "undefined",
@@ -18017,7 +18559,7 @@ async function collectBatchDiagnostics(files, severity, lspService, options) {
18017
18559
  const total = allDiags.length;
18018
18560
  const truncated = total > MAX_DIAGNOSTICS;
18019
18561
  const display = truncated ? allDiags.slice(0, MAX_DIAGNOSTICS) : allDiags;
18020
- const { clean, unconfirmed, timedOut, rootFallbackReasons } = tallyConfirmation(results);
18562
+ const { clean, unconfirmed, timedOut, primaryUnavailable, rootFallbackReasons } = tallyConfirmation(results);
18021
18563
  for (const result of results) {
18022
18564
  result.outcome = classifyBatchFileOutcome(result);
18023
18565
  }
@@ -18049,6 +18591,7 @@ async function collectBatchDiagnostics(files, severity, lspService, options) {
18049
18591
  unconfirmed,
18050
18592
  timedOut,
18051
18593
  rootFallbackReasons,
18594
+ primaryUnavailable,
18052
18595
  outcomeCounts,
18053
18596
  incompleteFiles: Math.max(0, files.length - results.length)
18054
18597
  };
@@ -18058,7 +18601,7 @@ async function expandPathsToFiles(cwd, rawPaths) {
18058
18601
  const directories = [];
18059
18602
  const seenFileKeys = /* @__PURE__ */ new Set();
18060
18603
  for (const entry of rawPaths) {
18061
- const abs = normalizeMapKey(path36.resolve(cwd, entry));
18604
+ const abs = normalizeMapKey(resolveHostToolPath(entry, cwd));
18062
18605
  let stat;
18063
18606
  try {
18064
18607
  stat = fs22.statSync(abs);
@@ -18103,7 +18646,7 @@ async function runBatchFileDiagnostics(absPaths, severity, lspService, options,
18103
18646
  details: { mode: "batch", severity, filesChecked: 0 }
18104
18647
  };
18105
18648
  }
18106
- const { results, fileErrors, lspHealthWarnings, total, truncated, display, primaryDisplay, auxiliaryDisplay, clean, unconfirmed, timedOut, rootFallbackReasons, outcomeCounts, incompleteFiles } = await collectBatchDiagnostics(absPaths, severity, lspService, options);
18649
+ const { results, fileErrors, lspHealthWarnings, total, truncated, display, primaryDisplay, auxiliaryDisplay, clean, unconfirmed, timedOut, rootFallbackReasons, primaryUnavailable, outcomeCounts, incompleteFiles } = await collectBatchDiagnostics(absPaths, severity, lspService, options);
18107
18650
  const navigationOnly = results.filter((result) => result.diagnosticsUnsupported).length;
18108
18651
  const dispositionSuppressed = results.reduce((sum, result) => sum + (result.dispositionSuppressed ?? 0), 0);
18109
18652
  const lines = [
@@ -18129,7 +18672,7 @@ async function runBatchFileDiagnostics(absPaths, severity, lspService, options,
18129
18672
  const outcomeDetails = results.map((result) => ({
18130
18673
  file: result.file,
18131
18674
  outcome: result.outcome,
18132
- reason: result.tooLargeReason ?? result.inconclusiveReason ?? result.error ?? result.unavailable
18675
+ reason: batchOutcomeReason(result)
18133
18676
  }));
18134
18677
  const tooLargeLines = renderBatchOutcomeLines(outcomeDetails);
18135
18678
  if (tooLargeLines.length > 0) {
@@ -18139,8 +18682,11 @@ async function runBatchFileDiagnostics(absPaths, severity, lspService, options,
18139
18682
  lines.push("", "LSP health warnings:", ...lspHealthWarnings.slice(0, 10));
18140
18683
  }
18141
18684
  if (unconfirmed > 0) {
18142
- lines.push("", `${clean} file${clean === 1 ? "" : "s"} confirmed clean, ${unconfirmed} unconfirmed: ${unconfirmedReasonClause(unconfirmed, timedOut, rootFallbackReasons)} NOT the same as 0 diagnostics.`);
18685
+ lines.push("", `${clean} file${clean === 1 ? "" : "s"} confirmed clean, ${unconfirmed} unconfirmed: ${unconfirmedReasonClause(unconfirmed, timedOut, rootFallbackReasons, primaryUnavailable)} NOT the same as 0 diagnostics.`);
18143
18686
  }
18687
+ const auxiliaryOnlyLine = auxiliaryOnlyFindingsLine(results);
18688
+ if (auxiliaryOnlyLine)
18689
+ lines.push("", auxiliaryOnlyLine);
18144
18690
  if (display.length === 0) {
18145
18691
  if (unconfirmed === 0 && outcomeCounts.clean === results.length) {
18146
18692
  lines.push("", "No diagnostics found.");
@@ -18185,7 +18731,7 @@ async function runBatchFileDiagnostics(absPaths, severity, lspService, options,
18185
18731
  outcomes: results.map((result) => ({
18186
18732
  file: result.file,
18187
18733
  outcome: result.outcome,
18188
- reason: result.tooLargeReason ?? result.inconclusiveReason ?? result.error ?? result.unavailable,
18734
+ reason: batchOutcomeReason(result),
18189
18735
  primaryDiagnosticsCount: result.diagnostics.filter((diagnostic) => diagnostic.serverId === result.primaryServerId).length,
18190
18736
  auxiliaryDiagnosticsCount: result.diagnostics.filter((diagnostic) => diagnostic.serverId !== result.primaryServerId).length
18191
18737
  })),
@@ -18253,26 +18799,30 @@ async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
18253
18799
  }
18254
18800
  };
18255
18801
  }
18256
- const { results, fileErrors, lspHealthWarnings, total, truncated, display, primaryDisplay, auxiliaryDisplay, clean, unconfirmed, timedOut, rootFallbackReasons, outcomeCounts } = await collectBatchDiagnostics(filesToProcess, severity, lspService, options);
18802
+ const { results, fileErrors, lspHealthWarnings, total, truncated, display, primaryDisplay, auxiliaryDisplay, clean, unconfirmed, timedOut, rootFallbackReasons, primaryUnavailable, outcomeCounts } = await collectBatchDiagnostics(filesToProcess, severity, lspService, options);
18257
18803
  const navigationOnly = results.filter((result) => result.diagnosticsUnsupported).length;
18258
18804
  const dispositionSuppressed = results.reduce((sum, result) => sum + (result.dispositionSuppressed ?? 0), 0);
18259
18805
  const suppressedLine = dispositionSuppressedLine(dispositionSuppressed);
18260
18806
  const outcomeDetails = results.map((result) => ({
18261
18807
  file: result.file,
18262
18808
  outcome: result.outcome,
18263
- reason: result.tooLargeReason ?? result.inconclusiveReason ?? result.error ?? result.unavailable
18809
+ reason: batchOutcomeReason(result)
18264
18810
  }));
18265
- const tooLargeLines = renderBatchOutcomeLines(outcomeDetails, (file) => path36.relative(absPath, file));
18811
+ const auxiliaryOnlyLine = auxiliaryOnlyFindingsLine(results);
18812
+ const tooLargeLines = renderBatchOutcomeLines(outcomeDetails, (file) => path37.relative(absPath, file));
18266
18813
  let text;
18267
18814
  if (total === 0) {
18268
- const cleanLine = unconfirmed > 0 ? `${clean} clean \xB7 ${unconfirmed} unconfirmed: ${unconfirmedReasonClause(unconfirmed, timedOut, rootFallbackReasons)} NOT the same as 0 diagnostics.` : "No diagnostics found.";
18815
+ const cleanLine = unconfirmed > 0 ? `${clean} clean \xB7 ${unconfirmed} unconfirmed: ${unconfirmedReasonClause(unconfirmed, timedOut, rootFallbackReasons, primaryUnavailable)} NOT the same as 0 diagnostics.` : "No diagnostics found.";
18269
18816
  text = [
18270
18817
  `Directory: ${absPath}`,
18271
18818
  `Files scanned: ${filesToProcess.length}${wasCapped ? ` (capped at ${MAX_FILES})` : ""}`,
18272
18819
  ...lspHealthWarnings.length > 0 ? [
18273
18820
  "LSP unavailable for one or more files:",
18274
18821
  ...lspHealthWarnings.slice(0, 10)
18275
- ] : unconfirmed > 0 || outcomeCounts.clean === filesToProcess.length ? [cleanLine] : [],
18822
+ ] : [],
18823
+ // #4403: an unconfirmed file says why even beside a health warning,
18824
+ // which names what was missing but not that the file is unconfirmed.
18825
+ ...unconfirmed > 0 || lspHealthWarnings.length === 0 && outcomeCounts.clean === filesToProcess.length ? [cleanLine] : [],
18276
18826
  ...tooLargeLines.length > 0 ? ["", "Files too large for LSP diagnostics:", ...tooLargeLines] : [],
18277
18827
  ...suppressedLine ? ["", suppressedLine] : []
18278
18828
  ].join("\n");
@@ -18288,13 +18838,14 @@ async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
18288
18838
  // whole found diagnostics elsewhere.
18289
18839
  ...unconfirmed > 0 ? [
18290
18840
  "",
18291
- `${clean} other file${clean === 1 ? "" : "s"} confirmed clean, ${unconfirmed} unconfirmed: ` + unconfirmedReasonClause(unconfirmed, timedOut, rootFallbackReasons)
18841
+ `${clean} other file${clean === 1 ? "" : "s"} confirmed clean, ${unconfirmed} unconfirmed: ` + unconfirmedReasonClause(unconfirmed, timedOut, rootFallbackReasons, primaryUnavailable)
18292
18842
  ] : [],
18843
+ ...auxiliaryOnlyLine ? ["", auxiliaryOnlyLine] : [],
18293
18844
  ""
18294
18845
  ];
18295
18846
  const toRelative = (d) => ({
18296
18847
  ...d,
18297
- file: path36.relative(absPath, d.file)
18848
+ file: path37.relative(absPath, d.file)
18298
18849
  });
18299
18850
  if (primaryDisplay.length > 0) {
18300
18851
  lines.push(`Primary findings (${primaryDisplay.length}):`);
@@ -18325,7 +18876,7 @@ async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
18325
18876
  filesScanned: filesToProcess.length,
18326
18877
  capped: wasCapped,
18327
18878
  diagnostics: display.map((d) => ({
18328
- file: path36.relative(absPath, d.file),
18879
+ file: path37.relative(absPath, d.file),
18329
18880
  line: d.line,
18330
18881
  character: d.character,
18331
18882
  severity: d.severity,
@@ -18351,11 +18902,6 @@ async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
18351
18902
  }
18352
18903
  };
18353
18904
  }
18354
- function dispositionSuppressedLine(suppressed) {
18355
- if (suppressed <= 0)
18356
- return void 0;
18357
- return `suppressed by disposition: ${suppressed} finding(s) dropped from this result because they're marked false-positive/won't-fix, disabled by \`.pi-lens.json\` rule policy, or suppressed by an inline \`pi-lens-ignore\` comment. Not a clean verdict for those locations \u2014 they were found, then intentionally hidden.`;
18358
- }
18359
18905
  function applySeverityFilter(diags, severity) {
18360
18906
  if (severity === "all")
18361
18907
  return diags;
@@ -18422,7 +18968,7 @@ async function findFullScanBindingMismatches(results) {
18422
18968
  return mismatched;
18423
18969
  }
18424
18970
  function createLensDiagnosticsTool(cacheManager2, getCwd, getLspService = getLSPService, flushPending = async () => {
18425
- }, nextWriteIndex, captureLspStatusRepaint, getRuntime, isLensGuardEnabled2 = () => true) {
18971
+ }, nextWriteIndex, captureLspStatusRepaint, getRuntime, isLensGuardEnabled2 = () => true, getFlag) {
18426
18972
  const lspProbe = createLspDiagnosticsTool(nextWriteIndex, (info) => {
18427
18973
  const runtime = getRuntime?.();
18428
18974
  if (!runtime)
@@ -18443,7 +18989,7 @@ function createLensDiagnosticsTool(cacheManager2, getCwd, getLspService = getLSP
18443
18989
  message: `inline_blocker: retired for ${info.filePath}`
18444
18990
  });
18445
18991
  }
18446
- }, getLspService);
18992
+ }, getLspService, getFlag);
18447
18993
  return {
18448
18994
  name: "lens_diagnostics",
18449
18995
  label: "Project Diagnostics",
@@ -18455,7 +19001,7 @@ function createLensDiagnosticsTool(cacheManager2, getCwd, getLspService = getLSP
18455
19001
  const files2 = details.filesChecked ?? details.filesScanned ?? 0;
18456
19002
  const noun = count === 1 ? "diagnostic" : "diagnostics";
18457
19003
  const filePath = typeof details?.filePath === "string" ? details.filePath : Array.isArray(args?.paths) && args.paths.length === 1 && typeof args.paths[0] === "string" ? args.paths[0] : void 0;
18458
- const singleFile = (details?.mode === "file" || files2 === 1) && typeof filePath === "string" ? ` ${path37.basename(filePath)}` : "";
19004
+ const singleFile = (details?.mode === "file" || files2 === 1) && typeof filePath === "string" ? ` ${path38.basename(filePath)}` : "";
18459
19005
  const scope = files2 > 1 ? ` across ${files2} files` : singleFile;
18460
19006
  if (isError)
18461
19007
  return `lens_diagnostics lsp \u2014 ${text.split("\n")[0] ?? "error"}`;
@@ -18473,7 +19019,10 @@ function createLensDiagnosticsTool(cacheManager2, getCwd, getLspService = getLSP
18473
19019
  if ((details.incompleteFiles ?? 0) > 0)
18474
19020
  return `lens_diagnostics${scope} \u2014 incomplete (${details.incompleteFiles} files not confirmed)`;
18475
19021
  if (count === 0 && details.unconfirmed)
18476
- return details.timedOut ? `lens_diagnostics${scope} \u2014 timed out (result may be incomplete)` : `lens_diagnostics${scope} \u2014 unconfirmed (server cannot confirm clean)`;
19022
+ return details.timedOut ? `lens_diagnostics${scope} \u2014 timed out (result may be incomplete)` : (
19023
+ // #4322: no language server answered; it is not a silent server.
19024
+ details.primaryUnavailable !== void 0 ? `lens_diagnostics${scope} \u2014 unconfirmed (no language server answered)` : `lens_diagnostics${scope} \u2014 unconfirmed (server cannot confirm clean)`
19025
+ );
18477
19026
  return `lens_diagnostics${scope} \u2014 ${count} ${noun}`;
18478
19027
  }
18479
19028
  const scanning = scanningSummaryLine(details, text);
@@ -18583,6 +19132,7 @@ function createLensDiagnosticsTool(cacheManager2, getCwd, getLspService = getLSP
18583
19132
  const hasExplicitPaths = Array.isArray(lspParams.paths) && lspParams.paths.length > 0;
18584
19133
  if (scope === "workspace" && !hasExplicitPath && !hasExplicitPaths) {
18585
19134
  lspParams.path = cwd;
19135
+ lspParams.pathAlreadyResolved = true;
18586
19136
  }
18587
19137
  const lspStart = Date.now();
18588
19138
  const result = await lspProbe.execute(_toolCallId, lspParams, signal, onUpdate, ctx.signal ? { cwd, signal: ctx.signal } : { cwd });
@@ -18701,7 +19251,7 @@ function appendProjectDiagnosticsDeltaLines(groups, cwd, report, severity, inclu
18701
19251
  const diagnostics = [...gated.live, ...gated.stale];
18702
19252
  const byFile = /* @__PURE__ */ new Map();
18703
19253
  for (const diagnostic of diagnostics) {
18704
- const filePath = path37.resolve(diagnostic.filePath);
19254
+ const filePath = path38.resolve(diagnostic.filePath);
18705
19255
  const bucket = byFile.get(filePath) ?? [];
18706
19256
  bucket.push(diagnostic);
18707
19257
  byFile.set(filePath, bucket);
@@ -18828,7 +19378,7 @@ function applyDeltaFreshnessGate(files, cwd, generatedAt) {
18828
19378
  })).filter((file) => file.warnings.length > 0);
18829
19379
  }
18830
19380
  function getDeltaFileGroup(groups, cwd, filePath) {
18831
- const rel = path37.relative(cwd, filePath);
19381
+ const rel = path38.relative(cwd, filePath);
18832
19382
  let group = groups.get(rel);
18833
19383
  if (!group) {
18834
19384
  group = {
@@ -18952,7 +19502,7 @@ Summary (turn delta): ${aw} actionable warning${aw === 1 ? "" : "s"} \xB7 ${cq}
18952
19502
  function createCurrentIgnoreFilter(cwd) {
18953
19503
  try {
18954
19504
  const matcher = getProjectIgnoreMatcher(cwd);
18955
- return (filePath) => !matcher.isIgnored(path37.resolve(filePath), false);
19505
+ return (filePath) => !matcher.isIgnored(path38.resolve(filePath), false);
18956
19506
  } catch {
18957
19507
  return () => true;
18958
19508
  }
@@ -18963,7 +19513,7 @@ function resolvePathsScope(cwd, rawPaths) {
18963
19513
  if (rawPaths.length > MAX_PATHS_ENTRIES) {
18964
19514
  throw new Error(`paths has ${rawPaths.length} entries, over the ${MAX_PATHS_ENTRIES} cap. Narrow the list or omit paths to scan without a restriction.`);
18965
19515
  }
18966
- const entries = rawPaths.filter((entry) => typeof entry === "string" && entry.trim().length > 0).map((entry) => path37.resolve(path37.isAbsolute(entry) ? entry : path37.resolve(cwd, entry)));
19516
+ const entries = rawPaths.filter((entry) => typeof entry === "string" && entry.trim().length > 0).map((entry) => resolveHostToolPath(entry, cwd));
18967
19517
  const missing = [];
18968
19518
  const fileEntries = [];
18969
19519
  const existingEntries = [];
@@ -18986,9 +19536,9 @@ function resolvePathsScope(cwd, rawPaths) {
18986
19536
  }
18987
19537
  const fileKeys = new Set(fileEntries.map((f) => normalizeFilePath(f)));
18988
19538
  const dirPrefixes = dirEntries.map((d) => normalizeFilePath(d));
18989
- const includeExplicitFile = (filePath) => fileKeys.has(normalizeFilePath(path37.resolve(filePath)));
19539
+ const includeExplicitFile = (filePath) => fileKeys.has(normalizeFilePath(path38.resolve(filePath)));
18990
19540
  const includeFile = (filePath) => {
18991
- const key = normalizeFilePath(path37.resolve(filePath));
19541
+ const key = normalizeFilePath(path38.resolve(filePath));
18992
19542
  if (fileKeys.has(key))
18993
19543
  return true;
18994
19544
  return dirPrefixes.some((prefix) => key === prefix || key.startsWith(`${prefix}/`));
@@ -19009,7 +19559,7 @@ function pathsScopeCacheOnlyNote(scope) {
19009
19559
  function pathsScopeMissingNote(scope) {
19010
19560
  if (!scope || scope.missing.length === 0)
19011
19561
  return "";
19012
- const shown2 = scope.missing.slice(0, 10).map((p) => path37.basename(p));
19562
+ const shown2 = scope.missing.slice(0, 10).map((p) => path38.basename(p));
19013
19563
  const more = scope.missing.length > shown2.length ? ` (+${scope.missing.length - shown2.length} more)` : "";
19014
19564
  return `
19015
19565
 
@@ -19140,7 +19690,7 @@ function projectDiagnosticToWidget(diagnostic, filePath) {
19140
19690
  }
19141
19691
  function diagnosticDedupKey(filePath, diagnostic) {
19142
19692
  const ruleId = normalizeRuleId(diagnostic.rule ?? diagnostic.tool ?? "");
19143
- return [path37.resolve(filePath), diagnostic.line ?? "?", ruleId].join(":");
19693
+ return [path38.resolve(filePath), diagnostic.line ?? "?", ruleId].join(":");
19144
19694
  }
19145
19695
  function runnerIdOf(diagnostic) {
19146
19696
  return diagnostic.tool ?? diagnostic.rule?.split(":", 1)[0] ?? "";
@@ -19154,8 +19704,8 @@ function runnerRetirementDecision(diagnostic, filePath, authoritativeRunnerIds,
19154
19704
  const realFilePath = realpathOrResolve(filePath);
19155
19705
  for (const entry of coverage) {
19156
19706
  const root = entry.root;
19157
- const relative9 = path37.relative(root, realFilePath);
19158
- const underRoot = relative9 === "" || !relative9.startsWith("..") && !path37.isAbsolute(relative9);
19707
+ const relative9 = path38.relative(root, realFilePath);
19708
+ const underRoot = relative9 === "" || !relative9.startsWith("..") && !path38.isAbsolute(relative9);
19159
19709
  if (!underRoot)
19160
19710
  continue;
19161
19711
  if (entry.files.has(realFilePath)) {
@@ -19205,7 +19755,11 @@ var UNCONFIRMED_REASON_SENTENCE = {
19205
19755
  binding_mismatch: "the file changed on disk since this result was computed",
19206
19756
  // #2052: no server was asked. Say WHY and say it is permanent for this
19207
19757
  // path, so the reader does not read an empty result as "clean" or retry it.
19208
- outside_project_root: "the file is outside every initialized session project root, so no language server was asked"
19758
+ outside_project_root: "the file is outside every initialized session project root, so no language server was asked",
19759
+ // #4403: the per-file note's wording, one sentence per cause.
19760
+ primary_unavailable: `no primary language server answered (${PRIMARY_UNAVAILABLE_CAUSE.unstarted})`,
19761
+ primary_disabled: `no primary language server answered (${PRIMARY_UNAVAILABLE_CAUSE.disabled})`,
19762
+ primary_disabled_fallback: `no primary language server answered (${PRIMARY_UNAVAILABLE_CAUSE.disabledFallback})`
19209
19763
  };
19210
19764
  var UNCONFIRMED_REASON_COUNT_LABEL = {
19211
19765
  budget: "timed out",
@@ -19214,7 +19768,10 @@ var UNCONFIRMED_REASON_COUNT_LABEL = {
19214
19768
  service_destroyed: "service reset mid-sweep",
19215
19769
  error: "errored",
19216
19770
  binding_mismatch: "stale binding",
19217
- outside_project_root: "outside project root"
19771
+ outside_project_root: "outside project root",
19772
+ primary_unavailable: "no primary started",
19773
+ primary_disabled: "primary disabled in config",
19774
+ primary_disabled_fallback: "primary disabled, fallback did not answer"
19218
19775
  };
19219
19776
  function classifyUnconfirmedReason(result, mismatched) {
19220
19777
  if (mismatched.has(result))
@@ -19279,7 +19836,7 @@ function mergeDiagnosticsWithWidgetSummaries(widgetSummaries, lspResults, projec
19279
19836
  const byFile = /* @__PURE__ */ new Map();
19280
19837
  const seen = /* @__PURE__ */ new Set();
19281
19838
  for (const summary of widgetSummaries) {
19282
- const filePath = path37.resolve(summary.filePath);
19839
+ const filePath = path38.resolve(summary.filePath);
19283
19840
  if (authoritativeLspFiles?.has(filePath))
19284
19841
  continue;
19285
19842
  const retained = summary.diagnostics ?? [];
@@ -19307,7 +19864,7 @@ function mergeDiagnosticsWithWidgetSummaries(widgetSummaries, lspResults, projec
19307
19864
  byFile.set(filePath, summarizeDiagnostics(filePath, diagnostics, existing?.hasFinalSnapshot ?? true));
19308
19865
  };
19309
19866
  for (const result of lspResults) {
19310
- const filePath = path37.resolve(result.filePath);
19867
+ const filePath = path38.resolve(result.filePath);
19311
19868
  for (const diagnostic of result.diagnostics ?? []) {
19312
19869
  addDiagnostic(filePath, {
19313
19870
  ...lspDiagnosticToWidget(diagnostic),
@@ -19327,19 +19884,19 @@ function mergeDiagnosticsWithWidgetSummaries(widgetSummaries, lspResults, projec
19327
19884
  });
19328
19885
  }
19329
19886
  for (const diagnostic of projectSnapshot?.diagnostics ?? []) {
19330
- addDiagnostic(path37.resolve(diagnostic.filePath), projectDiagnosticToWidget(diagnostic, diagnostic.filePath));
19887
+ addDiagnostic(path38.resolve(diagnostic.filePath), projectDiagnosticToWidget(diagnostic, diagnostic.filePath));
19331
19888
  }
19332
19889
  for (const diagnostic of deltaDiagnostics) {
19333
19890
  const widget = projectDiagnosticToWidget(diagnostic, diagnostic.filePath);
19334
19891
  const observedAt = widget.observedAt ?? deltaObservedAt;
19335
- addDiagnostic(path37.resolve(diagnostic.filePath), {
19892
+ addDiagnostic(path38.resolve(diagnostic.filePath), {
19336
19893
  ...widget,
19337
19894
  ...observedAt !== void 0 && { observedAt }
19338
19895
  });
19339
19896
  }
19340
19897
  return [...byFile.values()];
19341
19898
  }
19342
- async function applyInlineSuppressionsToSummaries(summaries, cwd, policyMap) {
19899
+ async function applyInlineSuppressionsToSummaries(summaries, cwd, policyMap, dispositionDropped) {
19343
19900
  return Promise.all(summaries.map(async (summary) => {
19344
19901
  if (!summary.diagnostics.length)
19345
19902
  return summary;
@@ -19353,12 +19910,19 @@ async function applyInlineSuppressionsToSummaries(summaries, cwd, policyMap) {
19353
19910
  });
19354
19911
  return policyKept2.length === summary.diagnostics.length ? summary : summarizeDiagnostics(summary.filePath, policyKept2, summary.hasFinalSnapshot);
19355
19912
  }
19356
- const { kept: policyKept } = applyFindingPolicy(summary.diagnostics, {
19913
+ const { kept: policyKept, inlineKept, disposed } = applyFindingPolicy(summary.diagnostics, {
19357
19914
  cwd,
19358
19915
  filePath: summary.filePath,
19359
19916
  content,
19360
19917
  policyMap
19361
19918
  });
19919
+ if (disposed.length < inlineKept.length) {
19920
+ const survivors = new Set(disposed);
19921
+ for (const d of inlineKept) {
19922
+ if (!survivors.has(d))
19923
+ dispositionDropped.push(d);
19924
+ }
19925
+ }
19362
19926
  for (const d of policyKept) {
19363
19927
  const { weak } = anchorsForDiagnostic(cwd, summary.filePath, d, content);
19364
19928
  if (getDisposition(cwd, weak)?.disposition === "flagged") {
@@ -19516,13 +20080,21 @@ async function formatFullMode(cwd, severity, lspService, cacheManager2, options
19516
20080
  const unconfirmedLspResults = lspResults.filter((result2) => result2.timedOut || result2.error || mismatchedLspResults.has(result2));
19517
20081
  const authoritativeLspFiles = /* @__PURE__ */ new Set();
19518
20082
  const unreconciledFiles = /* @__PURE__ */ new Set();
20083
+ const policyMap = loadProjectRulePolicyMap(cwd);
19519
20084
  for (const result2 of fullyCoveredLspResults) {
19520
20085
  try {
19521
20086
  const diagnostics = convertLspDiagnostics(result2.diagnostics, result2.filePath, { scanOrigin: "lens_diagnostics_full" });
19522
20087
  const retagged = retagAuxiliaryDiagnostics(diagnostics, result2.diagnostics, "", { cwd, fileRole: detectFileRole(result2.filePath) });
20088
+ const content = readFileContent(result2.filePath);
20089
+ const { kept: policyKept } = applyFindingPolicy(retagged, {
20090
+ cwd,
20091
+ filePath: result2.filePath,
20092
+ content: content ?? "",
20093
+ policyMap
20094
+ });
19523
20095
  const retired = reconcileScanDiagnostics(
19524
20096
  result2.filePath,
19525
- retagged,
20097
+ policyKept,
19526
20098
  true,
19527
20099
  // The service reserves this token when the file enters the scan. The
19528
20100
  // fallback preserves compatibility with older test doubles/services.
@@ -19535,32 +20107,31 @@ async function formatFullMode(cwd, severity, lspService, cacheManager2, options
19535
20107
  result2.observedAt
19536
20108
  );
19537
20109
  if (retired === true) {
19538
- authoritativeLspFiles.add(path37.resolve(result2.filePath));
20110
+ authoritativeLspFiles.add(path38.resolve(result2.filePath));
19539
20111
  } else {
19540
- unreconciledFiles.add(path37.resolve(result2.filePath));
20112
+ unreconciledFiles.add(path38.resolve(result2.filePath));
19541
20113
  recordDegradationOnce({
19542
20114
  kind: "diagnostic-retained-unreconciled",
19543
- subject: `${path37.resolve(result2.filePath)}:lsp`,
20115
+ subject: `${path38.resolve(result2.filePath)}:lsp`,
19544
20116
  reason: "confirmed diagnostic result could not replace retained widget state"
19545
20117
  });
19546
20118
  }
19547
20119
  } catch {
19548
- unreconciledFiles.add(path37.resolve(result2.filePath));
20120
+ unreconciledFiles.add(path38.resolve(result2.filePath));
19549
20121
  recordDegradationOnce({
19550
20122
  kind: "diagnostic-retained-unreconciled",
19551
- subject: `${path37.resolve(result2.filePath)}:lsp`,
20123
+ subject: `${path38.resolve(result2.filePath)}:lsp`,
19552
20124
  reason: "confirmed diagnostic result failed during widget reconciliation"
19553
20125
  });
19554
20126
  }
19555
20127
  }
19556
- const policyMap = loadProjectRulePolicyMap(cwd);
19557
20128
  const scannedSnapshot = filterProjectDiagnosticsSnapshot(rawProjectSnapshot, includeFile);
19558
20129
  const authoritativeRunnerIds = new Set(extracted.analyzed ?? []);
19559
20130
  const authoritativeRunnerCoverage = extracted.authoritativeCoverage ?? [];
19560
20131
  const foldedProjectSnapshot = foldExtraDiagnosticsIntoSnapshot(scannedSnapshot, extracted.diagnostics.filter((d) => includeFile(d.filePath)), extracted.runners, cwd);
19561
20132
  const projectSnapshot = applyProjectRulePolicy(foldedProjectSnapshot, policyMap, cwd);
19562
20133
  const projectDelta = applyProjectRulePolicy(filterProjectDiagnosticsDeltaReport(loadProjectDiagnosticsDeltaReport(cwd), includeFile), policyMap, cwd);
19563
- const authoritativeRetiredCount = getFileDiagnosticSummaries().filter((summary) => includeFile(summary.filePath) && authoritativeLspFiles.has(path37.resolve(summary.filePath)) && (summary.diagnostics?.length ?? 0) > 0).length;
20134
+ const authoritativeRetiredCount = getFileDiagnosticSummaries().filter((summary) => includeFile(summary.filePath) && authoritativeLspFiles.has(path38.resolve(summary.filePath)) && (summary.diagnostics?.length ?? 0) > 0).length;
19564
20135
  if (authoritativeRetiredCount > 0) {
19565
20136
  logLatency({
19566
20137
  type: "phase",
@@ -19613,9 +20184,10 @@ async function formatFullMode(cwd, severity, lspService, cacheManager2, options
19613
20184
  }
19614
20185
  });
19615
20186
  }
19616
- let summaries = await applyInlineSuppressionsToSummaries(mergeDiagnosticsWithWidgetSummaries(getFileDiagnosticSummaries().filter((summary) => includeFile(summary.filePath)), confirmedLspResults, projectSnapshot, projectDelta, authoritativeLspFiles, authoritativeRunnerIds, authoritativeRunnerCoverage), cwd, policyMap);
20187
+ const dispositionDropped = [];
20188
+ let summaries = await applyInlineSuppressionsToSummaries(mergeDiagnosticsWithWidgetSummaries(getFileDiagnosticSummaries().filter((summary) => includeFile(summary.filePath)), confirmedLspResults, projectSnapshot, projectDelta, authoritativeLspFiles, authoritativeRunnerIds, authoritativeRunnerCoverage), cwd, policyMap, dispositionDropped);
19617
20189
  if (unreconciledFiles.size > 0) {
19618
- summaries = summaries.map((summary) => unreconciledFiles.has(path37.resolve(summary.filePath)) ? summarizeDiagnostics(summary.filePath, markUnreconciledFindings(summary.diagnostics), summary.hasFinalSnapshot) : summary);
20190
+ summaries = summaries.map((summary) => unreconciledFiles.has(path38.resolve(summary.filePath)) ? summarizeDiagnostics(summary.filePath, markUnreconciledFindings(summary.diagnostics), summary.hasFinalSnapshot) : summary);
19619
20191
  }
19620
20192
  const parsedProjectScanObservedAt = projectSnapshot?.scannedAt ? Date.parse(projectSnapshot.scannedAt) : void 0;
19621
20193
  const projectScanObservedAt = parsedProjectScanObservedAt !== void 0 && Number.isFinite(parsedProjectScanObservedAt) ? parsedProjectScanObservedAt : void 0;
@@ -19637,14 +20209,26 @@ async function formatFullMode(cwd, severity, lspService, cacheManager2, options
19637
20209
  sources: projectDelta.sources,
19638
20210
  turnIndex: projectDelta.turnIndex
19639
20211
  }
19640
- }, 0, pathsScope);
20212
+ }, 0, pathsScope, 0, {
20213
+ dropped: dispositionDropped,
20214
+ // #1617: the #1616 suppressed-bucket rule applied to the analyzers —
20215
+ // a finding marked false-positive/won't-fix drops out of
20216
+ // `diagnostics` (fresh-fetch.ts's `record()`), and the drop stays
20217
+ // visible as a count. Review-round F4 (#1625): per-lane attribution
20218
+ // ("gitleaks 2, knip 1") says WHICH analyzer's marks are doing the
20219
+ // suppressing. A lane that's 100% suppressed still won't appear in
20220
+ // `runners`/`cold` as distinct from "ran clean" — #1623's territory.
20221
+ analyzers: extracted.dispositionSuppressed ?? 0,
20222
+ analyzersByLane: extracted.dispositionSuppressedByLane ?? {}
20223
+ });
19641
20224
  const missingNote = pathsScopeMissingNote(pathsScope);
19642
20225
  const unconfirmedByReason = tallyUnconfirmedReasons(unconfirmedLspResults, mismatchedLspResults);
19643
20226
  const lspServerBreakdown = tallyLspByPrimaryServer(confirmedLspResults, unconfirmedLspResults);
19644
20227
  const serverBreakdownClause = lspServerBreakdown.size > 1 ? ` \u2014 by server: ${formatLspServerBreakdown(lspServerBreakdown)}.` : "";
20228
+ const retryClause = [...unconfirmedByReason.keys()].every((reason) => reason === "primary_disabled") ? "" : "; re-run mode=full to retry them";
19645
20229
  const unconfirmedLspNote = unconfirmedLspResults.length > 0 ? `
19646
20230
 
19647
- \u26A0 LSP sweep: ${confirmedLspResults.length} file(s) confirmed via LSP, ${unconfirmedLspResults.length} unconfirmed (${formatUnconfirmedReasonClause(unconfirmedByReason)})${serverBreakdownClause} \u2014 NOT the same as 0 diagnostics for: ${unconfirmedLspResults.slice(0, 20).map((result2) => result2.filePath).join(", ")}${unconfirmedLspResults.length > 20 ? ", \u2026" : ""}. These files' LSP contribution is excluded from this result; re-run mode=full to retry them (they may still show findings above from cached/project-runner state).` : "";
20231
+ \u26A0 LSP sweep: ${confirmedLspResults.length} file(s) confirmed via LSP, ${unconfirmedLspResults.length} unconfirmed (${formatUnconfirmedReasonClause(unconfirmedByReason)})${serverBreakdownClause} \u2014 NOT the same as 0 diagnostics for: ${unconfirmedLspResults.slice(0, 20).map((result2) => result2.filePath).join(", ")}${unconfirmedLspResults.length > 20 ? ", \u2026" : ""}. These files' LSP contribution is excluded from this result${retryClause} (they may still show findings above from cached/project-runner state).` : "";
19648
20232
  const uncoveredScannerIds = [
19649
20233
  ...new Set(partiallyCoveredLspResults.flatMap((result2) => result2.unconfirmedServerIds ?? []))
19650
20234
  ].sort((a, b) => Number(a > b) - Number(a < b));
@@ -19692,10 +20276,6 @@ test-runner coverage is edit-scoped, NOT a full-project run: its findings only r
19692
20276
  const failedNote = failedAnalyzers.length > 0 ? `
19693
20277
 
19694
20278
  failed (ran, but no trustworthy result): ${failedAnalyzers.map(({ id, summary }) => `${id} \u2014 ${summary}`).join(", ")}. These analyzers were not cached and will be retried; absence of their findings is NOT a clean verdict.` : "";
19695
- const dispositionSuppressedByLaneText = Object.entries(extracted.dispositionSuppressedByLane ?? {}).map(([id, count]) => `${id} ${count}`).join(", ");
19696
- const dispositionSuppressedNote = (extracted.dispositionSuppressed ?? 0) > 0 ? `
19697
-
19698
- suppressed by disposition: ${extracted.dispositionSuppressed} finding(s) dropped from this result because they're marked false-positive or won't-fix (${dispositionSuppressedByLaneText}). Not a clean verdict for those locations \u2014 they were found, then intentionally hidden.` : "";
19699
20279
  const walkUnsafeRoot = explicitFiles === void 0 && isAtOrAboveHomeDir(cwd);
19700
20280
  const walkUnsafeRootNote = walkUnsafeRoot ? `
19701
20281
 
@@ -19743,7 +20323,9 @@ served from cache this call (not re-run): ${cachedAgeEntries.map(([id, ms]) => `
19743
20323
  coldReasons: extracted.coldReasons ?? {},
19744
20324
  failedAnalyzers,
19745
20325
  analyzerTimingsMs: extracted.timings,
19746
- dispositionSuppressed: extracted.dispositionSuppressed ?? 0,
20326
+ // #1634: every lane the call filtered — the sweep's and the
20327
+ // analyzers' — in one count, the one the text states.
20328
+ dispositionSuppressed: result.details.dispositionSuppressed ?? 0,
19747
20329
  dispositionSuppressedByLane: extracted.dispositionSuppressedByLane ?? {},
19748
20330
  // #1623: ms-old each cache-read-by-design lane's data was (today, only
19749
20331
  // test-runner) — lets a caller distinguish "just ran" from "served
@@ -19798,18 +20380,18 @@ served from cache this call (not re-run): ${cachedAgeEntries.map(([id, ms]) => `
19798
20380
  content: [
19799
20381
  {
19800
20382
  type: "text",
19801
- text: result.content[0].text + note + unconfirmedLspNote + auxiliaryCoverageNote + lspPrimaryVsAuxiliaryNote + partialNote + coldNote + testRunnerEditScopedNote + failedNote + dispositionSuppressedNote + walkUnsafeRootNote + scanTruncatedNote + generatedSkipNote + abortedNote + freshNote + cachedAgeNote + cheapScanStatusNote + missingNote
20383
+ text: result.content[0].text + note + unconfirmedLspNote + auxiliaryCoverageNote + lspPrimaryVsAuxiliaryNote + partialNote + coldNote + testRunnerEditScopedNote + failedNote + walkUnsafeRootNote + scanTruncatedNote + generatedSkipNote + abortedNote + freshNote + cachedAgeNote + cheapScanStatusNote + missingNote
19802
20384
  }
19803
20385
  ],
19804
20386
  details: { ...resultWithCold.details, timedOut }
19805
20387
  };
19806
20388
  }
19807
- if (missingNote || coldNote || partialNote || testRunnerEditScopedNote || failedNote || dispositionSuppressedNote || walkUnsafeRootNote || scanTruncatedNote || generatedSkipNote || abortedNote || freshNote || cachedAgeNote || cheapScanStatusNote || unconfirmedLspNote || auxiliaryCoverageNote || lspPrimaryVsAuxiliaryNote) {
20389
+ if (missingNote || coldNote || partialNote || testRunnerEditScopedNote || failedNote || walkUnsafeRootNote || scanTruncatedNote || generatedSkipNote || abortedNote || freshNote || cachedAgeNote || cheapScanStatusNote || unconfirmedLspNote || auxiliaryCoverageNote || lspPrimaryVsAuxiliaryNote) {
19808
20390
  return {
19809
20391
  content: [
19810
20392
  {
19811
20393
  type: "text",
19812
- text: result.content[0].text + unconfirmedLspNote + auxiliaryCoverageNote + lspPrimaryVsAuxiliaryNote + partialNote + coldNote + testRunnerEditScopedNote + failedNote + dispositionSuppressedNote + walkUnsafeRootNote + scanTruncatedNote + generatedSkipNote + abortedNote + freshNote + cachedAgeNote + cheapScanStatusNote + missingNote
20394
+ text: result.content[0].text + unconfirmedLspNote + auxiliaryCoverageNote + lspPrimaryVsAuxiliaryNote + partialNote + coldNote + testRunnerEditScopedNote + failedNote + walkUnsafeRootNote + scanTruncatedNote + generatedSkipNote + abortedNote + freshNote + cachedAgeNote + cheapScanStatusNote + missingNote
19813
20395
  }
19814
20396
  ],
19815
20397
  details: resultWithCold.details
@@ -19825,12 +20407,21 @@ async function projectResolvedCwd(summary, cwd) {
19825
20407
  diagnostics: summary.diagnostics ?? []
19826
20408
  };
19827
20409
  }
19828
- async function formatAllMode(cwd, severity, summaries = getFileDiagnosticSummaries(), detailOverrides = { mode: "all" }, staleDropped = 0, pathsScope, dependencyDemoted = 0) {
20410
+ async function formatAllMode(cwd, severity, summaries = getFileDiagnosticSummaries(), detailOverrides = { mode: "all" }, staleDropped = 0, pathsScope, dependencyDemoted = 0, fullModeDisposition = { dropped: [], analyzers: 0, analyzersByLane: {} }) {
19829
20411
  const footerCapped = summaries.reduce((n, s) => n + (s.diagnostics ?? []).filter((d) => d.footerRetired === true).length, 0);
19830
20412
  const staleNote = (staleDropped > 0 ? ` (${staleDropped} changed file${staleDropped === 1 ? "" : "s"} omitted as stale \u2014 use mode=full to rescan)` : "") + (dependencyDemoted > 0 ? ` (${dependencyDemoted} blocking finding${dependencyDemoted === 1 ? "" : "s"} demoted to [stale] \u2014 an imported file changed since; re-run to confirm)` : "") + (footerCapped > 0 ? ` (${footerCapped} demoted finding${footerCapped === 1 ? "" : "s"} capped after ${DEPENDENCY_DRIFT_MAX_DELIVERIES} unconfirmed deliveries \u2014 no longer shown in the pi-lens footer, still listed here; re-run to confirm)` : "");
19831
20413
  const isFullMode = detailOverrides.mode === "full";
19832
20414
  const includeFile = createScopedFileFilter(cwd, pathsScope);
19833
20415
  const policyMap = loadProjectRulePolicyMap(cwd);
20416
+ const inScope = (rows) => rows.reduce((n, d) => n + (matchesSeverity(d, severity) ? 1 : 0), 0);
20417
+ const sweepSuppressed = isFullMode ? inScope(fullModeDisposition.dropped) : 0;
20418
+ const lanes = [];
20419
+ for (const [id, count] of Object.entries(fullModeDisposition.analyzersByLane)) {
20420
+ if (count > 0)
20421
+ lanes.push(`${id} ${count}`);
20422
+ }
20423
+ const suppressedBreakdown = lanes.length > 0 ? `analyzer lanes: ${lanes.join(", ")}` : void 0;
20424
+ let dispositionSuppressed = sweepSuppressed + fullModeDisposition.analyzers;
19834
20425
  const dispositioned = isFullMode ? summaries : summaries.map((s) => {
19835
20426
  const diagnostics = s.diagnostics ?? [];
19836
20427
  const kept = applyCachedDispositions(diagnostics, cwd, s.filePath);
@@ -19838,6 +20429,9 @@ async function formatAllMode(cwd, severity, summaries = getFileDiagnosticSummari
19838
20429
  root: cwd,
19839
20430
  filePath: s.filePath
19840
20431
  });
20432
+ if (includeFile(s.filePath)) {
20433
+ dispositionSuppressed += inScope(getSuppressedDiagnostics(s.filePath)) + inScope(diagnostics) - inScope(kept);
20434
+ }
19841
20435
  if (policyKept.length === kept.length && kept.length === diagnostics.length)
19842
20436
  return s;
19843
20437
  return summarizeDiagnostics(s.filePath, policyKept, s.hasFinalSnapshot);
@@ -19863,14 +20457,19 @@ async function formatAllMode(cwd, severity, summaries = getFileDiagnosticSummari
19863
20457
  return s.blocking > 0 || s.errors > 0 || s.warnings > 0 || s.advisories > 0 || (s.diagnostics ?? []).some((d) => d.stale);
19864
20458
  });
19865
20459
  const pathsNote = isFullMode ? "" : pathsScopeCacheOnlyNote(pathsScope);
20460
+ const suppressedLine = dispositionSuppressedLine(dispositionSuppressed, suppressedBreakdown);
20461
+ const suppressedNote = suppressedLine ? `
20462
+ ${suppressedLine}` : "";
20463
+ const suppressedDetails = dispositionSuppressed > 0 ? { dispositionSuppressed } : {};
19866
20464
  if (withIssues.length === 0) {
19867
- const text = (visibleSummaries.length === 0 ? "No files diagnosed yet this session." : `No ${severity === "all" ? "" : severity + " "}issues across ${visibleSummaries.length} file${visibleSummaries.length === 1 ? "" : "s"} diagnosed this session. \u2713`) + staleNote + pathsNote;
20465
+ const text = (visibleSummaries.length === 0 ? "No files diagnosed yet this session." : `No ${severity === "all" ? "" : severity + " "}issues across ${visibleSummaries.length} file${visibleSummaries.length === 1 ? "" : "s"} diagnosed this session. \u2713`) + staleNote + suppressedNote + pathsNote;
19868
20466
  return {
19869
20467
  content: [{ type: "text", text }],
19870
20468
  details: {
19871
20469
  ...detailOverrides,
19872
20470
  filesChecked: visibleSummaries.length,
19873
- staleDropped
20471
+ staleDropped,
20472
+ ...suppressedDetails
19874
20473
  }
19875
20474
  };
19876
20475
  }
@@ -19882,7 +20481,7 @@ async function formatAllMode(cwd, severity, summaries = getFileDiagnosticSummari
19882
20481
  let totalWarnings = 0;
19883
20482
  let totalAdvisories = 0;
19884
20483
  for (const s of rendered) {
19885
- const rel = path37.relative(cwd, s.filePath);
20484
+ const rel = path38.relative(cwd, s.filePath);
19886
20485
  const parts = [];
19887
20486
  if (s.blocking > 0)
19888
20487
  parts.push(`\u{1F534} ${s.blocking} blocking`);
@@ -19934,7 +20533,10 @@ Summary (${visibleSummaries.length} files diagnosed this session):`,
19934
20533
  ].filter(Boolean).join("\n");
19935
20534
  return {
19936
20535
  content: [
19937
- { type: "text", text: lines.join("\n") + summary + staleNote }
20536
+ {
20537
+ type: "text",
20538
+ text: lines.join("\n") + summary + staleNote + suppressedNote
20539
+ }
19938
20540
  ],
19939
20541
  details: {
19940
20542
  ...detailOverrides,
@@ -19943,14 +20545,15 @@ Summary (${visibleSummaries.length} files diagnosed this session):`,
19943
20545
  totalErrors,
19944
20546
  totalWarnings,
19945
20547
  totalAdvisories,
19946
- staleDropped
20548
+ staleDropped,
20549
+ ...suppressedDetails
19947
20550
  }
19948
20551
  };
19949
20552
  }
19950
20553
 
19951
20554
  // dist/tools/lsp-navigation.js
19952
20555
  import * as nodeFs2 from "node:fs";
19953
- import * as path38 from "node:path";
20556
+ import * as path39 from "node:path";
19954
20557
  import { fileURLToPath as fileURLToPath2, pathToFileURL as pathToFileURL2 } from "node:url";
19955
20558
 
19956
20559
  // dist/tools/lsp-structured-output.js
@@ -20310,6 +20913,12 @@ function operationSupportStatus(operation, support) {
20310
20913
  return support.callHierarchy;
20311
20914
  return null;
20312
20915
  }
20916
+ function noServerAnsweredText(filePath) {
20917
+ const { disabledServerIds, fallbackServerIds } = disabledPrimaries(filePath);
20918
+ const cause = primaryUnavailableCause([...fallbackServerIds, ...disabledServerIds], disabledServerIds, fallbackServerIds);
20919
+ const who = disabledServerIds.length > 0 ? `Primary LSP (${disabledServerIds.join(", ")}) did not answer` : "no language server answered";
20920
+ return `No LSP server available for ${path39.basename(filePath)}: ${who} (${PRIMARY_UNAVAILABLE_CAUSE[cause]}).`;
20921
+ }
20313
20922
  function emptyReasonForOperation(operation) {
20314
20923
  if (operation === "signatureHelp")
20315
20924
  return "position-sensitive-or-no-signature";
@@ -20660,7 +21269,7 @@ function collectSearchReadsForOperation(operation, result, callHierarchyItem) {
20660
21269
  }
20661
21270
  function formatCapabilities(snapshots, filePath) {
20662
21271
  if (snapshots.length === 0) {
20663
- return filePath ? `No active LSP server for ${path38.basename(filePath)}. Open/touch the file first or run another LSP operation to start the server.` : "No active LSP servers in this session.";
21272
+ return filePath ? `No active LSP server for ${path39.basename(filePath)}. Open/touch the file first or run another LSP operation to start the server.` : "No active LSP servers in this session.";
20664
21273
  }
20665
21274
  const rows = [
20666
21275
  ["definition", (s) => !!s.operationSupport.definition],
@@ -20690,7 +21299,7 @@ function formatCapabilities(snapshots, filePath) {
20690
21299
  ];
20691
21300
  const lines = [];
20692
21301
  for (const snapshot of snapshots) {
20693
- const label = filePath ? `${snapshot.serverId} (${path38.basename(filePath)})` : `${snapshot.serverId} (${snapshot.root})`;
21302
+ const label = filePath ? `${snapshot.serverId} (${path39.basename(filePath)})` : `${snapshot.serverId} (${snapshot.root})`;
20694
21303
  lines.push(label);
20695
21304
  for (const [name, supported, note] of rows) {
20696
21305
  const suffix = note ? ` (${note})` : "";
@@ -20718,7 +21327,7 @@ function classifyCodeActions(actions) {
20718
21327
  }
20719
21328
  return { quickfix, refactor, other };
20720
21329
  }
20721
- async function openFileBestEffort(lspService, filePath, waitForDiagnostics = false) {
21330
+ async function openFileBestEffort(lspService, filePath, waitForDiagnostics = false, excludeServerIds) {
20722
21331
  let fileContent;
20723
21332
  try {
20724
21333
  fileContent = nodeFs2.readFileSync(filePath, "utf-8");
@@ -20731,7 +21340,8 @@ async function openFileBestEffort(lspService, filePath, waitForDiagnostics = fal
20731
21340
  await lspService.touchFile(filePath, fileContent, {
20732
21341
  diagnostics: waitForDiagnostics ? "document" : "none",
20733
21342
  source: "lsp_navigation",
20734
- clientScope: waitForDiagnostics ? "all" : "primary"
21343
+ clientScope: waitForDiagnostics ? "all" : "primary",
21344
+ ...excludeServerIds !== void 0 && { excludeServerIds }
20735
21345
  });
20736
21346
  } catch {
20737
21347
  }
@@ -20881,6 +21491,7 @@ function createLspNavigationTool(getFlag, mutationDeps) {
20881
21491
  const session = mutationDeps?.runtime?.captureSessionGeneration?.();
20882
21492
  const startedAt = Date.now();
20883
21493
  let supported = null;
21494
+ let noFileClient = false;
20884
21495
  let diagnosticsMode = "unknown";
20885
21496
  let columnResolution;
20886
21497
  let openedFileContent;
@@ -21030,7 +21641,7 @@ function createLspNavigationTool(getFlag, mutationDeps) {
21030
21641
  resultCount: 0
21031
21642
  });
21032
21643
  }
21033
- const filePath = rawPath ? path38.isAbsolute(rawPath) ? rawPath : path38.resolve(ctx.cwd || ".", rawPath) : "";
21644
+ const filePath = rawPath ? resolveHostToolPath(rawPath, ctx.cwd || ".") : "";
21034
21645
  let filePathIsDirectory = false;
21035
21646
  if (filePath) {
21036
21647
  try {
@@ -21057,16 +21668,17 @@ function createLspNavigationTool(getFlag, mutationDeps) {
21057
21668
  });
21058
21669
  }
21059
21670
  if (operation === "workspaceDiagnostics") {
21671
+ const policyCwd = ctx.cwd || process.cwd();
21060
21672
  const wsDiagSupport = rawPath ? await lspService.getWorkspaceDiagnosticsSupport(filePath) : await lspService.getWorkspaceDiagnosticsSupport(void 0, workspaceScopeAttribution);
21061
21673
  diagnosticsMode = wsDiagSupport?.mode ?? "unknown";
21062
21674
  if (rawPath && !filePathIsDirectory) {
21063
- const hasLSP2 = lspService.supportsLSP(filePath);
21675
+ const hasLSP2 = lspService.supportsLSP(filePath) || disabledPrimaries(filePath).disabledServerIds.length > 0;
21064
21676
  if (!hasLSP2) {
21065
21677
  return finalize({
21066
21678
  content: [
21067
21679
  {
21068
21680
  type: "text",
21069
- text: `No LSP server available for ${path38.basename(filePath)}. Check that the language server is installed.`
21681
+ text: `No LSP server available for ${path39.basename(filePath)}. Check that the language server is installed.`
21070
21682
  }
21071
21683
  ],
21072
21684
  isError: true
@@ -21077,8 +21689,12 @@ function createLspNavigationTool(getFlag, mutationDeps) {
21077
21689
  resultCount: 0
21078
21690
  });
21079
21691
  }
21080
- await openFileBestEffort(lspService, filePath, true);
21081
- const diagnostics = await lspService.getDiagnostics(filePath);
21692
+ const excludeServerIds = configDisabledAuxiliaryLspServerIds((name) => getFlag(name, ctx.cwd));
21693
+ const openedContent = await openFileBestEffort(lspService, filePath, true, excludeServerIds);
21694
+ const policy = applyProbeFindingPolicy(filePath, await lspService.getDiagnostics(filePath, void 0, excludeServerIds), policyCwd, openedContent, "all", "lsp_navigation");
21695
+ const diagnostics = policy.kept;
21696
+ const suppressedLine = dispositionSuppressedLine(policy.suppressed);
21697
+ const primaryUnavailable = describePrimaryUnavailable(void 0, lspService.getDiagnosticsHealth(filePath))?.text;
21082
21698
  const result3 = [
21083
21699
  {
21084
21700
  filePath,
@@ -21097,14 +21713,23 @@ function createLspNavigationTool(getFlag, mutationDeps) {
21097
21713
  {
21098
21714
  type: "text",
21099
21715
  text: `${note2}
21100
- ${JSON.stringify(result3)}`
21716
+ ${JSON.stringify(result3)}${primaryUnavailable ? `
21717
+ Note: ${primaryUnavailable}` : ""}${suppressedLine ? `
21718
+ Note: ${suppressedLine}` : ""}`
21101
21719
  }
21102
21720
  ],
21103
21721
  details: {
21104
21722
  operation,
21105
21723
  resultCount: resultCount2,
21106
21724
  diagnosticsMode,
21107
- coverage: "requested-file"
21725
+ coverage: "requested-file",
21726
+ ...policy.suppressed > 0 && {
21727
+ dispositionSuppressed: policy.suppressed
21728
+ },
21729
+ ...primaryUnavailable !== void 0 && {
21730
+ unconfirmed: true,
21731
+ primaryUnavailable
21732
+ }
21108
21733
  }
21109
21734
  }, {
21110
21735
  operation,
@@ -21113,12 +21738,23 @@ ${JSON.stringify(result3)}`
21113
21738
  resultCount: resultCount2
21114
21739
  });
21115
21740
  }
21116
- const allDiagnostics = await lspService.getAllDiagnostics();
21117
- const result2 = Array.from(allDiagnostics.entries()).map(([trackedFile, { diags }]) => ({
21118
- filePath: trackedFile,
21119
- diagnostics: diags,
21120
- count: diags.length
21121
- }));
21741
+ const allDiagnostics = await lspService.getAllDiagnostics(configDisabledAuxiliaryLspServerIds((name) => getFlag(name, ctx.cwd)));
21742
+ let snapshotSuppressed = 0;
21743
+ const result2 = Array.from(allDiagnostics.entries()).map(([trackedFile, { diags, ts }]) => {
21744
+ let kept = diags;
21745
+ if (diags.length > 0) {
21746
+ const policy = applyProbeFindingPolicy(trackedFile, diags, policyCwd, readFileContent(trackedFile), "all", "lsp_navigation");
21747
+ snapshotSuppressed += policy.suppressed;
21748
+ kept = policy.kept;
21749
+ }
21750
+ return {
21751
+ filePath: trackedFile,
21752
+ diagnostics: kept,
21753
+ count: kept.length,
21754
+ lag: formatCacheAgeLabel(ts)
21755
+ };
21756
+ });
21757
+ const snapshotSuppressedLine = dispositionSuppressedLine(snapshotSuppressed);
21122
21758
  const noteMap2 = {
21123
21759
  "push-only": "Note: push-only tracked diagnostics snapshot (not full workspace pull diagnostics).",
21124
21760
  pull: "Note: tracked diagnostics snapshot from active clients. Provide path to force file-level diagnostics collection"
@@ -21129,14 +21765,18 @@ ${JSON.stringify(result3)}`
21129
21765
  {
21130
21766
  type: "text",
21131
21767
  text: `${note}
21132
- ${JSON.stringify(result2)}`
21768
+ ${JSON.stringify(result2)}${snapshotSuppressedLine ? `
21769
+ Note: ${snapshotSuppressedLine}` : ""}`
21133
21770
  }
21134
21771
  ],
21135
21772
  details: {
21136
21773
  operation,
21137
21774
  resultCount: result2.length,
21138
21775
  diagnosticsMode,
21139
- coverage: "tracked-open-files"
21776
+ coverage: "tracked-open-files",
21777
+ ...snapshotSuppressed > 0 && {
21778
+ dispositionSuppressed: snapshotSuppressed
21779
+ }
21140
21780
  }
21141
21781
  }, {
21142
21782
  operation,
@@ -21167,7 +21807,7 @@ ${JSON.stringify(result2)}`
21167
21807
  content: [
21168
21808
  {
21169
21809
  type: "text",
21170
- text: `No LSP server available for ${path38.basename(filePath)}. Check that the language server is installed.`
21810
+ text: disabledPrimaries(filePath).disabledServerIds.length > 0 ? noServerAnsweredText(filePath) : `No LSP server available for ${path39.basename(filePath)}. Check that the language server is installed.`
21171
21811
  }
21172
21812
  ],
21173
21813
  isError: true
@@ -21180,13 +21820,14 @@ ${JSON.stringify(result2)}`
21180
21820
  }
21181
21821
  if (needsFilePath) {
21182
21822
  const support = await lspService.getOperationSupport(filePath);
21823
+ noFileClient = support === null;
21183
21824
  supported = operationSupportStatus(operation, support);
21184
21825
  if (supported === false) {
21185
21826
  return finalize({
21186
21827
  content: [
21187
21828
  {
21188
21829
  type: "text",
21189
- text: `LSP server for ${path38.basename(filePath)} does not advertise support for ${operation}`
21830
+ text: `LSP server for ${path39.basename(filePath)} does not advertise support for ${operation}`
21190
21831
  }
21191
21832
  ],
21192
21833
  isError: true,
@@ -21315,7 +21956,7 @@ ${JSON.stringify(result2)}`
21315
21956
  if (!newFilePath || newFilePath.trim().length === 0) {
21316
21957
  throw new Error("__BADINPUT__ newFilePath parameter required for rename_file");
21317
21958
  }
21318
- const resolvedNewFilePath = path38.isAbsolute(newFilePath) ? newFilePath : path38.resolve(ctx.cwd || ".", newFilePath);
21959
+ const resolvedNewFilePath = resolveHostToolPath(newFilePath, ctx.cwd || ".");
21319
21960
  const result2 = await lspService.renameFile(filePath, resolvedNewFilePath, {
21320
21961
  cwd: ctx.cwd || ".",
21321
21962
  apply: apply ?? false,
@@ -21459,7 +22100,15 @@ ${JSON.stringify(result2)}`
21459
22100
  }, { operation, filePath, failureKind: "lsp_error", resultCount: 0 });
21460
22101
  }
21461
22102
  const isEmpty = !result || Array.isArray(result) && result.length === 0;
21462
- const fileCtx = filePath ? " at " + path38.basename(filePath) : "";
22103
+ if (isEmpty && noFileClient) {
22104
+ return finalize({
22105
+ content: [
22106
+ { type: "text", text: noServerAnsweredText(filePath) }
22107
+ ],
22108
+ isError: true
22109
+ }, { operation, filePath, failureKind: "no_server", resultCount: 0 });
22110
+ }
22111
+ const fileCtx = filePath ? " at " + path39.basename(filePath) : "";
21463
22112
  const lineCtx = line ? ":" + line + ":" + character : "";
21464
22113
  let output = isEmpty ? "No results for " + operation + fileCtx + lineCtx : JSON.stringify(result);
21465
22114
  if (isEmpty && operation === "workspaceSymbol" && !rawPath) {
@@ -21701,11 +22350,11 @@ function resolveDefaultCwd() {
21701
22350
  const fromArg = process.argv.find((arg) => arg.startsWith("--cwd="))?.slice("--cwd=".length);
21702
22351
  return fromArg ?? process.env.PI_LENS_MCP_CWD ?? process.cwd();
21703
22352
  }
21704
- var DEFAULT_CWD = path39.resolve(resolveDefaultCwd());
22353
+ var DEFAULT_CWD = path40.resolve(resolveDefaultCwd());
21705
22354
  var lspReadyCwds = /* @__PURE__ */ new Set();
21706
22355
  var SERVER_FILE = fileURLToPath3(import.meta.url);
21707
- var SERVER_DIR = path39.dirname(SERVER_FILE);
21708
- var WORKER_PATH = path39.join(SERVER_DIR, "worker.js");
22356
+ var SERVER_DIR = path40.dirname(SERVER_FILE);
22357
+ var WORKER_PATH = path40.join(SERVER_DIR, "worker.js");
21709
22358
  var REBUILD_SCRIPT = resolveRebuildScript(SERVER_FILE);
21710
22359
  var STALENESS_STAT_PATH = process.env.PI_LENS_MCP_STALENESS_STAT_PATH ?? SERVER_FILE;
21711
22360
  var BUILD_STAMP = computeBuildStamp(STALENESS_STAT_PATH);
@@ -21727,7 +22376,7 @@ function isWarmBuildStale() {
21727
22376
  function findRepoRoot(start) {
21728
22377
  let dir = start;
21729
22378
  for (let depth = 0; depth < 6; depth++) {
21730
- const pkgPath = path39.join(dir, "package.json");
22379
+ const pkgPath = path40.join(dir, "package.json");
21731
22380
  if (fs25.existsSync(pkgPath)) {
21732
22381
  try {
21733
22382
  const pkg = JSON.parse(fs25.readFileSync(pkgPath, "utf8"));
@@ -21736,16 +22385,16 @@ function findRepoRoot(start) {
21736
22385
  } catch {
21737
22386
  }
21738
22387
  }
21739
- const parent = path39.dirname(dir);
22388
+ const parent = path40.dirname(dir);
21740
22389
  if (parent === dir)
21741
22390
  break;
21742
22391
  dir = parent;
21743
22392
  }
21744
- return path39.resolve(start, "..", "..");
22393
+ return path40.resolve(start, "..", "..");
21745
22394
  }
21746
- var REPO_ROOT = process.env.PI_LENS_MCP_REPO_ROOT ? path39.resolve(process.env.PI_LENS_MCP_REPO_ROOT) : findRepoRoot(SERVER_DIR);
22395
+ var REPO_ROOT = process.env.PI_LENS_MCP_REPO_ROOT ? path40.resolve(process.env.PI_LENS_MCP_REPO_ROOT) : findRepoRoot(SERVER_DIR);
21747
22396
  async function ensureReady(cwd) {
21748
- const normalized = path39.resolve(cwd);
22397
+ const normalized = path40.resolve(cwd);
21749
22398
  if (!shouldInitializeSessionRoot(normalized, lspReadyCwds)) {
21750
22399
  return;
21751
22400
  }
@@ -21791,22 +22440,17 @@ function getAutoSessionStatus() {
21791
22440
  }
21792
22441
  var IPC_PATH = ipcPathForCwd(DEFAULT_CWD);
21793
22442
  function rejectForeignTurnCwd(route, cwd) {
21794
- const message = `${route} cwd outside this server's workspace: ${path39.resolve(cwd)} not within ${DEFAULT_CWD}`;
22443
+ const message = `${route} cwd outside this server's workspace: ${path40.resolve(cwd)} not within ${DEFAULT_CWD}`;
21795
22444
  console.error(`[pi-lens-mcp] rejected ${message}`);
21796
22445
  return message;
21797
22446
  }
21798
22447
  function isWithinServerWorkspace(cwd) {
21799
- const target = path39.resolve(cwd);
21800
- const rel = path39.relative(DEFAULT_CWD, target);
21801
- return !rel.startsWith("..") && !path39.isAbsolute(rel);
22448
+ const target = path40.resolve(cwd);
22449
+ const rel = path40.relative(DEFAULT_CWD, target);
22450
+ return !rel.startsWith("..") && !path40.isAbsolute(rel);
21802
22451
  }
21803
22452
  function startIpcServer() {
21804
- if (process.platform !== "win32") {
21805
- try {
21806
- fs25.unlinkSync(IPC_PATH);
21807
- } catch {
21808
- }
21809
- }
22453
+ unlinkStaleUnixSocket(IPC_PATH);
21810
22454
  const requestQueue = createWarmIpcRequestQueue();
21811
22455
  const ipc = net.createServer((socket) => {
21812
22456
  socket.setEncoding("utf8");
@@ -21908,12 +22552,6 @@ function startIpcServer() {
21908
22552
  ipc.close();
21909
22553
  } catch {
21910
22554
  }
21911
- if (process.platform !== "win32") {
21912
- try {
21913
- fs25.unlinkSync(IPC_PATH);
21914
- } catch {
21915
- }
21916
- }
21917
22555
  };
21918
22556
  process.on("exit", cleanup);
21919
22557
  }
@@ -21974,7 +22612,11 @@ var lensDiagnosticsTool = createLensDiagnosticsTool(
21974
22612
  // `lens-guard` off — a config bypass on a durable cross-surface store.
21975
22613
  // Matches index.ts:1656 and the two sibling readers
21976
22614
  // (clients/runtime-tool-call.ts, clients/runtime-tool-result.ts).
21977
- () => isLensGuardEnabled()
22615
+ () => isLensGuardEnabled(),
22616
+ // #4323: `opengrep.enabled=false` keeps the explicit `source=lsp` probe from
22617
+ // starting Opengrep, as it does for `pilens_analyze`. Resolved per call
22618
+ // against the call's own project, like `lspNavigationTool` below (#792).
22619
+ (name, cwd) => createMcpHost(void 0, cwd).getFlag(name)
21978
22620
  );
21979
22621
  var astGrepClient = new AstGrepClient();
21980
22622
  var astGrepSearchTool = createAstGrepSearchTool(astGrepClient);
@@ -22293,7 +22935,7 @@ function enabledToolsForCwd(cwd) {
22293
22935
  }
22294
22936
  function formatAnalyze(result, cwd, mode, servedBy) {
22295
22937
  const lspNote = result.lsp ? ` \xB7 lsp ${result.lsp.diagnosticCount} (${result.lsp.status}${result.lsp.failureKind ? `: ${result.lsp.failureKind}` : ""}, ${result.lsp.durationMs}ms)` : "";
22296
- const summary = `${path39.relative(cwd, result.filePath) || result.filePath} [${mode}] \u2014 ${result.counts.blockers} blocking, ${result.counts.warnings} warning(s), ${result.counts.advisories} advisory(ies), ${result.counts.diagnostics} total` + (result.latency ? ` \xB7 ${result.latency.totalDurationMs}ms` : "") + lspNote + (result.counts.fixed > 0 ? ` \xB7 ${result.counts.fixed} auto-fixed` : "") + (servedBy ? `
22938
+ const summary = `${path40.relative(cwd, result.filePath) || result.filePath} [${mode}] \u2014 ${result.counts.blockers} blocking, ${result.counts.warnings} warning(s), ${result.counts.advisories} advisory(ies), ${result.counts.diagnostics} total` + (result.latency ? ` \xB7 ${result.latency.totalDurationMs}ms` : "") + lspNote + (result.counts.fixed > 0 ? ` \xB7 ${result.counts.fixed} auto-fixed` : "") + (servedBy ? `
22297
22939
 
22298
22940
  servedBy: ${servedBy}` : "");
22299
22941
  return renderToolText(summary, servedBy ? { ...result, servedBy } : result);
@@ -22427,7 +23069,7 @@ ${outcome.output}`, {
22427
23069
  }
22428
23070
  const { deduped, byRule, byFile } = summarizeScan(snapshot.diagnostics);
22429
23071
  const topRules = Object.entries(byRule).sort((a, b) => b[1] - a[1]);
22430
- const topFiles = Object.entries(byFile).sort((a, b) => b[1] - a[1]).slice(0, 15).map(([file, count]) => ({ file: path39.relative(cwd, file), count }));
23072
+ const topFiles = Object.entries(byFile).sort((a, b) => b[1] - a[1]).slice(0, 15).map(([file, count]) => ({ file: path40.relative(cwd, file), count }));
22431
23073
  const summaryLines = [
22432
23074
  `Scanned ${snapshot.filesScanned} file(s) [${snapshot.runners.join(", ")}] \u2192 ${deduped.length} unique diagnostic(s)` + (snapshot.diagnostics.length !== deduped.length ? ` (${snapshot.diagnostics.length} raw, ${snapshot.diagnostics.length - deduped.length} duplicate)` : ""),
22433
23075
  ...topRules.slice(0, 12).map(([rule, count]) => ` ${count}\xD7 ${rule}`)
@@ -22438,8 +23080,13 @@ ${outcome.output}`, {
22438
23080
  const skipNotice = generatedSkipNotice(snapshot, "mcp");
22439
23081
  if (skipNotice)
22440
23082
  summaryLines.push(skipNotice);
23083
+ const { dispositionSuppressed } = snapshot;
23084
+ const suppressedLine = dispositionSuppressedLine(dispositionSuppressed);
23085
+ if (suppressedLine)
23086
+ summaryLines.push(suppressedLine);
22441
23087
  return renderToolText(summaryLines.join("\n"), {
22442
23088
  filesScanned: snapshot.filesScanned,
23089
+ ...dispositionSuppressed > 0 ? { dispositionSuppressed } : {},
22443
23090
  runners: snapshot.runners,
22444
23091
  uniqueDiagnostics: deduped.length,
22445
23092
  rawDiagnostics: snapshot.diagnostics.length,
@@ -22480,7 +23127,7 @@ ${stalenessNote}` : ""), {
22480
23127
  }
22481
23128
  const lines = [
22482
23129
  `Top ${results.length} file(s) for "${query}":`,
22483
- ...results.map((result, i) => ` ${i + 1}. ${path39.relative(cwd, result.file)} (score ${result.score.toFixed(2)}, ${result.hits} hit(s), line ${result.startLine})`),
23130
+ ...results.map((result, i) => ` ${i + 1}. ${path40.relative(cwd, result.file)} (score ${result.score.toFixed(2)}, ${result.hits} hit(s), line ${result.startLine})`),
22484
23131
  ...stalenessNote ? ["", stalenessNote] : [],
22485
23132
  ...coverage ? [
22486
23133
  `Index covers ${coverage.files} files${coverage.truncated ? " (capped \u2014 results may be incomplete)" : ""}.`
@@ -22490,7 +23137,7 @@ ${stalenessNote}` : ""), {
22490
23137
  query,
22491
23138
  coverage,
22492
23139
  results: results.map((result) => {
22493
- const relFile = path39.relative(cwd, result.file);
23140
+ const relFile = path40.relative(cwd, result.file);
22494
23141
  return {
22495
23142
  file: relFile,
22496
23143
  score: result.score,
@@ -22529,12 +23176,12 @@ ${stalenessNote}` : ""), {
22529
23176
  });
22530
23177
  if (!report.available) {
22531
23178
  return {
22532
- ...renderToolText(`No module report for ${path39.relative(cwd, path39.resolve(cwd, file))} \u2014 not a symbol-bearing file, or unreadable.`, report, true),
23179
+ ...renderToolText(`No module report for ${path40.relative(cwd, path40.resolve(cwd, file))} \u2014 not a symbol-bearing file, or unreadable.`, report, true),
22533
23180
  isError: true
22534
23181
  };
22535
23182
  }
22536
23183
  const graphStaleness = graphStalenessNote(report.graphBuiltAt, "Review graph");
22537
- const summary = `${path39.relative(cwd, report.path) || report.path} [${report.staleness}] \u2014 ${report.summary.symbols} symbol(s), ${report.summary.exports} exported, ${report.api.length} in public API` + (graphStaleness ? `
23184
+ const summary = `${path40.relative(cwd, report.path) || report.path} [${report.staleness}] \u2014 ${report.summary.symbols} symbol(s), ${report.summary.exports} exported, ${report.api.length} in public API` + (graphStaleness ? `
22538
23185
 
22539
23186
  ${graphStaleness}` : "");
22540
23187
  if (view === "compact") {
@@ -22595,7 +23242,7 @@ ${graphStaleness}` : "");
22595
23242
  if (!result.found) {
22596
23243
  const suggestionSuffix = result.suggestions?.length ? ` Did you mean: ${result.suggestions.join(", ")}?` : " Use pilens_module_report to list symbols.";
22597
23244
  return {
22598
- ...renderToolText(`Symbol "${symbol}" not found in ${path39.basename(file)}.${suggestionSuffix}`, {
23245
+ ...renderToolText(`Symbol "${symbol}" not found in ${path40.basename(file)}.${suggestionSuffix}`, {
22599
23246
  found: false,
22600
23247
  ...result.suggestions ? { suggestions: result.suggestions } : {}
22601
23248
  }),
@@ -22604,7 +23251,7 @@ ${graphStaleness}` : "");
22604
23251
  }
22605
23252
  const sigSuffix = result.signature ? ` ${result.signature}` : "";
22606
23253
  const ambiguityNote = result.ambiguous ? ` (${result.ambiguous.count} matches \u2014 returned the ${result.kind}; pass \`kind\` to disambiguate: ${result.ambiguous.kinds.join(", ")})` : "";
22607
- const header = `${result.kind} ${result.name}${ambiguityNote}${sigSuffix} ${path39.relative(cwd, result.path)}:${result.startLine}-${result.endLine}`;
23254
+ const header = `${result.kind} ${result.name}${ambiguityNote}${sigSuffix} ${path40.relative(cwd, result.path)}:${result.startLine}-${result.endLine}`;
22608
23255
  return {
22609
23256
  content: [
22610
23257
  {
@@ -22642,14 +23289,14 @@ ${result.source ?? ""}`
22642
23289
 
22643
23290
  Nested outline:
22644
23291
  ${JSON.stringify(result.outline)}` : "";
22645
- const text = result.error ? `Could not read enclosing range in ${path39.basename(file)}:${result.line}: ${result.error}${warningSuffix}${outlineSuffix}` : `No enclosing symbol/callback found in ${path39.basename(file)}:${result.line}.${warningSuffix}`;
23292
+ const text = result.error ? `Could not read enclosing range in ${path40.basename(file)}:${result.line}: ${result.error}${warningSuffix}${outlineSuffix}` : `No enclosing symbol/callback found in ${path40.basename(file)}:${result.line}.${warningSuffix}`;
22646
23293
  return {
22647
23294
  ...renderToolText(text, { found: false, line: result.line }),
22648
23295
  isError: true
22649
23296
  };
22650
23297
  }
22651
23298
  const range = result.partial ? `${result.startLine}-${result.endLine} (partial of ${result.enclosingStartLine}-${result.enclosingEndLine})` : `${result.startLine}-${result.endLine}`;
22652
- const header = `${result.kind} ${result.name} ${path39.relative(cwd, result.path)}:${range}`;
23299
+ const header = `${result.kind} ${result.name} ${path40.relative(cwd, result.path)}:${range}`;
22653
23300
  return {
22654
23301
  content: [
22655
23302
  {
@@ -22684,7 +23331,7 @@ ${result.source ?? ""}`
22684
23331
  ]),
22685
23332
  ...renderLspBrokenStatusLines(brokenServers),
22686
23333
  ...renderDegradationLines(degradations),
22687
- last ? `Last dispatch: ${path39.basename(last.filePath)} \u2014 ${last.totalDurationMs}ms, ${last.totalDiagnostics} diagnostic(s)` : "Last dispatch: none yet",
23334
+ last ? `Last dispatch: ${path40.basename(last.filePath)} \u2014 ${last.totalDurationMs}ms, ${last.totalDiagnostics} diagnostic(s)` : "Last dispatch: none yet",
22688
23335
  `Diagnostics this session: ${stats.totalShown} shown \xB7 ${stats.totalAutoFixed} auto-fixed \xB7 ${stats.totalUnresolved} unresolved`,
22689
23336
  autoSession ? `Auto session_start: ${autoSession.succeeded ? "succeeded" : autoSession.error ? "FAILED" : autoSession.attempted ? "in progress" : "not yet attempted"}${autoSession.firedAt ? ` (fired ${autoSession.firedAt})` : ""}${autoSession.error ? ` \u2014 ${autoSession.error}` : ""}` : "Auto session_start: disabled (PI_LENS_MCP_AUTO_SESSION not set)",
22690
23337
  turnEnd ? `Stop-hook turn-end: ${turnEnd.ran} ran \xB7 ${turnEnd.skipped} skipped${turnEnd.lastRunAt ? ` (last ran ${turnEnd.lastRunAt})` : ""}${turnEnd.lastSkipReason ? ` \u2014 last skip: ${turnEnd.lastSkipReason}${turnEnd.lastSkipAt ? ` at ${turnEnd.lastSkipAt}` : ""}` : ""}` : "Stop-hook turn-end: no activity recorded (hook not installed, or no Stop yet)",
@@ -22750,7 +23397,7 @@ ${result.source ?? ""}`
22750
23397
  const limit = typeof args.limit === "number" && Number.isFinite(args.limit) ? Math.max(1, Math.floor(args.limit)) : 5;
22751
23398
  const fileFilter = typeof args.file === "string" ? args.file : void 0;
22752
23399
  const recent = recentLatency(limit, fileFilter);
22753
- const summary = recent.length === 0 ? "No dispatch latency reports yet." : recent.map((report) => `${path39.basename(report.filePath)}: ${report.totalDurationMs}ms (${report.totalDiagnostics} diag${report.stoppedEarly ? ", stopped early" : ""})`).join("\n");
23400
+ const summary = recent.length === 0 ? "No dispatch latency reports yet." : recent.map((report) => `${path40.basename(report.filePath)}: ${report.totalDurationMs}ms (${report.totalDiagnostics} diag${report.stoppedEarly ? ", stopped early" : ""})`).join("\n");
22754
23401
  return renderToolText(summary, recent);
22755
23402
  }
22756
23403
  if (name === "pilens_session_start") {