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.
Edition 1.2 targets APM 0.31.0. The original scaffold and Meridian recording
remain labeled 0.23.1. The current local fixture
keeps the same small setup: one checkout instruction, shared across three targets.
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). The examples here use APM's current working-draft shape, without an explicit
schema selector; the init template is a starting point, not the complete set of allowed fields
(APM 0.31.0 manifest reference).
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 and source — not the APM-managed copies under .github/,
.claude/, or .cursor/. Those directories can also contain human-owned
files; their location alone does not make every file generated output.
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.
A declaration also sets an authority boundary: eligible package manifests and
component declarations determine what can be deployed; directory conventions supply defaults
where the relevant declaration is absent. Selected targets define the destination set, and
package or per-dependency restrictions can narrow it, not silently add harnesses. That is
reviewable deployment intent, not a guarantee that an agent obeys a rule or runs in a sandbox
(package authority;
Chapter 8's runtime boundary).
You do not start from a blank file. Plain apm init --yes in an empty directory
creates only apm.yml — no .apm/ source tree, lockfile, or harness
deployment. You then edit its sensible defaults
(APM 0.31.0 init reference).
The historical scaffold below retains its example identity and annotations:
Historical scaffold shape for acme-agents: name from the directory,
author from Git configuration, commented targets, and
includes: auto. These defaults remain in 0.31.0. license is absent
from plain init, not forbidden by the schema. 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: {}
Already have native-layout skills? Use Chapter
5's discovery/apply workflow: apm init --discover inventories admissible local
packages; consented apply merges their references without moving or translating the sources.
Installation is a later step. Plain init --yes can overwrite an authored manifest;
it is not the merge shortcut.
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:
Plain-init keys, with their current types and meaning — not an exhaustive schema.
Field set checked with empty-directory init and the current local fixture.
apm v0.31.0
Key
Type
What it declares
Carries
name
string
The package's declared identity. Init defaults to the directory name; a dependency's source coordinates are a separate choice.
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)
The selected harness destinations. If absent, a CLI selection, saved default, or filesystem detection decides. Pin the list for a shared repo.
Portability
dependencies.apm
list of strings or objects
Package references: Git, local paths, or experimental registry sources. Empty is valid. Source forms appear below; installs follow in Chapter 5.
seeds Reproducibility
dependencies.mcp
list of strings or objects
MCP registry references or server objects, not a name→map. Connection trust is developed in Chapter 8, policy in Chapter 9.
seeds Security / Governance
includes
auto | list
Consent for local content from the selected source layout. An explicit list is not an install filter, but is exhaustive for plugin packing; see the operation-specific pitfall.
Portability
scripts
map
Named shell commands (name: "command") run with experimental 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 destination choice repeatable for developers and CI, rather than silently
tracking whichever tool folders happen to exist on the last person's machine
(manifest
schema §3.6). The target-selection rules below explain
overrides and bootstrap. And includes: auto records consent for the local primitives
discussed next.
Meridian authors in .apm/; installed copies go in apm_modules/
A manifest brings your own sources and referenced packages into the same deployment workflow.
Meridian's locally-authored primitives live under the
.apm/ source tree (instructions/, prompts/,
skills/, agents/, hooks/) — “the conventional
source root for APM packages.” Referenced packages are declared under
dependencies.apm and materialized at install time into the generated, gitignored
apm_modules/. They can come from Git, local paths, or an enabled registry.
You edit your source; you never hand-edit
apm_modules/ (package
anatomy).
.apm/ is Meridian's chosen convention, not the only supported layout. Skills and
plugins can retain their native source trees. An eligibleapm.yml —
accompanied by .apm/ or APM/MCP dependencies — takes precedence over a
co-located plugin marker; metadata-only apm.yml does not force that route. Root skills
and skill collections are separate supported forms
(package layouts and precedence).
A root SKILL.md beside apm.yml is a supported hybrid layout.
Do not confuse that with setting type: hybrid: the top-level type field
remains reserved for future overrides, not an active switch for selecting primitive types
(reserved type field).
For Meridian's layout, includes: auto explicitly consents to local content.
With no remote dependencies, apm install treats this repo as the source package
and projects its eligible .apm/ primitives to the selected targets
(your
first package). That is precisely Meridian's first manifest: one valid 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
used here: dependencies.apm references packages, while dependencies.mcp
declares tool connections. Both accept lists of strings or typed objects. The following are
separate fixture examples, not additions to Meridian's first manifest.
Git declaration from the current core/pinned-skill fixture: one public skill at
a full commit, not the whole sample package. Needs public Git
access on a cold cache. Fixture independently checked on 2026-09-16.
apm v0.31.0
The source identity implements Chapter 2's pinning
lesson. In a Git object, use ref, not registry-style version;
unknown object keys are rejected. path selects content inside that repository.
Git objects also accept explicit clone URLs for other Git servers. A tag such as
#v1.0.0 is readable but can be moved; the full commit above fixes the source
revision, and the lockfile records the resolved graph
(Git dependency forms).
Without git, a path: ./pkg object refers to a local package.
alias gives it a local resolution name; skills selects from the skills
it actually declares, and dependency targets intersects the install's target set.
This is the authority boundary in concrete form:
Local-path selection fixture, including its own pkg/ source: the project selects
Copilot and Claude, but this dependency reaches only Claude, with only kept.
Observed selected-skill deployment, not a plugin runtime test.
Its CI audit exits 1 because this legacy-plugin input has no
pkg/apm.yml; the successful selection is not an audit-clean guarantee.
apm v0.31.0
That fixture's legacy plugin.json declares
"skills": ["./skills/kept", "./extra/custom"]; an extra conventional skill directory
is not permission to deploy it. An explicit "skills": [] means none, while omitting
the key permits conventional discovery. Consumer selection cannot resurrect undeclared skills
(declaration-first plugin intake).
Git remains the default path, not the only shipped source. APM 0.31.0 includes
experimental REST registries, enabled explicitly with
apm experimental enable registries. A named registry supplies the endpoint;
its dependency object uses id and version, not Git's ref.
The client and HTTP API exist; that does not establish a Microsoft-operated public searchable hub
(registry guide).
Selected fields from the loopback registry fixture: fixture is a configured
registry name, operations/exact its package ID, and 1.0.0 the selector.
Requires the isolated test server and experimental registry
feature; this is not a public endpoint or a standalone setup recipe.apm v0.31.0
A configured default registry can reroute plain owner/repo#selector shorthand.
Explicit git: stays Git, so it makes the source choice unambiguous even on a machine
with a registry default. Keep package registries distinct from MCP registries and Git proxies;
distribution choices return in Chapter 10.
MCP entries describe connections, not ordinary primitive packages. The self-defined
object below supplies a name, transport, executable, and arguments. The list also accepts MCP
registry-reference strings; objects are not its only legal form
(MCP dependency forms).
MCP object shape from a configuration-only fixture. The executable is deliberately nonexistent;
no MCP server or harness was started. MCP identity lookup can
require network access. Trust and runtime boundaries belong to
Chapter 8.
apm v0.31.0
Reviewability includes the edit itself. The current APM dependency writer preserved the tested
leading comment, blank line, and target-list style when adding a package. A
direct MCP add uses a separate path that still reserialized the tested manifest
and removed its comment. Review the diff after either operation; do not assume every writer
preserves formatting
(APM dependency writer;
Chapter 5's add workflow).
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 valid empty declarations; there is no need to fill them yet.
A local path is not an immutable Git pin: its source must remain available with the project.
Nor does apm install --frozen universally veto declaration changes. In 0.31.0, a
newly declared local-path dependency can deploy and rewrite an existing lock; see
Chapter 5's verified exception.
Frozen's limited structural checks are not a runtime security guarantee or a content audit.
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. The historical snapshot and current
fixture are shown separately: the same declaration still expresses the same shared intent.
Historical exact chapter snapshot of Meridian's first manifest (v0.1.0):
identity, three targets, and consent for local content. The original end-to-end
apm install recording used 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: {}
Current minimal fixture at
backend/examples/updates/1.2/core/local-instruction/apm.yml.
The same Meridian v0.1.0 declaration and instruction were independently
installed, restored, frozen, and audited on 2026-09-16; no network or agent runtime.
apm v0.31.0
# Materialize one local instruction in three native target formats.
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. Install
discovers it without path enumeration; includes: auto records consent. 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. The source is unchanged.
apm v0.23.1; rechecked with v0.31.0
---
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.
Historical apm install recording: one local instruction, three targets, no network
or fetched package. Its wording and timing are not a current-output promise.
apm v0.23.1
Run in the current local-instruction fixture directory with the manifest and
source above. The same bare command deploys the three native files below; verified offline
on 2026-09-16. apm v0.31.0
apm install
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, reproduced from the current fixture.
Expected paths and native content were checked, not inferred from exit 0.
apm v0.31.0
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, as in the original 0.23.1 projection.
apm v0.31.0
---
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.
The current serializer quotes the description.
apm v0.31.0
---
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 apm.lock.yaml. The current local-only fixture's actual lock has
dependencies: [], three deployments ownership records, and the local
deployed-file lists and hashes. Fresh 0.31.0 locks omit generated_at; compare the
recorded state, not a timestamp
(current lockfile contract).
The full treatment is Chapter 6. What matters
here is what you authored: one manifest, one instruction, and a reviewable file the whole team
now shares.
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).
Plain empty-directory 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 consents to the selected local source layout. An explicit list does
not filter install discovery, but is exhaustive for plugin packing. You edit source; APM owns
its materialized copies, not every file in a harness directory.
The scaffold is not the whole schema: license is valid optional metadata and
devDependencies keeps author tooling out of the published dependency closure.
A hybrid layout does not activate the reserved type: hybrid field.
Pin targets for a durable destination choice. Use ref on Git objects,
id/version for registry packages, and path for local
references. MCP accepts registry strings as well as server objects. Review the declaration,
rather than relying on directory names or the last invocation's flags.
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.