From 9a42e212309599b0fe133caa9f4e2f8e2310524b Mon Sep 17 00:00:00 2001 From: tend-agent <270458913+tend-agent@users.noreply.github.com> Date: Wed, 1 Jul 2026 09:07:06 +0000 Subject: [PATCH] fix(mention): skip no-op tend-mention session on bot's own comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The verify gate's bot-comment guard (#608) only catches GitHub App / Bot accounts via comment.user.type == "Bot". Tend's bot is a PAT-based User account, so its own comments fall through to the engagement heuristics and spin up a handle session that exits silently — pure waste. This recurs on the monthly tracking-issue rollover, where review-reviewers posts evidence-gist links on its own tracking issue. Add a guard skipping self-authored issue_comment events, wired from github.event.comment.user.login. Scoped to issue_comment so the pull_request_review self-review paths keep their intentional actionable-signal handling. Co-Authored-By: Claude Opus 4.8 --- generator/src/tend/templates/mention.yaml.j2 | 15 +++++++ ...orkflow_minimal_codex_regtest[mention].out | 15 +++++++ ...test_workflow_minimal_regtest[mention].out | 15 +++++++ ...t_workflow_with_setup_regtest[mention].out | 15 +++++++ generator/tests/test_generate.py | 43 +++++++++++++++++++ 5 files changed, 103 insertions(+) diff --git a/generator/src/tend/templates/mention.yaml.j2 b/generator/src/tend/templates/mention.yaml.j2 index 631414c..9386967 100644 --- a/generator/src/tend/templates/mention.yaml.j2 +++ b/generator/src/tend/templates/mention.yaml.j2 @@ -95,6 +95,20 @@ jobs: exit 0 fi + # The Bot-type check above only catches GitHub App / Bot accounts. + # Our own bot is a PAT-based User account, so its comments fall + # through to the engagement heuristics below (bot-authored issue, or + # "bot has prior comments") and spin up a handle job that the prompt's + # self-loop guard then exits silently — pure waste. The bot never + # needs to respond to its own comment, so skip here. Runs after the + # @-mention check so a (hypothetical) self-summons is still honored. + # Fires on, e.g., the monthly tracking-issue rollover where the bot + # posts evidence-gist links on its own issue. + if [ "$EVENT_NAME" = "issue_comment" ] && [ "$COMMENT_AUTHOR" = "<>" ]; then + echo "should_run=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + # The bot's own empty-body APPROVED review is terminal — nothing to # act on. The engagement heuristic below counts this very review # (BOT_REVIEWS > 0) and would spin up a no-op session. Gate on three @@ -171,6 +185,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.<> }} EVENT_NAME: ${{ github.event_name }} COMMENT_BODY: ${{ github.event.comment.body || github.event.review.body }} + COMMENT_AUTHOR: ${{ github.event.comment.user.login }} COMMENT_AUTHOR_TYPE: ${{ github.event.comment.user.type }} ISSUE_BODY: ${{ github.event.issue.body }} ISSUE_OR_PR_NUMBER: ${{ github.event.issue.number }} diff --git a/generator/tests/_regtest_outputs/test_generate.test_workflow_minimal_codex_regtest[mention].out b/generator/tests/_regtest_outputs/test_generate.test_workflow_minimal_codex_regtest[mention].out index 7c85356..3b7952f 100644 --- a/generator/tests/_regtest_outputs/test_generate.test_workflow_minimal_codex_regtest[mention].out +++ b/generator/tests/_regtest_outputs/test_generate.test_workflow_minimal_codex_regtest[mention].out @@ -80,6 +80,20 @@ jobs: exit 0 fi + # The Bot-type check above only catches GitHub App / Bot accounts. + # Our own bot is a PAT-based User account, so its comments fall + # through to the engagement heuristics below (bot-authored issue, or + # "bot has prior comments") and spin up a handle job that the prompt's + # self-loop guard then exits silently ? pure waste. The bot never + # needs to respond to its own comment, so skip here. Runs after the + # @-mention check so a (hypothetical) self-summons is still honored. + # Fires on, e.g., the monthly tracking-issue rollover where the bot + # posts evidence-gist links on its own issue. + if [ "$EVENT_NAME" = "issue_comment" ] && [ "$COMMENT_AUTHOR" = "test-bot" ]; then + echo "should_run=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + # The bot's own empty-body APPROVED review is terminal ? nothing to # act on. The engagement heuristic below counts this very review # (BOT_REVIEWS > 0) and would spin up a no-op session. Gate on three @@ -156,6 +170,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.TEND_BOT_TOKEN }} EVENT_NAME: ${{ github.event_name }} COMMENT_BODY: ${{ github.event.comment.body || github.event.review.body }} + COMMENT_AUTHOR: ${{ github.event.comment.user.login }} COMMENT_AUTHOR_TYPE: ${{ github.event.comment.user.type }} ISSUE_BODY: ${{ github.event.issue.body }} ISSUE_OR_PR_NUMBER: ${{ github.event.issue.number }} diff --git a/generator/tests/_regtest_outputs/test_generate.test_workflow_minimal_regtest[mention].out b/generator/tests/_regtest_outputs/test_generate.test_workflow_minimal_regtest[mention].out index 3f5c1ff..1f71396 100644 --- a/generator/tests/_regtest_outputs/test_generate.test_workflow_minimal_regtest[mention].out +++ b/generator/tests/_regtest_outputs/test_generate.test_workflow_minimal_regtest[mention].out @@ -80,6 +80,20 @@ jobs: exit 0 fi + # The Bot-type check above only catches GitHub App / Bot accounts. + # Our own bot is a PAT-based User account, so its comments fall + # through to the engagement heuristics below (bot-authored issue, or + # "bot has prior comments") and spin up a handle job that the prompt's + # self-loop guard then exits silently ? pure waste. The bot never + # needs to respond to its own comment, so skip here. Runs after the + # @-mention check so a (hypothetical) self-summons is still honored. + # Fires on, e.g., the monthly tracking-issue rollover where the bot + # posts evidence-gist links on its own issue. + if [ "$EVENT_NAME" = "issue_comment" ] && [ "$COMMENT_AUTHOR" = "test-bot" ]; then + echo "should_run=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + # The bot's own empty-body APPROVED review is terminal ? nothing to # act on. The engagement heuristic below counts this very review # (BOT_REVIEWS > 0) and would spin up a no-op session. Gate on three @@ -156,6 +170,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.TEND_BOT_TOKEN }} EVENT_NAME: ${{ github.event_name }} COMMENT_BODY: ${{ github.event.comment.body || github.event.review.body }} + COMMENT_AUTHOR: ${{ github.event.comment.user.login }} COMMENT_AUTHOR_TYPE: ${{ github.event.comment.user.type }} ISSUE_BODY: ${{ github.event.issue.body }} ISSUE_OR_PR_NUMBER: ${{ github.event.issue.number }} diff --git a/generator/tests/_regtest_outputs/test_generate.test_workflow_with_setup_regtest[mention].out b/generator/tests/_regtest_outputs/test_generate.test_workflow_with_setup_regtest[mention].out index 8dc689b..153466a 100644 --- a/generator/tests/_regtest_outputs/test_generate.test_workflow_with_setup_regtest[mention].out +++ b/generator/tests/_regtest_outputs/test_generate.test_workflow_with_setup_regtest[mention].out @@ -80,6 +80,20 @@ jobs: exit 0 fi + # The Bot-type check above only catches GitHub App / Bot accounts. + # Our own bot is a PAT-based User account, so its comments fall + # through to the engagement heuristics below (bot-authored issue, or + # "bot has prior comments") and spin up a handle job that the prompt's + # self-loop guard then exits silently ? pure waste. The bot never + # needs to respond to its own comment, so skip here. Runs after the + # @-mention check so a (hypothetical) self-summons is still honored. + # Fires on, e.g., the monthly tracking-issue rollover where the bot + # posts evidence-gist links on its own issue. + if [ "$EVENT_NAME" = "issue_comment" ] && [ "$COMMENT_AUTHOR" = "test-bot" ]; then + echo "should_run=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + # The bot's own empty-body APPROVED review is terminal ? nothing to # act on. The engagement heuristic below counts this very review # (BOT_REVIEWS > 0) and would spin up a no-op session. Gate on three @@ -156,6 +170,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.TEND_BOT_TOKEN }} EVENT_NAME: ${{ github.event_name }} COMMENT_BODY: ${{ github.event.comment.body || github.event.review.body }} + COMMENT_AUTHOR: ${{ github.event.comment.user.login }} COMMENT_AUTHOR_TYPE: ${{ github.event.comment.user.type }} ISSUE_BODY: ${{ github.event.issue.body }} ISSUE_OR_PR_NUMBER: ${{ github.event.issue.number }} diff --git a/generator/tests/test_generate.py b/generator/tests/test_generate.py index 4232057..08faa3a 100644 --- a/generator/tests/test_generate.py +++ b/generator/tests/test_generate.py @@ -511,6 +511,49 @@ def test_mention_verify_skips_bot_comments_without_mention(tmp_path: Path) -> No ) +def test_mention_verify_skips_self_authored_comments(tmp_path: Path) -> None: + """The bot's own comments must not spin up a handle session. + + The Bot-type check only catches GitHub App / Bot accounts. Tend's own bot + is a PAT-based User account, so its comments fall through to the engagement + heuristics (bot-authored issue, or "bot has prior comments") and reach the + handle job, which exits silently via the prompt's self-loop guard — pure + waste. This recurs on the monthly tracking-issue rollover (review-reviewers + posts evidence-gist links on its own issue) and on duplicate-tracking + notices. Skip self-authored `issue_comment` events at the gate.""" + cfg = Config.load(_minimal_config(tmp_path)) + workflows = {wf.filename: wf for wf in generate_all(cfg)} + mention = workflows["tend-mention.yaml"] + data = yaml.safe_load(mention.content) + check_step = next( + s for s in data["jobs"]["verify"]["steps"] if s.get("id") == "check" + ) + run = check_step["run"] + + assert check_step["env"].get("COMMENT_AUTHOR") == ( + "${{ github.event.comment.user.login }}" + ), ( + "verify must wire github.event.comment.user.login so the script can " + "recognize the bot's own (User-type) comments" + ) + + assert '"$COMMENT_AUTHOR" = "test-bot"' in run, ( + "verify must short-circuit issue_comment events authored by the bot " + "itself — its User-type account is missed by the Bot-type check" + ) + + # The self-authored guard must run after the @-mention check (so a + # self-summons is still honored) and before the bot-authored-issue + # short-circuit that would otherwise spin up the no-op session. + mention_idx = run.index("grep -qF '@test-bot'") + self_guard_idx = run.index('"$COMMENT_AUTHOR" = "test-bot"') + issue_author_idx = run.index('"$ISSUE_AUTHOR" = "test-bot"') + assert mention_idx < self_guard_idx < issue_author_idx, ( + "self-authored comment guard must run after the @-mention check and " + "before the bot-authored-issue short-circuit" + ) + + def test_mention_verify_no_concurrency(tmp_path: Path) -> None: """verify job must not have concurrency — a non-mention comment can cancel an explicit @bot mention if both arrive on the same PR within seconds (#93)."""