feat(pack): unify 'apm pack' to produce bundle and marketplace.json (closes #722)#1042
Conversation
Twelve findings from the copilot-pull-request-reviewer pass on PR #1038. Code fixes (in src/): - Remove unused DEPRECATION_MESSAGE import in commands/marketplace.py - Remove unused LOCAL_SOURCE_RE import in marketplace/yml_editor.py - _has_marketplace_block() now raises MarketplaceYmlError on YAML parse errors and OS read errors instead of swallowing them as 'no config' -- fixes a misleading message on malformed apm.yml. - migrate_marketplace_yml() validates that apm.yml round-trips to a mapping; empty apm.yml now treated as an empty mapping (CommentedMap) so the marketplace block can still be inserted. - _is_apm_yml_with_marketplace() now requires the marketplace value itself to be a mapping; previously a non-dict value would crash _get_marketplace_container() callers on .get() access. - 'apm marketplace init' applies the same empty-vs-non-mapping guard on apm.yml round-trip; non-mapping top level is a hard error, empty file is treated as an empty mapping. - 'apm init --marketplace' no longer derives marketplace owner from the project name (which produced misleading github.com/<project> URLs); the template's acme-org placeholder is used instead. - _check_gitignore_for_marketplace_json warning text refreshed: 'Both apm.yml and the generated marketplace.json must be tracked'. - Renamed test_source_dot_traversal to test_local_source_accepted (the behavior changed at fold time). - init_template.py module docstring now describes both renderers. - test_apm_yml_marketplace_loader.py docstring corrected: strict-key enforcement is inside the marketplace block only. Regression tests (tests/unit/marketplace/test_review_fixes.py, +12): - malformed apm.yml surfaces a clear MarketplaceYmlError - migrate rejects list/scalar top level, accepts empty file - _is_apm_yml_with_marketplace rejects non-mapping marketplace values - 'apm marketplace init' rejects non-mapping apm.yml, accepts empty Docs (delivered by doc-writer agent): - Full rewrite of docs/src/content/docs/guides/marketplace-authoring.md around the apm.yml block; cites microsoft/azure-skills as the byte-for-byte build proof. Adds local-path packages section and a migration section. - One-line fix in guides/marketplaces.md (marketplace.yml -> apm.yml). - reference/cli-commands.md: rewrote init/build/outdated/check/doctor blurbs, added 'apm marketplace migrate' reference, added '--marketplace' flag to 'apm init' options/examples. - reference/manifest-schema.md: added optional 'marketplace:' to the top-level shape with a pointer to the authoring guide. - packages/apm-guide/.apm/skills/apm-usage/commands.md and package-authoring.md: refreshed authoring tables and shape; called out experimental gate and deprecation. - CHANGELOG.md: Added/Changed/Deprecated entries under [Unreleased] citing #1038. Validation: - 6757 unit tests pass (6745 prior + 12 new regression). - Real-world build proof: cloned microsoft/azure-skills, appended a marketplace: block to its apm.yml derived from the hand-authored marketplace.json, ran 'apm marketplace build', and diffed -- byte- for-byte identical (sha256 02f76bfc...). Closes review of #1038. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Follow-up to the marketplace-authoring consolidation work (PR #1038), tightening edge-case handling around apm.yml parsing/migration/init flows and refreshing the public + embedded docs to reflect the single-apm.yml source-of-truth model.
Changes:
- Harden marketplace config detection/migration/init against malformed, empty, or non-mapping
apm.ymlinputs (with clearer typed errors). - Add regression tests covering the reviewed edge cases.
- Refresh docs/guide content + command reference + schema docs + changelog entries to reflect the
apm.yml marketplace:block authoring surface.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/marketplace/test_yml_schema.py | Renames a test to match updated local-path source behavior. |
| tests/unit/marketplace/test_review_fixes.py | Adds regression coverage for malformed/empty/non-mapping apm.yml scenarios and helper tightening. |
| tests/unit/marketplace/test_apm_yml_marketplace_loader.py | Corrects docstring to reflect unknown-key validation scope. |
| src/apm_cli/marketplace/yml_editor.py | Tightens _is_apm_yml_with_marketplace to require a mapping marketplace block. |
| src/apm_cli/marketplace/migration.py | Makes _has_marketplace_block surface read/parse errors; makes migration tolerate empty apm.yml and reject non-mappings. |
| src/apm_cli/marketplace/init_template.py | Updates module docstring to describe both template renderers. |
| src/apm_cli/commands/marketplace.py | Drops unused import; makes marketplace init handle empty/non-mapping apm.yml; updates .gitignore warning wording. |
| src/apm_cli/commands/init.py | Stops deriving marketplace owner from project name when --marketplace is used. |
| packages/apm-guide/.apm/skills/apm-usage/package-authoring.md | Updates embedded authoring docs to the apm.yml marketplace: model and adds migration guidance. |
| packages/apm-guide/.apm/skills/apm-usage/commands.md | Updates embedded command reference for marketplace authoring and apm init --marketplace. |
| docs/src/content/docs/reference/manifest-schema.md | Documents optional top-level marketplace: in apm.yml. |
| docs/src/content/docs/reference/cli-commands.md | Adds apm marketplace migrate reference; updates init/build/authoring docs for the apm.yml block model. |
| docs/src/content/docs/guides/marketplaces.md | Fixes legacy marketplace.yml reference to apm.yml. |
| docs/src/content/docs/guides/marketplace-authoring.md | Rewrites the guide around apm.yml marketplace: + .claude-plugin/marketplace.json output and migration/local-path packages. |
| CHANGELOG.md | Adds Unreleased entries describing the marketplace authoring consolidation/migration/init changes. |
Copilot's findings
- Files reviewed: 15/15 changed files
- Comments generated: 4
| apm_data = rt.load(apm_text) | ||
|
|
There was a problem hiding this comment.
migrate_marketplace_yml() validates legacy marketplace.yml up front, but apm.yml is still parsed via ruamel without catching YAML parse errors. If apm.yml is malformed, this will raise a ruamel exception (and the CLI will surface a generic "Migration failed"), rather than a consistent MarketplaceYmlError like _has_marketplace_block(). Consider catching ruamel/yaml parse errors around rt.load(apm_text) and re-raising MarketplaceYmlError with an "Invalid YAML in apm.yml" message.
| apm_data = rt.load(apm_text) | |
| try: | |
| apm_data = rt.load(apm_text) | |
| except Exception as exc: | |
| from ruamel.yaml.error import YAMLError as RuamelYAMLError | |
| if isinstance(exc, RuamelYAMLError): | |
| raise MarketplaceYmlError("Invalid YAML in apm.yml.") from exc | |
| raise |
Picks up the AW_APM_PACKAGES JSON-array fix from gh-aw v0.71.2 (shared/apm.md realignment in github/gh-aw#29002), which caused the PR Review Panel run on PR #1042 to fail at the 'Validate downloaded bundles match matrix manifest' step. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- migration.py: wrap ruamel apm.yml load; raise typed
MarketplaceYmlError("apm.yml is malformed: ...") instead of
leaking ruamel.yaml.YAMLError to the caller. Mirrors the existing
legacy marketplace.yml error path.
- init.py: when 'apm init --marketplace' is invoked but the
marketplace_authoring experimental flag is disabled, append the
block (option b -- lower friction, harmless if unused) and emit a
CommandLogger.warning() pointing at the flag name and enablement
command.
- yml_editor.py: add 'data: object' type hint to
_is_apm_yml_with_marketplace() to satisfy the project-wide type-hint
requirement.
- CHANGELOG.md: condense Unreleased marketplace entries to one line
per entry per Keep a Changelog convention; strip nested bullets
and prose.
Tests:
- test_migrate_with_malformed_apm_yml_raises_typed_error
- TestInitMarketplaceFlagWarnsWhenExperimentalDisabled
::test_warns_with_experimental_flag_name
Full unit suite: 6759 passed.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Rewrite marketplace authoring guide to use 'apm pack' and the apm.yml marketplace: block as the single source of truth. - Update CLI command reference: remove 'apm marketplace build' entry, refresh 'apm pack' flag table, refresh 'apm marketplace init'. - Update apm-usage skill (commands.md) to match. - Remove all references to the marketplace_authoring experimental flag. Closes part of #722. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reads apm.yml and detects which artifacts to produce based on the presence of 'dependencies:' (bundle) and 'marketplace:' (marketplace.json) blocks. A single 'apm pack' invocation now replaces the legacy 'apm marketplace build' subcommand. Changes: - New BuildOrchestrator (src/apm_cli/core/build_orchestrator.py) with pluggable ArtifactProducer protocol and BundleProducer + MarketplaceProducer implementations. - pack command gains --offline, --include-prerelease, and --marketplace-output flags. Help text documents exit codes. - 'apm marketplace build' is hard-removed: invoking it exits 2 with a one-line migration message. - 'marketplace_authoring' experimental flag deleted (GA). - 'apm marketplace init' and 'apm init --marketplace' next-step hints now point at 'apm pack'. - 'apm marketplace publish' error wording updated. - New tests: 14 orchestrator unit tests, 9 pack integration tests, and one byte-for-byte snapshot test against microsoft/azure-skills@bef1f05 (sha256 02f76bfc0e5bbf7fdf1de1dda1f84c4da6e986913b6647973c0ffe39c1d5003b). - Stale tests removed: test_marketplace_build.py, test_marketplace_gating.py, and the marketplace_authoring experimental-flag class. - CHANGELOG updated under Added / Changed / Removed. Validation: - 6706 unit + console tests pass (uv run pytest tests/unit tests/test_console.py) - 10 new integration tests pass - azure-skills snapshot proof matches byte-for-byte Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Per copilot-pull-request-reviewer comment on PR #1042: Keep a Changelog entries should be one concise line per PR. The previous entry (418 chars, multi-clause) is condensed to 165 chars matching the convention. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Assessed the 4 copilot-pull-request-reviewer comments from 14:28 UTC; all addressed:
The reviewer's snapshot was taken before the GA-promotion + cleanup commits landed, so #2 reads as a regression but is actually obsolete — see the Removed section in CHANGELOG. |
Promotes [Unreleased] -> [0.11.0] - 2026-04-29 and bumps pyproject.toml + uv.lock to 0.11.0. Version-bump rationale: 0.11.0 (minor bump) chosen over 0.10.1 because this release ships one BREAKING removal (`apm marketplace build` -> exits 2, use `apm pack`) plus several net-new features (Dev Container Feature, Codex project-scoped MCP, `marketplace:` block in apm.yml, `apm pack` unification, multi-org `apps[]`). Strict semver in 0.x: minor for features-with-break, patch only for bugfixes. Milestone admin (done out-of-band): - Renamed milestone #8 `0.10.1` -> `0.11.0` - Created milestone #9 `0.12.0` as next-up bucket - Moved 43 open items (42 issues + 1 open PR #999) from `0.11.0` -> `0.12.0` - 6 closed items stay in `0.11.0` PRs shipping in 0.11.0 (22 commits since v0.10.0): User-facing features: - #1042/#722 `apm pack` unifies bundle + marketplace.json (BREAKING: `apm marketplace build` removed) - #1038 `marketplace:` block in apm.yml + `apm marketplace migrate` - #803 /#502 Codex project-scoped MCP (`.codex/config.toml`) + user-scope primitives - #861 Dev Container Feature `ghcr.io/microsoft/apm/apm-cli` - #982/#984 shared/apm.md `apps:` array for cross-org private packages - #820 `target:` in apm.yml validates at parse time - #1032 `apm marketplace add` honors manifest.name (Claude Code parity) - #1000/#998/#994 unified `--policy` / `--policy-source` accepted forms User-facing fixes: - #1015 ADO Entra ID auth + `apm install --update` pre-flight abort - #1019/#1020 GEMINI.md only created when target requested - #1008 marketplace producer respects GITHUB_HOST + multi-host URL forms - #1018 POSIX paths in auto-discovery output (Windows compat) - #996 drop stray 'specify' from generated file footer Maintainer tooling: - #1043 NOTICE.md per CELA template - #1045/#1044 NOTICE drift gate + license-policy gate in CI - #1033 shared/apm.md `[a b]` import-input repair (gh-aw#29076 paper-cut) - #1030 panel workflows skip-don't-fail on unmatched labels; gh-aw v0.71.1 - #1026 shared/apm.md recompiled to apm-action v1.5.0 + bundles-file - #1022 review-panel: true fan-out + binary verdict + label automation - #918 complexity audit + benchmarks suite - #1002 CodeQL clear-text-storage false-positive resolved (token -> placeholder) Files changed: - pyproject.toml: 0.10.0 -> 0.11.0 - uv.lock: regenerated (version field only) - CHANGELOG.md: [Unreleased] promoted to [0.11.0] - 2026-04-29 NOTICE drift check passes against the bumped lockfile. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Promotes [Unreleased] -> [0.11.0] - 2026-04-29 and bumps pyproject.toml + uv.lock to 0.11.0. Version-bump rationale: 0.11.0 (minor bump) chosen over 0.10.1 because this release ships one BREAKING removal (`apm marketplace build` -> exits 2, use `apm pack`) plus several net-new features (Dev Container Feature, Codex project-scoped MCP, `marketplace:` block in apm.yml, `apm pack` unification, multi-org `apps[]`). Strict semver in 0.x: minor for features-with-break, patch only for bugfixes. Milestone admin (done out-of-band): - Renamed milestone #8 `0.10.1` -> `0.11.0` - Created milestone #9 `0.12.0` as next-up bucket - Moved 43 open items (42 issues + 1 open PR #999) from `0.11.0` -> `0.12.0` - 6 closed items stay in `0.11.0` PRs shipping in 0.11.0 (22 commits since v0.10.0): User-facing features: - #1042/#722 `apm pack` unifies bundle + marketplace.json (BREAKING: `apm marketplace build` removed) - #1038 `marketplace:` block in apm.yml + `apm marketplace migrate` - #803 /#502 Codex project-scoped MCP (`.codex/config.toml`) + user-scope primitives - #861 Dev Container Feature `ghcr.io/microsoft/apm/apm-cli` - #982/#984 shared/apm.md `apps:` array for cross-org private packages - #820 `target:` in apm.yml validates at parse time - #1032 `apm marketplace add` honors manifest.name (Claude Code parity) - #1000/#998/#994 unified `--policy` / `--policy-source` accepted forms User-facing fixes: - #1015 ADO Entra ID auth + `apm install --update` pre-flight abort - #1019/#1020 GEMINI.md only created when target requested - #1008 marketplace producer respects GITHUB_HOST + multi-host URL forms - #1018 POSIX paths in auto-discovery output (Windows compat) - #996 drop stray 'specify' from generated file footer Maintainer tooling: - #1043 NOTICE.md per CELA template - #1045/#1044 NOTICE drift gate + license-policy gate in CI - #1033 shared/apm.md `[a b]` import-input repair (gh-aw#29076 paper-cut) - #1030 panel workflows skip-don't-fail on unmatched labels; gh-aw v0.71.1 - #1026 shared/apm.md recompiled to apm-action v1.5.0 + bundles-file - #1022 review-panel: true fan-out + binary verdict + label automation - #918 complexity audit + benchmarks suite - #1002 CodeQL clear-text-storage false-positive resolved (token -> placeholder) Files changed: - pyproject.toml: 0.10.0 -> 0.11.0 - uv.lock: regenerated (version field only) - CHANGELOG.md: [Unreleased] promoted to [0.11.0] - 2026-04-29 NOTICE drift check passes against the bumped lockfile. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chore(release): cut 0.11.0 Promotes [Unreleased] -> [0.11.0] - 2026-04-29 and bumps pyproject.toml + uv.lock to 0.11.0. Version-bump rationale: 0.11.0 (minor bump) chosen over 0.10.1 because this release ships one BREAKING removal (`apm marketplace build` -> exits 2, use `apm pack`) plus several net-new features (Dev Container Feature, Codex project-scoped MCP, `marketplace:` block in apm.yml, `apm pack` unification, multi-org `apps[]`). Strict semver in 0.x: minor for features-with-break, patch only for bugfixes. Milestone admin (done out-of-band): - Renamed milestone #8 `0.10.1` -> `0.11.0` - Created milestone #9 `0.12.0` as next-up bucket - Moved 43 open items (42 issues + 1 open PR #999) from `0.11.0` -> `0.12.0` - 6 closed items stay in `0.11.0` PRs shipping in 0.11.0 (22 commits since v0.10.0): User-facing features: - #1042/#722 `apm pack` unifies bundle + marketplace.json (BREAKING: `apm marketplace build` removed) - #1038 `marketplace:` block in apm.yml + `apm marketplace migrate` - #803 /#502 Codex project-scoped MCP (`.codex/config.toml`) + user-scope primitives - #861 Dev Container Feature `ghcr.io/microsoft/apm/apm-cli` - #982/#984 shared/apm.md `apps:` array for cross-org private packages - #820 `target:` in apm.yml validates at parse time - #1032 `apm marketplace add` honors manifest.name (Claude Code parity) - #1000/#998/#994 unified `--policy` / `--policy-source` accepted forms User-facing fixes: - #1015 ADO Entra ID auth + `apm install --update` pre-flight abort - #1019/#1020 GEMINI.md only created when target requested - #1008 marketplace producer respects GITHUB_HOST + multi-host URL forms - #1018 POSIX paths in auto-discovery output (Windows compat) - #996 drop stray 'specify' from generated file footer Maintainer tooling: - #1043 NOTICE.md per CELA template - #1045/#1044 NOTICE drift gate + license-policy gate in CI - #1033 shared/apm.md `[a b]` import-input repair (gh-aw#29076 paper-cut) - #1030 panel workflows skip-don't-fail on unmatched labels; gh-aw v0.71.1 - #1026 shared/apm.md recompiled to apm-action v1.5.0 + bundles-file - #1022 review-panel: true fan-out + binary verdict + label automation - #918 complexity audit + benchmarks suite - #1002 CodeQL clear-text-storage false-positive resolved (token -> placeholder) Files changed: - pyproject.toml: 0.10.0 -> 0.11.0 - uv.lock: regenerated (version field only) - CHANGELOG.md: [Unreleased] promoted to [0.11.0] - 2026-04-29 NOTICE drift check passes against the bumped lockfile. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore(changelog): tighten 0.11.0 entries to lead with user impact Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore(changelog): move Dev Container Feature to Maintainer tooling (not yet published) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore(changelog): de-dupe within 0.11.0 (combine #722 Removed bullets, drop #820 Fixed pointer) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
feat(pack): unify
apm packto produce bundle and marketplace.jsonTL;DR
Two build-shaped verbs (
apm packfor bundles,apm marketplace buildformarketplace.json) collapse into one:apm packreadsapm.ymland produces whichever artifacts the manifest declares — bundle whendependencies:is present,.claude-plugin/marketplace.jsonwhenmarketplace:is present, both when both are. Themarketplace_authoringexperimental flag is removed (GA), andapm marketplace initnow appends toapm.ymlinstead of scaffolding a standalonemarketplace.yml. Closes #722.Important
Breaking:
apm marketplace buildis deleted (no deprecation alias). Invoking it exits 2 with a one-line migration message. Update CI scripts to callapm pack.Problem (WHY)
dependencies:andmarketplace:blocks in theirapm.ymlhad to rememberapm packandapm marketplace buildand run them in the right order. The user-reported framing: "now we have apm marketplace build and apm build / apm pack? this is bad ux." ([FEATURE] Addapm marketplace generatecommand to pack all plugins and produce amarketplace.json#722)marketplace.jsonto CWD, but Claude Code reads.claude-plugin/marketplace.jsonfrom the repo root per the official plugin marketplace spec. Publishers had to remember a manual copy step or risk pushing a stale file.marketplace_authoringhad been stable for two minor releases; theapm experimental enablestep was friction without a corresponding stability story.apm marketplace initcreated a standalonemarketplace.ymleven after PR feat(marketplace): fold marketplace.yml into apm.yml's 'marketplace:' block (closes #722, implements #1036) #1038 madeapm.ymlthe canonical home for themarketplace:block.Why these matter: the user-facing surface had drifted from the PROSE principle: "Favor small, chainable primitives over monolithic frameworks." Two verbs that share a manifest, a lockfile, and an output directory are not chainable primitives — they are duplication. The Agent Skills guidance applies in mirror image: "Add what the agent lacks, omit what it knows" — the second verb added nothing the first could not infer from the manifest.
Approach (WHAT)
apm packreadsapm.yml, runsBundleProducerand/orMarketplaceProducerbased on declared blocks.apm marketplace buildoutright; install a custom error in themarketplacegroup that exits 2 with a migration hint.marketplace_authoringexperimental flag and all its call sites + tests..claude-plugin/marketplace.json(Anthropic-canonical). Override via--marketplace-output.apm marketplace initappends amarketplace:block to existingapm.yml(errors if missing or already present).--offline,--include-prerelease,--marketplace-output.npm packflag-scope behavior).Implementation (HOW)
src/apm_cli/core/build_orchestrator.py(new, 274 lines) — Strategy pattern:BuildOrchestrator+ArtifactProducerProtocol +BundleProducerandMarketplaceProduceradapters.detect_outputs()does a raw YAML key check onapm.yml(plus a legacymarketplace.ymlsibling check) to decide which producers to run. Producers are thin wrappers around the existingbundle.packer.pack_bundleandmarketplace.builder.MarketplaceBuilder— no new build logic.src/apm_cli/commands/pack.py— Rewires the click command to buildBuildOptionsand call the orchestrator. Adds--offline,--include-prerelease,--marketplace-output. Help text rewritten to document the manifest-driven mental model with three example clusters (bundle-only, marketplace-only, both).src/apm_cli/commands/marketplace.py— Removes_authoring_visible()and_require_authoring_flag()and every call site. AddsMarketplaceGroup(click.Group)that catches thebuildsubcommand name and raisesUsageErrorwith the migration message.apm marketplace initrewritten to operate onapm.ymlviaruamel.yamlround-trip (preserves comments).src/apm_cli/core/experimental.py—marketplace_authoringentry deleted from theFLAGSdict.src/apm_cli/marketplace/init_template.py— Template now describes amarketplace:block to append, not a standalone file. Allapm marketplace buildreferences replaced withapm pack.tests/unit/commands/test_marketplace_{build,gating}.py. Added:tests/unit/core/test_build_orchestrator.py(14 tests),tests/integration/test_pack_unified.py(9 tests),tests/integration/test_azure_skills_marketplace.py(1 byte-identical-reproduction test). Net: -82 + 24 + 1 = -57 tests, all due to gate/build subcommand removal.docs/src/content/docs/guides/marketplace-authoring.mdrewritten end-to-end. CLI reference, manifest schema,experimental.md, and theapm-usageskill all updated to match.Diagrams
Legend: how
apm packroutes a project'sapm.ymlto one or both producers; the dashed boxes mark the unified entrypoint and the new orchestrator that did not exist before this PR.flowchart LR User([User runs<br/>apm pack]):::new Manifest[apm.yml] Detect{detect_outputs}:::new Orchestrator[BuildOrchestrator]:::new subgraph Producers Bundle[BundleProducer] Market[MarketplaceProducer] end Lockfile[(apm.lock.yaml)] BuildDir["./build/[name]/"] ClaudeDir[.claude-plugin/<br/>marketplace.json] User --> Orchestrator Orchestrator --> Detect Manifest --> Detect Detect -- "dependencies:" --> Bundle Detect -- "marketplace:" --> Market Detect -- "neither" --> Err[/exit 1:<br/>Nothing to pack/]:::new Bundle --> Lockfile Bundle --> BuildDir Market --> Manifest Market --> ClaudeDir classDef new stroke-dasharray: 5 5,stroke:#0969da,stroke-width:2px;Legend: invocation flow when the project has both blocks — both producers run sequentially against the same manifest and lockfile, each writing to its canonical location.
sequenceDiagram participant U as User participant P as apm pack participant O as BuildOrchestrator participant B as BundleProducer participant M as MarketplaceProducer participant FS as Filesystem U->>P: apm pack P->>O: run(BuildOptions) O->>O: detect_outputs(apm.yml) Note over O: both blocks present<br/>routes to BUNDLE and MARKETPLACE O->>B: produce() B->>FS: write ./build/[name]/ B-->>O: ProducerResult O->>M: produce() M->>FS: write .claude-plugin/marketplace.json M-->>O: ProducerResult O-->>P: BuildResult P-->>U: Packed 2 artifactsTrade-offs
apm marketplace build. Chose hard delete with a one-line migration error; rejected a transitional shim. Per repo policy: "we still favor shipping fast over lengthy deprecation cycles" (CLAUDE.md). The custom click error gives users a single clear migration step; a silent alias would let drift fester.--marketplace-only/--bundle-onlyflags. Manifest is the control plane; users skip a producer by removing the corresponding block. Adding override flags would invite combinatorial nonsense (--bundle-only --marketplace-only= error? both?) and contradict the mental model.marketplace.ymlas a read-only legacy fallback. Already-deprecated; removal is queued for v0.13. Doing both (introduce orchestrator AND remove legacy reader) in one PR would balloon scope and break users mid-migration.apm.yml/lockfile; parallelism risks contention for sub-second wall-clock gain. Revisit only if a third producer with isolated I/O appears.Benefits
apm packfromapm --help; the marketplace path no longer requires reading a separate doc page first.apm pack && git add .claude-plugin/marketplace.json && git commit— no manual copy step, no drift between the file Claude Code reads and the fileapmwrites.apm experimental enable marketplace_authoringstep removed from every quickstart.tests/integration/test_azure_skills_marketplace.pyclones (or replays a snapshot of)microsoft/azure-skillsand assertsapm packproduces amarketplace.jsonwith sha25602f76bf...3b, matching the hand-authored file in that repo. This is regression coverage against future drift.ArtifactProducerimplementation, not a new top-level command.Validation
uv run pytest tests/unit tests/test_console.py -x:uv run pytest tests/integration/test_pack_unified.py tests/integration/test_azure_skills_marketplace.py -x:uv run pytest tests/unit/core/test_build_orchestrator.py -x:azure-skills reproduction (excerpt from
test_azure_skills_marketplace.py::test_apm_pack_reproduces_azure_skills_marketplace_json_byte_for_byte):Files changed (40)
Net: +2121 / -1579 across 40 files.
How to test
git checkout fix/marketplace-fold-review-comments && uv sync --extra dev.dependencies:inapm.yml, runapm pack→ expect./build/<name>/only.marketplace:inapm.yml, runapm pack→ expect.claude-plugin/marketplace.jsononly.apm marketplace build→ expect exit 2 with the one-line migration message pointing atapm pack.https://github.com/microsoft/azure-skills, runapm packin it, thensha256sum .claude-plugin/marketplace.json→ expect02f76bfc0e5bbf7fdf1de1dda1f84c4da6e986913b6647973c0ffe39c1d5003b.Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com