fix: support catalog-only marketplace plugins - #2709
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes marketplace installs for “catalog-only” plugins (no apm.yml / SKILL.md / plugin manifest on disk) by preserving admitted deployable metadata from the marketplace catalog through resolution, then synthesizing an apm.yml before normal package validation.
Changes:
- Extend
MarketplacePluginparsing to retain a deep-copied, admittedmanifestsubset (name/description/version/lspServers/mcpServers) that is excluded from identity/hash. - Thread the admitted manifest through marketplace resolution into
DependencyReference.marketplace_manifest. - Materialize a synthetic
apm.ymlfrom the catalog manifest when needed (fail-closed + cleanup on failure), with new resolver/unit tests covering the LSP path.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/deps/test_shared.py | Adds unit coverage for metadata-only plugin materialization and fail-closed cleanup. |
| tests/test_apm_resolver.py | Adds end-to-end resolver coverage ensuring catalog metadata is retained and used during install. |
| src/apm_cli/models/dependency/reference.py | Adds marketplace_manifest field on DependencyReference to carry admitted catalog metadata. |
| src/apm_cli/marketplace/models.py | Parses and deep-copies admitted catalog manifest fields onto MarketplacePlugin (excluded from hash/eq). |
| src/apm_cli/deps/apm_resolver.py | Captures the plugin manifest from marketplace resolution onto the resolved DependencyReference. |
| src/apm_cli/deps/_shared.py | Implements materialize_marketplace_manifest() and integrates it into _validate_and_load_package(). |
Suppressed comments (1)
src/apm_cli/deps/_shared.py:34
- The materialization failure messages don’t include which dependency (or path) failed, which makes resolver/installer errors harder to diagnose. Include the dep repo_url and target path in the raised ValueError messages.
if "lspServers" in declared and not package.get_lsp_dependencies():
raise ValueError("Marketplace LSP metadata did not materialize a valid dependency")
if "mcpServers" in declared and not package.get_mcp_dependencies():
raise ValueError("Marketplace MCP metadata did not materialize a valid dependency")
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
APM Review Panel:
|
| Persona | B | R | N | Takeaway |
|---|---|---|---|---|
| Python Architect | 1 | 1 | 0 | Require per-component validation and one guarded materialization owner. |
| CLI Logging Expert | 0 | 1 | 0 | Make catalog failures accurate and actionable. |
| DevX UX Expert | 1 | 2 | 0 | Fix silent and partial failures before relying on this path. |
| Supply Chain Security Expert | 2 | 1 | 0 | Prevent path escapes and replayable failed synthesis. |
| OSS Growth Hacker | 0 | 1 | 0 | Add a user-facing compatibility release note. |
| Doc Writer | 0 | 1 | 0 | Document catalog-only marketplace packages. |
| Test Coverage Expert | 0 | 2 | 0 | Add scenario mapping and fixture-backed integration coverage. |
| Performance Expert | 1 | 0 | 0 | Track catalog metadata variants during consumer materialization. |
B = blocking-severity findings, R = recommended, N = nits.
Counts are signal strength, not gates. The maintainer ships.
Top 5 follow-ups
- [Supply Chain Security Expert] (blocking-severity) Make synthesis path-safe and transactional so symlinks and cleanup failures cannot escape or replay rejected output.
- [Python Architect] (blocking-severity) Validate every declared LSP and MCP server at one canonical boundary and propagate materialization failures.
- [Performance Expert] (blocking-severity) Track deterministic catalog metadata variants without fragmenting the bare download cache.
- [Test Coverage Expert] Add scenario mapping and fixture-backed marketplace coverage for success and failure paths.
- [CLI Logging Expert] Surface accurate diagnostics, then document the contract and release note.
Architecture
classDiagram
class MarketplacePlugin
class DependencyReference
class APMDependencyResolver
class SharedDeps
class PluginParser
MarketplacePlugin --> DependencyReference : admitted metadata
APMDependencyResolver --> SharedDeps : materializes
SharedDeps --> PluginParser : synthesizes through
flowchart TD
A[Marketplace catalog] --> B[Admit safe fields]
B --> C[Resolve dependency]
C --> D[Stage synthetic manifest]
D --> E{Every server valid?}
E -->|yes| F[Commit and validate]
E -->|no| G[Reject and clean]
Recommendation
Fold transactional path safety, per-component fail-closed propagation, deterministic manifest-variant handling, integration coverage, diagnostics, and documentation into this PR before maintainer review.
Full per-persona findings
Python Architect
- [blocking] Validate every declared catalog server, not merely each nonempty category at
src/apm_cli/deps/_shared.py:31. - [recommended] Add a static boundary guard for the catalog materialization owner.
CLI Logging Expert
- [recommended] Report catalog validation failures accurately instead of labeling them as download failures.
DevX UX Expert
- [blocking] Invalid catalog metadata can be swallowed during resolver work.
- [recommended] Mixed valid and invalid declarations can install partially.
- [recommended] Document catalog-only extraction in the install contract.
Supply Chain Security Expert
- [blocking] A dangling
apm.ymlsymlink can redirect synthesis outside the package. - [blocking] Failed cleanup can leave a rejected generated manifest reusable on retry.
- [recommended] Reject mixed valid and invalid declarations in full.
OSS Growth Hacker
- [recommended] Add the marketplace compatibility fix to
CHANGELOG.md.
Auth Expert -- inactive
The touched dependency and marketplace paths do not change authentication, token, credential, or host behavior.
Doc Writer
- [recommended] Add the catalog-only path and fail-closed limits to the package-type reference and marketplace guide.
Test Coverage Expert
- [recommended] Add Scenario Evidence mapping for issue [BUG] Metadata-only marketplace plugins fail package validation during install #2708.
- [recommended] Add a fixture-backed marketplace integration test.
Performance Expert
- [blocking] Warm installs can reuse a generated manifest for different catalog metadata because consumer materialization does not track the variant.
This panel is advisory. It does not block merge. Re-apply the panel-review label after addressing feedback to re-run.
APM Review Panel:
|
| Persona | B | R | N | Takeaway |
|---|---|---|---|---|
| Python Architect | 0 | 1 | 1 | Architecture findings are resolved; remove the duplicate release heading. |
| CLI Logging Expert | 0 | 0 | 0 | Catalog failures now surface actionably. |
| DevX UX Expert | 0 | 0 | 0 | Silent and partial failures are resolved and documented. |
| Supply Chain Security Expert | 0 | 0 | 0 | Initial security issues are fixed and tested. |
| OSS Growth Hacker | 0 | 1 | 0 | Correct the duplicate release heading. |
| Doc Writer | 1 | 0 | 0 | Remove the conflicting duplicate release section. |
| Test Coverage Expert | 0 | 1 | 0 | Add successful full-install fixture coverage. |
| Performance Expert | 0 | 1 | 0 | Replace quadratic dependency merging with a name index. |
B = blocking-severity findings, R = recommended, N = nits.
Counts are signal strength, not gates. The maintainer ships.
Top 3 follow-ups
- [Doc Writer] (blocking-severity) Remove the duplicate
## [0.29.0] - 2026-08-26heading -- the public release record must be unambiguous. - [Test Coverage Expert] Add a fixture-backed successful full
apm installtest -- direct resolver coverage does not prove the user command path. - [Performance Expert] Use an O(n) name index in
_union_dep_listand add a scaling guard -- append-time linear scans compound with catalog size.
Architecture
flowchart TD
A[Marketplace catalog] --> B[Canonical materializer]
B --> C[Stage manifest]
C --> D{Every server valid?}
D -->|yes| E[Atomic commit]
D -->|no| F[Reject and clean]
E --> G[Install pipeline]
Recommendation
Fold these three bounded changes, rerun CI, then ship.
Full per-persona findings
Python Architect
- [recommended] Remove the duplicate 0.29.0 changelog heading.
- [nit] Align the catalog metadata carrier name only if that field is changed later; no churn now.
CLI Logging Expert
No findings.
DevX UX Expert
No findings.
Supply Chain Security Expert
No findings.
OSS Growth Hacker
- [recommended] Remove the duplicate and incorrectly dated v0.29.0 heading.
Auth Expert -- inactive
The marketplace and dependency changes do not alter auth behavior.
Doc Writer
- [blocking] Remove the duplicate 0.29.0 heading with the conflicting date.
Test Coverage Expert
- [recommended] Add a successful full install fixture test for catalog-only LSP and MCP metadata.
Performance Expert
- [recommended] Replace
_union_dep_listquadratic nested-dict membership checks with an O(n) name index and scaling guard.
This panel is advisory. It does not block merge. Re-apply the panel-review label after addressing feedback to re-run.
APM Review Panel:
|
| Persona | B | R | N | Takeaway |
|---|---|---|---|---|
| Python Architect | 0 | 0 | 1 | Preserve the centralized transaction shape. |
| CLI Logging Expert | 0 | 2 | 0 | Consolidate repeated errors and avoid absolute paths. |
| DevX UX Expert | 0 | 0 | 0 | Full success and failure paths pass. |
| Supply Chain Security Expert | 2 | 0 | 0 | Bind cache reuse to full bytes and remove root-dependent output. |
| OSS Growth Hacker | 0 | 0 | 0 | Changelog and marketplace framing are clear. |
| Doc Writer | 0 | 0 | 0 | Docs and changelog are accurate. |
| Test Coverage Expert | 1 | 0 | 0 | Add symlinked .apm integration coverage. |
| Performance Expert | 0 | 0 | 0 | Dependency union is linear and variant handling is bounded. |
B = blocking-severity findings, R = recommended, N = nits.
Counts are signal strength, not gates. The maintainer ships.
Top 4 follow-ups
- [Supply Chain Security Expert] (blocking-severity) Validate complete generated manifest content before cache reuse -- a preserved header must not admit appended dependencies.
- [Supply Chain Security Expert] (blocking-severity) Remove absolute checkout-root substitution from generated bytes and hashes -- equivalent metadata must remain reproducible.
- [Test Coverage Expert] (blocking-severity) Add integration coverage proving a symlinked
.apmdirectory is rejected without an outside write. - [CLI Logging Expert] Emit one package-level failure using package identity instead of repeated absolute-path diagnostics.
Architecture
flowchart TD
A[Catalog metadata] --> B[Build canonical expected bytes]
B --> C{Existing generated bytes match?}
C -->|yes| D[Reuse]
C -->|no| E[Stage and validate]
E --> F{Paths safe and all servers valid?}
F -->|yes| G[Atomic commit]
F -->|no| H[Reject and clean]
Recommendation
Fold the demonstrated cache-integrity and portability fixes, add the symlink integration trap, consolidate the diagnostic, and rerun CI.
Full per-persona findings
Python Architect
- [nit] Preserve the current centralized transaction shape.
CLI Logging Expert
- [recommended] Render catalog rejection once through CommandLogger.
- [recommended] Name the marketplace package instead of its absolute checkout path.
DevX UX Expert
No findings.
Supply Chain Security Expert
- [blocking] Do not trust the generated manifest header as a complete integrity check.
- [blocking] Keep synthesized catalog manifests independent of checkout path.
OSS Growth Hacker
No findings.
Auth Expert -- inactive
The touched files do not affect authentication or credential resolution.
Doc Writer
No findings.
Test Coverage Expert
- [blocking] Symlinked
.apmrejection lacks a malicious-input integration test.
Performance Expert
No findings.
This panel is advisory. It does not block merge. Re-apply the panel-review label after addressing feedback to re-run.
APM Review Panel:
|
| Persona | B | R | N | Takeaway |
|---|---|---|---|---|
| Python Architect | 0 | 0 | 1 | All architecture findings are resolved with one guarded owner. |
| CLI Logging Expert | 0 | 0 | 0 | One stable package-level diagnostic is verified. |
| DevX UX Expert | 0 | 1 | 0 | Install behavior is correct; it suggested optional recovery wording. |
| Supply Chain Security Expert | 0 | 0 | 0 | Full-byte repair, portable placeholders, and symlink defenses pass. |
| OSS Growth Hacker | 0 | 0 | 1 | Marketplace framing is clear; wording polish is optional. |
| Doc Writer | 0 | 0 | 0 | Docs, links, and changelog chronology are accurate. |
| Test Coverage Expert | 0 | 0 | 0 | Focused marketplace regressions, including .apm symlinks, pass. |
| Performance Expert | 0 | 0 | 0 | Dependency union is linear and warm reuse adds no network RTT. |
B = blocking-severity findings, R = recommended, N = nits.
Counts are signal strength, not gates. The maintainer ships.
Architecture
flowchart TD
A[Marketplace metadata] --> B[Canonical materializer]
B --> C[Stage portable manifest]
C --> D{Complete bytes and paths valid?}
D -->|yes| E[Atomic commit]
D -->|no| F[Reject and clean]
E --> G[Resolve placeholders in memory]
G --> H[Install and lock]
Recommendation
Ship the reviewed commit as-is; all checks are green and no substantive foldable item remains.
Folded in this run
- (panel) Transactional, path-safe synthesis and cleanup -- resolved in
7a01860. - (panel) Per-component fail-closed validation and resolver error propagation -- resolved in
7a01860. - (panel) Warm manifest-variant rematerialization -- resolved in
7a01860. - (panel) Canonical owner table, static boundary, and architecture assertion -- resolved in
7a01860. - (panel) Fixture-backed successful and rejected full install paths -- resolved in
be7c20c. - (panel) Linear dependency union and deterministic anti-scan test -- resolved in
be7c20c. - (panel) Duplicate changelog release heading removal -- resolved in
be7c20c. - (panel) Complete generated-byte verification before cache reuse -- resolved in
e5937dc. - (panel) Checkout-root-independent bytes with in-memory placeholder resolution -- resolved in
e5937dc. - (panel) Symlinked
.apmintegration regression trap -- resolved ine5937dc. - (panel) Single stable marketplace-identity diagnostic -- resolved in
e5937dc.
Regression-trap evidence (mutation-break gate)
- Per-component validation, symlink rejection, stage cleanup, variant handling, resolver error recording, full-install materialization, linear union, complete-byte repair, portable placeholders, marketplace identity, and AC35 were each removed in turn; their named regression tests failed as expected; every guard was restored.
Lint contract
uv run --frozen --extra dev ruff check src/ tests/ and uv run --frozen --extra dev ruff format --check src/ tests/ both passed at e5937dc. Pylint R0801, auth signals, architecture boundaries, file length, and diff checks also passed.
CI
All checks passed on CI run 33304715591 and Merge Gate 33304715670 after 1 CI recovery iteration.
Mergeability status
| PR | head SHA | CEO stance | iters | folds | defers | Copilot rounds | CI | mergeable | mergeStateStatus | notes |
|---|---|---|---|---|---|---|---|---|---|---|
| #2709 | e5937dc |
ship_now | 4 | 11 | 0 | 2 | green | MERGEABLE | BLOCKED | pending required review |
Convergence
4 outer iterations; 2 Copilot rounds with no inline comments. Final panel recommendation: ship_now.
Ready for maintainer review.
Full per-persona findings
Python Architect
No actionable findings. Preserve the centralized adapter plus transaction-script shape.
CLI Logging Expert
No findings. The package identity appears exactly once in the user-visible failure.
DevX UX Expert
- [recommended] Suggested a more explicit recovery action. CEO arbitration declined this as non-load-bearing because the dedicated logging review and exact output assertion prove the current contract.
Supply Chain Security Expert
No findings. Full-byte cache repair, portable placeholder preservation, and symlink defenses passed focused probes.
OSS Growth Hacker
- [nit] Optional user-outcome wording for the changelog entry.
Auth Expert -- inactive
The PR changes no authentication or credential-resolution surface.
Doc Writer
No findings.
Test Coverage Expert
No findings.
Performance Expert
No findings. Measured 1K-to-10K scaling remained near linear and warm reuse performed no network operation.
This panel is advisory. It does not block merge. Re-apply the panel-review label after addressing feedback to re-run.
Preserve admitted inline LSP/MCP metadata without trusting unrelated catalog fields.\n\nRefs microsoft#2708
Make catalog-only marketplace synthesis transactional, path-safe, variant-aware, and fail-closed per the review-panel follow-ups. Add canonical-owner guards, functional regression coverage, and user-facing documentation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
apm-spec-waiver: Marketplace catalog compatibility translates admitted metadata into the existing OpenAPM manifest contract; it adds no normative OpenAPM requirement. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Exercise successful catalog-only installs through the real command path, keep dependency merging linear at catalog scale, and remove the merge-created duplicate release heading. Addresses final panel follow-ups. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rebuild canonical generated bytes before cache reuse, keep plugin-root placeholders portable on disk and resolve them in memory, cover symlinked .apm rejection, and consolidate marketplace diagnostics. Addresses terminal panel findings. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
e5937dc to
e56e5fa
Compare
|
Rebased onto current main at b68fcad -> e56e5fa. Conflicting paths resolved (faithful merge of both intents):
Lint contract: Post-push mergeability: Ready for maintainer review. |
59b70a5
into
microsoft:main
Description
Marketplace catalog entries can contain deployable LSP/MCP metadata even when their downloaded source has no
apm.yml,SKILL.md, or plugin manifest. Preserve admitted inline catalog metadata through dependency resolution and synthesize the package manifest before ordinary validation.The fix is fail-closed:
name,description,version,lspServers, andmcpServersare retained;Fixes #2708
Type of change
Testing
Validation evidence:
git diff --checkpassed;How to test
Spec conformance (OpenAPM v0.1)
Scenario Evidence
tests/integration/test_marketplace_install_local.py::test_catalog_only_marketplace_installs_all_inline_servers;tests/integration/test_marketplace_install_local.py::test_invalid_catalog_only_metadata_fails_install_resolutionapm-spec-waiver: Marketplace catalog compatibility translates admitted metadata into the existing OpenAPM manifest contract; it adds no normative OpenAPM requirement.