@@ -1268,11 +1268,28 @@ def finding_inline_comment_body(finding)
12681268 ] . join ( "\n " )
12691269 end
12701270
1271- # Build the review summary body. `inline_findings` are anchored to the diff as
1272- # individual comments; `summary_findings` could not be anchored (their line is
1273- # not part of the diff) and are listed here with their path:line instead.
1274- def review_summary_body ( repo :, pr :, inline_findings :, summary_findings :, comment_min_confidence :, target_url :)
1275- total = inline_findings . length + summary_findings . length
1271+ def append_summary_findings ( lines , title , findings , omission_label :)
1272+ return if findings . empty?
1273+
1274+ lines << title
1275+ findings . first ( MAX_FINDINGS_PER_COMMENT ) . each do |finding |
1276+ location = finding . fetch ( "code_location" )
1277+ lines << "- **P#{ finding . fetch ( "priority" ) } #{ format ( "%.2f" , finding . fetch ( "confidence_score" ) ) } #{ finding . fetch ( "lens" ) } ** `#{ location . fetch ( "path" ) } :#{ location . fetch ( "line" ) } `: #{ finding . fetch ( "title" ) } "
1278+ lines << " - #{ finding . fetch ( "body" ) } "
1279+ end
1280+ if findings . length > MAX_FINDINGS_PER_COMMENT
1281+ lines << "- _#{ findings . length - MAX_FINDINGS_PER_COMMENT } additional #{ omission_label } omitted; inspect the workflow artifact for the full ledger._"
1282+ end
1283+ lines << ""
1284+ end
1285+
1286+ # Build the summary comment body. `inline_findings` are anchored to the diff as
1287+ # individual comments. If there are more anchorable findings than the inline
1288+ # comment cap allows, `overflow_inline_findings` are listed in the summary with
1289+ # their locations. `summary_findings` could not be anchored because their line
1290+ # is not part of the diff and are also listed here with path:line.
1291+ def review_summary_body ( repo :, pr :, inline_findings :, overflow_inline_findings :, summary_findings :, comment_min_confidence :, target_url :)
1292+ total = inline_findings . length + overflow_inline_findings . length + summary_findings . length
12761293 lines = [
12771294 MARKER ,
12781295 "**EvalOps PR lens review**" ,
@@ -1287,18 +1304,18 @@ def review_summary_body(repo:, pr:, inline_findings:, summary_findings:, comment
12871304 lines << ""
12881305 end
12891306
1290- unless summary_findings . empty?
1291- lines << "Findings outside the diff (not inline-anchorable):"
1292- summary_findings . first ( MAX_FINDINGS_PER_COMMENT ) . each do | finding |
1293- location = finding . fetch ( "code_location" )
1294- lines << "- **P #{ finding . fetch ( "priority" ) } #{ format ( "%.2f" , finding . fetch ( "confidence_score" ) ) } #{ finding . fetch ( "lens" ) } ** ` #{ location . fetch ( "path" ) } : #{ location . fetch ( "line" ) } `: #{ finding . fetch ( "title" ) } "
1295- lines << " - #{ finding . fetch ( "body" ) } "
1296- end
1297- if summary_findings . length > MAX_FINDINGS_PER_COMMENT
1298- lines << "- _ #{ summary_findings . length - MAX_FINDINGS_PER_COMMENT } additional finding(s) omitted; inspect the workflow artifact for the full ledger._"
1299- end
1300- lines << " "
1301- end
1307+ append_summary_findings (
1308+ lines ,
1309+ "Additional diff findings (not posted inline due to the #{ MAX_FINDINGS_PER_COMMENT } -comment cap):" ,
1310+ overflow_inline_findings ,
1311+ omission_label : "diff finding(s) "
1312+ )
1313+ append_summary_findings (
1314+ lines ,
1315+ "Findings outside the diff (not inline-anchorable):" ,
1316+ summary_findings ,
1317+ omission_label : "finding(s) "
1318+ )
13021319
13031320 lines << "_Repo: #{ repo } PR: ##{ pr } _"
13041321 lines . join ( "\n " )
@@ -1339,6 +1356,27 @@ def delete_marker_review_comments(repo:, pr:)
13391356 end
13401357 end
13411358
1359+ def post_summary_comment ( repo :, pr :, body :)
1360+ gh_api (
1361+ "--method" , "POST" , "repos/#{ repo } /issues/#{ pr } /comments" ,
1362+ input : JSON . generate ( body : body )
1363+ )
1364+ end
1365+
1366+ def post_inline_comment ( repo :, pr :, head_sha :, finding :)
1367+ location = finding . fetch ( "code_location" )
1368+ gh_api (
1369+ "--method" , "POST" , "repos/#{ repo } /pulls/#{ pr } /comments" ,
1370+ input : JSON . generate (
1371+ commit_id : head_sha ,
1372+ path : location . fetch ( "path" ) ,
1373+ line : Integer ( location . fetch ( "line" ) ) ,
1374+ side : "RIGHT" ,
1375+ body : finding_inline_comment_body ( finding )
1376+ )
1377+ )
1378+ end
1379+
13421380 # Remove the bot's prior published artifacts for this PR (marker issue-comment
13431381 # left by the legacy code path, and prior marker inline review comments) so the
13441382 # publication is idempotent across re-runs on the same head.
@@ -1347,42 +1385,34 @@ def clear_prior_publication(repo:, pr:)
13471385 delete_marker_review_comments ( repo : repo , pr : pr )
13481386 end
13491387
1350- # Publish findings as a single PR review: a summary body plus inline comments
1388+ # Publish findings as a marker summary issue comment plus inline PR comments
13511389 # anchored to each anchorable finding's code_location. Findings whose line is
1352- # not in the diff are folded into the summary body. Idempotent: prior marker
1353- # comments are deleted first.
1390+ # not in the diff, or that overflow the inline comment cap, are folded into the
1391+ # summary body. Idempotent: prior marker comments are deleted first.
13541392 def publish_review ( repo :, pr :, head_sha :, inline_findings :, summary_findings :, comment_min_confidence :, target_url :)
13551393 clear_prior_publication ( repo : repo , pr : pr )
13561394 return if inline_findings . empty? && summary_findings . empty?
13571395
1358- comments = inline_findings . first ( MAX_FINDINGS_PER_COMMENT ) . map do |finding |
1359- location = finding . fetch ( "code_location" )
1360- {
1361- path : location . fetch ( "path" ) ,
1362- line : Integer ( location . fetch ( "line" ) ) ,
1363- side : "RIGHT" ,
1364- body : finding_inline_comment_body ( finding )
1365- }
1366- end
1396+ inline_to_publish = inline_findings . first ( MAX_FINDINGS_PER_COMMENT )
1397+ overflow_inline_findings = inline_findings . drop ( MAX_FINDINGS_PER_COMMENT )
13671398
1368- payload = {
1369- commit_id : head_sha ,
1370- event : "COMMENT" ,
1399+ post_summary_comment (
1400+ repo : repo ,
1401+ pr : pr ,
13711402 body : review_summary_body (
13721403 repo : repo ,
13731404 pr : pr ,
1374- inline_findings : inline_findings ,
1405+ inline_findings : inline_to_publish ,
1406+ overflow_inline_findings : overflow_inline_findings ,
13751407 summary_findings : summary_findings ,
13761408 comment_min_confidence : comment_min_confidence ,
13771409 target_url : target_url
1378- ) ,
1379- comments : comments
1380- }
1381-
1382- gh_api (
1383- "--method" , "POST" , "repos/#{ repo } /pulls/#{ pr } /reviews" ,
1384- input : JSON . generate ( payload )
1410+ )
13851411 )
1412+
1413+ inline_to_publish . each do |finding |
1414+ post_inline_comment ( repo : repo , pr : pr , head_sha : head_sha , finding : finding )
1415+ end
13861416 end
13871417
13881418 def blocking_findings ( findings , block_min_confidence :)
@@ -1649,6 +1679,7 @@ def markdown_meta_report(result)
16491679 # single knob and now maps to the *block* threshold. The comment threshold
16501680 # defaults lower so medium-confidence findings are still shown.
16511681 legacy_block = ENV [ "PR_LENS_MIN_CONFIDENCE" ]
1682+ legacy_block = nil if legacy_block . to_s . empty?
16521683 options = {
16531684 comment_min_confidence : Float (
16541685 ENV . fetch ( "PR_LENS_COMMENT_MIN_CONFIDENCE" , EvalOpsPrLensReview ::DEFAULT_COMMENT_MIN_CONFIDENCE )
0 commit comments