After this chapter you can use APM's maintenance triad — apm outdated,
apm update, and apm audit — to keep dependencies current deliberately,
separating freshness, permission to change, and integrity, and reviewing
actual manifest, lockfile, and deployment outcomes rather than trusting a successful exit alone.
Edition 1.2 targets exactly APM 0.31.0. Captions marked “observed” refer
to recorded command-and-file checks in the
operations research;
source-inspected rules are distinguished below. The retained Meridian excerpt is an exact
historical v0.23.1 snapshot, not a current transcript or a new verification stamp.
Chapter 6 gave you a recorded dependency graph
to replay. But a frozen graph, left alone, quietly ages. Upstream skills gain fixes, prompts get
sharper, instructions track new conventions — and a project pinned six months ago can be
reproducible and stale. The failure mode from
Chapter 1 inverts: instead of silent drift you get silent
staleness. Reproducibility is a floor, not a ceiling.
So the obvious next worry is: if everything is pinned, how does anything move forward
safely? Use the same separation you want from an application package manager:
Inspect availability — apm outdated reports what is newer upstream.
Authorize a change — apm update previews and applies a permitted refresh.
Check the result — apm audit checks recorded integrity and deployment consistency.
These are three different questions. A newer release can be outside your constraint. An allowed
update still needs review. A current version can have a broken local deployment. The loop protects
Reproducibility by making change deliberate,
not by making change impossible.
This chapter uses lifecycle in that maintenance sense, not for the entire
scaffold-to-runtime flow. It sits alongside Chapter 5's
four-command consumer habit. The
consumer guide likewise
groups update, outdated, and audit as lifecycle automation.
The cadence is yours to choose; it is not a reason to refresh dependencies on every restore.
The three-question model remains useful in 0.31.0, but it is
not an absolute side-effect guarantee. In particular, “update” need not move a version,
and “verify” may need to acquire missing locked content.
The maintenance triad at APM 0.31.0: intended job and observed operational boundaries.
Verb
Question
State and network boundary
apm outdated
Report: what is newer upstream?
Queries sources without changing project files. Unreachable or unresolved sources can be unknown.
apm update
Change: which permitted refresh do you accept?
May change manifest, lock, cache, and deployments; may instead be a no-op or repair. Queries upstream. Trusted lifecycle scripts add side effects.
apm audit
Verify: does recorded state agree with the installation?
Diagnosis, not ordinary repair. Replay uses scratch/cache; CI hydration and policy discovery can need network. Explicit stripping is a separate mutating operation.
Report: freshness is not permission
apm outdated starts with the lockfile's dependency inventory, not a scan of the deployed
bytes. A floating Git branch is not automatically outdated: when its locked commit still equals
the current tip, there is nothing newer to report. Once the tip moves, the report can change even
though your manifest, lock, and deployed files have not.
Do not extend that observation into “pinned dependencies never show outdated.” The
target-tag reference
describes matching-tag comparisons for Git tags and comparison with eligible annotated semver tags
for full-SHA revision pins. The separate
Git revision-update contract
can propose a reviewed SHA rewrite to the commit behind an eligible stable annotated semver tag,
not an arbitrary branch tip or lightweight tag. Those special Git-tag rules are
source-inspected here, not a newly executed tag-publication experiment.
A pin records current identity; it does not hide later availability.
Experimental registry rows make the distinction especially clear:
Current is the locked version, Wanted is the highest version
satisfying the manifest constraint, and Latest is the highest published parseable
version under the registry's ordering. Latest can be outside the constraint and can be a prerelease.
Wanted uses declarations from the root or installed package manifests: the inventory is lock-based,
but Wanted is not independent of your declarations.
Observed 0.31.0 values from the operations/registry loopback fixture before updating;
all three rows report outdated. The manifest
supplies one exact pin and two ranges.
Package
Selector
Current
Wanted
Latest
operations/exact
1.0.0
1.0.0
1.0.0
2.0.0-beta.1
operations/range
^1.0.0
1.0.0
1.1.0
2.0.0-beta.1
operations/other
^1.0.0
1.0.0
1.1.0
2.0.0-beta.1
registry: fixture (outside constraint) explains why a published Latest is not a
requested upgrade. Even the exact pin is reported outdated, while its Wanted remains
1.0.0. Registry comparison adds the Wanted column; Git-only output keeps
Current / Latest / Status / Source, and mixed Git rows use - for Wanted.
This is a shipped experimental client, not evidence of a Microsoft-hosted public
registry; the exercise uses only a controlled loopback source.
Finding outdated dependencies exits 0: this is an informational report, not a build
gate. Do not treat unknown as proof of freshness, either. Local dependencies are skipped.
The inspected command has neither a package filter nor a JSON-output flag — the JSON option
used below belongs to audit.
Change: review the permitted resolution
apm update is the recommended consented refresh of existing declarations, not the only
operation capable of changing resolution. Explicit manifest edits followed by install, and
install or lock refresh modes, can also change it. The distinction from
Chapter 5 is replaying unchanged intent versus
reconciling changed intent, not a magical guarantee attached to one verb.
Preview with apm update --dry-run; a package argument can narrow the refresh.
The observed selected plan correctly reported
1 updated, 2 unchanged. Separate exact-registry and Git-semver controls reported
All dependencies already at their latest matching refs. A plan need not overstate
movement or print an unknown destination. A correct plan is useful evidence, but it is not a
substitute for checking the applied state.
--yes accepts a plan; it does not widen a constraint. In the registry
control, an all-package update advanced allowed ranges to 1.1.0 and kept the exact
1.0.0 pin. Noninteractive selected change without --yes exited
1 with Cannot prompt for confirmation in non-interactive shell.
The interactive default of No is the
documented contract;
an unattended job must handle the observed noninteractive refusal as a failure, not a successful decline.
Nor does every successful update move a version or rewrite the lock. An unchanged-ref update
restored an empty materialized cache without consent or lock mutation. An MCP-only accepted update
repaired missing native configuration; its preview did not. These are repairs, not dependency
bumps. Dependency maintenance is also distinct from upgrading the APM executable itself.
Verify: know what the gate actually checked
Audit combines content scanning with consistency checks and an install replay. CI mode checks
hashes where the lock supplies a baseline; replay compares expected deployment with the working
tree. Canonical ownership adds a separate question: does each recorded owner still name a valid
dependency or supported workspace/bundle owner? This implements
Chapter 6's recorded-state model, not upstream
freshness.
Two modes on the same deliberately edited Copilot rule in the operations repro
fixture: advisory audit exits 0; CI audit exits 1. Neither repairs it.
Offline local-source control. apm v0.31.0 — observed
Here bare audit means the mode without --ci, even when you request JSON.
Its JSON reported "passed": true while stderr reported ordinary drift.
That result is not contradictory: ordinary replay drift is advisory in this mode.
--ci is the integrity gate.
Isolated APM 0.31.0 observations from the operations repro and security controls,
not a universal three-exit-code formula.
Condition
Bare audit
CI audit
Clean recorded local project
0
0
Benign modified deployed rule
0, drift advisory
1, hash/replay drift
Invalid canonical owner / active owner
1
1
Replayed file has no recorded ownership, even though bytes agree
Not measured in this control
1, unrecorded
Critical character in an unrecorded governed file
1
1
Warning-only U+200B content, installed and correctly hashed
2
0
Warning-only content does not, by itself, fail CI; adding a hash mismatch would be a different
condition. Invalid ownership is also not merely advisory drift: it fails both modes.
In a repair control, apm prune removed invalid owner rows, but the next audit still found
an unrecorded file. Install then restored complete ownership and the audit passed. Removing bad
metadata is not proof that every deployment discrepancy has been repaired.
Ten checks ran in the clean local fixture, including deployment-ledger-owners,
content-integrity, and drift. Ten is an observation, not an API
constant. Empty projects, configuration, policy, and early failures can change the total.
--no-fail-fast requests diagnostic coverage after the first failure;
-f json changes the report format
(baseline checks).
Read check names, findings, and scope, not a fixed “all nine passed” string.
The source-inspected flag contract
distinguishes whole-project audit from a package selector or --file: the latter
narrow scanning and skip replay. --no-drift explicitly reduces replay coverage;
it is not a repair. The earlier _local/package control returned 0 but
selected no files, so it proved no successful scan. Chapter
8's corrected ./package control records three scanned files instead.
Check the selected input and coverage as well as the exit code, and keep the full-project gate
for the maintenance loop.
Cold is not the same as offline
A warm or local-source audit can work offline. Bare replay remains cache-only; the target reference
documents an informational skip on a cache miss. CI mode instead can hydrate a
lock-pinned scratch replay when modules are absent. A public cold-cache control
exited 0 without checkout writes; making its source unreachable exited 1,
not a harmless green skip. A local-source cold control succeeded without remote acquisition.
Policy discovery can require network too. “Audit is always offline” is obsolete.
Those unchanged-checkout results are scoped observations, not a guarantee for every package form.
Chapter 5 records a marker-present plugin replay defect that
even mutated materialized cache files. The MCP-only operations control also returned CI
0 for missing or edited native MCP configuration. Inspect actual native artifacts;
an audit is not a comprehensive native-config byte comparator or runtime health test.
Likewise, a fixture's “enforcement skipped” warning is not evidence of organization
policy compliance. The observed default policy-fetch/hash-failure posture can leave baseline CI
green, and APM_POLICY_DISABLE=1 suppressed even an explicit policy source.
Chapter 9 explains the authority and bypass boundaries;
Chapter 11 protects the required job's inputs.
Do not turn a baseline integrity result into a wider security or governance certificate.
With matching declarations, a lock, and unchanged local sources, ordinary install replays recorded
state; use the triad to review a refresh. If you deliberately change a selector, install reconciles
that new intent: in the registry probe,
editing operations/exact from 1.0.0 to 1.1.0, preserving its
registry declaration, then running apm install moved it to 1.1.0 with exit
0. That is explicit permission, unlike merely seeing a newer Latest.
apm install --frozen is neither a read-only command nor a universal declaration-change
veto. It requires a lock and performs limited structural checks; in 0.31.0 a newly declared local
dependency can still deploy and rewrite it. Keep
Chapter 5's local-path exception in mind,
and use Chapter 6 for the full restore boundary.
Audit separately. Do not hand-edit the generated lock to manufacture consistency.
Cleanup: preserve source, then inspect what remains
Removal and target contraction are reconciliation, not permission to erase whole harness folders.
Preserve local sources, surviving shared owners, and unrelated human-authored content. The
target uninstall reference
and release notes describe stronger deletion-failure reporting. The observed Windows behavior
below does not satisfy every part of that promise.
1; edited file, declaration, and lock retained. Some module cleanup had already happened: no blanket rollback.
Exclusive Windows OS lock on a materialized file; apm uninstall ./package
0 and Uninstall complete, but the locked file remained. The dependency declaration and ownership were removed; the dependency-only fixture lost its lockfile.
0 with a warning; the edited stale target file remained.
A directory occupies a required native MCP JSON path; apm install
1; repeating with --force still failed. Force is not general I/O recovery.
The Windows locked-file case is a reproduced limitation, not a network skip and not proof that
every uninstall fails. Do not equate a success message with absence of artifacts. Inspect remaining
files and the manifest/lock before choosing a retry: after the declaration is gone, the same
identifier can fail selection. Preserve edits and resolve the specific filesystem problem rather
than deleting source or broad target directories to make a check look clean.
Meridian's first monthly refresh, as a pull request
The staff engineer still follows the normal triad: report, review a change, verify immediately.
To make “newer upstream” repeatable without pushing a live release or editing a lock,
this exercise uses a tiny registry fixture. It is a laboratory for Meridian's maintenance habit,
not a migration of the team's Git-based setup to a new service.
Use the registry suite of the
operations fixture runner
with the prepared absolute APM 0.31.0 executable. It copies the fixture and genuine lock to a new
short scratch directory, isolates HOME/cache, and enables registries only there. It serves anonymous
reads on 127.0.0.1:18431, with no publishing endpoint or credentials.
The fixed port is part of the recorded URLs; if it is busy, do not stop an unrelated process.
Do not run these fixture mutations in your book checkout.
backend/examples/updates/1.2/operations/registry/apm.yml — complete fixture
manifest. One exact pin, two ranges, one target; experimental registry support is enabled by
the isolated runner. loopback network only.
apm v0.31.0 — observed fixture
The runner first restores and audits the genuine all-1.0.0 lock while the source offers
only that version. It then exposes 1.1.0 and 2.0.0-beta.1.
The Current / Wanted / Latest table now has a concrete cause.
Anonymous HTTP loopback is a supported fixture exception, not permission to send credentials over
HTTP to a real registry.
The triad, with preview and an explicit selection, in the operations registry suite.
The first three commands exit 0; the immediate audit exits 1
for the cache-state failure explained below. loopback network only.
apm v0.31.0 — observed
apm outdated
apm update operations/range --dry-run --verbose
# Review the plan before applying.
apm update operations/range --yes
apm audit --ci --no-fail-fast -f json
Selected preview from the operations registry fixture, condensed from
registry-selected-plan: accurate movement, two unchanged dependencies, no project
writes in this script-free control. loopback network only.
apm v0.31.0 — observed excerpt
The lock diff agrees with that plan. But the immediate audit finds something that the plan and
lock alone cannot prove: modified: .github/instructions/other.instructions.md.
Inspecting the three layers explains it:
Observed state immediately after the selected registry update at APM 0.31.0, before repair.
Dependency
Locked version
Deployed text
Materialized cache
operations/range
1.1.0
1.1.0
1.1.0
operations/other (unselected)
1.0.0
1.0.0
1.1.0
operations/exact
1.0.0
1.0.0
1.0.0
This is a reproduced selected-registry-update cache contamination failure in 0.31.0.
The unselected dependency kept its locked pin and deployed bytes, but its cache advanced.
content-integrity passed because the deployed bytes still matched the lock;
cache-based drift replay failed. This does not establish that all updates are broken,
nor is it an unavailable-network case. It explains why a successful update and a plausible diff
must not be silently presented as a clean result.
In this controlled state, restoring from the unchanged contract repaired the cache:
Operations registry repair: frozen install restores the two cached
other files to 1.0.0 without moving the lock; the following full audit
exits 0. Both commands exit 0.
loopback source available for repair.
apm v0.31.0 — observed
Keep the failed audit as well as the repair evidence. Do not replace it with
--no-drift, hand-edit the lock, or upgrade the unselected package just to make the
discrepancy disappear. The repair establishes recovery for this fixture, not a universal
repair guarantee for every audit failure.
Read a change, not a clock
New 0.31.0 locks omit generated_at; unchanged legacy locks can retain it, and
Git-semver resolved_at has a different purpose. Neither a timestamp nor an
“updated N” counter proves movement. Compare complete lock bytes or hashes and the
relevant file inventories
(recorded identity and content).
Timestamp-free evidence at APM 0.31.0. Each row compares its own before/after state;
the public Git no-op is a separate observed snapshot, not a promise about future releases.
Control
Complete lock bytes
Other observed evidence
Selected registry preview
Unchanged
Recorded project file-hash inventory unchanged.
Selected registry apply
Changed
Only the selected locked version moved; the unselected cache discrepancy still failed audit.
Frozen registry repair
Unchanged
Two cache files restored; deployed bytes unchanged; subsequent CI audit 0.
Public Git ^1.0.0 preview and apm update --yes
Unchanged in both
Both exited 0 with matching-ref no-op messages; recorded project file-hash inventories unchanged. needs public network
The pull request's review surface is broader than a lock diff alone: apm.yml shows
intent, apm.lock.yaml records resolution and ownership, and the audit plus actual
artifacts test the result. An unchanged manifest does not by itself prove pins held; an unchanged
lock does not by itself prove a clean cache. That is the practical payoff of keeping all three
questions separate.
Historical Meridian lock diff — APM 0.23.1 only
The original 2026-07-02 experiment reconstructed an older checkout by manually setting the
transitive's starting commit to its genuine earlier value. The subsequent update was real,
but that simulated setup is not an authoring recipe. This exact retained
excerpt records one commit change and the old timestamp-bearing format; the timestamp is not
the current proof of change. Do not recreate the experiment by editing a generated lock.
Exact historical apm.lock.yaml diff excerpt, including its original annotations.
Public-source update after a simulated older starting lock, recorded 2026-07-02.
needs public networkapm v0.23.1 — historical snapshot, not rerun at 0.31.0
--- apm.lock.yaml (before update)
+++ apm.lock.yaml (after update)
-generated_at: '2026-07-02T01:17:41.266376+00:00'
+generated_at: '2026-07-02T01:23:19.474392+00:00' # advanced: update rewrote the resolved content
- repo_url: github/awesome-copilot # the transitive #main dep
- resolved_commit: a4aebcd4bd354b59a6a6c12ab9c5c98a9d9e0276 # old (behind)
+ resolved_commit: 3169734bc2fb25d5e092130fc93d24b0dee3ac3a # new (branch tip)
Reproducible is a floor, not a ceiling. Keep the report → reviewed change
→ verify loop, with an owner and a cadence. Meridian chose monthly.
Availability is not permission. Registry Current, Wanted, and Latest answer
different questions. An exact pin can be outdated without moving under update; an explicit
selector edit followed by install is new intent.
Update is not always a version change. Accurate unchanged plans, no-ops, and
repair-only outcomes exist. Preview can still execute trusted lifecycle scripts; inspect that
boundary rather than assuming no side effects.
Audit is scoped integrity evidence. Ordinary bare-audit drift is advisory;
invalid owners fail both modes; correctly hashed Warning-only content can pass CI. Cold CI
replay can need network, and check totals vary. It is not a vulnerability feed.
Review actual state, not clocks or success messages. The selected registry
update's cache failure and Windows uninstall leftovers are real 0.31.0 limits. Preserve failure
evidence, protect source, and verify a deliberate repair; do not suppress checks to manufacture
a clean refresh.
Next
Audit was your first look at the checks around agent-context integrity.
Chapter 8 — Security by Default opens the install-time
pipeline: scanning authorized deployable content before agent-readable deployment, content-hash
checks, and scoped executable/MCP trust. Cache and staging writes can happen earlier, and none of
these checks is runtime sandboxing. That is the next layer of
Provenance / security, with its guarantees and
observed limits kept separate.