Policy Reference
Complete reference for apm-policy.yml — the configuration file that defines organization-wide governance rules for APM packages.
Schema overview
Section titled “Schema overview”name: "Contoso Engineering Policy"version: "1.0.0"extends: org # Optional: inherit from parent policyenforcement: block # warn | block | offfetch_failure: warn # warn | block, default warn (org-side knob; see Section 9.5)
cache: ttl: 3600 # Policy cache TTL in seconds
dependencies: allow: [] # Allowed dependency patterns deny: [] # Denied dependency patterns require: [] # Required packages require_resolution: project-wins # project-wins | policy-wins | block max_depth: 50 # Max transitive dependency depth
mcp: allow: [] # Allowed MCP server patterns deny: [] # Denied MCP server patterns transport: allow: [] # stdio | sse | http | streamable-http self_defined: warn # deny | warn | allow trust_transitive: false # Trust transitive MCP servers
compilation: target: allow: [] # vscode | claude | cursor | opencode | codex | all enforce: null # Enforce specific target (must be present in list) strategy: enforce: null # distributed | single-file source_attribution: false # Require source attribution
manifest: required_fields: [] # Required apm.yml fields scripts: allow # allow | deny content_types: allow: [] # instructions | skill | hybrid | prompts
unmanaged_files: action: ignore # ignore | warn | deny directories: [] # Directories to monitorTop-level fields
Section titled “Top-level fields”Human-readable policy name. Appears in audit output.
version
Section titled “version”Policy version string (e.g., "1.0.0"). Informational — not used for resolution.
enforcement
Section titled “enforcement”Controls how violations are reported:
| Value | Behavior |
|---|---|
off | Policy checks are skipped |
warn | Violations are reported but do not fail the audit |
block | Violations abort apm install (exit 1) AND fail apm audit --ci |
extends
Section titled “extends”Inherit from a parent policy. See Inheritance.
| Value | Source |
|---|---|
org | Parent org’s .github/apm-policy.yml |
owner/repo | Cross-org policy from a specific repository |
https://... | Direct URL to a policy file |
fetch_failure
Section titled “fetch_failure”Org-side posture when consumers cannot fetch this policy AND have a stale cached copy. Optional. Default: warn.
| Value | Behavior |
|---|---|
warn | Loud warning emitted; install proceeds with the cached policy (or with no policy if cache is empty). Default. |
block | Fail-closed when a cached policy is available but a refresh fails. |
Consumers can opt into fail-closed semantics for the no-cache case from their apm.yml via policy.fetch_failure_default: block — see Network failure semantics for the full matrix and apm.yml policy block for the consumer-side fields.
Time-to-live in seconds for the cached policy file. Default: 3600 (1 hour). The cache is stored in apm_modules/.policy-cache/.
dependencies
Section titled “dependencies”Controls which packages repositories can depend on.
List of allowed dependency patterns. If non-empty, only matching dependencies are permitted.
dependencies: allow: - "contoso/**" # Any repo under contoso org - "contoso-eng/*" # Any repo directly under contoso-eng - "third-party/approved" # Exact matchList of denied dependency patterns. Deny takes precedence over allow.
dependencies: deny: - "untrusted-org/**" - "*/deprecated-*"require
Section titled “require”Packages that must be present in every repository’s apm.yml. Supports optional version pins:
dependencies: require: - "contoso/agent-standards" # Must be a dependency - "contoso/security-rules#v2.0.0" # Must be at specific versionrequire_resolution
Section titled “require_resolution”Controls what happens when a required package’s version conflicts with the repository’s declared version:
| Value | Behavior |
|---|---|
project-wins | Repository’s declared version takes precedence |
policy-wins | Policy’s pinned version overrides the repository |
block | Conflict causes a check failure |
max_depth
Section titled “max_depth”Maximum allowed transitive dependency depth. Default: 50. Set lower to limit supply chain depth:
dependencies: max_depth: 3 # Direct + 2 levels of transitiveControls MCP (Model Context Protocol) server configurations.
allow / deny
Section titled “allow / deny”Pattern lists for MCP server names. Same glob syntax as dependency patterns.
mcp: allow: - "github-*" - "internal-*" deny: - "untrusted-*"transport.allow
Section titled “transport.allow”Restrict which transport protocols MCP servers can use:
mcp: transport: allow: - stdio - streamable-httpValid values: stdio, sse, http, streamable-http.
self_defined
Section titled “self_defined”Controls MCP servers defined directly in a repository (not from packages):
| Value | Behavior |
|---|---|
allow | Self-defined MCP servers are permitted |
warn | Self-defined MCP servers trigger a warning |
deny | Self-defined MCP servers fail the audit |
trust_transitive
Section titled “trust_transitive”Whether to trust MCP servers declared by transitive dependencies. Default: false.
compilation
Section titled “compilation”target.allow / target.enforce
Section titled “target.allow / target.enforce”Control which compilation targets are permitted. With multi-target support, these policies apply to every item in the target list:
enforce: The enforced target must be present in the target list. Fails if missing (e.g.,enforce: vscoderequiresvscodeto appear intarget: [claude, vscode]).allow: Every target in the list must be in the allowed set. Rejects any target not listed.
compilation: target: allow: [vscode, claude] # Only these targets allowed enforce: vscode # Must be present in the target listenforce takes precedence over allow. Use one or the other.
strategy.enforce
Section titled “strategy.enforce”Require a specific compilation strategy:
compilation: strategy: enforce: distributed # or: single-filesource_attribution
Section titled “source_attribution”Require source attribution in compiled output:
compilation: source_attribution: truemanifest
Section titled “manifest”required_fields
Section titled “required_fields”Fields that must be present and non-empty in every repository’s apm.yml:
manifest: required_fields: - version - descriptionscripts
Section titled “scripts”Whether the scripts section is allowed in apm.yml:
| Value | Behavior |
|---|---|
allow | Scripts section is permitted |
deny | Scripts section causes a check failure |
content_types.allow
Section titled “content_types.allow”Restrict which content types packages can declare:
manifest: content_types: allow: - instructions - skill - promptsunmanaged_files
Section titled “unmanaged_files”Detect files in governance directories that are not tracked by APM.
action
Section titled “action”| Value | Behavior |
|---|---|
ignore | Unmanaged files are not checked |
warn | Unmanaged files trigger a warning |
deny | Unmanaged files fail the audit |
directories
Section titled “directories”Directories to scan for unmanaged files. Defaults:
unmanaged_files: directories: - .github/agents - .github/instructions - .github/hooks - .cursor/rules - .claude - .opencodePattern matching
Section titled “Pattern matching”Allow and deny lists use glob-style patterns:
| Pattern | Matches |
|---|---|
contoso/* | contoso/repo but not contoso/org/repo |
contoso/** | contoso/repo, contoso/org/repo, any depth |
*/approved | any-org/approved |
exact/match | Only exact/match |
* matches any characters within a single path segment (no /). ** matches across any number of segments.
Deny patterns are evaluated first. If a reference matches any deny pattern, it fails regardless of the allow list. An empty allow list permits everything not denied.
Check reference
Section titled “Check reference”Baseline checks (always run with --ci)
Section titled “Baseline checks (always run with --ci)”| Check | Validates |
|---|---|
lockfile-exists | apm.lock.yaml is present when apm.yml declares dependencies |
ref-consistency | Every dependency’s manifest ref matches the lockfile’s resolved ref |
deployed-files-present | All files listed in lockfile deployed_files exist on disk |
no-orphaned-packages | No lockfile packages are absent from the manifest |
skill-subset-consistency | skills: selections in apm.yml match skill_subset in the lockfile |
config-consistency | MCP server configs match lockfile baseline |
content-integrity | Deployed files contain no critical hidden Unicode characters and their SHA-256 hashes match the lockfile |
includes-consent | Advisory check that includes: selections in the manifest match what was deployed |
Policy checks (run with --ci --policy)
Section titled “Policy checks (run with --ci --policy)”Dependencies:
| Check | Validates |
|---|---|
dependency-allowlist | Every dependency matches the allow list |
dependency-denylist | No dependency matches the deny list |
required-packages | Every required package is in the manifest |
required-packages-deployed | Required packages appear in lockfile with deployed files |
required-package-version | Required packages with version pins match per require_resolution |
transitive-depth | No dependency exceeds max_depth |
MCP:
| Check | Validates |
|---|---|
mcp-allowlist | MCP server names match the allow list |
mcp-denylist | No MCP server matches the deny list |
mcp-transport | MCP transport values are in the allowed list |
mcp-self-defined | Self-defined MCP servers comply with policy |
Compilation:
| Check | Validates |
|---|---|
compilation-target | Compilation target matches policy |
compilation-strategy | Compilation strategy matches policy |
source-attribution | Source attribution is enabled if required |
Manifest:
| Check | Validates |
|---|---|
required-manifest-fields | All required fields are present and non-empty |
scripts-policy | Scripts section absent if policy denies it |
Unmanaged files:
| Check | Validates |
|---|---|
unmanaged-files | No untracked files in governance directories |
Inheritance
Section titled “Inheritance”Policies can inherit from a parent using extends. This enables a three-level chain:
Enterprise hub -> Org policy -> Repo overrideTighten-only merge rules
Section titled “Tighten-only merge rules”A child policy can only tighten constraints — never relax them:
| Field | Merge rule |
|---|---|
enforcement | Escalates: off < warn < block |
cache.ttl | min(parent, child) |
| Allow lists | Intersection — child narrows parent’s allowed set |
| Deny lists | Union — child adds to parent’s denied set |
require | Union — combines required packages |
require_resolution | Escalates: project-wins < policy-wins < block |
max_depth | min(parent, child) |
mcp.self_defined | Escalates: allow < warn < deny |
manifest.scripts | Escalates: allow < deny |
unmanaged_files.action | Escalates: ignore < warn < deny |
source_attribution | parent OR child — either enables it |
trust_transitive | parent AND child — both must allow it |
The inheritance chain is limited to 5 levels. Cycles are detected and rejected.
Example: repo override
Section titled “Example: repo override”# Repo-level apm-policy.ymlname: "Frontend Team Policy"version: "1.0.0"extends: org # Inherits org policy, can only tighten
dependencies: deny: - "legacy-org/**" # Additional deny on top of org policyExamples
Section titled “Examples”Minimal: deny-only policy
Section titled “Minimal: deny-only policy”name: "Block Untrusted Sources"version: "1.0.0"enforcement: block
dependencies: deny: - "untrusted-org/**"Standard org policy
Section titled “Standard org policy”name: "Contoso Engineering"version: "1.0.0"enforcement: block
dependencies: allow: - "contoso/**" - "contoso-oss/**" require: - "contoso/agent-standards" max_depth: 5
mcp: deny: - "untrusted-*" transport: allow: [stdio, streamable-http] self_defined: warn
manifest: required_fields: [version, description]
unmanaged_files: action: warnEnterprise hub with inheritance
Section titled “Enterprise hub with inheritance”name: "Enterprise Baseline"version: "2.0.0"enforcement: block
dependencies: deny: - "banned-org/**" max_depth: 10
mcp: self_defined: deny trust_transitive: false
manifest: scripts: denyname: "Contoso Policy"version: "1.0.0"extends: "enterprise-org/.github" # Inherits enterprise baseline
dependencies: allow: - "contoso/**" require: - "contoso/agent-standards" max_depth: 5 # Tightens from 10 to 5Install-time enforcement
Section titled “Install-time enforcement”1. What APM policy is
Section titled “1. What APM policy is”apm-policy.yml is the contract an organization publishes to govern which packages, MCP servers, compilation targets, and manifest shapes its repositories may use. The schema is documented above; this section covers how that contract is enforced at apm install time.
2. Discovery and applicability
Section titled “2. Discovery and applicability”APM auto-discovers policy from <org>/.github/apm-policy.yml for any GitHub remote — both github.com and GitHub Enterprise (GHE). Repositories on non-GitHub remotes (ADO, GitLab, plain git) currently fall through with no policy applied; this is tracked as a follow-up. Repositories with no detectable git remote (unpacked bundles, temp directories) emit an explicit “could not determine org” line and skip discovery.
The --policy <override> flag is audit-only today — it works on apm audit --ci but is not yet wired through apm install. Use the escape hatches in section 8 if you need to bypass install-time enforcement for a single invocation.
3. Inheritance and composition
Section titled “3. Inheritance and composition”Policy resolves through the chain documented in Inheritance above: enterprise hub -> org -> repo override. The merge is tighten-only: a child can narrow allow lists, add deny entries, and escalate enforcement, but never relax a parent constraint. The full merge rule table is in Tighten-only merge rules.
Install-time enforcement and apm audit --ci both resolve the full multi-level extends: chain (enterprise hub -> org -> repo, or any depth up to MAX_CHAIN_DEPTH = 5). The walker fetches each parent via the same single-policy fetcher used for direct discovery, so caching, retries, and source-prefix handling are consistent across levels. Cycles (A extends B, B extends A) are detected by tracking visited refs and abort the walk with a clear error. If a parent fetch fails midway, APM merges the policies it already resolved and emits a [!] Policy chain incomplete: <ref> unreachable, using <N> of <M> policies warning so the operator learns that an upstream policy was unreachable.
4. What gets enforced
Section titled “4. What gets enforced”Install-time enforcement runs the same rule families documented in Check reference:
- Dependencies —
allow,deny,require(presence + optional version pin),max_depth. - MCP —
allow,deny,transport.allow,self_defined,trust_transitive. - Compilation —
target.allow/target.enforce(target-aware, evaluated against the resolved target list). - Manifest —
required_fields,scripts,content_types.allow. - Unmanaged files —
actionagainst the configureddirectories.
5. When enforcement runs
Section titled “5. When enforcement runs”| Command | Behaviour |
|---|---|
apm install | NEW — runs the policy gate after dependency resolution and before integration / target writes. Blocks before any files are deployed. |
apm install <pkg> | NEW — snapshots apm.yml, runs the gate, rolls back the manifest on a block. |
apm install --mcp | NEW — dedicated MCP preflight on the --mcp branch. |
apm deps update | NEW — runs the install pipeline, so the same gate applies. |
apm install --dry-run | NEW — read-only preflight; renders “would be blocked by policy” verdicts without mutating anything. |
apm audit --ci | Existing — runs the same checks against the on-disk manifest + lockfile. |
pack and bundle are out of scope: they are author-side operations on packages being published, not consumers of dependencies.
6. Enforcement levels
Section titled “6. Enforcement levels”enforcement is documented in Top-level fields. The same three values (off / warn / block) apply at install time.
require_resolution: project-wins has a specific, narrow semantic that applies identically at install and audit time:
- It downgrades version-pin mismatches on required packages from a block to a warning. The repo’s declared version is honoured.
- It does NOT downgrade missing required packages — those still block under
enforcement: block. - It does NOT override an inherited org
deny— a parent’s deny always wins over a child’s allow or local declaration.
7. CLI examples
Section titled “7. CLI examples”All examples below use the literal output APM emits today. Symbol legend: [+] success, [!] warning, [x] error, [i] info, [*] summary.
Successful install with policy resolved
Section titled “Successful install with policy resolved”apm install (verbose) against an org publishing enforcement: block, all dependencies allowed:
$ apm install --verbose[i] Resolving dependencies...[i] Policy: org:contoso/.github (cached, fetched 12m ago) -- enforcement=block[+] Installed 4 APM dependencies, 2 MCP servers in 1.2sWithout --verbose, the Policy: line is suppressed for enforcement=warn and enforcement=off. Under enforcement=block it is always shown (rendered as a [!] warning) so users know blocking is active.
Block: denied dependency aborts the install
Section titled “Block: denied dependency aborts the install”$ apm install[i] Resolving dependencies...[!] Policy: org:contoso/.github -- enforcement=block[x] Policy violation: acme/evil-pkg -- Blocked by org policy at org:contoso/.github -- remove `acme/evil-pkg` from apm.yml, contact admin to update policy, or use `--no-policy` for one-off bypass[x] Install aborted: 1 policy check failed$ echo $?1The gate runs after dependency resolution and before any integrator writes files — apm_modules/ and target configs are untouched.
Warn: denied dependency renders, install succeeds
Section titled “Warn: denied dependency renders, install succeeds”Same denied dep, but the org policy ships enforcement: warn:
$ apm install[i] Resolving dependencies...[+] Installed 4 APM dependencies, 2 MCP servers in 1.2s
[!] Policy acme/evil-pkg -- Blocked by org policy at org:contoso/.github -- remove `acme/evil-pkg` from apm.yml, contact admin to update policy, or use `--no-policy` for one-off bypassViolations flow through DiagnosticCollector and surface in the end-of-install summary under the Policy category. Exit code is 0.
--no-policy flag: loud warning, install proceeds
Section titled “--no-policy flag: loud warning, install proceeds”$ apm install --no-policy[!] Policy enforcement disabled by --no-policy for this invocation. This does NOT bypass apm audit --ci. CI will still fail the PR for the same policy violation.[i] Resolving dependencies...[+] Installed 4 APM dependencies, 2 MCP servers in 1.2sAPM_POLICY_DISABLE=1 env var: identical wording
Section titled “APM_POLICY_DISABLE=1 env var: identical wording”$ APM_POLICY_DISABLE=1 apm install[!] Policy enforcement disabled by APM_POLICY_DISABLE=1 for this invocation. This does NOT bypass apm audit --ci. CI will still fail the PR for the same policy violation.[i] Resolving dependencies...[+] Installed 4 APM dependencies, 2 MCP servers in 1.2sThe warning is emitted on every invocation and cannot be silenced.
--dry-run with mixed allowed + denied + warn dependencies
Section titled “--dry-run with mixed allowed + denied + warn dependencies”Preview output is capped at five lines per severity bucket; overflow collapses into a single tail line:
$ apm install --dry-run[i] Resolving dependencies...[i] Policy: org:contoso/.github -- enforcement=block[!] Would be blocked by policy: acme/evil-pkg -- denylist match: acme/evil-pkg[!] Would be blocked by policy: acme/banned -- denylist match: acme/banned[!] Would be blocked by policy: vendor/old -- denylist match: vendor/old[!] Would be blocked by policy: vendor/legacy -- denylist match: vendor/legacy[!] Would be blocked by policy: third/party -- denylist match: third/party[!] ... and 2 more would be blocked by policy. Run `apm audit` for full report.[!] Policy warning: contrib/optional -- required-package missing version pin[i] Dry-run: no files writtenapm install <pkg> blocked → manifest unchanged
Section titled “apm install <pkg> blocked → manifest unchanged”apm install <pkg> mutates apm.yml before the pipeline runs. On a policy block, APM restores the manifest from a snapshot:
$ apm install acme/evil-pkg[i] Resolving dependencies...[!] Policy: org:contoso/.github -- enforcement=block[x] Policy violation: acme/evil-pkg -- Blocked by org policy at org:contoso/.github -- remove `acme/evil-pkg` from apm.yml, contact admin to update policy, or use `--no-policy` for one-off bypass[i] apm.yml restored to its previous state.[x] Install aborted: 1 policy check failed$ echo $?1Transitive MCP server blocked
Section titled “Transitive MCP server blocked”When a dep brings in an MCP server denied by mcp.deny or rejected by mcp.transport.allow, APM packages still install but MCP configs are not written:
$ apm install[i] Resolving dependencies...[!] Policy: org:contoso/.github -- enforcement=block[+] Installed 4 APM dependencies in 0.8s[x] Transitive MCP server(s) blocked by org policy. APM packages remain installed; MCP configs were NOT written.
[!] Policy contrib/sketchy-mcp -- transport `http` not in mcp.transport.allow=[stdio]$ echo $?18. Escape hatches
Section titled “8. Escape hatches”Non-bypass contract: every escape hatch below is single-invocation, is not persisted to disk, and does NOT change CI behaviour. apm audit --ci will still fail the PR for the same policy violation. These hatches exist to unblock local debugging, not to circumvent governance.
| Hatch | Scope |
|---|---|
--no-policy flag | Available on apm install, apm install <pkg>, and apm install --mcp. Skips discovery and enforcement for one invocation; emits a loud warning. Not currently exposed on apm deps update. |
APM_POLICY_DISABLE=1 env var | Equivalent to --no-policy. Same loud warning. |
APM_POLICY is reserved for a future override env var and is not equivalent to APM_POLICY_DISABLE.
9. Cache and offline behaviour
Section titled “9. Cache and offline behaviour”Resolved effective policy is cached under apm_modules/.policy-cache/. Default TTL is cache.ttl from the policy itself (3600 seconds). Beyond TTL, APM will serve a stale cache on refresh failure with a loud warning, up to a hard ceiling of 7 days (MAX_STALE_TTL). --no-cache forces a fresh fetch and ignores any cached entry. Cache writes are atomic (temp file + rename) to survive concurrent installs.
9.5. Network failure semantics
Section titled “9.5. Network failure semantics”When discovery cannot reach the policy source, APM behaves as follows:
- Cached, stale within 7 days — use the cached policy and emit a warning naming the cache age and the fetch error. Enforcement still applies.
- Cache miss or stale beyond 7 days, fetch fails — emit a loud warning every invocation; do NOT block the install by default, to keep developers unblocked when GitHub is unreachable. Opt in to fail-closed behaviour with
policy.fetch_failure: blockon the org policy (applies when a cached policy is available) orpolicy.fetch_failure_default: blockin the project’sapm.yml(applies when no policy is available at all). Both default towarn. - Garbage response (HTTP 200 with non-YAML body, e.g. captive portal HTML) — same posture as fetch failure: warn loudly by default, block when the project pins
policy.fetch_failure_default: block.
9.5.1. No-policy outcomes (no_git_remote / absent / empty)
Section titled “9.5.1. No-policy outcomes (no_git_remote / absent / empty)”Three additional outcomes describe “discovery succeeded but produced no enforceable policy”:
no_git_remote— the working tree has nooriginremote (shallow CI clone, ephemeral worktree, source pulled via tarball), so APM cannot derive an org to look up.absent— the resolved org has noapm-policy.ymlat the discovered source.empty— the file exists but parses to an empty policy (no rules).
These outcomes honour the same knob as fetch failures on both apm install and apm audit --ci:
warn(default):[!]warning on stderr explaining the cause; install / audit proceeds.block:[x]error on stderr; install raisesPolicyViolationError,apm audit --ciexits 1.
Explicit --policy <file> falls through these three outcomes — an opt-in pointer at a baseline file is treated as the authoritative source.
Example — consumer-side opt-in to fail-closed semantics in apm.yml:
name: my-projectversion: '1.0'policy: fetch_failure_default: block9.6. Hash pin: policy.hash (consumer-side verification)
Section titled “9.6. Hash pin: policy.hash (consumer-side verification)”The org-side fetch_failure knob does not protect against a successful 200 OK response that happens to return valid YAML constructed by a compromised mirror, captive portal, or man-in-the-middle. To close that gap, projects can pin the exact bytes they expect to receive from the org policy source — the pip --require-hashes equivalent for apm-policy.yml:
name: my-projectversion: '1.0'policy: hash: "sha256:6a8c...e2f1" # SHA-256 of the raw apm-policy.yml bytes hash_algorithm: sha256 # optional; sha256 (default), sha384, sha512Compute the digest from the canonical org-policy file:
shasum -a 256 .github/apm-policy.yml | awk '{print "sha256:" $1}'When set, every install / apm policy status / apm audit --ci verifies the hash of the fetched leaf policy bytes (UTF-8 encoded, before YAML parsing — so re-serialized semantically-equivalent YAML still fails). A mismatch is always fail-closed regardless of policy.fetch_failure / policy.fetch_failure_default. The pin applies only to the leaf policy; parents in an extends: chain remain the leaf author’s responsibility.
A malformed pin (unsupported algorithm, wrong length, non-hex) is rejected at parse time — silently ignoring it would defeat the security guarantee. MD5 and SHA-1 are not accepted.
Compute the pin on Linux with sha256sum .github/apm-policy.yml | awk '{print "sha256:" $1}'.
9.7. apm policy status: diagnostic snapshot
Section titled “9.7. apm policy status: diagnostic snapshot”Inspect the current policy posture without running an install or audit. The default exit code is always 0, so it is safe for human and SIEM use:
$ apm policy status APM Policy Status+--------------------+-----------------------------------+| Field | Value |+--------------------+-----------------------------------+| Outcome | found || Source | org:contoso/.github || Enforcement | block || Cache age | 12m ago || Extends chain | none || Effective rules | 3 dependency denies; 2 mcp denies |+--------------------+-----------------------------------+JSON output for CI / scripting:
$ apm policy status --json{ "outcome": "found", "source": "org:contoso/.github", "enforcement": "block", "cache_age_seconds": 720, "extends_chain": [], "rule_counts": { ... }, "rule_summary": ["3 dependency denies", "2 mcp denies"]}Flags:
--policy-source <ref>overrides discovery (path,owner/repo,https://..., ororg).--no-cacheforces a fresh fetch.--json/-o jsonswitches to JSON output.--checkexits non-zero (1) when no usable policy is found (anything other thanoutcome=found). Use this for CI pre-checks that must fail when org policy is unreachable or misconfigured. Default behaviour (without--check) remains exit-0.
$ apm policy status --check # exits 1 if outcome != "found"$ apm policy status --check --json # exit 1 + JSON body for CI tooling9.8. apm audit --ci auto-discovery
Section titled “9.8. apm audit --ci auto-discovery”When --policy (alias --policy-source) is omitted, apm audit --ci mirrors the install-time discovery path: it auto-discovers the org policy from the git remote, applying the same checks CI runs in production. Add --no-policy to skip discovery for a single invocation:
$ apm audit --ci # auto-discovers org policy$ apm audit --ci --policy <local> # explicit override$ apm audit --ci --no-policy # baseline checks only10. Error and exit-code reference
Section titled “10. Error and exit-code reference”Discovery outcomes
Section titled “Discovery outcomes”Each row maps a PolicyFetchResult.outcome to its exit impact, severity, the message APM emits, and the recommended fix.
| Outcome | Exit | Severity | Primary message | Remediation |
|---|---|---|---|---|
found | 0 (or 1 if checks fail under block) | info / block | Policy: <source> (cached, fetched Nm ago) -- enforcement=<level> | None; enforcement applied. Under block, fix violations or use --no-policy for one-off bypass. |
absent | 0 | info | No org policy found for <host_org> | None required. To publish one, see section 11. |
cached_stale | 0 (enforcement still applies) | warn | Policy: <source> (cached, fetched Nm ago) -- enforcement=<level> plus refresh-error warning | Restore network reachability or run with --no-cache once connectivity returns. |
cache_miss_fetch_fail | 0 | warn | Could not fetch org policy (<error>) -- policy enforcement skipped for this invocation | Retry, check VPN/firewall/gh auth status/GITHUB_APM_PAT. Fail-open by design (CEO-ratified); CI will still fail for the same violation. |
garbage_response | 0 | warn | Could not fetch org policy (invalid YAML body from <source>) -- policy enforcement skipped for this invocation | Likely a captive portal or auth wall returning HTML. Restore direct connectivity, then re-run. |
malformed | 0 (no enforcement) | warn | Policy at <source> is malformed -- contact your org admin to fix the policy file | Contact org admin to fix the YAML. Validate locally with apm audit --ci --policy <local-path>. |
manifest-parse | 1 (always) | error | Cannot parse apm.yml: <error> | Fix the YAML syntax error in apm.yml. This is a local audit check (not a fetch outcome) — malformed manifests always fail the audit unconditionally. |
disabled | 0 | warn | Policy enforcement disabled by --no-policy for this invocation. This does NOT bypass apm audit --ci. CI will still fail the PR for the same policy violation. | Single-invocation only. Drop the flag / env var to re-enable. |
no_git_remote | 0 | warn | Could not determine org from git remote; policy auto-discovery skipped | Run inside a checkout with a GitHub remote, or set the remote with git remote add origin <url>. |
empty | 0 | warn | Org policy is present but empty; no enforcement applied | Org admin should populate the policy file (see section 11) or remove it. |
hash_mismatch | 1 (always) | error | Policy hash mismatch: pinned hash does not match fetched policy. Update apm.yml policy.hash or contact your org admin. | Inspect the diff between expected and actual digest in the error output. If the org legitimately rotated the policy, recompute and update policy.hash in apm.yml. Otherwise, treat as a potential supply-chain compromise and contact your org admin. |
Violation classes
Section titled “Violation classes”When enforcement=block, any of the following exit 1 and abort before integration. When enforcement=warn, they render in the post-install summary under the Policy category and exit 0.
| Class | Origin | Primary message | Remediation |
|---|---|---|---|
denylist | dependencies.deny match | Policy violation: <dep> -- Blocked by org policy at <source> -- remove <dep> from apm.yml, contact admin to update policy, or use --no-policy for one-off bypass | Remove the dep from apm.yml, request an org-policy update, or --no-policy for one-off local debugging. |
allowlist | Dep not in non-empty dependencies.allow | Policy violation: <dep> -- not in dependencies.allow | Add the dep to the org allowlist or switch to an approved package. |
required | Missing dependencies.require entry, or pin mismatch | Policy violation: <required-dep> -- required by org policy but not declared in apm.yml (or ... required >=X but apm.yml pins <Y>) | Add the required dep to apm.yml (and pin the required version). Pin mismatches downgrade to warn under require_resolution: project-wins; missing required deps still block. |
transport | MCP transport not in mcp.transport.allow | Policy violation: <mcp-server> -- transport <t> not in mcp.transport.allow=[<list>] | Switch the server to an allowed transport, or request mcp.transport.allow updates. |
target | Resolved target not in compilation.target.allow (or violates target.enforce) | Policy violation: target <t> -- not in compilation.target.allow=[<list>] | Re-run with --target <allowed>, or update compilation.target in apm.yml. Evaluated post-targets phase, so CLI overrides are honoured. |
transitive_mcp | MCP server pulled in by a transitive dep, blocked by mcp.deny/transport/self_defined | Transitive MCP server(s) blocked by org policy. APM packages remain installed; MCP configs were NOT written. plus per-server Policy violation: ... | Remove the offending dep, request an org policy update, or set mcp.trust_transitive: true if the org chooses to allow transitive MCP entries. |
All violation messages above flow through InstallLogger.policy_violation; under block they print inline as [x] errors and exit 1. Use apm audit --ci --format json for the same set of findings in machine-readable form.
11. For org admins
Section titled “11. For org admins”Checklist to publish a policy:
- Create
<org>/.github/apm-policy.ymlin the org’s.githubrepository. - Start from the Standard org policy example above and trim it to the minimum that reflects your governance posture.
- Set
enforcement: warnfirst. Let CI surface diagnostics across consuming repos for one cycle without breaking installs. - When the warn-cycle is clean, switch to
enforcement: block. Communicate the change in your org’s CHANGELOG/announcements channel —apm installwill start failing for any non-compliant repo. - Use
extends:to layer team-specific policies on top of the org baseline rather than forking the file.
Recommended starter:
name: "<Org> APM Policy"version: "0.1.0"enforcement: warn
dependencies: allow: - "<org>/**" max_depth: 5
mcp: self_defined: warn
manifest: required_fields: [version, description]Related
Section titled “Related”- Governance — conceptual overview, bypass contract, and rollout playbook
- CI Policy Enforcement — step-by-step CI setup tutorial
- GitHub Rulesets — enforce policy as a required status check