After this chapter you can author a valid apm.yml — the first real APM file you
write — that declares a project's identity, the harnesses it
targets, and one locally-authored instruction, and
explain why that single committed file is what turns Chapter
1's scattered, per-machine setup into portable,
reviewable, version-controlled state.
Objective
Concept/Theory
A manifest is “what this project needs,” written down
Before a manifest, a project's agent context is whatever files happen to sit on each machine —
the exact drift Chapter 1 diagnosed at Meridian: a
copilot-instructions.md here, a private review.prompt.md there, Cursor
rules no one else can see. Nothing declares what the project needs; each laptop is its own
source of truth, so the setups quietly diverge. A manifest replaces “whatever
is on my disk” with “what this repository declares” — one version-controlled
file that travels with the code. This is where portability stops being a slogan and becomes a file
you can open in a pull request.
In APM that file is apm.yml, and the docs describe it as a contract: it
“declares the full closure of agent primitive dependencies, MCP servers, scripts, compilation
settings … for a project” and is “the contract between package authors, runtimes,
and integrators”
(manifest
schema). Only two fields are required to parse — name and version
— and everything else is optional
(manifest
schema §2). A valid manifest can be three lines long
(package
anatomy).
Chapter 1 ended with a sketch of Meridian's apm.yml and called it “a
promise, not yet a program.” This chapter makes it real: the same shape, now a file the tool
actually reads. If you have ever opened a package.json, you already know how to read it
— “the shape mirrors package.json on purpose: name,
version, dependencies… scripts”
(package
anatomy).
What the manifest is, and what it is not
Two distinctions keep the rest of the book straight. First, a manifest is not the
per-machine files it replaces. Those were many hand-edited outputs; the manifest is one
authored input, committed to the repo, that those outputs are generated from. You edit the
declaration — never the deployed copies under .github/, .claude/, or
.cursor/, which APM regenerates on install.
Second, a manifest is not a lockfile. The manifest is human-authored declared
intent — what you want, which may include a moving branch or a version range.
The lockfile (apm.lock.yaml, Chapter
6) is the machine-generated resolved state — the exact commits and content
hashes you actually got. The manifest says what; the lockfile records exactly which
(package
anatomy). This chapter is entirely about the first file.
The manifest is the Portability surface — its whole job — but it also seeds the other three properties without yet delivering them: it is the input the lockfile resolves (Reproducibility, Chapter 6), the surface a policy inspects (Governance, Chapter 9), and the declaration that makes provenance reviewable (Provenance / security, Chapter 8). Get the declaration right here and the other three chapters have something honest to work with.
In APM
What apm init writes, key by key
You do not start from a blank file. apm init “creates exactly one file — the
manifest” (your
first package), then you edit it. Run in an empty directory, it emits sensible defaults —
and three of them surprise readers who hand-wrote a sketch first, so read the caption closely:
apm init generates in an empty acme-agents directory —
name from the directory, author from your git config. Note
the identity line is author: (there is no license: key),
targets: is commented out, and includes defaults to
auto. apm v0.23.1
# apm.yml -- one file, sensible defaults; you edit it from here
name: acme-agents
version: 1.0.0
description: APM project for acme-agents
author: Ravi Sharma # auto-detected from your git user.name
# Which agent platforms to deploy to (uncomment to pin):
# targets:
# - copilot
# - claude
dependencies:
apm: []
mcp: []
includes: auto
scripts: {}
Every key here is a question the team used to answer per-machine, now answered in one reviewable place. The point is the meaning and shape of each field, not an exhaustive flag tour:
| Key | Type | What it declares | Carries |
|---|---|---|---|
name |
string | The package's identity — its slug when another repo depends on it. Defaults to the directory name. | Portability |
version |
string (semver) | This package's own version. Meridian starts pre-1.0 at 0.1.0 and advances one beat per chapter. |
Portability |
description |
string | A one-line human summary shown in listings. Replace the boilerplate. | Portability |
author |
string | Optional attribution. apm init auto-fills it from your git user.name; set it to the team or org for a shared repo. |
Portability |
targets |
list (commented by default) | Which harnesses to deploy to. Absent means APM auto-detects from the repo; uncomment and pin for a shared repo. | Portability |
dependencies.apm |
list | External primitive packages, git-sourced and pinned by ref. Empty is valid. Filled for real in Chapter 5. | seeds Reproducibility |
dependencies.mcp |
list of objects | MCP server declarations (external tools). A list of objects, not a name→map. Declared and blocked in Chapter 8 (security), policy-governed in Chapter 9. | seeds Security / Governance |
includes |
auto | list |
Which local .apm/ primitives to compile and deploy. auto discovers everything under .apm/. |
Portability |
scripts |
map | Named shell commands (name: "command") run with apm run. Empty for now; used in Chapter 5. |
Portability |
Two of those keys read straight out of Chapter 3. targets is the target ≠
harness distinction, now written down: you declare the targets, and the
harness still runs independently. Pinning them in the
manifest makes the deployed output deterministic “for every developer, CI runner, and cloud
agent,” rather than silently tracking whichever tool folders happen to exist on the last
person's machine
(manifest
schema §3.6). And includes: auto is what connects the manifest to the primitives
you author locally, discussed next.
.apm/ is where you author; apm_modules/ is what you install
A manifest describes two sources of primitives that look different but land in the same place.
Locally-authored primitives are files your repo writes itself, under the
.apm/ source tree (instructions/, prompts/,
skills/, agents/, hooks/) — “the conventional
source root for APM packages.” External dependencies are primitives pulled from
other repositories, declared under dependencies and fetched at install time into the
generated, gitignored apm_modules/. You edit .apm/; you never hand-edit
apm_modules/ (package
anatomy).
includes: auto is the bridge for the first kind: with no remote dependencies declared at
all, apm install simply “walks your local .apm/ tree and deploys what
it finds” — “APM treats your repo as the package and deploys its .apm/
content” (your
first package). That is precisely Meridian's first manifest: one local instruction, zero remote
dependencies. Consuming other people's packages (Chapter 5) is an addition to local
authoring, never a precondition for it.
The two dependency buckets — shape now, installs in Chapter 5
Meridian declares no dependencies at v0.1.0, but you should recognize the two buckets
when you meet them. dependencies.apm is a list of git-sourced package refs;
dependencies.mcp is a list of MCP server objects. Here is the shape only —
do not add either to your first manifest:
v0.1.0 —
remote packages arrive in Chapter 5, MCP servers in
Chapter 8. apm v0.23.1
dependencies:
apm:
- microsoft/apm-sample-package#v1.0.0 # owner/repo[/subpath]#<ref> -- pin the ref
mcp:
- name: fetch # each entry is an OBJECT, not a name -> map
registry: false
transport: stdio
command: npx
args: ["-y", "@modelcontextprotocol/server-fetch"]
Pinning the git ref (#v1.0.0) is what keeps a restore deterministic and a diff honest
— the reproducibility lesson from Chapter 2,
enforced here by convention. The subpath form (owner/repo/skills/<name>) selects a
single primitive from a monorepo. Both are declared here and installed for real in Chapter 5.
When to use / pitfalls
Keep your first manifest minimal
The dividing line from Chapter 1 still holds: commit an
apm.yml the moment context is shared and plural — multiple
developers, multiple harnesses, onboarding that should be reproducible. For a spike you will throw
away, a manifest is ceremony you do not need yet.
When you do write one, resist the urge to fill every key. A strong first manifest declares only three
things: identity (name, version, a real
description), targets (the harnesses your team actually uses), and the
local primitives it authors under .apm/ (via includes: auto).
Remote dependencies and scripts are real features — they are just
Chapter 5's beat, added when you need them, not scaffolding to
fill in up front. The empty dependencies buckets and scripts: {} that
apm init leaves behind are inert and harmless; keep them and move on.
Worked example
Meridian's first real apm.yml
Here is the payoff. Meridian takes the apm init scaffold, sets its identity, uncomments
and pins three targets, and authors one local instruction — the checkout-domain rule that
drifted in Chapter 1 (“use the Money value object; treat retries as
idempotent”). No remote dependencies, no scripts. This is the whole file:
v0.1.0): identity, three targets, and — via
includes: auto — one local instruction. Verified end to end with
apm install; no network. apm v0.23.1
name: meridian-checkout-agent-context
version: 0.1.0
description: Shared AI-agent context for the Meridian checkout service.
author: Meridian Checkout Team
targets:
- copilot
- claude
- cursor
dependencies:
apm: []
mcp: []
includes: auto
scripts: {}
The instruction itself is authored once, under a recognized primitive directory, so
includes: auto discovers it with zero extra configuration. Its frontmatter carries a
description and an applyTo glob — the same instruction shape from
Chapter 3:
.apm/instructions/meridian-checkout.instructions.md — one source of truth for
the checkout rule, authored by hand. apm v0.23.1
---
description: Meridian checkout service engineering rules
applyTo: "**/*.{ts,tsx,md}"
---
Use the Meridian `Money` value object for currency math. Treat checkout retries as
idempotent operations keyed by `paymentAttemptId`. Never suggest storing card data in
application logs.
With the manifest and the one source file in place, a single command restores the context. Running
apm install with no package argument means “deploy from
apm.yml”: APM treats the local project as one package and fans its instruction out
to the declared targets.
apm install deploying the local instruction to all three targets — no network,
no package fetched. The local project is the package. apm v0.23.1
$ apm install
[i] Targets: claude, copilot, cursor (source: apm.yml)
[+] <project root> (local)
|-- 3 rule(s) integrated -> 3 targets
[i] Added apm_modules/ to .gitignore
[*] Installed 1 APM dependency in 2.5s.
This is the concrete portability win. One source file, one command, and the same intent lands in three harnesses' native locations — different directories, different frontmatter keys, even a different file extension, with zero per-harness edits:
| Harness | Deployed path | Frontmatter | Transform vs. your source |
|---|---|---|---|
| copilot | .github/instructions/meridian-checkout.instructions.md |
description + applyTo |
unchanged — native .instructions.md |
| claude | .claude/rules/meridian-checkout.md |
paths only |
applyTo → paths; description dropped; .md |
| cursor | .cursor/rules/meridian-checkout.mdc |
description + globs |
applyTo → globs; description kept; .mdc |
The transforms are exactly Chapter 3's “compiled is not byte-identical” rule, now proven on Meridian's own content. Read the two projected files side by side and the point is undeniable — same rule, each in its harness's native shape:
.claude/rules/meridian-checkout.md — applyTo became
paths, and description was dropped. apm v0.23.1
---
paths:
- "**/*.{ts,tsx,md}"
---
Use the Meridian `Money` value object for currency math. Treat checkout retries as
idempotent operations keyed by `paymentAttemptId`. Never suggest storing card data in
application logs.
.cursor/rules/meridian-checkout.mdc — applyTo became
globs, description was kept, and the extension is .mdc.
apm v0.23.1
---
description: Meridian checkout service engineering rules
globs: "**/*.{ts,tsx,md}"
---
Use the Meridian `Money` value object for currency math. Treat checkout retries as
idempotent operations keyed by `paymentAttemptId`. Never suggest storing card data in
application logs.
Two side effects are worth naming so they are not mistaken for drift. apm install
auto-appended apm_modules/ to .gitignore (it is generated, never committed),
and it wrote an apm.lock.yaml recording the deployed files and their content hashes. With
only local primitives, that lockfile is short and has no dependencies — the full
lockfile treatment is Chapter 6. What matters here
is what you authored: one manifest, one instruction, and a reviewable file the whole team now shares.
Recap & next
Recap
-
A manifest turns “what this project needs” into reviewable,
version-controlled state. In APM that file is
apm.yml— the Portability surface, and the input that seeds reproducibility, governance, and provenance in later chapters. - You author one input; APM generates the outputs. The manifest is declared intent (what); the lockfile is resolved state (exactly which, Chapter 6).
-
apm initwrites one file with sensible defaults: identity (name/version/description/author), commentedtargets, two emptydependenciesbuckets,includes: auto, andscripts: {}. Onlynameandversionare required to parse. -
includes: autoauto-discovers your locally-authored primitives under.apm/; it is not a restrictive allowlist. You edit.apm/; APM generatesapm_modules/and the per-harness output. -
Mind the corrections: the identity line is
author:, notlicense:; commentedtargets:means auto-detect (pin it for a shared repo); anddependencies.mcpis a list of objects. -
Meridian committed its first real manifest (
v0.1.0): identity, three targets, and one local instruction thatapm installdeployed to Copilot, Claude Code, and Cursor — one source, three native files, zero per-harness edits.
Next
Meridian's manifest declares its own context beautifully — but it depends on no one else yet.
Chapter 5 — Install & Restore fills the
dependencies.apm bucket with a real, pinned git source, runs apm install to
consume an external package for the first time, and turns the empty scripts map into
runnable commands — the everyday install and restore loop.