Chapter 5

Install & Restore

Install and restore a project's agent context with the daily APM consumer loop.

Objective

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.

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:

The four-command consumer loop, each verb mapped to the moment it belongs to. Definitions from the consumer ramp (Use APM packages).
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:

Adding one pinned public dependency to Meridian. The add mutates 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:

The lockfile record install writes for the pinned dependency — exact commit + content hash. A generated artifact; never hand-edited. apm v0.23.1
# 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.

Bare 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.

Which form of apm install to reach for, by intent.
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:

Meridian's 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.yml is a recipe; install is the meal — it resolves, gates, scans, deploys, and writes apm.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 init once, apm install <pkg> to add, bare apm install to 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 (mutate apm.yml, re-lock). Restore reproduces — it never upgrades.
  • Targets resolve by an explicit chain (--target > apm.yml targets: > configured default > auto-detect), --target is a per-run override, and a no-target install exits 2 rather than guessing — there is no implicit “one true harness.”
  • apm run is experimental and shells out to a runtime CLI you supply on PATH; apm preview compiles the prompt offline at zero cost. A missing runtime is a harness problem, not an apm bug.
  • 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.