Skip to content

fix(#5575): match linked-PR closing keywords instead of substring search - #5578

Merged
waynesun09 merged 3 commits into
mainfrom
fix-5575-dispatch-pr-check
Aug 7, 2026
Merged

fix(#5575): match linked-PR closing keywords instead of substring search#5578
waynesun09 merged 3 commits into
mainfrom
fix-5575-dispatch-pr-check

Conversation

@waynesun09

@waynesun09 waynesun09 commented Jul 24, 2026

Copy link
Copy Markdown
Member

Summary

Fixes #5575. The dispatch "Check for existing PRs" guard used --search "N in:title,body", which matches any open PR that mentions the issue number anywhere in its title/body — including unrelated cross-references — and skipped code dispatch whenever one existed. Its bot-authored-PR exclusion also never worked: GraphQL's Bot.login omits the REST [bot] suffix, so the literal string comparison against "fullsend-ai-coder[bot]" never matched anything.

Concretely, on #5569, two /fs-code comments both routed to STAGE="code" but were silently skipped because PR #5192 had a single line referencing #5569 as an unrelated, out-of-scope finding — not an actual fix.

Fix

Replace the substring search with a GraphQL query for closedByPullRequestsReferences, which only returns PRs that actually close the issue via Fixes/Closes/Resolves keywords — GitHub's own linking mechanism, not a text match. The bot exclusion now matches on author.__typename == "Bot" combined with the GraphQL-format login (fullsend-ai-coder, no [bot] suffix).

Applied to:

  • internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml (org-mode scaffold)
  • .github/workflows/reusable-dispatch.yml (per-repo reusable workflow)
  • internal/scaffold/fullsend-repo/scripts/pre-code.sh — a second, independent existing-PR gate that runs later in the same code job with the identical bug (same substring search, same broken bot-login comparison). Left unfixed, the original false-positive-skip failure mode could still recur through this path even after the dispatch-level guard is fixed. pre-code-test.sh's mock fixtures hardcoded the REST-suffixed bot login format and so validated the wrong assumption about what the API actually returns — corrected those too.

Additional correctness fixes found during review:

  • closedByPullRequestsReferences returns MERGED PRs regardless of includeClosedPrs, so the jq filter now explicitly requires .state == "OPEN" — otherwise a long-since-merged closer would permanently block re-dispatch on a reopened issue.
  • Added issues: read to the dispatch/route jobs' permissions, since the query now resolves through the Issue type rather than pull-requests-only fields.
  • Separated stderr from the query's stdout capture so an incidental warning on an otherwise-successful call can't pollute the result and trigger a false-positive skip.
  • Added a clarifying note to docs/contributing/bot-identities.md about the REST vs. GraphQL login format discrepancy, since it's now cited from three call sites.

dispatch.yml's lint-workflow-size cap bumped from 560 to 590 to accommodate the larger, more explicit query and permission grant (no size cap applies to reusable-dispatch.yml).

Test plan

@waynesun09
waynesun09 requested a review from a team as a code owner July 24, 2026 16:05
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Fix dispatch guard to detect issue-closing PRs via GraphQL (no substring matches)

🐞 Bug fix ⚙️ Configuration changes 🕐 20-40 Minutes

Grey Divider

AI Description

• Replace issue-number substring search with GraphQL-linked closing PR lookup
• Fix bot-author exclusion by using GraphQL bot identity fields
• Bump workflow-size lint cap for the scaffolded dispatcher workflow
Diagram

graph TD
  A["Dispatch workflow"] --> B["Check existing PRs"] --> C["gh api graphql"] --> D{{"GitHub GraphQL API"}} --> E["closedByPullRequestsReferences"] --> F{"Linked open closing PRs?\n(excluding coder bot)"}
  F -->|"yes"| G["Skip dispatch"]
  F -->|"no"| H["Proceed dispatch"]

  subgraph Legend
    direction LR
    _p["Process step"] ~~~ _d{"Decision"} ~~~ _e{{"External API"}}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use REST issue timeline events
  • ➕ Avoids embedding GraphQL query strings in YAML
  • ➕ Timeline events can provide richer context (linked/unlinked, merged state)
  • ➖ Requires timeline preview headers/permissions in some setups
  • ➖ More client-side filtering logic; likely noisier than purpose-built GraphQL field
2. Keep search but tighten qualifiers
  • ➕ Simpler than GraphQL; stays within gh pr list UX
  • ➖ Still fundamentally heuristic; can regress on edge cases (mentions, quotes, changelogs)
  • ➖ Hard to reliably model GitHub’s actual closing-link semantics

Recommendation: Prefer the PR’s current approach: closedByPullRequestsReferences directly reflects GitHub’s closing-link mechanism (Fixes/Closes/Resolves) and eliminates substring false positives. The bot exclusion using author.__typename == "Bot" plus the GraphQL-format login is also more robust than REST-style [bot] string matching.

Files changed (2) +49 / -19

Bug fix (2) +49 / -19
reusable-dispatch.ymlSwitch PR guard to GraphQL linked-closing-PR query + bot exclusion fix +24/-9

Switch PR guard to GraphQL linked-closing-PR query + bot exclusion fix

• Replaces 'gh pr list --search "N in:title,body"' with a GraphQL query for 'closedByPullRequestsReferences' to only consider PRs that actually close the issue via GitHub keywords. Fixes the bot exclusion by filtering on 'author.__typename == "Bot"' and the GraphQL-format bot login, and adds warning handling when the query fails.

.github/workflows/reusable-dispatch.yml

dispatch.ymlMirror GraphQL linked-closing-PR guard in scaffolded dispatcher + size cap bump +25/-10

Mirror GraphQL linked-closing-PR guard in scaffolded dispatcher + size cap bump

• Applies the same linked-PR GraphQL guard logic as the reusable workflow so scaffolds behave consistently. Increases the workflow-size lint cap (560→580) to accommodate the expanded query/comments.

internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml

@github-actions

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown

Site preview

Preview: https://4573d87d-site.fullsend-ai.workers.dev

Commit: 14867d70fcaf9eb3847cc1f356f9390ed391beb1

@qodo-code-review

qodo-code-review Bot commented Jul 24, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (2) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Raised lint-workflow-size cap 📘 Rule violation ▣ Testability
Description
The PR increases the lint-workflow-size maximum line cap in dispatch.yml, which weakens an
existing lint constraint rather than keeping the workflow within the established limit. This can
allow future growth that reduces maintainability and defeats the intent of the workflow-size check.
Code

internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml[2]

+# lint-workflow-size: max-lines=580
Relevance

●●● Strong

Raising workflow-size lint caps has a close, explicit rejection precedent; likely reverted instead
of weakening lint.

PR-#5244

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 1062076 prohibits weakening linters to make failures pass. The diff explicitly
increases the workflow-size lint maximum from 560 to 580 in the modified workflow header.

Rule 1062076: Do not weaken tests or linters to make failures pass
internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml[1-2]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The workflow-size lint constraint is relaxed from `max-lines=560` to `max-lines=580`.

## Issue Context
Compliance discourages weakening linters to make changes pass; prefer refactoring to stay within the existing cap (e.g., move complex scripting into a reusable script/composite action).

## Fix Focus Areas
- internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml[1-5]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Stderr pollutes LINKED_PRS ✓ Resolved 🐞 Bug ☼ Reliability
Description
The PR-check captures gh api graphql output with 2>&1, so any warnings printed to stderr will be
mixed into LINKED_PRS and treated as “found PRs”, potentially skipping dispatch even when there
are no linked PRs. This behavior is duplicated in both workflow copies.
Code

.github/workflows/reusable-dispatch.yml[R339-355]

+          LINKED_PRS=$(gh api graphql -f query='
+            query($owner: String!, $repo: String!, $number: Int!) {
+              repository(owner: $owner, name: $repo) {
+                issue(number: $number) {
+                  closedByPullRequestsReferences(first: 20) {
+                    nodes { number author { login __typename } }
+                  }
+                }
+              }
+            }' -f owner="${OWNER}" -f repo="${REPO_NAME}" -F number="${ISSUE_NUMBER}" \
+            --jq '[.data.repository.issue.closedByPullRequestsReferences.nodes[]
+                   | select(.author.__typename != "Bot" or .author.login != "fullsend-ai-coder")]
+                   | .[].number' \
+            2>&1) || { echo "::warning::Linked-PR query failed for issue #${ISSUE_NUMBER}: ${LINKED_PRS}"; LINKED_PRS=""; }
+          if [[ -n "${LINKED_PRS}" ]]; then
+            echo "::notice::Open PR(s) linked to close issue #${ISSUE_NUMBER} found — skipping code dispatch"
            echo "skipped=true" >> "${GITHUB_OUTPUT}"
Relevance

●●● Strong

Mixing stderr into a data variable can change control flow; team often accepts workflow reliability
hardening.

PR-#1688

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The code explicitly merges stderr into the captured variable and then uses a non-empty check on that
variable to decide whether to skip dispatch, so any stderr text will change control flow.

.github/workflows/reusable-dispatch.yml[339-355]
internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml[272-288]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The PR-check step redirects stderr into stdout (`2>&1`) inside the command substitution that populates `LINKED_PRS`. Any stderr output (warnings, rate-limit messages, transient notices) will make `LINKED_PRS` non-empty and can incorrectly trigger the skip path.

### Issue Context
The workflow already has an error-handling branch that wants to surface errors; that can be done without mixing stderr into the data channel used for control flow.

### Fix Focus Areas
- .github/workflows/reusable-dispatch.yml[339-356]
- internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml[272-289]

### Implementation notes
- Capture stdout only for `LINKED_PRS` and capture stderr separately, e.g.:
 - `api_err=$(mktemp)`
 - `LINKED_PRS=$(gh api graphql ... --jq '...' 2>"$api_err") || { echo "::warning::... $(tr '\n' ' ' <"$api_err")"; LINKED_PRS=""; }`
 - Ensure the temp file is cleaned up.
- Alternatively, drop `2>&1` and in the failure branch print a generic warning without embedding raw stderr into workflow commands.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Brittle bot exclusion ✓ Resolved 🐞 Bug ≡ Correctness
Description
The linked-PR filter only excludes PRs authored by a Bot whose GraphQL login is exactly
"fullsend-ai-coder", but the repo’s bot identity documentation lists the coder bot as
"fullsend-ai-coder[bot]", so the coder-bot PR may still be treated as a human PR and block dispatch.
This mismatch is also reinforced by the in-workflow comment that points readers to the doc as the
source of truth while hardcoding the non-doc login form.
Code

.github/workflows/reusable-dispatch.yml[R349-351]

+            --jq '[.data.repository.issue.closedByPullRequestsReferences.nodes[]
+                   | select(.author.__typename != "Bot" or .author.login != "fullsend-ai-coder")]
+                   | .[].number' \
Relevance

●● Moderate

Bot identity handling has mixed conventions ([bot] vs GraphQL login); unclear if team will change
filter.

PR-#2373

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The workflows hardcode a bot login without the [bot] suffix in the jq filter, while the repo
documentation lists the bot login with the suffix, so the intended exclusion is inconsistent with
the documented identity source that the workflow itself references.

.github/workflows/reusable-dispatch.yml[330-352]
internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml[263-285]
docs/contributing/bot-identities.md[5-15]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The PR-check step attempts to exclude bot-authored linked PRs, but it hardcodes a GraphQL login value ("fullsend-ai-coder") that conflicts with the documented bot login ("fullsend-ai-coder[bot]"). As a result, the exclusion may fail and a bot-authored linked PR can incorrectly trigger the “skip code dispatch” path.

### Issue Context
- The workflow comment explicitly points maintainers to `docs/contributing/bot-identities.md` for the login, but the string used in jq does not match that doc.
- This guard exists in two places and should be fixed consistently.

### Fix Focus Areas
- .github/workflows/reusable-dispatch.yml[330-356]
- internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml[263-289]
- docs/contributing/bot-identities.md[5-15]

### Implementation notes
- Either (a) update the jq predicate to accept both forms (e.g., `fullsend-ai-coder` and `fullsend-ai-coder[bot]`), or (b) update the docs/comment to explicitly document the GraphQL-vs-REST difference and keep the code as-is.
- Consider rewriting the predicate for clarity, e.g. `select(!(.author.__typename == "Bot" and (.author.login == "fullsend-ai-coder" or .author.login == "fullsend-ai-coder[bot]")))`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

4. gh api graphql in workflow 📘 Rule violation ⌂ Architecture
Description
The workflows add a direct GitHub GraphQL API call via gh api graphql outside
internal/forge/github/, violating the restriction that GitHub API calls must be confined to that
package. This increases the risk of inconsistent auth/rate-limit handling and makes forge
interactions harder to audit and maintain.
Code

.github/workflows/reusable-dispatch.yml[R339-352]

+          LINKED_PRS=$(gh api graphql -f query='
+            query($owner: String!, $repo: String!, $number: Int!) {
+              repository(owner: $owner, name: $repo) {
+                issue(number: $number) {
+                  closedByPullRequestsReferences(first: 20) {
+                    nodes { number author { login __typename } }
+                  }
+                }
+              }
+            }' -f owner="${OWNER}" -f repo="${REPO_NAME}" -F number="${ISSUE_NUMBER}" \
+            --jq '[.data.repository.issue.closedByPullRequestsReferences.nodes[]
+                   | select(.author.__typename != "Bot" or .author.login != "fullsend-ai-coder")]
+                   | .[].number' \
+            2>&1) || { echo "::warning::Linked-PR query failed for issue #${ISSUE_NUMBER}: ${LINKED_PRS}"; LINKED_PRS=""; }
Relevance

● Weak

Past reviews rejected enforcing “GitHub API calls only in internal/forge/github” for other gh/REST
callsites.

PR-#2277
PR-#4901

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 1062054 restricts direct GitHub API/GraphQL call sites to internal/forge/github/.
The new workflow code invokes gh api graphql to query GitHub GraphQL from .github/workflows/...
and the scaffold workflow, which are outside the allowed directory.

Rule 1062054: Restrict direct GitHub API calls to internal/forge/github
.github/workflows/reusable-dispatch.yml[339-352]
internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml[272-285]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The workflow directly calls the GitHub GraphQL API via `gh api graphql` from `.github/workflows/reusable-dispatch.yml` and `internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml`. Compliance requires GitHub API interactions to be routed through `internal/forge/github/`.

## Issue Context
This PR introduced a new guard implementation that queries `closedByPullRequestsReferences` using GraphQL.

## Fix Focus Areas
- .github/workflows/reusable-dispatch.yml[339-352]
- internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml[272-285]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context used
✅ Compliance rules (platform): 61 rules

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment thread internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml Outdated
Comment thread .github/workflows/reusable-dispatch.yml
Comment thread .github/workflows/reusable-dispatch.yml Outdated
Comment thread internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml Outdated
Comment thread .github/workflows/reusable-dispatch.yml
Comment thread .github/workflows/reusable-dispatch.yml Outdated
Comment thread internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml Outdated
Comment thread .github/workflows/reusable-dispatch.yml
Comment thread .github/workflows/reusable-dispatch.yml Outdated
Comment thread internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml Outdated
Comment thread .github/workflows/reusable-dispatch.yml
Comment thread .github/workflows/reusable-dispatch.yml Outdated
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 24, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 4:19 PM UTC · Ended 4:32 PM UTC
Commit: 2f2eb78 · View workflow run →

@waynesun09
waynesun09 force-pushed the fix-5575-dispatch-pr-check branch from 2f2eb78 to 59dfbce Compare July 24, 2026 16:21
waynesun09 added a commit that referenced this pull request Jul 24, 2026
The dispatch "Check for existing PRs" guard used --search "N in:title,body",
which matches any PR that mentions the issue number anywhere in its body —
including unrelated cross-references — and skipped code dispatch whenever
one existed. Its bot-authored-PR exclusion also never worked: GraphQL's
Bot.login omits the REST "[bot]" suffix, so the literal string comparison
against "fullsend-ai-coder[bot]" never matched.

Replace both with a GraphQL query for closedByPullRequestsReferences,
which only returns PRs that actually close the issue via Fixes/Closes/
Resolves keywords, and match the bot exclusion on __typename == "Bot"
instead of a REST-formatted login string. The query also returns MERGED
PRs regardless of includeClosedPrs, so filter on .state == "OPEN"
explicitly — otherwise a long-merged closer would permanently block
re-dispatch on a reopened issue. Add issues: read to both jobs'
permissions, since the query now resolves through the Issue type rather
than pull-requests-only fields. Separate stderr from the query's stdout
so an incidental warning on an otherwise-successful call can't pollute
the result and trigger a false-positive skip.

internal/scaffold/fullsend-repo/scripts/pre-code.sh had the identical
bug (same substring search, same broken bot-login comparison) and runs
as a second gate in the same code-dispatch pipeline, so the original
false-positive-skip failure mode could still recur through that path.
Apply the same fix there and correct pre-code-test.sh's mock fixtures,
which hardcoded the REST-suffixed bot login format and so validated the
wrong assumption about what the API actually returns.

Verified against production data: #5569 (false
positive: PR #5192 only mentions the issue, now correctly proceeds),
#1320 (historical: closed by now-merged PR #2373, now correctly
proceeds since the closer is no longer open), #5560 (bot-authored
closer, correctly excluded), and #5575 itself (open, human-authored PR
#5578 with "Fixes #5575" in its body, correctly detected as blocking).

Assisted-by: Claude
Signed-off-by: Wayne Sun <gsun@redhat.com>
@codecov

codecov Bot commented Jul 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 24, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 4:34 PM UTC · Completed 4:50 PM UTC
Commit: 59dfbce · View workflow run →

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review

Findings

Medium

  • [comment-contradicts-code] internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml:279 — The comment at lines 279–285 states "this job's token lacks issues: read (neither this job nor the shim-workflow-call.yaml caller grants it)" and "Do NOT add the scope to this job alone." However, issues: read was added to this job's permissions block at line 29 in this PR. The parenthetical "neither this job..." is factually wrong — this job now declares the permission. The "Do NOT add" admonition contradicts what was done. The runtime analysis is correct (in org/workflow_call mode the effective token still lacks the scope because the shim caller does not grant it), but the comment will mislead future readers.
    Remediation: Rephrase to acknowledge the declared permission, e.g. "NOTE (org/workflow_call mode): although this job declares issues: read, the shim-workflow-call.yaml caller does not grant it. Since a called workflow cannot exceed its caller's grant, the effective token still lacks the scope."

  • [protected-path] .github/workflows/reusable-dispatch.yml — This file is under the .github/ protected path. The PR links to issue dispatch.yml "Check for existing PRs" guard blocks /fs-code forever due to bot-login format mismatch + overbroad mention search #5575 and explains the rationale (fixing two compounding bugs in the dispatch guard). Human approval is required for protected-path changes regardless of context.

Low

  • [GHA workflow command injection] .github/workflows/reusable-dispatch.yml, internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml — The ::warning:: command interpolates stderr from gh api graphql via $(tr '\n' ' ' < "${GQL_ERR}" | sed 's/::/__/g'). Both sanitization measures are present: tr strips literal newlines and sed replaces :: sequences. The error source is GitHub's own API server (not attacker-controlled), making this a defense-in-depth concern rather than an exploitable vulnerability.

  • [pr-body-inaccuracy] — The PR body states the lint-workflow-size cap was bumped from 560 to 590; the actual change is 580 → 610.

  • [docs-staleness] docs/guides/user/building-custom-agents.md:319 — The phrase "an open PR already addresses the issue" is slightly imprecise after this PR's change to closing-keyword linkage. The dispatch code now checks for PRs linked via Fixes/Closes/Resolves keywords. The core doc (docs/agents/code.md) was updated with precise language in this PR.

  • [docs-staleness] docs/normative/prescript-output/v1/README.md:7 — The normative contract uses "an open PR already addresses the issue" as an illustrative example of why a pre-script might skip. This describes the output protocol concept, not the matching algorithm, so is not factually wrong — but could benefit from more precise language.

Previous run

Review

Findings

High

  • [missing-permission] internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml — The scaffold dispatch workflow's dispatch job permissions block does not include issues: read, but the new GraphQL query accesses repository.issue(number:).closedByPullRequestsReferences(...) which requires that scope. The reusable-dispatch.yml correctly adds issues: read (line 83), but the scaffold was not updated. At runtime, the gh api graphql call will fail because the GITHUB_TOKEN lacks issues: read. The error handler sets LINKED_PRS="" and logs a warning, causing the guard to silently pass — every code dispatch will proceed even when a linked PR already exists, defeating the purpose of the fix.
    Remediation: Add issues: read to the dispatch job's permissions block.

Medium

  • [scope-vs-description-mismatch] The PR body explicitly claims changes to internal/scaffold/fullsend-repo/scripts/pre-code.sh and pre-code-test.sh and states "23 cases pass, including two new regression tests." However, neither file appears in the diff (only 4 files changed). The PR body also claims issues: read was added to the scaffold dispatch job but only reusable-dispatch.yml has it. Either the changes were accidentally dropped or the description is inaccurate.
    Remediation: Either add the missing changes or update the PR body to reflect the current scope.

  • [protected-path] .github/workflows/reusable-dispatch.yml — This file is under the .github/ protected path. The PR links to issue dispatch.yml "Check for existing PRs" guard blocks /fs-code forever due to bot-login format mismatch + overbroad mention search #5575 and explains the rationale (fixing two compounding bugs in the dispatch guard). Human approval is required for protected-path changes regardless of context.

Low

  • [GHA workflow command injection] .github/workflows/reusable-dispatch.yml, internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml — The ::warning:: command interpolates stderr from gh api graphql via $(tr '\n' ' ' < "${GQL_ERR}" | sed 's/::/__/g'). Both sanitization measures are present: tr strips literal newlines and sed replaces :: sequences. The error source is GitHub's own API server (not attacker-controlled), making this a defense-in-depth concern rather than an exploitable vulnerability.

Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (2)

Review

Reason: stale-head

The review agent reviewed commit 4ea754dc1fdc804d562cc346504ad6d9356e2503 but the PR HEAD is now 4df01b120a7661483498db657a1e1e87a747ccbb. This review was discarded to avoid approving unreviewed code.

Previous run (3)

Review

Findings

Medium

Low

  • [GHA workflow command injection] .github/workflows/reusable-dispatch.yml, internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml — The ::warning:: command interpolates stderr from gh api graphql via $(tr '\n' ' ' < "${GQL_ERR}" | sed 's/::/__/g'). Both sanitization measures are now present: tr strips literal newlines and sed replaces :: sequences. The error source is GitHub's own API server (not attacker-controlled), making this a defense-in-depth concern rather than an exploitable vulnerability.

  • [fail-open] .github/workflows/reusable-dispatch.yml — When the GraphQL query fails, LINKED_PRS is set to empty and dispatch proceeds. This is a pre-existing, intentional fail-open (the old code used 2>/dev/null || true). The new code improves on it by emitting a ::warning::. Note: the query now requires issues: read permission (added to both workflows); un-re-scaffolded caller shims without this permission will see the query fail on every invocation.

  • [authorization-logic-change] .github/workflows/reusable-dispatch.yml — The bot exclusion filter now only excludes fullsend-ai-coder (the coder bot) instead of both fullsend-ai[bot] and fullsend-ai-coder[bot]. Per docs/contributing/bot-identities.md, the triage bot (fullsend-ai) does not create PRs, so the narrower exclusion is correct and more conservative.

  • [edge-case] .github/workflows/reusable-dispatch.ymlclosedByPullRequestsReferences(first: 20) caps results at 20 PRs. If an issue has more than 20 PRs with closing keywords, some may be missed. Extremely unlikely in practice.

  • [scope-documentation-mismatch] The PR body claims changes to internal/scaffold/fullsend-repo/scripts/pre-code.sh and pre-code-test.sh, but these files are not in the current diff (only 4 files changed). The PR body should be updated to reflect the current scope, or a note added explaining those changes live in a separate PR or repo.

Previous run (4)

Review

Findings

Medium

Low

  • [GHA workflow command injection] .github/workflows/reusable-dispatch.yml, internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml, internal/scaffold/fullsend-repo/scripts/pre-code.sh — The ::warning:: command interpolates stderr from gh api graphql via $(tr '\n' ' ' < "${GQL_ERR}"). The tr strips literal newlines (improving on the prior review's cat-based concern), but does not sanitize :: sequences that could be interpreted as workflow command delimiters. The error source is GitHub's own API server (not attacker-controlled), making this a defense-in-depth concern rather than an exploitable vulnerability.
    Remediation: Pipe through sed 's/::/__/g' in addition to the newline stripping, or redirect to stderr instead of using a workflow command.

  • [fail-open] .github/workflows/reusable-dispatch.yml — When the GraphQL query fails, LINKED_PRS is set to empty and dispatch proceeds. This is a pre-existing, intentional fail-open (the old code used 2>/dev/null || true). The new code improves on it by emitting a ::warning::.

  • [authorization-logic-change] .github/workflows/reusable-dispatch.yml — The bot exclusion filter now only excludes fullsend-ai-coder (the coder bot) instead of both fullsend-ai[bot] and fullsend-ai-coder[bot]. Per docs/contributing/bot-identities.md, the triage bot (fullsend-ai) does not create PRs, so the narrower exclusion is correct and more conservative.

  • [comment-style] .github/workflows/reusable-dispatch.yml:335, internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml:268 — Multi-line comment breaks the word false-positives across lines as # -positives, which visually resembles a markdown bullet list item rather than a mid-word continuation.
    Remediation: Reflow the comment to keep false-positives on a single line.

  • [stale-description] docs/agents/code.md:13 — The code agent documentation states the pre-script "checks for open PRs linked to the issue" without describing the mechanism. This PR changed from substring search to closing-keyword linkage (closedByPullRequestsReferences). The high-level description is still accurate, but users debugging dispatch behavior would benefit from knowing that only PRs with Fixes/Closes/Resolves keywords are considered blocking.
    Remediation: Consider adding a clarifying note to docs/agents/code.md.

Previous run (5)

Review

Findings

Medium

  • [protected-path] .github/workflows/reusable-dispatch.yml — This file is under the .github/ protected path. The PR links to issue dispatch.yml "Check for existing PRs" guard blocks /fs-code forever due to bot-login format mismatch + overbroad mention search #5575 and explains the rationale (fixing two compounding bugs in the dispatch guard). Human approval is required for protected-path changes regardless of context.

  • [test-accuracy] internal/scaffold/fullsend-repo/scripts/pre-code-test.sh:477 — The GITHUB_OUTPUT test skip-output-false-on-no-prs passes an empty string as mock data, which with the updated mock now simulates a GraphQL failure (mock exits 1 for empty input), not a "no linked PRs" scenario. The test still passes because both paths produce skipped=false, but it no longer tests what its name implies. The actual "no PRs" scenario is covered by no-existing-prs-proceeds (using NO_PRS_JSON), but that test checks stdout, not GITHUB_OUTPUT.
    Remediation: Change the test input from "" to "${NO_PRS_JSON}" to test the "no linked PRs" scenario via GITHUB_OUTPUT as the name suggests.

Low

  • [GHA workflow command injection] .github/workflows/reusable-dispatch.yml, internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml, internal/scaffold/fullsend-repo/scripts/pre-code.sh — The ::warning:: command interpolates stderr from gh api graphql via $(cat "${GQL_ERR}"). If the API error contains literal newlines, subsequent lines could be parsed as new workflow commands by the GHA runner. The error source is GitHub's own API server (not attacker-controlled), making this a defense-in-depth concern rather than an exploitable vulnerability.
    Remediation: Pipe through tr -d '\n' before interpolation, or use stderr output instead of a workflow command.

  • [fail-open] .github/workflows/reusable-dispatch.yml — When the GraphQL query fails, LINKED_PRS is set to empty and dispatch proceeds. This is a pre-existing, intentional fail-open (the old code used 2>/dev/null || true). The new code improves on it by emitting a ::warning::.

  • [authorization-logic-change] .github/workflows/reusable-dispatch.yml — The bot exclusion filter now only excludes fullsend-ai-coder (the coder bot) instead of both fullsend-ai[bot] and fullsend-ai-coder[bot]. Per docs/contributing/bot-identities.md, the triage bot (fullsend-ai) does not create PRs, so the narrower exclusion is correct and more conservative.

Previous run

Review

Findings

Medium

  • [protected-path] .github/workflows/reusable-dispatch.yml — This file is under the .github/ protected path. The PR links to issue dispatch.yml "Check for existing PRs" guard blocks /fs-code forever due to bot-login format mismatch + overbroad mention search #5575 and explains the rationale (fixing two compounding bugs in the dispatch guard). Human approval is required for protected-path changes regardless of context.

  • [test-accuracy] internal/scaffold/fullsend-repo/scripts/pre-code-test.sh:445 — The GITHUB_OUTPUT test skip-output-false-on-no-prs passes an empty string as mock data, which with the updated mock now simulates a GraphQL failure (mock exits 1 for empty input), not a "no linked PRs" scenario. The test still passes because both paths produce skipped=false, but it no longer tests what its name implies. The actual "no PRs" scenario is covered by no-existing-prs-proceeds (using NO_PRS_JSON), but that test checks stdout, not GITHUB_OUTPUT.
    Remediation: Change the test input from "" to "${NO_PRS_JSON}" to test the "no linked PRs" scenario via GITHUB_OUTPUT as the name suggests.

Low

  • [GHA workflow command injection] .github/workflows/reusable-dispatch.yml, internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml, internal/scaffold/fullsend-repo/scripts/pre-code.sh — The ::warning:: command interpolates stderr from gh api graphql via $(cat "${GQL_ERR}"). If the API error contains literal newlines, subsequent lines could be parsed as new workflow commands by the GHA runner. The error source is GitHub's own API server (not attacker-controlled), making this a defense-in-depth concern rather than an exploitable vulnerability.
    Remediation: Pipe through tr -d '\n' before interpolation, or use stderr output instead of a workflow command.

  • [fail-open] .github/workflows/reusable-dispatch.yml — When the GraphQL query fails, LINKED_PRS is set to empty and dispatch proceeds. This is a pre-existing, intentional fail-open (the old code used 2>/dev/null || true). The new code improves on it by emitting a ::warning::.

  • [authorization-logic-change] .github/workflows/reusable-dispatch.yml — The bot exclusion filter now only excludes fullsend-ai-coder (the coder bot) instead of both fullsend-ai[bot] and fullsend-ai-coder[bot]. Per docs/contributing/bot-identities.md, the triage bot (fullsend-ai) does not create PRs, so the narrower exclusion is correct and more conservative.


Labels: PR modifies dispatch workflow and pre-code scripts — entirely within the dispatch subsystem

Previous run (6)

Review

Findings

Medium

  • [protected-path] .github/workflows/reusable-dispatch.yml — This file is under the .github/ protected path. The PR links to issue dispatch.yml "Check for existing PRs" guard blocks /fs-code forever due to bot-login format mismatch + overbroad mention search #5575 and explains the rationale (fixing two compounding bugs in the dispatch guard). Human approval is required for protected-path changes regardless of context.

  • [test-accuracy] internal/scaffold/fullsend-repo/scripts/pre-code-test.sh:477 — The GITHUB_OUTPUT test skip-output-false-on-no-prs passes an empty string as mock data, which with the updated mock now simulates a GraphQL failure (mock exits 1 for empty input), not a "no linked PRs" scenario. The test still passes because both paths produce skipped=false, but it no longer tests what its name implies. The actual "no PRs" scenario is covered by no-existing-prs-proceeds (using NO_PRS_JSON), but that test checks stdout, not GITHUB_OUTPUT.
    Remediation: Change the test input from "" to "${NO_PRS_JSON}" to test the "no linked PRs" scenario via GITHUB_OUTPUT as the name suggests.

Low

  • [GHA workflow command injection] .github/workflows/reusable-dispatch.yml, internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml, internal/scaffold/fullsend-repo/scripts/pre-code.sh — The ::warning:: command interpolates stderr from gh api graphql via $(cat "${GQL_ERR}"). If the API error contains literal newlines, subsequent lines could be parsed as new workflow commands by the GHA runner. The error source is GitHub's own API server (not attacker-controlled), making this a defense-in-depth concern rather than an exploitable vulnerability.
    Remediation: Pipe through tr -d '\n' before interpolation, or use stderr output instead of a workflow command.

  • [fail-open] .github/workflows/reusable-dispatch.yml — When the GraphQL query fails, LINKED_PRS is set to empty and dispatch proceeds. This is a pre-existing, intentional fail-open (the old code used 2>/dev/null || true). The new code improves on it by emitting a ::warning::.

  • [authorization-logic-change] .github/workflows/reusable-dispatch.yml — The bot exclusion filter now only excludes fullsend-ai-coder (the coder bot) instead of both fullsend-ai[bot] and fullsend-ai-coder[bot]. Per docs/contributing/bot-identities.md, the triage bot (fullsend-ai) does not create PRs, so the narrower exclusion is correct and more conservative.

Previous run (7)

Review

Findings

Medium

  • [protected-path] .github/workflows/reusable-dispatch.yml — This file is under the .github/ protected path. The PR links to issue dispatch.yml "Check for existing PRs" guard blocks /fs-code forever due to bot-login format mismatch + overbroad mention search #5575 and explains the rationale (fixing two compounding bugs in the dispatch guard). Human approval is required for protected-path changes regardless of context.

  • [test-accuracy] internal/scaffold/fullsend-repo/scripts/pre-code-test.sh:445 — The GITHUB_OUTPUT test skip-output-false-on-no-prs passes an empty string as mock data, which with the updated mock now simulates a GraphQL failure (mock exits 1 for empty input), not a "no linked PRs" scenario. The test still passes because both paths produce skipped=false, but it no longer tests what its name implies. The actual "no PRs" scenario is covered by no-existing-prs-proceeds (using NO_PRS_JSON), but that test checks stdout, not GITHUB_OUTPUT.
    Remediation: Change the test input from "" to "${NO_PRS_JSON}" to test the "no linked PRs" scenario via GITHUB_OUTPUT as the name suggests.

Low

  • [GHA workflow command injection] .github/workflows/reusable-dispatch.yml, internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml, internal/scaffold/fullsend-repo/scripts/pre-code.sh — The ::warning:: command interpolates stderr from gh api graphql via $(cat "${GQL_ERR}"). If the API error contains literal newlines, subsequent lines could be parsed as new workflow commands by the GHA runner. The error source is GitHub's own API server (not attacker-controlled), making this a defense-in-depth concern rather than an exploitable vulnerability.
    Remediation: Pipe through tr -d '\n' before interpolation, or use stderr output instead of a workflow command.

  • [fail-open] .github/workflows/reusable-dispatch.yml — When the GraphQL query fails, LINKED_PRS is set to empty and dispatch proceeds. This is a pre-existing, intentional fail-open (the old code used 2>/dev/null || true). The new code improves on it by emitting a ::warning::.

  • [authorization-logic-change] .github/workflows/reusable-dispatch.yml — The bot exclusion filter now only excludes fullsend-ai-coder (the coder bot) instead of both fullsend-ai[bot] and fullsend-ai-coder[bot]. Per docs/contributing/bot-identities.md, the triage bot (fullsend-ai) does not create PRs, so the narrower exclusion is correct and more conservative.


Labels: PR modifies dispatch workflow and pre-code scripts — entirely within the dispatch subsystem

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added requires-manual-review Review requires human judgment component/dispatch Workflow dispatch and triggers labels Jul 24, 2026
@waynesun09
waynesun09 force-pushed the fix-5575-dispatch-pr-check branch from 59dfbce to b3e57dd Compare July 29, 2026 20:05
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 29, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 8:06 PM UTC · Completed 8:20 PM UTC
Commit: b3e57dd · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

waynesun09 added a commit that referenced this pull request Jul 29, 2026
The dispatch "Check for existing PRs" guard used --search "N in:title,body",
which matches any PR that mentions the issue number anywhere in its body —
including unrelated cross-references — and skipped code dispatch whenever
one existed. Its bot-authored-PR exclusion also never worked: GraphQL's
Bot.login omits the REST "[bot]" suffix, so the literal string comparison
against "fullsend-ai-coder[bot]" never matched.

Replace both with a GraphQL query for closedByPullRequestsReferences,
which only returns PRs that actually close the issue via Fixes/Closes/
Resolves keywords, and match the bot exclusion on __typename == "Bot"
instead of a REST-formatted login string. The query also returns MERGED
PRs regardless of includeClosedPrs, so filter on .state == "OPEN"
explicitly — otherwise a long-merged closer would permanently block
re-dispatch on a reopened issue. Add issues: read to both jobs'
permissions, since the query now resolves through the Issue type rather
than pull-requests-only fields. Separate stderr from the query's stdout
so an incidental warning on an otherwise-successful call can't pollute
the result and trigger a false-positive skip.

internal/scaffold/fullsend-repo/scripts/pre-code.sh had the identical
bug (same substring search, same broken bot-login comparison) and runs
as a second gate in the same code-dispatch pipeline, so the original
false-positive-skip failure mode could still recur through that path.
Apply the same fix there and correct pre-code-test.sh's mock fixtures,
which hardcoded the REST-suffixed bot login format and so validated the
wrong assumption about what the API actually returns.

Verified against production data: #5569 (false
positive: PR #5192 only mentions the issue, now correctly proceeds),
#1320 (historical: closed by now-merged PR #2373, correctly proceeds
since the closer is no longer open), #5560 (bot-authored closer,
correctly excluded), and #5575 itself (open, human-authored PR #5578
with a "Fixes" keyword, correctly detected as blocking).

Also bump closedByPullRequestsReferences's first from 20 to 100 (the
connection's API max) at all three call sites, since a long-lived,
repeatedly-reopened issue could otherwise silently truncate past the
20th closing-PR reference; add null-safety around the nodes array and
author login so a missing field degrades gracefully instead of erroring
or printing "null"; sanitize captured stderr before interpolating it
into a ::warning:: workflow command; fix a pre-code-test.sh case that
mocked a query failure while asserting the "no linked PRs" behavior;
and document a third bot-login format returned by gh's own --json
output (app/<slug> with a separate is_bot flag).

Assisted-by: Claude (fix, review), Grok (review)
Signed-off-by: Wayne Sun <gsun@redhat.com>
@waynesun09
waynesun09 force-pushed the fix-5575-dispatch-pr-check branch from b3e57dd to b9aa139 Compare July 29, 2026 20:42
@waynesun09
waynesun09 force-pushed the fix-5575-dispatch-pr-check branch from b9aa139 to f6fea94 Compare July 29, 2026 21:17
waynesun09 added a commit that referenced this pull request Jul 29, 2026
The dispatch "Check for existing PRs" guard used --search "N in:title,body",
which matches any PR that mentions the issue number anywhere in its body —
including unrelated cross-references — and skipped code dispatch whenever
one existed. Its bot-authored-PR exclusion also never worked: GraphQL's
Bot.login omits the REST "[bot]" suffix, so the literal string comparison
against "fullsend-ai-coder[bot]" never matched.

Replace both with a GraphQL query for closedByPullRequestsReferences,
which only returns PRs that actually close the issue via Fixes/Closes/
Resolves keywords, and match the bot exclusion on __typename == "Bot"
instead of a REST-formatted login string. The query also returns MERGED
PRs regardless of includeClosedPrs, so filter on .state == "OPEN"
explicitly — otherwise a long-merged closer would permanently block
re-dispatch on a reopened issue. Add issues: read to both jobs'
permissions, since the query now resolves through the Issue type rather
than pull-requests-only fields. Separate stderr from the query's stdout
so an incidental warning on an otherwise-successful call can't pollute
the result and trigger a false-positive skip.

internal/scaffold/fullsend-repo/scripts/pre-code.sh had the identical
bug (same substring search, same broken bot-login comparison) and runs
as a second gate in the same code-dispatch pipeline, so the original
false-positive-skip failure mode could still recur through that path.
Apply the same fix there and correct pre-code-test.sh's mock fixtures,
which hardcoded the REST-suffixed bot login format and so validated the
wrong assumption about what the API actually returns.

Verified against production data: #5569 (false
positive: PR #5192 only mentions the issue, now correctly proceeds),
#1320 (historical: closed by now-merged PR #2373, correctly proceeds
since the closer is no longer open), #5560 (bot-authored closer,
correctly excluded), and #5575 itself (open, human-authored PR #5578
with a "Fixes" keyword, correctly detected as blocking).

Also bump closedByPullRequestsReferences's first from 20 to 100 (the
connection's API max) at all three call sites, since a long-lived,
repeatedly-reopened issue could otherwise silently truncate past the
20th closing-PR reference; add null-safety around the nodes array and
author login so a missing field degrades gracefully instead of erroring
or printing "null"; sanitize captured stderr before interpolating it
into a ::warning:: workflow command; fix a pre-code-test.sh case that
mocked a query failure while asserting the "no linked PRs" behavior;
and document a third bot-login format returned by gh's own --json
output (app/<slug> with a separate is_bot flag).

Assisted-by: Claude (fix, review), Grok (review)
Signed-off-by: Wayne Sun <gsun@redhat.com>
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 29, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 9:20 PM UTC · Completed 9:37 PM UTC
Commit: f6fea94 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot removed the requires-manual-review Review requires human judgment label Aug 7, 2026
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 7, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 2:02 PM UTC · Ended 2:04 PM UTC

Commit: 4fecd78 · View workflow run →

The dispatch "Check for existing PRs" guard used --search "N in:title,body",
which matches any PR that mentions the issue number anywhere in its body —
including unrelated cross-references — and skipped code dispatch whenever
one existed. Its bot-authored-PR exclusion also never worked: GraphQL's
Bot.login omits the REST "[bot]" suffix, so the literal string comparison
against "fullsend-ai-coder[bot]" never matched.

Replace both with a GraphQL query for closedByPullRequestsReferences,
which only returns PRs that actually close the issue via Fixes/Closes/
Resolves keywords, and match the bot exclusion on __typename == "Bot"
instead of a REST-formatted login string. The query also returns MERGED
PRs regardless of includeClosedPrs, so filter on .state == "OPEN"
explicitly — otherwise a long-merged closer would permanently block
re-dispatch on a reopened issue. Add issues: read to both jobs'
permissions, since the query now resolves through the Issue type rather
than pull-requests-only fields. Separate stderr from the query's stdout
so an incidental warning on an otherwise-successful call can't pollute
the result and trigger a false-positive skip.

internal/scaffold/fullsend-repo/scripts/pre-code.sh had the identical
bug (same substring search, same broken bot-login comparison) and runs
as a second gate in the same code-dispatch pipeline, so the original
false-positive-skip failure mode could still recur through that path.
Apply the same fix there and correct pre-code-test.sh's mock fixtures,
which hardcoded the REST-suffixed bot login format and so validated the
wrong assumption about what the API actually returns.

Verified against production data: #5569 (false
positive: PR #5192 only mentions the issue, now correctly proceeds),
proceeds since the closer is no longer open), #5560 (bot-authored
closer, correctly excluded), and #5575 itself (open, human-authored PR

Assisted-by: Claude
Signed-off-by: Wayne Sun <gsun@redhat.com>
In org/workflow_call mode neither the scaffold dispatch job nor the
shim-workflow-call.yaml caller grants issues: read, so the new
closedByPullRequestsReferences query is denied and the guard fails
open — dedup still holds via the pre-code.sh gate on a minted app
token. Record why the scope must not be added to the called job
alone: a called workflow cannot exceed its caller's grant, so repos
with un-reconciled shims would hard-fail at job start. Safe rollout
order, if ever wanted, is shim template first, then this job.

Assisted-by: Claude
Signed-off-by: Wayne Sun <gsun@redhat.com>
@waynesun09
waynesun09 force-pushed the fix-5575-dispatch-pr-check branch from 4fecd78 to 133ae91 Compare August 7, 2026 14:03
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 7, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 2:05 PM UTC · Completed 2:22 PM UTC

Commit: 133ae91 · View workflow run →

@waynesun09
waynesun09 enabled auto-merge August 7, 2026 14:05
@fullsend-ai-review
fullsend-ai-review Bot dismissed their stale review August 7, 2026 14:22

Superseded by updated review

@fullsend-ai-review fullsend-ai-review 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.

See the review comment for full details.

Comment thread internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml
Comment thread .github/workflows/reusable-dispatch.yml
@fullsend-ai-review fullsend-ai-review Bot added the requires-manual-review Review requires human judgment label Aug 7, 2026
Commit 4df01b1 removed issues: read from the scaffold dispatch job
(bundled in a CI-retrigger commit): the shim-workflow-call.yaml caller
does not grant the scope, and a called workflow requesting more than
its caller grants fails at job start — breaking every org-mode
dispatch until shims are re-reconciled. The rebase that produced
9f9a9ca dropped that commit as presumed-empty, silently restoring
the permission and contradicting the fail-open NOTE added in
133ae91. Re-remove it; org mode intentionally fails open here and
pre-code.sh enforces dedup on a minted app token.

Assisted-by: Claude
Signed-off-by: Wayne Sun <gsun@redhat.com>
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 7, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 2:30 PM UTC · Completed 2:50 PM UTC

Commit: 14867d7 · View workflow run →

@waynesun09
waynesun09 added this pull request to the merge queue Aug 7, 2026
Merged via the queue into main with commit f398f78 Aug 7, 2026
24 of 26 checks passed
@waynesun09
waynesun09 deleted the fix-5575-dispatch-pr-check branch August 7, 2026 14:50
@fullsend-ai-review

Copy link
Copy Markdown

Review skipped — this PR is already merged.

The /fs-review command only reviews open pull requests.

Posted by fullsend post-review check

@fullsend-ai-retro

fullsend-ai-retro Bot commented Aug 7, 2026

Copy link
Copy Markdown

🤖 Finished Retro · ✅ Success · Started 2:53 PM UTC · Completed 3:11 PM UTC

Commit: 14867d7 · View workflow run →

@fullsend-ai-retro

Copy link
Copy Markdown

Retro: PR #5578 — fix dispatch existing-PR guard

Verdict: workflow went well. No novel proposals needed — all improvement areas are already covered by existing open issues.

What happened

PR #5578 was a human-authored fix for #5575, a critical bug in the dispatch workflow's "Check for existing PRs" guard. The guard used a substring search (gh pr list --search "N in:title,body") that matched any PR mentioning the issue number, combined with a broken bot-login comparison (REST [bot] suffix vs GraphQL format). The fix replaced both with a GraphQL closedByPullRequestsReferences query.

The PR was open for 14 days (Jul 24 – Aug 7), received 11 review.yml dispatches (8 completed, 2 cancelled, 1 post-merge skip), human approval from rh-hemartin on Aug 4, and was merged after 7 force-pushes (mostly rebases).

Review agent quality

The fullsend review agent performed excellently on this PR:

  • Zero false positives across 8 completed reviews. Every finding was either actionable or correctly marked as informational.
  • Two high-value true positives that directly led to code fixes:
    1. [high] missing-permission — A rebase accidentally reintroduced issues: read to the scaffold dispatch.yml after it had been deliberately removed (the scaffold runs as workflow_call, where the caller's permissions apply). The agent caught this regression on the first review after the rebase introduced it.
    2. [medium] comment-contradicts-code — After the human pushed a fix, a code comment still said "this job's token lacks issues: read" while the permission was present at line 29.
  • Well-calibrated severity — Low-severity informational findings (fail-open, authorization-logic-change, edge-case) were correctly not escalated, and the human confirmed each as intentional/acceptable.
  • Contrast with qodo-code-review: qodo had 2–3 false positives, including flagging the GraphQL bot login format as a bug — the exact issue the PR was fixing.

Iteration cost

11 review workflow runs is high for a 4-file, +77/−20 change. The iteration count was driven by:

  • 7 force-pushes over 14 days (each triggers a review)
  • Stale-head re-dispatch mechanism firing on Aug 7 13:35
  • Author reply comments on Aug 7 generating pull_request_review/submitted events (6 fullsend.yaml shim runs triggered, 2 cancelled, 4 succeeded as no-ops)

Evidence for existing open issues

Autonomy readiness

The review agent's zero-false-positive record on this PR demonstrates high precision for workflow file changes. However, the PR correctly carried the requires-manual-review label — workflow files are security-sensitive, and the HIGH finding required human judgment about the workflow_call permission model to resolve correctly (the permission, not the comment, was the error). The agent identified the problem; the human determined the correct resolution direction. This complementary model is working well for this class of change.

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

Labels

component/dispatch Workflow dispatch and triggers requires-manual-review Review requires human judgment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dispatch.yml "Check for existing PRs" guard blocks /fs-code forever due to bot-login format mismatch + overbroad mention search

2 participants