Chapter 4

The Manifest: apm.yml

Author a valid apm.yml that declares Meridian's first shared agent-context dependencies.

Objective

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.

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, dependenciesscripts” (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 intentwhat 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:

The shape 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:

The keys apm init writes, at a glance — what each declares, its type, and the property it carries. Field set confirmed against a fresh apm init. apm v0.23.1
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:

Shape only: the two dependency buckets. Meridian declares neither at 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:

Meridian's first real manifest (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:

One authored source → three native harness files. Observed on disk after apm install; frontmatter shown as APM actually projected it. apm v0.23.1
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 applyTopaths; description dropped; .md
cursor .cursor/rules/meridian-checkout.mdc description + globs applyToglobs; 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.mdapplyTo 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.mdcapplyTo 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 init writes one file with sensible defaults: identity (name/version/description/author), commented targets, two empty dependencies buckets, includes: auto, and scripts: {}. Only name and version are required to parse.
  • includes: auto auto-discovers your locally-authored primitives under .apm/; it is not a restrictive allowlist. You edit .apm/; APM generates apm_modules/ and the per-harness output.
  • Mind the corrections: the identity line is author:, not license:; commented targets: means auto-detect (pin it for a shared repo); and dependencies.mcp is a list of objects.
  • Meridian committed its first real manifest (v0.1.0): identity, three targets, and one local instruction that apm install deployed 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.