Skip to content

Fix reviewer publication recovery without expanding permissions - #69474

Merged
pelikhan merged 1 commit into
mainfrom
pelikhan-reviewer-publication-fix
Oct 16, 2026
Merged

pelikhan merged 1 commit into
mainfrom
pelikhan-reviewer-publication-fix

Conversation

@pelikhan

Copy link
Copy Markdown
Collaborator

Summary

The PR Code Quality Reviewer exhausted its five-denial budget after prefixing review publication with an unallowed availability probe, leaving no terminal safe output.

Keep the existing tool allowlist unchanged and instruct the reviewer to invoke the stdout-only printf | safeoutputs publication pipelines directly, without availability probes. Specify allowed optional-file checks, keep analysis static instead of attempting unallowed test runners, and bound recovery to one corrected publication attempt followed by an explicit report_incomplete pipeline.

Add regression assertions for the publication/recovery contract and regenerate the workflow lock. The generated lock changes only the prompt hash; permissions, credentials, output budgets, and runtime controls remain unchanged.

Validation

  • make fmt
  • make recompile (344 workflows compiled)
  • go test ./pkg/cli -run '^TestPRCodeQualityReviewerWorkflow' -count=1
  • make agent-report-progress (impacted tests, lint, schema checks, and workflow drift passed)
  • gh aw compile pr-code-quality-reviewer --dry-run --schedule-seed github/gh-aw passed source/model checks and shellcheck with the existing gh-aw-detection experimental notice; optional scanners were not run.
  • Checked all three documented publication/recovery pipelines against the unchanged SDK permission handler; unsupported probes and commands remain denied.

No workflow was dispatched. Hosted publication remains unverified.

Remove availability-probe dependencies, keep optional reads within the existing tool contract, and document bounded incomplete reporting before the denial budget is exhausted.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@pelikhan
pelikhan marked this pull request as ready for review October 16, 2026 19:37
Copilot AI max review requested due to automatic review settings October 16, 2026 19:37
@pelikhan
pelikhan merged commit 7d5a6f9 into main Oct 16, 2026
34 of 46 checks passed
@pelikhan
pelikhan deleted the pelikhan-reviewer-publication-fix branch October 16, 2026 19:38
@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

✅ Test Quality Sentinel completed test quality analysis.

Test Quality Sentinel

Outcome: reported_no_action

The agent completed and reported no action. A no-op output alone does not verify that all required work was completed or that publication was possible.

Agent messages are omitted from this comment. The category uses structured run status, not interpretation of agent prose.

Generated from Test Quality Sentinel · sonnet50 · 13.2 AIC · ⌖ 15.1 AIC · ◇ 6.53 AIC · ⊞ 12.6K · ◷

🧪 Test quality analysis by Test Quality Sentinel

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

✅ PR Code Quality Reviewer completed the code quality review.

🔎 Code quality review by PR Code Quality Reviewer

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

✅ Ponytail Reviewer completed successfully!

Ponytail Reviewer

Outcome: reported_no_action

The agent completed and reported no action. A no-op output alone does not verify that all required work was completed or that publication was possible.

Agent messages are omitted from this comment. The category uses structured run status, not interpretation of agent prose.

Generated from Ponytail Reviewer · gpt56 · 6.04 AIC · ⌖ 5.33 AIC · ⊞ 14.6K · ◷

Generated by Ponytail Reviewer for #69474

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

✅ Design Decision Gate 🏗️ completed the design decision gate check. See the comment below for the result and any generated ADR draft.

Design Decision Gate 🏗️

Outcome: reported_no_action

The agent completed and reported no action. A no-op output alone does not verify that all required work was completed or that publication was possible.

Agent messages are omitted from this comment. The category uses structured run status, not interpretation of agent prose.

Generated from Design Decision Gate 🏗️ · opus55 · 11 AIC · ⌖ 36.3 AIC · ◇ 15.7 AIC · ⊞ 1.9K · ◷

🏗️ ADR gate enforced by Design Decision Gate 🏗️

@github-actions

Copy link
Copy Markdown
Contributor

Comment Memory

Peek at saved memory (pr-code-quality-reviewer)
reviewed_at: 2026-10-16T19:40:31Z
review_event: REQUEST_CHANGES
top_themes:
  - broken fallback to unavailable report_incomplete tool
  - ineffective contract test for forbidden command probes
files_reviewed:
  - .github/workflows/pr-code-quality-reviewer.md
  - pkg/cli/pr_code_quality_reviewer_workflow_contract_test.go
comment_count: 2

Note

This comment is managed by comment memory.

Expand the saved memory block to view or edit the persistent context for this thread.
Edit only the text inside the backtick fences; workflow metadata and the footer are regenerated automatically.

Learn more about comment memory

🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt54 · 37.4 AIC · ⌖ 5.94 AIC · ⊞ 12.4K · ◷
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Request changes

The patch still leaves the reviewer with a broken failure path and a weak contract test, so it does not actually guarantee review publication under denial conditions.

Blocking themes
  • The new fallback instructs the agent to call report_incomplete, but that tool is not enabled for this workflow, so the documented recovery path can fail in the exact scenario it is supposed to save.
  • The new test for forbidden command probes uses a literal NotContains check that does not match real YAML command entries, so CI will not reliably catch the regression this PR claims to prevent.
  • Static review only: I did not execute repo code or tests.

🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt54 · 37.4 AIC · ⌖ 5.94 AIC · ⊞ 12.4K
Comment /review to run again

If a publication command is denied, distinguish an unavailable CLI from unallowed commands or file writes in the pipeline. Correct the command to the stdout-only `printf` pipeline above without widening permissions. Make at most one corrected publication attempt, then stop probing and emit `report_incomplete` through the available safe-output transport with the concrete blocker, before exhausting the five-denial budget; do not call `noop` or claim the review was published. With this workflow's CLI transport, use:

```bash
printf '%s' '{"reason":"Review publication blocked: describe the concrete tool denial or CLI error; no review was published."}' | safeoutputs report_incomplete .

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This recovery path points the reviewer at safeoutputs report_incomplete, but that tool is not enabled for this workflow, so the first publication failure still strands the run without a terminal safe output.

💡 Why this is still broken

The safe-output contract for this job exposes create_pull_request_review_comment, submit_pull_request_review, create_check_run, missing_tool, missing_data, and noop—not report_incomplete. That means the new fallback command will be denied in exactly the failure path where we need it most, so the reviewer still cannot emit the required terminal action.

Suggested fix:

Either add `report_incomplete` to the workflow's enabled safe-output tools, or change the documented recovery path to use one of the tools that is actually available here.

Right now this patch teaches the agent to take a recovery action that the sandbox cannot perform.


text := string(content)
assert.NotContains(t, text, "command -v safeoutputs", "Publication must not depend on an unallowed availability probe")
assert.NotContains(t, text, " - command:*\n")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This assertion does not protect the contract you describe: assert.NotContains(..., " - command:*\n") only rejects that exact literal text, so a real YAML - command: entry still passes and CI gives false confidence.

💡 Use a pattern check instead of an impossible literal

The regression here is line-pattern based, not exact-string based. As written, the test only fails if someone literally pastes the asterisk into the workflow, which means the forbidden command stanza can come back without tripping this guard.

Suggested fix:

assert.NotRegexp(t, regexp.MustCompile(`(?m)^\s*- command:`), text)

Any equivalent line-based matcher would work; the important part is validating actual YAML command entries instead of a single impossible literal.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

A successful corrected publication can still trigger incomplete reporting and fail the run.

1 open finding
What changed in this PR

Updates the PR Code Quality Reviewer’s publication and recovery guidance without expanding permissions.

Changes:

  • Directs stdout-only publication with bounded recovery.
  • Specifies allowed file checks and static-only analysis.
  • Adds regression assertions and refreshes the generated prompt hash.
File Description
pkg/​cli/​pr_code_quality_reviewer_workflow_contract_test.go Adds publication, recovery, and tool-use assertions.
.github/​workflows/​pr-code-quality-reviewer.md Clarifies publication pipelines, recovery, and analysis limits.
.github/​workflows/​pr-code-quality-reviewer.lock.yml Updates only the prompt body hash.

🧠 Review effort: Max


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

```

If a publication command is denied, distinguish an unavailable CLI from unallowed commands or file writes in the pipeline. Correct the command to the stdout-only `printf` pipeline above without widening permissions. If the corrected command still cannot run, emit `report_incomplete` through the available safe-output transport with the concrete blocker; do not call `noop` or claim the review was published.
If a publication command is denied, distinguish an unavailable CLI from unallowed commands or file writes in the pipeline. Correct the command to the stdout-only `printf` pipeline above without widening permissions. Make at most one corrected publication attempt, then stop probing and emit `report_incomplete` through the available safe-output transport with the concrete blocker, before exhausting the five-denial budget; do not call `noop` or claim the review was published. With this workflow's CLI transport, use:

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Skills-Based Review 🧠

Applied /diagnosing-bugs and /tdd (per pr-triage). This is a well-scoped, well-tested fix for a real bug: the reviewer workflow prepended an unauthorized command -v safeoutputs availability probe before publishing, burning denials against the default 5-denial budget (confirmed via pkg/constants/engine_constants.go DefaultMaxToolDenials = 5) before any terminal safe output could be emitted.

📋 Key Themes & Highlights

Root cause vs. symptom (/diagnosing-bugs)

  • The fix addresses the actual root cause, not just the symptom: it replaces ambiguous instructions ("if reviewed before", "at least 3 bytes") that implicitly invited shell probing (stat, test -f, command substitution) with explicit, allowed-tool instructions (glob for existence checks, exact wc -c invocation).
  • Verified glob is a neutral builtin tool (COPILOT_SDK_NEUTRAL_BUILTIN_TOOLS in actions/setup/js/copilot_sdk_tool_config.cjs) that bypasses the tools.bash allowlist entirely — so routing the optional-file check through glob is technically sound and doesn't require widening tools.bash.
  • The explicit prohibition list (stat, awk, shell tests, command substitution, || true) directly matches what's absent from the workflow's actual tools.bash allowlist (cat, date, echo, find, git:*, grep, head, ls, nl, printf, pwd, sed, sort, safeoutputs, tail, uniq, wc, yq) — the fix is consistent with the real tool surface, not just aspirational.
  • The new bounded-recovery contract ("at most one corrected publication attempt... before exhausting the five-denial budget") converts an unbounded retry loop into a budgeted one, which is the correct fix shape for a denial-budget exhaustion bug.

Regression coverage (/tdd)

  • TestPRCodeQualityReviewerWorkflowToolContract and the extended TestPRCodeQualityReviewerWorkflowPublicationContract assertions directly encode the new contract (absence of the probe, presence of the glob/wc -c instructions, the bounded-recovery language) — these are genuine regression tests, not just restating implementation.
  • Left one minor inline nit: the NotContains(" - command:*\n") assertion (line 70) would benefit from a short comment explaining its intent (guarding against re-adding the command builtin, which would re-enable the probe) — non-blocking.

Scope discipline

  • Confirmed via the compiled lock file that permissions/budgets are genuinely unchanged (GH_AW_MAX_TOOL_DENIALS: 5 unchanged) — the PR's claim of not expanding permissions holds up.

No security, correctness, or architectural concerns found. Approving.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · copilot · sonnet50 · 205.9 AIC · ⌖ 16.3 AIC · ⊞ 10.1K
Comment /matt to run again


text := string(content)
assert.NotContains(t, text, "command -v safeoutputs", "Publication must not depend on an unallowed availability probe")
assert.NotContains(t, text, " - command:*\n")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[/tdd] This NotContains(" - command:*\n") guard is a good regression test but its intent (prevent re-adding the command builtin to tools.bash, which would re-enable the command -v safeoutputs probe that caused the original bug) isn't obvious from the assertion alone.

💡 Suggestion

Add a short inline comment above the assertion, e.g.:

// Guard against re-introducing the `command` builtin, which would let the
// workflow probe tool availability via `command -v` again (the original bug).
assert.NotContains(t, text, "    - command:*\n")

This keeps the test self-documenting per /tdd (test names/comments should read as specifications) without changing behavior.

@copilot please address this.

@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.92.5

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants