feat(apm): add reusable APM sync workflow + onboarding - #194
Conversation
Add a reusable workflow that refreshes installed APM primitives in the calling repo (`apm update`) and opens a PR with the result via the central open-pr composite — the agentic-primitive counterpart to config-sync. - apm-version is a required input (caller owns the version, per ADR 0001). - Optional app-id/app-private-key open the PR via a GitHub App so required CI fires, mirroring the release-please reusable; falls back to GITHUB_TOKEN otherwise. - No-op when the calling repo has no apm.yml.
Document how repos adopt the apm-sync reusable workflow: prerequisites (one-time apm install, App auth), a caller template, inputs, run behavior, and troubleshooting. Mirrors the release-please onboarding structure.
Uh oh!
There was an error while loading. Please reload this page.
zizmor flags the checkout step for not setting persist-credentials: false, but the open-pr step force-pushes the sync branch using the token checkout writes into the remote URL. Disabling it would break PR creation without adding isolation (the job runs no untrusted code). Mirror config-sync.yml's `# zizmor: ignore[artipacked]` suppression with a justification.
DevSecNinja
commented
Jun 21, 2026
Addressed the zizmor This is a justified suppression rather than a real vulnerability: the Mirrored the exact pattern already used in |
Apply yamlfmt (body: |- block scalar) and dprint (markdown table column alignment) auto-formatting to the apm-sync workflow and onboarding doc so the central lint reusable passes.
Dogfooding `apm install` into this repo committed apm.yml, apm.lock.yaml and APM-deployed prompt/instruction markdown under the harness dirs. Those are tool-generated/vendored content — yamllint/yamlfmt reject APM's 2-space sequence indentation, and dprint rejects the verbatim prompt bodies (`•` bullets, `3)` lists). Reformatting them is pointless (clobbered on the next `apm install`) and for prompt bodies actively harmful. Exclude APM-managed paths from the central synced linter configs, the same way CHANGELOG.md is already excluded from dprint: - yamllint `ignore` + yamlfmt `exclude`: apm.yml, apm.lock.yaml, apm_modules/ - dprint `excludes`: apm_modules/ and the APM harness output dirs (.github/prompts, .github/instructions, .github/chatmodes, .claude, .cursor, .opencode, .gemini, .windsurf, .kiro, .agents) This unblocks any repo that adopts APM, not just this one.
DevSecNinja
commented
Jun 21, 2026
Lint is green now. Root cause was bigger than my new files: What actually failed: this repo already dogfoods
So this was a pre-existing Fix (folded into this PR): exclude APM-managed/generated paths from the central synced linter configs — the same treatment
Since these are the central config-sync sources, the exclusions propagate to every repo that adopts APM — so this is the general fix, not a one-off. I verified all three linters pass CI-style ( Tradeoff to flag: excluding Also fixed in this PR: the zizmor |
What
Adds a central
apm-syncreusable workflow so any DevSecNinja repo can keep its installed APM primitives (prompts, instructions, agents, hooks) up to date with their upstream packages — the agentic-primitive counterpart toconfig-sync(which only distributes static files)..github/workflows/apm-sync.yml— reusable (workflow_call).docs/apm-sync-onboarding.md— adoption guide (mirrorsrelease-please-onboarding.md), linked from README.How it works
On each run (scheduled or
workflow_dispatchin the caller):apmCLI (curl -sSL https://aka.ms/apm-unix | sh,VERSIONpinned).apm update --yes— re-resolves every dep inapm.ymlto its latest matching ref, rewritesapm.lock.yaml, and redeploys compiled primitives into the harness dirs (.github/,.claude/, …).chore: sync APM primitivesPR via the existingactions/open-prcomposite. Branch protection respected — nothing auto-merges.apm.yml.Conventions followed
apm-versionisrequired: true— caller owns the version (ADR 0001).# renovate:comments on both the reusable ref and theapmversion.app-id+app-private-keyopen the PR via the GitHub App so required CI fires on it — same pattern/secrets as the release-please reusable (RELEASE_PLEASE_APP_ID/RELEASE_PLEASE_APP_PRIVATE_KEY); falls back toGITHUB_TOKEN.harden-runner(audit egress), pinned action SHAs, least-privilegepermissions.The bigger picture
This pairs with ai-toolkit#21, which ships the org conventions as an APM instruction primitive. Once both merge, a repo runs
apm install DevSecNinja/ai-toolkit --target copilotonce, and this workflow keeps it current automatically — replacing the closed config-sync approach (#193) for agentic content while config-sync keeps owning static config.Validation note
I authored this via API and can't run actionlint/CI from here. The workflow follows the repo's existing reusable conventions; please let the lint workflow run on the PR. One thing worth a reviewer's eye: the exact
apm updateredeploy semantics in a headless CI checkout (e.g. whether a repo with zero harness dirs needstargets:inapm.yml) — the onboarding doc tells consumers to pin--targetat install time, which covers the common case.