Context
#827 shipped install-time enforcement of apm-policy.yml. The shared discover_policy_with_chain() helper in src/apm_cli/policy/discovery.py resolves the immediate parent via extends: but does not recurse: grandparent / enterprise-hub policies are silently dropped at install time.
apm audit --ci resolves the full multi-level chain via resolve_policy_chain(). Install time should match.
Proposal
In discover_policy_with_chain(), replace the single discover_policy(parent) call with a recursive walk that:
- Loads each parent in turn
- Detects cycles (use a
seen: set[str] keyed on the resolved repo URL or canonical ref)
- Caps depth at a sane constant (suggest
MAX_CHAIN_DEPTH = 8) and emits a clear error if exceeded
- Merges via the existing tighten-only rules (
merge_effective_policy())
- Stores the full chain refs in the cache
meta.json so cache invalidation reflects all hops
Acceptance criteria
Out of scope
- Diamond inheritance / multiple parents (separate concern; today
extends: is single-string)
- Lazy loading of intermediate policies for very long chains
- Signing / integrity verification of intermediate hops
References
Context
#827 shipped install-time enforcement of
apm-policy.yml. The shareddiscover_policy_with_chain()helper insrc/apm_cli/policy/discovery.pyresolves the immediate parent viaextends:but does not recurse: grandparent / enterprise-hub policies are silently dropped at install time.apm audit --ciresolves the full multi-level chain viaresolve_policy_chain(). Install time should match.Proposal
In
discover_policy_with_chain(), replace the singlediscover_policy(parent)call with a recursive walk that:seen: set[str]keyed on the resolved repo URL or canonical ref)MAX_CHAIN_DEPTH = 8) and emits a clear error if exceededmerge_effective_policy())meta.jsonso cache invalidation reflects all hopsAcceptance criteria
discover_policy_with_chain()policy-reference.mdandgovernance.mdOut of scope
extends:is single-string)References
apm installtime, not only inapm audit --ci#827 (install-time enforcement that introduced this gap)policy.fetch_failure: warn|blockschema knob — sibling follow-up)src/apm_cli/policy/discovery.py:discover_policy_with_chainsrc/apm_cli/policy/chain_resolver.py:resolve_policy_chain(the audit-side recursion to mirror)