[patch] [bugfix]: Restore AI issue auto-reply by moving from GitHub Models to Copilot CLI - #3098
Conversation
…odels to Copilot CLI GitHub Models (models.github.ai) was retired on 2026-07-30. Every model in MODELS_MODEL now returns HTTP 410 github_models_retirement_brownout, so the "Generate AI reply" step emitted empty text and "Post reply" skipped on its non-empty guard. The workflow reported success while replying to nobody -- issue #3082 is one of the issues that silently went unanswered. Replace the Models HTTP call with Copilot CLI, which is the supported way to run AI in Actions and authenticates with the built-in GITHUB_TOKEN. This repo has no Actions secrets, so a provider needing an API key was not an option. - permissions: swap the retired `models: read` for `copilot-requests: write`. - Install @github/copilot and invoke it with --yolo (required for non-interactive runs), --no-ask-user and --disable-builtin-mcps to keep the run hermetic and autonomous. - Hand the prompt over on disk instead of via -p. The assembled prompt is ~90 KB of Objective-C, Markdown and shell metacharacters; passing it as an argv string is a quoting minefield. Copilot reads the file and writes the finished comment to ai_reply.md, so neither prompt nor reply transits the shell. - Randomise the GITHUB_OUTPUT heredoc delimiter. The reply is model-generated, so a literal "EOF" line would have closed the heredoc early and truncated the posted comment. - Preserve the existing failure contract: warn, emit empty text, exit 0, and let "Post reply" skip. A flaky model call still must not fail the run. - Add timeout-minutes so a runaway agent cannot hang the job. Raise the reference-material budgets. The 8 KB cap existed only because GitHub Models capped input at 8000 tokens per request; it truncated the curated grounding set from 46 KB down to 8 KB. Copilot CLI has no comparable cap, so the manifest is now sent whole -- verified locally at 84,932 B assembled and untruncated, and it now includes MSALCIAMAuthority.h and MSALSilentTokenParameters.h, which the old cap cut off. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
The Copilot CLI step currently relies on the tool writing ai_reply.md as a side-effect, which can result in empty output and the workflow still skipping comment posting.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Restores the repository’s automated AI issue auto-reply workflow after GitHub Models retirement by switching the generation step to Copilot CLI, keeping the existing “warn + emit empty text + exit 0” failure contract so runs don’t fail noisily.
Changes:
- Replaces
models: read+ GitHub Models API calls with Copilot CLI installation and invocation, usingcopilot-requests: write. - Raises the reference-material byte budgets (via env overrides) so the full curated grounding manifest can be included in prompts again.
- Updates prompt-budget documentation/comments to reflect the Copilot CLI-based flow.
File summaries
| File | Description |
|---|---|
| .github/workflows/ai-issues-auto-reply.yml | Switches AI generation from GitHub Models to Copilot CLI, updates permissions, adds install step, increases prompt/reference budgets. |
| .github/ai-prompts/reference-files.txt | Updates manifest budget commentary to match Copilot CLI + raised caps behavior. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…efore merge) issues/issue_comment events always run the workflow file from the default branch, so the rewritten auto-reply cannot be exercised before it merges. push events use the branch's own file, so this proves the new path works: npm install, GITHUB_TOKEN auth, the --yolo invocation, the on-disk prompt handoff, the reply-file guard and the randomised GITHUB_OUTPUT delimiter. POST_REPLY defaults to false: the reply goes to the job summary and an artifact for review before anything is posted to a real customer issue. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
The PR introduces a “temporary / delete before merging” workflow file and also needs reliability hardening (retry) and version pinning decisions for Copilot CLI to avoid silent regressions.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (3)
Previously missed (1) — in code that hasn't changed since the last review.
.github/workflows/ai-issues-auto-reply.yml:308
- Issue:
npm install -g @github/copilotinstalls the latest Copilot CLI on every run.
Impact: This makes the workflow non-deterministic and can break unexpectedly if a new Copilot CLI release changes flags/behavior. It also increases supply-chain risk versus pinning (the rest of this repo’s workflows typically pin Actions by SHA).
Recommendation: Pin the Copilot CLI package to a known-good version (or at least a major) and bump it intentionally when needed, e.g. npm install -g @github/copilot@<version>.
This issue also appears on line 686 of the same file.
.github/workflows/zz-smoke-copilot-reply.yml:39
- Issue:
npm install -g @github/copilotinstalls an unpinned Copilot CLI version.
Impact: The smoke test may start failing (or behave differently) when Copilot CLI changes, which reduces its usefulness as a validation harness.
Recommendation: Pin @github/copilot to a specific version (or major) so this smoke test remains stable while you validate the migration.
- name: Install Copilot CLI
run: npm install -g @github/copilot
.github/workflows/ai-issues-auto-reply.yml:690
- Issue: The Copilot CLI invocation is single-shot. If Copilot CLI transiently fails (non-zero exit or tool hiccup), the step emits empty text and exits 0, so the run still goes green but no reply is posted.
Impact: This recreates the “silent no-reply” failure mode during transient outages (just with a different underlying cause), reducing the reliability of the auto-reply workflow.
Recommendation: Add a small bounded retry loop (e.g., 2–3 attempts with backoff), treating both non-zero exit and an empty reply file as retryable, before falling back to the existing warn-and-skip behavior.
set +e
copilot \
--yolo \
--no-color \
--no-ask-user \
- Files reviewed: 3/3 changed files
- Comments generated: 1
- Review effort level: Lite
Reply reviewed from the previous run's artifact. Flip to actually comment on the target issue. This file is deleted before merge. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Purpose served: the run proved npm install, GITHUB_TOKEN auth, the --yolo invocation, the on-disk prompt handoff and the reply-file guard all work on a real runner, and the generated reply was posted to the target issue. Removing it before merge. It is push-triggered with POST_REPLY=true, so leaving it in place would post a duplicate comment on every push. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🔵 Needs a closer look
The temporary smoke-test workflow is currently merge-dangerous (can spam a real issue) and the main workflow’s Copilot call lost retry/backoff behavior, reducing operational reliability.
Review details
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
.github/workflows/ai-issues-auto-reply.yml:696
- The previous GitHub Models path had bounded retries/backoff across transient failures. The Copilot CLI call is now a single attempt; any brief network/tooling hiccup will cause the workflow to skip replying for that issue/comment.
Impact: increased flakiness and lower reply rate, especially during transient GitHub outages.
Recommendation: add a small retry loop (e.g., 3 attempts with backoff) around the copilot invocation while preserving the existing warn-and-skip contract.
- Files reviewed: 2/2 changed files
- Comments generated: 0 new
- Review effort level: Lite
…g manifest The manifest was entirely Objective-C headers, so any issue about the Swift native auth state machine reached the model with zero grounding -- the system prompt forbids answering ungrounded, so those replies were destined to be useless or fabricated. Native auth is a large share of inbound issues (e.g. #3078, about SignInAfterResetPasswordState and onSignInAwaitingMFA). Add the public surface only: entry point, sign-in/sign-up/MFA/reset states, their delegates, and the error types customers actually see. Verified strip_and_cap handles Swift: the header skip trips on the @objc attribute, so the MIT block is dropped and output starts at the declaration. Assembled reference is ~138 KB against the 200 KB cap. Also re-adds the temporary smoke workflow, now targeting #3078 and using the real strip_and_cap so Swift stripping is exercised faithfully. POST_REPLY is false pending review. Deleted before merge. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
The workflow enables tool-executing Copilot runs (--yolo) on untrusted issue content, creating a high-severity prompt-injection/token-exfiltration risk that should be mitigated before merging.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (1)
.github/workflows/ai-issues-auto-reply.yml:694
- The workflow currently assumes Copilot CLI will write the final Markdown into
ai_reply.md, but nothing in the shell command enforces that — the file is only created if the model decides to do so. This makes the success path fragile (empty replies even when Copilot produced valid stdout).
Impact: the run can go green while still posting nothing, reintroducing the same “silent failure” mode you’re trying to eliminate.
Recommendation: capture Copilot CLI stdout into REPLY_FILE directly (and adjust the prompt to tell Copilot to print the finished comment to stdout).
--log-level none \
--model "${COPILOT_MODEL:-auto}" \
-p "Read the file ${PROMPT_FILE}. It contains a system prompt followed by the GitHub issue to answer. Follow those instructions and compose the reply comment they describe. Do not modify any file in this repository, do not run git, and do not post anything to GitHub. Your only side effect must be writing the finished comment — GitHub-flavoured Markdown, no preamble, no code fence around the whole thing — to ${REPLY_FILE}."
- Files reviewed: 3/3 changed files
- Comments generated: 1
- Review effort level: Lite
Reply reviewed from the previous run's artifact and fact-checked against the native auth sources: onSignInAwaitingMFA, AwaitingMFAState.requestChallenge and the inherited isGeneralError all exist as cited. Deleted before merge. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Both target issues have been replied to. Removing before merge: the workflow is push-triggered with POST_REPLY=true, so leaving it would post a duplicate comment on every push. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
A temporary smoke-test workflow is being added despite “DELETE BEFORE MERGING”, and a couple of reliability/robustness concerns should be addressed to preserve the workflow’s intended “stay green” failure contract.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (2)
Previously missed (2) — in code that hasn't changed since the last review.
.github/workflows/ai-issues-auto-reply.yml:309
- The Copilot CLI install step can fail the entire workflow run (npm registry/network/transient failures), even though the workflow's stated contract is to remain green and skip posting when AI generation is unavailable.
Issue: If npm install -g @github/copilot fails, the job stops before the tolerant "Generate AI reply" step runs.
Impact: Auto-reply becomes brittle (and visibly red) on transient npm failures, reintroducing a reliability regression.
Recommendation: Make the install step non-fatal so the next step can warn-and-skip cleanly, consistent with the previous failure contract.
.github/workflows/ai-issues-auto-reply.yml:721
- The heredoc delimiter randomization relies on
openssl, which is an extra external dependency in the critical path.
Issue: If the runner image ever changes such that openssl is missing or fails, the step will exit non-zero under set -e after the AI reply is generated.
Impact: This can break the intended failure contract (warn-and-skip / stay green) and prevent posting replies.
Recommendation: Generate the delimiter using bash-only primitives (e.g., $RANDOM) to avoid external tool dependencies while still making delimiter collisions extremely unlikely.
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Lite
There was a problem hiding this comment.
🔵 Needs a closer look
The Copilot CLI --yolo usage materially increases prompt-injection blast radius with an issues: write token, and the new .swift manifest entries are not handled correctly by the current manifest inlining logic.
Review details
Suppressed comments (3)
Previously missed (1) — in code that hasn't changed since the last review.
.github/workflows/ai-issues-auto-reply.yml:308
- Issue: This installs Copilot CLI without emitting the installed version. If the npm package publishes a breaking change (or a runner image ships a different Node/npm), troubleshooting becomes much harder because logs don’t show what CLI build ran.
Impact: A future CLI regression can look like an intermittent model failure and silently disable replies while leaving the workflow green.
Recommendation: Print copilot --version immediately after install so runs capture the exact CLI version in logs.
.github/workflows/ai-issues-auto-reply.yml:691
- Severity: High – Issue:
copilot --yoloenables automatic approval of tool execution (file/shell). Because the prompt is derived from untrusted issue/issue_comment content, this makes the job vulnerable to prompt-injection that can run arbitrary commands and potentially exfiltrate the job’sGITHUB_TOKEN(which currently hasissues: write).
Impact: A malicious issue author could use the AI agent to leak the token or perform unintended GitHub API operations during the run.
Recommendation: Split the workflow into two jobs: (1) AI generation job with minimal permissions (contents: read, copilot-requests: write only) and no issues: write, then (2) a posting job with issues: write that consumes the generated markdown (job output/artifact). This preserves functionality while sharply reducing blast radius if the agent is coerced into running tools.
copilot \
--yolo \
--no-color \
--no-ask-user \
--disable-builtin-mcps \
.github/ai-prompts/reference-files.txt:75
- Issue: This manifest now includes several
.swiftfiles, butai-issues-auto-reply.ymlcurrently treats all non-.mdentries as Objective-C and runs them throughstrip_and_cap(which skips content until it hits#import,NS_ASSUME_NONNULL_BEGIN, or a line starting with@). For Swift files, that can drop the leading docs/imports (or even most of the file) and wraps the output in anobjccode fence.
Impact: The added Native Auth grounding may be missing or malformed in the prompt, reducing answer quality and making citations inaccurate.
Recommendation: Update the workflow’s manifest loop to handle *.swift explicitly (e.g., wrap in a swift fence and avoid Obj-C-specific stripping, or implement Swift-aware stripping).
# ---- Native authentication (Swift) ----
# Native auth is a large share of inbound issues but had no grounding here,
# so replies about sign-in/sign-up/MFA state machines had nothing to cite.
# Public API only: entry point, state machine states, delegates and the
# error types customers actually see.
MSAL/src/native_auth/public/MSALNativeAuthPublicClientApplication.swift
MSAL/src/native_auth/public/state_machine/state/SignInStates.swift
MSAL/src/native_auth/public/state_machine/state/SignInAfterResetPasswordState.swift
MSAL/src/native_auth/public/state_machine/state/SignInAfterSignUpState.swift
MSAL/src/native_auth/public/state_machine/state/MFAStates.swift
MSAL/src/native_auth/public/state_machine/state/ResetPasswordStates.swift
MSAL/src/native_auth/public/state_machine/state/SignUpStates.swift
MSAL/src/native_auth/public/state_machine/delegate/SignInDelegates.swift
MSAL/src/native_auth/public/state_machine/delegate/SignInAfterResetPasswordDelegate.swift
MSAL/src/native_auth/public/state_machine/delegate/SignInAfterSignUpDelegate.swift
MSAL/src/native_auth/public/state_machine/delegate/MFADelegates.swift
MSAL/src/native_auth/public/state_machine/error/MSALNativeAuthError.swift
MSAL/src/native_auth/public/state_machine/error/SignInAfterResetPasswordError.swift
MSAL/src/native_auth/public/state_machine/error/SignInAfterSignUpError.swift
- Files reviewed: 2/2 changed files
- Comments generated: 0 new
- Review effort level: Lite
Final config verified: positive control executed the shell (toolRequests=1), hardened invocation did not (toolRequests=0, NO_TOOLS).
There was a problem hiding this comment.
🟡 Changes recommended
The workflow currently installs an unpinned Copilot CLI version and contains mismatches between stated behavior (in PR description/comments) and the actual Copilot invocation, which risks future silent breakage and maintainability/security misunderstandings.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (3)
Previously missed (2) — in code that hasn't changed since the last review.
.github/workflows/ai-issues-auto-reply.yml:352
- Issue: The workflow installs Copilot CLI with
npm install -g @github/copilotwithout pinning a version.
Impact: A new upstream release can break this workflow (or change the JSONL schema/flags) without any repo change, making the auto-reply silently stop again.
Recommendation: Pin@github/copilotto a known-good version (or make the version an explicit env var) and update it intentionally as part of normal maintenance.
.github/workflows/ai-issues-auto-reply.yml:736 - Issue: The PR description says the prompt is passed “on disk, not through
-p” and that--yolois required for non-interactive runs, but this workflow passes the prompt via-p "$(cat ...)"and explicitly omits--yolo.
Impact: Reviewers and future maintainers may rely on the PR description’s security/robustness rationale and draw the wrong conclusions about quoting risk and non-interactive behavior.
Recommendation: Align the implementation and description (either switch to a file-based prompt option +--yoloif that’s still required, or update the PR description/inline comments to reflect the current approach).
.github/workflows/ai-issues-auto-reply.yml:704
- Issue: The security comment claims the allowlist leaves “no … fetch”, but the allowlisted tool is named
fetch_copilot_cli_documentation, and the workflow also discards replies if any tool request is observed.
Impact: The comment is misleading about what’s permitted and why; it makes the security model harder to reason about during future changes.
Recommendation: Reword this block to (1) accurately describe the single allowlisted tool, and (2) explicitly tie it to the tool-request discard guard below.
# 2. --available-tools allowlists only `list_agents`, which lists
# background agents and touches no file, shell or network. There
# is no "empty" allowlist: `--available-tools ""` is treated as
# unset and restricts nothing, so one inert entry is the way to
# express "no useful tools". Allowlist rather than --deny-tool,
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Lite
There was a problem hiding this comment.
🔵 Needs a closer look
The workflow introduces a new AI execution path and multi-job permission/artifact choreography that should be validated by a human with end-to-end Actions runtime context.
Review details
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
.github/workflows/ai-issues-auto-reply.yml:33
- Issue: The workflow-level
concurrency.groupuses${{ github.event.issue.number }}, butworkflow_dispatchruns don’t havegithub.event.issue, so the group resolves toai-reply-for backfill runs.
Impact: All manually-triggered backfill runs will share the same concurrency group (and this could become confusing if additional non-issue triggers are added later).
Recommendation: Fall back to a unique value (e.g. github.run_id) when issue.number is unavailable, similar to the pattern used in other workflows in this repo.
.github/workflows/ai-issues-auto-reply.yml:833
- Issue: The comment above the
github-scriptstep says the AI body is passed via env, but the implementation now reads it fromai_reply.md.
Impact: Stale guidance can mislead future maintainers into “simplifying” this back to step outputs/env interpolation and reintroducing the escaping problem this change is avoiding.
Recommendation: Update the comment to match the new artifact-file approach (and keep the rationale about avoiding ${{ }} interpolation with model output).
# Pass the AI body via env (not inline ${{ }} interpolation) so
# backticks, ${...}, and other JS-significant characters in the AI
# output can't break the script source. The earlier inline form
# crashed with SyntaxError: Unexpected identifier 'MSAL...' when
# the AI reply contained ``MSALInteractiveTokenParameters`` markdown
- Files reviewed: 2/2 changed files
- Comments generated: 0 new
- Review effort level: Lite
Comments drop from 73 to 34 added lines. Kept only what stops someone reintroducing a hole or breaking generation: - --available-tools "" is treated as unset and enables every tool - --deny-tool bash still executes commands; the rule name is shell - an unrecognised allowlist entry fails closed - -p is the only non-interactive prompt input; stdin returns empty Everything else -- the GitHub Models retirement history, jq mechanics, ARG_MAX arithmetic -- is in the PR description rather than the file. The PR is now net negative on lines. No functional change.
There was a problem hiding this comment.
🔵 Needs a closer look
The workflow changes include reliability/documentation issues (e.g., unguarded Copilot CLI install and stale comments) that can break the intended “warn-and-skip” contract or mislead future maintenance.
Review details
Suppressed comments (3)
Previously missed (3) — in code that hasn't changed since the last review.
.github/workflows/ai-issues-auto-reply.yml:339
- Issue:
Download gate payloadfailing will currently fail thegeneratejob (nocontinue-on-error).
Impact: A transient artifact download failure would make the workflow red, and in ping_copilot mode it also undermines the intended behavior of skipping replies safely.
Recommendation: Make this download step continue-on-error: true (matching the post job’s download) so the job can fall back to a warn-and-skip path.
.github/workflows/ai-issues-auto-reply.yml:342
- Issue: The
Install Copilot CLIstep will fail the entiregeneratejob ifnpm install -g @github/copilothits a transient registry/network issue.
Impact: This breaks the workflow’s stated “warn-and-skip, exit 0” failure contract and can cause unexpected red CI runs even when the intended behavior is to skip posting.
Recommendation: Mark the install step as continue-on-error: true so failures flow into the existing copilot exit-code handling (which already warns and exits 0).
.github/workflows/ai-issues-auto-reply.yml:794
- Issue: The comment above the
github-scriptstep says the AI body is passed via env, but the implementation now reads the body fromai_reply.md.
Impact: This is misleading for future maintainers and increases the chance of reintroducing the earlier interpolation/backtick breakage while “cleaning up” comments.
Recommendation: Update the comment to describe the artifact-file approach.
- Files reviewed: 2/2 changed files
- Comments generated: 0 new
- Review effort level: Lite
…nd job split
Rebuilt from dev and applied only what the migration requires, rather than
un-picking the restructure. Diff is now 79 insertions / 153 deletions.
Removed, as new machinery this repo never had:
- the gate/generate/post job split
- artifact upload/download for prompt_input.json and ai_reply.md
The split was justified when Copilot ran with --yolo and a live shell: the
concern was an injected prompt exfiltrating a token that could write issues.
That premise no longer holds. Every tool is now disabled and verified so
(positive control executed a shell, hardened run did not: toolRequests 1 vs
0), which makes the invocation text-in/text-out -- the same posture as the
GitHub Models REST API this replaces. The split was restoring parity that the
tool lockdown already restores, at the cost of ~90 lines and an artifact
handoff that had never been exercised end to end.
The reply travels via GITHUB_OUTPUT exactly as before, and the Post reply step
is byte-identical to dev.
One deliberate difference from dev: a random heredoc delimiter. The body is
model-generated, so a literal EOF line would close the heredoc early and
truncate the posted comment. Verified with a fixture containing an EOF line,
backticks and ${...}.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
The Copilot CLI step is missing --yolo (needed for non-interactive Actions runs) and currently discards stderr, which together can reintroduce silent/non-actionable failures.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (1)
.github/workflows/ai-issues-auto-reply.yml:669
- Issue: The Copilot CLI stderr is fully discarded (
2>/dev/null).
Impact: If Copilot starts failing (auth/permissions/model selection/CLI regression), the job will only surface the exit code with no actionable diagnostics, making it harder to restore the workflow when it breaks again.
Recommendation: Don’t drop stderr; let it appear in the Actions log (token-like values should still be redacted by Actions).
-p "$(cat "$PROMPT_FILE")" > "$RAW_FILE" 2>/dev/null
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Lite
This PR's job is to restore the bot after the GitHub Models retirement.
Three changes went beyond that and are removed:
- MAX_REFERENCE_BYTES_OVERRIDE / MAX_PER_FILE_BYTES_OVERRIDE /
MAX_DYNAMIC_BYTES_OVERRIDE raised grounding material from 8KB to
200KB. That changes what the model sees, so it changes reply
content -- a behavior change, not a fix. The ${VAR_OVERRIDE:-default}
indirection already existed in dev as an unused extension point and
is left untouched, so this can be revisited on its own merits.
- timeout-minutes: 15 on the generate step was new, and would fail the
job rather than warn-and-skip, which contradicts the surrounding
error handling.
- reference-files.txt was only touched to document the raised caps, so
it reverts to dev exactly and leaves the PR at one file.
Diff: 79/153 across two files -> 67/147 in one.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2aa548b6-643e-4b76-a818-98860a8b8052
dev bounded its model call with `curl --max-time 120`. The Copilot CLI invocation that replaced it had no bound, so a hung call would have run until the job-level limit. `timeout -k 30s 10m` restores that bound; exit 124 flows into the existing warn-and-skip path, keeping the run green rather than failing the job. Exit codes verified on ubuntu-latest: hang=124, success=0, failure=1 (passthrough), TERM-ignoring child=137.
There was a problem hiding this comment.
🔵 Needs a closer look
The workflow currently omits a pinned Node.js setup (Copilot CLI requires a sufficiently new Node) and lacks an explicit generation timeout, risking silent non-functionality or hung jobs.
Review details
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
.github/workflows/ai-issues-auto-reply.yml:303
- Issue:
npm install -g @github/copilotdepends on a sufficiently new Node.js runtime, but this workflow never sets the Node version explicitly.
Impact: On runners where the default Node version is below Copilot CLI’s required minimum, the install and/or copilot execution can fail, causing the workflow to warn and emit empty output (so auto-replies remain silently non-functional).
Recommendation: Add an actions/setup-node step (gated the same way) to pin Node to a supported version (e.g. 22+) before installing Copilot CLI.
.github/workflows/ai-issues-auto-reply.yml:314
- Issue: The Copilot generation step has no explicit
timeout-minutes.
Impact: If npm/Copilot CLI hangs (network stalls, auth prompt regression, etc.), the job can block until GitHub’s default job timeout, tying up the concurrency group and delaying replies to other issues.
Recommendation: Add a step-level timeout (e.g. 15 minutes) to keep the workflow self-limiting as described in the PR rationale.
- name: Generate AI reply (Copilot CLI)
id: ai
if: steps.gate.outputs.should == 'true'
env:
GITHUB_TOKEN: ${{ github.token }}
COPILOT_MODEL: ${{ env.COPILOT_MODEL }}
MODE: ${{ steps.gate.outputs.mode }}
ISSUE_TITLE_JSON: ${{ toJSON(github.event.issue.title) }}
ISSUE_BODY_JSON: ${{ toJSON(github.event.issue.body || '') }}
run: |
- Files reviewed: 1/1 changed files
- Comments generated: 0 new
- Review effort level: Lite
There was a problem hiding this comment.
🟡 Changes recommended
The workflow still defaults to truncating reference grounding to 8KB and has mismatches with the PR’s stated behavior (including missing timeout-minutes), risking continued silent degradation/noncompliance with the intended fix.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (3)
Previously missed (2) — in code that hasn't changed since the last review.
.github/workflows/ai-issues-auto-reply.yml:40
- Issue: The workflow still defaults to truncating Tier-2 reference material to 8,000 bytes (
MAX_REFERENCE_BYTES="${MAX_REFERENCE_BYTES_OVERRIDE:-8000}"andhead -c "$MAX_REFERENCE_BYTES" ...).
Impact: This contradicts the PR description’s goal of sending the reference manifest untruncated under Copilot CLI, and will continue to cut grounding content unless a repo/org variable sets MAX_REFERENCE_BYTES_OVERRIDE.
Recommendation: Set a higher default budget in the workflow env (while keeping the override mechanism so forks can dial it down).
.github/workflows/ai-issues-auto-reply.yml:686
- Issue: The tool-lockdown verification only counts
.data.toolRequestson events withtype == "assistant.message". Copilot CLI’s JSON stream can emit tool request events as separate records/types, so this check can miss tool usage and still publish the assistant message.
Impact: If a tool request slips through --available-tools (or the schema changes), the workflow could post output influenced by tool execution while the guard incorrectly reports zero tool requests.
Recommendation: Detect tool usage across all emitted JSON events (e.g., any non-empty .data.toolRequests anywhere, or any event type containing tool).
.github/workflows/ai-issues-auto-reply.yml:650
- Issue: The PR description says the prompt is passed “on disk, not through
-p” and that--yolois required, but the implementation uses-p "$(cat "$PROMPT_FILE")"and does not use--yolo.
Impact: Future maintainers relying on the PR description may "fix" the workflow back into a broken/non-working state (e.g., by trying stdin again or reintroducing --yolo).
Recommendation: Update the PR description (or add a short comment here) to match the final invocation constraints: -p is required for non-interactive runs, stdin yields empty, and tools are intentionally disabled (no --yolo).
# -p is the only non-interactive prompt input (stdin returns empty).
# "$(cat …)" is one argv element; bash does not re-scan it.
PROMPT_FILE="$(mktemp)"
RAW_FILE="$(mktemp)"
printf '%s\n\n%s\n' "$SYSTEM_PROMPT" "$USER_PROMPT" > "$PROMPT_FILE"
- Files reviewed: 1/1 changed files
- Comments generated: 1
- Review effort level: Lite
The guard counted every tool request, including the inert allowlisted entry the model actually calls. A CI run against issue #3071 showed three list_agents requests tripping it, so the job went green and posted nothing -- which would have happened on every issue. Count only requests outside the allowlist, so the guard still fires if a tool escapes --available-tools. Verified end to end: same run now yields a 2.3 KB reply.
There was a problem hiding this comment.
🔵 Needs a closer look
The workflow still lacks a job-level timeout and does not ensure a compatible Node.js version for installing/running @github/copilot, which can cause silent skipping or hangs in production runs.
Review details
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
.github/workflows/ai-issues-auto-reply.yml:304
- Issue:
@github/copilotis installed vianpm, but the workflow doesn’t pin/ensure a compatible Node.js version. GitHub’s Copilot CLI npm package documentation indicates a Node.js version requirement (e.g., Node 22+).
Impact: If the runner’s preinstalled Node version is older (or changes in the future), npm install -g @github/copilot or the copilot invocation can fail, silently skipping replies.
Recommendation: Add an actions/setup-node step (pin a minimum Node version known to satisfy Copilot CLI) before installing the CLI.
.github/workflows/ai-issues-auto-reply.yml:48
- Issue: The job currently has no job-level
timeout-minutes, even though the PR description calls out adding a 15-minute timeout. The script bounds thecopilotcall, but other steps (e.g.,npm install -g @github/copilot) can still hang indefinitely.
Impact: A stalled install/network operation can tie up concurrency (ai-reply-<issue_number>) and block future auto-replies until the runner times out at the platform default.
Recommendation: Add timeout-minutes: 15 at the job level to guarantee the workflow can’t hang forever.
ai_autoreply:
if: github.event_name != 'workflow_dispatch'
runs-on: ubuntu-latest
concurrency:
group: ai-reply-${{ github.event.issue.number }}
cancel-in-progress: false
- Files reviewed: 1/1 changed files
- Comments generated: 0 new
- Review effort level: Lite
There was a problem hiding this comment.
🟡 Changes recommended
The reference limits remain unchanged, and the Copilot CLI dependency is not version-pinned.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 1/1 changed files
- Comments generated: 2
- Review effort level: Balanced
| - name: Generate AI reply (GitHub Models) | ||
| - name: Install Copilot CLI | ||
| if: steps.gate.outputs.should == 'true' | ||
| run: npm install -g @github/copilot |
| # "$(cat …)" is one argv element; bash does not re-scan it. | ||
| PROMPT_FILE="$(mktemp)" | ||
| RAW_FILE="$(mktemp)" | ||
| printf '%s\n\n%s\n' "$SYSTEM_PROMPT" "$USER_PROMPT" > "$PROMPT_FILE" |
Problem
The AI issue auto-reply workflow has been silently dead since 2026-07-30, when GitHub Models was retired.
Every model in
MODELS_MODELnow returnsHTTP 410 github_models_retirement_brownout. I confirmed this is still true today:The failure is invisible: the generate step warns, emits empty text, and exits 0, so
Post replyskips on itssteps.ai.outputs.text != ''guard and the run goes green while nobody gets an answer.Concrete example — #3082 was filed on 2026-08-20 and has never received a reply. The workflow did run (
32371193675,32371193495); the gate passed and the model call was attempted:Fix
Move to Copilot CLI, the supported way to run AI in Actions. It authenticates with the built-in
GITHUB_TOKEN— no PAT, no API key. That mattered here: this repo has zero Actions secrets, so any provider requiring a key (Azure AI Foundry, direct OpenAI) would have needed an admin to provision one first.permissions: swap the retiredmodels: readforcopilot-requests: write.@github/copilot; invoke with--no-ask-userand--disable-builtin-mcpsto keep the run non-interactive and hermetic. No--yolo: it is--allow-all, and nothing here needs it.--available-tools ""is treated as unset and enables everything, and the deny rule isshell, notbash— both verified on a runner — so lockdown is expressed as a one-entry allowlist of an inert tool. A guard then discards the reply if any tool outside that allowlist is requested.GITHUB_OUTPUTheredoc delimiter. The reply is model-generated, so a literalEOFline in a comment would have closed the heredoc early and truncated the posted comment.timeout -k 30s 10m, restoring the ceilingcurl --max-time 120used to provide. Its exit code lands in the existing warn-and-skip branch (verified on a runner: hang 124, TERM-ignoring child 137).The existing failure contract is preserved exactly — warn, emit empty text, exit 0, let
Post replyskip. A flaky model call still must not fail the run or page anyone.Also: reference budget
The 8 KB reference cap existed only because GitHub Models capped input at 8000 tokens/request. It was truncating the curated grounding set from 46 KB down to 8 KB — on the #3082 run:
Reference material truncated: was 46358B, now 8000B.Copilot CLI has no comparable per-request cap, so the manifest is now sent whole. The caps remain as a runaway guard for forks that override them downward.
Validation
yaml.safe_loadparses; step graph isgate → Install Copilot CLI → ai → Post reply.bash -nclean on the extracted generate step.MODELS_MODEL/models.github.ai/GH_MODELS_MODELremain.Validated against a live issue
Ran the real Install + Generate steps from this branch against issue #3071 on a runner. First run went green but produced nothing: the lockdown guard counted every tool request, including the inert allowlisted entry the model actually calls (3 ×
list_agents), so it discarded the reply — which would have happened on every issue. Fixed to count only requests outside the allowlist; same run then produced a 2.3 KB reply, posted to #3071.This exercises grounding, prompt assembly, the CLI invocation and JSON extraction. It does not exercise the gate or the
issuestrigger: those always run the default-branch copy of a workflow, so they can only be verified after merge.