[BUG] audit --ci: detect local-path MCP declaration removal (symmetric source-vs-lock diff)#2145
Open
danielmeppiel wants to merge 3 commits into
Open
[BUG] audit --ci: detect local-path MCP declaration removal (symmetric source-vs-lock diff)#2145danielmeppiel wants to merge 3 commits into
danielmeppiel wants to merge 3 commits into
Conversation
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR tightens apm audit --ci's config-consistency check so it detects removed transitive MCP server declarations (lock-only entries) by building a complete current declaration view and performing a symmetric source-vs-lock comparison.
Changes:
- Expand
config-consistencyto resolve MCP declarations from the root manifest plus lock-bounded dependency manifests (including local-source deps), then diff name sets in both directions. - Add integration + unit regression coverage for transitive MCP resolution (local source, installed remote) and for the removed-declaration failure case.
- Update docs to describe the broadened declaration graph and provenance semantics.
Show a summary per file
| File | Description |
|---|---|
| src/apm_cli/policy/ci_checks.py | Builds a complete “current MCP” view from root + locked deps and performs symmetric source-vs-lock comparison. |
| tests/unit/policy/test_ci_checks.py | Adds unit coverage for transitive MCP resolution paths and updates diagnostics expectations. |
| tests/integration/test_transitive_mcp_audit_e2e.py | Adds an end-to-end regression test ensuring removal of a transitive MCP block fails audit --ci. |
| docs/src/content/docs/reference/lockfile-spec.md | Clarifies provenance’s purpose (diagnostic attribution). |
| docs/src/content/docs/reference/baseline-checks.md | Documents the broader declaration graph and updated failure modes (needs a small correction per comments). |
Review details
- Files reviewed: 5/5 changed files
- Comments generated: 2
- Review effort level: Low
Comment on lines
+263
to
+266
| details.append( | ||
| f"{locked_dep.repo_url}: cannot parse local package manifest ({exc}) -- " | ||
| "fix the manifest or re-run 'apm install'" | ||
| ) |
Comment on lines
+96
to
99
| - **What it verifies.** That the complete current MCP declaration graph -- the root manifest, current local-package sources, and lock-bounded installed package manifests -- matches the `mcp_configs` baseline stored in the lockfile. | ||
| - **Fails when.** A server's resolved config differs from the lockfile, a source declaration was added, or a previously locked declaration was removed. | ||
| - **Exception.** A lockfile-only server is expected when `mcp_config_provenance` identifies the sub-package that contributed it, so those transitive servers are not treated as orphans. | ||
| - **Remediation.** Run `apm install` to reconcile the MCP configuration. |
apm-spec-waiver: Symmetric source-vs-lock audit closes a removal-detection gap in existing lockfile consistency semantics; no new OpenAPM artifact or wire behavior. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fix(audit): detect removed transitive MCP declarations
TL;DR
apm audit --cinow builds the complete current MCP declaration view before comparing it withapm.lock.yaml. The comparison is symmetric, so changed, added, and removed server declarations all failconfig-consistency; unchanged local and installed package declarations remain clean.Note
Fixes #2136. If #2132 merges first, rebase this branch because both changes touch
src/apm_cli/policy/ci_checks.py.Problem (WHY)
dependencies.mcpblock, the server remained inmcp_configs, butapm audit --ciexited 0 with "All checks passed". See [BUG] apm audit --ci misses local-path sub-package MCP declaration REMOVAL (exits 0); change-drift exits 1 #2136._check_config_consistencybuiltcurrent_configsfrom only the root manifest, then exempted every lock-only server carrying provenance.The fix keeps the gate based on deterministic source and lock state: "Grounding outputs in deterministic tool execution transforms probabilistic generation into verifiable action."
Reproduction
./packages/agent-configwhose manifest declares MCP servershadcn.apm audit --ci --no-policy --no-fail-fast; it exits 0.dependencies.mcpblock.config-consistencyreportsshadcn: in lockfile but not in current source (declared by agent-config)and exits 1.Approach (WHAT)
mcp_config_provenanceonly to identify the declaring package in diagnostics, never as a removal exemption.Implementation (HOW)
src/apm_cli/policy/ci_checks.py_check_config_consistencyto resolve the complete declaration view and perform a symmetric source-vs-lock diff.tests/integration/test_transitive_mcp_audit_e2e.pytests/unit/policy/test_ci_checks.pydocs/src/content/docs/reference/baseline-checks.mddocs/src/content/docs/reference/lockfile-spec.mdDiagram
Legend: The dashed nodes are the declaration-resolution and symmetric-diff behavior added by this PR.
flowchart LR subgraph Source[Current source view] R[Root manifest] L[Local package sources] P[Installed package manifests] end subgraph Compare[Config consistency] V[Deduplicated MCP configs] D[Symmetric source vs lock diff] end K[Lock mcp_configs] X[Exit 1 with ownership diagnostic] R --> V L --> V P --> V V --> D K --> D D --> X classDef new stroke-dasharray: 5 5; class L,P,D new;Trade-offs
apm_modules; stale untracked packages cannot enter the audit view.MCPIntegratorserialization and deduplication instead of a new MCP schema or target-specific adapter.Benefits
config-consistencyfail and returns exit 1.Validation
Regression test on base before implementation:
Targeted audit suite after implementation:
Manual reproduction after implementation:
Lint mirror:
Scenario Evidence
apm audit --cireport drift and exit nonzerotests/integration/test_transitive_mcp_audit_e2e.py::test_ci_audit_rejects_removed_transitive_mcp_declaration(regression-trap for #2136)apm audit --cicleantests/integration/test_transitive_mcp_audit_e2e.py::test_force_install_then_ci_audit_accepts_transitive_mcptests/unit/policy/test_ci_checks.py::TestConfigConsistency::test_transitive_mcp_server_resolved_from_local_sourceand::test_transitive_mcp_server_resolved_from_installed_remoteHow to test
uv run --extra dev python -m pytest tests/integration/test_transitive_mcp_audit_e2e.py -q; expect 2 passed.--trust-transitive-mcp; expect the unchanged audit to exit 0.dependencies.mcpblock and rerunapm audit --ci --no-policy --no-fail-fast; expect exit 1 and the lock-only diagnostic.Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com