Skip to content

feat(workflows): automate apm-review-panel via github/gh-aw#824

Merged
danielmeppiel merged 5 commits intomainfrom
feat/pr-review-panel-workflow
Apr 21, 2026
Merged

feat(workflows): automate apm-review-panel via github/gh-aw#824
danielmeppiel merged 5 commits intomainfrom
feat/pr-review-panel-workflow

Conversation

@danielmeppiel
Copy link
Copy Markdown
Collaborator

Summary

Adds a github/gh-aw workflow that runs the apm-review-panel skill
against any PR labelled panel-review and posts a single
synthesized verdict comment. Companion to #823 which dogfooded the
panel skill + persona agents into APM-managed primitives.

How it works

  • Trigger: pull_request with types: [labeled, synchronize] and
    names: [panel-review]. A maintainer applies the label; the
    workflow runs.
  • Dependency: microsoft/apm#4f345ea -- the apm-action pre-job
    packs the skill + 7 personas + supporting agents and unpacks
    them into the runner workspace at .github/skills/ and
    .github/agents/.
  • Agent: read-only permissions, runs the apm-review-panel routing
    (specialists -> CEO arbitration -> single comment).
  • Output: safe-outputs.add-comment posts the verdict to the
    triggering PR via gh-aw's permission-isolated downstream job.

Why not pull_request_target

gh-aw hard-blocks pull_request_target on public repositories at
activation time (validate_lockdown_requirements.cjs). gh-aw's
safe-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.md form with packages: -- compiles to the
same apm job. shared/apm.md is fetched from github/gh-aw
v0.68.3 and committed at .github/workflows/shared/apm.md per the
gh-aw relative-import resolution rule.

Limitations (v1)

  • Same-repo PRs only (default fork policy). Fork PR support needs
    forks: + a PAT-backed safe-outputs token; out of scope for v1.
  • Comment size capped at GitHub's 65,536 chars; prompt asks for
    <600 lines.

Test plan

After merge, apply panel-review to a draft PR and confirm:

  1. APM bundle unpacks under .github/skills/apm-review-panel/.
  2. All 7 personas execute.
  3. One verdict comment lands.

First smoke test will reveal the exact bundle path; iterate if needed.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

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>
Copilot AI review requested due to automatic review settings April 21, 2026 22:24
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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-panel gh-aw workflow (source + compiled lock) gated by PR labeling and safe-outputs comment publishing.
  • Add a shared apm.md import 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')
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
(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'))))

Copilot uses AI. Check for mistakes.
Comment on lines +6 to +12
# 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]
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

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

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).

Suggested change
# 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]

Copilot uses AI. Check for mistakes.
Comment on lines +386 to +399
- 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();
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
- 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.

Copilot uses AI. Check for mistakes.
Comment on lines +49 to +53
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 }}
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +73 to +79
- 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 }}
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

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

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).

Copilot uses AI. Check for mistakes.
@danielmeppiel danielmeppiel added the panel-review Trigger the apm-review-panel gh-aw workflow label Apr 21, 2026
@github-actions
Copy link
Copy Markdown

APM Review Panel Verdict

Disposition: APPROVE


Per-persona findings

Python Architect: No Python source changes in this PR. The new files are all workflow configuration
(.github/workflows/, .github/aw/). Structure is correct: source .md and compiled .lock.yml
co-located, shared/apm.md lives in the shared workflow directory. The imports: pattern is the
established gh-aw convention. No architectural concerns.

CLI Logging Expert: No changes to CommandLogger, _rich_* helpers, DiagnosticCollector, or
any APM CLI output path. The workflow prompt text in pr-review-panel.md uses only ASCII and follows
the STATUS_SYMBOLS conventions in its instructions to the agent. Not applicable beyond this check.

DevX UX Expert: The workflow is maintainer-facing (not end-user), so npm/pip parity is not the
lens. That said, the design is ergonomically solid:

  • Label gate (panel-review) is the correct cost-control UX pattern -- familiar from other
    GH Actions workflows.
  • synchronize re-run behavior is documented inline in the source comment.
  • timeout-minutes: 30 is reasonable for a multi-agent review.
  • One clarification flag (see below): the source comment asserts "synchronize re-runs on new pushes
    once the PR carries the label" -- the compiled YAML's activation condition only checks label name
    for labeled events, not for synchronize events. Whether pre_activation's check_membership.cjs
    enforces current label presence for synchronize is not visible from the compiled YAML alone.
    Not blocking (internal tooling, write-access contributors only), but worth a follow-up verification.

Supply Chain Security Expert: Strong posture throughout.

  • Trigger is pull_request (NOT pull_request_target) -- correct for public repo safety.
  • Fork protection enforced: github.event.pull_request.head.repo.id == github.repository_id in both
    pre_activation and activation conditions.
  • Top-level permissions: {} (explicit deny-all). Agent job scoped to pull-requests: read.
    safe_outputs job granted write only at the scoped job level (pull-requests: write).
  • All action SHAs pinned in both pr-review-panel.lock.yml (manifest header) and
    .github/aw/actions-lock.json. Every action reference is commit-SHA pinned with version comment.
  • APM package pinned at a specific commit SHA:
    microsoft/apm#4f345ead68e186db861340a50d26242be57a8f5d. Deterministic install.
  • COPILOT_GITHUB_TOKEN explicitly excluded from agent sandbox env (--exclude-env COPILOT_GITHUB_TOKEN)
    -- token cannot leak into agent execution context.
  • safe-outputs.add-comment: max: 1 caps write blast radius.
  • GH_AW_PLUGINS_TOKEN || GH_AW_GITHUB_TOKEN || GITHUB_TOKEN cascade for APM package fetch follows
    established repo auth patterns.
  • Pre-activation gate is write-access membership check (admin,maintainer,write) -- untrusted
    contributors cannot activate the COPILOT_GITHUB_TOKEN spend path.
  • No findings.

Auth Expert: Token handling is clean.

  • APM action fetch uses GH_AW_PLUGINS_TOKEN || GH_AW_GITHUB_TOKEN || GITHUB_TOKEN -- standard
    three-tier cascade matching APM's auth fallback philosophy.
  • MCP server token: GH_AW_GITHUB_MCP_SERVER_TOKEN || GH_AW_GITHUB_TOKEN || GITHUB_TOKEN -- correct
    cascade, no hardcoded fallback to a privileged token.
  • COPILOT_GITHUB_TOKEN is scoped to the agent runner environment only; the compiled YAML explicitly
    excludes it from the sandbox (--exclude-env COPILOT_GITHUB_TOKEN), preventing token bleeding into
    bash tools or MCP tool output.
  • Git remote auth uses github.token (the ephemeral installation token) -- minimal scope, auto-expiring.
  • No direct os.getenv("...TOKEN...") patterns (this is workflow YAML, not Python, but the
    equivalent env: anti-pattern is absent). All secret references use ${{ secrets.NAME }}.
  • No findings.

OSS Growth Hacker (side-channel annotation to CEO):
This PR is a high-leverage dogfooding moment: APM reviews its own PRs using APM-packaged skills.
The one-line CHANGELOG entry undersells it. Story hook: "We use apm install microsoft/apm inside a
GitHub Actions workflow to run a seven-persona review panel on every labeled PR -- the same workflow
is now live in this repo." This belongs in the next release post and could anchor the "APM in CI"
narrative. Recommend a follow-up to expand the CHANGELOG blurb or add a docs page on
"Using APM workflows for automated review."


CEO arbitration

No specialist disagreements to arbitrate. The PR is clean and well-scoped: one new gh-aw workflow,
one shared workflow import, one compiled lockfile, one actions-lock update, and a correctly-formatted
CHANGELOG entry. The supply chain posture is exemplary -- SHA-pinned actions, fork-protected trigger,
scoped permissions, token exclusion from sandbox. The one open question (whether synchronize events
correctly check current label presence in pre_activation) is an internal tooling concern confined
to write-access contributors; it does not affect end users or the security boundary.

On the Growth Hacker's annotation: agreed this is a strong story. The CHANGELOG entry is complete and
correct per repo conventions -- an expanded narrative belongs in a follow-up release post or docs
page, not in this PR's CHANGELOG line. The APM pinned SHA (4f345ead) will need updating as the
panel skill evolves; recommend tracking that as a maintenance practice (update the pin when the skill
changes materially).

Approve and merge.


Required actions before merge

None. The PR is ready to merge as-is.


Optional follow-ups

  • Verify (or document explicitly) that pre_activation's check_membership.cjs checks current PR
    label presence on synchronize events when names: [panel-review] is declared in the source.
    If it does not, add contains(github.event.pull_request.labels.*.name, 'panel-review') to the
    activation job condition as an explicit guard.
  • Establish a maintenance convention for updating the pinned SHA in pr-review-panel.md when the
    apm-review-panel skill or panel agent definitions change materially.
  • Consider a short docs page or README callout on "APM-powered PR review" to capitalize on the
    dogfooding story angle (Growth Hacker recommendation).

Generated by PR Review Panel for issue #824 · ● 1.2M ·

danielmeppiel and others added 3 commits April 22, 2026 01:03
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>
@github-actions
Copy link
Copy Markdown

DevX UX Expert

This PR has zero impact on CLI command surfaces, flags, or help text — it is pure CI/CD infra and does not alter any apm command a contributor or end-user would invoke. The label-based activation pattern (panel-review) is a GitHub-native interaction and is reasonably discoverable for maintainers who know to look at the repo label list; however, there is no in-repo documentation (e.g., a CONTRIBUTING.md note or label description) telling new maintainers when to apply the label. That small discoverability gap aside, the contributor UX is strictly additive: external PRs from forks are explicitly documented as a v2 limitation, so first-time contributors will not see a confusing or broken panel comment on their own PRs. The safe-outputs.add-comment constraint (max 1) prevents comment spam. No UX regressions identified.

Recommendation: Add a one-liner to CONTRIBUTING.md explaining the panel-review label and who is authorized to apply it, so the activation path is discoverable without reading the workflow YAML.


OSS Growth Hacker

The 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 (panel-review) is actually a growth positive for community contributors: they will see the panel verdict comment on their PRs (once the maintainer labels it), giving them richer, faster feedback than a typical human review cycle — that is a meaningful contributor experience differentiator. The fork limitation (same-repo PRs only, v2 for forks) should be flagged in the PR description as a known gap so early adopters who try to replicate the pattern in their own repos are not confused.

Growth strategy angle (for WIP/growth-strategy.md): Add a "dogfooding" section documenting this as a repeatable pattern — "Install APM, define a review panel skill, wire it to gh-aw, get automated multi-persona PR review." This is a high-conversion demo path: it turns APM from a dependency manager into a visible workflow accelerator, which is exactly the story needed to move the project from 250 stars to 1K+.

Generated by PR Review Panel for issue #824 · ● 1.1M ·

@danielmeppiel danielmeppiel merged commit c27e6ce into main Apr 21, 2026
16 checks passed
@danielmeppiel danielmeppiel deleted the feat/pr-review-panel-workflow branch April 21, 2026 23:31
danielmeppiel added a commit that referenced this pull request Apr 22, 2026
- 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>
danielmeppiel added a commit that referenced this pull request Apr 22, 2026
- 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

panel-review Trigger the apm-review-panel gh-aw workflow

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants