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 (replay unchanged intent from its lockfile). 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 a script that names a harness needs that runtime CLI on your PATH.
This is the operation that turns Chapter 4's declaration into
the same supported context in each tool's native format — the moment
Portability stops being a promise and becomes a
fact.
Edition 1.2 — current behavior: APM 0.31.0. Current examples below were
independently checked on 2026-09-16. The original full-package Meridian loop and live Copilot
recording remain explicitly historical v0.23.1 examples; their outputs, hashes, and timings have
not been relabeled. Use the
small current fixtures to follow along with this edition.
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
declared dependencies, materializes their content, and integrates eligible primitives and local
sources for the selected, supported targets. The resulting lockfile records installed state for
the next contributor
(APM 0.31.0 install reference).
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 its intent.
Portability is not cross-harness byte equality: Copilot instructions, Claude rules,
and Cursor rules can have different frontmatter while expressing the same source instruction.
Reproducibility compares each target's output with the corresponding output from the same sources,
lockfile, targets, and CLI version. 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. Think
resolve, policy gate, scan, integrate, lockfile: applicable policy and security checks gate
agent-readable deployment. Acquisition, cache, and staging writes can happen earlier; the scan
covers the authorized deployable content, not every file in a source repository. That boundary is
Chapter 8's subject; here it is enough to see install as
gated deployment, not a copy
(install behavior and safety).
The package and native-rule installs in this chapter record apm.lock.yaml, seeding
Reproducibility. An empty or compile-only
operation need not produce a lockfile; successful exit alone does not prove materialization.
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).
Scaffolding with apm init is one-time; the other three verbs 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).
With an unchanged manifest and matching lockfile, bare install is a restore:
it reuses the locked dependency graph. apm install <pkg> is an
add — it declares a dependency in dependencies.apm, installs it,
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, unchanged intent replays; changed intent
is reconciled. Editing a dependency's ref, changing local sources, or installing
without a lockfile can cause ordinary install to resolve or materialize something new. For a
deliberate refresh of an unchanged declaration, use the consent-gated apm update
workflow in Chapter 7. Do not confuse replay with refresh — but
do not expect an old lock to overrule a ref you explicitly changed, either
(locked replay and fresh resolution).
You already met apm init in Chapter 4: it
“creates a minimal apm.yml … so you can start running apm install
immediately” (apm
init). Plain apm init --yes in an empty directory still creates only
apm.yml — no source tree, deployment, or lockfile. Existing context has a separate
onboarding path below. Then the other three verbs do their usual jobs: 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. Its original recorded add pinned the direct
dependency, resolved its transitive graph, deployed supported primitives into all three harnesses,
and updated both apm.yml and the lockfile. This is materialization in action.
The following full-package recording remains historical; the
current exercise deliberately selects just one immutable public skill
rather than claiming to have rerun this larger graph.
Historical Meridian add: the full public sample and its transitive dependency, recorded
2026-07-02. The add changed the manifest and re-locked; the target flag won.
needs networkapm v0.23.1 — not rerun at 0.31.0
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 the materialized package tree). Separate deployed-file
hashes describe the native outputs. You do not author this file; APM
generates it. Chapter 6 studies it in full —
here, just notice it appear:
Historical generated lock excerpt for the full package — exact commit plus package-tree
hash, not a complete restore file. apm v0.23.1 — not rerun at 0.31.0
# 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 recorded lock pinned it by resolved_commit. Replaying that unchanged graph differs
from resolving it afresh without the lock — a
Chapter 6 concern, not an install failure in this
recording.)
Restore: bare apm install replays unchanged intent
Re-running apm install with no argument is the onboarding path from the Meridian beat.
With unchanged declarations and a matching lockfile, Git dependencies reuse the locked graph,
including transitive manifests. Local source files must also remain unchanged to reproduce their
outputs. An intact apm_modules/ tree or cache can serve a replay; a fresh clone may
need network access to hydrate the same locked source. Cache availability affects acquisition,
not which commit the lock selected.
Historical warm restore of the full-package fixture. Its lock bytes were unchanged; the
recorded timing and legacy generated_at are not current reproducibility tests.
apm v0.23.1 — not rerun at 0.31.0
$ 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
In 0.31.0, newly generated locks omit generated_at; legacy locks may retain it.
Compare complete lock bytes and the expected native outputs, not a clock. The current local and
public fixtures produced byte-stable locks on replay. Some restores now print
No changes -- install state already up to date; others still print an installed
dependency count. Neither wording is a universal completeness check
(lockfile and replay contract).
For CI, apm install --frozen requires an existing lock and performs
limited structural checks. It can still fetch locked content into a cold cache.
In 0.31.0, its direct-dependency presence check skips local paths: a newly declared local
dependency can still deploy and rewrite the old lock with exit 0
(frozen consistency check).
It is not an on-disk content audit; that separate job belongs to apm audit.
We use both in the current exercise,
then develop them in Chapter 6 and
Chapter 7.
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.ymltargets: > 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). With an existing manifest,
the flag is a per-invocation override; it does not replace the authored target list. Bootstrap is
different: a positional add with no manifest can create apm.yml and persist the supplied
targets. Pin and review targets: for a durable choice rather than relying on a flag
from someone's earlier command
(selection and bootstrap).
Targets authorize destinations; package restrictions can narrow that set, not expand it. When a
manifest's target set changes, install reconciles recorded ownership rather than treating whole
harness directories as disposable. The tested input was an eligible APM package with a co-located
legacy plugin.json marker. In the marker-free control, reducing four manifest targets
to Copilot removed stale managed outputs for the other targets while retaining Copilot's
outputs and the package source. Source files, surviving shared owners, and unrelated human-authored
context are not blanket cleanup targets
(deployment ownership).
This control omitted only pkg/plugin.json before first install; install and CI audit
exited 0 before and after contraction. With the legacy marker present, cleanup still
worked, but a 0.31.0 replay defect made CI audit expect legacy commands, exit 1, and
modify apm_modules/ (not authored sources, the lock, or deployed files).
Meridian's three targets consume native per-file rules. Do not generalize that to every profile:
some instruction targets, such as Codex, need explicit apm compile for aggregate
AGENTS.md context. The local-instruction-only Codex probe exited 0
without a lockfile or AGENTS.md; compilation was a separate step. Default compilation
also preserved an existing unmarked, hand-authored root file
(target matrix).
This is Portability with explicit boundaries,
not an implicit “one true harness.”
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 runcompiles 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).
That requirement follows the script: an ordinary shell script does not require an AI runtime.
apm preview is the compile-only sibling: it writes the script's compiled prompt to
.apm/compiled/without invoking the runtime. For a local prompt this is offline
and incurs no inference cost, ideal when you only need to inspect compilation. The current small
probe declares review: "copilot -p .apm/prompts/review.prompt.md"; its shorter filename
is separate from Meridian's checkout-review.prompt.md.
Current scripts-probe fixture: preview compiles a local prompt without running Copilot.
Windows output, display formatting condensed; observed exit 0. Offline.
apm v0.31.0 — verified 2026-09-16
$ apm preview review
Original command: copilot -p .apm/prompts/review.prompt.md
Compiled command: copilot
Compiled prompt files: .apm\compiled\review.txt
[*] Preview complete! Use 'apm run review' to execute.
A successful preview proves compilation, not a successful agent session. The historical worked
example below records a real Copilot invocation; it was not repeated for this update. Current
execution checks used only a harmless shell command and a deliberately missing executable.
Both jobs use the same command; the argument distinguishes add from the normal install path.
Choose by intent: unchanged manifest and lock mean replay, while edited declarations require
reconciliation.
Which form of apm install to reach for, by intent.
You want to…
Run…
Because
set up a freshly cloned repo
apm install (bare)
with the committed lock and sources, restore replays the pinned set
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 reconciles the changed declaration; the resolved state and lock may change
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. The
authored snapshot below preserves that story milestone. It and the following list/run recordings
belong to the original v0.23.1 worked example, not a fresh execution of the full graph on 0.31.0.
Historical Meridian apm.yml at v0.2.0 — one pinned full package plus one review
script. public dependency needs networkapm v0.23.1 — not rerun at 0.31.0
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
In that recording, apm list showed the declared script, and
apm run review invoked it with the copilot runtime available on
PATH:
Historical listing of the v0.2.0 manifest's script.
apm v0.23.1 — not rerun at 0.31.0
$ apm list
Available Scripts
Script Command
review copilot -p .apm/prompts/checkout-review.prompt.md
Historical real apm run review execution — experimental.
Copilot supplied the runtime and incurred the cost; this timing is not a current benchmark.
needs harness CLI and its service accessapm v0.23.1 — not rerun at 0.31.0
$ 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, supported context in Copilot's and Cursor's native forms —
Portability, materialized.
Edition 1.2: reproduce a small current setup
Now test the same materialization/replay boundary with small, controlled inputs. Use
APM 0.31.0,
not a moving latest release; the
official installation guide
covers the available installation channels. The book repository contains three fixtures under
backend/examples/updates/1.2/core/. They isolate behaviors without changing Meridian's
v0.2.0 milestone or substituting a new result for the historical full-package graph.
Start with local-instruction for an offline exercise. In a fresh, short-path scratch
directory outside the book checkout, copy the selected fixture's apm.yml and the
authored sources listed below, including hidden directories. For this first-materialization
exercise, start without apm_modules/, generated outputs, or a lockfile; keep the fixture's
recorded apm.lock.yaml separately for comparison. Real clone-and-restore onboarding
instead brings the committed lock along from the start. Do not run init over either copy.
Exact current fixture inputs and expected native files. All fixtures pin targets; these are
file-materialization checks, not live harness sessions. APM 0.31.0; independently checked 2026-09-16.
Fixture
Source accompanying apm.yml
Expected result
local-instruction — offline
.apm/instructions/meridian-checkout.instructions.md, the instruction from
Chapter 4
.github/instructions/meridian-checkout.instructions.md,
.claude/rules/meridian-checkout.md, and
.cursor/rules/meridian-checkout.mdc; the authored source stays in place.
.agents/skills/checkout-review/SKILL.md for Copilot; the original
Claude-layout skill remains the source, not a newly authorized Claude deployment.
pinned-skill — needs public Git access on a cold cache
No local source tree; the full manifest below selects one public skill at an immutable commit
.agents/skills/style-checker/SKILL.md shared by Copilot/Cursor, and
.claude/skills/style-checker/SKILL.md for Claude.
For the network-backed variant, this is the complete input manifest. Selecting only
style-checker avoids the full sample package's floating transitive dependency:
backend/examples/updates/1.2/core/pinned-skill/apm.yml — immutable source
identity and three explicit targets. needs network on a cold cacheapm v0.31.0 — verified 2026-09-16
# A single public skill at an immutable commit, with no transitive dependencies.
name: pinned-public-skill
version: 1.0.0
targets: [copilot, claude, cursor]
dependencies:
apm:
- git: microsoft/apm-sample-package
ref: fb2851683be0e0e7711421d518bd8dba23b0b1f6
path: .apm/skills/style-checker
mcp: []
includes: auto
In each selected scratch directory, materialize once, repeat with unchanged inputs, then check the
two different boundaries: frozen install's limited structural checks, with the
local-path exception, and CI audit's integrity checks.
These last two commands are a verification exercise, not extra everyday verbs to memorize:
Run consecutively inside each chosen current fixture copy. Independent execution confirmed
0 / 0 / 0 / 0 for install, replay, frozen install, and CI audit in all three.
Local fixtures are offline; the public skill needs network on a cold cache.
apm v0.31.0 — commands verified 2026-09-16
Check the native files in the table, not just the exit code. The repeated install and frozen
install left each complete lock byte-identical. A separate cold-cache public-skill test started
with only the manifest and lock: frozen install fetched the pinned content, exited 0,
and preserved the lock bytes. That is reproducible acquisition, not a promise of offline access.
Selected fields from the genuine pinned-skill/apm.lock.yaml — identity and
content, not a timestamp. Use the complete fixture lock for restore, never this excerpt.
apm v0.31.0 — verified 2026-09-16
None of these three newly generated locks contains generated_at. Their full files
also record deployments ownership and per-file hashes. The public skill's
version: unknown does not weaken its explicit Git commit, and its package-tree hash
is intentionally different from the historical full-package hash above. Top-level fields
need not be identical across lock types: the local-instruction-only lock has no
apm_version field.
Keep the hash meanings separate: deployed UTF-8 text hashes normalize CRLF line endings, whereas
package-tree hashes describe the actual materialized bytes. Neither means that APM rewrites all
source files, nor that different harness-native projections must have identical bytes
(lockfile hashes and ownership).
The payoff is still Anika's simple habit: review intent once, then restore and check the recorded
result instead of copying context by hand.
Install materializes the manifest.apm.yml is a recipe; install is the
meal — it resolves, gates, scans, and deploys supported content. The package and native-rule
examples record apm.lock.yaml; an empty or compile-only operation may not. 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 to scaffold once,
apm install <pkg> to add, bare apm install to restore,
apm run <script> to run. Everything heavier is lifecycle
(Chapter 7). Discovery with consented apply is a separate migration
aid: it declares existing packages without installing them.
One command, two jobs. Bare install replays unchanged intent from the locked
graph; apm install <pkg> adds a declaration and re-locks. Ordinary install
reconciles edited intent, and cold restores may need network. Compare lock bytes and native
outputs — new locks have no generated_at timestamp to compare.
Targets resolve by an explicit chain (--target > apm.ymltargets: > configured default > auto-detect). The flag overrides an existing
manifest for one run; bootstrap can persist it. Work needing a destination but lacking a target
exits 2, while mixed successful installs can include skips. Check what arrived.
apm run is experimental and executes the command the script names;
a harness script needs its CLI on PATH. apm preview compiles local prompts
without runtime cost, but proves neither agent execution nor installation completeness.
Meridian reached v0.2.0 (one pinned dependency + one script), and Anika onboarded
with git clone + apm install — the
Chapter 1 drift, solved.
Next
The current fixtures left you with genuine lockfiles and unchanged replay results.
Chapter 6 — The Lockfile & Reproducibility
opens that file up: what resolved_commit and content_hash guarantee, how
apm install --frozen supplies a limited structural CI gate with the
local-path exception, distinct from content audit, and why
reproducing the recorded state — not preserving a timestamp — supports the
supply-chain story.