Skip to content

feat(pack,doctor,init): vendor-neutral producer first-run UX#1362

Merged
danielmeppiel merged 4 commits into
mainfrom
danielmeppiel/marketplace-monorepo-ux
May 18, 2026
Merged

feat(pack,doctor,init): vendor-neutral producer first-run UX#1362
danielmeppiel merged 4 commits into
mainfrom
danielmeppiel/marketplace-monorepo-ux

Conversation

@danielmeppiel
Copy link
Copy Markdown
Collaborator

@danielmeppiel danielmeppiel commented May 18, 2026

TL;DR

This PR closes the two most-cited gaps in the producer first-run experience (#1322 monorepo plugins; #1332 per-package versions) by making apm pack, apm marketplace doctor, and apm marketplace init honest, vendor-neutral, and quietly opinionated. The post-pack output now lists the artifacts you actually shipped (and one docs URL for every consumer recipe), the scaffold stops scaring producers with a spurious [!] No plugin.json warning, the codex toggle becomes a one-line edit, and a new format coverage doctor row nudges single-format producers toward two-format reach. This is Wave 2 of the producer-experience epic (#1348); Wave 1 lives in microsoft/apm-action#40 and is independent.

Problem (WHY)

  • Producers do not know what apm pack actually built. The previous output ended at Built marketplace.json [claude] -> ... with no list of all written files and no pointer to how a consumer would install them. The CLI is vendor-agnostic but the consumer-side install command is not, so silence here punts the question onto the producer with no link to chase.
  • A misleading [!] No plugin.json found warning fires on marketplace-only projects. A marketplace publishes references to plugins, not a plugin bundle of its own. Surfacing a bundle warning on a producer that has not asked to author a bundle is anti-Progressive-Disclosure: "Context arrives just-in-time, not just-in-case.".
  • Scaffold pushed producers off the codex path. The previous # codex: block in the init template was multi-line and indented, which made the on-ramp feel like opting into a feature, not flipping a switch. Codex is in the same known_output_names() list as Claude; the scaffold should treat it like one edit, not a paragraph.
  • tag_pattern was undocumented at the per-package level. Monorepo producers (Understanding Expectations #1322) had to read the schema or hunt examples to learn that each package can ship its own tag pattern.
  • apm marketplace doctor did not surface format coverage. Producers had no quick way to see "you publish for Claude; you could also publish for Codex by adding codex: {}."

Anchor: "Add what the agent lacks, omit what it knows" — APM ships the producer enough context to make a vendor-neutral choice, then gets out of the way.

Approach (WHAT)

Acceptance criterion What ships
G2 apm pack no longer emits the misleading [!] No plugin.json found warning when apm.yml ships a marketplace: block; demoted to an _rich_info pointer at most.
G3+B After a successful pack, a vendor-neutral artifact catalog is appended: header + per-output ljust-aligned [profile] path rows + one docs pointer. Never names a vendor CLI surface inline. Suppressed in dry-run.
G4 apm init --marketplace / apm marketplace init scaffold uses a single-line # codex: {} toggle plus a note that enabling codex requires category: per package.
G5 Per-package examples in the scaffold include a commented tag_pattern: "{name}-v{version}" (snake_case, matching schema) on the remote and local-path entries.
G7 apm marketplace doctor adds an informational format coverage row listing configured vs. supported outputs. Always passes; skipped when no marketplace: block.

Decisions deliberately rejected (see Trade-offs): per-vendor install commands inline in the post-pack output; auto-enabling codex; failing doctor when only one format is configured.

Implementation (HOW)

File What changed
src/apm_cli/bundle/plugin_exporter.py Added _has_marketplace_block() helper; gated the legacy missing-plugin.json warning behind it via a keyword-only suppress_missing_warning flag on _find_or_synthesize_plugin_json. Genuine parse errors still warn.
src/apm_cli/commands/pack.py New MARKETPLACE_DOCS_URL constant; _render_marketplace_result now records per-output (profile, path) tuples and calls _render_marketplace_catalog on non-dry-run runs. Catalog renders one info header, ljust-aligned [profile] path rows, and one docs pointer.
src/apm_cli/marketplace/init_template.py Rewrote the outputs: and per-package sample regions of _MARKETPLACE_BLOCK_TEMPLATE: single-line codex toggle + snake_case tag_pattern examples + CI/--json tip.
src/apm_cli/commands/marketplace/doctor.py Added the format-coverage _DoctorCheck between the marketplace config and duplicate-names checks; guarded by if yml_obj is not None. Informational only -- does not affect exit code.
docs/src/content/docs/producer/publish-to-a-marketplace.md New ## Consume from any assistant section with the four-row table covering APM-aware / Claude / Codex / Cursor consumers (and the confirmed Codex marketplace URL).
packages/apm-guide/.apm/skills/apm-usage/commands.md Doc-sync per the repo's documentation rule 4: refreshed the apm pack and apm marketplace doctor rows.
CHANGELOG.md Wave 2 entries integrated into the existing Fixed and Changed sections (no new section headers).

Tests added: 1220 unit + integration tests pass in the impacted suites, including four new G3+B regression tests (artifact catalog, dry-run suppression, vendor-neutrality, column alignment), four G4/G5 template tests (roundtrip, single-line toggle, snake_case, schema parse), three G7 doctor tests (partial, full, skip), and three Wave-2 e2e tests (vendor-neutral catalog real path, dry-run suppression, no-warning regression).

Diagrams

Sequence of apm pack on a marketplace-only project after this PR -- showing where the new vendor-neutral catalog renders and where the old warning used to fire:

sequenceDiagram
    autonumber
    participant U as Producer
    participant CLI as apm pack
    participant E as plugin_exporter
    participant P as pack._render_marketplace_result
    participant C as _render_marketplace_catalog

    U->>CLI: apm pack
    CLI->>E: export_plugin_bundle(...)
    Note over E: G2 -- marketplace block detected.<br/>plugin.json synthesis suppressed.<br/>No spurious bundle warning.
    E-->>CLI: bundle ready (or absent)
    CLI->>P: render(report, dry_run=False)
    P->>P: per-output success lines
    P->>C: catalog(written=[(claude, path), (codex, path)])
    C-->>U: catalog header + aligned profile rows + single docs pointer
Loading

Format-coverage decision tree inside apm marketplace doctor -- shows why the new row never blocks exit:

flowchart LR
    A[doctor start] --> B{marketplace:<br/>block parses?}
    B -- no --> Z[skip format coverage row]
    B -- yes --> C[compute configured = yml.outputs]
    C --> D[compute missing = supported - configured]
    D --> E{missing empty?}
    E -- yes --> F["[i] Publishing for all known formats"]
    E -- no --> G["[i] Configured ... Also supported ..."]
    F --> Y[informational -- does not affect exit code]
    G --> Y
Loading

Trade-offs

  • Vendor-neutral catalog vs. per-assistant install commands inline. Inline claude plugin install ... would be the friendliest one-line answer for that specific reader, but APM is vendor-agnostic and the install command set changes per assistant and over time. We chose one stable docs URL plus aligned artifact rows. The vendor-neutrality regression test asserts no copilot plugin install, claude plugin install, codex plugin install, or cursor plugin install substring ever leaks into the catalog.
  • Doctor format coverage is informational, never fails. Failing producers who deliberately ship for one format would punish a legitimate choice. The row nudges (with a copy-paste codex: {} line) but never blocks.
  • Single-line # codex: {} toggle, not on-by-default. Auto-enabling codex would break producers who have not added per-package category: (codex requires it). Keep the toggle one keystroke away; document the category: constraint right under it.
  • tag_pattern per package is snake_case in scaffolds, but tagPattern at marketplace level is camelCase. This matches the existing schema (_BUILD_KEYS vs _PACKAGE_ENTRY_KEYS). The scaffold is faithful to the schema rather than to a stylistic preference.

Benefits

  1. Producers see exactly what they shipped: [i] Marketplace artifacts ready: plus the path for each format. No more "did it write the codex file? let me ls .agents/...".
  2. Marketplace-only producers stop receiving a warning they cannot act on. Confidence in the tool goes up, support load goes down.
  3. Codex enablement becomes a one-keystroke edit (# codex: {} -> codex: {}). Reaches more consumers without changing producer cognitive load.
  4. Monorepo producers (Understanding Expectations #1322) see the tag_pattern knob in the scaffold immediately, in snake_case, with both an aggregator and a local-path example.
  5. apm marketplace doctor becomes a coverage report: producers learn at audit time which formats they could publish without changing their packages.

Validation

Unit + integration test runs (1220 passing for impacted suites)
$ uv run --extra dev pytest \
    tests/integration/marketplace/ \
    tests/unit/commands/test_pack.py \
    tests/unit/commands/test_marketplace_doctor.py \
    tests/unit/marketplace/ \
    tests/unit/test_plugin_exporter.py -q --tb=short
1220 passed, 7 skipped in 9.40s
Lint (canonical contract, both silent)
$ uv run --extra dev ruff check src/ tests/
All checks passed!

$ uv run --extra dev ruff format --check src/ tests/
764 files already formatted
Real-fixture run on zava-agent-config (7-package monorepo) -- G2 + G3+B
$ cd /tmp/zava-test && apm pack

[*] Packed 1 file(s) -> build/zava-agent-config-6.1.0
[i] Plugin bundle ready -- contains plugin.json plus plugin-native directories ...
[i] Share with: apm install build/zava-agent-config-6.1.0
[*] Built marketplace.json [claude] (7 package(s)) -> /private/tmp/zava-test/.claude-plugin/marketplace.json
[*] Built marketplace.json [codex] (7 package(s)) -> /private/tmp/zava-test/.agents/plugins/marketplace.json
[i] Marketplace artifacts ready:
[i]   [claude] /private/tmp/zava-test/.claude-plugin/marketplace.json
[i]   [codex ] /private/tmp/zava-test/.agents/plugins/marketplace.json
[i] How consumers install from this marketplace varies by AI assistant.
[i] See: https://microsoft.github.io/apm/producer/publish-to-a-marketplace/#consume-from-any-assistant

No [!] No plugin.json found warning surfaced (proves G2). Catalog shows aligned [claude] and [codex ] columns plus single docs pointer (proves G3+B).

Real-fixture doctor on zava (G7 -- both states)
$ cd /tmp/zava-test && apm marketplace doctor
... (outputs: {claude, codex}) ...
| format coverage    | [i]    | Publishing for all known formats: claude, codex. |

$ # after removing 'codex: {}' from outputs
$ apm marketplace doctor
| format coverage    | [i]    | Configured: claude. Also supported: codex. Add e.g. 'codex: {}' ... |

Both states render correctly; exit code stays 0 throughout (informational).

Real init fixture (G4 + G5)
$ apm init --marketplace --yes my-mkt
$ grep -A 4 'outputs:' my-mkt/apm.yml | head -5
  outputs:
    claude: {}
    # codex: {}
    #
    # Note: enabling codex requires every package below to declare

$ grep tag_pattern my-mkt/apm.yml
      # tag_pattern: "{name}-v{version}"
      # tag_pattern: "{name}-v{version}"

Single-line codex toggle present (G4); snake_case tag_pattern on both the example package and the local-path entry (G5).

Scenario Evidence

User-promise scenario Test path APM principle
Producer packs a marketplace-only project and sees what shipped tests/integration/marketplace/test_pack_ux_e2e.py::TestVendorNeutralCatalog::test_catalog_lists_both_profiles_with_docs_pointer Transparency
Producer packs a marketplace-only project and is NOT scolded for a missing plugin.json tests/integration/marketplace/test_pack_ux_e2e.py::TestNoSpuriousPluginJsonWarning::test_marketplace_only_project_no_plugin_json_warning Honesty / Progressive Disclosure
Dry-run pack never advertises artifacts that were not written tests/integration/marketplace/test_pack_ux_e2e.py::TestVendorNeutralCatalog::test_dry_run_suppresses_catalog Honesty
Catalog never leaks a vendor CLI surface inline tests/unit/commands/test_pack.py::test_post_pack_catalog_vendor_neutral Vendor-neutrality
Producer running doctor sees configured vs. supported formats tests/integration/marketplace/test_doctor_integration.py::TestDoctorFormatCoverage::test_partial_coverage_lists_missing_formats Discoverability
Doctor stays silent on the new row when no marketplace block tests/integration/marketplace/test_doctor_integration.py::TestDoctorFormatCoverage::test_no_marketplace_block_skips_row Progressive Disclosure
apm init --marketplace writes a one-keystroke codex toggle tests/unit/marketplace/test_init_template.py::TestRenderMarketplaceBlock::test_single_line_codex_toggle Low-friction on-ramp
Monorepo producer sees tag_pattern per-package in scaffold tests/unit/marketplace/test_init_template.py::TestRenderMarketplaceBlock::test_snake_case_tag_pattern_per_package Discoverability
Schema accepts the scaffolded tag_pattern once uncommented tests/unit/marketplace/test_init_template.py::TestRenderMarketplaceBlock::test_uncommented_tag_pattern_parses_through_schema Round-trip correctness

How to test

  1. git fetch origin danielmeppiel/marketplace-monorepo-ux && git checkout danielmeppiel/marketplace-monorepo-ux.
  2. uv sync --extra dev.
  3. uv run --extra dev pytest tests/integration/marketplace/ tests/unit/commands/test_pack.py tests/unit/commands/test_marketplace_doctor.py tests/unit/marketplace/ tests/unit/test_plugin_exporter.py -q --tb=short -- expect 1220 passed.
  4. uv run --extra dev ruff check src/ tests/ && uv run --extra dev ruff format --check src/ tests/ -- both must be silent.
  5. Optional real-fixture validation: in a separate temp dir, clone or copy a marketplace-only repo (e.g. zava-agent-config), add outputs: {claude: {}, codex: {}} to marketplace:, run apm pack, and confirm the catalog block + single docs pointer + no plugin.json warning. Then run apm marketplace doctor to see the new format coverage row.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

Closes #1322 (jKlaus, monorepo plugins) and #1332 (R. Collet, per-package
versions); addresses #1348 acceptance criteria G2, G3+B, G4,
G5, and G7 (format coverage). Wave 2 of the producer-experience epic.

What changes (vendor-neutral throughout):

- G2 (plugin_exporter): marketplace-publishing projects no longer
  emit the misleading '[!] No plugin.json found' warning when there is
  no bundle to author. When apm.yml ships a 'marketplace:' block,
  plugin.json synthesis is transparent (demoted to a single _rich_info
  pointer at most). A genuine parse-error path keeps the warning.

- G3+B (pack): after a successful build, append a vendor-neutral
  artifact catalog ('Marketplace artifacts ready:' + per-output
  ljust-aligned '[profile] path' rows + one docs pointer to
  producer/publish-to-a-marketplace/#consume-from-any-assistant).
  Suppressed in dry-run. Never names a vendor CLI surface inline
  (no 'copilot plugin install', 'claude plugin install', etc.) -- APM
  is vendor-agnostic; per-assistant install paths live in docs.

- G4 (init_template): 'apm marketplace init' / 'apm init --marketplace'
  scaffold now uses a single-line '# codex: {}' toggle instead of a
  multi-line block. Producers flip it on by removing the '# '.

- G5 (init_template): per-package examples include a commented
  'tag_pattern: "{name}-v{version}"' (snake_case, matching the
  schema) on both the remote example and the local-path example, so
  monorepo authors find the right knob immediately.

- G7 partial (marketplace doctor): a new informational 'format
  coverage' row lists configured vs. supported outputs and nudges
  producers toward formats they don't yet publish (e.g. 'Configured:
  claude. Also supported: codex. Add e.g. codex: {} ...'). Always
  passes; never affects exit code. Skipped when no marketplace block.

Documentation:

- docs/.../publish-to-a-marketplace.md gains a 'Consume from any
  assistant' section with anchor #consume-from-any-assistant -- the
  single source of truth for per-assistant install paths, including
  the confirmed Codex URL.
- packages/apm-guide/.apm/skills/apm-usage/commands.md updated per
  doc-sync rule 4 (apm pack + marketplace doctor entries).
- CHANGELOG.md Unreleased entries integrated into Fixed/Changed.

Validation:

- 1220/1220 unit+integration tests green for impacted suites
  (test_pack, test_marketplace_doctor, test_plugin_exporter, test_init_template,
   test_pack_ux_e2e, test_doctor_integration).
- Lint silent: ruff check + ruff format --check both pass.
- Real-fixture validation on zava-agent-config (7-package monorepo):
  - With outputs: {claude, codex} -> catalog shows aligned [claude]
    and [codex ] columns with single docs pointer.
  - With outputs: {claude} only -> doctor surfaces 'format coverage'
    row nudging codex enablement.
  - No spurious 'No plugin.json' warning at any time.
- Real init: fresh 'apm init --marketplace my-mkt' produces the
  single-line codex toggle and per-package snake_case tag_pattern
  comment as designed.
- Vendor-neutrality regression guard: test asserts the post-pack
  output never contains 'copilot plugin install', 'claude plugin
  install', 'codex plugin install', or 'cursor plugin install'.

Wave 1 (microsoft/apm-action#40) is independent and does not need to
merge before this PR; the action's new pass-through inputs only
become consumer-visible when v1 is re-cut.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 18, 2026 07:20
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves producer first-run UX around marketplace packing, initialization scaffolds, and doctor diagnostics, with matching docs and regression coverage.

Changes:

  • Demotes/suppresses plugin.json synthesis chatter for marketplace authoring flows.
  • Adds a vendor-neutral post-pack marketplace artifact catalog and doctor format coverage row.
  • Updates marketplace init scaffolding, docs, guide skill content, changelog, and tests.
Show a summary per file
File Description
src/apm_cli/bundle/plugin_exporter.py Adjusts plugin.json synthesis messaging and marketplace-block suppression.
src/apm_cli/commands/pack.py Adds marketplace artifact catalog and docs pointer after successful pack.
src/apm_cli/commands/marketplace/doctor.py Adds informational format coverage diagnostics.
src/apm_cli/marketplace/init_template.py Updates marketplace block scaffold for outputs and per-package tag_pattern.
tests/unit/test_plugin_exporter.py Covers new synthesis info/suppression behavior.
tests/unit/marketplace/test_init_template.py Covers updated marketplace block scaffold.
tests/unit/commands/test_pack.py Covers post-pack catalog rendering behavior.
tests/unit/commands/test_marketplace_doctor.py Covers doctor format coverage row.
tests/integration/marketplace/test_pack_ux_e2e.py Adds CLI-level marketplace catalog/no-warning coverage.
tests/integration/marketplace/test_doctor_integration.py Adds integration coverage for doctor format coverage.
docs/src/content/docs/producer/publish-to-a-marketplace.md Documents consumer installation paths by assistant.
packages/apm-guide/.apm/skills/apm-usage/commands.md Updates generated command guidance for pack and doctor UX.
CHANGELOG.md Adds release notes for the marketplace UX changes.

Copilot's findings

Comments suppressed due to low confidence (2)

tests/unit/commands/test_pack.py:192

  • This test does not actually validate column alignment: line.split("]")[0] produces different strings for different profile names whether or not the shorter label is padded, so len(label_lengths) == 2 would still pass if the catalog rendered [codex] unpadded. Compare the rendered label widths or the exact row prefixes so a regression in the alignment logic is caught.
    catalog_rows = [line for line in logger.infos if line.startswith("  [")]
    assert len(catalog_rows) == 2
    # Both rows have an aligned label width (e.g. "[claude]" and "[codex] ")
    label_lengths = {line.split("]")[0] for line in catalog_rows}
    assert len(label_lengths) == 2  # both profiles present

CHANGELOG.md:32

  • These changelog entries also violate the project changelog format by including internal goal labels (G3+B, G4, G7) and long explanatory prose instead of concise one-line entries ending with the PR number only.
- `apm pack` now appends a vendor-neutral catalog after per-output success lines, listing each marketplace artifact and pointing at one docs anchor (`producer/publish-to-a-marketplace/#consume-from-any-assistant`) that enumerates the per-assistant install path. The catalog deliberately never names a vendor CLI surface (Copilot, Claude, Codex, Cursor, ...) -- APM is vendor-agnostic and the install command varies by AI assistant. The block is suppressed in dry-run mode. (#1348, G3+B)
- `apm marketplace init` now scaffolds the outputs map as a single-line `# codex: {}` commented toggle, replacing the verbose multi-line `# codex:\n  #   path: ...` block. Authors enabling Codex output now flip one line, plus a one-line reminder that Codex requires `category:` on each package. (#1348, G4)
- `apm marketplace doctor` adds a new informational `format coverage` row when the project ships a `marketplace:` block: it lists which outputs are configured (`claude`, `codex`, ...) and which supported formats are missing, with a one-line nudge on how to enable them. Always passes (informational); never affects exit code. Skipped when there is no marketplace config. (#1348, G7)
  • Files reviewed: 13/13 changed files
  • Comments generated: 5

dry_run_flags = [False] * len(profiles)
path_map = {
"claude": Path(".claude-plugin/marketplace.json"),
"codex": Path(".codex/plugins/marketplace.json"),
Comment thread tests/unit/commands/test_pack.py Outdated
Comment on lines +130 to +132
assert any(
"publish-to-a-marketplace" in line and "consume-from-any-assistant" in line
for line in logger.infos
Comment on lines +370 to +372
# Single docs pointer, no per-vendor install commands.
assert "publish-to-a-marketplace" in out
for forbidden in (
Comment thread CHANGELOG.md Outdated
Comment on lines +12 to +13
- `apm pack` for marketplace-publishing projects (`marketplace:` block in `apm.yml`, no `dependencies:`) no longer prints a misleading yellow `No plugin.json found ... consider running apm init --plugin` warning. The synthesis path from `apm.yml` is the APM-native source of truth; it is now reported as an `[i]` info line when the user is genuinely authoring a bare plugin, and suppressed entirely when the surrounding apm.yml ships a `marketplace:` block. Resolves a consistent producer-confusion signal observed in the wild on monorepo plugin shapes. (#1348, G2)
- `apm marketplace init` and `apm init --marketplace` no longer scaffold a commented `tagPattern: "example-package-v{version}"` example under `packages:` -- the marketplace schema uses snake-case `tag_pattern` per package (the camelCase `tagPattern` is the marketplace-level `build.tagPattern` field and would fail schema validation if uncommented under `packages:`). The scaffold now writes `# tag_pattern: "{name}-v{version}"` so producers who uncomment it get a working monorepo-friendly tag template. (#1348, G5)
Comment thread tests/unit/commands/test_pack.py Outdated
# ---------------------------------------------------------------------------


def _build_report_with_outputs(profiles_paths):
Daniel Meppiel and others added 3 commits May 18, 2026 10:16
- test_marketplace_init: assert single-line '# codex: {}' toggle per G4
  design (was asserting stale verbose codex path).
- test_pack_ux_e2e: fix mocked Codex output path to .agents/plugins/
  marketplace.json (production default in output_profiles.py); rewrite
  URL substring assertion to use urlparse per repo test convention.
- test_pack: add type annotations to _build_report_with_outputs helper;
  rewrite docs-URL assertion to use urlparse.
- CHANGELOG: condense Wave 2 entries to canonical (#1348) format;
  drop internal G2/G3/G4/G5/G7 taxonomy labels.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ommended

Original table presented APM and assistant-native install as
parallel options. APM is the recommended path because it is the only
one that gives consumers a committed apm.lock.yaml, content-hash
pinning, transitive resolution, security scan, and apm audit --ci
drift detection (per consumer/install-packages and
reference/lockfile-spec). Native marketplace commands remain valid
as a fallback for non-APM consumers (no producer-side lock-in), but
trade away the governance posture.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PR #1336 (1e02d12) made MCPIntegrator.install gate calls to
resolve_targets(cwd) and fail closed with NoHarnessError when cwd
lacks harness markers. The 7 new tests added in that PR (in
TestInstallMCPDependencies, TestInstallSelfDefinedSkipLogic,
TestDiffAwareSelfDefinedInstall) mock _install_for_runtime and
_check_self_defined_servers_needing_installation but not
resolve_targets, so they rely on the worker's cwd having harness
markers.

Under pytest-xdist worksteal scheduling, a sibling test can leave
a worker in a tmp cwd (no markers), causing these 7 tests to fail
intermittently with '[x] No harness detected' instead of reaching
the install path.

Add a shared _isolated_targets fixture that patches
apm_cli.core.target_detection.resolve_targets to return
targets=['copilot'], decoupling the install-gate tests from cwd.
Verified by running the file from /tmp (empty cwd): 64/64 pass.

Drive-by fix for #1336; unblocks PR #1362 CI.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@danielmeppiel danielmeppiel merged commit 974d88d into main May 18, 2026
11 checks passed
@danielmeppiel danielmeppiel deleted the danielmeppiel/marketplace-monorepo-ux branch May 18, 2026 09:52
sergio-sisternes-epam pushed a commit that referenced this pull request May 19, 2026
* feat(pack,doctor,init): vendor-neutral producer first-run UX

Closes #1322 (jKlaus, monorepo plugins) and #1332 (R. Collet, per-package
versions); addresses #1348 acceptance criteria G2, G3+B, G4,
G5, and G7 (format coverage). Wave 2 of the producer-experience epic.

What changes (vendor-neutral throughout):

- G2 (plugin_exporter): marketplace-publishing projects no longer
  emit the misleading '[!] No plugin.json found' warning when there is
  no bundle to author. When apm.yml ships a 'marketplace:' block,
  plugin.json synthesis is transparent (demoted to a single _rich_info
  pointer at most). A genuine parse-error path keeps the warning.

- G3+B (pack): after a successful build, append a vendor-neutral
  artifact catalog ('Marketplace artifacts ready:' + per-output
  ljust-aligned '[profile] path' rows + one docs pointer to
  producer/publish-to-a-marketplace/#consume-from-any-assistant).
  Suppressed in dry-run. Never names a vendor CLI surface inline
  (no 'copilot plugin install', 'claude plugin install', etc.) -- APM
  is vendor-agnostic; per-assistant install paths live in docs.

- G4 (init_template): 'apm marketplace init' / 'apm init --marketplace'
  scaffold now uses a single-line '# codex: {}' toggle instead of a
  multi-line block. Producers flip it on by removing the '# '.

- G5 (init_template): per-package examples include a commented
  'tag_pattern: "{name}-v{version}"' (snake_case, matching the
  schema) on both the remote example and the local-path example, so
  monorepo authors find the right knob immediately.

- G7 partial (marketplace doctor): a new informational 'format
  coverage' row lists configured vs. supported outputs and nudges
  producers toward formats they don't yet publish (e.g. 'Configured:
  claude. Also supported: codex. Add e.g. codex: {} ...'). Always
  passes; never affects exit code. Skipped when no marketplace block.

Documentation:

- docs/.../publish-to-a-marketplace.md gains a 'Consume from any
  assistant' section with anchor #consume-from-any-assistant -- the
  single source of truth for per-assistant install paths, including
  the confirmed Codex URL.
- packages/apm-guide/.apm/skills/apm-usage/commands.md updated per
  doc-sync rule 4 (apm pack + marketplace doctor entries).
- CHANGELOG.md Unreleased entries integrated into Fixed/Changed.

Validation:

- 1220/1220 unit+integration tests green for impacted suites
  (test_pack, test_marketplace_doctor, test_plugin_exporter, test_init_template,
   test_pack_ux_e2e, test_doctor_integration).
- Lint silent: ruff check + ruff format --check both pass.
- Real-fixture validation on zava-agent-config (7-package monorepo):
  - With outputs: {claude, codex} -> catalog shows aligned [claude]
    and [codex ] columns with single docs pointer.
  - With outputs: {claude} only -> doctor surfaces 'format coverage'
    row nudging codex enablement.
  - No spurious 'No plugin.json' warning at any time.
- Real init: fresh 'apm init --marketplace my-mkt' produces the
  single-line codex toggle and per-package snake_case tag_pattern
  comment as designed.
- Vendor-neutrality regression guard: test asserts the post-pack
  output never contains 'copilot plugin install', 'claude plugin
  install', 'codex plugin install', or 'cursor plugin install'.

Wave 1 (microsoft/apm-action#40) is independent and does not need to
merge before this PR; the action's new pass-through inputs only
become consumer-visible when v1 is re-cut.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(pack,tests): address PR #1362 review comments and CI failure

- test_marketplace_init: assert single-line '# codex: {}' toggle per G4
  design (was asserting stale verbose codex path).
- test_pack_ux_e2e: fix mocked Codex output path to .agents/plugins/
  marketplace.json (production default in output_profiles.py); rewrite
  URL substring assertion to use urlparse per repo test convention.
- test_pack: add type annotations to _build_report_with_outputs helper;
  rewrite docs-URL assertion to use urlparse.
- CHANGELOG: condense Wave 2 entries to canonical (#1348) format;
  drop internal G2/G3/G4/G5/G7 taxonomy labels.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* docs(publish): reframe consume section to position apm install as recommended

Original table presented APM and assistant-native install as
parallel options. APM is the recommended path because it is the only
one that gives consumers a committed apm.lock.yaml, content-hash
pinning, transitive resolution, security scan, and apm audit --ci
drift detection (per consumer/install-packages and
reference/lockfile-spec). Native marketplace commands remain valid
as a fallback for non-APM consumers (no producer-side lock-in), but
trade away the governance posture.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(tests): stub resolve_targets in MCP install tests

PR #1336 (fc48f83) made MCPIntegrator.install gate calls to
resolve_targets(cwd) and fail closed with NoHarnessError when cwd
lacks harness markers. The 7 new tests added in that PR (in
TestInstallMCPDependencies, TestInstallSelfDefinedSkipLogic,
TestDiffAwareSelfDefinedInstall) mock _install_for_runtime and
_check_self_defined_servers_needing_installation but not
resolve_targets, so they rely on the worker's cwd having harness
markers.

Under pytest-xdist worksteal scheduling, a sibling test can leave
a worker in a tmp cwd (no markers), causing these 7 tests to fail
intermittently with '[x] No harness detected' instead of reaching
the install path.

Add a shared _isolated_targets fixture that patches
apm_cli.core.target_detection.resolve_targets to return
targets=['copilot'], decoupling the install-gate tests from cwd.
Verified by running the file from /tmp (empty cwd): 64/64 pass.

Drive-by fix for #1336; unblocks PR #1362 CI.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Daniel Meppiel <copilot-rework@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants