feat: always post fresh review, supersede old with collapsed body #38
Reference in New Issue
Block a user
Delete Branch "feat/34-always-post-fresh"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #34
Summary
Replaces the edit-in-place +
reviewUnchanged()skip logic with a simpler model:<details>Evaluated against <sha>What changed
reviewUnchanged()function entirely--update-existingflag (always supersede+post)buildSupersededBody()— creates the collapsed history formatCommitIDtoReviewstruct (needed for supersede body)TestReviewUnchanged, addedTestBuildSupersededBodyWhy
The old logic caused reviews to silently skip re-posting when content was unchanged, leaving stale badges that blocked merges. This was a recurring friction point requiring manual intervention (delete stale reviews + push empty commits).
Net effect
Original reviewSuperseded — see current review for up-to-date findings.
Previous findings (commit
ecbae332)Gpt Review
Summary
The changes simplify review flow by always posting a fresh review and superseding the previous one with clear, user-friendly formatting. Legacy update-in-place logic is removed, making the process more robust and maintaining review history. All code and tests are updated accordingly.
Recommendation
APPROVE — The patch is correct, improves maintainability, and passes CI. The revised supersede logic is clear, and obsolete complexity is removed. The new 'buildSupersededBody' approach is idiomatic and the Review struct is cleanly extended to include CommitID. Tests cover the updated logic and edge cases. Merge as-is.
Review by gpt
Evaluated against
ecbae332Security Review
Summary
The changes simplify the review posting strategy and add a safe superseding flow. No exploitable security issues are evident; CI passed. One minor hardening opportunity exists around verifying ownership when locating previous reviews to supersede.
Findings
cmd/review-bot/main.goRecommendation
APPROVE — Overall the refactor appears sound, with CI passing. The new always-fresh review plus superseded-history approach does not introduce material security risks. As a defense-in-depth improvement, when locating an existing review to supersede and when scanning timeline events via the sentinel, also assert that the review/user login matches the authenticated account to avoid attempting to edit others' comments in edge cases. Otherwise, the commit footer and superseded body construction are benign, and the existing input validation for reviewer names mitigates injection via the sentinel.
Review by security
Evaluated against
fdd75699@@ -307,56 +316,33 @@ func main() {}[MINOR] Supersede logic identifies prior review and corresponding timeline event solely by a sentinel substring without confirming authorship. Although the Gitea API should prevent editing comments not owned by the token, an attacker could craft a review containing the sentinel and cause unnecessary edit attempts. Consider verifying that the located review/timeline event belongs to the authenticated user (e.g., fetch current user and compare to Review.User.Login) before calling EditComment.
Original reviewSuperseded — see current review for up-to-date findings.
Previous findings (commit
ecbae332)Sonnet Review
Summary
Overall approach is sound and simplifies the review flow, and CI is passing. However, there is a logic bug in the supersede path: the strict check compares the Gitea user login to the reviewer-name flag, which will usually not match and prevents superseding older reviews.
Findings
cmd/review-bot/main.gocmd/review-bot/main_test.goRecommendation
REQUEST_CHANGES — Fix the supersede logic by ensuring findOwnReviewStrict receives the correct expected login. For example, reuse the same ownLogin discovery used in hasSharedToken (scan existingReviews for the first review containing the sentinel and record r.User.Login), then pass that login into findOwnReviewStrict. Alternatively, if the token identity is trusted, pass an empty string to skip the strict check and rely on the sentinel alone. After correcting this, add unit tests for findOwnReviewStrict to validate both matching and mismatching login scenarios. Once addressed, the new always-post + supersede behavior should work as intended.
Review by sonnet
Evaluated against
ecbae332@@ -314,4 +323,4 @@existingReviews, err := giteaClient.ListReviews(ctx, owner, repoName, prNumber)if err != nil {slog.Warn("could not list existing reviews", "pr", prNumber, "error", err)} else {[MINOR] Selecting which previous review to supersede relies on findOwnReview returning the first match. If ListReviews does not guarantee reverse-chronological ordering, this may supersede an older review instead of the most recent one. Consider choosing the most recent review (e.g., by CreatedAt/ID) or filtering out already-superseded bodies.
@@ -540,3 +515,1 @@if r.State == newEvent && r.Body == newBody {return true}// buildSupersededBody creates the body for a superseded review: struck-through banner[NIT] If commitSHA is empty, the superseded summary renders as "Previous findings (commit )". Consider omitting the commit text when the SHA is empty to avoid awkward formatting.
@@ -543,0 +515,4 @@// buildSupersededBody creates the body for a superseded review: struck-through banner// with collapsed original content and the commit it was evaluated against.func buildSupersededBody(originalBody, commitSHA, sentinel string) string {shortSHA := commitSHA[MINOR] buildSupersededBody appends the sentinel to the superseded comment body, which means future runs will still detect the superseded review when looking for the sentinel. This can make selecting the correct review to supersede ambiguous. Consider removing the sentinel from superseded bodies or adding a marker (e.g., "Superseded") that findOwnReview skips.
Gpt Review
Summary
The changes simplify review flow by always posting a fresh review and superseding the previous one with clear, user-friendly formatting. Legacy update-in-place logic is removed, making the process more robust and maintaining review history. All code and tests are updated accordingly.
Recommendation
APPROVE — The patch is correct, improves maintainability, and passes CI. The revised supersede logic is clear, and obsolete complexity is removed. The new 'buildSupersededBody' approach is idiomatic and the Review struct is cleanly extended to include CommitID. Tests cover the updated logic and edge cases. Merge as-is.
Review by gpt
Evaluated against
ecbae332Security Review
Summary
Changes implement a safer, simpler review posting strategy without introducing new security risks. No injection, auth, or data exposure concerns were observed in the modified code paths.
Recommendation
APPROVE — The update to always post a fresh review and supersede prior reviews appears sound and does not weaken security. Path handling for system prompt files remains guarded against traversal and symlink attacks, and added content (short commit SHA) is non-sensitive. Proceed with merge.
Review by security
Evaluated against
ecbae332Sonnet Review
Summary
Overall approach is sound and simplifies the review flow, and CI is passing. However, there is a logic bug in the supersede path: the strict check compares the Gitea user login to the reviewer-name flag, which will usually not match and prevents superseding older reviews.
Findings
cmd/review-bot/main.gocmd/review-bot/main_test.goRecommendation
REQUEST_CHANGES — Fix the supersede logic by ensuring findOwnReviewStrict receives the correct expected login. For example, reuse the same ownLogin discovery used in hasSharedToken (scan existingReviews for the first review containing the sentinel and record r.User.Login), then pass that login into findOwnReviewStrict. Alternatively, if the token identity is trusted, pass an empty string to skip the strict check and rely on the sentinel alone. After correcting this, add unit tests for findOwnReviewStrict to validate both matching and mismatching login scenarios. Once addressed, the new always-post + supersede behavior should work as intended.
Review by sonnet
Evaluated against
ecbae332@@ -314,4 +323,4 @@existingReviews, err := giteaClient.ListReviews(ctx, owner, repoName, prNumber)if err != nil {slog.Warn("could not list existing reviews", "pr", prNumber, "error", err)} else {[MAJOR] findOwnReviewStrict is called with expectedLogin set to reviewer-name (*reviewerName), but reviews[i].User.Login contains the Gitea account login. This mismatch will typically cause the strict check to filter out all candidate reviews, so older reviews are never marked as superseded. Pass the actual Gitea login (e.g., derived via the sentinel from existing reviews or via a /user API call) or disable the strict check by passing an empty expectedLogin.
Gpt Review
Summary
This change eliminates in-place review updates in favor of always posting a fresh review and superseding older ones with a collapsed/strikethrough format. The logic is simpler, more robust, and CI passes.
Recommendation
APPROVE — The PR achieves its intended result: always posting a new review for every run and correctly archiving prior reviews. Dead code (
reviewUnchanged) and unused flags are cleanly removed, and the new utility (buildSupersededBody) is well-tested. The expanded tests for supersedure, skipping, edge cases, and the updated Review struct are sound. As the CI is green and the overall approach aligns with the repo's conventions, it is ready to merge.Review by gpt
Evaluated against
f6baa41bSecurity Review
Summary
Changes simplify review posting and add a safe supersede mechanism. No new security-relevant issues were introduced and CI has passed.
Recommendation
APPROVE — The new always-post + supersede flow looks sound from a security perspective. Inputs embedded into review bodies are validated or originate from trusted sources (commit SHA, prior bot review). Path handling for the optional system prompt remains guarded against traversal and symlink breakout. Proceed with merge.
Review by security
Evaluated against
f6baa41bSonnet Review
Summary
Clear, coherent refactor that simplifies the review update strategy and addresses stale review issues. Tests have been updated appropriately and CI is passing. A couple of small robustness improvements are suggested around locating the correct timeline comment to supersede.
Findings
cmd/review-bot/main.gocmd/review-bot/main.goRecommendation
APPROVE — Overall, the change achieves the intended behavior: always posting a fresh review and superseding previous ones with a clear, collapsed history. CI is green and tests cover the new logic. To make the supersede operation more robust when multiple reviews exist, update GetTimelineReviewCommentID usage (or the function itself) to identify and patch the latest matching review (e.g., match by CommitID or choose the event with the highest ID) so the correct previous review is superseded consistently. Otherwise, the PR looks good to merge.
Review by sonnet
Evaluated against
f6baa41b@@ -344,1 +332,3 @@}commentID, err := giteaClient.GetTimelineReviewCommentID(ctx, owner, repoName, prNumber, sentinel)if err != nil {slog.Warn("could not find old review comment ID for supersede", "error", err)[MINOR] When superseding the previous review, GetTimelineReviewCommentID is used to find a timeline comment containing the sentinel, but it returns the first matching event rather than the latest. If multiple reviews with the same sentinel exist, this may patch an older review instead of the most recent non-superseded one found by findOwnReview. Consider matching the timeline event to the selected review (e.g., by comparing CommitID or body) or selecting the latest matching event by ID.
@@ -543,1 +518,4 @@shortSHA := commitSHAif len(shortSHA) > 8 {shortSHA = shortSHA[:8]}[NIT] buildSupersededBody appends the sentinel to the superseded comment. This is useful for diagnostics but also increases the chance that future timeline scans pick a superseded event. Since findOwnReview already skips superseded bodies, consider adjusting GetTimelineReviewCommentID to prefer the latest or match by CommitID to avoid editing superseded entries, even if they contain the sentinel.