Skip to content

t2800: fix pulse idempotency guard for external-contributor label check#2801

Merged
alex-solovyev merged 1 commit intomainfrom
bugfix/pulse-idempotency-guard
Mar 4, 2026
Merged

t2800: fix pulse idempotency guard for external-contributor label check#2801
alex-solovyev merged 1 commit intomainfrom
bugfix/pulse-idempotency-guard

Conversation

@alex-solovyev
Copy link
Collaborator

@alex-solovyev alex-solovyev commented Mar 4, 2026

Summary

  • Replace fragile echo "$labels" | grep -q '^external-contributor$' with jq any(.labels[]; .name == "external-contributor") which returns a clean boolean true/false — no grep anchor ambiguity with multi-line output, trailing whitespace, or stderr contamination
  • Restructure the external-contributor gate from if/elif/else to if/else { if/else } so the comment-existence fallback is always reached when the label is not found (or API fails), instead of being skipped when grep fails unexpectedly
  • Apply the same jq-based pattern to the permission-failure comment idempotency check for consistency

Root Cause

PR #2796 fixed the 2>/dev/null silent-failure issue but kept the grep -q '^external-contributor$' pattern. This pattern is fragile because:

  1. ^ and $ anchors can fail with trailing whitespace/CR in the output
  2. 2>&1 mixes stderr into the label output, potentially breaking line boundaries
  3. As agent prompt documentation (not a script), the LLM interpreting these instructions could misapply the grep pattern

The elif structure also meant that if the grep check failed (even when the label was present), the code would skip the comment-existence fallback and go straight to posting a new comment — producing duplicates every pulse cycle (~2 min).

Verification

Tested against PR #2792 (the PR with 18 duplicate comments):

  • jq any() correctly returns true when external-contributor label is present
  • jq select+test correctly counts existing comments
  • Edge cases verified: API failure, no labels, empty output — all fall through to comment check safely

Closes #2800

Summary by CodeRabbit

  • Chores
    • Improved the robustness of automation script handling for labels and comments by refining detection mechanisms and edge case management.
    • Enhanced fallback logic to ensure reliable label and comment operations in various workflow states.
    • Strengthened reliability against potential API inconsistencies and multi-state scenarios.

Root cause: the grep pattern with ^ and $ anchors on echo output was fragile —
it could fail with trailing whitespace, carriage returns, or stderr contamination
from 2>&1. Since this is agent prompt documentation (not a script), the LLM
interpreting these instructions could also misapply the grep pattern.

Two fixes applied:
1. Label check: replace 'echo $labels | grep -q ^external-contributor$' with
   jq any(.labels[]; .name == "external-contributor") which returns a clean
   boolean true/false — no parsing ambiguity
2. Comment fallback: restructure from elif to nested if/else so the comment
   check is ALWAYS reached when the label is not found (or API fails), instead
   of being skipped when grep fails unexpectedly
3. Permission-failure comment check: same pattern — replace grep -qF with
   jq select+test for consistent, robust matching

Also applied the same jq-based pattern to the permission-failure comment
idempotency check for consistency.

Closes #2800
@github-actions github-actions bot added the bug Auto-created from TODO.md tag label Mar 4, 2026
@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request refines the idempotency guards within the pulse.md script, primarily focusing on the handling of external contributor labels and permission failure comments. The changes aim to prevent duplicate comments and ensure accurate state management by replacing fragile grep commands with more robust jq expressions and restructuring conditional logic for better fallback behavior, thereby improving the overall reliability of the automation.

Highlights

  • Robust Label Check: Replaced the grep-based external-contributor label check with a jq any() expression for improved reliability, addressing issues with multi-line output and grep anchor ambiguities.
  • Improved Idempotency Logic: Restructured the conditional flow for external-contributor checks from if/elif/else to nested if/else statements, ensuring that the fallback check for existing comments is always performed when the label is absent or API calls fail.
  • Consistent Comment Idempotency: Applied the same jq select+test pattern to the permission-failure comment idempotency check, enhancing its robustness and consistency with the other changes.
Changelog
  • .agents/scripts/commands/pulse.md
    • Improved the external-contributor label check by replacing grep with jq any() for more reliable boolean results.
    • Refactored the logic for handling external-contributor comments and labels to ensure the comment existence fallback is always evaluated.
    • Updated the permission-failure comment idempotency check to use jq select+test for robust matching.
Activity
  • No human activity has occurred on this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 4, 2026

Walkthrough

The pulse script's external contributor label and comment handling was refactored to replace fragile grep-based string matching with robust jq-driven boolean checks and count operations. A fallback mechanism was introduced to detect and conditionally re-apply labels when missing, and permission-failure handling now uses jq-computed counts for idempotent comment posting.

Changes

Cohort / File(s) Summary
External Contributor Label Detection
.agents/scripts/commands/pulse.md
Replaced grep-based label existence check with jq's any() function returning boolean; introduced fallback logic to detect existing comments and conditionally re-add labels or post new comments atomically.
Permission Check Failure Handling
.agents/scripts/commands/pulse.md
Replaced grep-based comment detection with jq-computed count (perm_comment_count); posts manual-intervention comment only when count is zero, preventing duplicate comments.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

bug

Poem

🔧 Grep gave way to jq's keen eye,
Boolean flags now ask and reply,
Fallback paths catch what's been done,
No duplicate labels in the sun,
The script's now strong, robust, and spry! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title precisely describes the main change: fixing the idempotency guard mechanism for the external-contributor label check in the pulse script, which is the core objective of this pull request.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch bugfix/pulse-idempotency-guard

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions
Copy link

github-actions bot commented Mar 4, 2026

🔍 Code Quality Report

�[0;35m[MONITOR]�[0m Code Review Monitoring Report

�[0;34m[INFO]�[0m Latest Quality Status:
SonarCloud: 0 bugs, 0 vulnerabilities, 113 code smells

�[0;34m[INFO]�[0m Recent monitoring activity:
Wed Mar 4 02:41:12 UTC 2026: Code review monitoring started
Wed Mar 4 02:41:12 UTC 2026: SonarCloud - Bugs: 0, Vulnerabilities: 0, Code Smells: 113

📈 Current Quality Metrics

  • BUGS: 0
  • CODE SMELLS: 113
  • VULNERABILITIES: 0

Generated on: Wed Mar 4 02:41:14 UTC 2026


Generated by AI DevOps Framework Code Review Monitoring

@sonarqubecloud
Copy link

sonarqubecloud bot commented Mar 4, 2026

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request makes a solid improvement by replacing fragile grep commands with more robust jq queries to enhance the idempotency of label and comment checks. This is a great step towards preventing duplicate comments. I've included a couple of suggestions to further strengthen the logic, especially for handling failure scenarios, to make the checks even more resilient. My feedback also includes removing 2>/dev/null in line with project best practices to improve debuggability.

Note: Security Review has been skipped due to the limited scope of the PR.

Comment on lines +90 to +97
if [ "$existing_comment" != "0" ] && [ -n "$existing_comment" ]; then
# Comment already exists but label is missing — re-add the label only
gh api "repos/<slug>/issues/<number>/labels" -X POST -f 'labels[]=external-contributor' 2>/dev/null || true
else
# Neither label nor comment exists — post comment and add label atomically
gh pr comment <number> --repo <slug> --body "This PR is from an external contributor (@<author>). Auto-merge is disabled for external PRs — a maintainer must review and merge manually." \
&& gh api "repos/<slug>/issues/<number>/labels" -X POST -f 'labels[]=external-contributor' 2>/dev/null || true
fi

Choose a reason for hiding this comment

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

medium

This logic for checking existing_comment can be made more robust to prevent posting duplicate comments. The current condition [ "$existing_comment" != "0" ] && [ -n "$existing_comment" ] will evaluate to false if $existing_comment is an empty string (e.g., if the gh command fails and produces no output), causing a new comment to be posted and potentially creating a duplicate.

A safer approach is to invert the logic and only post a comment if you are certain there are none. By checking [ "$existing_comment" = "0" ], you ensure that a new comment is posted only when the count is definitively zero. In all other cases (comment exists, or the check failed), the script will attempt the safer, idempotent action of re-adding the label.

Additionally, 2>/dev/null should be avoided to ensure error messages are visible for debugging. The || true is sufficient to prevent script termination on failure.

Suggested change
if [ "$existing_comment" != "0" ] && [ -n "$existing_comment" ]; then
# Comment already exists but label is missing — re-add the label only
gh api "repos/<slug>/issues/<number>/labels" -X POST -f 'labels[]=external-contributor' 2>/dev/null || true
else
# Neither label nor comment exists — post comment and add label atomically
gh pr comment <number> --repo <slug> --body "This PR is from an external contributor (@<author>). Auto-merge is disabled for external PRs — a maintainer must review and merge manually." \
&& gh api "repos/<slug>/issues/<number>/labels" -X POST -f 'labels[]=external-contributor' 2>/dev/null || true
fi
if [ "$existing_comment" = "0" ]; then
# Neither label nor comment exists — post comment and add label atomically
gh pr comment <number> --repo <slug> --body "This PR is from an external contributor (@<author>). Auto-merge is disabled for external PRs — a maintainer must review and merge manually." \
&& gh api "repos/<slug>/issues/<number>/labels" -X POST -f 'labels[]=external-contributor' || true
else
# Comment may exist (or check failed), but label is missing — re-add the label only
gh api "repos/<slug>/issues/<number>/labels" -X POST -f 'labels[]=external-contributor' || true
fi
References
  1. Avoid using '2>/dev/null' for blanket suppression of command errors in shell scripts to ensure that authentication, syntax, or system issues remain visible for debugging.

if ! echo "$comments" | grep -qF 'Permission check failed'; then
# Uses jq select+test for robust matching — avoids grep issues with multi-line output.
perm_comment_count=$(gh pr view <number> --repo <slug> --json comments --jq '[.comments[].body | select(test("Permission check failed"))] | length' 2>&1) || true
if [ "$perm_comment_count" = "0" ] || [ -z "$perm_comment_count" ]; then

Choose a reason for hiding this comment

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

medium

For improved robustness and to prevent duplicate comments, this check can be simplified. The || [ -z "$perm_comment_count" ] part of the condition could cause a new comment to be posted if the gh command fails and produces an empty string, even if a comment already exists.

By only checking [ "$perm_comment_count" = "0" ], you ensure a comment is posted only when the count is definitively zero. If the command fails and the variable is empty or contains an error message, the condition will be false, and the script will safely do nothing, preventing duplicate comments. This aligns with the fail-safe principle of idempotency checks.

Suggested change
if [ "$perm_comment_count" = "0" ] || [ -z "$perm_comment_count" ]; then
if [ "$perm_comment_count" = "0" ]; then

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
.agents/scripts/commands/pulse.md (2)

107-110: Mirror marker-based matching in permission-failure comments too.

This block has the same broad-text matching risk. Consider using a unique marker token (for example, [pulse-permission-check-failed]) in both the comment body and jq selector for strict idempotency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/scripts/commands/pulse.md around lines 107 - 110, The
permission-failure comment isn’t idempotent; update the jq selector that sets
perm_comment_count (variable perm_comment_count with jq test("Permission check
failed")) to match a unique marker token (e.g.
"[pulse-permission-check-failed]") and add that same marker token into the gh pr
comment body string emitted by the gh pr comment command so the
select(test(...)) and the posted comment use the identical marker for strict,
idempotent matching.

88-97: Use a deterministic marker token for comment idempotency matching.

Current phrase matching is broad and may match unrelated human comments. Add a hidden/stable marker in the bot comment and key jq test() to that marker.

Proposed diff
-  existing_comment=$(gh pr view <number> --repo <slug> --json comments --jq '[.comments[].body | select(test("external contributor"; "i"))] | length' 2>&1) || true
+  existing_comment=$(gh pr view <number> --repo <slug> --json comments --jq '[.comments[].body | select(test("\\[pulse-external-contributor\\]"))] | length' 2>&1) || true
@@
-    gh pr comment <number> --repo <slug> --body "This PR is from an external contributor (@<author>). Auto-merge is disabled for external PRs — a maintainer must review and merge manually." \
+    gh pr comment <number> --repo <slug> --body "[pulse-external-contributor] This PR is from an external contributor (@<author>). Auto-merge is disabled for external PRs — a maintainer must review and merge manually." \
       && gh api "repos/<slug>/issues/<number>/labels" -X POST -f 'labels[]=external-contributor' 2>/dev/null || true
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/scripts/commands/pulse.md around lines 88 - 97, The existing_comment
detection uses a broad regex and should instead match a deterministic hidden
marker; update the bot comment body sent by gh pr comment (the string passed to
gh pr comment <number> --repo <slug> --body ...) to include a stable token
(e.g., "[bot-marker:external-contributor:<hash>]" or similar) and change the
existing_comment assignment (the jq filter in existing_comment=$(gh pr view ...
--jq '...') ) to test only for that token using jq
test("bot-marker:external-contributor:<hash>"; "i"); keep the rest of the logic
(re-adding label vs posting comment+label) intact so the presence check is
idempotent and deterministic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In @.agents/scripts/commands/pulse.md:
- Around line 107-110: The permission-failure comment isn’t idempotent; update
the jq selector that sets perm_comment_count (variable perm_comment_count with
jq test("Permission check failed")) to match a unique marker token (e.g.
"[pulse-permission-check-failed]") and add that same marker token into the gh pr
comment body string emitted by the gh pr comment command so the
select(test(...)) and the posted comment use the identical marker for strict,
idempotent matching.
- Around line 88-97: The existing_comment detection uses a broad regex and
should instead match a deterministic hidden marker; update the bot comment body
sent by gh pr comment (the string passed to gh pr comment <number> --repo <slug>
--body ...) to include a stable token (e.g.,
"[bot-marker:external-contributor:<hash>]" or similar) and change the
existing_comment assignment (the jq filter in existing_comment=$(gh pr view ...
--jq '...') ) to test only for that token using jq
test("bot-marker:external-contributor:<hash>"; "i"); keep the rest of the logic
(re-adding label vs posting comment+label) intact so the presence check is
idempotent and deterministic.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b438d090-1ce6-43c6-bbc6-bb722e741e68

📥 Commits

Reviewing files that changed from the base of the PR and between 49f1415 and fcfa64d.

📒 Files selected for processing (1)
  • .agents/scripts/commands/pulse.md

@alex-solovyev alex-solovyev merged commit aa35156 into main Mar 4, 2026
31 of 33 checks passed
@alex-solovyev alex-solovyev deleted the bugfix/pulse-idempotency-guard branch March 4, 2026 02:50
alex-solovyev added a commit that referenced this pull request Mar 4, 2026
… function (t1391)

The LLM supervisor kept re-implementing the inline bash idempotency
guard incorrectly on each pulse cycle, causing 15+ duplicate
'external contributor' comments on PR #2792 despite 4 prior fix
attempts (PRs #2794, #2796, #2801, #2803) — all in pulse.md prompt
text.

Root cause: the check is deterministic (one correct answer regardless
of context) but was encoded as prompt guidance that the LLM had to
re-implement each cycle. Per the 'Intelligence Over Determinism'
principle, deterministic logic belongs in the harness, not the prompt.

Changes:
- Add check_external_contributor_pr() to pulse-wrapper.sh: checks
  BOTH label AND comment, captures exit codes separately, fails
  closed on any API error, only posts when confirmed safe
- Add check_permission_failure_pr() companion function for the
  permission API failure case
- Add source guard (BASH_SOURCE check) so pulse-wrapper.sh can be
  sourced for its functions without triggering the full lifecycle
- Update pulse.md to call the helper functions instead of inline bash
- Update Hard Rule 12 to reference the helper functions

Closes #2809
alex-solovyev added a commit that referenced this pull request Mar 4, 2026
… function (t1391) (#2810)

The LLM supervisor kept re-implementing the inline bash idempotency
guard incorrectly on each pulse cycle, causing 15+ duplicate
'external contributor' comments on PR #2792 despite 4 prior fix
attempts (PRs #2794, #2796, #2801, #2803) — all in pulse.md prompt
text.

Root cause: the check is deterministic (one correct answer regardless
of context) but was encoded as prompt guidance that the LLM had to
re-implement each cycle. Per the 'Intelligence Over Determinism'
principle, deterministic logic belongs in the harness, not the prompt.

Changes:
- Add check_external_contributor_pr() to pulse-wrapper.sh: checks
  BOTH label AND comment, captures exit codes separately, fails
  closed on any API error, only posts when confirmed safe
- Add check_permission_failure_pr() companion function for the
  permission API failure case
- Add source guard (BASH_SOURCE check) so pulse-wrapper.sh can be
  sourced for its functions without triggering the full lifecycle
- Update pulse.md to call the helper functions instead of inline bash
- Update Hard Rule 12 to reference the helper functions

Closes #2809
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Auto-created from TODO.md tag

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: pulse posts duplicate external-contributor comments despite label guard

1 participant