diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7c476a71f..924feb1ae 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,15 +10,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
+- `includes:` manifest field (auto | list) for explicit governance of local `.apm/` content. Closes audit-blindness gap (#887).
+- `apm audit --ci` now verifies hash integrity of locally deployed files, detecting hand-edits and config drift. (#887)
+- `policy.manifest.require_explicit_includes` policy field enforces explicit `includes` lists (rejects `auto` + undeclared). (#887)
+- `includes-consent` advisory appears in `apm audit` CLI/JSON output when local content is deployed without an explicit `includes:` declaration (#887)
- `apm-primitives-architect` agent: reusable persona for designing and critiquing `.apm/` skill bundles. (#882)
- CI: add `APM Self-Check` to `ci.yml` for `apm audit --ci`, regeneration-drift validation, and `merge-gate.yml` `EXPECTED_CHECKS` coverage. (#885)
### Changed
+- Lockfile in-memory shape: a synthesized self-entry now appears in `LockFile.dependencies` for local content. The on-disk YAML format is unchanged (data still serialized as flat `local_deployed_files`/`local_deployed_file_hashes` fields). (#887)
- Hardened `apm-review-panel` skill: one-comment output contract, pre-arbitration completeness gate, Hybrid E Auth Expert routing, verdict template extracted to `assets/`, and `python-architect` mandatory three-artifact PR review contract (classDiagram + flowchart + Design patterns). (#882)
- CI: smoke tests in `build-release.yml`'s `build-and-test` job (Linux x86_64, Linux arm64, Windows) are now gated to promotion boundaries (tag/schedule/dispatch) instead of running on every push to main. Push-time smoke duplicated the merge-time smoke gate in `ci-integration.yml` and burned ~15 redundant codex-binary downloads/day. Tag-cut releases still run smoke as a pre-ship gate; nightly catches upstream codex URL drift; merge-time still gates merges into main. (#878)
- CI docs: clarify that branch-protection ruleset must store the check-run name (`gate`), not the workflow display string (`Merge Gate / gate`); document the merge-gate aggregator in `cicd.instructions.md` and mark the legacy stub workflow as deprecated.
+### Fixed
+
+- Audit blindness for local `.apm/` content -- `apm audit --ci` now detects drift, missing files, and content tampering on locally-authored files (not just installed packages). (#887)
+- Packer leak risk: local-content fields (`local_deployed_files`, `local_deployed_file_hashes`) are now stripped from bundled lockfiles, preventing phantom self-entries on unpack. (#887)
+
### Removed
- CI: deleted `ci-integration-pr-stub.yml`. The four stubs were a holdover from the pre-merge-gate model where branch protection required each Tier 2 check name directly. After #867, branch protection requires only `gate`, so the stubs are dead weight. Reduced `EXPECTED_CHECKS` in `merge-gate.yml` to just `Build & Test (Linux)`.
diff --git a/apm.lock.yaml b/apm.lock.yaml
index ebc5b1c48..3ceb90fca 100644
--- a/apm.lock.yaml
+++ b/apm.lock.yaml
@@ -33,16 +33,16 @@ local_deployed_file_hashes:
.github/agents/agentic-workflows.agent.md: sha256:d1ea2d038e2af8be11d6c95b3213b03b9777fae46f0438efa95d5a803e6c3765
.github/agents/apm-ceo.agent.md: sha256:dfc436e6eeffc7ec1c2f556edb78e4a5166ac36d162ea720d08b4b79af0a9938
.github/agents/apm-primitives-architect.agent.md: sha256:6c01eab74ba18d70f21d45010d636cc6535d63cee81da12e61898d8036e0b028
- .github/agents/auth-expert.agent.md: sha256:efdc8c7fd046409f4467ecf14da9f0d5f0e4a86372e5885c3763e89ff6f9ea69
+ .github/agents/auth-expert.agent.md: sha256:85409aab097cf239e5aa7ad61db6c4586be9884ef64a45fa9c894017b046b56b
.github/agents/cli-logging-expert.agent.md: sha256:24bf6c4b420c42292700ad0eb80b53d275be5c9cb186d471d706211f8419e3b9
.github/agents/devx-ux-expert.agent.md: sha256:3472680f43b2b4411b9437ec31529216afd4e576e1874c14430935e7f1ded1f2
.github/agents/doc-analyser.agent.md: sha256:47b1d0204904b786c19d4fe84343e86cdab6f92f862f676ba741ffe6e1385679
.github/agents/doc-writer.agent.md: sha256:328a5b9ea079869b8ccd914a6e2135c204225a5eedb42f59a1ec73058f7f0b47
.github/agents/oss-growth-hacker.agent.md: sha256:8d18f5be46913c40ad3aa66fb984575a88988cfac402d39353cdfb09f7e582c5
- .github/agents/python-architect.agent.md: sha256:80443a15945e39c56ae9d45983c2671eccc29b6dcb65bf328ca5dc8ecc87f48d
+ .github/agents/python-architect.agent.md: sha256:32ed3390cb0e41fea28b3fd95b00124cd097ea0db51f992d2349e6837742723c
.github/agents/supply-chain-security-expert.agent.md: sha256:9a4e731b12e7658f71d54c22e90f80ce0c45e3eacbb069b8505ed96ec9e79ba5
.github/instructions/changelog.instructions.md: sha256:1e51ec4c74e847967962bd279dc4c6e582c5d3578490b3c28d5f3acd3e05f73e
- .github/instructions/cicd.instructions.md: sha256:170e6fa09bcf4064d33420ffca6b3125bf7011982c4c7a00320af71f2f6c6bf9
+ .github/instructions/cicd.instructions.md: sha256:9c0fafc74f743aa97e5adba2168d66c9e3a327b135065e3b804bdbb5f04cda5d
.github/instructions/cli.instructions.md: sha256:8e39e8d5047ce88575cb02f87c2bcede584dfef258bd86f7466c7badf136541a
.github/instructions/doc-sync.instructions.md: sha256:bb3816254f8df6bffc6faacd556871f36903e9d7f348982f1e2de0339384c696
.github/instructions/encoding.instructions.md: sha256:93db7377dc896f6efecf2c5d8c5d89255a555562f468d034d64c42edd5cf46d5
diff --git a/apm.yml b/apm.yml
index 69a008238..dcfe211d0 100644
--- a/apm.yml
+++ b/apm.yml
@@ -1,6 +1,7 @@
name: apm
-version: 0.9.0
+version: 0.10.0
description: APM (Agent Package Manager) -- ship and govern AI agent context
+includes: auto
author: Microsoft
license: MIT
diff --git a/docs/src/content/docs/enterprise/apm-policy.md b/docs/src/content/docs/enterprise/apm-policy.md
index a3a5bf4c0..bb41048b1 100644
--- a/docs/src/content/docs/enterprise/apm-policy.md
+++ b/docs/src/content/docs/enterprise/apm-policy.md
@@ -92,7 +92,7 @@ Policy is evaluated at two points. Both use the same policy file and the same me
### CI time (audit gate)
-`apm audit --ci --policy org` runs the same checks (plus 6 baseline lockfile checks) and is intended as a required status check on pull requests. It produces SARIF output that GitHub Code Scanning renders inline on the PR diff.
+`apm audit --ci --policy org` runs the same checks (plus 7 baseline lockfile checks) and is intended as a required status check on pull requests. It produces SARIF output that GitHub Code Scanning renders inline on the PR diff.
For setup, see [CI Policy Enforcement](../../guides/ci-policy-setup/).
diff --git a/docs/src/content/docs/enterprise/governance-guide.md b/docs/src/content/docs/enterprise/governance-guide.md
index 84636bd96..9002353d6 100644
--- a/docs/src/content/docs/enterprise/governance-guide.md
+++ b/docs/src/content/docs/enterprise/governance-guide.md
@@ -85,12 +85,15 @@ The scope matrix below is the contract. Every row maps a security or operational
| Source attribution | `compilation.source_attribution` | `source-attribution` | `[i] audit-only` | Yes |
| Required manifest fields | `manifest.required_fields` | `required-manifest-fields` | `[i] audit-only` | Yes |
| Manifest scripts policy | `manifest.scripts` | `scripts-policy` | `[i] audit-only` | Yes |
+| Explicit manifest includes | `manifest.require_explicit_includes` | `explicit-includes` | `[i] audit-only` | Yes |
| Unmanaged files in governed dirs | `unmanaged_files.action`, `.directories` | `unmanaged-files` | `[i] audit-only` | Yes |
| Cache TTL override | `policy.cache.ttl` | -- | `[!] parsed but not enforced` (cache reader uses hardcoded 1h) | -- |
| Transitive MCP trust (policy field) | `mcp.trust_transitive` | -- | `[!] parsed but not enforced` (gate is the `--trust-transitive-mcp` CLI flag) | -- |
| Manifest content types | `manifest.content_types` | -- | `[!] parsed but not enforced` | -- |
-The full schema and the canonical 6+16 check enumeration live in the [Policy Reference](../policy-reference/). The 6 baseline lockfile checks (lockfile presence, ref consistency, deployed files present, no orphaned packages, MCP config consistency, content integrity) run on every `apm audit --ci` regardless of policy and are non-bypassable -- they are covered in section 7.
+The full schema and the canonical 7+17 check enumeration live in the [Policy Reference](../policy-reference/). The 7 baseline lockfile checks (lockfile presence, ref consistency, deployed files present, no orphaned packages, MCP config consistency, content integrity, includes consent) run on every `apm audit --ci` regardless of policy and are non-bypassable -- they are covered in section 7.
+
+`manifest.require_explicit_includes` (`bool`, default `false`) deserves a callout: when set to `true`, the `explicit-includes` check rejects any `apm.yml` that omits `includes:` or sets `includes: auto`. Use this when every published local file must be enumerated in the manifest and reviewable in PR diffs. See the [`includes` field](../../reference/manifest-schema/#39-includes) for the three accepted forms.
---
@@ -104,7 +107,6 @@ Be clear with stakeholders about what is out of scope today:
- **File integration paths.** Where files land on disk inside the repo is decided by the integrators in each APM package. Policy cannot rewrite a package's file layout.
- **Custom agent tools beyond MCP.** If an agent stack ships its own non-MCP tool plugins, they sit outside policy scope. Govern them indirectly through `dependencies.allow`/`deny` and `unmanaged_files`.
- **Token scopes and OAuth scopes.** APM does not audit the scope of the GitHub PAT or app token used to fetch policies and packages. Manage that through the standard GitHub controls on the token issuer.
-- **Drift on deployed files after install.** `deployed_file_hashes` are recorded in the lockfile but never re-verified. A developer who hand-edits a deployed instruction file post-install will not be detected by `apm install` or `apm audit --ci`. See section 14.
- **Anything `apm compile` or `apm run` does.** Those commands trust whatever `apm install` placed on disk. They do not re-check policy.
For the underlying threat model (what the content scanner protects against, MCP trust boundary, dependency provenance), see the [Security Model](../security/).
@@ -134,7 +136,7 @@ graph TD
DryRun["apm install --dry-run"] --> DryPre["[*] Preview only
install_preflight dry_run=True
'Would be blocked' lines, no raise"]
- Audit["apm audit --ci
--policy <scope>"] --> AuditRun["[*] Enforcement point 4
6 baseline + 16 policy checks
(only enforcer of audit-only fields)"]
+ Audit["apm audit --ci
--policy <scope>"] --> AuditRun["[*] Enforcement point 4
7 baseline + 17 policy checks
(only enforcer of audit-only fields)"]
style Start fill:#e3f2fd,stroke:#1976d2,stroke-width:2px,color:#000
style Gate fill:#f3e5f5,stroke:#7b1fa2,stroke-width:3px,color:#000
@@ -163,7 +165,7 @@ When you install an APM package that itself declares MCP dependencies, those MCP
### 5d. `apm audit --ci --policy `
-The only enforcer of the audit-only checks (`compilation-strategy`, `source-attribution`, `required-manifest-fields`, `scripts-policy`, `unmanaged-files`). Runs the 6 baseline lockfile checks unconditionally, then -- if a policy is discovered or supplied -- runs the 16 policy checks. This is the check you wire into branch protection.
+The only enforcer of the audit-only checks (`compilation-strategy`, `source-attribution`, `required-manifest-fields`, `scripts-policy`, `unmanaged-files`). Runs the 7 baseline lockfile checks unconditionally, then -- if a policy is discovered or supplied -- runs the 17 policy checks. This is the check you wire into branch protection.
---
@@ -272,25 +274,25 @@ This is the certitude section. Read it twice if you are deciding whether `apm au
| Surface | What it bypasses LOCALLY | What it CANNOT bypass | Reviewable in |
|---|---|---|---|
-| `apm install --no-policy` | All 16 policy checks at install (incl. transitive MCP, hash pin) | The 6 baseline checks in `apm audit --ci` | git diff of `apm.lock.yaml` in PR |
-| `APM_POLICY_DISABLE=1` env | Same as `--no-policy` plus the 16 audit policy checks | The 6 baseline checks in `apm audit --ci` | PR diff; CI env vars in Actions logs |
+| `apm install --no-policy` | All 17 policy checks at install (incl. transitive MCP, hash pin) | The 7 baseline checks in `apm audit --ci` | git diff of `apm.lock.yaml` in PR |
+| `APM_POLICY_DISABLE=1` env | Same as `--no-policy` plus the 17 audit policy checks | The 7 baseline checks in `apm audit --ci` | PR diff; CI env vars in Actions logs |
| Manual edit to `apm.lock.yaml` | Nothing; install regenerates the file each run | Audit baseline `ref-consistency` and `deployed-files-present` | git diff |
-| Manual edit to deployed file post-install | Content-equality drift (no re-hash) | Hidden-Unicode scan in `apm audit` content mode | git diff of the deployed file in PR |
+| Manual edit to deployed file post-install | Local file content until next audit | Audit baseline `content-integrity` (re-hashes deployed files); hidden-Unicode scan in `apm audit` content mode | git diff of the deployed file in PR |
| Direct `git clone` of an APM package, bypassing install | Everything; nothing detects out-of-band file drops | Audit baseline `no-orphaned-packages` and audit-only `unmanaged-files` | git diff |
| Fork repo to a personal org | Org policy auto-discovery (resolves to fork's `.github`) | Whatever your CI requires on the canonical repo | branch protection on canonical repo |
| `--trust-transitive-mcp` CLI flag | The transitive MCP preflight (second pass) | Direct MCP preflight; baseline content scan; audit MCP checks | CI command lines and Actions logs |
| `--allow-insecure` CLI flag | The HTTP-MCP refusal (lets a `http://` MCP through) | All `mcp.*` policy rules; audit MCP checks | CI command lines and Actions logs |
-| `apm install --force` | On-disk collision detection AND content-scan blocks | The 16 policy checks; baseline checks at next audit | CI command lines; PR diff of overwritten files |
+| `apm install --force` | On-disk collision detection AND content-scan blocks | The 17 policy checks; baseline checks at next audit | CI command lines; PR diff of overwritten files |
Notes on specific rows:
- **`apm install --no-policy`** also bypasses the `apm install --mcp` preflight, the transitive-MCP preflight, and any project-side `policy.hash` pin.
-- **`APM_POLICY_DISABLE=1`** short-circuits discovery to `outcome="disabled"` everywhere -- including `apm audit --ci`, where the 16 policy checks are skipped (the 6 baseline checks still run).
+- **`APM_POLICY_DISABLE=1`** short-circuits discovery to `outcome="disabled"` everywhere -- including `apm audit --ci`, where the 17 policy checks are skipped (the 7 baseline checks still run).
- **Manual lockfile edits**: `content_hash` mismatch on registry-proxy deps is caught at the next install when downloads resume.
- **Direct `git clone`**: `unmanaged-files` only flags governed dirs and only when configured to `warn` / `deny`.
- **Fork-to-personal-org**: discovery resolves via `git remote get-url origin`; branch protection on the upstream repo is the trust boundary.
-**The non-bypass contract.** The 6 baseline lockfile checks (run by `apm audit --ci` *without* `--no-policy` or `APM_POLICY_DISABLE=1`) are unconditional. They do not consult the policy file, do not depend on org discovery, and are not affected by either escape hatch. Combined with branch protection that requires `apm audit --ci` to pass, no developer override is invisible: a `--no-policy` install leaves a lockfile that audit will reject if the result is inconsistent, and an `APM_POLICY_DISABLE=1` audit run cannot itself bypass the baseline checks. Every override appears in the PR diff, in the workflow file, or in the Actions environment configuration -- all of which are reviewable in code review.
+**The non-bypass contract.** The 7 baseline lockfile checks (run by `apm audit --ci` *without* `--no-policy` or `APM_POLICY_DISABLE=1`) are unconditional. They do not consult the policy file, do not depend on org discovery, and are not affected by either escape hatch. Combined with branch protection that requires `apm audit --ci` to pass, no developer override is invisible: a `--no-policy` install leaves a lockfile that audit will reject if the result is inconsistent, and an `APM_POLICY_DISABLE=1` audit run cannot itself bypass the baseline checks. Every override appears in the PR diff, in the workflow file, or in the Actions environment configuration -- all of which are reviewable in code review.
**Workstation blast radius.** Because "file presence is execution" for agent files (an instruction or chat-mode file on disk is consumed by the agent runtime as soon as it is opened), the fork-to-personal-org bypass mitigates the *org's* trust gate but not the *individual workstation's*: between fork-clone-install and PR creation, the developer's machine already has the tainted files. Compensating control: an MDM-deployed mirror of `/.github/apm-policy.yml` consulted by a wrapper script around `apm install`, or a workstation-level allowlist of permitted git remotes for APM-managed repos.
@@ -312,7 +314,7 @@ When `apm install` returns exit code 0 and the effective org policy is in `enfor
You are NOT guaranteed:
-- That files on disk are still what install wrote. There is no drift detection on `deployed_file_hashes`.
+- That files on disk are still what install wrote. `apm install` itself does not re-verify deployed files; drift is caught by `apm audit --ci` baseline `content-integrity` (re-hashes against `deployed_file_hashes`).
- That prompt or instruction content is semantically safe. Only the hidden-Unicode scan runs.
- That the audit-only checks (`compilation-strategy`, `source-attribution`, `required-manifest-fields`, `scripts-policy`, `unmanaged-files`) passed. Run `apm audit --ci --policy ` in CI for those.
- That non-APM files in the repo conform to anything. APM only governs files it placed.
@@ -487,7 +489,6 @@ We publish this list because silent gaps are worse than known ones. Every item b
These are the sharp edges. Plan around them; do not assume they are solved.
-- **No drift detection on deployed files.** `deployed_file_hashes` are recorded in the lockfile but never re-verified by `apm install` or `apm audit --ci`. A developer who hand-edits a deployed instruction file post-install will not be detected. Operational mitigation: rely on git diff in PR review for files in `.github/`, `.apm/`, and other governed directories.
- **`policy.cache.ttl` field is parsed but not honored.** The cache reader uses a hardcoded 1-hour TTL. Setting `policy.cache.ttl: 86400` in your policy will be silently ignored. Operational mitigation: do not rely on this field; assume 1-hour cache TTL universally.
- **`mcp.trust_transitive` policy field is parsed but not enforced.** The transitive-MCP gate is the `--trust-transitive-mcp` CLI flag, NOT the policy field. Operational mitigation: govern transitive MCP trust through CI command lines and code review of workflow files, not through policy YAML.
- **`manifest.content_types` field is parsed but no check enforces it.** Operational mitigation: do not advertise this field as a control to stakeholders.
@@ -498,7 +499,7 @@ These are the sharp edges. Plan around them; do not assume they are solved.
- **Non-GitHub remotes are not auto-discovered.** If your project's `git remote get-url origin` points to ADO, GitLab, or a plain git host, policy auto-discovery falls through with no policy applied. Operational mitigation: pass `apm audit --ci --policy ` explicitly in those CI environments.
- **Trust anchor is `git remote get-url origin`.** A developer who pushes the project to a personal org will have policy discovery resolve `/.github/apm-policy.yml` -- which they control. Operational mitigation: branch protection on the canonical repo is the trust boundary; nothing about a personal fork can bypass what your CI requires before merge.
- **`apm install --dry-run` silently downgrades hash-mismatch.** In dry-run, `raise_blocking_errors=False` (outcome_routing.py:104-119) causes the mismatch to surface as `discovery_miss` with no "Would be blocked" line and exit 0. Operational mitigation: rely on `apm audit --ci` in CI for hash-pin verification, not on `apm install --dry-run`.
-- **`apm install --no-policy` help text is misleading.** It claims "Does NOT bypass apm audit --ci" -- this is only true for the 6 baseline lockfile checks; the 16 policy checks ARE bypassed in audit when this flag (or `APM_POLICY_DISABLE=1`) is set. Operational mitigation: do not rely on the help text; the bypass contract in section 7 is authoritative.
+- **`apm install --no-policy` help text is misleading.** It claims "Does NOT bypass apm audit --ci" -- this is only true for the 7 baseline lockfile checks; the 17 policy checks ARE bypassed in audit when this flag (or `APM_POLICY_DISABLE=1`) is set. Operational mitigation: do not rely on the help text; the bypass contract in section 7 is authoritative.
- **Gate + transitive-MCP preflight may double-emit the same MCP violation.** A single bad transitive MCP can produce two SARIF alerts with the same rule and different code paths. Operational mitigation: dedupe by `(rule_id, server_name)` when aggregating alerts in your SIEM or dashboard.
- **No signed attestation that the gate ran.** APM does not currently produce a signed (e.g. SLSA / sigstore) attestation for the install gate or the audit run. Non-repudiation depends on the GitHub Actions audit log plus branch-protection enforcement of the required check. Operational mitigation: pair APM with branch protection requiring `apm audit --ci` as a status check; rely on GitHub's audit log for auditor evidence.
@@ -598,7 +599,7 @@ policy:
- [`apm-policy.yml`](../apm-policy/) -- the file's mental model.
- [CI Policy Enforcement](../../guides/ci-policy-setup/) -- step-by-step CI wiring with YAML.
-- [Policy Reference](../policy-reference/) -- complete schema, the canonical 6+16 check enumeration, the 12-row merge rule table, exit codes.
+- [Policy Reference](../policy-reference/) -- complete schema, the canonical 7+17 check enumeration, the 12-row merge rule table, exit codes.
- [Security Model](../security/) -- threat model, MCP trust boundary, content scanning, token handling.
- [Adoption Playbook](../adoption-playbook/) -- broader APM rollout (governance is one phase).
- [Lockfile Spec](../../reference/lockfile-spec/) -- lockfile schema for forensic queries.
diff --git a/docs/src/content/docs/guides/ci-policy-setup.md b/docs/src/content/docs/guides/ci-policy-setup.md
index 0dc828fd3..18f7b841d 100644
--- a/docs/src/content/docs/guides/ci-policy-setup.md
+++ b/docs/src/content/docs/guides/ci-policy-setup.md
@@ -13,8 +13,8 @@ Set up automated policy enforcement so every pull request is checked against you
## Prerequisites
- An organization on GitHub with repositories using APM
-- `apm audit --ci` runs 6 baseline consistency checks with no configuration
-- `apm audit --ci --policy org` adds 16 policy checks defined in `apm-policy.yml`
+- `apm audit --ci` runs 7 baseline consistency checks with no configuration
+- `apm audit --ci --policy org` adds 17 policy checks defined in `apm-policy.yml`
For the full policy schema, see the [Policy Reference](../../enterprise/policy-reference/).
@@ -84,7 +84,7 @@ This catches lockfile/manifest drift, missing files, and hidden Unicode — with
## Step 3: Enable policy enforcement
-Add `--policy org` to run the full 16 policy checks on top of baseline:
+Add `--policy org` to run the full 17 policy checks on top of baseline:
:::note
Since this release, `apm audit --ci` auto-discovers the org policy. `--policy org` remains valid as an explicit override; use `--no-policy` to skip discovery.
diff --git a/docs/src/content/docs/integrations/ci-cd.md b/docs/src/content/docs/integrations/ci-cd.md
index 021305b51..5c2221e44 100644
--- a/docs/src/content/docs/integrations/ci-cd.md
+++ b/docs/src/content/docs/integrations/ci-cd.md
@@ -147,7 +147,7 @@ apm install
## Governance with `apm audit`
-`apm audit --ci` verifies lockfile consistency in CI (6 baseline checks, no configuration). Add `--policy org` to enforce organizational rules (16 additional checks). For full setup including SARIF integration and GitHub Code Scanning, see the [CI Policy Enforcement guide](../../guides/ci-policy-setup/).
+`apm audit --ci` verifies lockfile consistency in CI (7 baseline checks, no configuration). Add `--policy org` to enforce organizational rules (17 additional checks). For full setup including SARIF integration and GitHub Code Scanning, see the [CI Policy Enforcement guide](../../guides/ci-policy-setup/).
For content scanning and hidden Unicode detection, `apm install` automatically blocks critical findings. Run `apm audit` for on-demand reporting. See [Governance](../../enterprise/governance-guide/) for the full governance model.
diff --git a/docs/src/content/docs/reference/cli-commands.md b/docs/src/content/docs/reference/cli-commands.md
index 01b4691f5..cd1981167 100644
--- a/docs/src/content/docs/reference/cli-commands.md
+++ b/docs/src/content/docs/reference/cli-commands.md
@@ -424,7 +424,7 @@ apm audit [PACKAGE] [OPTIONS]
- `-v, --verbose` - Show info-level findings and file details
- `-f, --format [text|json|sarif|markdown]` - Output format: `text` (default), `json` (machine-readable), `sarif` (GitHub Code Scanning), `markdown` (step summaries). Cannot be combined with `--strip` or `--dry-run`.
- `-o, --output PATH` - Write report to file. Auto-detects format from extension (`.sarif`, `.sarif.json` → SARIF; `.json` → JSON; `.md` → Markdown) when `--format` is not specified.
-- `--ci` - Run lockfile consistency checks for CI/CD gates. Exit 0 if clean, 1 if violations found. Auto-discovers org policy from the org `.github` repo unless `--no-policy` is set.
+- `--ci` - Run lockfile consistency checks for CI/CD gates. Exit 0 if clean, 1 if violations found. Auto-discovers org policy from the org `.github` repo unless `--no-policy` is set. Runs the 7 baseline checks: lockfile presence, ref consistency, deployed files present, no orphaned packages, MCP config consistency, content integrity (Unicode + hash drift on every deployed file including local content), includes consent (advisory).
- `--policy SOURCE` - *(Experimental)* Override discovery: `org` (auto-discover from org), file path, or URL. Without this flag, `--ci` auto-discovers.
- `--no-policy` - Skip policy discovery and enforcement entirely. Equivalent to `APM_POLICY_DISABLE=1`.
- `--no-cache` - Force fresh policy fetch (skip cache). Only relevant with policy discovery active.
@@ -498,6 +498,7 @@ apm audit --ci --policy org --no-fail-fast
- **Critical**: Tag characters (U+E0001–E007F), bidi overrides (U+202A–E, U+2066–9), variation selectors 17–256 (U+E0100–E01EF, Glassworm attack vector)
- **Warning**: Zero-width spaces/joiners (U+200B–D), variation selectors 1–15 (U+FE00–FE0E), bidi marks (U+200E–F, U+061C), invisible operators (U+2061–4), annotation markers (U+FFF9–B), deprecated formatting (U+206A–F), soft hyphen (U+00AD), mid-file BOM
- **Info**: Non-breaking spaces, unusual whitespace, emoji presentation selector (U+FE0F). ZWJ between emoji characters is context-downgraded to info.
+- **Hash drift (`--ci` only)**: Files deployed by `apm install` whose on-disk SHA-256 no longer matches the value recorded in the lockfile (`deployed_file_hashes`). Covers content from package dependencies AND local `.apm/` content via the synthesized self-entry.
### `apm policy` - Inspect organization policy
diff --git a/docs/src/content/docs/reference/lockfile-spec.md b/docs/src/content/docs/reference/lockfile-spec.md
index 1c42bc202..8955d20b1 100644
--- a/docs/src/content/docs/reference/lockfile-spec.md
+++ b/docs/src/content/docs/reference/lockfile-spec.md
@@ -162,6 +162,41 @@ dependencies:
Lock files generated before this feature omit `content_hash`. APM handles this
gracefully — verification is skipped and the hash is populated on the next install.
+### 4.5 Self-Entry Convention
+
+For uniform traversal, the in-memory `dependencies` map includes a synthesized
+entry representing the host project's own local `.apm/` content. This entry is
+materialized on read and stripped on write -- it is **never serialized** to disk.
+
+The on-disk YAML format is unchanged: the host project's local content lives in
+the flat top-level fields `local_deployed_files` and `local_deployed_file_hashes`
+(see [section 4.4](#44-content-integrity) for the hashing scheme used for
+verification). `LockFile.from_yaml()` synthesizes the self-entry from those
+fields; `LockFile.to_yaml()` removes it before serialization. Round-trip is
+byte-stable.
+
+The synthesized entry MUST follow this convention:
+
+| Field | Value |
+|-------|-------|
+| Map key | `"."` (single dot) |
+| `repo_url` | `""` |
+| `local_path` | `"."` |
+| `source` | `"local"` |
+| `is_dev` | `true` |
+| `depth` | `0` |
+| `deployed_files` | populated from `local_deployed_files` |
+| `deployed_file_hashes` | populated from `local_deployed_file_hashes` |
+
+`is_dev: true` is non-negotiable. Plugin bundle exporters (`apm pack --format
+plugin`) skip dev dependencies; this flag ensures the host project's own content
+is excluded from distributable bundles via the existing dev-dependency filter,
+without requiring exporters to special-case the self-entry.
+
+Consumers iterating `dependencies` SHOULD treat the `"."` key as the host
+project. Consumers reading the on-disk YAML directly will not see this entry --
+they MUST read `local_deployed_files` and `local_deployed_file_hashes` instead.
+
## 5. Path Conventions
All paths in `deployed_files` MUST use forward slashes (POSIX format),
diff --git a/docs/src/content/docs/reference/manifest-schema.md b/docs/src/content/docs/reference/manifest-schema.md
index dde0a3922..573925e9b 100644
--- a/docs/src/content/docs/reference/manifest-schema.md
+++ b/docs/src/content/docs/reference/manifest-schema.md
@@ -48,6 +48,7 @@ license:
target:
type:
scripts: