Findings that reference a file+line within the diff are now posted as inline comments directly on that line. Findings outside the diff range stay in the body only.
Changes
gitea/diff.go: Parse unified diff to extract new-file line numbers per hunk
Findings that reference a file+line within the diff are now posted as inline comments directly on that line. Findings outside the diff range stay in the body only.
## Changes
- `gitea/diff.go`: Parse unified diff to extract new-file line numbers per hunk
- `gitea/client.go`: `PostReview` accepts optional `[]ReviewComment`
- `cmd/review-bot/main.go`: Maps findings → inline comments when line is in diff
## Tests
- Diff parser: multi-hunk, new files, empty diff, boundary lines
- PostReview: with comments, nil comments (omitted from payload)
Closes #26
Findings that reference a file+line within the diff are now posted as
inline comments directly on that line, in addition to appearing in the
summary table. Findings outside the diff range stay in the body only.
Implementation:
- gitea/diff.go: ParseDiffNewLines extracts new-file line numbers from
each hunk in the unified diff
- gitea/client.go: PostReview accepts optional []ReviewComment with
path + new_position + body (omitempty when nil)
- cmd/review-bot/main.go: maps findings → inline comments when the line
exists in the diff, passes them to PostReview
Tests:
- diff parser: multi-hunk, new files, empty diff, boundary lines
- PostReview: with comments, nil comments (omitted from payload)
- Hunk headers without comma ("@@ -1 +1 @@") now parse correctly by
splitting on comma OR space instead of comma only
- Explicit skip for "\ No newline at end of file" lines (was already
safe but now documents intent)
- Tests added for both edge cases (TDD: tests written first, confirmed
failure, then fixed)
Addresses sonnet findings #1 and #2 from PR #26 review.
Before posting, compare the new review body+event against the existing
review with the same sentinel. If identical, skip entirely — this
preserves conversation threads on inline comments and avoids
re-notifying reviewers for findings they already know about.
Only re-posts when findings actually change (fixed, new, or different).
Tests: 6 cases covering identical, different body, different state,
stale reviews, and different sentinels.
gpt-review-bot
approved these changes 2026-05-02 05:18:17 +00:00
The changes add support for mapping findings to inline comments on changed lines in the diff, refactoring review update logic, and extending test coverage for new behaviors. Implementation is idiomatic, test-backed, and aligns with repo conventions.
Recommendation
APPROVE — The PR is well-structured and incorporates a robust solution for associating findings with inline comments, including utility for diff range mapping and a conservative in-place review update strategy. The changes are covered with comprehensive tests and maintain clean separation of concerns. No issues found—ready to merge.
Review by gpt
# Gpt Review
## Summary
The changes add support for mapping findings to inline comments on changed lines in the diff, refactoring review update logic, and extending test coverage for new behaviors. Implementation is idiomatic, test-backed, and aligns with repo conventions.
## Recommendation
**APPROVE** — The PR is well-structured and incorporates a robust solution for associating findings with inline comments, including utility for diff range mapping and a conservative in-place review update strategy. The changes are covered with comprehensive tests and maintain clean separation of concerns. No issues found—ready to merge.
---
*Review by gpt*
<!-- review-bot:gpt -->
The changes add inline review comments mapped to diff lines and introduce safe, well-structured Gitea client methods for posting comments and editing review bodies. CI passed and the code follows good error handling and path safety practices, with only minor hardening opportunities identified.
Findings
#
Severity
File
Line
Finding
1
[MINOR]
gitea/client.go
138
PostReview constructs the request URL using raw owner and repo values without path-escaping (fmt.Sprintf(".../repos/%s/%s/...", owner, repo)). If these inputs ever contain characters like '/' or '..', it could lead to path injection or incorrect endpoints. Other methods (e.g., ListReviews) use url.PathEscape for these components; apply the same here.
2
[NIT]
cmd/review-bot/main.go
241
Inline comment bodies include unescaped LLM-generated content (f.Finding). While Gitea typically sanitizes Markdown/HTML, relying solely on downstream sanitization can risk XSS if settings change. Consider minimally sanitizing or constraining the content format before posting.
Recommendation
APPROVE — Overall, the implementation is sound and security-conscious (notably the workspace-bound file reads and consistent error wrapping). To further harden:
In gitea/client.go PostReview, escape owner and repo with url.PathEscape to align with other methods and prevent path injection edge cases.
Consider sanitizing or restricting the format of inline comment bodies prior to submission, or explicitly document the reliance on Gitea's Markdown sanitization.
With these minor adjustments, the feature should be secure in typical deployments.
Review by security
# Security Review
## Summary
The changes add inline review comments mapped to diff lines and introduce safe, well-structured Gitea client methods for posting comments and editing review bodies. CI passed and the code follows good error handling and path safety practices, with only minor hardening opportunities identified.
## Findings
| # | Severity | File | Line | Finding |
|---|----------|------|------|--------|
| 1 | [MINOR] | `gitea/client.go` | 138 | PostReview constructs the request URL using raw owner and repo values without path-escaping (fmt.Sprintf(".../repos/%s/%s/...", owner, repo)). If these inputs ever contain characters like '/' or '..', it could lead to path injection or incorrect endpoints. Other methods (e.g., ListReviews) use url.PathEscape for these components; apply the same here. |
| 2 | [NIT] | `cmd/review-bot/main.go` | 241 | Inline comment bodies include unescaped LLM-generated content (f.Finding). While Gitea typically sanitizes Markdown/HTML, relying solely on downstream sanitization can risk XSS if settings change. Consider minimally sanitizing or constraining the content format before posting. |
## Recommendation
**APPROVE** — Overall, the implementation is sound and security-conscious (notably the workspace-bound file reads and consistent error wrapping). To further harden:
- In gitea/client.go PostReview, escape owner and repo with url.PathEscape to align with other methods and prevent path injection edge cases.
- Consider sanitizing or restricting the format of inline comment bodies prior to submission, or explicitly document the reliance on Gitea's Markdown sanitization.
With these minor adjustments, the feature should be secure in typical deployments.
---
*Review by security*
<!-- review-bot:security -->
Well-scoped feature addition enabling inline review comments with a solid diff parser and comprehensive tests. API changes are propagated correctly and review update strategy is improved to preserve threads.
Findings
#
Severity
File
Line
Finding
1
[MINOR]
gitea/client.go
62
ReviewComment uses JSON field name "new_position"; verify this matches Gitea's API for creating review comments. Some APIs expect "position" or "line" instead, and a mismatch could silently drop inline comments.
2
[MINOR]
cmd/review-bot/main.go
244
Inline comment mapping uses new-file line numbers as NewPosition. Ensure this aligns with the server’s expectation (diff position vs. file line number) to avoid misplaced or rejected inline comments.
3
[NIT]
gitea/diff.go
18
Diff parser extracts new-file line ranges from unified diffs; consider documenting assumptions (e.g., only "+++ b/" headers are used to set current file) and any limitations (renames, binary diffs), though tests cover common cases.
4
[NIT]
cmd/review-bot/main.go
311
When marking a previous review as superseded, EditComment errors are logged but not acted upon. If superseding is critical to your workflow, consider surfacing or retrying these failures.
Recommendation
APPROVE — The implementation cleanly introduces inline review comments by parsing unified diffs and extending the Gitea client to submit comments alongside reviews. Tests cover the diff parser (multi-hunk, new files, edge markers) and the new client behavior (comments omitempty, EditComment, timeline lookup). The refactored review update strategy (edit-in-place, supersede on state change, worst-wins escalation without deletion) is thoughtful and documented in the new REVIEW_STRATEGY.md. One point to double-check is the payload schema for inline comments: "new_position" may not match what Gitea expects; confirm against the Gitea API (whether it requires "position" or "line" semantics, and whether values are diff positions vs. file line numbers). Aside from that, the changes are solid and CI has passed. I recommend approval.
Review by sonnet
# Sonnet Review
## Summary
Well-scoped feature addition enabling inline review comments with a solid diff parser and comprehensive tests. API changes are propagated correctly and review update strategy is improved to preserve threads.
## Findings
| # | Severity | File | Line | Finding |
|---|----------|------|------|--------|
| 1 | [MINOR] | `gitea/client.go` | 62 | ReviewComment uses JSON field name "new_position"; verify this matches Gitea's API for creating review comments. Some APIs expect "position" or "line" instead, and a mismatch could silently drop inline comments. |
| 2 | [MINOR] | `cmd/review-bot/main.go` | 244 | Inline comment mapping uses new-file line numbers as NewPosition. Ensure this aligns with the server’s expectation (diff position vs. file line number) to avoid misplaced or rejected inline comments. |
| 3 | [NIT] | `gitea/diff.go` | 18 | Diff parser extracts new-file line ranges from unified diffs; consider documenting assumptions (e.g., only "+++ b/" headers are used to set current file) and any limitations (renames, binary diffs), though tests cover common cases. |
| 4 | [NIT] | `cmd/review-bot/main.go` | 311 | When marking a previous review as superseded, EditComment errors are logged but not acted upon. If superseding is critical to your workflow, consider surfacing or retrying these failures. |
## Recommendation
**APPROVE** — The implementation cleanly introduces inline review comments by parsing unified diffs and extending the Gitea client to submit comments alongside reviews. Tests cover the diff parser (multi-hunk, new files, edge markers) and the new client behavior (comments omitempty, EditComment, timeline lookup). The refactored review update strategy (edit-in-place, supersede on state change, worst-wins escalation without deletion) is thoughtful and documented in the new REVIEW_STRATEGY.md. One point to double-check is the payload schema for inline comments: "new_position" may not match what Gitea expects; confirm against the Gitea API (whether it requires "position" or "line" semantics, and whether values are diff positions vs. file line numbers). Aside from that, the changes are solid and CI has passed. I recommend approval.
---
*Review by sonnet*
<!-- review-bot:sonnet -->
[NIT] Hunk header parsing uses strings.Split(line, "+") and then trims at comma/space. While adequate for typical diffs, consider a more targeted parse (e.g., locating the first '+' between the '@@' markers) to be more robust against unusual hunk headers.
**[NIT]** Hunk header parsing uses `strings.Split(line, "+")` and then trims at comma/space. While adequate for typical diffs, consider a more targeted parse (e.g., locating the first '+' between the '@@' markers) to be more robust against unusual hunk headers.
Replace the delete-and-repost strategy with edit-in-place:
1. No existing review → POST new (first run)
2. Same state, same body → skip entirely (threads preserved)
3. Same state, body changed → PATCH body in place via timeline API
4. State change needed → PATCH old body to "Superseded", POST new
This preserves conversation threads on inline comments. Replies to
findings are never lost. The only time a new review is posted is on
first run or when the state transitions (APPROVED ↔ REQUEST_CHANGES).
New Gitea client methods:
- EditComment: PATCH /repos/{owner}/{repo}/issues/comments/{id}
- GetTimelineReviewCommentID: finds the comment ID for a review body
by scanning the issue timeline for the sentinel
Also simplifies shouldEscalate: removes the login parameter requirement
for pre-posting scenarios (uses findOwnReview to get login from existing
review instead).
Tests: findOwnReview (4 cases), EditComment (2 cases),
GetTimelineReviewCommentID (2 cases), shouldEscalate (8 cases updated).
Solid feature addition: parses unified diffs and posts inline comments with good test coverage. However, the review escalation logic was altered in a way that can incorrectly post APPROVED when a sibling bot review has REQUEST_CHANGES on the first run, which is a functional regression.
Findings
#
Severity
File
Line
Finding
1
[MAJOR]
cmd/review-bot/main.go
268
Escalation now happens before posting and depends on deriving ownLogin from an existing review containing the sentinel. On the first run (no existing review with this sentinel), ownLogin is empty and escalation is skipped, so an APPROVED review may be posted even if a sibling bot (same token) already has REQUEST_CHANGES. Previously this was handled by escalating after posting using the posted user login.
2
[MINOR]
gitea/client.go
388
EditComment marshals the payload with json.Marshal but ignores the error (data, _ := json.Marshal(payload)). This can mask unexpected failures; handle and return the marshal error similar to PostReview.
3
[NIT]
cmd/review-bot/main.go
499
In reviewUnchanged, the condition existingEvent == r.State is redundant since existingEvent is assigned from r.State. Simplify to r.State == newEvent && r.Body == newBody.
Recommendation
REQUEST_CHANGES — Address the escalation logic regression to ensure the bot doesn't post APPROVED when a sibling review from the same user (same token) has REQUEST_CHANGES on a first run without a prior sentinel. Options:
Query the current user (the bot) via the Gitea API using the token to obtain ownLogin before deciding on escalation; then call shouldEscalate with that login.
Or retain a post-posting escalation fallback: after posting, if event == APPROVED, fetch reviews and re-check escalation using the posted review's user.login (as the previous implementation did), updating or superseding as needed.
Additionally, in gitea/client.go’s EditComment, handle the json.Marshal error instead of ignoring it to comply with the repository’s error-handling conventions. Finally, clean up the redundant comparison in reviewUnchanged for clarity. With these changes, the inline comment feature and review update strategy will be robust and aligned with the intended behavior.
Review by sonnet
# Sonnet Review
## Summary
Solid feature addition: parses unified diffs and posts inline comments with good test coverage. However, the review escalation logic was altered in a way that can incorrectly post APPROVED when a sibling bot review has REQUEST_CHANGES on the first run, which is a functional regression.
## Findings
| # | Severity | File | Line | Finding |
|---|----------|------|------|--------|
| 1 | [MAJOR] | `cmd/review-bot/main.go` | 268 | Escalation now happens before posting and depends on deriving ownLogin from an existing review containing the sentinel. On the first run (no existing review with this sentinel), ownLogin is empty and escalation is skipped, so an APPROVED review may be posted even if a sibling bot (same token) already has REQUEST_CHANGES. Previously this was handled by escalating after posting using the posted user login. |
| 2 | [MINOR] | `gitea/client.go` | 388 | EditComment marshals the payload with json.Marshal but ignores the error (data, _ := json.Marshal(payload)). This can mask unexpected failures; handle and return the marshal error similar to PostReview. |
| 3 | [NIT] | `cmd/review-bot/main.go` | 499 | In reviewUnchanged, the condition `existingEvent == r.State` is redundant since existingEvent is assigned from r.State. Simplify to `r.State == newEvent && r.Body == newBody`. |
## Recommendation
**REQUEST_CHANGES** — Address the escalation logic regression to ensure the bot doesn't post APPROVED when a sibling review from the same user (same token) has REQUEST_CHANGES on a first run without a prior sentinel. Options:
- Query the current user (the bot) via the Gitea API using the token to obtain ownLogin before deciding on escalation; then call shouldEscalate with that login.
- Or retain a post-posting escalation fallback: after posting, if event == APPROVED, fetch reviews and re-check escalation using the posted review's user.login (as the previous implementation did), updating or superseding as needed.
Additionally, in gitea/client.go’s EditComment, handle the json.Marshal error instead of ignoring it to comply with the repository’s error-handling conventions. Finally, clean up the redundant comparison in reviewUnchanged for clarity. With these changes, the inline comment feature and review update strategy will be robust and aligned with the intended behavior.
---
*Review by sonnet*
<!-- review-bot:sonnet -->
[MAJOR] Escalation now happens before posting and depends on deriving ownLogin from an existing review containing the sentinel. On the first run (no existing review with this sentinel), ownLogin is empty and escalation is skipped, so an APPROVED review may be posted even if a sibling bot (same token) already has REQUEST_CHANGES. Previously this was handled by escalating after posting using the posted user login.
**[MAJOR]** Escalation now happens before posting and depends on deriving ownLogin from an existing review containing the sentinel. On the first run (no existing review with this sentinel), ownLogin is empty and escalation is skipped, so an APPROVED review may be posted even if a sibling bot (same token) already has REQUEST_CHANGES. Previously this was handled by escalating after posting using the posted user login.
[NIT] In reviewUnchanged, the condition existingEvent == r.State is redundant since existingEvent is assigned from r.State. Simplify to r.State == newEvent && r.Body == newBody.
**[NIT]** In reviewUnchanged, the condition `existingEvent == r.State` is redundant since existingEvent is assigned from r.State. Simplify to `r.State == newEvent && r.Body == newBody`.
[MINOR] EditComment marshals the payload with json.Marshal but ignores the error (data, _ := json.Marshal(payload)). This can mask unexpected failures; handle and return the marshal error similar to PostReview.
**[MINOR]** EditComment marshals the payload with json.Marshal but ignores the error (data, _ := json.Marshal(payload)). This can mask unexpected failures; handle and return the marshal error similar to PostReview.
Security-focused changes look sound: inline comments are restricted to lines present in the diff, API requests use context and timeouts, and edit/escalation logic avoids destructive actions when not permitted. CI passed. I have a couple of minor hardening suggestions.
Findings
#
Severity
File
Line
Finding
1
[MINOR]
gitea/client.go
140
PostReview constructs the reviews API URL using owner and repo without url.PathEscape, which can lead to malformed paths if these contain unexpected characters or slashes. While inputs are typically controlled, escaping path segments (as done elsewhere in this client) is a defense-in-depth improvement.
2
[MINOR]
cmd/review-bot/main.go
477
shouldEscalate relies on detecting a 'sentinel' substring in review bodies to identify sibling bot reviews. An untrusted user could spoof the sentinel in their own review, potentially causing unintended escalation. Consider additionally verifying the matched review's user login matches the bot's account (derived independently from the API) before treating it as a sibling.
Recommendation
APPROVE — Overall, the implementation is secure and aligns with best practices: inline comments are only posted on lines verified from the parsed diff, all HTTP calls use a bounded http.Client with context, and update-in-place logic gracefully falls back on permission errors. To further harden the code: (1) use url.PathEscape for owner and repo in PostReview (and other endpoints that still use raw values) to prevent path mishandling; (2) tighten sibling review identification by cross-checking the bot's own login or another reliable identifier to prevent sentinel spoofing from influencing escalation decisions. With these minor adjustments, the changes should be robust.
Review by security
# Security Review
## Summary
Security-focused changes look sound: inline comments are restricted to lines present in the diff, API requests use context and timeouts, and edit/escalation logic avoids destructive actions when not permitted. CI passed. I have a couple of minor hardening suggestions.
## Findings
| # | Severity | File | Line | Finding |
|---|----------|------|------|--------|
| 1 | [MINOR] | `gitea/client.go` | 140 | PostReview constructs the reviews API URL using owner and repo without url.PathEscape, which can lead to malformed paths if these contain unexpected characters or slashes. While inputs are typically controlled, escaping path segments (as done elsewhere in this client) is a defense-in-depth improvement. |
| 2 | [MINOR] | `cmd/review-bot/main.go` | 477 | shouldEscalate relies on detecting a 'sentinel' substring in review bodies to identify sibling bot reviews. An untrusted user could spoof the sentinel in their own review, potentially causing unintended escalation. Consider additionally verifying the matched review's user login matches the bot's account (derived independently from the API) before treating it as a sibling. |
## Recommendation
**APPROVE** — Overall, the implementation is secure and aligns with best practices: inline comments are only posted on lines verified from the parsed diff, all HTTP calls use a bounded http.Client with context, and update-in-place logic gracefully falls back on permission errors. To further harden the code: (1) use url.PathEscape for owner and repo in PostReview (and other endpoints that still use raw values) to prevent path mishandling; (2) tighten sibling review identification by cross-checking the bot's own login or another reliable identifier to prevent sentinel spoofing from influencing escalation decisions. With these minor adjustments, the changes should be robust.
---
*Review by security*
<!-- review-bot:security -->
[MINOR] shouldEscalate relies on detecting a 'sentinel' substring in review bodies to identify sibling bot reviews. An untrusted user could spoof the sentinel in their own review, potentially causing unintended escalation. Consider additionally verifying the matched review's user login matches the bot's account (derived independently from the API) before treating it as a sibling.
**[MINOR]** shouldEscalate relies on detecting a 'sentinel' substring in review bodies to identify sibling bot reviews. An untrusted user could spoof the sentinel in their own review, potentially causing unintended escalation. Consider additionally verifying the matched review's user login matches the bot's account (derived independently from the API) before treating it as a sibling.
[MINOR] PostReview constructs the reviews API URL using owner and repo without url.PathEscape, which can lead to malformed paths if these contain unexpected characters or slashes. While inputs are typically controlled, escaping path segments (as done elsewhere in this client) is a defense-in-depth improvement.
**[MINOR]** PostReview constructs the reviews API URL using owner and repo without url.PathEscape, which can lead to malformed paths if these contain unexpected characters or slashes. While inputs are typically controlled, escaping path segments (as done elsewhere in this client) is a defense-in-depth improvement.
1. First-run escalation regression (MAJOR): Add post-posting escalation
fallback. After posting APPROVED on first run, check if a sibling
from the same user has REQUEST_CHANGES — if so, mark ours as
superseded and re-post as REQUEST_CHANGES.
2. json.Marshal error handling (MINOR): Return error from EditComment
instead of ignoring it with blank identifier.
3. Redundant condition (NIT): Remove dead assignment in reviewUnchanged
where existingEvent was assigned from r.State then compared to itself.
Explains the edit-in-place approach, state transition rules, worst-wins
escalation, and inline comment lifecycle. Includes a Mermaid state
diagram for visual reference.
aweiker
approved these changes 2026-05-02 06:12:57 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Findings that reference a file+line within the diff are now posted as inline comments directly on that line. Findings outside the diff range stay in the body only.
Changes
gitea/diff.go: Parse unified diff to extract new-file line numbers per hunkgitea/client.go:PostReviewaccepts optional[]ReviewCommentcmd/review-bot/main.go: Maps findings → inline comments when line is in diffTests
Closes #26
- Hunk headers without comma ("@@ -1 +1 @@") now parse correctly by splitting on comma OR space instead of comma only - Explicit skip for "\ No newline at end of file" lines (was already safe but now documents intent) - Tests added for both edge cases (TDD: tests written first, confirmed failure, then fixed) Addresses sonnet findings #1 and #2 from PR #26 review.Gpt Review
Summary
The changes add support for mapping findings to inline comments on changed lines in the diff, refactoring review update logic, and extending test coverage for new behaviors. Implementation is idiomatic, test-backed, and aligns with repo conventions.
Recommendation
APPROVE — The PR is well-structured and incorporates a robust solution for associating findings with inline comments, including utility for diff range mapping and a conservative in-place review update strategy. The changes are covered with comprehensive tests and maintain clean separation of concerns. No issues found—ready to merge.
Review by gpt
Security Review
Summary
The changes add inline review comments mapped to diff lines and introduce safe, well-structured Gitea client methods for posting comments and editing review bodies. CI passed and the code follows good error handling and path safety practices, with only minor hardening opportunities identified.
Findings
gitea/client.gocmd/review-bot/main.goRecommendation
APPROVE — Overall, the implementation is sound and security-conscious (notably the workspace-bound file reads and consistent error wrapping). To further harden:
With these minor adjustments, the feature should be secure in typical deployments.
Review by security
Sonnet Review
Summary
Well-scoped feature addition enabling inline review comments with a solid diff parser and comprehensive tests. API changes are propagated correctly and review update strategy is improved to preserve threads.
Findings
gitea/client.gocmd/review-bot/main.gogitea/diff.gocmd/review-bot/main.goRecommendation
APPROVE — The implementation cleanly introduces inline review comments by parsing unified diffs and extending the Gitea client to submit comments alongside reviews. Tests cover the diff parser (multi-hunk, new files, edge markers) and the new client behavior (comments omitempty, EditComment, timeline lookup). The refactored review update strategy (edit-in-place, supersede on state change, worst-wins escalation without deletion) is thoughtful and documented in the new REVIEW_STRATEGY.md. One point to double-check is the payload schema for inline comments: "new_position" may not match what Gitea expects; confirm against the Gitea API (whether it requires "position" or "line" semantics, and whether values are diff positions vs. file line numbers). Aside from that, the changes are solid and CI has passed. I recommend approval.
Review by sonnet
@@ -452,0 +482,4 @@for _, r := range reviews {if r.Stale {continue}[MINOR] old findingSuperseded by newer review@@ -0,0 +43,4 @@currentFile = ""continue}[NIT] Hunk header parsing uses
strings.Split(line, "+")and then trims at comma/space. While adequate for typical diffs, consider a more targeted parse (e.g., locating the first '+' between the '@@' markers) to be more robust against unusual hunk headers.Replace the delete-and-repost strategy with edit-in-place: 1. No existing review → POST new (first run) 2. Same state, same body → skip entirely (threads preserved) 3. Same state, body changed → PATCH body in place via timeline API 4. State change needed → PATCH old body to "Superseded", POST new This preserves conversation threads on inline comments. Replies to findings are never lost. The only time a new review is posted is on first run or when the state transitions (APPROVED ↔ REQUEST_CHANGES). New Gitea client methods: - EditComment: PATCH /repos/{owner}/{repo}/issues/comments/{id} - GetTimelineReviewCommentID: finds the comment ID for a review body by scanning the issue timeline for the sentinel Also simplifies shouldEscalate: removes the login parameter requirement for pre-posting scenarios (uses findOwnReview to get login from existing review instead). Tests: findOwnReview (4 cases), EditComment (2 cases), GetTimelineReviewCommentID (2 cases), shouldEscalate (8 cases updated).Sonnet Review
Summary
Solid feature addition: parses unified diffs and posts inline comments with good test coverage. However, the review escalation logic was altered in a way that can incorrectly post APPROVED when a sibling bot review has REQUEST_CHANGES on the first run, which is a functional regression.
Findings
cmd/review-bot/main.gogitea/client.gocmd/review-bot/main.goexistingEvent == r.Stateis redundant since existingEvent is assigned from r.State. Simplify tor.State == newEvent && r.Body == newBody.Recommendation
REQUEST_CHANGES — Address the escalation logic regression to ensure the bot doesn't post APPROVED when a sibling review from the same user (same token) has REQUEST_CHANGES on a first run without a prior sentinel. Options:
Additionally, in gitea/client.go’s EditComment, handle the json.Marshal error instead of ignoring it to comply with the repository’s error-handling conventions. Finally, clean up the redundant comparison in reviewUnchanged for clarity. With these changes, the inline comment feature and review update strategy will be robust and aligned with the intended behavior.
Review by sonnet
@@ -254,3 +265,4 @@existingReviews, err := giteaClient.ListReviews(ctx, owner, repoName, prNumber)if err != nil {log.Printf("Warning: could not list existing reviews: %v", err)} else {[MAJOR] Escalation now happens before posting and depends on deriving ownLogin from an existing review containing the sentinel. On the first run (no existing review with this sentinel), ownLogin is empty and escalation is skipped, so an APPROVED review may be posted even if a sibling bot (same token) already has REQUEST_CHANGES. Previously this was handled by escalating after posting using the posted user login.
@@ -449,3 +496,4 @@}return false}[NIT] In reviewUnchanged, the condition
existingEvent == r.Stateis redundant since existingEvent is assigned from r.State. Simplify tor.State == newEvent && r.Body == newBody.@@ -346,0 +385,4 @@return 0, fmt.Errorf("parse timeline (page %d): %w", page, err)}for _, ev := range events {if ev.Type == "review" && strings.Contains(ev.Body, sentinel) {[MINOR] EditComment marshals the payload with json.Marshal but ignores the error (data, _ := json.Marshal(payload)). This can mask unexpected failures; handle and return the marshal error similar to PostReview.
Security Review
Summary
Security-focused changes look sound: inline comments are restricted to lines present in the diff, API requests use context and timeouts, and edit/escalation logic avoids destructive actions when not permitted. CI passed. I have a couple of minor hardening suggestions.
Findings
gitea/client.gocmd/review-bot/main.goRecommendation
APPROVE — Overall, the implementation is secure and aligns with best practices: inline comments are only posted on lines verified from the parsed diff, all HTTP calls use a bounded http.Client with context, and update-in-place logic gracefully falls back on permission errors. To further harden the code: (1) use url.PathEscape for owner and repo in PostReview (and other endpoints that still use raw values) to prevent path mishandling; (2) tighten sibling review identification by cross-checking the bot's own login or another reliable identifier to prevent sentinel spoofing from influencing escalation decisions. With these minor adjustments, the changes should be robust.
Review by security
@@ -438,14 +477,52 @@ func validateReviewerName(name string) error {return nil[MINOR] shouldEscalate relies on detecting a 'sentinel' substring in review bodies to identify sibling bot reviews. An untrusted user could spoof the sentinel in their own review, potentially causing unintended escalation. Consider additionally verifying the matched review's user login matches the bot's account (derived independently from the API) before treating it as a sibling.
@@ -131,15 +138,18 @@ func (c *Client) GetFileContentRef(ctx context.Context, owner, repo, filepath, r// PostReview submits a review to a PR and returns the created review.// event should be "APPROVED" or "REQUEST_CHANGES".[MINOR] PostReview constructs the reviews API URL using owner and repo without url.PathEscape, which can lead to malformed paths if these contain unexpected characters or slashes. While inputs are typically controlled, escaping path segments (as done elsewhere in this client) is a defense-in-depth improvement.
Ship it.