After this chapter you can use APM's pre-deployment scan, integrity checks, and scoped consent
controls while distinguishing their tested coverage from publisher authentication and runtime
sandboxing.
Version scope: book 1.2, apm v0.31.0, against the
previous reviewed baseline of v0.23.1. Current results come from the
operations probes
and core MCP probes.
Command excerpts below use apm as shorthand for the exact-version executable used in
those isolated tests, not a different release selected from your PATH.
A prompt is a program, so installation is the gate
Chapter 7 introduced apm audit as the on-demand integrity
tool — hidden-Unicode plus drift, explicitly not a CVE feed. The hidden-character
scanner also runs during installation: it checks the authorized deployable source
set before agent-readable deployment. Acquisition and cache writes can already have
happened. The distinction matters: the preventive boundary is activation, not the first disk write,
and a later audit answers a different question about the resulting state.
Start from a claim APM makes about the thing it manages: “Agent context is executable —
a prompt is a program for an LLM. APM treats it that way”
(the three
promises). An instruction file steers every generation; a prompt is a runnable procedure; a skill
tells the agent how; and an MCP server grants it tool and data access. Installing an
agent package therefore imports behavior the same way importing an npm package imports code —
which makes both its provenance (where it came from) and its content
(what the bytes actually say) security-relevant, not stylistic. This is the intuition
Chapter 1 named: “a prompt is, in effect, a program
for an LLM, so an unvetted prompt or transitive MCP server is real supply-chain surface.”
Now the timing. You might expect to install a dependency, inspect it, and only then run it.
Installation scripts already complicate that expectation in conventional package managers.
Agent context adds another problem: a watching harness may ingest a prompt as soon as it appears
in .github/prompts/ or another native context directory. There need not be a separate
“start” command. A post-install review cannot undo a read that already happened.
That is why the v0.31.0 security model
places scanning before deployment.
That single idea justifies the whole chapter. An unvetted prompt is unreviewed behavioral
input to an agent; an undeclared transitive MCP server is an unaudited
capability grant — network, filesystem, tool calls — that arrived as a side effect of
installing something else. Consent therefore needs a scope: which dependency, which capability,
and which destination? A clean scan or a familiar display name cannot answer all three.
The four properties still fit together. Chapters 4 and
5 established
Portability; Chapters
6 and 7 established
Reproducibility. Here, those declarations
and recorded hashes also support Provenance / security.
Chapter 9 adds organizational
Governance at the same installation boundary.
Each control implements part of the pre-deployment boundary.
They are not interchangeable: a content scan, an integrity comparison, a graph-depth decision,
and an executable approval can legitimately report different things.
APM v0.31.0 controls and their boundaries; the examples below separate documented intent from observed output.
Require scoped approval for code-bearing primitives
Broad gate is opt-in; plugin bin/ has additional consent rules
Compare the decision with actual deployment; the local MCP and parked-hook limits matter
Scan: hidden Unicode can hide instructions in plain sight
Text can contain characters that a reviewer cannot readily see. APM detects known hidden-character
classes; it does not decide whether the surrounding instruction is benign
(audit reference).
The safe controls below contain only benign character-class markers, not an injection payload.
Severity, bare-audit exit codes, and deployment consequences. Observed on apm v0.31.0 in OP08.1.
Severity
Illustrative control
Bare apm audit exit
Install consequence
Critical
Bidirectional override U+202E
1
Blocks affected deployment; the mixed-package probe exited 1
Warning
Zero-width space U+200B
2 (only if no Critical)
Reported, but deploys; the Warning-only install exited 0
Info
Legitimate emoji sequence
0 (shown with --verbose)
Non-blocking
Use apm audit --file for a standalone rules file or review document. This isolates the
character scan from project drift. The security fixture creates critical.md with a
benign U+202E marker and warning.md with U+200B.
No invisible characters are embedded in the chapter's code.
Standalone scan and strip controls from the security suite. Comments record observed
exits, not a literal stdout transcript. Local, inert test text.
apm v0.31.0
Warning-only is not automatically a CI failure. In a separate scratch copy of
operations/repro, the security suite adds a benign Warning marker to the source
instruction and generates a fresh lock through installation. The deployed content is flagged but
correctly hashed. Its bare audit exits 2; its CI audit exits 0.
Editing an already-deployed file instead would also introduce hash drift — a different test.
Here -f json selects machine-readable results, and --no-fail-fast
collects diagnostic coverage rather than stopping at the first failed check; neither disables a check.
Installed Warning-only content with matching integrity records; security suite,
warning-installed-bare/warning-installed-ci.
Observed on apm v0.31.0.
Deployment authorization also determines scan scope. A cache is not a directory of universally
approved content. Package declarations and selected primitives determine what may enter a harness;
the scanner checks that deployable source set.
Three inert OP08.1 controls that locate the scan boundary, observed on apm v0.31.0.
Controlled input
Observed result
Critical-class marker in an ordinary README outside the deployable primitives
Clean primitive installed; exit 0
Declared skill subset selects a clean skill, excluding a marker-bearing skill
Only the selected skill deployed; exit 0
One clean package and one with a Critical-bearing primitive
The last row is not an atomic all-or-nothing batch. Nor is a blocked deployment proof that no cache
or staging files were written. --force can bypass the Critical-finding block
(install reference);
it is an explicit break-glass exception, not normal remediation or a reason to run unsafe content.
For remediation, --strip --dry-run previews the selected file changes without writing
them. --file plus --strip rewrites the named file. Project-mode stripping
cleans deployed copies, not the package source: the probes observed the marker returning on frozen
restore. A durable fix changes the reviewed source or selects a clean upstream revision.
Pin: reproducibility records also detect tampering
Chapter 6's lockfile gives an integrity check a
reference point. Keep two hash contracts distinct: a package-tree content_hash covers
sorted relative paths and actual materialized bytes; deployed UTF-8 text hashes
normalize CRLF to LF. That normalization does not rewrite every source byte, and
different harness representations need not have identical hashes
(lockfile specification).
The CRLF-only control passed CI with unchanged lock bytes; an ordinary text edit did not.
The local operations/repro fixture has one dependency and Copilot, Claude, and Cursor
instruction projections. Its runner appends only Benign fixture edit. to a scratch
deployed instruction. This tests changed bytes without introducing a malicious instruction:
After the inert edit to .github/instructions/library.instructions.md: ordinary drift
is advisory in bare audit and a failure in CI. The repro suite also checks Claude
and Cursor. Observed on apm v0.31.0.
A hash mismatch establishes change, not its cause. Review the source and the lock together:
someone able to replace both content and its expected hash is not authenticated by that hash.
apm install --frozen remains a structural manifest/lock control with the bounded cases
discussed in Chapter 6, not a comprehensive
native-file integrity audit.
Other audit results are not ordinary drift. Observed on apm v0.31.0 in OP07.2 and OP08.1–2.
Controlled state
Bare audit
CI audit
Invalid canonical deployment owner or active owner
1
1
Critical-class marker in an unrecorded governed file, whole-project audit
1
1
Unrelated user-owned hook added to merged Claude settings
Not exercised
0
APM-owned hook sidecar edited or missing
Not exercised
1
Ownership is part of the contract: not every edit to a shared native settings file is APM drift.
Also keep audit scope explicit. In an independent v0.31.0 rerun of the Critical control above,
apm audit ./package -f json scanned the dependency's three deployed files and
excluded the unrelated unrecorded marker, exiting 0. The earlier
_local/package selector matched no deployed files; its green exit was not evidence
of a scoped scan. A whole-project claim needs a whole-project check. Check names and findings
are useful evidence; a fixed “eight checks” total is not an API.
Withhold: distinguish direct from transitive MCP
MCP trust uses dependency-graph depth, not filesystem nesting. A package listed in
your manifest is direct, at depth 1. A dependency of that package is transitive, at
depth 2. “A dependency declares this server” therefore does not by itself
mean “this is a transitive server.” The
MCP trust model
distinguishes these choices.
Configuration-only self-defined MCP controls, observed on apm v0.31.0; no server was launched.
Declaration
Observed result
Server supplied by a direct package dependency, depth 1
Trusted by the depth rule and configured; install exit 0
Self-defined server supplied at depth 2, not re-declared by the consumer
Candidate withheld with re-declaration guidance; ordinary skills installed; exit 0
Consumer explicitly re-declares the named server in dependencies.mcp
Named server configured; the transitive-candidate warning still appeared; exit 0
The headline is withholding, not a build break. Re-declaration makes one capability
a reviewable manifest decision. The documented --trust-transitive-mcp alternative
opts into transitive self-defined servers for that invocation; it is broader and does not add the
same explicit manifest entry. Prefer the narrow, reviewed declaration. A warning alone does not
prove the server is absent: another authorized declaration may have configured it.
These controls do not establish the behavior of every registry-resolved or private-source MCP path.
Inspect the correct destination. Copilot CLI and VS Code MCP are separate targets in this release:
Native paths observed in the inert MCP fixture on apm v0.31.0, OP08.3.
Runtime
Project file
Container
Copilot CLI
.github/mcp.json
mcpServers
VS Code
.vscode/mcp.json
servers
Claude Code
.mcp.json
mcpServers
Gate: executable approval is a separate decision
The broad Executable Trust Gate is opt-in through an
executables: block — even executables: {} — or applicable
organization executable policy. Its documented scope is dependency hooks, bin/,
self-defined MCP, and canvas extensions. Text primitives are outside this particular approval gate,
not free of behavioral risk. Their scanning and review still matter
(approval reference).
Use apm approve --list and apm policy explain to inspect deciding layers;
use apm approve or apm deny to record a reviewed decision. This asks which
executable kinds a dependency may supply, whereas MCP depth asks whether the capability was
directly chosen or inherited. Keep the mechanisms separate.
Plugin bin: invocation consent does not imply deployment
Do not turn the broad gate's opt-in status into “all executables deploy unconditionally.”
Plugin bin/ has additional explicit invocation consent. The v0.31.0 project-scope
probes observed:
Project-scope bin consent outcomes, OP08.3; observed on apm v0.31.0 without running an executable.
Invocation condition
Observed outcome
Noninteractive default
Exit 0; bin executables skipped as not trusted
Explicit --trust-bin
Exit 0; diagnostic said global scope was needed for Claude Code bin deployment
--no-trust-bin or the tested frozen invocation
Exit 0; no bin deployment
Actual global/user-scope deployment and its permissions were not exercised.
Do not widen scope merely to make a test appear successful. The
target contract
also says invocation consent cannot override policy denial. Neither a consent flag nor an
exit 0 is proof that a binary was installed or is safe to run.
Use a check for the question it can actually answer. Rely on the install scan before authorized
deployment, audit recorded state after materialization, and review each capability before granting
consent. Do not substitute a green command for inspection of the relevant output.
Keep credential authority separate too. In the registry probes, a user/project destination-binding
mismatch withheld credentials but could still permit an anonymous request; it did not
universally block network access. Authentication over HTTP was refused. These tests used only a
public non-credential marker. Do not put secrets in manifests or locks, and do not assume a
placeholder in one native MCP file proves no plaintext was materialized elsewhere
(OP08.3 and core MCP observations).
When a control reports a limit, keep it visible. Do not disable drift checking, broaden trust,
or switch to global installation just to produce a clean exit. Review the dependency, inspect
the relevant native files and lock records, and retain harness-level restrictions.
Back to the beat. Meridian's staff engineer adds checkout-review-pack at
v0.3.0. The pack is useful markdown, but it depends on checkout-review-tools,
which declares local-fetch. The depth-two control
demonstrates the withhold-with-warning boundary: useful package content can install while that
self-defined MCP candidate is withheld.
needs networkSKIPPED-needs-network: the private
meridian-finance/checkout-review-pack fetch needs unavailable private repository
authorization; it is not an executed production transcript.
Meridian reviews the tool and makes v0.3.1's decision explicit: re-declare the reviewed
local-fetch connection in the consumer's dependencies.mcp, rather than
blanket-trusting transitive servers. Then inspect the native destinations and lock records.
If the review rejects the capability, remove the unwanted dependency instead of granting consent.
Either way, Chapter 1's invisible capability has become a
decision the team can review.
1. Use a direct local control to test the other gate
The following is deliberately a direct dependency control, not a recreation of
Meridian's private depth-two graph. The complete v0.31.0 operations/trust fixture contains two
manifests, an ordinary instruction, and a hook whose only command is a harmless echo.
Its MCP command is intentionally nonexistent. No harness or MCP server is started.
Consumer apm.yml from the local trust fixture. The empty executable block
opts into the broad gate; it does not certify withholding. Observed
apm v0.31.0.
The dependency's package/apm.yml, alongside its existing .apm/ fixture
files. This self-defined entry configures a connection without executing it.
Observed apm v0.31.0.
In the isolated noninteractive test, installation returned success. Inspection reported
default-deny for both executable kinds. That was not the final deployment state:
Decision/output comparison in trust-limit, with the OP08.3 explanation probe.
Known local-path limitation, not an audit-clean pre-approval example.
apm v0.31.0
The instruction deployed; the unapproved hook did not.
The inspected .github/mcp.json nevertheless contained the local
operations-trust-fixture connection. The lock recorded MCP configuration even
while the dependency's exec_status was gated_pending_approval.
CI replay incorrectly expected the parked hooks, reporting unintegrated for
.claude/apm-hooks.json, .claude/settings.json, and
.github/hooks/package-notice.json.
These are two upstream limitations: MCP output disagreed with the executable decision, and
parked-hook replay failed. They are not network skips. The separate MCP-only control's clean
audit also means that “CI passed” cannot prove this approval boundary was enforced.
3. Record reviewed consent, without disguising the limitation
For this inspected inert fixture only, the approval control records consent and reinstalls.
It demonstrates how the approved state is represented; approving an unwanted real dependency
merely to silence an audit failure would be the wrong response.
Canonical local-path approval followed by installation and audit; all three returned
0 in the inert trust-limit control.
apm v0.31.0
Approval inspection now reported project-allow. A separate denial control reported
project-deny and reproduced the parked-hook replay failure. Deny ceilings remain part
of the intended trust ladder, but a decision label is not a substitute for observing deployment.
The clean approved control does not erase the pre-approval defect.
4. Reproduce the boundary without starting a service
Use the complete fixture runner, not an install in this book's root. It requires Python 3.10+,
PyYAML 6, and an already prepared absolute APM 0.31.0 executable. It copies fixtures into a new
short scratch directory, isolates HOME/config/cache, removes inherited credentials, and records
results there. Self-defined MCP conflict detection can still attempt a registry identity lookup;
these probes bound it to an unavailable loopback endpoint with short timeouts. They do not
demonstrate that MCP configuration is intrinsically network-free.
Reproduction entry point for the recorded trust-limit suite. Run from the book
checkout; APM operations run only in scratch. The suite reproduces a known failure, not a
successful MCP-withholding guarantee. apm v0.31.0
$Apm = '<absolute path to the prepared APM 0.31.0 executable>'
python .\backend\examples\updates\1.2\operations\verify.py --apm $Apm --suite trust-limit
The same runner provides the security, repro, and hooks
controls cited earlier. Those use only inert markers, ordinary text edits, or unexecuted harmless
hook definitions. None of this exercise requires a real server, credentials, global installation,
or an agent session.
Scan before authorized deployment. Agent-readable file presence can activate
context. APM scans the deployable source set, not the whole cache before any write; a mixed
package batch can partially deploy and still exit 1.
Severity and command mode both matter. Critical findings give bare-audit
exit 1; Warning-only gives 2. Correctly hashed installed Warning
content passed CI with 0. Stripping deployed copies is not a durable source fix.
Hashes detect change within a defined scope. Deployed text normalizes line
endings for hashing; raw package-tree hashes have a different contract. Ordinary instruction
drift was advisory in bare audit and failed CI; invalid ownership failed both. Neither a hash
nor frozen restore proves publisher trust or complete native-config coverage.
MCP depth and executable consent are separate. Direct means graph depth
1; the tested depth-two candidate was withheld without failing install.
Re-declaration is a narrow, reviewable choice. In the direct local executable-gate control,
MCP configured despite default-deny, and parked hooks exposed a replay failure.
Consent is not confinement. Canonical approvals, lifecycle-script trust,
and plugin-bin invocation consent have distinct scopes. APM is not a runtime sandbox, and
the checksum-verified Windows executable inspected here reported NotSigned.
Next
The natural next question is organizational: which sources, scopes, and primitives are allowed
here, and how do you enforce that reviewed choice?
Chapter 9 — Governance & Policy adds the configurable
layer through apm-policy.yml:
Governance at install time, with tighten-only
inheritance and a warn→block rollout. Policy needs a protected enforcement point and evidence
that the intended rule ran; it does not turn these controls into runtime sandboxing.