Publishing APM Primitives and Packages

Publishing means: getting a primitive or package into a tagged release of calab-ai/apm-registry so it shows up in the next portal-data.json snapshot.

CLI status (May 2026): gh calab apm publish primitive and gh calab apm publish package are not yet shipped. No PR matching feat/apm-publish-commands is open in calab-ai/gh-calab at the time of writing. The current production flow is the manual PR + semantic-release path documented below. This page will be updated to point at the CLI commands as soon as they merge.


The release pipeline at a glance

flowchart LR
    A[Author] --> B[PR to apm-registry]
    B --> C[CI: schema + manifest validation]
    C --> D[PR review + merge]
    D --> E[semantic-release tags<br/>primitives/ &#124; packages/]
    E --> F[Catalog regenerate<br/>portal-data.json]
    F --> G[GitHub Release attaches<br/>portal-data.json artifact]
    G --> H[Marketplace + handbook<br/>pick up next sync]

There is no publishing endpoint outside the registry repo. Every release is a Git tag created by semantic-release running in CI on main.


Manual flow (today)

1. Branch from main

git clone https://github.com/calab-ai/apm-registry
cd apm-registry
git checkout -b feat/<your-primitive-or-package>

2. Add the content

  • For a primitive: drop a new directory under primitives/<type>/<name>/ with apm-primitive.json and the entrypoint file. See Authoring primitives.
  • For a package: drop a new directory under packages/<name>/ with apm-package.json. See Authoring packages.

3. Validate locally

pip install 'jsonschema==4.*'
python scripts/validate_primitive_manifests.py
# (and the equivalent for packages, once added)

CI runs the same gates on every PR via .github/workflows/validate-primitive-manifests.yml.

4. Conventional Commit message

The release pipeline derives the new version from your commit message. Use Conventional Commits:

Commit prefixBumpUse it when…
feat(<scope>): …minorAdding a new primitive, adding a primitive to a package, additive metadata.
fix(<scope>): …patchFixing a typo, correcting frontmatter, non-behavioral cleanup.
feat(<scope>)!: … or BREAKING CHANGE: footermajorRemoving a primitive, replacing one, or any consumer-breaking change.
chore, docs, cinoneTooling and docs that don’t require a release.

The <scope> should be the primitive or package name (e.g. feat(workspace-base): add summarise skill).

5. Open the PR

gh pr create --repo calab-ai/apm-registry \
  --base main \
  --title "feat(<scope>): <summary>" \
  --body  "<what + why + test evidence>"

PR review is owned by the relevant CODEOWNERS group; default is @calab-ai/guild-delivery for content under primitives/ and packages/.

6. Merge → semantic-release tags

On merge to main, semantic-release (configured per docs/release-guidelines.md in the registry) will:

  1. Determine the next version from your commit messages.
  2. Push a tag of the appropriate shape:
    • Primitive: refs/tags/primitives/<type>/<name>/v<version>
    • Package: refs/tags/packages/<name>/v<version>
    • Catalog snapshot: refs/tags/catalog/v<version>
  3. Create a GitHub Release with the changelog and attach the regenerated portal-data.json artifact.

You can verify release creation against the latest GA release.

7. Catalog refresh

The catalog generator (catalog/generate.ps1) re-reads every primitive and package manifest and rewrites catalog/portal-data.json. The marketplace portal and the handbook’s APM Marketplace consume the new artifact on their next sync.


Future: gh calab apm publish

The intended developer surface — once the CLI commands ship — will collapse the steps above into:

# Publish a primitive (lints manifest, opens PR, drives release on merge)
gh calab apm publish primitive primitives/skills/my-new-skill
 
# Publish a package
gh calab apm publish package packages/my-new-package

Design notes (subject to change until merged):

  • Both subcommands accept either a directory path or a primitive/package id.
  • Both run the same validate_*_manifests.py checks gh calab apm validate runs locally.
  • Output follows the standard JSON envelope from docs/apm-cli-contract.md:
    • data.published.kind{primitive, package}
    • data.published.id, data.published.version, data.published.tag
  • Exit codes follow the standard contract — 4 for validation, 5 for tag conflict, 7 for auth.

When the publish PR opens in calab-ai/gh-calab, this page will link to it directly. Until then, file feature requests there.


Tag-conflict prevention

Each tag must be unique. If you bump version in your manifest but a tag of that shape already exists (e.g. someone else released the same number first), semantic-release will fail. Resolution:

  1. Pull the latest tags: git fetch --tags origin.
  2. Check git tag -l "primitives/<type>/<name>/*" (or packages/<name>/*).
  3. Bump your manifest version past the highest existing tag.
  4. Amend or add a fix-up commit and re-push.