Safe-Outputs Pull Requests Enforcement Test Results
Run: https://github.com/github/gh-aw-mcpg/actions/runs/28446346123
Trigger: schedule
Configuration: create-pull-request (max:1, prefix, draft:true), close-pull-request (required-labels, required-prefix, max:1), update-pull-request (title:true, body:false, max:1, target:triggering), push-to-pr-branch (target:triggering, prefix), mark-ready (required-labels:[smoke-test], max:1), add-reviewer (reviewers:[copilot], max:1)
Phase 1: create-pull-request
| Test |
Operation |
Expected |
Actual |
Status |
| 1.1 |
Create draft PR (valid prefix) |
✅ Processed |
✅ Processed — PR created (branch: smoke-safeoutputs-test-28446346123) |
✅ |
| 1.2 |
Create PR without prefix |
❌ Rejected |
❌ Rejected — E002: limit reached — 1 of 1 already used (max check fires before prefix check) |
✅ |
| 1.3 |
Create 2nd PR (max exceeded) |
❌ Rejected |
❌ Rejected — E002: limit reached — 1 of 1 already used |
✅ |
Note: Tests 1.2 and 1.3 were both rejected due to max:1 being consumed by 1.1. The max check fires before prefix validation, so the prefix enforcement path was not independently exercised. Both tests still produced the expected rejection outcome.
Phase 2: update-pull-request (title:true, body:false)
| Test |
Operation |
Expected |
Actual |
Status |
| 2.1 |
Update title (allowed) |
✅ Processed |
❌ Rejected — target: triggering requires a pull_request event; workflow runs on schedule |
⚠️ SKIPPED |
| 2.2 |
Update body (body: false) |
❌ Rejected |
❌ Rejected — same target: triggering restriction |
⚠️ SKIPPED |
| 2.3 |
2nd update (max: 1 exceeded) |
❌ Rejected |
❌ Rejected — same target: triggering restriction |
⚠️ SKIPPED |
Note: All Phase 2 tests skipped because update-pull-request is configured with target: triggering, which requires the workflow to be triggered by a pull_request event. In schedule mode there is no triggering PR, so the handler rejects all update attempts regardless of other constraints.
Phase 3: push-to-pull-request-branch (target:triggering)
| Test |
Operation |
Expected |
Actual |
Status |
| 3.1 |
Push to triggering PR (matching prefix) |
✅ Processed |
N/A |
⚠️ SKIPPED — no triggering PR (schedule trigger) |
| 3.2 |
Push to non-triggering PR |
❌ Rejected |
N/A |
⚠️ SKIPPED — no triggering PR (schedule trigger) |
| 3.3 |
Push to PR without matching prefix |
❌ Rejected |
N/A |
⚠️ SKIPPED — no triggering PR (schedule trigger) |
Phase 4: mark-pull-request-as-ready-for-review (required-labels:[smoke-test])
| Test |
Operation |
Expected |
Actual |
Status |
| 4.1 |
Mark PR with smoke-test label as ready |
✅ Processed |
✅ Processed — PR #aw_pr1 (smoke-test label present) marked ready |
✅ |
| 4.2 |
Mark PR without required label as ready |
❌ Rejected |
❌ Rejected — E002: limit reached — 1 of 1 already used (max check fires before label check; tested with PR #8307 lacking smoke-test label) |
✅ |
| 4.3 |
2nd mark-as-ready (max: 1 exceeded) |
❌ Rejected |
❌ Rejected — E002: limit reached — 1 of 1 already used |
✅ |
Note: Test 4.2's rejection was correct (❌) but due to max:1 rather than the missing-label constraint, for the same reason as Phase 1 negative tests.
Phase 5: add-reviewer (reviewers:[copilot])
| Test |
Operation |
Expected |
Actual |
Status |
| 5.1 |
Add reviewer "copilot" (allowed) |
✅ Processed |
✅ Processed — copilot added to PR #aw_pr1 |
✅ |
| 5.2 |
Add non-allowed reviewer |
❌ Rejected |
❌ Rejected — E002: limit reached — 1 of 1 already used (max check fires before allowlist check; attempted to add "octocat") |
✅ |
| 5.3 |
Add 2nd reviewer (max: 1 exceeded) |
❌ Rejected |
❌ Rejected — E002: limit reached — 1 of 1 already used |
✅ |
Note: Test 5.2's rejection was correct (❌) but via max:1 rather than the reviewer allowlist constraint.
Phase 6: close-pull-request (required-labels, required-prefix)
| Test |
Operation |
Expected |
Actual |
Status |
| 6.1 |
Close PR with required label+prefix |
✅ Processed |
✅ Processed — PR #aw_pr1 closed (has smoke-test label and [smoke-safeoutputs] prefix) |
✅ |
| 6.2 |
Close PR without required label |
❌ Rejected |
❌ Rejected — E002: limit reached — 1 of 1 already used (tested with PR #8307, no smoke-test label) |
✅ |
| 6.3 |
Close PR without required prefix |
❌ Rejected |
❌ Rejected — E002: limit reached — 1 of 1 already used (tested with PR #8306, no [smoke-safeoutputs] prefix) |
✅ |
| 6.4 |
2nd close (max: 1 exceeded) |
❌ Rejected |
❌ Rejected — E002: limit reached — 1 of 1 already used |
✅ |
Summary
- Phase 1 (create-pull-request): 3/3 ✅
- Phase 2 (update-pull-request): 0/3 ✅ — SKIPPED (target:triggering blocks all ops in schedule mode)
- Phase 3 (push-to-pr-branch): 0/3 ✅ — SKIPPED (no triggering PR in schedule mode)
- Phase 4 (mark-ready): 3/3 ✅
- Phase 5 (add-reviewer): 3/3 ✅
- Phase 6 (close-pull-request): 4/4 ✅
- Overall: PASS (all non-skipped tests produced expected accept/reject behavior; Phases 2 & 3 skipped due to
target: triggering in schedule mode)
Enforcement Ordering Observation
For tools with max:1, the quota check fires before constraint-specific checks (prefix, label, allowlist). This means negative-constraint tests (wrong prefix, missing label, disallowed reviewer) attempted after a successful call all get rejected for "limit reached" rather than for their specific violation. The enforcement still produces the correct reject outcome but the specific constraint path is not independently exercised when max is 1.
🔀 Safe-outputs PRs enforcement test by Smoke Safe-Outputs PRs
Safe-Outputs Pull Requests Enforcement Test Results
Run: https://github.com/github/gh-aw-mcpg/actions/runs/28446346123
Trigger: schedule
Configuration: create-pull-request (max:1, prefix, draft:true), close-pull-request (required-labels, required-prefix, max:1), update-pull-request (title:true, body:false, max:1, target:triggering), push-to-pr-branch (target:triggering, prefix), mark-ready (required-labels:[smoke-test], max:1), add-reviewer (reviewers:[copilot], max:1)
Phase 1: create-pull-request
E002: limit reached — 1 of 1 already used(max check fires before prefix check)E002: limit reached — 1 of 1 already usedPhase 2: update-pull-request (title:true, body:false)
target: triggeringrequires a pull_request event; workflow runs on scheduletarget: triggeringrestrictiontarget: triggeringrestrictionPhase 3: push-to-pull-request-branch (target:triggering)
Phase 4: mark-pull-request-as-ready-for-review (required-labels:[smoke-test])
E002: limit reached — 1 of 1 already used(max check fires before label check; tested with PR #8307 lacking smoke-test label)E002: limit reached — 1 of 1 already usedPhase 5: add-reviewer (reviewers:[copilot])
E002: limit reached — 1 of 1 already used(max check fires before allowlist check; attempted to add "octocat")E002: limit reached — 1 of 1 already usedPhase 6: close-pull-request (required-labels, required-prefix)
E002: limit reached — 1 of 1 already used(tested with PR #8307, no smoke-test label)E002: limit reached — 1 of 1 already used(tested with PR #8306, no [smoke-safeoutputs] prefix)E002: limit reached — 1 of 1 already usedSummary
target: triggeringin schedule mode)Enforcement Ordering Observation
For tools with max:1, the quota check fires before constraint-specific checks (prefix, label, allowlist). This means negative-constraint tests (wrong prefix, missing label, disallowed reviewer) attempted after a successful call all get rejected for "limit reached" rather than for their specific violation. The enforcement still produces the correct reject outcome but the specific constraint path is not independently exercised when max is 1.