Skip to content

feat: marketplace maintainer UX (#722)#790

Draft
sergio-sisternes-epam wants to merge 9 commits intomainfrom
feat/722-marketplace-maintainer-ux
Draft

feat: marketplace maintainer UX (#722)#790
sergio-sisternes-epam wants to merge 9 commits intomainfrom
feat/722-marketplace-maintainer-ux

Conversation

@sergio-sisternes-epam
Copy link
Copy Markdown
Collaborator

Description

First pass of the maintainer-side marketplace tooling tracked in #722. Builds on the foundations landing in #677 (semver engine, MarketplaceValidator skeleton, security advisories, publish skeleton).

Design context and the full UX proposal are captured in the discussion thread on the tracking issue: #722 (comment).

Hard rule driving this work

marketplace.json is Anthropic's standard, unaltered. APM emits the artifact byte-for-byte against Anthropic's schema; APM-only build inputs live in marketplace.yml and are stripped at compile time. A golden-file test enforces round-trip compatibility with Claude Code.

High-level scope

  • apm marketplace init — scaffold marketplace.yml
  • apm marketplace build — compile marketplace.yml -> Anthropic-compliant marketplace.json (concurrent ref resolution, diff output, dry-run)
  • apm marketplace check — validation + freshness for CI (--strict, per-rule toggles)
  • apm marketplace outdated — maintainer-side discovery of stale ranges
  • apm marketplace publish — transactional add/update of a package entry (PR-first, --resume/--abort recovery)
  • apm marketplace doctor — preflight probe for git / gh / tokens / ls-remote

Detailed plan, UX previews, and the 45-item UX-risk absorption map live in the session plan and will be distilled into a design doc / changelog entry during delivery.

Fixes #722

Type of change

  • Bug fix
  • New feature
  • Documentation
  • Maintenance / refactor

Testing

  • Tested locally
  • All existing tests pass
  • Added tests for new functionality (if applicable)

Draft — work in progress. Please hold review until marked ready.

Sergio Sisternes and others added 2 commits April 20, 2026 18:02
Empty commit to open the draft PR. Implementation follows.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…or, publish (#722)

Introduces a full marketplace-maintainer command surface designed around
an authored marketplace.yml that compiles to an Anthropic-compliant
marketplace.json (byte-for-byte), plus a publisher that updates
downstream consumers via PR.

New commands:
- apm marketplace init       scaffold marketplace.yml
- apm marketplace build      compile yml -> marketplace.json
- apm marketplace outdated   show packages with newer upstream refs
- apm marketplace check      validate refs, tag_pattern, schema
- apm marketplace doctor     diagnose repo/tooling issues
- apm marketplace publish    push updates to consumer apm.yml via PR

Library modules (src/apm_cli/marketplace/):
- yml_schema, builder, tag_pattern, ref_resolver, semver
- init_template, publisher, pr_integration, git_stderr

Design invariant: marketplace.json matches Anthropic's standard exactly.
APM-only fields (build:, per-entry version ranges, ref:, subdir:,
tag_pattern:, includePrerelease:) live only in marketplace.yml and are
stripped during compile. metadata: is verbatim pass-through; packages:
is renamed to plugins: per Anthropic's schema.

Consumer updates follow the existing apm.yml dependencies.apm string
format (plugin@marketplace[#ref]). Raw git refs only - semver ranges
are not accepted in the consumer syntax.

Docs: new guide at docs/src/content/docs/guides/marketplace-authoring.md,
CLI reference entries, and skill updates for commands + package-authoring.

Tests: 4824 unit+integration tests passing (68 new integration tests,
7 live e2e tests default-skipped behind APM_E2E_MARKETPLACE).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread tests/unit/marketplace/test_pr_integration.py Dismissed
sergio-sisternes-epam pushed a commit to sergio-sisternes-epam/apm that referenced this pull request Apr 20, 2026
Wrap the state-file path in rich.text.Text(..., no_wrap=True) so it
renders on a single line and does not break the publish-state.json
substring that CI tests rely on when the terminal width is 80 cols.

(microsoft#790)

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

Rich was breaking `publish-state.json` across lines at 80-col width, which
(a) made the path uncopyable for users and (b) caused CI test assertions
checking for the substring to fail. Route the final state-file line through
`console.print(..., soft_wrap=True)` with a `Text(..., no_wrap=True)` so the
path is preserved verbatim regardless of terminal width.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sergio-sisternes-epam sergio-sisternes-epam force-pushed the feat/722-marketplace-maintainer-ux branch 2 times, most recently from 6ec634c to 72d1b7b Compare April 20, 2026 19:37
Introduce `apm marketplace plugin {add,set,remove}` for programmatic
management of marketplace.yml entries. Uses ruamel.yaml for round-trip
YAML editing that preserves comments and formatting.

- `plugin add <source>` appends a validated entry with remote verification
- `plugin set <name>` updates fields on an existing entry
- `plugin remove <name>` deletes an entry with confirmation prompt

Includes 37 unit tests, documentation updates (authoring guide, skill
reference, CHANGELOG), and ruamel.yaml>=0.18.0 as a new dependency.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sergio-sisternes-epam sergio-sisternes-epam force-pushed the feat/722-marketplace-maintainer-ux branch from 72d1b7b to 56c20fe Compare April 21, 2026 10:21
Address UX review findings for the plugin subgroup:

- Show subcommand names in plugin group help text
- Guard `plugin set` against zero-field invocations
- Standardise `plugin remove` confirmation via click.confirm
- Extract shared _is_interactive() helper to _helpers.py
- Remove dead --no-verify flag from `plugin set`
- Document plugin commands in CLI reference

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sergio-sisternes-epam
Copy link
Copy Markdown
Collaborator Author

APM Expert Review Panel -- PR #790

Reviewers: Python Architect, CLI Logging Expert, DevX UX Expert, Supply Chain Security Expert, APM CEO, OSS Growth Hacker

Panel Verdict: Approve with conditions

This is APM's most significant feature since apm install. The marketplace authoring toolkit transforms APM from a consumer of curated registries into the toolchain that powers registry creation itself -- a capability no competitor (npm, cargo, homebrew) offers. The architecture is sound, the test coverage is excellent (1.5:1 test-to-code ratio, 4794 tests), and the feature is entirely additive with zero breaking changes.

Two conditions must be met before merge. Seven high-priority findings should be tracked for fast-follow.


Conditions for Merge

# Condition Owner Rationale
C1 File a follow-up issue to extract commands/marketplace.py (1,835 LOC) into per-command modules under commands/marketplace/ Architect / CEO God-files kill review velocity; this is APM's most complex command group
C2 Wire the authoring guide into the docs sidebar (astro.config.mjs) and add consumer-to-author cross-links Growth Without this, the feature ships invisible to 90%+ of docs visitors

Findings by Severity

Critical (4 findings)

# Area Finding Reviewer
S1 Security output field in marketplace.yml has no path traversal guard -- ../../.bashrc would be written outside the repo Security
L1 Logging publish state-file line crashes without Rich fallback (bare from rich.text import Text with no try/except) Logging
L2 Logging _helpers._get_console() never returns None -- all colorama fallback branches across 9 render functions are dead code Logging
UX1 UX validate and check are two confusingly overlapping validation commands with no clear differentiation DevX

High / Important (14 findings)

# Area Finding Reviewer
S2 Security Token redaction regex misses http:// URLs and ?token= query params; copy-pasted in 3 files Security
S3 Security git ls-remote does not set GIT_TERMINAL_PROMPT=0 -- can hang on interactive credential prompt Security
S4 Security ConsumerTarget.repo and branch are not validated against injection patterns Security
A1 Architecture 3 identical _atomic_write() implementations across builder, yml_editor, publisher Architect
A2 Architecture 3 identical _redact_token() / _TOKEN_RE copies across ref_resolver, publisher, pr_integration Architect
A3 Architecture Builder.resolve() uses hidden state smuggling (self._resolve_errors) instead of returning a result object Architect
A4 Architecture Cross-module private import: yml_editor imports _SOURCE_RE from yml_schema Architect
A5 Architecture check command reimplements builder's ref-matching logic Architect
L3 Logging Stack traces swallowed in verbose mode -- all except Exception handlers print one-line error regardless of --verbose Logging
L4 Logging doctor hardcodes GITHUB_TOKEN/GH_TOKEN env var names instead of using AuthResolver Logging
UX2 UX Three different confirmation patterns across destructive commands; two hang in CI without --yes DevX
UX3 UX Docs list --marketplace-yml PATH option that doesn't exist in code DevX
UX4 UX plugin add doesn't enforce --version/--ref mutual exclusivity at CLI level DevX
UX5 UX outdated has no summary line on default path and no CI-friendly exit code DevX

Notes (11 findings)

# Area Finding Reviewer
S5 Security version_pins.py fail-open design: deleted pin file silently disables ref-swap detection Security
A6 Architecture __init__.py eagerly imports all 40+ symbols, defeating lazy loading Architect
A7 Architecture locals().get("pr") anti-pattern in publish command Architect
L5 Logging 18 bare click.echo() calls bypass logging infrastructure Logging
L6 Logging doctor doesn't use DiagnosticCollector (hand-rolls its own) Logging
L7 Logging Rich markup [dim]...[/dim] used directly in console.print() (markup injection precedent) Logging
UX6 UX Bare except Exception swallows actionable context in consumer commands DevX
UX7 UX publish confirmation uses hand-rolled prompt instead of click.confirm DevX
UX8 UX doctor doesn't check gh CLI presence despite docs claiming it does DevX
UX9 UX plugin subgroup missing list subcommand (incomplete CRUD) DevX
G1 Growth No consumer-to-author bridge in docs (consumer guide, first-package, README) Growth

What's Done Well

The panel unanimously highlighted these strengths:

Area Assessment
Error hierarchy Clean MarketplaceError tree with actionable messages and suggested commands
Frozen dataclasses Consistent frozen=True discipline across all domain models -- thread-safe by default
Security posture yaml.safe_load everywhere, zero shell=True, token redaction on all error paths, path traversal guards on source/subdir fields
Injectable dependencies PrIntegrator(runner=), Publisher(clock=, runner=), RefResolver -- composition over inheritance
Builder pipeline Clean load -> resolve -> compose -> write with concurrent resolution and per-remote locks
Semver implementation Dependency-free, correct prerelease ordering, AND-composition of ranges
Test coverage 1.5:1 test-to-code ratio; every module has dedicated unit + integration tests
Authoring guide Quickstart in 4 commands, complete schema reference, troubleshooting table, recipes section
Scaffolding UX init generates richly-commented YAML with inline docs, working examples, and next-steps panel

Strategic Assessment (CEO)

Positioning: This is APM's moat-defining feature. No competitor offers self-hosted marketplace authoring with semver resolution and cross-repo PR-driven publish. This is the npm publish moment for AI agent registries.

Naming: marketplace namespace and plugin subgroup ratified. Advisory: add one sentence bridging packages: (yml) vs plugin (CLI) naming in the authoring guide.

Dependency: ruamel.yaml addition justified -- no alternative for comment-preserving YAML editing. Correctly lazy-imported (only loaded on plugin add/set/remove).

Release: This merits a dedicated v0.9.0 minor release, not a patch. Story angle: "Build your own AI plugin registry in 4 commands."


Priority Fix Order

Priority Findings Effort
P0 -- before merge S1 (output path traversal), C1 (file split issue), C2 (sidebar + cross-links) Small-medium
P1 -- before merge S3 (GIT_TERMINAL_PROMPT), A7 (locals().get), L1 (Rich crash) Small
P2 -- fast follow S2 (token regex), S4 (target validation), A1-A2 (DRY), L3 (verbose tracebacks) Medium
P3 -- next iteration A3 (state smuggling), A5 (check duplication), UX1 (validate vs check), UX9 (plugin list) Medium-large
Backlog All notes-severity findings Small each

Panel composition and routing

Six specialist agents reviewed independently, with findings consolidated by the orchestrator:

  • Python Architect: Module structure, design patterns, coupling, extensibility (11 findings)
  • CLI Logging Expert: CommandLogger usage, Rich fallbacks, verbose mode, output structure (12 findings)
  • DevX UX Expert: Command naming, help text, flags, interactive/CI patterns (10 findings)
  • Supply Chain Security Expert: Token handling, path safety, YAML safety, injection vectors (8 findings)
  • APM CEO: Positioning, naming, scope, dependency, release strategy (7 strategic assessments)
  • OSS Growth Hacker: Discoverability, story angles, contributor funnel, README (10 findings)

Per panel protocol, specialists raised findings independently. The CEO arbitrated strategic calls (naming ratification, dependency approval, release framing). The Growth Hacker annotated discoverability gaps and escalated the sidebar registration as critical.

Sergio Sisternes and others added 3 commits April 21, 2026 18:39
Security:
- Add path traversal guard on marketplace.yml output field (S1)
- Suppress git credential prompts with GIT_TERMINAL_PROMPT=0 (S3)
- Validate ConsumerTarget repo/branch against injection (S4)

Architecture:
- Replace locals().get("pr") with explicit variable (A7)
- Make SOURCE_RE public in yml_schema (A4)

Logging:
- Add Rich fallback for publish state-file display (L1)

UX:
- Enforce --version/--ref mutual exclusivity in plugin add (UX4)
- Remove phantom --marketplace-yml from CLI reference docs (UX3)

Docs:
- Wire marketplace authoring guide into docs sidebar (C2)
- Add consumer-to-author cross-link in marketplace guide (G1)

12 new tests covering all security and UX fixes.
Resolves panel findings S1, S3, S4, A7, A4, L1, UX3, UX4, C2, G1.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Architecture:
- Extract shared atomic_write() to marketplace/_io.py (A1)
- Extract shared redact_token() to marketplace/_git_utils.py (A2)
- Fix token redaction regex to cover http:// and ?token= (S2)

Logging:
- Add verbose traceback output to 5 exception handlers (L3)

UX:
- Add summary line to outdated command output (UX5)
- Exit code 1 when packages are outdated, matching npm/pip (UX5)

17 new tests covering DRY utilities, verbose tracebacks, and
outdated summary/exit behaviour.

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

- Add GIT_TERMINAL_PROMPT=0 and GIT_ASKPASS=echo to publisher._run_git()
  chokepoint (8 subprocess calls including clone/push) — completes S3 fix
- Add --version/--ref mutual exclusivity to plugin set (NEW-1 from panel)
- Update stale _TOKEN_RE docstring references in publisher and pr_integration (N2)
- Tests: TestRunGitEnv (publisher), plugin set conflict test

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sergio-sisternes-epam
Copy link
Copy Markdown
Collaborator Author

APM Expert Review Panel -- Round 2 (Fix Commit Validation)

Scope: 3 fix commits (7169bac, e0eb078, 092c6e4), 26 files, +620/-100
Review of: Fixes for all 16 findings from Round 1 (P0/P1/P2) + Round 2 action items

Panel Verdict: APPROVE (6/6 specialists)

Specialist Verdict Summary
Python Architect APPROVE DRY consolidation clean, aliasing pragmatic, no new anti-patterns
CLI Logging Expert APPROVE All logging fixes correct, verbose tracebacks well-placed
DevX UX Expert APPROVE UX fixes clean, exit-code convention correct
Supply Chain Security APPROVE WITH NOTES S1/S2/S4 solid; S3 gap in publisher _run_git() fixed in 092c6e4
OSS Growth Hacker APPROVE WITH NOTES Sidebar + cross-links correct, recommends exit-code docs
APM CEO APPROVE Both merge conditions satisfied. PR cleared for merge.

Merge Conditions -- Verified


Security Fixes -- Validated

ID Fix Security Review
S1 Output path traversal -- defence-in-depth (parse + resolve) PASS -- both layers use sanctioned path_security.py guards
S2 Token regex consolidated -- covers https://, http://, ?token= PASS -- 10 test vectors verified
S3 GIT_TERMINAL_PROMPT=0 on all git subprocesses PASS -- ref_resolver + publisher _run_git() both hardened
S4 ConsumerTarget injection -- _SAFE_REPO_RE + _SHELL_META_RE PASS -- 16 bypass vectors tested, shell=True never used

Architecture Fixes -- Validated

ID Fix Architecture Review
A1 3x _atomic_write consolidated to _io.py PASS -- laser-focused module, __all__ declared
A2 3x _redact_token consolidated to _git_utils.py PASS -- alias pattern pragmatic, zero call-site churn
A4 _SOURCE_RE made public as SOURCE_RE PASS
A7 locals().get("pr") replaced with explicit variable PASS

Logging + UX Fixes -- Validated

ID Fix Review
L1 Rich Text crash fallback PASS -- click.echo fallback correct
L3 Verbose tracebacks in 5 error handlers PASS -- well-placed, SystemExit re-raised
UX3 Phantom --marketplace-yml removed from docs PASS
UX4 --version/--ref mutual exclusivity (plugin add + plugin set) PASS
UX5 outdated summary line + exit code 1 PASS -- matches npm/pip convention
C2 Sidebar wiring for authoring guide PASS
G1 Consumer-to-author cross-link PASS

New Findings from Round 2 (Non-Blocking)

ID Severity Finding Status
N1 Low builder._atomic_write is now a pure passthrough -- can inline Deferred
N2 Low Stale _TOKEN_RE docstring references Fixed (092c6e4)
N3 Low Redundant (BuildError, Exception) catch tuple in outdated Deferred
N4 Low Error rows uncounted in outdated summary Deferred
NEW-1 Medium plugin set needs --version/--ref mutual exclusivity Fixed (092c6e4)

Post-Merge Recommendations

  1. Tag merge commit for v0.9.0 release notes
  2. CHANGELOG lead: "Build your own AI plugin registry in 4 commands"
  3. Monitor Issue refactor: split commands/marketplace.py into per-command modules #821 for community pickup (contributor funnel test)
  4. Track version-pins fail-open (S5) as known-accepted risk with [security] label

Test Coverage

4825 tests passing (+31 new tests from fix commits), 0 failures.

Every security fix has corresponding test coverage:

  • Path traversal: 4 tests (parse + resolve layers)
  • Token redaction: 7 tests (including mixed patterns)
  • Credential prompt suppression: 4 tests (ref_resolver + publisher)
  • ConsumerTarget injection: 5 tests (repo + branch vectors)
  • Mutual exclusivity: 2 tests (plugin add + plugin set)

…/set

When no --ref is provided, plugin add now resolves HEAD to a concrete
40-char SHA via git ls-remote before storing it in marketplace.yml.
When --ref HEAD or a branch name is given, a warning is emitted and
the ref is auto-resolved to its current SHA for supply-chain safety.
Explicit SHAs and tags are stored as-is.

Adds resolve_ref_sha() to RefResolver for single-ref lookups.
26 new tests covering all resolution paths.
Updates CLI reference, marketplace guide, and CHANGELOG.

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.

[FEATURE] Add apm marketplace generate command to pack all plugins and produce a marketplace.json

2 participants