Skip to content

[Feat] Restore line-anchored inline PR review comments on all providers - #1129

Merged
mrubens merged 4 commits into
developfrom
feat/inline-review-comments
Aug 6, 2026
Merged

mrubens merged 4 commits into
developfrom
feat/inline-review-comments

Conversation

@mrubens

@mrubens mrubens commented Aug 6, 2026 •

Copy link
Copy Markdown
Contributor

Summary

When the review workflow moved to the provider-neutral manage_source_control surface, it lost the ability to create new line-anchored inline comments: findings could only reply to existing review threads or be carried in the canonical summary comment as file.ts:42 references.

This restores inline commenting by adding a create_pull_request_review_comment action to manage_source_control, implemented for all five providers, and updating the review-code skill to use it.

Design

  • One comment per call. Each finding is its own API call, so a single bad anchor can never reject a batch of findings.
  • Server-resolved anchor SHA. The platform resolves the PR head SHA (GitHub) or diff_refs (GitLab) at call time; callers pass only path, line, side, and an optional startLine/startSide range.
  • Anchor rejection is a retryable error, not a capability gap. GitHub 422 / GitLab 400 / Gitea 422 anchor failures surface as a 422 with the provider message and retry guidance. applied:false stays reserved for genuine provider capability gaps, and this action never returns it.
  • Multi-line ranges are honored natively on GitHub and Azure DevOps and degrade to the end line with a warning on GitLab, Gitea, and Bitbucket.
  • Returned threadId matches the read surface per provider so the agent can immediately reply to or resolve the thread it just created.
  • The GitHub reply-quote injection explicitly excludes this action: review findings are agent-authored, not replies to a pending user message.

Provider mappings

Provider API Anchor validation
GitHub pulls.createReviewComment with call-time head SHA 422 on out-of-diff anchors
GitLab POST .../discussions with a position built from MR diff_refs 400 on unmappable positions (mapped to 422)
Gitea POST .../pulls/{n}/reviews with one positioned comment 422
Bitbucket POST .../comments with inline: {path, to/from} none (out-of-diff anchors land as non-diff comments)
Azure DevOps thread POST with threadContext file range none

Skill changes

All four review appendices in review-code/SKILL.md now:

  • prefer replying to an existing matching thread (unchanged), then post a new line-anchored inline comment per finding,
  • use provider-aware suggestion syntax (suggestion fences on GitHub/Gitea, suggestion:-0+0 on GitLab, plain code blocks on Bitbucket/ADO),
  • on anchor rejection: re-check the hunk, retry exactly once, then fall back to carrying the finding in the summary checklist with a file.ts:42 reference (the pre-migration recovery contract),
  • warn that Bitbucket/ADO do not validate anchors against the diff.

The finding_without_thread_anchor error scenario is replaced by inline_comment_anchor_rejected.

Review feedback addressed

  • GitLab positions now carry the file's real old_path/new_path resolved from the merge request diff list, so anchors on renamed files work (same-path fallback when the diff listing is unavailable).
  • The GitLab thread reader now falls back to old_path/old_line for old-side diff notes, so deleted-line threads stay matchable in sync reviews instead of being duplicated.

Known limitations

  • GitLab comments on unchanged context lines will be rejected (GitLab needs both old and new line numbers on the same position, which requires hunk-level diff parsing this change does not do); the skill steers agents toward changed lines and the retry/summary-carry path absorbs misses.
  • GitLab/Gitea/Bitbucket get single-line anchors only for now.

Testing

  • SDK: per-provider payload-asserting tests including LEFT-side variants, multi-line ranges, anchor-rejection mapping, missing diff_refs handling, renamed-file path resolution, and old-side thread anchoring.
  • API: dispatch + a regression test pinning that reply quoting is not applied to the new action.
  • Worker MCP: validation, param forwarding, and an end-to-end registered-tool test.
  • Skill text: assertions pinning the new inline-comment instructions, suggestion guidance, and recovery scenario.
  • pnpm lint, pnpm check-types, pnpm knip all pass.
  • Verified end-to-end on a local deployment: a github_pr_review task against a seeded-bug PR posted four correctly line-anchored inline review comments, catching all three seeded bugs plus one unseeded real issue.

The review skill lost the ability to create new line-anchored inline
comments when it moved to the provider-neutral manage_source_control
surface; findings could only reply to existing threads or be carried in
the summary comment as file:line references.

This adds a create_pull_request_review_comment action (one finding per
call) implemented for GitHub, GitLab, Gitea, Bitbucket, and Azure
DevOps, with anchor rejections surfaced as retryable 422 errors and the
review-code skill updated to post inline comments with provider-aware
suggestion blocks and a retry-once-then-summary-carry recovery.
@roomote-community

roomote-community Bot commented Aug 6, 2026 •

Copy link
Copy Markdown
Contributor

No code issues found. See task

  • packages/sdk/src/server/lib/pull-requests/source-control-pull-request-writes.ts:853 GitLab rename anchors always send the same path as both old_path and new_path, so changed lines in renamed files are rejected and cannot be published inline.
  • packages/sdk/src/server/lib/pull-requests/source-control-pull-request-writes.ts:855 GitLab LEFT-side comments are written with only old_line, but the read surface only exposes new_path and new_line; subsequent reviews cannot recognize the existing deleted-line thread and can duplicate it.
  • packages/sdk/src/server/lib/pull-requests/source-control-pull-request-writes.ts:1122 The GitLab rename lookup still stops after 50 diff pages. On merge requests where a renamed target appears later, it falls back to identical paths and GitLab rejects the inline anchor; follow pagination until it ends or surface an explicit fallback instead.

Reviewed e4b8e2f

mrubens added 3 commits August 6, 2026 00:28
Review feedback: positions now carry the file's real old_path/new_path
resolved from the merge request diff list (renamed files anchored
correctly, same-path fallback when the listing is unavailable), and the
thread reader falls back to old_path/old_line so deleted-line threads
stay matchable in sync reviews.
Review feedback: the five-page cap could miss a renamed file on merge
requests with more than 500 changed files. Follow pagination until the
listing ends, with a backstop beyond GitLab's own ~3,000-file diff cap
so it only guards against runaway loops.
…s hit

Review feedback: rather than an unbounded pagination loop against
arbitrary self-hosted GitLab servers, keep the runaway backstop but
report the same-path fallback explicitly through the write result
warnings (and in the anchor-rejection message) when the scan ends
before the diff listing does.
@mrubens
mrubens merged commit be419db into develop Aug 6, 2026
19 checks passed
@mrubens
mrubens deleted the feat/inline-review-comments branch August 6, 2026 11:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant