After this chapter you can run APM's four-command consumer loop — apm init,
apm install <pkg>, bare apm install, and apm run <script>
— and tell its two install jobs apart: add (grow the manifest and re-lock) versus
restore (reproduce exactly what is already declared). You will read the lockfile entry an
install writes, know why a no-target install stops with exit 2 instead of guessing a
harness, and know why apm run needs a runtime CLI on your PATH. This is the
operation that turns Chapter 4's declaration into identical
on-disk context in every developer's tools — the moment
Portability stops being a promise and becomes a
fact.
Objective
Concept/Theory
A manifest is a recipe, not a meal
Chapter 4 gave Meridian an apm.yml. On its own,
that file changes nothing about what an agent sees: no instruction is in force, no prompt is
available, no MCP server is wired up. A manifest is a declaration of what should exist;
something still has to make it exist. Install is that something. It “resolves
the dependencies declared in apm.yml, downloads them … and deploys the resulting
primitives plus the project's own .apm/ content into every harness target it detects. It
writes apm.lock.yaml so the next install on any machine reproduces the same files”
(apm install).
We call this move materialization: turning the declared state (apm.yml
plus its lockfile) into real files each tool reads natively. It is where the book's first property
becomes observable. Chapters 1–4 argued that scattered, hand-copied context drifts; a
manifest only ends that drift if there is a single, mechanical way to realize it identically
everywhere — “the next contributor runs bare apm install and gets the same
bytes, pinned by commit and content hash”
(Install
packages). Editing the manifest without installing leaves the declared and materialized states out
of sync — the very gap Chapter 6 formalizes.
Install is not “just downloading packages,” either. It is a fail-closed pipeline —
resolve, policy gate, scan, integrate, lockfile — and a security scan (plus an org
policy gate, if present) runs “before writing anything to disk”
(Install
packages). That gate is Chapter 8's subject; here it is
enough to see install as gated deployment, not a copy. And notice the by-product: every
install writes apm.lock.yaml, which seeds
Reproducibility — the lockfile itself is
Chapter 6, so for now just notice it appear.
The daily loop: four commands, one habit
APM's everyday surface is deliberately tiny. The consumer ramp names “the four commands you'll
use almost every day,” then draws the line: “That's the loop. Everything else is either
lifecycle automation (update, outdated, audit) or a workflow
extension” (Use APM
packages). The point is a habit, not a flag reference — four verbs that map onto
four real moments in a team's week:
| Command | Job | The everyday moment (Meridian) |
|---|---|---|
apm init |
scaffold the manifest — one-time per project | when the manifest was born in Chapter 4 |
apm install <pkg> |
add a dependency and re-lock | Lena needs a new shared skill |
apm install |
restore from the lockfile | Anika clones a fresh repo |
apm run <script> |
run a declared script (experimental) | anyone runs the checkout-review prompt |
Only apm init is one-time; the other three recur. Heavier maintenance —
update, outdated, audit — is deliberate work saved for
Chapter 7, not part of the daily path. And the loop does not grow when
Governance arrives: “Consumer commands are
unchanged when policy is enforced — you'll just see more [x] blocks at install time
when something is denied” (Use
APM packages). The same four verbs survive Chapters 8–9 intact.
Restore vs. add: one command, two jobs
The single command apm install does two conceptually different things depending on whether
you hand it an argument. “With no arguments it installs everything from apm.yml. With
one or more PACKAGE_REF arguments it adds those packages to apm.yml …
and installs only what was added”
(apm install).
Bare install is a restore — it replays the manifest and lockfile and deploys the
same bytes. apm install <pkg> is an add — it resolves a new
dependency, writes it into dependencies.apm, and re-locks.
These are the two halves of team dependency work. Add is how the manifest grows — a
reviewable change to project state, exactly like npm install <pkg> editing
package.json. Restore is how everyone else catches up without thinking, and it is
where the whole book's onboarding promise lands: git clone + apm install
replaces the wiki page, the Slack thread, and the “copy my prompt files” dance from
Chapter 1. Crucially, restore reproduces; it does not
upgrade. “To refresh dependencies to their latest matching versions or refs, use
apm update”
(Install
packages) — that consent-gated move is Chapter 7. Conflating
“restore” with “update” is the one error this chapter most wants to prevent.
In APM
One install command, two jobs
You already met apm init in Chapter 4: it
“creates a minimal apm.yml … so you can start running apm install
immediately” (apm
init), and you run it once. The rest of this section is the other three verbs, each tied to the
concept it implements: add and restore materialize the manifest, targeting decides
where files land, and apm run executes a declared script.
Add: apm install <pkg> grows the manifest and re-locks
Meridian's team wants a public package's primitives. One command pins it, resolves it (transitively),
deploys it into all three harnesses, and updates both apm.yml and the lockfile. This is
materialization in action — a one-line declaration becomes real files the tools read:
apm.yml and re-locks;
--target wins the precedence chain. needs network
apm v0.23.1
$ apm install microsoft/apm-sample-package#v1.0.0 --target copilot,claude,cursor
[*] Validating 1 package...
[+] microsoft/apm-sample-package#v1.0.0
[*] Updated apm.yml with 1 new package(s) # <- add mutates the manifest
[>] Installing 1 new package...
[i] Targets: claude, copilot, cursor (source: --target flag) # <- flag beat apm.yml targets:
[+] microsoft/apm-sample-package #v1.0.0 @fb285168
|-- 2 prompts, 3 agents, 4 commands, 3 rules, 1 skill (across 3 targets)
[+] <project root> (local)
|-- 3 rule(s) adopted -> 3 targets (files unchanged) # <- your local instruction preserved
[!] 1 dependency unpinned: github/awesome-copilot -- add #tag or #sha to prevent drift
[*] Installed 2 APM dependencies in 56.0s. # exit 0
The add wrote one pinned line into the manifest — owner/repo#<ref>, the
deterministic form from Chapter 4:
apm.yml after the add — the pinned entry APM inserted into
dependencies.apm. apm v0.23.1
dependencies:
apm:
- microsoft/apm-sample-package#v1.0.0 # pinned ref -> deterministic restore
mcp: []
And it wrote the lockfile record — the by-product that seeds
Reproducibility. Notice the two fields that make
restore exact: resolved_commit (the precise SHA the tag v1.0.0 pointed at) and
content_hash (the fingerprint of what was deployed). You do not author this file; APM
generates it. Chapter 6 studies it in full —
here, just notice it appear:
# apm.lock.yaml (generated)
dependencies:
- repo_url: microsoft/apm-sample-package
resolved_commit: fb2851683be0e0e7711421d518bd8dba23b0b1f6 # the exact SHA it became
resolved_ref: v1.0.0 # the ref you pinned
content_hash: sha256:744cca54cc8ff7ca90aa1dd621c2f98c6291cd793815afe8518001cc94b8aba9
package_type: apm_package
One pinned line plus one command produced roughly twenty real files across three tools — no
hand-copying, pinned by commit and hash for the next person. The manifest edit was
declaration; this command was materialization. (The
[!] 1 dependency unpinned warning is about the sample's own transitive dependency —
the lockfile still pins it by resolved_commit, so restore stays deterministic; it is a
Chapter 6 concern about future drift, not a failure
here.)
Restore: bare apm install reproduces byte-for-byte
Re-running apm install with no argument is the onboarding path from the Meridian beat. It
is idempotent and cache-served, not a fresh download: APM replays the pipeline from the
content-addressed apm_modules/ cache and re-emits a byte-identical lockfile.
apm install restores from the lockfile — served from cache (~10s vs ~56s
cold), and the lockfile is byte-stable. This is the git clone + apm install
promise. apm v0.23.1
$ apm install # bare = restore (no package argument)
[i] Targets: claude, copilot, cursor (source: apm.yml) # <- now from the manifest, not a flag
[+] microsoft/apm-sample-package #v1.0.0 @fb285168 (cached) # <- served from apm_modules/
|-- (files unchanged)
[+] <project root> (local) |-- 3 rule(s) adopted (files unchanged)
[*] Installed 1 APM dependency in 10.3s. # exit 0 (vs 56.0s cold)
# apm.lock.yaml is byte-identical -- generated_at unchanged: 2026-07-02T00:01:57...+00:00
That byte-stable lockfile is the proof behind the promise: the same declaration produces the same
bytes on any machine. Restore reproduces; it never chases newer versions. (A stricter,
CI-oriented cousin, apm install --frozen, refuses to resolve anything new and fails if
manifest and lockfile have drifted — the npm ci parallel, developed in
Chapter 6.)
Targeting at install time — no implicit “one true harness”
Install deploys into targets — the target-vs-harness split from
Chapter 3, now doing visible work. Which harnesses receive
files is resolved by an explicit precedence chain, not a hardcoded favorite:
--target > apm.yml targets: > a configured default >
auto-detection of harness directories already present
(apm install).
In the add above, the --target flag won (source: --target flag); on the next
bare install, the manifest won (source: apm.yml). The flag is a per-invocation
override — it did not write targets: into apm.yml. Pinning
targets: in the manifest is the recommended way to make deployment identical across
machines. And when there is nothing to target, APM does not guess a harness — it stops and
teaches (see the pitfalls below). This is Portability
made mechanical: one declared source set, many native destinations.
Run a declared script: apm run (experimental) and apm preview
The fourth verb executes a script from the manifest's scripts: block. It is stamped
experimental — “the run command surface is marked
experimental … flags and behavior may change before 1.0”
(apm run). It is also
more than literal shell: for a script like
copilot -p .apm/prompts/checkout-review.prompt.md, apm run
compiles the .prompt.md (frontmatter stripped) and injects the resulting
content into copilot -p <content> — the prompt file is compiled, not
passed verbatim. Then it shells out to the named runtime. Critically, APM does not ship the
runtimes: “the runtime CLI must be on your PATH”
(Run scripts).
apm preview is the safe, offline sibling: it compiles the script's prompt and writes it to
.apm/compiled/ without invoking any runtime — zero cost, ideal for docs and
CI where you only need to see what would run:
apm preview review compiles the prompt and invokes no runtime — offline and
zero-cost. apm v0.23.1
$ apm preview review # compile the prompt, run nothing
Original command: copilot -p .apm/prompts/checkout-review.prompt.md
Compiled command: copilot # <- the -p <file> is compiled out
Compiled prompt files: .apm/compiled/checkout-review.txt # <- prompt body, frontmatter stripped
[*] Preview complete! Use 'apm run review' to execute. # exit 0, no runtime invoked
You will run the script for real in the worked example below — where the harness-CLI requirement becomes the load-bearing caveat.
When to use / pitfalls
Restore or add? Pick by intent
Both jobs are the same command; the argument decides which. Choose by what you are trying to do, and remember that bare install reproduces — it does not upgrade.
| You want to… | Run… | Because |
|---|---|---|
| set up a freshly cloned repo | apm install (bare) |
restore reproduces the pinned set from the lockfile |
| add a new dependency to the project | apm install <pkg>#<ref> |
add writes it into apm.yml and re-locks — a reviewable diff |
re-materialize after editing apm.yml by hand |
apm install (bare) |
it replays the manifest so declared and on-disk state match again |
| move to newer versions | apm update (Chapter 7) |
restore never upgrades; version moves are deliberate and consent-gated |
Worked example
Meridian, committed as v0.2.0
Two everyday actions carried Meridian from the Chapter 4
manifest (v0.1.0: one local instruction, three targets) to v0.2.0: an add
pinned a public dependency, and a scripts: entry declared a review workflow. Here is the
clean, committable result — the exact file on disk after a restore, since restore never reflows
it:
apm.yml at v0.2.0 — the restore-stable authored form you commit
(one pinned dependency + one declared script). apm v0.23.1
name: meridian-checkout-agent-context
version: 0.2.0
description: Shared AI-agent context for the Meridian checkout service.
author: Meridian Checkout Team
targets:
- copilot
- claude
- cursor
dependencies:
apm:
- microsoft/apm-sample-package#v1.0.0 # added this chapter -- pinned, deterministic
mcp: []
includes: auto
scripts:
review: "copilot -p .apm/prompts/checkout-review.prompt.md" # apm run review -> the checkout-review prompt
With the script declared, apm list shows it, and apm run review executes it
— provided the copilot runtime is on PATH:
apm list renders the declared scripts from apm.yml.
apm v0.23.1
$ apm list
Available Scripts
Script Command
review copilot -p .apm/prompts/checkout-review.prompt.md
apm run review executing for real — experimental, and it needs
the copilot runtime CLI on PATH (the runtime does the work and incurs the
cost). apm v0.23.1
$ apm run review # experimental; runtime CLI must be on PATH
[>] Running script: review
Compiling prompt... +- .apm/prompts/checkout-review.prompt.md
Executing copilot runtime... Command: copilot Prompt content: 253 characters # <- content injected
... copilot runs the checkout review ...
[+] Copilot execution completed successfully (188.68s)
[*] Script executed successfully! # exit 0 (the runtime did -- and billed -- the work)
That is the loop, end to end: the team added a dependency and a script to reach v0.2.0, and
Anika restored the whole thing with git clone + apm install. Same
declaration, same bytes, in Copilot and Cursor alike —
Portability, materialized.
Recap & next
Recap
-
Install materializes the manifest.
apm.ymlis a recipe; install is the meal — it resolves, gates, scans, deploys, and writesapm.lock.yaml. This is where Portability becomes a fact, and it seeds Reproducibility as a by-product. -
The daily loop is four commands, one habit:
apm initonce,apm install <pkg>to add, bareapm installto restore,apm run <script>to run. Everything heavier is lifecycle (Chapter 7). -
One command, two jobs. Bare install restores (reproduce the pinned set,
cache-served, byte-stable);
apm install <pkg>adds (mutateapm.yml, re-lock). Restore reproduces — it never upgrades. -
Targets resolve by an explicit chain (
--target>apm.ymltargets:> configured default > auto-detect),--targetis a per-run override, and a no-target install exits2rather than guessing — there is no implicit “one true harness.” -
apm runis experimental and shells out to a runtime CLI you supply onPATH;apm previewcompiles the prompt offline at zero cost. A missing runtime is a harness problem, not anapmbug. -
Meridian reached v0.2.0 (one pinned dependency + one script), and Anika onboarded
with
git clone+apm install— the Chapter 1 drift, solved.
Next
Every install quietly wrote apm.lock.yaml, and every restore reproduced it byte for byte.
Chapter 6 — The Lockfile & Reproducibility
opens that file up: what resolved_commit and content_hash guarantee, how
apm install --frozen turns restore into a strict CI gate, and why byte-for-byte
reproduction is the property the whole supply-chain story rests on.