Skip to content

Count CodeRabbit's completion reply toward loop convergence#17

Merged
kristofferR merged 4 commits into
mainfrom
fix/loop-completion-reply-convergence
Jul 4, 2026
Merged

Count CodeRabbit's completion reply toward loop convergence#17
kristofferR merged 4 commits into
mainfrom
fix/loop-completion-reply-convergence

Conversation

@kristofferR

@kristofferR kristofferR commented Jul 4, 2026

Copy link
Copy Markdown
Owner

Problem

crq loop gates convergence on reviewed_by — a bot review object whose commit matches the exact head. When CodeRabbit processes an @coderabbitai review command but has nothing new to say (typical for trivial incremental pushes), it replies "Review finished" without submitting a review object, so reviewed_by stays false and the loop exits 2 (timeout) on a round that is in fact complete. This hit PRs #13, #15, and #16 during their review loops.

Fix

Feedback now counts a configured-bot issue comment as satisfying ReviewedBy — but only under a strict anchor: crq's own state (InFlight, History, or the live feedback wait) must prove a review command was fired for the current head, and the reply must have landed at/after that fire. Rate-limit replies are excluded. Without a state anchor, the pre-existing rule stands: issue comments never satisfy ReviewedBy, so a stale completion summary from an earlier head can't falsely converge a new round.

Covered by tests for the completion, pre-fire-reply, no-anchor, and rate-limit-reply cases.

https://claude.ai/code/session_01W4RViwpNDBmT2fs3qhQNx7

Summary by CodeRabbit

  • New Features
    • Added configurable recognition of an auto-generated “review finished” completion message (with the ability to disable it by setting the marker to empty).
    • Enhanced completion-aware feedback convergence by matching completion replies to the correct fired head/time.
  • Bug Fixes
    • Fixed cases where completion replies weren’t applied correctly across out-of-order or overlapping rounds.
    • Prevented adopting or waiting on review commands that were already answered by a prior completion reply.
    • Improved pump behavior to clear in-flight slots and awaiting feedback when completion-backed completion is detected.

A re-review with nothing new to say produces no review object —
CodeRabbit replies "Review finished" to the command and submits
nothing — so Feedback's commit-checked ReviewedBy scan never flips and
crq loop times out on a round that is complete.

The completion reply is the missing signal. A bare issue comment
carries no commit, so it only counts when crq's own state proves a
command was fired for the current head (InFlight, History, or the live
feedback wait) and the reply landed at/after that fire; rate-limit
replies are excluded. Without a state anchor the pre-existing rule
stands: issue comments never satisfy ReviewedBy.

Claude-Session: https://claude.ai/code/session_01W4RViwpNDBmT2fs3qhQNx7
@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a CompletionMarker config field with empty-value preservation, then extends feedback and service convergence logic to recognize matching completion replies and clear or skip completed rounds. New tests cover config loading, reply pairing, adoption, and sweep behavior.

Changes

Completion-marker config

Layer / File(s) Summary
Completion marker config
internal/crq/config.go, internal/crq/config_test.go
Adds CompletionMarker to config loading, reads CRQ_COMPLETION_MARKER with empty-value support, and tests env/file preservation of empty markers.

Completion-reply convergence

Layer / File(s) Summary
Completion reply fallback
internal/crq/feedback.go, internal/crq/feedback_test.go
Adds completion-reply detection and a Feedback fallback that uses persisted fired-time state to mark the configured bot reviewed, with tests for fired timing, round overlap, and paired command handling.

Service flow updates

Layer / File(s) Summary
Service convergence checks
internal/crq/service.go, internal/crq/service_test.go
Updates review-command adoption, feedback completion checks, inflight status, and sweep handling to account for completion replies tied to the fired command, with tests for adoption, clearing, and sweep completion.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Service
  participant Feedback
  participant Store
  participant IssueComments

  Service->>Service: existingReviewCommand
  Service->>Feedback: Feedback(repo, pr, head)
  Feedback->>Store: load persisted wait state
  Store-->>Feedback: fired time / awaiting entry
  Feedback->>IssueComments: scan for completion replies
  IssueComments-->>Feedback: matching comments
  Feedback-->>Service: ReviewedBy / convergence state
  Service->>Service: requiredFeedbackComplete / sweepFeedbackWaits
Loading

Possibly related PRs

Poem

I hop by comments, soft and bright,
"Review finished" guides the night.
When rounds complete, I tap the queue,
And empty waits go poof—hoppity do! 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly captures the main change: treating CodeRabbit's completion reply as loop-convergence evidence.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/loop-completion-reply-convergence

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1b680e98cf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/crq/feedback.go Outdated
@kristofferR

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

The configured bot also posts acknowledgement/skip/progress replies
after a command; accepting any post-fire bot comment could converge a
round that is still running. The fallback now matches a configurable
CompletionMarker (CRQ_COMPLETION_MARKER, default "Review finished");
an empty marker disables the fallback.

Claude-Session: https://claude.ai/code/session_01W4RViwpNDBmT2fs3qhQNx7

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 17434c02e4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/crq/feedback.go Outdated
@kristofferR

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

An earlier round still finishing can post its "Review finished" after a
new head's command fires, so a timestamp-only check could converge the
new round before its review ran. The bot answers every command with
exactly one auto-generated reply, so replies are now paired
chronologically with the earliest unanswered command; only a completion
whose paired command is the one fired for this round counts. Unpairable
replies are ignored, failing toward the old wait-then-timeout behavior.

Claude-Session: https://claude.ai/code/session_01W4RViwpNDBmT2fs3qhQNx7

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/crq/config.go`:
- Line 95: The CompletionMarker config in config.go is treating an explicit
empty CRQ_COMPLETION_MARKER as unset because it uses stringEnv, so the default
“Review finished” always wins. Update the configuration lookup in the config
loading path to distinguish presence from value for CompletionMarker, or add a
helper that preserves empty strings, so CRQ_COMPLETION_MARKER="" stays empty
instead of falling back to the default.

In `@internal/crq/feedback.go`:
- Around line 201-240: The fallback completion-reply logic in Feedback is
correct but too dense; extract it into a dedicated helper to simplify
orchestration and improve testability. Move the anchor resolution and completion
scan block into a method such as s.applyCompletionReplyFallback, keeping the
current InFlight/History/AwaitingFeedback lookup and the configured-bot,
non-rate-limited, completion-marker, notBefore checks unchanged. Update Feedback
to call the helper and use the existing symbols like needsConfiguredBotReview,
feedbackWaitStart, s.store.Load, and s.isCompletionReply so the behavior stays
identical.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: b2ffb2d9-4f7c-4e07-b816-5b8417409a37

📥 Commits

Reviewing files that changed from the base of the PR and between 50fc61d and 17434c0.

📒 Files selected for processing (3)
  • internal/crq/config.go
  • internal/crq/feedback.go
  • internal/crq/feedback_test.go
📜 Review details
🔇 Additional comments (3)
internal/crq/config.go (1)

16-34: LGTM!

internal/crq/feedback.go (1)

1007-1030: LGTM!

internal/crq/feedback_test.go (1)

72-156: LGTM!

Traced all five scenarios (fired-after, pre-fire, no-anchor, rate-limit, non-completion ack) against the implementation in feedback.go — assertions match the intended semantics.

Comment thread internal/crq/config.go Outdated
Comment thread internal/crq/feedback.go Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 41b36830a2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/crq/feedback.go Outdated
Comment on lines +1037 to +1038
if strings.TrimSpace(c.Body) == command && !s.isConfiguredBot(c.User.Login) {
pending = append(pending, commentTime(c))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip already-answered commands when pairing completions

When the same PR already contains an older @coderabbitai review that completed by submitting a review object (the existing inflightStatus path accepts that as done without requiring an issue-comment reply), this queues that old command in pending and nothing ever removes it. The current Review finished reply is then paired with the old timestamp, notBefore(cmdAt, firedAt) is false, and the configured bot remains unreviewed, so clean reruns still time out on PRs with earlier review commands.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skipped: comment is from another GitHub bot.

Comment thread internal/crq/feedback.go Outdated
Comment on lines +219 to +220
if !firedAt.IsZero() && s.completionReplyForFiredCommand(issueComments, firedAt) {
markReviewed(report.ReviewedBy, s.cfg.Bot)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject completed adopted commands before marking reviewed

When a later regular push is queued with a head commit whose committer date predates an earlier CodeRabbit command, existingReviewCommand can adopt that earlier command because a no-findings round leaves no review object for it to reject. The fresh evidence is that recordExistingReviewPosted then records that old command under the new head, so this completion fallback accepts the old Review finished reply and marks the new head reviewed without ever firing a review for it; this can make crq loop converge stale feedback.

Useful? React with 👍 / 👎.

Comment thread internal/crq/feedback.go Outdated
Comment on lines +219 to +220
if !firedAt.IsZero() && s.completionReplyForFiredCommand(issueComments, firedAt) {
markReviewed(report.ReviewedBy, s.cfg.Bot)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clear completion-backed waits outside crq loop

For autoreview-fired rounds that finish with only CodeRabbit's completion reply and no review object, there may be no Loop polling to call clearFeedbackWait; this branch only flips the transient FeedbackReport. Pump still clears/sweeps AwaitingFeedback from submitted reviews, so these completed no-finding rounds remain shown as waiting until the timeout even though the completion signal has arrived.

Useful? React with 👍 / 👎.

@kristofferR

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 08b8febb14

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/crq/feedback.go
Comment on lines +1111 to +1112
if len(pending) > 0 {
pending = pending[1:]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid pairing reviewed round completions with newer commands

When an older @coderabbitai review submits a review object and then its auto-reply is posted/updated to Review finished after a newer command has been fired, this branch removes the old command from pending before the old auto-reply is processed. The old completion is then paired with the newer pending command, so completionReplyForFiredCommand can mark the new head reviewed before its own review has completed; the review should not make a later auto-reply available to satisfy a different command.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skipped: comment is from another GitHub bot.

Comment thread internal/crq/feedback.go
Comment on lines +1084 to +1085
case s.isConfiguredBot(c.User.Login) && s.isAutoReply(c.Body):
events = append(events, event{kind: eventAutoReply, at: commentTime(c), id: c.ID, comment: c})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Match completion replies without the calibration footer

When a configured reviewer posts a completion-only reply that contains CRQ_COMPLETION_MARKER but not the CodeRabbit calibration footer (for example a custom bot, or a CodeRabbit reply whose hidden footer changes), this gate never records the comment as an auto-reply because it requires isAutoReply/CRQ_CAL_REPLY_MARKER. In that setup completionReplyForFiredCommand returns false and no-findings rounds still time out even though the operator configured a completion marker; the completion marker itself needs to qualify as a terminal reply.

Useful? React with 👍 / 👎.

Comment thread internal/crq/feedback.go
Comment on lines +1119 to +1123
pending = pending[1:]
out = append(out, reviewCommandReply{
commandID: cmd.ID,
commandAt: commentTime(cmd),
completion: s.isCompletionReply(ev.comment.Body) && !s.isRateLimited(ev.comment.Body),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Don't consume commands on progress auto-replies

If the bot emits a progress/ack auto-reply as a separate issue comment before the final Review finished reply, this pop removes the command even though the appended pair is marked completion: false. The later completion then finds no pending command and is ignored, so a clean no-findings round can sit in AwaitingFeedback until timeout; non-terminal auto-replies should not close the command unless they are the completion/rate-limit reply being handled.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/crq/service.go (1)

1111-1166: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the duplicated clear-and-reload logic.

The "clear feedback wait, then reload state (falling back to the stale state on error)" block is duplicated verbatim in both exit branches (all-reviewed vs completion-reply). Extracting avoids future divergence if one path is updated without the other.

♻️ Proposed refactor
+func (s *Service) clearFeedbackWaitAndReload(ctx context.Context, repo string, pr int, head string, state State) State {
+	s.clearFeedbackWait(ctx, repo, pr, head)
+	if updated, _, err := s.store.Load(ctx); err == nil {
+		return updated
+	}
+	return state
+}
+
 func (s *Service) sweepFeedbackWaits(ctx context.Context, state State) State {
 	...
 	reviewedBy := reviewedByForRound(reviews, botSet(awaited), oldest.Head, oldest.StartedAt)
 	if allReviewed(reviewedBy) {
-		s.clearFeedbackWait(ctx, oldest.Repo, oldest.PR, oldest.Head)
-		if updated, _, err := s.store.Load(ctx); err == nil {
-			return updated
-		}
-		return state
+		return s.clearFeedbackWaitAndReload(ctx, oldest.Repo, oldest.PR, oldest.Head, state)
 	}
 	...
 	if !s.completionReplyForFiredCommand(comments, reviews, oldest.StartedAt) {
 		return state
 	}
-	s.clearFeedbackWait(ctx, oldest.Repo, oldest.PR, oldest.Head)
-	if updated, _, err := s.store.Load(ctx); err == nil {
-		return updated
-	}
-	return state
+	return s.clearFeedbackWaitAndReload(ctx, oldest.Repo, oldest.PR, oldest.Head, state)
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/crq/service.go` around lines 1111 - 1166, The clear-feedback-wait
followed by store reload fallback logic is duplicated in sweepFeedbackWaits, in
both the allReviewed and completionReply branches. Extract that repeated
sequence into a small helper in Service (or a local helper) that calls
clearFeedbackWait and then s.store.Load, returning the refreshed state when
successful and the original state on error, and use it in both places to keep
the behavior identical.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/crq/config_test.go`:
- Around line 103-114: In TestLoadConfigPreservesEmptyCompletionMarkerFromFile,
the t.Cleanup closure ignores errors from os.Setenv and os.Unsetenv, which trips
errcheck. Update the cleanup logic to handle the return values explicitly, using
the existing old/had environment-state handling in the test, and make sure any
failure restoring CRQ_COMPLETION_MARKER is reported through the test (for
example via t.Fatal/t.Errorf) rather than discarded.

---

Outside diff comments:
In `@internal/crq/service.go`:
- Around line 1111-1166: The clear-feedback-wait followed by store reload
fallback logic is duplicated in sweepFeedbackWaits, in both the allReviewed and
completionReply branches. Extract that repeated sequence into a small helper in
Service (or a local helper) that calls clearFeedbackWait and then s.store.Load,
returning the refreshed state when successful and the original state on error,
and use it in both places to keep the behavior identical.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8e257626-45ff-4180-ad2a-40d67bafe00b

📥 Commits

Reviewing files that changed from the base of the PR and between 17434c0 and 08b8feb.

📒 Files selected for processing (6)
  • internal/crq/config.go
  • internal/crq/config_test.go
  • internal/crq/feedback.go
  • internal/crq/feedback_test.go
  • internal/crq/service.go
  • internal/crq/service_test.go
📜 Review details
🧰 Additional context used
🪛 golangci-lint (2.12.2)
internal/crq/config_test.go

[error] 110-110: Error return value of os.Setenv is not checked

(errcheck)


[error] 112-112: Error return value of os.Unsetenv is not checked

(errcheck)

🔇 Additional comments (7)
internal/crq/config.go (1)

34-34: LGTM! This correctly resolves the previously-flagged issue where CRQ_COMPLETION_MARKER="" was indistinguishable from unset — stringEnvAllowEmpty now preserves an explicit empty value, matching the new tests in config_test.go.

Also applies to: 95-95, 173-179

internal/crq/config_test.go (1)

4-4: LGTM!

Also applies to: 90-102

internal/crq/feedback.go (2)

45-223: LGTM! The extraction into applyCompletionReplyFallback/completionFallbackFiredAt/reviewCommandReplies correctly resolves the previously-flagged density concern, and the FIFO command/reply pairing logic checks out against all three new test scenarios (fired-head completion, pre-fire rejection, cross-round rejection).

Also applies to: 980-991, 1025-1136


992-1023: 🚀 Performance & Scalability

No redundant state load here. Feedback doesn’t already thread a loaded State into completionFallbackFiredAt, so this fallback lookup stands on its own.

			> Likely an incorrect or invalid review comment.
internal/crq/feedback_test.go (1)

72-277: LGTM! Solid coverage of the completion-reply convergence contract, including the cross-round rejection and command-pairing edge cases called out in the PR objectives.

internal/crq/service.go (1)

452-550: LGTM! reviewCommandHasCompletionReply adoption guard, the reviewedByForRound/allReviewed/feedbackCompleteForRound extraction, and the requiredFeedbackComplete/inflightStatus signature updates all check out against the new tests.

Also applies to: 943-947, 961-993, 1000-1055

internal/crq/service_test.go (1)

843-893: LGTM! Fixtures correctly exercise the adoption-rejection, in-flight-clearing, and sweep-clearing paths added in service.go.

Also applies to: 1025-1072, 1271-1316

Comment on lines +103 to +114
func TestLoadConfigPreservesEmptyCompletionMarkerFromFile(t *testing.T) {
old, had := os.LookupEnv("CRQ_COMPLETION_MARKER")
if err := os.Unsetenv("CRQ_COMPLETION_MARKER"); err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
if had {
os.Setenv("CRQ_COMPLETION_MARKER", old)
} else {
os.Unsetenv("CRQ_COMPLETION_MARKER")
}
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Unchecked errors from os.Setenv/os.Unsetenv in cleanup.

golangci-lint's errcheck flags both calls in the t.Cleanup closure.

🔧 Proposed fix
 	t.Cleanup(func() {
 		if had {
-			os.Setenv("CRQ_COMPLETION_MARKER", old)
+			if err := os.Setenv("CRQ_COMPLETION_MARKER", old); err != nil {
+				t.Errorf("failed to restore CRQ_COMPLETION_MARKER: %v", err)
+			}
 		} else {
-			os.Unsetenv("CRQ_COMPLETION_MARKER")
+			if err := os.Unsetenv("CRQ_COMPLETION_MARKER"); err != nil {
+				t.Errorf("failed to unset CRQ_COMPLETION_MARKER: %v", err)
+			}
 		}
 	})
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func TestLoadConfigPreservesEmptyCompletionMarkerFromFile(t *testing.T) {
old, had := os.LookupEnv("CRQ_COMPLETION_MARKER")
if err := os.Unsetenv("CRQ_COMPLETION_MARKER"); err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
if had {
os.Setenv("CRQ_COMPLETION_MARKER", old)
} else {
os.Unsetenv("CRQ_COMPLETION_MARKER")
}
})
func TestLoadConfigPreservesEmptyCompletionMarkerFromFile(t *testing.T) {
old, had := os.LookupEnv("CRQ_COMPLETION_MARKER")
if err := os.Unsetenv("CRQ_COMPLETION_MARKER"); err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
if had {
if err := os.Setenv("CRQ_COMPLETION_MARKER", old); err != nil {
t.Errorf("failed to restore CRQ_COMPLETION_MARKER: %v", err)
}
} else {
if err := os.Unsetenv("CRQ_COMPLETION_MARKER"); err != nil {
t.Errorf("failed to unset CRQ_COMPLETION_MARKER: %v", err)
}
}
})
🧰 Tools
🪛 golangci-lint (2.12.2)

[error] 110-110: Error return value of os.Setenv is not checked

(errcheck)


[error] 112-112: Error return value of os.Unsetenv is not checked

(errcheck)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/crq/config_test.go` around lines 103 - 114, In
TestLoadConfigPreservesEmptyCompletionMarkerFromFile, the t.Cleanup closure
ignores errors from os.Setenv and os.Unsetenv, which trips errcheck. Update the
cleanup logic to handle the return values explicitly, using the existing old/had
environment-state handling in the test, and make sure any failure restoring
CRQ_COMPLETION_MARKER is reported through the test (for example via
t.Fatal/t.Errorf) rather than discarded.

Source: Linters/SAST tools

@kristofferR
kristofferR merged commit ecf470e into main Jul 4, 2026
1 check passed
@kristofferR
kristofferR deleted the fix/loop-completion-reply-convergence branch July 17, 2026 10:07
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