Files
holocron/plugins/kyberforge/docs/research/docs/microsoft-apm/releasing.md
Defame1297 062ca47a18 docs: correct claims left stale by today's apm-only commits
A five-agent review of today's seven commits found no executable
regressions and no dangling references, but a set of documents still
asserting, in present tense, machinery that ADR-0024 and its commits
removed. This corrects them in place, keeping the original text as the
historical record wherever the repo's amendment convention applies.

LESSONS.md: the 2026-06-21 entry prescribed a `claude plugin validate`
sweep that now fails on every plugin, so it is marked superseded with
the surviving gates named. The 2026-08-09 entry gained a recurrence
note: today's manifest deletion broke apm's MCP propagation exactly as
that lesson describes, and its prescribed repo-local grep could not
have caught it, because `plugin_parser.py` ships in the apm toolchain
installed outside this repository.

ADR-0019, ADR-0011 and ADR-0021: amendments extended to passages the
earlier correction passes stepped over -- a dead native-consumer guard,
Consequences bullets still calling for a `plugins/gitea/.mcp.json` that
must not be recreated, and a drift-gate list naming a deleted script.
ADR-0021's list is down to one gate, not two: `apm audit --ci` never
read `description` and was never a drift gate.

architecture.md and enrichments.md: the self-containment constraint is
restated on its live source, the agentskills.io APM package-mode spec,
rather than on Claude Code's plugin cache-install, which ADR-0024
consequence 6 pins as a superseded rationale. releasing.md's pointer to
the deleted sync script is rewritten as history.

tests/run-bats.sh and scripts/lib/batch-run.sh: comment-only. The
`.claude/skills/` exclusion comment claimed a duplication that is not
live yet; apm does not strip `tests/`, and the deployed tree is empty
of them only because the lockfile still resolves the six dependencies
to a pre-ADR-0024 commit carrying the flat mirror. The exclusion is
correct but forward-looking, and now says so.

SIMPLIFICATION-AUDIT.md: reconciled against what the commits actually
did. Two closed findings recorded conclusions that ADR-0024 reversed
hours later; findings 1, 3, 31 and 35 carried prescriptions voided the
same day; finding 28 is now recorded as having moved backwards, with
docs/adr/ measured at +336 lines over the day. The section 1 headline
table is re-measured at a6434e0 and labelled with its basis. The
ADR-0012 contradiction between finding 2b and section 8 is resolved in
2b's favour after reading the ADR: only finding 24 is governed by it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
2026-09-14 19:50:23 +00:00

9.1 KiB

topic, source_keys
topic source_keys
releasing
context7-microsoft-apm
apm-github-repo

apm pack full reference

apm pack                                      # plugin format (default), directory under ./build/
apm pack --archive                            # plugin bundle as .zip (default archive format)
apm pack --archive --archive-format tar.gz    # legacy CI pipelines that expect .tar.gz
apm pack --format apm -o ./dist               # legacy APM bundle layout, custom output path
apm pack --dry-run -v                         # resolve and print per-entry detail; write nothing
apm pack --offline                            # marketplace resolution: cached refs only
apm pack --include-prerelease                 # marketplace resolution: allow pre-release tags
apm pack --marketplace=claude --json          # JSON output for CI pipelines, one format only
apm pack --marketplace-path claude=dist/marketplace.json   # override one format's output path
apm pack --legacy-skill-paths                 # bundle skills under per-client paths, not shared .agents/skills/

apm pack reads both apm.yml and apm.lock.yaml. What it produces depends on which blocks apm.yml declares: a dependencies: block alone produces a bundle; a marketplace: block alone produces marketplace artifacts (.claude-plugin/marketplace.json etc.); both present produces both. A package with neither block (skills/agents authored directly, no dependency resolution, not marketplace-listed) has nothing for apm pack to do beyond the plugin.json/bundle synthesis.

--format plugin bundle structure

build/<name>-<version>/
├── plugin.json          # synthesized, schema-conformant per json.schemastore.org/claude-code-plugin.json
├── apm.lock.yaml         # enriched copy embedding a per-file bundle_files manifest (integrity check for `apm install <bundle>`)
├── agents/
├── skills/
├── commands/
└── hooks.json            # merged from .apm/hooks/*.json, single file (not a hooks/ directory)

This is the default (--format plugin, no flags) and the one most CI/release workflows use. --format apm produces the older, distinct "legacy APM bundle layout" instead — different consumers, not a superset/subset of each other.

Exit codes

Code Meaning
0 Success
1 Build or runtime error (e.g. network failure, missing reference)
2 Manifest schema validation error
3 --check-versions: per-package versions don't align with the configured marketplace.versioning.strategy
4 --check-clean: working tree is dirty — on-disk marketplace output doesn't match what a fresh pack would produce

These four are deliberately structured as release gates that block, not auto-fix — none of them rewrite files to make the check pass; a nonzero exit means stop the release and investigate, not "run again to self-heal."

--check-clean and --check-versions scope

Both are scoped to marketplace outputs specifically (whatever marketplace.claude/marketplace.codex in apm.yml configures — i.e. .claude-plugin/marketplace.json and equivalents), not to the bundle's plugin content:

  • --check-clean regenerates every configured marketplace output to a temp representation (including any --marketplace-path override) and diffs it against what's actually on disk at the effective path. Any difference is exit 4.
  • --check-versions verifies that every marketplace-listed package's version agrees with the configured marketplace.versioning.strategy (lockstep | tag_pattern | per_package — see monorepo-and-repo-shapes.md).

Neither one inspects a plugin's agents//skills//hooks.json directories at all — there is no apm-native "does the packed bundle match what's committed at the plugin root" check. That gap has to be closed by project-specific tooling if a repo commits compiled bundle content back into its plugin directories. This repo used to do exactly that — scripts/sync-plugin-content.sh --check diffed a committed flat skills//agents//hooks/ mirror at each plugin root against .apm/ — and no longer does: docs/adr/0024-apm-is-the-only-supported-install-path.md supersedes ADR-0017, deleting the mirror, the script and its pre-push gate, on the grounds that the native claude plugin install path they served had no users. The apm-side gap itself is unchanged and is still not an apm oversight to wait out; this repo simply stopped committing compiled bundle content, which is the other way to not have the problem.

The canonical release sequence

Documented as: apm pack (with release gates) → checksum the artifacts → create the GitHub release. A manual version, for repos that need custom packaging steps beyond what apm-action covers:

- run: pip install apm-cli
- run: |
    apm pack --check-versions --check-clean --json > pack-report.json
    for f in build/*.zip .claude-plugin/marketplace.json; do
      [ -f "$f" ] || continue
      sha256sum "$f" > "${f}.sha256"
    done
    gh release create "${GITHUB_REF_NAME}" \
      build/*.zip build/*.zip.sha256 \
      .claude-plugin/marketplace.json* \
      --title "${GITHUB_REF_NAME}" --notes-file CHANGELOG.md
  env:
    GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

--json on the combined pack --check-versions --check-clean invocation gives one machine-readable summary of both gates for the release job to inspect/log, rather than parsing prose output.

The equivalent using the official wrapper, for repos that don't need custom packaging:

on:
  push:
    tags: ["v*"]
jobs:
  release:
    permissions: { contents: write }
    steps:
      - uses: actions/checkout@v5
      - uses: microsoft/apm-action@v1
        with: { mode: release }
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

mode: release is a third distinct mode on the same action used for install/audit (see testing-and-validation.md) — it runs pack + the release gates + GitHub release creation internally, so a repo only needs this when it doesn't need the manual workflow's custom steps (checksums, custom release notes handling, non-GitHub-release publishing target).

apm publish — registry publish is a different artifact than apm pack's bundle

apm publish --package acme/my-skill --dry-run -v      # always dry-run first -- not trivially reversible
apm publish --package acme/my-skill
apm publish --package acme/my-skill --registry corp-main   # pick a registry when multiple are configured
apm publish --package acme/my-skill --zip ./build/my-package-1.0.0.zip   # publish a pre-built zip, skip auto-pack

By default apm publish auto-packs a flat registry archive — apm.yml, the .apm/ directory, and standard documentation files — and uploads that. This is explicitly a different layout than the plugin bundle apm pack --format plugin produces (no synthesized plugin.json, no agents//skills/ convention-directory flattening, symlinks excluded). Registry consumers install via apm install <package>, which resolves and deploys from this registry archive shape, not the plugin bundle shape — the two pack paths exist for two different consumers (a plugin host vs. an apm-aware installer) and aren't interchangeable.

Constraints: apm.yml must have name and version set. Publishing the same version twice returns a 409 Conflict — versions are immutable once published; fix by bumping the version, not by re-publishing over it.

Registries

A project declares registries in its manifest:

registries:
  internal:
    url: https://artifactory.example.com/artifactory/api/skills/internal
    aliases:
      - mirror.example.com
  default: internal

Registries are the "package-level hosting at scale" tier (see marketplace-and-registries.md's "Which mechanism to use" section) — heavier infrastructure than a git-based marketplace, worth it once package count or access-control needs outgrow git-based discovery.

Gotchas

  • apm pack's bundle (--format plugin, written under ./build/ by default) is a distribution artifact for apm install <bundle> consumers — it is not read by Claude Code's own installer, which clones/scans a plugin's git working directory directly via marketplace.json's source: path (see compile.md's note on this same point). Nothing in apm pack, --check-clean, or apm audit --ci reconciles the bundle against a plugin's committed working-tree content — that reconciliation, if a repo needs Claude Code to discover .apm/-authored content without waiting on the bundle, is necessarily project-specific tooling, not something to look for as a missing/misused apm flag.
  • --check-clean's "regenerate and diff" model only applies to marketplace-format outputs. A repo relying on committed plugin-bundle content (flat agents//skills//hooks.json at a plugin root, generated from .apm/) needs its own drift check for that content — --check-clean will not catch it going stale.
  • apm publish and apm pack are easy to conflate since both start from the same apm.yml/.apm/ source, but they produce structurally different artifacts for different consumers (registry install vs. plugin host install) — verifying one does not verify the other.