Skip to content

fix(agents): exempt closed issues from the format guard - #2997

Merged
stranske merged 3 commits into
mainfrom
fix/format-guard-exempt-closed-issues
Aug 9, 2026
Merged

fix(agents): exempt closed issues from the format guard#2997
stranske merged 3 commits into
mainfrom
fix/format-guard-exempt-closed-issues

Conversation

@stranske

@stranske stranske commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Addresses task 1 of #2991 (the closed-issue exemption). The attempt cap from that issue is not in this PR.

Problem

The guard's Resolve issue step fetches state:

gh issue view "$NUMBER" --repo "$GITHUB_REPOSITORY" \
  --json number,body,labels,state,author > issue.json

...and then never reads it. exempt and held were derived from labels, author and body text only. A closed issue was therefore still validated, commented on, and routed to the optimizer — whose rewrite re-fired the guard, looping the pair against work that was already delivered.

stranske/Fine-Art-Archive#464 was closed at 2026-08-08T05:08:02Z and kept formatting until 22:44Z: 17.5 hours and several hundred workflow runs after close, against a PR that had already merged.

Change

Read the field that was already being fetched, in both the root guard and the consumer template copy (the latter is what the 13 consumer repos run):

if [[ "$(jq -r '.state // "" | ascii_downcase' issue.json)" == "closed" ]]; then
  echo "Issue is closed — nothing to format."
  exempt=true
fi

exempt short-circuits before validation, so a closed issue produces no validation, no comment, and no dispatch. The match is case-insensitive because gh returns CLOSED while the REST API returns closed.

Tests

tests/workflows/test_issue_format_guard_closed_issues.py lifts the real Resolve issue step out of the workflow YAML and executes it against a fixture payload with gh stubbed, asserting on the actual $GITHUB_OUTPUT — rather than asserting on workflow text. Both guard copies are parameterised.

  • test_closed_issue_is_exempt — the [Follow-up] Unmet criteria from PR #463 #464 shape sets exempt=true.
  • test_open_issue_is_still_processed — an otherwise identical open issue stays exempt=false, so the exemption is narrow.
  • test_closed_state_is_matched_case_insensitivelyCLOSED and closed both exempt.
  • test_both_guard_copies_carry_the_closed_check — root and template cannot drift on this.

Deliberate-break demonstration: removing the check from the root guard fails test_closed_issue_is_exempt[.github/workflows/agents-issue-format-guard.yml], test_closed_state_is_matched_case_insensitively[...] and test_both_guard_copies_carry_the_closed_check (3 failed, 4 passed) — the exact production symptom. Reverted, 7 passed.

Also verified: check_template_drift.py reports unallowlisted drift: 0, and tests/workflows/test_agents_issue_optimizer_format_trigger.py + tests/scripts/test_issue_format.py still pass (40 passed).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Closed issues are now automatically exempt from formatting validation and routing.
    • Open issues continue through the existing formatting process.
    • Closed-state detection is case-insensitive.
  • Tests

    • Added coverage confirming consistent behavior across the primary and template workflows.

The guard fetched `state` in its `gh issue view --json` call and never read it,
so a closed issue was still validated, commented on, and routed to the
optimizer. The optimizer's rewrite re-fired the guard, and the pair looped
against work that was already delivered.

Fine-Art-Archive#464 was closed at 05:08Z and kept formatting until 22:44Z —
17.5 hours and several hundred workflow runs after close.

Read the field that was already being fetched, in both the root and consumer
template copies. The check is case-insensitive because `gh` returns CLOSED
while the REST API returns closed.

Tests execute the real "Resolve issue" step lifted from the workflow YAML
against a fixture payload, with `gh` stubbed, and assert exempt=true for a
closed issue while an otherwise identical open issue is still processed.

Closes part of #2991.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 8, 2026 23:41
@cursor

cursor Bot commented Aug 8, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 45 minutes

Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 81dc2ef1-e6f6-41db-9b08-2ac6ec2500e7

📥 Commits

Reviewing files that changed from the base of the PR and between 7315e93 and 3c7f5ba.

📒 Files selected for processing (1)
  • langsmith-fleet-worker-attempt.json
📝 Walkthrough

Walkthrough

The issue-format guard now exempts closed issues in both workflow copies. Tests verify closed and open issue behavior, case-insensitive state handling, and consistency between the workflows.

Changes

Issue Format Guard

Layer / File(s) Summary
Closed issue handling
.github/workflows/agents-issue-format-guard.yml, templates/consumer-repo/.github/workflows/agents-issue-format-guard.yml
Both workflow copies detect closed issues, log the skip, and set exempt=true before validation and optimizer routing.
Closed issue validation
tests/workflows/test_issue_format_guard_closed_issues.py
Tests execute the real workflow step and verify closed, open, case-insensitive, and cross-copy behavior.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested labels: verify:compare

🚥 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 and concisely describes exempting closed issues from the issue format guard.
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/format-guard-exempt-closed-issues

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

@stranske
stranske temporarily deployed to agent-high-privilege August 8, 2026 23:41 — with GitHub Actions Inactive
@agents-workflows-bot

Copy link
Copy Markdown
Contributor

Workflow source needed

PR #2997 needs either a linked GitHub issue or one valid non-issue Workflow Source before PR metadata automation can manage it safely.

Please do one of:

  • Add <!-- meta:issue:123 --> or a normal Closes #123 / Related to #123 line.
  • Check one Workflow Source option in the PR body.
  • Add a hidden marker such as <!-- workflow-source:local_request -->, <!-- workflow-source:manual_remote -->, <!-- workflow-source:review_followup -->, <!-- workflow-source:sync_campaign -->, or <!-- workflow-source:dependabot -->.
  • Add a workflow source label such as workflow:source-direct-pr, workflow:source-local-request, workflow:source-review-followup, workflow:source-sync, or workflow:no-automation.

Once a valid source is present, this warning will not be reposted.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Exempts closed GitHub issues from the agents-issue-format-guard workflow so the guard doesn’t validate/comment/dispatch formatting work on already-closed issues (preventing the format-guard ↔ optimizer loop seen in Fine-Art-Archive#464). Adds a focused test that executes the real “Resolve issue” step script from both the root workflow and the consumer template copy.

Changes:

  • Add a closed-issue exemption in the guard’s “Resolve issue” step (root + consumer template).
  • Add an integration-style pytest that runs the extracted step script with gh stubbed and asserts on $GITHUB_OUTPUT.
  • Add a drift-oriented assertion to help ensure both workflow copies carry the closed-state logic.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
tests/workflows/test_issue_format_guard_closed_issues.py New tests executing the real “Resolve issue” step for both workflow copies and asserting closed issues set exempt=true.
templates/consumer-repo/.github/workflows/agents-issue-format-guard.yml Exempt closed issues early in the guard to prevent routing/validation loops in consumer repos.
.github/workflows/agents-issue-format-guard.yml Mirror the same closed-issue exemption in the root workflow copy.
Suppressed comments (1)

tests/workflows/test_issue_format_guard_closed_issues.py:59

  • Building PATH via shutil.os.environ['PATH'] can raise a KeyError in environments where PATH is unset, and it still depends on shutil.os. Prefer os.environ.get(...) after importing os.
            "PATH": f"{bin_dir}:{shutil.os.environ['PATH']}",

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +12 to +14
import shutil
import subprocess
from pathlib import Path
Comment on lines +120 to +121
for path in GUARD_PATHS:
assert "ascii_downcase" in _resolve_step_script(path), f"{path} lacks the closed-state check"

@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: 5a69951a43

ℹ️ 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".

# produces a body edit, that edit re-fires this guard, and the pair loops
# against work that is already delivered — see Fine-Art-Archive#464, which
# kept formatting for 17.5 hours after it was closed.
if [[ "$(jq -r '.state // "" | ascii_downcase' issue.json)" == "closed" ]]; then

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 Recheck state before mutating or dispatching

When an issue closes after the Resolve issue snapshot but before later steps execute, this exemption remains false for the run. The stale-format step at lines 120-123 can then remove agents:formatted, and although the routing step deliberately refreshes live.json at lines 138-140, it checks only hold labels before dispatching the optimizer. Recheck the live state at both side-effect boundaries so a run already in progress cannot continue modifying or routing an issue after closure.

Useful? React with 👍 / 👎.

jq -r '.body // ""' issue.json > body.md
exempt=false
held=false
# A closed issue has nothing left to format. Routing one to the optimizer

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Update the documented exemption contract

This changes the consumer-facing guard contract, but docs/ci/WORKFLOWS.md:157 still says the guard validates manual dispatch and lists only durable/wontfix and bot issues as exempt, while .github/sync-manifest.yml:95 retains the same incomplete description. Document the closed-issue exemption alongside the synchronized workflow change so operators and consumers see the behavior the template now implements.

AGENTS.md reference: AGENTS.md:L62-L65

Useful? React with 👍 / 👎.

@stranske-keepalive

stranske-keepalive Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Automated Status Summary

Head SHA: 24a4bcd
Latest Runs: ⏳ pending — Gate
Required contexts: summary
Required: core tests (3.12): ⏳ pending, core tests (3.13): ⏳ pending, docker smoke: ⏳ pending, gate: ⏳ pending

Workflow / Job Result Logs
(no jobs reported) ⏳ pending

Coverage Overview

  • Coverage history entries: 1

Coverage Trend

Metric Value
Current 76.21%
Baseline 85.00%
Delta -8.79%
Minimum 70.00%
Status ✅ Pass

Top Coverage Hotspots (lowest coverage)

File Coverage Missing
scripts/issue_dedup_smoke.py 0.0% 4
scripts/runner_lib/__main__.py 0.0% 3
scripts/validate_template_sync.py 0.0% 81
scripts/langchain/topic_splitter.py 19.1% 57
tools/codex_log_analyzer.py 19.6% 140
scripts/repo_review_round2_runner.py 25.1% 344
scripts/prune_agent_stubs.py 39.7% 26
scripts/repo_review_round1_runner.py 40.7% 133
tools/ensure_workflow_timeout_variables.py 42.1% 74
scripts/sync_label_docs.py 42.9% 64
tools/discover_model_catalog.py 44.8% 55
scripts/repo_review_backlog_scan.py 45.3% 116
scripts/repo_review_body_writer.py 46.5% 86
tools/codex_session_analyzer.py 47.9% 59
scripts/create_verifier_labels.py 48.3% 58

Low Coverage Files (<50.0%)

File Coverage Missing
scripts/issue_dedup_smoke.py 0.0% 4
scripts/runner_lib/__main__.py 0.0% 3
scripts/validate_template_sync.py 0.0% 81
scripts/langchain/topic_splitter.py 19.1% 57
tools/codex_log_analyzer.py 19.6% 140
scripts/repo_review_round2_runner.py 25.1% 344
scripts/prune_agent_stubs.py 39.7% 26
scripts/repo_review_round1_runner.py 40.7% 133
tools/ensure_workflow_timeout_variables.py 42.1% 74
scripts/sync_label_docs.py 42.9% 64
tools/discover_model_catalog.py 44.8% 55
scripts/repo_review_backlog_scan.py 45.3% 116
scripts/repo_review_body_writer.py 46.5% 86
tools/codex_session_analyzer.py 47.9% 59
scripts/create_verifier_labels.py 48.3% 58

Updated automatically; will refresh on subsequent CI/Docker completions.


Keepalive checklist

Scope

No scope information available

Tasks

  • No tasks defined

Acceptance criteria

  • No acceptance criteria defined

@stranske

stranske commented Aug 8, 2026

Copy link
Copy Markdown
Owner Author

Runner dispatch state for autofix on PR #2997. Do not edit.

@github-actions github-actions Bot added the autofix Opt-in automated formatting & lint remediation label Aug 8, 2026
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Runner dispatch state for codex on PR #2997. Do not edit.

@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Autofix updated these files:

  • tests/workflows/test_issue_format_guard_closed_issues.py

@agents-workflows-bot
agents-workflows-bot Bot temporarily deployed to agent-high-privilege August 8, 2026 23:54 Inactive

@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 @.github/workflows/agents-issue-format-guard.yml:
- Around line 64-71: Update the route step in both
.github/workflows/agents-issue-format-guard.yml lines 64-71 and
templates/consumer-repo/.github/workflows/agents-issue-format-guard.yml lines
64-71 to check the re-fetched live.json state after the live issue fetch; when
live.json.state is closed, set the existing exemption and stop routing before
labeling, dispatching, or commenting. Keep both workflow copies synchronized.

In `@templates/consumer-repo/.github/workflows/agents-issue-format-guard.yml`:
- Around line 64-71: Update the optimizer dispatch route to re-check the freshly
fetched live issue state from live.json immediately before dispatch, not only
the initial issue.json state. When the live state is closed, skip optimizer
routing and mark the issue exempt using the existing control flow.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3c7107dc-b8dc-43b8-a834-2cc35d5b06d4

📥 Commits

Reviewing files that changed from the base of the PR and between 54d8b9e and 7315e93.

📒 Files selected for processing (3)
  • .github/workflows/agents-issue-format-guard.yml
  • templates/consumer-repo/.github/workflows/agents-issue-format-guard.yml
  • tests/workflows/test_issue_format_guard_closed_issues.py

Comment on lines +64 to +71
# A closed issue has nothing left to format. Routing one to the optimizer
# produces a body edit, that edit re-fires this guard, and the pair loops
# against work that is already delivered — see Fine-Art-Archive#464, which
# kept formatting for 17.5 hours after it was closed.
if [[ "$(jq -r '.state // "" | ascii_downcase' issue.json)" == "closed" ]]; then
echo "Issue is closed — nothing to format."
exempt=true
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Check the re-fetched issue state before dispatch.

Both workflows only check the state in issue.json. If the issue closes before the route step fetches live.json, the workflow can still label, dispatch, and comment on the closed issue.

  • .github/workflows/agents-issue-format-guard.yml#L64-L71: stop the route step when live.json.state is closed.
  • templates/consumer-repo/.github/workflows/agents-issue-format-guard.yml#L64-L71: apply the same route-step guard through the workflow sync source.
Proposed guard after the live.json fetch
           gh issue view "$NUMBER" --repo "$GITHUB_REPOSITORY" \
             --json number,body,labels,state,author > live.json
+          if [[ "$(jq -r '.state // "" | ascii_downcase' live.json)" == "closed" ]]; then
+            echo "Issue is now closed — skipping optimizer dispatch."
+            exit 0
+          fi
           jq -r '.body // ""' live.json > body.md
📍 Affects 2 files
  • .github/workflows/agents-issue-format-guard.yml#L64-L71 (this comment)
  • templates/consumer-repo/.github/workflows/agents-issue-format-guard.yml#L64-L71
🤖 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 @.github/workflows/agents-issue-format-guard.yml around lines 64 - 71, Update
the route step in both .github/workflows/agents-issue-format-guard.yml lines
64-71 and
templates/consumer-repo/.github/workflows/agents-issue-format-guard.yml lines
64-71 to check the re-fetched live.json state after the live issue fetch; when
live.json.state is closed, set the existing exemption and stop routing before
labeling, dispatching, or commenting. Keep both workflow copies synchronized.

Comment on lines +64 to +71
# A closed issue has nothing left to format. Routing one to the optimizer
# produces a body edit, that edit re-fires this guard, and the pair loops
# against work that is already delivered — see Fine-Art-Archive#464, which
# kept formatting for 17.5 hours after it was closed.
if [[ "$(jq -r '.state // "" | ascii_downcase' issue.json)" == "closed" ]]; then
echo "Issue is closed — nothing to format."
exempt=true
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Re-check the live issue state before optimizer dispatch.

The initial closed-state check does not protect a run when the issue closes before the route step re-fetches live.json. That route can still dispatch the optimizer for a closed issue.

🤖 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 `@templates/consumer-repo/.github/workflows/agents-issue-format-guard.yml`
around lines 64 - 71, Update the optimizer dispatch route to re-check the
freshly fetched live issue state from live.json immediately before dispatch, not
only the initial issue.json state. When the live state is closed, skip optimizer
routing and mark the issue exempt using the existing control flow.

@agents-workflows-bot
agents-workflows-bot Bot temporarily deployed to agent-high-privilege August 8, 2026 23:55 Inactive
@stranske
stranske merged commit e442592 into main Aug 9, 2026
55 checks passed
@stranske
stranske deleted the fix/format-guard-exempt-closed-issues branch August 9, 2026 00:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

autofix:patch autofix Opt-in automated formatting & lint remediation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants