feat(workflows): automate apm-review-panel via github/gh-aw#824
feat(workflows): automate apm-review-panel via github/gh-aw#824danielmeppiel merged 5 commits intomainfrom
Conversation
Automates the apm-review-panel skill against PRs labelled `panel-review`. Pulls panel skill + 7 persona agents from 4f345ea (PR #823 dogfood) via gh-aw dependencies:. Posts one synthesized verdict comment via safe-outputs.add-comment. Trigger uses pull_request (not pull_request_target -- gh-aw blocks the latter on public repos). Default fork policy restricts to same-repo PRs; cost-gated by the `panel-review` label. Compiled with gh aw v0.68 in strict mode. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds a new gh-aw workflow to run the apm-review-panel skill when a PR is marked for panel review, using an APM pre-job to bundle skills/agents and safe-outputs to publish a single synthesized verdict comment.
Changes:
- Introduce
pr-review-panelgh-aw workflow (source + compiled lock) gated by PR labeling and safe-outputs comment publishing. - Add a shared
apm.mdimport workflow to pack/restore APM packages as an artifact. - Update actions lock entries and changelog to reflect the new workflow.
Show a summary per file
| File | Description |
|---|---|
| CHANGELOG.md | Documents the new panel review workflow addition. |
| .github/workflows/shared/apm.md | Adds shared APM pack/restore jobs/steps used by the workflow import. |
| .github/workflows/pr-review-panel.md | Defines the gh-aw workflow and agent prompt for the panel review. |
| .github/workflows/pr-review-panel.lock.yml | Compiled workflow implementing activation/agent/detection/safe-outputs and APM pre-job wiring. |
| .github/aw/actions-lock.json | Adds pinned action entries needed by the compiled workflow. |
Copilot's findings
- Files reviewed: 5/5 changed files
- Comments generated: 5
| pre_activation: | ||
| if: > | ||
| (github.event_name != 'pull_request' || github.event.pull_request.head.repo.id == github.repository_id) && | ||
| (github.event_name != 'pull_request' || github.event.action != 'labeled' || github.event.label.name == 'panel-review') |
There was a problem hiding this comment.
The label gate is only enforced for the labeled action. On synchronize, this condition evaluates true for all same-repo PRs, so the workflow will run on every push even if the PR does not currently have the panel-review label (contradicting the PR description/cost gate). Add a label-present check for non-labeled events (e.g., check github.event.pull_request.labels contains panel-review) and apply the same fix to the activation job if: guard.
| (github.event_name != 'pull_request' || github.event.action != 'labeled' || github.event.label.name == 'panel-review') | |
| (github.event_name != 'pull_request' || ((github.event.action == 'labeled' && github.event.label.name == 'panel-review') || (github.event.action != 'labeled' && contains(github.event.pull_request.labels.*.name, 'panel-review')))) |
| # public repos). Cost-gate: only runs when a maintainer applies `panel-review`. | ||
| # `synchronize` re-runs on new pushes once the PR carries the label. | ||
| # Default fork policy (no `forks:` field) restricts to same-repo PRs only. | ||
| on: | ||
| pull_request: | ||
| types: [labeled, synchronize] | ||
| names: [panel-review] |
There was a problem hiding this comment.
pull_request events in GitHub Actions do not support names for label filtering, so this field is misleading (and the compiled workflow indicates label filtering is handled via job conditions). Remove names: and rely on an explicit job-level if: condition that checks the label set (especially important for synchronize).
| # public repos). Cost-gate: only runs when a maintainer applies `panel-review`. | |
| # `synchronize` re-runs on new pushes once the PR carries the label. | |
| # Default fork policy (no `forks:` field) restricts to same-repo PRs only. | |
| on: | |
| pull_request: | |
| types: [labeled, synchronize] | |
| names: [panel-review] | |
| # public repos). Cost-gate is enforced by workflow/job conditions that check | |
| # for the `panel-review` label. `synchronize` re-runs on new pushes once the | |
| # PR carries the label. Default fork policy (no `forks:` field) restricts to | |
| # same-repo PRs only. | |
| on: | |
| pull_request: | |
| types: [labeled, synchronize] |
| - name: Checkout PR branch | ||
| id: checkout-pr | ||
| if: | | ||
| github.event.pull_request || github.event.issue.pull_request | ||
| uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | ||
| with: | ||
| github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | ||
| script: | | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs'); | ||
| await main(); |
There was a problem hiding this comment.
The agent prompt explicitly says to never git checkout the PR head, but the compiled workflow performs a PR branch checkout. This weakens the intended trust boundary (untrusted code becomes present in the workspace and may be accidentally executed/read by tools). Either: (1) disable PR head checkout in the gh-aw workflow configuration (preferred if supported) and use gh pr diff/view only, or (2) update the prompt to reflect the actual behavior and add stricter guidance to avoid executing any checked-out PR code.
| - name: Checkout PR branch | |
| id: checkout-pr | |
| if: | | |
| github.event.pull_request || github.event.issue.pull_request | |
| uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 | |
| env: | |
| GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| with: | |
| github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs'); | |
| await main(); | |
| # Intentionally do not check out the PR head into the workspace. | |
| # Review logic must use PR metadata/diff APIs rather than local untrusted code. |
| uses: microsoft/apm-action@v1.4.1 | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GH_AW_PLUGINS_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | ||
| with: | ||
| dependencies: ${{ steps.apm_prep.outputs.deps }} |
There was a problem hiding this comment.
The fallback secrets.GITHUB_TOKEN is typically not defined (the default token is github.token). If the repo doesn't set GH_AW_PLUGINS_TOKEN/GH_AW_GITHUB_TOKEN, microsoft/apm-action may receive an empty GITHUB_TOKEN and fail to fetch packages. Update the fallback chain to use ${{ github.token }} as the final default.
| - name: Find APM bundle path | ||
| id: apm_bundle | ||
| run: echo "path=$(find /tmp/gh-aw/apm-bundle -name '*.tar.gz' | head -1)" >> "$GITHUB_OUTPUT" | ||
| - name: Restore APM packages | ||
| uses: microsoft/apm-action@v1.4.1 | ||
| with: | ||
| bundle: ${{ steps.apm_bundle.outputs.path }} |
There was a problem hiding this comment.
If the artifact is missing or the find returns no matches, steps.apm_bundle.outputs.path becomes empty and the restore step will fail with a less actionable error. Make the 'Find APM bundle path' step fail fast with a clear message when no bundle is found (and optionally print the directory listing to aid debugging).
APM Review Panel VerdictDisposition: APPROVE Per-persona findingsPython Architect: No Python source changes in this PR. The new files are all workflow configuration CLI Logging Expert: No changes to DevX UX Expert: The workflow is maintainer-facing (not end-user), so npm/pip parity is not the
Supply Chain Security Expert: Strong posture throughout.
Auth Expert: Token handling is clean.
OSS Growth Hacker (side-channel annotation to CEO): CEO arbitrationNo specialist disagreements to arbitrate. The PR is clean and well-scoped: one new gh-aw workflow, On the Growth Hacker's annotation: agreed this is a strong story. The CHANGELOG entry is complete and Approve and merge. Required actions before mergeNone. The PR is ready to merge as-is. Optional follow-ups
|
Self-referential dogfooding: the workflow lives in the same repo as
the APM package it depends on, so pin to the PR's own head SHA
instead of a frozen merge SHA. Net effects:
- Zero maintenance: no manual SHA bump after PRs that touch .apm/.
- Self-consistent review: a PR that modifies the panel skill or
persona agents is reviewed BY those modified primitives.
Mirrors the standard actions/checkout@${{ github.sha }} self-reference
pattern. gh-aw v0.68 preserves the expression through to the lock
file's AW_APM_PACKAGES env var.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reverts the ${{ github.sha }} self-reference. A PR that modifies the
panel skill or persona agents could otherwise trick its own review
into APPROVE by weakening the very prompts that judge it.
Pinning to main means the review always runs against the trusted,
already-merged panel -- changes to .apm/ only take effect AFTER they
have themselves been reviewed and merged. Same rationale as GitHub's
own guidance to pin workflow `uses:` to a ref, never to the PR head.
Auto-updates on every merge to main, zero maintenance, no
trust-on-first-use hole.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
APM 0.9.0 dependency parser accepts `owner/repo#ref` (hash) but rejects `owner/repo@ref` (at-sign) with 'Invalid repository path component'. Switching the spec from `@main` to `#main`. Same trust property: review runs against the merged-and-trusted panel on main, never against PR-modified primitives. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
DevX UX ExpertThis PR has zero impact on CLI command surfaces, flags, or help text — it is pure CI/CD infra and does not alter any Recommendation: Add a one-liner to OSS Growth HackerThe dogfooding narrative here is genuinely compelling: APM is a tool that manages AI agents and skills, and it is now using itself — via a gh-aw workflow — to automatically review its own PRs with a seven-persona expert panel. That is a tight, Tweet-sized story: "We built an AI review panel for OSS projects. Then we wired it up to review its own PRs." This belongs in the next release notes and deserves a callout in the README's "Why APM?" section as a concrete proof-of-concept. The maintainer-only label gate ( Growth strategy angle (for
|
- Bump version to 0.9.1 in pyproject.toml and uv.lock - Move CHANGELOG [Unreleased] entries into [0.9.1] - 2026-04-22 - Audit pass: 1 PR = 1 entry, no bloat - Consolidate the seven scattered entries from #832 into a single Added line that names the closed issues (#827/#829/#831/#834) and keeps the migration warning inline - Combine the three fork-PR fixes (#826, #836, #837) into one Fixed entry per the multi-PR convention - Drop the doc-only consolidation commits from the log Highlights of 0.9.1: - Install-time enforcement of org apm-policy.yml (#832) - pr-review-panel automation now usable from fork PRs (#824, #826, #836, #837) - Docs site publish gated on stable releases only (#822) - Repository dogfooding via .apm/ as primitive source of truth (#823) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Bump version to 0.9.1 in pyproject.toml and uv.lock - Move CHANGELOG [Unreleased] entries into [0.9.1] - 2026-04-22 - Audit pass: 1 PR = 1 entry, no bloat - Consolidate the seven scattered entries from #832 into a single Added line that names the closed issues (#827/#829/#831/#834) and keeps the migration warning inline - Combine the three fork-PR fixes (#826, #836, #837) into one Fixed entry per the multi-PR convention - Drop the doc-only consolidation commits from the log Highlights of 0.9.1: - Install-time enforcement of org apm-policy.yml (#832) - pr-review-panel automation now usable from fork PRs (#824, #826, #836, #837) - Docs site publish gated on stable releases only (#822) - Repository dogfooding via .apm/ as primitive source of truth (#823) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
Adds a github/gh-aw workflow that runs the
apm-review-panelskillagainst any PR labelled
panel-reviewand posts a singlesynthesized verdict comment. Companion to #823 which dogfooded the
panel skill + persona agents into APM-managed primitives.
How it works
pull_requestwithtypes: [labeled, synchronize]andnames: [panel-review]. A maintainer applies the label; theworkflow runs.
microsoft/apm#4f345ea-- the apm-action pre-jobpacks the skill + 7 personas + supporting agents and unpacks
them into the runner workspace at
.github/skills/and.github/agents/.(specialists -> CEO arbitration -> single comment).
safe-outputs.add-commentposts the verdict to thetriggering PR via gh-aw's permission-isolated downstream job.
Why not pull_request_target
gh-aw hard-blocks
pull_request_targeton public repositories atactivation time (
validate_lockdown_requirements.cjs). gh-aw'ssafe-outputs isolation replaces that trust boundary with a
stronger one: read-only agent + write-scoped detached publisher.
v0.68 frontmatter patch
The original draft used the older bare-list
dependencies:field.gh-aw v0.68 deprecated that shape ("The 'dependencies' field is
deprecated and no longer supported. Migrate to 'imports: - uses:
shared/apm.md'"). The committed workflow uses the documented
imports: shared/apm.mdform withpackages:-- compiles to thesame
apmjob.shared/apm.mdis fetched fromgithub/gh-awv0.68.3 and committed at
.github/workflows/shared/apm.mdper thegh-aw relative-import resolution rule.
Limitations (v1)
forks:+ a PAT-backed safe-outputs token; out of scope for v1.<600 lines.
Test plan
After merge, apply
panel-reviewto a draft PR and confirm:.github/skills/apm-review-panel/.First smoke test will reveal the exact bundle path; iterate if needed.
Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com