Skip to content

feat(marketplace): add sourceBase for package sources#1736

Merged
danielmeppiel merged 6 commits into
mainfrom
danielmeppiel/1519-marketplace-sourcebase
Jun 11, 2026
Merged

feat(marketplace): add sourceBase for package sources#1736
danielmeppiel merged 6 commits into
mainfrom
danielmeppiel/1519-marketplace-sourcebase

Conversation

@danielmeppiel

Copy link
Copy Markdown
Collaborator

feat(marketplace): add sourceBase for package sources

TL;DR

Adds a marketplace-level sourceBase field so marketplace authors can name package repositories relative to an enterprise git base path. Per-entry host-prefixed, full HTTPS URL, and local sources remain overrides, so this is additive when sourceBase is absent. This implements @leocamello's ratified Option A design and closes #1519.

Closes #1519

Problem (WHY)

Approach (WHAT)

Area Decision
Parse Add optional marketplace.sourceBase with HTTPS/FQDN/path validation and context-aware packages[].source validation.
Compose Resolve host-less remote package sources against the base at build time and carry the canonical URL into output mappers.
Override Leave host-prefixed, full HTTPS URL, and ./ local sources independent from the base.
Compatibility Keep absent-sourceBase manifests on the existing owner/repo -> default-host path.
Docs Document the schema, authoring workflow, guide skill, and changelog entry.

Implementation (HOW)

File What changed
src/apm_cli/marketplace/yml_schema.py Adds SOURCE_BASE_RE, source_base on MarketplaceConfig, strict sourceBase parsing, and context-aware package source validation.
src/apm_cli/marketplace/yml_editor.py Mirrors loader validation for package edits and derives names from the final source segment so single-segment relative sources work.
src/apm_cli/marketplace/builder.py Composes base-relative sources before ref resolution while preserving per-host resolver routing for overrides.
src/apm_cli/marketplace/output_mappers.py Emits canonical HTTPS URLs for composed entries in Claude and Codex outputs without changing existing github shorthand output.
tests/unit/marketplace/test_marketplace_source_base.py Covers composition, override precedence, inherited guard failures, and no-base compatibility.
Docs and changelog Updates schema reference, marketplace publishing guide, packaged usage guide, and [Unreleased].

Diagram

The diagram shows the new build-time routing decision. Only host-less remote entries compose onto sourceBase; overrides bypass it.

flowchart LR
    A["marketplace.sourceBase"] --> C["packages source"]
    C --> D{"local path"}
    D -->|"yes"| L["emit local source unchanged"]
    D -->|"no"| H{"host or HTTPS URL present"}
    H -->|"yes"| O["use per-entry override"]
    H -->|"no"| B{"base declared"}
    B -->|"yes"| N["compose base plus source"]
    B -->|"no"| G["preserve default-host owner/repo behavior"]
Loading

Trade-offs

  • Keeps Option B deferred: arbitrary-depth host-prefixed shorthand without a base remains out of scope, matching the board decision.
  • Stores a canonical source_url only on resolved packages rather than changing the resolver contract; this keeps the resolver URL-driven.
  • Requires explicit github.com/owner/repo inside a sourceBase marketplace when the package really lives on public GitHub; this is the intended confused-deputy fix.
  • Keeps sourceBase out of generated marketplace JSON; consumers receive standard Claude/Codex source objects.

Benefits

  1. Marketplace authors can publish packages from nested GitLab or enterprise group paths without repeating the base on every entry.
  2. Existing manifests without sourceBase retain their previous parse and output behavior.
  3. Cross-host packages remain explicit through host-prefixed or full URL overrides.
  4. The source-routing validation surface inherits the fix: add support for custom github endpoints (host.tld/owner/repo) as apm.yml entrypoint for command apm pack (marketplace) #1288 guard set and adds path-depth checks.

Validation

Scenario evidence

Scenario APM promise Proof
Relative package sources compose onto sourceBase. Produce tests/unit/marketplace/test_marketplace_source_base.py::TestSourceBaseBuildComposition::test_composes_relative_source_onto_base_for_resolution_and_output
Host-prefixed entries override the base. Govern tests/unit/marketplace/test_marketplace_source_base.py::TestSourceBaseBuildComposition::test_host_prefixed_source_overrides_source_base
Guard violations fail closed. Govern tests/unit/marketplace/test_marketplace_source_base.py::TestSourceBaseSchema::test_rejects_source_base_security_guard_violations
No-base manifests keep owner/repo behavior. Consume tests/unit/marketplace/test_marketplace_source_base.py::TestSourceBaseSchema::test_absent_source_base_keeps_owner_repo_source_unchanged
Commands run
uv run --extra dev pytest tests/unit/marketplace tests/unit/commands/test_marketplace_*.py tests/integration/test_yml_schema_validation.py tests/integration/test_yml_schema_phase3w5.py tests/integration/test_marketplace_builder_hermetic.py tests/integration/test_marketplace_builder_phase3w4.py -q --tb=short
2112 passed in 11.08s

uv run --extra dev ruff check src/ tests/ && uv run --extra dev ruff format --check src/ tests/ && uv run --extra dev python -m pylint --disable=all --enable=R0801 --min-similarity-lines=10 --fail-on=R0801 src/apm_cli/ && bash scripts/lint-auth-signals.sh
All checks passed!
1238 files already formatted
pylint rated 10.00/10
[+] auth-signal lint clean

python3 guardrail script for YAML I/O, file length, and raw relative_to checks
guardrails clean

TMPDIR=$PWD/.copilot-pr/tmp npm_config_cache=$PWD/.copilot-pr/npm-cache npx --yes -p @mermaid-js/mermaid-cli mmdc ... --quiet
mermaid validation passed

How to test

  • Add sourceBase: https://gitlab.example.com/group/marketplace to a marketplace block.
  • Add packages[].source: tool-name with a ref or version.
  • Run apm pack --dry-run --offline against cached refs or a test repo and confirm the output source URL includes /group/marketplace/tool-name.
  • Add source: github.com/owner/repo and confirm it emits as an override rather than under the base.
  • Remove sourceBase and confirm bare owner/repo entries still parse as before.

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

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 10, 2026 22:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds a marketplace-level sourceBase that lets marketplace authors specify package repositories relative to an enterprise HTTPS git base path, while preserving existing per-entry override forms (host-prefixed, full HTTPS URL, and local ./ sources).

Changes:

  • Extend marketplace schema/config to accept and validate marketplace.sourceBase, and relax packages[].source validation to allow relative sources when a base is present.
  • Compose base-relative sources during build resolution and carry a canonical source_url through to output mappers.
  • Update docs/guides and add unit tests covering composition, override precedence, and security guards.
Show a summary per file
File Description
src/apm_cli/marketplace/yml_schema.py Adds sourceBase parsing/validation and context-aware packages[].source validation.
src/apm_cli/marketplace/builder.py Resolves base-relative sources before ref resolution and plumbs canonical source_url into ResolvedPackage.
src/apm_cli/marketplace/output_mappers.py Emits canonical HTTPS URLs when available (via source_url) while retaining github shorthand where appropriate.
src/apm_cli/marketplace/yml_editor.py Mirrors loader validation in the editor and derives default names from the final source segment.
tests/unit/marketplace/test_marketplace_source_base.py New unit coverage for sourceBase behavior, precedence, and guardrails.
docs/src/content/docs/reference/manifest-schema.md Documents marketplace.sourceBase and the new relative packages[].source behavior.
docs/src/content/docs/producer/publish-to-a-marketplace.md Adds authoring guidance/examples for sourceBase.
packages/apm-guide/.apm/skills/apm-usage/package-authoring.md Updates the packaged usage guide to include sourceBase authoring guidance.
CHANGELOG.md Adds an Unreleased entry for the new sourceBase capability.

Copilot's findings

  • Files reviewed: 9/9 changed files
  • Comments generated: 1

Comment on lines +472 to 475
first_segment = source.split("/", 1)[0]
looks_like_unsupported_host_override = "/" in source and bool(
re.fullmatch(_HOST_PAT, first_segment)
)
Folds copilot-pull-request-reviewer follow-up on the sourceBase
validator. The confused-deputy guard (board-ratified: validation
inherits #1288 guards exactly) is kept intact; instead the
manifest-schema sourceBase section now documents that a relative
source whose FQDN-like first segment forms no valid override shape is
rejected at parse time rather than silently composed onto the base.
Adds a regression test locking that behavior and a clarifying code
comment. Also ends the CHANGELOG entry with the PR number per repo
convention.

Refs #1519

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

Copy link
Copy Markdown
Collaborator Author

APM Review Panel: ship_with_followups

Adds marketplace sourceBase so enterprise authors name packages relative to a git base; per-entry host/URL/local forms stay overrides, absent base is byte-for-byte unchanged.

cc @danielmeppiel @sergio-sisternes-epam -- a fresh advisory pass is ready for your review.

This PR lands @leocamello's ratified Option A cleanly. The panel signals converge strongly: the parse-time guard set inherits #1288 exactly (https-only, FQDN host, reject userinfo/port/query/fragment/.git) and extends it with validate_path_segments + SOURCE_BASE_RE for arbitrary path depth, the composition is build-time only, and the resolver contract is untouched (it stays URL-driven). The one intentional behavior change -- a declared base means bare owner/repo and FQDN-first non-override sources no longer silently route to github.com -- is a net security win (confused-deputy footgun removed), not a regression, and it is fenced behind opt-in sourceBase. Security, auth, and docs all clear. No blocking findings.

The design is also general enough for the #676 / future-ADO marketplace surface: sourceBase is a full HTTPS URL with arbitrary-depth path segments (underscore allowed), so https://dev.azure.com/org/project/_git + relative repo composes correctly and routes through the per-host AuthResolver path. That generality is the right call -- the follow-ups below are about locking it in with evidence, not redesigning anything.

Aligned with: Secure by default -- the sourceBase guard fails closed and removes a silent-default-host exfiltration path; Multi-harness / multi-host -- composed entries route through per-host AuthResolver token selection and emit canonical Claude/Codex URLs, keeping the design ADO-reuse-general; Governed by policy -- source-routing validation surface is centralized and strict (unknown keys rejected, traversal refused).

Panel summary

Persona B R N Takeaway
Supply Chain Security Expert 0 0 0 Guard inherits #1288 exactly; affirm the FQDN-first rejection -- it closes a confused-deputy footgun. Do not loosen.
Auth Expert 0 0 0 Composed host routes through _get_resolver_for_host -> AuthResolver; host->token selection correct, ADO-base reuse holds.
Doc Writer 0 0 0 Schema ref (7.5), publishing guide, packaged skill (Rule 4), and changelog all updated; no drift.
Python Architect 0 1 0 Clean helper extraction; shared _validate_source_value removes editor/loader drift. Add a version-range composition test.
Test Coverage Expert 0 1 0 Additive, override-vs-compose, and guard-rejection regressions all covered; version-range compose path is the one gap.
DevX UX Expert 0 0 1 Relative names + base are ergonomic; reject error names the override forms so authors recover fast.
CLI Logging Expert 0 0 1 Validation messages are ASCII, specific, and per-violation; consistent with existing marketplace errors.
OSS Growth Hacker 0 0 0 Unlocks nested-GitLab / enterprise-group publishing without per-entry repetition -- a real adoption lever.

B = blocking-severity findings, R = recommended, N = nits.
Counts are signal strength, not gates. The maintainer ships.

Top 3 follow-ups

  1. [Test Coverage Expert] Add a version: semver composition test -- The build-composition test exercises the explicit-ref branch; a version-range entry under sourceBase would lock the compose-onto-base behavior for _resolve_version_range too.
  2. [Python Architect] Add an ADO-shaped sourceBase fixture (https://dev.azure.com/org/project/_git + relative repo) -- Proves the cross-host generality Marketplace add source parity with the Anthropic spec #676 will depend on and guards against accidental narrowing of SOURCE_BASE_RE.
  3. [Doc Writer] One-line cross-reference in Section 7.5 toward non-default-host reuse -- Helps future ADO/Marketplace add source parity with the Anthropic spec #676 authors discover that sourceBase already supports their base shape.

Architecture

classDiagram
    class MarketplaceConfig {
      +str|None source_base
      +tuple~PackageEntry~ packages
    }
    class PackageEntry {
      +str source
      +str|None host
    }
    class ResolvedPackage {
      +str source_repo
      +str|None host
      +str|None source_url
    }
    class MarketplaceBuilder {
      +_remote_source_coordinates(entry)
      +_resolved_output_host(source_host, source_url)
      +_resolve_entry(entry)
    }
    MarketplaceConfig "1" o-- "many" PackageEntry
    MarketplaceBuilder ..> MarketplaceConfig : reads source_base
    MarketplaceBuilder ..> PackageEntry : resolves
    MarketplaceBuilder ..> ResolvedPackage : emits
Loading
flowchart TD
    P["parse packages[].source"] --> S["split_host_from_source"]
    S --> H{"host present?"}
    H -->|"yes (override)"| O["use per-entry host + owner/repo"]
    H -->|"no"| B{"sourceBase declared?"}
    B -->|"yes"| C["split_source_base -> compose base + source; set source_url"]
    B -->|"no"| G["preserve default-host owner/repo behavior"]
    O --> R["_get_resolver_for_host -> AuthResolver token select"]
    C --> R
    G --> R
Loading

Recommendation

Ship it. The work is complete, board-ratified, strictly additive, and CI is green across lint, R0801, auth-signal, and the full marketplace test matrix. Track the version-range composition test as the highest-signal follow-up so both resolution branches carry a base-compose regression guard; the ADO fixture and the Section 7.5 cross-reference are cheap insurance for the #676 reuse. None of these block merge -- the maintainer performs the protected-branch merge.


Full per-persona findings

Supply Chain Security Expert

No findings. _parse_source_base enforces the #1288 guard set verbatim (https-only scheme, FQDN host via _HOST_PAT, rejects userinfo/port/query/fragment and trailing .git) and adds validate_path_segments(reject_empty=True) plus SOURCE_BASE_RE for arbitrary-depth segment safety. The FQDN-first relative-source rejection is the correct call: a value that names a host but forms no valid host/owner/repo override is refused rather than silently composed onto the base, removing a confused-deputy exfiltration path. Affirmed -- do not loosen.

Auth Expert

Active. Composed remote entries derive their host from split_source_base and flow through _get_resolver_for_host(source_host), so a non-default base host (e.g. gitlab.example.com, dev.azure.com) is classified and tokened via AuthResolver exactly like a per-entry host override. Bare owner/repo with no base still resolves to the default host. _resolved_output_host correctly returns the base host directly (not _effective_host-normalized) when source_url is set, so the canonical URL is emitted rather than github shorthand. Host->token selection is correct and the ADO-base reuse generality holds.

Doc Writer

Active. No findings. Section 7.5 of manifest-schema.md documents sourceBase shape, override-vs-compose semantics, arbitrary path depth, and the FQDN-first rejection rationale; publish-to-a-marketplace.md shows the authoring workflow; the packaged package-authoring.md skill resource is updated per repo Rule 4; CHANGELOG has an [Unreleased] entry. No drift.

Python Architect

Active.

  • [recommended] Build-composition coverage exercises only the explicit-ref path at tests/unit/marketplace/test_marketplace_source_base.py.
    A version: semver entry under sourceBase would lock the compose-onto-base behavior in _resolve_version_range as well, since both branches now thread source_host/source_url.
    Suggested: add a parametrized version-range case asserting resolved.source_url and source_repo.

    The refactor itself is clean: _remote_source_coordinates and _resolved_output_host isolate the new routing decision, and hoisting _validate_source_value into yml_schema (consumed by yml_editor) eliminates the prior duplicated-validator drift risk. No file-length or R0801 concerns (CI green).

Test Coverage Expert

Active.

  • [recommended] The new test module covers additive/no-base compatibility, override-vs-compose precedence, single-segment rejection without base, FQDN-first rejection, the 10-case guard-violation matrix, and the editor relative-source path -- strong. The one gap is the version: resolution branch for composition.
    Suggested: mirror test_composes_relative_source_onto_base_for_resolution_and_output with a version: "^1.0.0" entry.

DevX UX Expert

Active.

  • [nit] Authoring ergonomics are good: sourceBase + bare relative names removes per-entry base repetition, and the rejection error enumerates the four override forms so an author who hits the confused-deputy guard can recover without reading the spec. Consider surfacing the "use an explicit host-prefixed override or full https URL" hint in the error string itself (docs already state it).

CLI Logging Expert

Active.

  • [nit] Validation messages are ASCII-only, specific per violation (userinfo / port / query / fragment / .git / FQDN / empty-segment), and consistent with existing MarketplaceYmlError phrasing. No logging of token or credential material on the per-host resolver path. No action required.

OSS Growth Hacker

Active. No findings. This directly unlocks the canonical enterprise pattern from #1519 (self-hosted GitLab with subgroup nesting) and lets large internal marketplaces drop per-entry base repetition -- a concrete adoption and contributor-onboarding lever for enterprise users.

Performance Expert -- inactive

Diff touches only parse-time (yml_schema.py, yml_editor.py) and build-time composition (builder.py, output_mappers.py); it does not touch deps/**, cache/**, install/phases/**, install/pipeline.py, install/resolve.py, or transport, and claims no perf win. Per-host resolvers remain cached. No performance lens required.

This panel is advisory. It does not block merge. Re-apply the
panel-review label after addressing feedback to re-run.

@leocamello

Copy link
Copy Markdown

Nice — the panel's ship_with_followups read matches what I saw building the same feature: the parse-time guard set and build-time composition are clean. One runtime follow-up worth folding in before merge, in the same spirit as the panel's test-coverage follow-up.

apm marketplace check stays broken for any sourceBase entry (and for the pre-existing #1288 host-prefixed form), even though apm pack works. The panel couldn't catch this from the diff: check.py isn't in the PR, and there's no check test with a non-default host — so CI is green while the command fails. It only shows up running check against a real non-default host (which is how I hit it end-to-end against a self-managed GitLab).

Why

Composition here is build-time (_remote_source_coordinates / _route_entry), so the parsed PackageEntry still carries host=None and the relative source (my-package). apm marketplace check doesn't go through the builder — it runs its own resolver:

resolver = RefResolver(offline=offline)          # no host, no token
refs = resolver.list_remote_refs(entry.source)   # entry.source is the *relative* path

so it issues git ls-remote https://github.com/my-package.git → exit 128 (wrong host, no token, no composition). pack works only because composition + the per-host resolver live on the builder path that check doesn't share.

Repro

marketplace:
  owner: { name: t }
  sourceBase: https://gitlab.example.com/group/sub/team/project
  packages:
    - name: my-package
      source: my-package
      version: "^0.1.0"
apm pack              # succeeds: marketplace.json has the composed URL + sha
apm marketplace check # fails: "Git failed during ls-remote (exit 128)"

This matters because apm marketplace check is the CI gate enterprise sourceBase authors will reach for first — the feature's headline audience.

Fix shape

Because composition is build-time, check needs to route each entry through the same composition + per-host token logic the builder uses, not a bare default-host resolver. Cleanest is to factor the build-time routing (_remote_source_coordinates + per-host token) into a small shared helper that both builder.py and check.py call (keeps them from drifting).

I worked through exactly this on a draft branch (#1731): a shared marketplace/auth_helpers.resolve_token_for_host, a per-host resolver in check.py, a local-source short-circuit, and three check regressions (composed → host + token, host-prefixed override → github, local → zero ls-remote). My branch composes at parse time so check only needed the per-host resolver; adapting it to this PR's build-time routing is the small delta. Happy to push a commit onto this branch, adapt it, or just leave #1731 as a reference — whatever's least friction.

At minimum, a check regression with a non-default host would keep this from shipping silently broken — it's adjacent to the version-range composition test the panel already flagged.

…ted shapes

Folds the apm-review-panel and Copilot follow-ups for PR #1736 as
regression traps on the sourceBase parse/compose surface, no production
change:

- version-range branch: assert a `version:` entry composes onto
  sourceBase and routes through the composed owner/repo coordinate
  (the prior coverage only exercised the explicit-`ref` branch).
  Mutation-break verified: dropping source_url in _resolve_version_range
  makes the test fail.
- ADO-shaped base: assert https://dev.azure.com/org/project/_git
  validates and composes a relative repo, so the field shape stays
  general enough for the #1010 ADO-reuse-sourceBase path.
- dotted subgroup: assert a relative `team.tools/repo` (dotted owner)
  composes onto the base rather than being treated as a host override,
  locking the accepted behavior the Copilot inline questioned.

addresses CEO follow-up FU-1 (version-range compose) and FU-2 (ADO
fixture); addresses Copilot inline on src/apm_cli/marketplace/yml_schema.py.

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

Copy link
Copy Markdown
Collaborator Author

Shepherd-driver: ready for maintainer review (CI green, mergeable)

Follow-ups from the ship_with_followups panel pass folded as
regression traps on the sourceBase parse/compose surface. No
production code changed; this run is test-only plus this advisory.
The panel already ran -- this is the shepherd terminal note, not a
second panel pass.

Folded in this run

  • (panel FU-1, Test Coverage) version: semver composition test --
    locks that a versioned package composes onto sourceBase and routes
    through the composed owner/repo coordinate on the
    _resolve_version_range branch (prior coverage only exercised the
    explicit-ref branch). Mutation-break gated. Resolved in 2dcb39c5.
  • (panel FU-2, Python Architect/forward-compat) ADO-shaped sourceBase
    fixture -- asserts https://dev.azure.com/org/project/_git validates
    and composes a relative repo. Confirms the field shape is NOT too
    narrow for the feat: ADO marketplace support (marketplace.yml with Azure DevOps repos) #1010 path where ADO REUSES sourceBase rather than
    adding a separate host field. SOURCE_BASE_RE + validators already
    accept this base (no guard loosened). Resolved in 2dcb39c5.
  • (copilot-derived) dotted-subgroup relative source test -- asserts
    team.tools/repo (dotted owner) composes onto the base, locking the
    accepted behavior so a future guard change cannot silently start
    rejecting valid dotted GitLab subgroup names. Resolved in 2dcb39c5.

Copilot signals reviewed

  • src/apm_cli/marketplace/yml_schema.py:480 -- NOT-LEGIT: the inline
    states the cited example team.tools/repo is rejected by the
    FQDN-first guard. Verified empirically it is ACCEPTED (matches the
    two-segment owner/repo shape and composes onto the base). The
    looks_like_unsupported_host_override guard only fires on values
    matching no valid shape with an FQDN-like first segment (e.g.
    host.tld/a/b/c), which is the board-affirmed confused-deputy
    refusal already documented in manifest-schema.md Section 7.5. No
    loosening performed (board scope fence). The concern is converted
    into a positive regression trap (dotted-subgroup test above).

Consumer-install confirm (issue #1010 / #1014 surface) -- FLAGGED, out of this PR's surface

Confirmed via code trace: this PR fixes DISCOVERY/AUTHORING --
output_mappers.py now emits canonical HTTPS URLs (host preserved) for
sourceBase-composed entries into the generated marketplace JSON. But
the consumer INSTALL path still strips the host to github.com. Root
cause: src/apm_cli/marketplace/resolver.py _resolve_url_source()
(approx lines 525-545) parses a url source with
DependencyReference.parse(url) then returns only dep.repo_url
(owner/repo), dropping the host; downstream
github_downloader.py does host = dep_ref.host or default_host(),
so a non-default host (self-managed GitLab, ADO) routes INSTALL to
github.com and suggests the GitHub token -- exactly leocamello's #1010
repro. An in-code comment there already tags this as tracked by #1010.

This fix lives in the CONSUMER install resolver path, OUTSIDE this PR's
stated parse/compose surface (yml_schema.py, yml_editor.py,
builder.py, output_mappers.py), and crosses the board scope fence
"Resolver UNCHANGED (already URL-driven)". Per the fold-vs-defer rubric
it is DEFERRED, not folded, and FLAGGED here for the maintainer as an
in-scope-for-#1519/#1010-surface bug to track separately.

Deferred (out-of-scope follow-ups)

  • (panel/consumer-install) Preserve the marketplace entry's real host on
    the INSTALL path so a url source routes to its true host instead of
    github.com -- scope boundary: lives in marketplace/resolver.py
    _resolve_url_source (consumer install resolver), outside this PR's
    parse/compose surface and behind the board "Resolver UNCHANGED" fence;
    suggested follow-up: track under feat: ADO marketplace support (marketplace.yml with Azure DevOps repos) #1010 (host-preserving URL-source
    resolution).

Regression-trap evidence (mutation-break gate)

  • test_composes_relative_source_onto_base_for_version_range_resolution
    -- deleted the source_url=source_url wiring in
    _resolve_version_range; the test FAILED as expected
    (assert '' == 'https'); guard restored.

Lint contract

uv run --extra dev ruff check src/ tests/ and
uv run --extra dev ruff format --check src/ tests/ both silent;
pylint R0801 10.00/10; auth-signal lint clean.

CI

All required checks green on 2dcb39c5
(Lint, Build & Test Shard 1/2, Coverage Combine, Spec conformance,
NOTICE Drift, APM Self-Check, CodeQL Analyze actions/python, PR Binary
Smoke, gate, build); deploy skipped. 0 CI fix iterations.

Mergeability status

Captured from gh pr view 1736 --json mergeable,mergeStateStatus,headRefOid
immediately after the last push (shepherd-driver step X.8).

PR head SHA CEO stance iters folds defers Copilot rounds CI mergeable mergeStateStatus notes
#1736 2dcb39c5 ship_with_followups 1 3 1 1 green MERGEABLE BLOCKED BLOCKED = awaiting maintainer approval on protected branch; no conflict

Convergence

1 outer iteration; 1 Copilot round. Final panel verdict
(carried from the prior pass): ship_with_followups. All panel
RECOMMENDED follow-ups folded; the only deferral is the consumer-install
host-preservation fix, flagged above as outside this PR's surface.
Ready for maintainer review -- the shepherd does not merge.

@danielmeppiel

Copy link
Copy Markdown
Collaborator Author

APM Review Panel: ship_with_followups

sourceBase lets enterprise marketplace authors define a git base once, eliminating per-package repetition and unlocking multi-host portability without breaking existing manifests.

cc @sergio-sisternes-epam -- a fresh advisory pass is ready for your review.

All nine panelists converge on the same signal: this PR is architecturally sound, security-validated, performance-neutral, and test-covered. The python-architect confirms the frozen-dataclass + Strategy mapper pattern is idiomatic; supply-chain-security confirms HTTPS-only, no userinfo/port/query/fragment/.git, and path-segment guards; test-coverage confirms schema guards, composition branches, output mapping, overrides, ADO shape, and dotted subgroups are all exercised. No panelist raised a blocking or required finding.

The only substantive gap is documentation clarity: the doc-writer's test-backed finding (test_dotted_subgroup_relative_source_composes_onto_base, passed) proves that two-segment owner/repo composes onto sourceBase rather than acting as an override, yet the publish guide example does not make this explicit. This is a real discoverability risk for producers but does not break correctness or security -- the behavior is well-defined and tested.

The resolver/consumer-install host-stripping question is known and owned by #1010's second dispatch; it is not a #1519 issue and no panelist treated it as one.

Dissent. python-architect and devx-ux-expert both flagged the underscore-prefixed cross-module import as a nit. They agree on severity and remedy (rename or export via __all__). No dissent to resolve.

Aligned with: Portable by manifest: sourceBase is a manifest-level declaration that composes without external state, preserving single-file portability. Secure by default: validation enforces HTTPS-only, FQDN host, no userinfo/port/query/fragment, no .git suffix, and path-segment guards. Multi-harness multi-host: sourceBase directly enables enterprise multi-host marketplaces (GitHub, GitLab, ADO) from a single manifest. Pragmatic as npm: absent sourceBase, behavior is unchanged; existing manifests require zero migration.

Growth signal. sourceBase is a launchable enterprise multi-host signal. GitLab and future ADO marketplace authors gain a concrete reason to adopt APM over single-host alternatives. The changelog and producer guide should lead with the enterprise outcome (compose once, consume everywhere) rather than the mechanism name.

Panel summary

Persona B R N Takeaway
Python Architect 0 0 1 sourceBase cross-module coupling is a nit; frozen-dataclass + Strategy mapper pattern is idiomatic and sound.
CLI Logging Expert 0 0 0 No CLI echo, rich helpers, CommandLogger, DiagnosticCollector, or progress output surfaces changed; validation errors follow existing MarketplaceYmlError style.
DevX UX Expert 0 0 2 sourceBase composes intuitively for producers; no user-facing CLI surface or error wording regressed.
Supply Chain Security Expert 0 0 1 sourceBase validation covers critical attack surfaces: https-only, FQDN host, no userinfo/port/query/fragment/.git, and path segment guards.
OSS Growth Hacker 0 0 2 Enterprise sourceBase is a strong multi-host adoption story; current docs and changelog cover it without needing README churn.
Auth Expert 0 0 0 sourceBase-derived hosts route through builder._get_resolver_for_host(source_host) and the existing per-host AuthResolver path; #1010 owns consumer-install resolver follow-up.
Doc Writer 0 3 1 Docs are accurate on validation/edge cases but should state explicitly that two-segment owner/repo also composes under sourceBase.
Test Coverage Expert 0 0 0 New test_marketplace_source_base.py covers schema guards, composition branches, output mapping, editor behavior, overrides, ADO shape, and dotted subgroups.
Performance Expert 0 0 2 No hot-path regression: sourceBase composition is in-memory string work with no extra network RTTs or filesystem I/O.

B = blocking-severity findings, R = recommended, N = nits.
Counts are signal strength, not gates. The maintainer ships.

Top 3 follow-ups

  1. [Doc Writer] Annotate or clarify the two-segment owner/repo example in the publish guide so producers understand it composes onto sourceBase rather than acting as an override. -- Test evidence (test_dotted_subgroup_relative_source_composes_onto_base, passed) confirms the behavior. Without an explicit note, producers may misread owner/repo as GitHub shorthand and get unexpected results.
  2. [Doc Writer] Add a sentence to manifest-schema section 7.5 stating that any non-override relative source, including owner/repo, composes onto sourceBase. -- The common two-segment case is only implied; making it explicit closes the discoverability gap for the reference doc.
  3. [OSS Growth Hacker] Lead the changelog entry with the enterprise benefit before naming the sourceBase mechanism. -- Release readers convert faster when the first words describe the outcome rather than the feature name.

Architecture

classDiagram
    direction LR
    class MarketplaceConfig {
        <<ValueObject>>
        +source_base str or None
        +packages tuple[PackageEntry]
    }
    class PackageEntry {
        <<ValueObject>>
        +source str
        +host str or None
        +is_local bool
    }
    class MarketplaceBuilder {
        <<Orchestrator>>
        +_remote_source_coordinates(entry) tuple
        +_resolve_entry(entry) ResolvedPackage
    }
    class ResolvedPackage {
        <<ValueObject>>
        +source_repo str
        +host str or None
        +source_url str or None
    }
    class OutputMappers {
        <<Strategy Functions>>
        +_remote_source_url(pkg) str or None
    }
    class YmlSchema {
        <<Validation Module>>
        +split_source_base()
        +_parse_source_base()
        +_validate_source_value()
    }
    class YmlEditor {
        <<Round-trip Editor>>
        +add_plugin_entry()
    }
    MarketplaceConfig *-- PackageEntry : contains
    MarketplaceBuilder ..> MarketplaceConfig : reads source_base
    MarketplaceBuilder ..> ResolvedPackage : produces source_url
    OutputMappers ..> ResolvedPackage : emits URL or shorthand
    YmlEditor ..> YmlSchema : reuses validation
    class MarketplaceConfig:::touched
    class MarketplaceBuilder:::touched
    class ResolvedPackage:::touched
    class OutputMappers:::touched
    class YmlSchema:::touched
    class YmlEditor:::touched
    classDef touched fill:#fff3b0,stroke:#d47600
Loading
flowchart TD
    A[apm pack marketplace] --> B[load apm.yml]
    B --> C{package source local?}
    C -->|yes| D[emit local source unchanged]
    C -->|no| E{entry host or HTTPS override?}
    E -->|yes| F[use per-entry host and repo]
    E -->|no| G{sourceBase set?}
    G -->|yes| H[split sourceBase into host and path]
    H --> I[compose base path plus package source]
    G -->|no| J[preserve default owner/repo behavior]
    F --> K[resolve refs with host-specific resolver]
    I --> K
    J --> K
    K --> L[ResolvedPackage with source_url when composed]
    L --> M[Claude/Codex output mappers]
    M --> N{source_url present?}
    N -->|yes| O[emit canonical HTTPS URL]
    N -->|no| P[emit host URL or github shorthand]
Loading

Recommendation

Merge now. CI is green, all panelists agree on soundness, and no blocking or required findings exist. Track the doc-writer's two publish-guide clarifications as a fast follow-up PR -- they improve producer discoverability but do not affect correctness or security.


Full per-persona findings

Python Architect

  • [nit] Cross-module import of private helpers _parse_source_base / _validate_source_value from yml_schema into yml_editor at src/apm_cli/marketplace/yml_editor.py
    Prefixing with underscore signals module-private scope. When yml_editor imports these, it couples to an unstable internal API. If validation evolves, the private contract may change without notice. This is a nit because the sibling-module coupling radius is bounded.
    Suggested: Rename to parse_source_base / validate_source_value and export via __all__, or extract to a shared marketplace validation module.

CLI Logging Expert

No findings.

DevX UX Expert

  • [nit] Producer guide could clarify owner/repo path semantics under sourceBase
    A producer seeing sourceBase may wonder whether relative paths like owner/repo are resolved against the base or are default-host shorthand. One sentence or inline YAML comment naming base + relative = full URL would avoid trial-and-error.
  • [nit] Private-looking helper names imported cross-module reduce contributor discoverability
    Underscore-prefixed helpers imported across sibling modules signal module-private APIs. Renaming or exporting would better convey the intended shared validation contract.

Supply Chain Security Expert

  • [nit] Trailing slash normalization removes only one slash before reject_empty catches the rest
    A multi-trailing-slash value remains invalid because validate_path_segments with reject_empty catches empty path segments. rstrip('/') would be a cosmetic clarity improvement, not a security fix.

OSS Growth Hacker

  • [nit] Changelog hook could lead with enterprise outcome before naming sourceBase
    Release readers convert faster when the first words describe the benefit (enterprise marketplace paths without repetition) rather than the mechanism.
  • [nit] A before/after YAML snippet would make the producer value more immediately visible at docs/src/content/docs/producer/publish-to-a-marketplace.md
    Enterprise users scanning docs benefit from seeing repeated full paths collapse into one sourceBase plus short sources. This is conversion polish, not required correctness.

Auth Expert

No findings.

Doc Writer

  • [recommended] publish guide example uses acme-org/pinned-package under sourceBase without explaining that it composes onto the base at docs/src/content/docs/producer/publish-to-a-marketplace.md:109
    With sourceBase set, plain owner/repo is not an override; the builder composes it onto the base. A reader may mistake acme-org/pinned-package for default GitHub shorthand unless the example comments the composed result or uses an explicit host-prefixed override.
    Suggested: Annotate the entry as composed, change it to github.com/acme-org/pinned-package if it is meant to demonstrate an override, or make it single-segment like the first example.
    Proof (passed): tests/unit/marketplace/test_marketplace_source_base.py::TestSourceBaseBuildComposition::test_dotted_subgroup_relative_source_composes_onto_base -- proves: A two-segment source composes onto sourceBase rather than acting as an override. [devx,portability-by-manifest]
    assert resolved.source_repo == 'platform/marketplaces/team.tools/repo'
  • [recommended] manifest schema section 7.5 does not explicitly say owner/repo composes when sourceBase is set at docs/src/content/docs/reference/manifest-schema.md:812
    Section 7.5 documents single-segment composition and override forms, but the common two-segment owner/repo case is only implied. The implementation composes any non-override relative source, including owner/repo and deeper paths.
    Suggested: Add a sentence: when sourceBase is set, any source that is not a host-prefixed, full HTTPS URL, or local ./ override composes onto the base, including owner/repo and deeper relative paths.
  • [recommended] Override-rule prose is duplicated across docs and apm-guide at packages/apm-guide/.apm/skills/apm-usage/package-authoring.md
    The override rule now appears in the producer guide, manifest schema, and apm-guide. The reference should be canonical, with shorter summaries elsewhere to reduce drift.
    Suggested: Keep the sample and point readers to manifest-schema.md section 7.5 for the full sourceBase rules.
  • [nit] Appendix B revision history was not bumped for the new normative sourceBase field at docs/src/content/docs/reference/manifest-schema.md:963
    The manifest schema reference keeps a revision history for schema-surface additions. sourceBase is a normative field and should have a row.
    Suggested: Add a revision row noting marketplace.sourceBase and Section 7.5 semantics.

Test Coverage Expert

No findings.

Performance Expert

  • [nit] split_source_base called per marketplace entry adds negligible repeated string work
    A short string split per package is below the noise floor even for large marketplaces and does not change network or filesystem cost.
  • [nit] urlparse and regex validation of sourceBase is a one-shot parse cost
    Validation runs once during YAML load on a short string and is not on the resolver network hot path.

This panel is advisory. It does not block merge. Re-apply the panel-review label after addressing feedback to re-run.

danielmeppiel and others added 3 commits June 11, 2026 14:28
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@danielmeppiel

Copy link
Copy Markdown
Collaborator Author

APM Review Panel: ship_now

sourceBase generalizes marketplace package resolution beyond host/owner/repo, unlocking enterprise Git topologies (ADO, GitLab subgroups) with zero migration cost for existing manifests.

cc @sergio-sisternes-epam -- a fresh advisory pass is ready for your review.

Nine panelists converge on ship with no blocking findings. The design is strictly additive: sourceBase defaults to None, existing manifests see no behavior change, and validation inherits the battle-tested #1288 guard chain (HTTPS-only, FQDN, reject userinfo/port/query/fragment, validate_path_segments). The python-architect confirms frozen-dataclass composition with cached lazy-init is architecturally sound; supply-chain-security confirms defense-in-depth layering with no bypass; auth-expert confirms host extraction feeds cleanly into the existing resolver without credential leak; performance-expert confirms no hot-path regression.

The only item that rose above nit-tier from multiple panelists is the doc-writer's observation that the publishing guide demonstrates sourceBase composition twice (inline comments and a Before/After block). The devx-ux-expert simultaneously recommends the Before/After as the stronger teaching device while requesting a cross-link to Section 7.5 and a one-line hook. These are complementary polish, not conflicts. I side with shipping the current doc surface as-is and tracking the de-duplication as optional post-merge polish -- the Before/After block is the clear winner and the inline comments do no harm during the enterprise launch beat.

The test-coverage-expert raised a missing Codex-mapper integration assertion. Per the evidence contract: this is outcome:missing on a multi-harness-support surface, which inherits elevated weight. However, the expert's own rationale acknowledges both mappers invoke the identical _remote_source_url helper, making this a low-risk gap. I weight it as the top recommended follow-up but not merge-blocking given the shared code path and existing Claude-mapper assertion proving the composition logic.

Dissent. No dissent between panelists. The doc-writer and oss-growth-hacker both flagged the publish guide's dual-illustration pattern; doc-writer leans toward trimming one, growth-hacker wants to keep the visual diff. Both are optional polish and do not conflict on ship posture.

Aligned with: portable by manifest: sourceBase is a manifest-level declaration enabling portable resolution across arbitrary Git hosting topologies without per-package URL duplication; secure by default: validation inherits #1288 guards exactly; multi-harness multi-host: composition emits canonical HTTPS URLs consumed by mapper strategies; pragmatic as npm: declare once, reference short names.

Growth signal. The before/after YAML visual diff in the publishing guide is a reusable enterprise-launch asset. Repeat this pattern for future features that reduce manifest verbosity -- it converts scanning readers into adopters by proving the payoff in two glanceable blocks. Credit leocamello Option A design in release comms.

Panel summary

Persona B R N Takeaway
Python Architect 0 1 2 Clean additive design: frozen dataclass composition, cached lazy-init, context-aware validation. No architectural faults.
CLI Logging Expert 0 0 2 No CLI output surface touched; marketplace validation errors follow the existing MarketplaceYmlError pattern correctly. Ship.
DevX UX Expert 0 2 2 sourceBase ergonomics are strong: before/after docs, actionable error on confused-deputy, and apm marketplace add respects base. Two recommended polish items.
Supply Chain Security Expert 0 0 1 sourceBase validation is defense-in-depth layered: HTTPS-only, FQDN, rejects dangerous URL components, validate_path_segments, confused-deputy guard. No blocking issues.
OSS Growth Hacker 0 1 2 Before/after YAML example in the publishing guide is a strong conversion asset; CHANGELOG wording is user-outcome-first. Ship.
Auth Expert 0 0 1 sourceBase host extraction correctly feeds into _get_resolver_for_host; no auth bypass or credential leak introduced.
Doc Writer 0 1 2 Docs accurately mirror sourceBase semantics with a clean canonical-in-7.5 hierarchy; minor non-bloat and skill-link polish only. Ship.
Test Coverage Expert 0 0 1 sourceBase feature ships with solid unit plus integration coverage for the Claude mapper; Codex mapper path is a low-risk nit.
Performance Expert 0 0 1 No hot-path overhead or cache regression; sourceBase composition is cached string concat in the producer-only marketplace build pipeline.

B = blocking-severity findings, R = recommended, N = nits.
Counts are signal strength, not gates. The maintainer ships.

Top 5 follow-ups

  1. [Test Coverage Expert] Add Codex output mapper integration assertion for sourceBase-composed URLs -- Missing assertion on a multi-harness-support surface. Low risk (shared helper) but closes a regression-trap gap for the second harness.
  2. [Python Architect] Replace getattr(yml, 'source_base', None) with direct attribute access -- Frozen dataclass guarantees field existence; getattr would silently swallow a future rename refactor.
  3. [DevX UX Expert] Add confused-deputy error hint suggesting sourceBase or owner/repo form -- Actionable error messages reduce support burden for enterprise adopters hitting the generic rejection.
  4. [Doc Writer] De-duplicate sourceBase composition illustration in publish guide -- Pick one teaching device (Before/After wins) to avoid guide bloat as the page grows.
  5. [Auth Expert] Lowercase host in split_source_base return value -- Defensive normalization for case-sensitive token cache keys. Low risk today but prevents a class of subtle bugs.

Architecture

classDiagram
    direction LR
    class MarketplaceConfig {
      <<ValueObject>>
      +source_base str or None
      +packages tuple[PackageEntry]
    }
    class PackageEntry {
      <<ValueObject>>
      +source str
      +host str or None
      +is_local bool
    }
    class MarketplaceBuilder {
      <<Facade>>
      -_source_base_parts _SourceBaseCoords or None
      -_source_base_parts_loaded bool
      +_get_source_base_parts() _SourceBaseCoords
      +_remote_source_coordinates(entry) tuple
      +compose_marketplace_json(resolved) dict
    }
    class _SourceBaseCoords {
      <<ValueObject>>
      +host str
      +path_prefix str
      +source_base str
      +org_hint str
    }
    class MarketplaceOutputMapper {
      <<Strategy>>
      +compose(config, resolved) MapperResult
    }
    class ResolvedPackage {
      <<ValueObject>>
      +source_repo str
      +host str or None
      +source_url str or None
    }
    MarketplaceBuilder *-- _SourceBaseCoords : caches
    MarketplaceBuilder ..> MarketplaceConfig : reads
    MarketplaceBuilder ..> PackageEntry : iterates
    MarketplaceBuilder ..> ResolvedPackage : produces
    MarketplaceBuilder ..> MarketplaceOutputMapper : delegates
    MarketplaceOutputMapper <|-- ClaudeMarketplaceMapper
    MarketplaceOutputMapper <|-- CodexMarketplaceMapper
    class _SourceBaseCoords:::touched
    class MarketplaceBuilder:::touched
    class MarketplaceConfig:::touched
    class ResolvedPackage:::touched
    classDef touched fill:#fff3b0,stroke:#d47600
Loading
flowchart TD
    A[apm marketplace build] --> B[MarketplaceBuilder._load_yml]
    B --> C[yml_schema.parse_source_base]
    C --> D{sourceBase present?}
    D -->|no| E[source_base = None on MarketplaceConfig]
    D -->|yes| F[NET guard: HTTPS FQDN path validation]
    F --> G[MarketplaceConfig.source_base = validated URL]
    G --> H[MarketplaceBuilder._get_source_base_parts]
    H --> I[split_source_base -> host and path_prefix]
    I --> J[Cache _SourceBaseCoords]
    J --> K[_remote_source_coordinates]
    K --> L{entry.host set?}
    L -->|yes| M[Override: return entry host and source]
    L -->|no| N{_source_base_parts cached?}
    N -->|no| O[Default: return entry source unchanged]
    N -->|yes| P[Compose repo_path and source_url]
    P --> Q[return base_host repo_path source_url org_hint]
    Q --> R[_resolve_entry -> ResolvedPackage]
    R --> S[compose_marketplace_json]
    S --> T[output mapper emits canonical HTTPS URL]
Loading

Recommendation

All nine panelists recommend ship with zero blocking findings. CI is green, the feature is strictly additive with no migration, validation is defense-in-depth, and the PR closes #1519 with credited community design. Merge now; track the Codex-mapper test gap and the getattr cleanup as optional fast-follows in a subsequent PR or the next batch sweep.


Full per-persona findings

Python Architect

  • [nit] _SourceBaseCoords could be a module-level NamedTuple instead of a frozen dataclass at src/apm_cli/marketplace/builder.py:106
    Design patterns: _SourceBaseCoords is an internal value object with a derived property (org_hint). A frozen dataclass is fine here. Not worth changing -- the current shape is idiomatic and correct.
  • [nit] split_source_base trusts its input is already validated but has no assert/docstring guard at src/apm_cli/marketplace/yml_schema.py:117
    split_source_base is exported and documents a parse_source_base-validated precondition. Current call sites validate first, so this is low risk.
    Suggested: Add an assert after removeprefix if desired.
  • [recommended] Builder getattr fallback for source_base is overly defensive given frozen dataclass contract at src/apm_cli/marketplace/builder.py:386
    MarketplaceConfig is a frozen dataclass where source_base always exists. Direct attribute access would surface a rename immediately.
    Suggested: Replace getattr(yml, 'source_base', None) with yml.source_base.

CLI Logging Expert

  • [nit] sourceBase composition silently succeeds with no verbose-mode trace at src/apm_cli/marketplace/builder.py
    When --verbose is active, the builder could log the composition step so humans debugging resolution can see the routing decision. Existing host routing also lacks verbose traces, so this is polish only.
    Suggested: Add logger.debug for sourceBase composition if desired.
  • [nit] parse_source_base error messages omit the offending value at src/apm_cli/marketplace/yml_schema.py:524
    CLI error message guidance prefers naming the thing that failed. Existing sourceBase errors name the field but not the raw value. This is minor polish.
    Suggested: Include raw_source_base in the error string if desired.

DevX UX Expert

  • [recommended] Error message for confused-deputy rejection names no concrete next action when sourceBase is absent at src/apm_cli/marketplace/yml_schema.py
    When sourceBase is not set and a user writes source: tool, the generic accepted-forms list could add a hint to set sourceBase or use owner/repo form.
    Suggested: Append a hint in source_base is None branch of validate_source_value.
  • [recommended] Manifest-schema docs lack a quick cross-link from packages[].source table row to Section 7.5 at docs/src/content/docs/reference/manifest-schema.md
    A user scanning the table may miss composition semantics. A parenthetical see Section 7.5 would improve discoverability.
    Suggested: Append a section link in the source field description row.
  • [nit] CHANGELOG entry could say what the user types, not what the feature is at CHANGELOG.md
    Action-first changelog phrasing is slightly easier to scan.
  • [nit] Publishing guide before/after snippet shows ref but not version at docs/src/content/docs/producer/publish-to-a-marketplace.md
    Most marketplace entries use version ranges; one version example would reinforce both branches.

Supply Chain Security Expert

  • [nit] _RELATIVE_SOURCE_RE matches traversal sequences before validate_path_segments rejects them at src/apm_cli/marketplace/yml_schema.py:96
    The code comment correctly documents regexes are shape filters and validate_path_segments is the canonical traversal guard. Tighter regex would be extra defense but current path is safe.
    Suggested: Optionally tighten _SEGMENT_PAT to reject bare-dot-only segments at regex layer.

OSS Growth Hacker

  • [recommended] Before/after example in publish guide lacks a one-line why-you-care hook above it at docs/src/content/docs/producer/publish-to-a-marketplace.md
    A sentence framing the payoff primes the reader to read the YAML diff as proof rather than more config.
    Suggested: Add a one-liner before the Before block.
  • [nit] CHANGELOG entry parenthetical could be the lead at CHANGELOG.md
    Minor growth polish; current wording already passes.
  • [nit] Manifest-schema source field description is long; consider a numbered list for scanability at docs/src/content/docs/reference/manifest-schema.md
    Enterprise adopters scanning reference docs may benefit from a compact list. Information is complete and correct.

Auth Expert

  • [nit] split_source_base does not lowercase the host before returning it at src/apm_cli/marketplace/yml_schema.py
    DNS hostnames are case-insensitive. Lowercasing extracted host would be defensive for case-sensitive token cache keys or per-host environment lookup. Current lowercase examples and FQDN validation make risk low.
    Suggested: return host.lower(), path_prefix

Doc Writer

  • [recommended] publish-to-a-marketplace.md demonstrates sourceBase composition twice at docs/src/content/docs/producer/publish-to-a-marketplace.md
    The canonical sample has inline resolution comments and the Before/After block re-illustrates the same behavior. The Before/After block is the stronger teaching device; avoid guide bloat.
    Suggested: Keep Before/After and drop inline resolution comments, or vice versa.
  • [nit] package-authoring skill points to Section 7.5 in plaintext rather than a path link at packages/apm-guide/.apm/skills/apm-usage/package-authoring.md
    The skill already uses relative docs links elsewhere; making this a link improves navigation consistency.
    Suggested: Link to ../../../../../docs/src/content/docs/reference/manifest-schema.md#75-marketplacepackages
  • [nit] Cross-page anchor Microsoft open source compliance: policy docs, license, and trademark notice #75-marketplacepackages is correct; keep it verified on future heading renames at docs/src/content/docs/producer/publish-to-a-marketplace.md:129
    Anchor currently resolves from docs/producer to manifest-schema Section 7.5; no action now.
    Proof (manual): A reader clicking the manifest schema link lands on Section 7.5.
    [manifest schema](../reference/manifest-schema/#75-marketplacepackages) ; target heading: ### 7.5. marketplace.packages

Test Coverage Expert

  • [nit] Codex output mapper sourceBase path lacks dedicated assertion in integration test at tests/integration/test_marketplace_builder_hermetic.py
    The integration test asserts the Claude mapper emits composed sourceBase URLs, while the Codex mapper also uses _remote_source_url without a parallel assertion. Risk is low because both mappers invoke the same helper.
    Suggested: Add a codex output spec to the existing sourceBase integration test or a sibling test.
    Proof (missing at): tests/integration/test_marketplace_builder_hermetic.py::test_build_writes_source_base_composed_url_to_codex_output -- proves: Codex consumers see the fully composed sourceBase URL for relative package sources [multi-harness-support,portability-by-manifest]
    assert codex_output["packages"][0]["source"]["url"] == "https://gitlab.example.com/platform/marketplaces/team/tool"

Performance Expert

  • [nit] _resolve_entry calls both _load_yml() and _remote_source_coordinates which also calls _get_source_base_parts -> _load_yml() at src/apm_cli/marketplace/builder.py:582
    Both calls resolve to a cached singleton, so the cost is one extra pointer comparison per entry. Not actionable now.

This panel is advisory. It does not block merge. Re-apply the panel-review label after addressing feedback to re-run.

@danielmeppiel danielmeppiel merged commit 6fa133f into main Jun 11, 2026
15 checks passed
@danielmeppiel danielmeppiel deleted the danielmeppiel/1519-marketplace-sourcebase branch June 11, 2026 15:33
sergio-sisternes-epam pushed a commit that referenced this pull request Jun 13, 2026
Sync the Stage 2 complexity/file-length refactor branch with main's 22
feature commits (Hermes #1726, Kiro IDE #1741, multi-host dep identity
#1735, same-repo remote path deps #1732, git_file_transport #1740,
revision pins #1738, marketplace sourceBase/source parity/inherit
description #1736/#1739/#1755, pack --archive .zip #1720, mcp optional
registry inputs #1734, and the v0.19.0/v0.20.0 releases).

Conflict resolution preserved both sides: main's new features ported
through the branch's extracted sibling modules, branch's tightened ruff
thresholds (max-statements=120, max-branches=40, max-complexity=35,
max-returns=8, max-args=12) and 800-line file limit retained.

All 7 CI-mirror lint gates pass; full unit suite green (17099 passed).

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.

marketplace.packages[].source: generalize beyond host/owner/repo (deeply-nested GitLab subgroups, arbitrary git hosts)

3 participants