After this chapter you can take agent context that already works in one repo — a prompt, an
instruction set, a skill — and extract it into a standalone, versioned APM
package that other teams install through the same Chapter
5 apm install loop, instead of copying the files by hand. You will see why
copy-paste reuse quietly recreates Chapter 1's drift, author a
producer apm.yml, snapshot the package with apm pack, and read the crucial
producer/consumer symmetry: producing adds no fifth property — it
makes you the source of Portability,
Reproducibility,
Provenance, and
Governance for everyone downstream.
Objective
Concept/Theory
Reuse-by-copy is the Chapter 1 problem wearing a helpful face
Chapter 1 opened on drift: three copies of a review prompt in three home directories, each checking a slightly different threat model. Chapters 4–9 fixed that inside one repo — a manifest, a lockfile, a policy gate. But the moment a second team wants your good prompt, the old failure is one paste away. Copy the checkout-review prompt into another repo and the two copies begin to diverge immediately: no shared version, no lockfile, no review trail, no way to answer “which prompt produced this behaviour?” The uncomfortable truth this chapter names is that reuse-by-copy recreates the exact drift the book set out to kill.
Producing is the fix. It is the act of extracting context that already works —
a prompt, a skill, an instruction set — into a standalone, versioned APM package that other
repos install through the normal apm install loop, rather than copying files by hand. A
copy is a point-in-time snapshot that starts drifting the instant it is made; a dependency is
a pinned, hashed reference that a lockfile can reproduce and apm update can advance
deliberately (Chapter 7). Producing converts copies into dependencies.
APM frames the whole ramp around one promise: “there is no separate ‘build
pipeline’ — the CLI is the build pipeline”
(Producer overview).
A package is just a directory with apm.yml + .apm/
Here is the part that removes the intimidation. There is no packaging tool, no build config, no
artifact type you have not already met. “An APM package is a directory with two things: an
apm.yml manifest and a .apm/ source tree. Everything else … is
generated, optional, or both”
(package
anatomy). The producer mental model is identical: “a producer package is just a directory
with apm.yml … .apm/ … README.md”
(Producer overview).
You already know this shape from both sides. In Chapter 3
and Chapter 4 you authored primitives under
.apm/instructions/ and .apm/prompts/; in
Chapter 5 you consumed other people's packages that
had exactly those directories. Producing is not new authoring — it is relocation into a
package shape. The same source-vs-build split from Chapter 3 is now your responsibility:
.apm/ is what you write and commit; .github/, .claude/,
.cursor/, and apm.lock.yaml are generated. “Edit the source under
.apm/ and re-run apm install — never edit the deployed copy”
(package
anatomy).
The symmetry: everything you consumed, you now become the source of
Producing does not add a fifth property to the book's four. It flips your role.
Every benefit the earlier chapters named as a consumer win, the reader now supplies to
downstream teams. When Meridian ships meridian-standards, its consumers get:
- Portability — one manifest compiles the same primitives onto Copilot, Claude Code, and Cursor (Chapter 5);
-
Reproducibility — a lockfile that
pins each dependency by
resolved_commitand fingerprints its source tree bycontent_hash, so a bareapm installrestores the same bytes; and, when a package is packed as a bundle, an extra per-file SHA-256 map on top (Chapter 6); - Provenance — a pinned, hashed, reviewable source that passes the same install-time scanners from Chapter 8;
-
Governance — the package installs through
the same
apm-policy.ymlgate from Chapter 9; a policy can even require it, exactly as Meridian'srequire: meridian-finance/meridian-standardsrule did.
This is the spine sentence of the whole book landing: the reader stops being an npm user and
becomes an npm publisher — the same manifest, lockfile, and policy discipline, now
pointed outward. APM states the symmetry directly: “every package you publish here installs
through the [consumer ramp's] apm install command”
(Producer overview). The
official ramp is a five-rung ladder — author → compile → preview → pack
→ publish — and this chapter walks the two rungs that matter for internal reuse:
author the package, and pack it, then closes the loop by installing
the result back into a scratch repo. “You don't need a marketplace to start”
(Producer overview).
In APM
A producer package is a directory you already know how to read
Here is meridian-standards as a plain directory. Nothing is scaffolded by a special
tool: an apm.yml, a README.md, and one subdirectory per primitive type under
.apm/. Read it and you will recognise every file from earlier chapters.
meridian-standards tree — manifest, README, and three primitives
under .apm/<type>/. plugin.json is not here: it is generated
by apm pack, not hand-written. apm v0.23.1
meridian-standards/
├── apm.yml # producer manifest (identity + distribution metadata)
├── README.md # what the package is and how to install it
└── .apm/ # the SOURCE tree — one directory per primitive type
├── instructions/
│ └── meridian-engineering.instructions.md # shared engineering rules (Money type, idempotency…)
├── prompts/
│ └── checkout-review.prompt.md # the checkout-review prompt, now reusable
└── skills/
└── secure-payment-review/
└── SKILL.md # PCI / payment-review skill
Two precisions keep you out of trouble later. First, author under
.apm/<type>/, not root convention directories: apm pack is
liberal and will also collect a root skills/ or instructions/ folder, but
apm install is stricter and may not discover those on the consumer side — so
a file that shows up in the bundle can be silently skipped on install, “silently removing those
guardrails from consumer environments”
(pack a
bundle). Second, commands ship as prompts — there is no
.apm/commands/ directory; a slash-command is authored as a
.apm/prompts/*.prompt.md
(author
primitives).
The producer manifest: identity + type + distribution metadata
A consumer manifest answers “what does my repo need?” A producer manifest also
answers “who is this package, where does it come from, and what is it for?” so
other people can evaluate and discover it. It is the same apm.yml schema you
wrote in Chapter 4 — producers simply fill in the
optional distribution fields a private consumer manifest can leave out. Here is Meridian's:
backend/examples/ch10/meridian-standards/apm.yml — the same schema as a consumer
manifest, with the distribution fields filled in. Verified against a fresh apm init
template + the synthesised plugin.json. apm v0.23.1
name: meridian-standards # REQUIRED — package identity (its slug when others depend on it)
version: 1.0.0 # REQUIRED — semver
description: Shared Meridian engineering instructions, prompts, and review skills.
author: Meridian Platform Engineering # string -> { name: "…" } in the generated plugin.json
license: MIT # SPDX id — recorded in the lockfile & SBOM (omitting it -> NOASSERTION)
repository: https://github.com/meridian-finance/meridian-standards
keywords: [meridian, checkout, fintech, ai-agents]
type: hybrid # REAL enum, but RESERVED/INERT at v0.23.1 — changes no behaviour today
targets: # incl. copilot/claude -> routes `apm pack` to plugin.json mode
- copilot
- claude
- cursor
includes: auto # consent to publish everything under .apm/ (the `apm init` default)
Only two fields are required to parse — name and version. Everything
else is optional, and unknown top-level keys are preserved but ignored
(manifest
schema §2). The distribution fields are the package's business card: repository
gives provenance, license sets reuse terms, keywords feed discovery, and
author records ownership.
| Key | Verdict | What it does for a shared package |
|---|---|---|
name |
required | Package identity — the slug other repos depend on. |
version |
required (semver) | The version consumers pin. Meridian ships 1.0.0. |
description |
optional, read | One-line summary; flows into plugin.json; apm pack warns if missing. |
author |
optional, read | Attribution. A string becomes { "name": … } in plugin.json. |
license |
optional, read | SPDX id. Recorded in the lockfile and SBOM; omitting it records NOASSERTION. |
repository |
optional, read | Provenance; mapped into plugin.json. |
keywords |
optional, read | Discovery tags; mapped into plugin.json (and marketplace tags). |
targets |
optional, validated | Harnesses to compile for. Unknown target values are a parse error; also routes apm pack. |
includes: auto |
optional (the default) | Consent to publish everything under .apm/. |
type |
real enum, but inert | instructions | skill | hybrid | prompts. Reserved for future overrides; no effect at v0.23.1. |
| (unknown key) | silently preserved | No error, no warning — kept in the file, ignored by the CLI. |
The metadata is not decorative: it becomes the package's identity card on the other end. When you do
not author a plugin.json yourself, apm pack synthesises one from
apm.yml — so apm.yml stays the single source of truth
(pack a
bundle):
plugin.json that apm pack generates for meridian-standards
— mapped straight from the manifest. Note type is absent.
apm v0.23.1
{
"name": "meridian-standards",
"version": "1.0.0",
"description": "Shared Meridian engineering instructions, prompts, and review skills.",
"author": { "name": "Meridian Platform Engineering" },
"license": "MIT",
"repository": "https://github.com/meridian-finance/meridian-standards",
"keywords": ["meridian", "checkout", "fintech", "ai-agents"]
}
apm pack — snapshot the package into a distributable artifact
apm pack is the pack rung of the ladder: it turns your authored primitives into something
you can hand off. The one rule to internalise before you run it: apm pack packs
the files your last apm install deployed — not the raw .apm/ tree.
So you always apm install first. “If apm pack reports ‘No deployed
files found’, your apm.lock.yaml has no deployed_files entries. Run
apm install first”
(pack a
bundle).
What apm pack produces is decided by which blocks your apm.yml
declares. This routing table is the thing to memorise — it explains why the same command behaves
very differently across packages:
If apm.yml contains… |
apm pack writes… |
Where |
|---|---|---|
a dependencies: block |
a bundle (plugin.json + primitive dirs + embedded lockfile) | ./build/<pkg>/, or a .zip with --archive |
a marketplace: block |
marketplace artifact(s) | .claude-plugin/marketplace.json |
targets: including claude or copilot |
plugin.json manifest(s), no bundle | in-tree: .github/plugin/, .claude-plugin/ |
| none of the above | “Nothing to pack” | exit 1 |
meridian-standards has targets: (including copilot and
claude) but no dependencies: block, so it lands in row 3
— plugin.json mode, not a bundle. The safe pre-flight is
apm pack --dry-run --verbose: it reports exactly what would be written and
touches nothing on disk. It runs entirely offline:
meridian-standards that is plugin.json mode:
two manifests would be written, no bundle. Both commands are offline, exit 0.
apm v0.23.1
$ apm install # STEP 1 — pack packs what install DEPLOYS, so install first
[i] Targets: claude, copilot, cursor (source: apm.yml)
[+] <project root> (local)
|-- 1 prompts integrated -> .github/prompts/
|-- 2 commands integrated -> .claude/commands/, .cursor/commands/
|-- 3 rule(s) integrated -> 3 targets
|-- 1 skill(s) integrated -> .agents/skills/, .claude/skills/
[*] Installed 1 APM dependency in 1.8s. # exit 0
$ apm pack --dry-run --verbose # STEP 2 — pre-flight: what WOULD pack write? (writes nothing)
Would write plugin manifest to .github/plugin/plugin.json # copilot target
Would write plugin manifest to .claude-plugin/plugin.json # claude target
# cursor is in targets: but gets NO plugin.json — the plugin contract is claude/copilot only
Scaffolds: apm plugin init and apm marketplace init
You do not have to hand-write the shape. apm plugin init scaffolds a fresh producer
package — it writes an apm.yml plus a plugin.json, and adds a
devDependencies: block (dev-only dependencies that are excluded from what
apm pack ships — the right home for test-only tooling). It is fully offline:
apm plugin init scaffolds rung 1 of the ladder — a valid producer skeleton, no
network. apm v0.23.1
$ apm plugin init -y --target copilot # scaffold a brand-new producer package (offline)
[+] Created apm.yml # standard template + a devDependencies: block
[+] Created plugin.json # exit 0
apm marketplace init is the discovery side. It adds a marketplace: block to
apm.yml; apm pack then builds a .claude-plugin/marketplace.json
that consumers register with apm marketplace add <owner>/<repo>. A
marketplace is a discovery index over git, not a hosted registry — “a
curated index of packages that one repo publishes and many repos install from”
(publish
to a marketplace). One trap worth knowing: unknown keys inside the
marketplace: block are rejected at parse time — stricter than the top-level
manifest, which silently ignores unknowns.
apm marketplace init scaffolds a marketplace: block (offline). Consumers
later reach it with apm marketplace add. apm v0.23.1
$ apm marketplace init --name meridian-marketplace --owner meridian-finance # offline, exit 0
# appended to apm.yml:
marketplace:
owner: { name: meridian-finance, url: https://github.com/meridian-finance }
build: { tagPattern: "v{version}" }
outputs: { claude: {} } # codex optional; each output writes its profile-default path
packages:
- name: example-package # placeholder the scaffold writes; edit to meridian-standards
description: Human-readable description of the package
source: meridian-finance/example-package
version: "^1.0.0"
apm publish — the registry path (experimental)
APM also has an apm publish verb that uploads to a REST registry — but it is
gated behind an experimental feature that is disabled by default, and publishing
itself pushes over the network. For this book it is SKIPPED-needs-network. The gate check
is verifiable offline, and it tells you exactly what is going on:
apm publish is gated behind the disabled registries feature. The gate is
verified offline; the actual upload is SKIPPED-needs-network.
apm v0.23.1
$ apm publish --package meridian-finance/meridian-standards --dry-run
Error: apm publish requires the experimental registries feature.
Enable with: apm experimental enable registries. # disabled by default
Even once enabled, publish uploads via
PUT /v1/packages/{owner}/{repo}/versions/{version} — a network operation against a
registry API that is still a v0.2 working draft. Most teams never need it: a
git ref is the zero-infrastructure distribution channel, and it is enough for
everything this chapter does. The registry route belongs to the enterprise story in
Chapter 11.
When to use / pitfalls
How to hand a package off
Producing the package is one thing; getting it to consumers is another. APM distribution is git-based today — there is no central public registry — so pick the channel by reach, not habit. For internal reuse, a pinned git ref is almost always the right answer.
| Channel | What it is | Reach for when… |
|---|---|---|
git ref (owner/repo#<ref>) |
The primary APM channel — zero infrastructure, pinned + hashed by the lockfile. | the consumer can reach your git host. Default choice for internal reuse. |
bundle (apm pack --archive) |
A .zip whose files are pinned by SHA-256; needs a dependencies: closure with remote refs. |
you must move it by hand — offline, air-gapped, or a customer hand-off. |
| marketplace | A discovery index over git (apm marketplace add), not a hosted registry. |
many repos need to discover many curated packages. |
registry (apm publish) |
Semver-range installs from a REST service — experimental (registries). |
your org runs a registry proxy (Chapter 11). |
Worked example
The producer → consumer round-trip
The real test of a produced package is not that it packs — it is that it stands alone. Local primitives can lean on paths, tools, or context that only exist in the origin repo, so the producer's discipline is to install the package into a fresh, throwaway repo and prove it works there. “Install your own package in a scratch repo before declaring it shipped” (Producer overview). This is the consumer ramp from Chapter 5, seen from the producer's chair.
You can do it fully offline with a local-path source install. From a fresh scratch
repo, install meridian-standards by path: APM resolves its .apm/ source
primitives (ignoring the producer's own compiled output), deploys them to the scratch repo's target,
and caches the package — exactly as it would for any git-sourced dependency, with no special
casing:
meridian-standards into a throwaway repo by local path.
The extracted context stands alone — exit 0, no network.
apm v0.23.1
$ apm init -y --target copilot # a scratch consumer repo, outside meridian-standards
$ apm install ../meridian-standards # local-path SOURCE install — fully offline
[*] Validating 1 package...
[+] …/meridian-standards
[>] Resolving …/meridian-standards...
|-- 1 prompts integrated -> .github/prompts/
|-- 1 instruction(s) integrated -> .github/instructions/
|-- 1 skill(s) integrated -> .agents/skills/
[*] Installed 1 APM dependency in 1.4s. # exit 0 — the extracted context stands alone
One caveat about that local-path install: APM records the dependency as a machine-specific
absolute path, which is not committable. For a portable manifest you use a relative
./ path (dev-only) or — the real story — a git ref. That is
how the loop actually closes: Meridian pushes meridian-standards to its private host,
tags v1.0.0, and meridian-checkout depends on it. Because that host is
private, the resolve is SKIPPED-needs-network in this book — but the manifest change
is the whole point:
meridian-checkout/apm.yml closing the loop — the extracted conventions return as a
pinned dependency, and the version graduates to 1.0.0. Resolving the private repo is
SKIPPED-needs-network. needs network
apm v0.23.1
# SKIPPED-needs-network: meridian-finance/meridian-standards is a private repo
name: meridian-checkout-agent-context
version: 1.0.0 # <- the beat: the checkout context graduates from v0.x to 1.0.0
author: Meridian Checkout Team
targets: [copilot, claude, cursor]
dependencies:
apm:
- meridian-finance/meridian-standards#v1.0.0 # the extracted conventions, back in as a governed dep
mcp: []
includes: auto
That single pinned line is the payoff of the whole book. The checkout team no longer copies
its own conventions between services — it depends on them. The lockfile pins the exact
commit and content hash (Chapter 6); the
install-time scanners vet the source (Chapter 8); and the org
policy from Chapter 9 can now
require: meridian-finance/meridian-standards, so every Meridian service restores
the same approved conventions, byte-for-byte. The four properties the reader consumed for nine
chapters are now the four properties they produce.
Recap & next
Recap
- Reuse-by-copy recreates Chapter 1's drift. Producing turns a working convention into one versioned, reviewable package that consumers restore identically — a dependency, not a fork.
-
A package is just a directory with an
apm.ymland a.apm/<type>/source tree. There is no separate build pipeline; the CLI is the build pipeline. Producing is relocation into a package shape, not new authoring. -
A producer manifest is the same schema as a consumer's, with the distribution
fields filled in (
description,author,license,repository,keywords). Onlyname+versionare required;typeis a real enum but inert at v0.23.1; unknown keys are silently preserved. -
apm packroutes by manifest content:dependencies:→ a bundle in./build;marketplace:→ a marketplace index;targets:(claude/copilot) →plugin.jsonin-tree; nothing → “Nothing to pack.”meridian-standardsis plugin.json mode. -
Mind the corrections: run
apm installbeforeapm pack(pack packs deployed files);-o distdoes not work — the default output is./build, and-o/--archiveonly apply in bundle mode. -
Distribution is git-based: a git ref is the zero-infra default; bundles are for
offline hand-off; a marketplace is a discovery index, not a registry; and
apm publish(experimentalregistries) isSKIPPED-needs-network. -
Meridian extracted its checkout-review prompt, engineering instructions, and secure-payment skill
into
meridian-standards, proved it stands alone with an offline round-trip, and closed the loop by depending onmeridian-finance/meridian-standards#v1.0.0— graduatingmeridian-checkoutto1.0.0.
Next
Meridian now produces a package its own teams consume — but a package that many teams
must adopt raises new questions: how do you gate it in CI, serve it in an air-gapped environment, and
govern its use across a whole organisation without turning developer setup into a bottleneck?
Chapter 11 — Enterprise at Fleet Scale takes the
producer and consumer ramps to org scale: apm audit --ci gating, a registry proxy,
air-gapped installs, and the adoption playbook that makes the
Chapter 9 policy stick.