Skip to content

fix(deps): preserve GitLab sparse-fetch transport - #2939

Merged
Daniel Meppiel (danielmeppiel) merged 7 commits into
mainfrom
danielmeppiel-gitlab-sparse-transport
Sep 11, 2026
Merged

fix(deps): preserve GitLab sparse-fetch transport#2939
Daniel Meppiel (danielmeppiel) merged 7 commits into
mainfrom
danielmeppiel-gitlab-sparse-transport

Conversation

@danielmeppiel

@danielmeppiel Daniel Meppiel (danielmeppiel) commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

fix(deps): preserve GitLab sparse-fetch transport

TL;DR

GitLab sparse downloads now execute the shared transport plan instead of silently rebuilding an SSH dependency as HTTPS. The requested URL reaches Git unchanged; its effective rewrite target controls authentication and REST eligibility. This implements #2938 and addresses the reproduction in #2929.

Closes #2938.
Fixes #2929.

Important

All required PR checks and the separate Linux x86-64 SSH-semver acceptance run pass on final shepherd candidate 81bbad6c15c9dd45136318c48994c06cb8871314. The binary was actually built from this exact candidate. Package-index configuration remained local; no repository, lockfile, or CI configuration changes were needed.

Problem (WHY)

  • The reported SSH alias with port 2222 became an HTTPS sparse-fetch remote. The new actual-Git-remote assertion fails on both recorded unfixed revisions, not merely on a mocked constructor.
  • A PAT could previously enable REST after an SSH failure. The issue's transport contract requires strict SSH to stay SSH unless protocol fallback is explicitly admitted.
  • [!] Live sparse checkouts were keyed too narrowly to distinguish prepared URL and authentication identities, so reuse also needed to follow the transport decision.

The proof follows Agent Skills' execution-first guidance:
"Run the skill against real tasks"
Here the concrete inputs are the reported manifest, real local Git materialization, and named failing assertions on deliberate mutations.

Approach (WHAT)

  • Share initial-scheme selection and custom-port warning construction across existing consumers; keep their fallback settings unchanged.
  • Execute each selected GitLab attempt with its requested URL, effective URL, and independently resolved authentication environment.
  • Permit REST only after plan exhaustion and a failed, actually executed, same-origin effective HTTPS attempt.
  • Reuse checkouts only for the same provider, ref, normalized requested/effective URLs, and authentication mode; evict only the failed instance.
  • Keep security, rewrite, validation, and filesystem errors terminal; retry only typed Git transport failures.

Implementation (HOW)

The principal execution boundary is download_gitlab_file.

File Intent and scope
src/apm_cli/deps/transport_selection.py Own shared initial-scheme selection and pure fallback-port warning construction.
src/apm_cli/deps/clone_engine.py Consume shared helpers; retain existing warning deduplication and clone policy.
src/apm_cli/deps/git_reference_resolver.py Use the same initial scheme for ref discovery.
src/apm_cli/install/validation.py Use the same initial scheme without relaxing validation fallback.
src/apm_cli/deps/download_strategies.py Execute prepared attempts, constrain REST, key live checkouts by prepared identity, and preserve concurrent replacements during eviction.
src/apm_cli/core/auth.py Make GitLab honor the existing native-credential-lookup restriction for non-HTTPS remotes; leave GitHub and ADO resolution unchanged.
scripts/architecture_linter/checks/transport_gitlab_sparse.py Add executable AST checks for selector, prepared URL, auth-owner, and REST-gate use.
scripts/architecture_linter/checks/transport_auth_platform.py Replace obsolete source-string matching with shared prepared-remote structural evidence.
scripts/architecture_linter/groups/transport_platform.py Register the new ownership check.
.apm/architecture/owners/transport-auth-platform.json Record the transport ownership rule and regression evidence.
tests/unit/deps/test_initial_transport_scheme.py Cover scheme precedence and shared warning decisions.
tests/unit/deps/test_gitlab_sparse_transport_contract.py Exercise parsing, auth isolation, fallback order, REST eligibility, terminal failures, and checkout identity.
tests/integration/test_gitlab_sparse_transport_contract.py Assert actual Git remotes, exact bytes and refs, reuse, synchronized concurrency, containment, and rewrite behavior.
tests/integration/test_gitlab_sparse_transport_mutations.py Require passing baselines and intended JUnit assertion failures for 14 isolated behavioral mutants, including the protocol-switch warning.
tests/integration/test_architecture_gitlab_sparse_transport.py Exercise the static rule against baseline and bypass variants, including M9.
tests/integration/test_architecture_owner_rule_mutations.py Include the new rule in the existing ownership mutation suite.
tests/test_gitlab_git_transport.py Preserve default-HTTPS REST expectations using typed Git failures and portable executable assertions.
tests/test_github_downloader.py Adapt existing shared sparse-transport regressions.
tests/unit/core/test_git_transport_policy.py Prove the real resolver does not invoke an HTTPS credential helper for unmanaged GitLab transports.
tests/unit/deps/test_download_strategies_selection.py Keep transport selection tests aligned with prepared attempts and typed failures.
tests/unit/deps/test_github_downloader_gitlab_routing.py Retain GitLab routing coverage with the corrected transport boundary.
tests/unit/deps/test_download_strategies_phase3.py Give legacy REST tests a real strict HTTPS plan and a typed Git failure before their REST assertions.
tests/unit/scripts/test_architecture_runner.py Include the new sparse-plan rule in the exact rule inventory contract.
tests/spec_conformance/test_gitlab_sparse_transport_reqs.py Bind real Git port/ref/reuse assertions to existing req-sc-013 and req-rs-016; do not waive the Mode B gate.
CONFORMANCE.json Regenerate requirement-to-test bindings for the three new conformance cases.
CONFORMANCE.md Regenerate the corresponding conformance counts.
docs/src/content/docs/consumer/authentication.md Document strict SSH, effective rewrites, credentials, and authorized REST behavior.
docs/src/content/docs/consumer/manage-dependencies.md Clarify sparse dependency transport and custom-port fallback behavior.
packages/apm-guide/.apm/skills/apm-usage/authentication.md Keep the distributed usage guidance aligned with consumer documentation.
CHANGELOG.md Record the GitLab sparse transport correction under Unreleased.
tests/integration/test_download_strategies_selection.py, test_download_strategies_phase3w5.py Correct existing REST callback fixtures to execute a real HTTPS plan and typed Git failure.
tests/unit/test_protocol_fallback_warning.py Align the existing clone-warning regression with the reviewed recovery advice and parsed live docs URL.
docs/src/content/docs/getting-started/authentication.md Qualify the canonical credential guidance specifically for sparse fetches.
packages/apm-guide/.apm/skills/apm-usage/dependencies.md Describe Git-first fetches with restricted REST fallback.

Diagrams

Dashed nodes show the newly explicit prepared-identity and REST-authorization boundaries; non-transport exceptions propagate rather than entering the retry path.

flowchart LR
    subgraph Policy["Shared transport policy"]
        A["DependencyReference"] --> B["TransportSelector.select"]
    end
    subgraph Preparation["Per-attempt preparation"]
        B --> C["Requested and effective URLs"]
        C --> D["Requested URL to Git"]
        C --> E["Effective URL to AuthResolver"]
    end
    subgraph Execution["GitLab sparse fetch"]
        D --> F["Prepared checkout identity"]
        E --> F
        F --> G["GitFileTransport"]
        G -->|"Success"| H["Requested file and ref"]
        G -->|"Typed Git failure"| I["Exhaust remaining plan"]
    end
    subgraph Eligibility["REST eligibility"]
        I --> J{"Executed same-origin effective HTTPS?"}
        J -->|"Yes"| K["GitLab REST"]
        J -->|"No"| L["Propagate failure"]
    end
    classDef new stroke-dasharray: 5 5;
    class C,D,E,F,I,J new;
Loading

Trade-offs

  • Strict transport over opportunistic recovery. An SSH failure with a PAT no longer silently becomes a REST request. Users must explicitly allow protocol fallback or use the HTTPS web endpoint.
  • Prepared identity over maximum checkout reuse. Distinct users, mirrors, protocols, ports, refs, or auth modes can create separate live checkouts. Persistent cache schema and identity are unchanged.
  • Narrow auth-owner correction over provider-wide refactoring. GitLab now obeys the existing lookup restriction; the historical private argument name and other provider behavior remain intact.
  • Local Git proof over a live SSH service dependency. Fixtures intercept only external fetch dispatch, then perform real Git initialization, remote configuration, sparse checkout, checkout, and reads. They do not certify customer DNS, SSH keys, proxies, or server availability.
  • No dependency or baseline relaxation. The lockfile is unchanged; syntax/import errors, collection failures, skips, and timeouts cannot count as killed mutants.

Benefits

  1. The reported manifest retains its SSH username, alias, custom port, repository, and requested ref in the actual Git remote.
  2. Strict SSH and HTTP failures produce zero unauthorized REST calls, including when a dummy PAT exists.
  3. Same-identity paths share one initial fetch, while distinct prepared identities and concurrent replacements remain isolated.
  4. All 14 behavioral mutants fail their intended assertions, and static bypass mutations fail the ownership rule.

Validation

Final PR candidate: 81bbad6c15c9dd45136318c48994c06cb8871314.
Base: e38261c5db4d893d6ddebc3925742e4e3bd2ba74.
Production source and lockfile are byte-identical from the bounded warning/doc fold 246eb0a276c3986bdfde271bd1ea5838a4279b53 to the final candidate; subsequent commits only correct tests and documentation.

Final shepherd validation

  • Final CI run and merge check succeeded. All 18 check-rollup entries are complete: 16 success, one neutral CodeQL summary, and one intentionally skipped deployment.
  • Exact-head targeted regression, owner, integration, conformance and quality selection: 457 passed in 170.96s, zero skips. This includes all 14 behavioral mutants and the static M9 boundary suite.
  • Each of the three deterministic owner touches was separately covered by named executed functional tests: 3 passed in 1.16s.
  • The actual Linux x86-64 candidate binary reports 0.30.0 (81bbad6). SSH-semver acceptance: 5 passed in 40.33s, zero skips. SHA256: e7f73b82b11050daa220a25df7b6914c0bec40f27ebac11fa40464a62600cbdf.
  • Full canonical lint, architecture boundaries, unchanged Linux YAML/2100-line/relative-path guards, assertion and duplication ratchets, and conformance orphan check all pass without baseline changes.
  • One CI recovery corrected a stale clone-warning test expectation; it did not change production code.
  • Both Copilot inline threads were answered and resolved. The two complete nine-persona panels folded bounded docs/diagnostic repairs. Existing validation credential ordering and shared alternate-mirror dedup remain explicitly deferred: changing them would alter behavior outside this sparse-fetch repair. No new issues, README edits, or provider-wide redesign were introduced.

Earlier candidate evidence (retained for traceability)

The following records apply to fbbc5a1f283841b51847ca07719b22e4bd5bdff8, not to the final candidate. Its CI run passed both Linux test shards, Lint, Windows Compatibility, Test Architecture Ratchets, PR Binary Smoke, Lifecycle Smoke and Coverage Combine, plus the separate spec, docs, CodeQL, NOTICE, CLA and merge checks.

uv run --frozen --no-sync pytest -p no:cacheprovider -q tests/unit/deps/test_download_strategies_phase3.py tests/unit/scripts/test_architecture_runner.py tests/unit/deps/test_gitlab_sparse_transport_contract.py tests/integration/test_gitlab_sparse_transport_contract.py tests/spec_conformance/test_gitlab_sparse_transport_reqs.py:

249 passed in 9.69s

uv run --frozen --no-sync pytest -p no:cacheprovider -q tests/spec_conformance/test_gitlab_sparse_transport_reqs.py tests/quality:

66 passed in 42.41s

The isolated Linux x86-64 container used the existing environment-local package mirror. Dependencies were exported from the frozen lockfile and installed with uv pip sync --require-hashes, followed by the original frozen sync and build commands. The binary reported Agent Package Manager (APM) CLI version 0.30.0 (fbbc5a1). git diff --exit-code passed before and after the build and acceptance run.

APM_BINARY_PATH="$PWD/dist/apm-linux-x86_64/apm" uv run --frozen --extra dev pytest -p no:cacheprovider -q tests/integration/test_ssh_semver_transport_contract.py:

5 passed in 38.25s
Local regression and lint evidence

Combined regression, architecture, and mutation selection using uv run --frozen --no-sync pytest (recorded output excerpt):

SKIPPED [1] tests/test_github_downloader.py:357: Integration test requiring network access
SKIPPED [1] tests/test_github_downloader.py:363: Integration test requiring network access
1043 passed, 2 skipped in 229.53s (0:03:49)

The two skips are pre-existing live-network cases. Every new proof case executed. The run also emitted pytest temporary-directory cleanup warnings; they did not change the test result.

uv run --frozen --no-sync ruff check src/ tests/ scripts/lint_architecture_boundaries.py scripts/architecture_linter/:

All checks passed!

uv run --frozen --no-sync ruff format --check src/ tests/ scripts/lint_architecture_boundaries.py scripts/architecture_linter/:

1849 files already formatted

The pylint R0801 gate, auth boundary script, architecture boundary script, assertion ratchet, and exact-duplicate ratchet pass. tests/quality passed 63 cases without baseline changes.

The three GNU-dependent CI scripts were extracted from .github/workflows/ci.yml and executed unchanged with bash -e -s in a Linux container against the read-only candidate:

Running unchanged Linux guard: Check YAML encoding safety
Running unchanged Linux guard: File length guardrail
Running unchanged Linux guard: Lint - no raw str(relative_to) patterns

All three exited 0. The Mermaid block was rendered successfully with mmdc.

Scenario Evidence

# Scenario (user promise) Principle(s) Test(s) proving it Type
1 My SSH manifest keeps its user, alias, and custom port and installs the requested bytes. Vendor-neutral, Governed by policy tests/integration/test_gitlab_sparse_transport_contract.py::test_real_git_manifest_remote_bytes_and_single_fetch (regression-trap for #2938 / #2929) integration
2 An SSH failure does not switch to REST just because I have a PAT. Secure by default, Governed by policy tests/unit/deps/test_gitlab_sparse_transport_contract.py::test_strict_ssh_failure_never_rest integration
3 My PAT never enters SSH or HTTP Git subprocesses. Secure by default tests/unit/deps/test_gitlab_sparse_transport_contract.py::test_unmanaged_transports_strip_pat integration
4 Explicit fallback follows the selected order, retains my port, warns once about the port, and reports executed protocol switches. DevX (pragmatic as npm), Governed by policy tests/unit/deps/test_gitlab_sparse_transport_contract.py::test_opt_in_fallback_order_port_and_warning; test_protocol_switch_warning_matches_executed_attempts integration
5 HTTPS REST recovery preserves the endpoint, headers, and requested ref. Vendor-neutral tests/unit/deps/test_gitlab_sparse_transport_contract.py::test_https_rest_preserves_headers_endpoint_and_ref integration
6 A safe local rewrite does not authorize an HTTPS REST request. Secure by default tests/integration/test_gitlab_sparse_transport_contract.py::test_effective_local_rewrite_does_not_authorize_rest integration
7 Traversal, symlink escape, and local read errors remain terminal. Secure by default tests/integration/test_gitlab_sparse_transport_contract.py::{test_traversal_is_terminal_before_fetch,test_cached_symlink_escape_is_terminal,test_local_read_failure_is_terminal} integration
8 A missing custom ref fails instead of installing a different ref. Governed by policy tests/integration/test_gitlab_sparse_transport_contract.py::test_missing_custom_ref_is_not_replaced integration
9 Concurrent paths reuse one checkout without discarding a replacement after failure. DevX (pragmatic as npm) tests/integration/test_gitlab_sparse_transport_contract.py::test_concurrent_paths_share_one_initial_checkout; tests/unit/deps/test_gitlab_sparse_transport_contract.py::test_failed_eviction_preserves_replacement integration
10 GitHub anonymous-first, throttle metadata, ADO, validation, and semver discovery keep their existing contracts. Vendor-neutral, Secure by default Existing test_public_github_anonymous_first.py, test_github_throttle_fallback.py, test_git_transport_policy.py, test_validation_strict_transport.py, and test_git_reference_resolver.py in the recorded regression selection integration

How to test

  • Restore development dependencies with uv sync --frozen --extra dev, then run uv run --frozen --extra dev pytest -p no:cacheprovider -q tests/unit/deps/test_initial_transport_scheme.py tests/unit/deps/test_gitlab_sparse_transport_contract.py tests/integration/test_gitlab_sparse_transport_contract.py; expect transport, credential, ref, and materialization assertions to pass.
  • Run uv run --frozen --extra dev pytest -p no:cacheprovider -q tests/integration/test_architecture_gitlab_sparse_transport.py tests/integration/test_gitlab_sparse_transport_mutations.py; expect passing baselines and intended mutant assertion failures recognized by the driver.
  • Run the existing lint, architecture, and tests/quality gates; use Linux for the unchanged GNU grep checks.
  • In an isolated Linux x86-64 checkout, run uv sync --frozen --extra dev --extra build, then UV_FROZEN=true uv run --extra dev --extra build bash scripts/build-binary.sh. Run APM_BINARY_PATH="$PWD/dist/apm-linux-x86_64/apm" uv run --frozen --extra dev pytest -p no:cacheprovider -q tests/integration/test_ssh_semver_transport_contract.py; require actual execution, not missing-binary skips.
  • Confirm required CI checks succeed on this PR's actual candidate before treating it as merge-ready.

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

Execute the shared transport plan using prepared remotes and per-attempt authentication. Gate REST on executed effective HTTPS, isolate live checkout identities, and add real-Git, architecture and mutation regression proof.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Reuse the real-Git transport contract as executable evidence for req-sc-013 and req-rs-016, rather than waiving the Mode B conformance gate.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Configure a real strict HTTPS transport plan and a typed Git failure in REST tests, and include the new sparse-plan rule in the frozen architecture inventory.

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

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.

Copilot review overview

🟡 Changes recommended

Typed transport fixtures and rewrite-target deduplication need correction; the documentation and symlink-test updates should also be completed.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review tier: Lite
Findings: 1 High severity · 1 Low severity

New issues introduced by this change (2)
Severity Finding
High severity src/​apm_cli/​deps/​download_strategies.py — This new typed-only catch leaves the existing integration regressions…
Low severity tests/​integration/​test_gitlab_sparse_transport_contract.py — Skipping solely by OS leaves this new symlink-containment regression untested on Windows runners…
What changed in this PR

This PR fixes GitLab sparse-fetch transport handling while preserving requested URLs, authentication boundaries, checkout reuse, and authorized REST fallback.

Changes:

  • Centralizes transport selection and fallback warnings.
  • Executes prepared GitLab attempts with transport-aware authentication.
  • Adds regression, architecture, conformance, documentation, and changelog coverage.
File Reviewed change
tests/​unit/​scripts/​test_architecture_runner.py Updates architecture rule inventory coverage.
tests/​unit/​deps/​test_initial_transport_scheme.py Tests scheme precedence and warning decisions.
tests/​unit/​deps/​test_gitlab_sparse_transport_contract.py Tests authentication, fallback, REST eligibility, and reuse.
tests/​unit/​deps/​test_github_downloader_gitlab_routing.py Preserves GitLab routing coverage.
tests/​unit/​deps/​test_download_strategies_selection.py Aligns selection tests with prepared attempts.
tests/​unit/​deps/​test_download_strategies_phase3.py Updates REST fallback fixtures.
tests/​unit/​core/​test_git_transport_policy.py Tests credential-helper restrictions.
tests/​test_gitlab_git_transport.py Preserves GitLab transport expectations.
tests/​test_github_downloader.py Updates shared downloader regressions.
tests/​spec_conformance/​test_gitlab_sparse_transport_reqs.py Binds transport behavior to conformance requirements.
tests/​integration/​test_gitlab_sparse_transport_mutations.py Validates behavioral mutations.
tests/​integration/​test_gitlab_sparse_transport_contract.py Verifies real Git materialization and symlink containment. Nit: improve platform-aware symlink setup.
tests/​integration/​test_architecture_owner_rule_mutations.py Adds ownership mutation coverage.
tests/​integration/​test_architecture_gitlab_sparse_transport.py Tests static transport checks.
src/​apm_cli/​install/​validation.py Aligns validation transport selection.
src/​apm_cli/​deps/​transport_selection.py Centralizes transport planning. Moderate: preserve distinct rewrite targets during deduplication.
src/​apm_cli/​deps/​git_reference_resolver.py Aligns ref discovery transport.
src/​apm_cli/​deps/​download_strategies.py Executes transport plans and gates REST fallback. Critical: update existing fixtures to raise typed transport errors.
src/​apm_cli/​deps/​clone_engine.py Reuses shared transport helpers.
src/​apm_cli/​core/​auth.py Applies transport-specific credential policy. Nit: update the canonical authentication documentation for HTTPS-only lookup.
scripts/​architecture_linter/​groups/​transport_platform.py Registers the new rule.
scripts/​architecture_linter/​checks/​transport_gitlab_sparse.py Adds sparse transport architecture checks.
scripts/​architecture_linter/​checks/​transport_auth_platform.py Updates authentication boundary checks.
packages/​apm-guide/​.apm/​skills/​apm-usage/​authentication.md Updates distributed authentication guidance.
docs/​src/​content/​docs/​consumer/​manage-dependencies.md Documents sparse transport fallback behavior.
docs/​src/​content/​docs/​consumer/​authentication.md Documents transport-aware authentication.
CONFORMANCE.md Regenerates conformance counts.
CONFORMANCE.json Regenerates conformance bindings.
CHANGELOG.md Records the GitLab transport fix.
.apm/​architecture/​owners/​transport-auth-platform.json Records transport ownership and evidence.
Suppressed comments (2)

src/apm_cli/core/auth.py:1191

  • Because this condition now suppresses GitLab credential-helper lookup for non-HTTPS remotes, the canonical auth page is stale: docs/src/content/docs/getting-started/authentication.md:388 and its package-source table at line 402 still promise GITLAB_APM_PAT -> GITLAB_TOKEN -> credential fill without the HTTPS-only qualifier. Update that canonical flow/table in this PR so the documented auth contract matches the new resolver behavior.
            host_info.kind not in ("generic", "gitlab") or allow_generic_credential_lookup

src/apm_cli/deps/transport_selection.py:450

  • The initial and chained attempts retain distinct requested/effective URLs, but _dedup_attempts() later keys only on (scheme, use_token). If these two candidates have different safe insteadOf targets (for example, two file or SSH mirrors), both collapse to the same scheme/auth pair and the second prepared attempt is dropped, so opt-in fallback can never reach the alternate mirror. Deduplicate on normalized requested/effective URLs as well and add a regression case for distinct rewrite targets.
                initial = [_SSH]
                chained = [_AUTH_HTTPS, _PLAIN_HTTPS] if has_token else [_PLAIN_HTTPS]
            elif initial_scheme == "https":
                initial = [_AUTH_HTTPS] if has_token else [_PLAIN_HTTPS]
                chained = [_SSH, _PLAIN_HTTPS] if has_token else [_SSH]

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/apm_cli/deps/download_strategies.py
Comment thread tests/integration/test_gitlab_sparse_transport_contract.py Outdated
@danielmeppiel

Copy link
Copy Markdown
Collaborator Author

APM Review Panel: needs_rework

PR #2939 restores GitLab sparse-fetch transport fidelity while keeping the fix bounded to the affected auth, rewrite, REST fallback, cache-identity, docs, and test contracts.

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

The panel mostly converges: architecture, auth, performance, growth, and test coverage see the core GitLab sparse-fetch repair as correctly bounded and materially evidenced. The strongest positive evidence is the exact-HEAD real-binary run in tests/integration/test_ssh_semver_transport_contract.py, where assert_unchanged(after_lock, observation.snapshots[-2]); assert_unchanged(after_lock, observation.snapshots[-1]) passed as part of 5 binary tests in 38.25s, backed by 13 behavioral mutants plus the static M9 guard. That supports the central promise: requested GitLab SSH/SCP identity reaches Git, effective rewrite controls auth and REST eligibility, and durable state is not silently rewritten across install/update/audit.

Security's blocking finding is real as an observation of validation behavior, but I do not weigh it as a regression introduced by this PR. The baseline check shows git show e38261c5db4d893d6ddebc3925742e4e3bd2ba74:src/apm_cli/install/validation.py already had dep_ctx = None if is_generic else auth_resolver.resolve_for_dep(dep_ref) at the same point in validation; the current PR only substitutes initial_transport_scheme for an equivalent inline scheme expression there. I therefore side with the auth specialist for this PR's sparse-fetch path: the actual AuthResolver.resolve_for_remote sparse contract is protected, and expanding validation into a provider-wide auth redesign would violate the maintainer's reservation. This is not a fabricated PASS claim and not an exploit dismissal; it is a scope and regression-boundary call.

Maintainer explicitly requires no scope creep. Preserve GitLab sparse-fetch transport and its directly coupled authentication, rewrite, REST eligibility, live checkout identity, error, regression-test, documentation and ownership-guard contracts. Do not broaden this into provider-wide auth/cache redesign, new user features, unrelated cleanup, documentation restructuring or project positioning work. Scope explicitly says existing GitHub, ADO, validation and ref discovery behavior preserved. Within that boundary, the concrete remaining defects are documentation/UX accuracy gaps: strict SSH wording must acknowledge same-host insteadOf rewrites, packaged dependency guidance must stop saying REST is never used, the dead docs link must be corrected, and the actual cross-protocol sparse fallback should warn like clone fallback.

Dissent. Dissent is explicit on the supply-chain finding: supply-chain-security classified pre-attempt validation credential lookup as blocking, while auth found no sparse-fetch auth blocker. I side against making it a PR blocker because the probed line is unchanged baseline behavior, not introduced by this diff, and the maintainer's reservation forbids broadening this fix into existing validation auth redesign.

Aligned with: Secure by default, Governed by policy, Multi-harness / multi-host, Pragmatic as npm

Growth signal. No positioning or growth-strategy amplification is needed. This is a trust-preserving transport/auth correctness fix with bounded docs and changelog value, not a README or launch-narrative beat.

Panel summary

Persona B R N Takeaway
Python Architect 0 0 1 No blocking architecture concerns; the PR centralizes GitLab sparse transport decisions and ships dual behavioral/static guardrails.
CLI Logging Expert 0 1 1 No blocking CLI-output issue; make opt-in GitLab sparse fallback transitions as visible as clone fallback transitions.
DevX UX Expert 0 2 0 No blocking DevX issue; the recovery guidance needs two small fixes to avoid sending users or agents to stale advice.
Supply Chain Security 1 0 0 GitLab SSH validation still resolves credentials before per-attempt policy; fix that auth-contract gap.
OSS Growth Hacker 0 0 0 No growth-surface blockers; docs and changelog stay bounded to the GitLab sparse transport contract without README or strategy scope creep.
Auth Expert 0 0 0 GitLab sparse-fetch auth now resolves per effective attempt, keeps SSH/native Git token-free, and limits REST to executed same-origin HTTPS; no auth blockers found.
Doc Writer 0 1 0 Docs remain scoped and guidance shrinks by 20 words. Qualify the strict-SSH guarantee to distinguish APM's requested URL from Git's effective rewrite target.
Test Coverage 0 0 0 GitLab sparse-fetch behavior is covered by mapped regression, mutation, architecture, conformance, and real-binary transport tests.
Performance Expert 0 0 0 No scoped performance or lock regression found; GitLab sparse fetch keeps one transport per prepared identity and retry work is bounded.

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

Top 5 follow-ups

  1. [Doc Writer] Fold the strict-SSH documentation correction before ship. -- The docs currently overstate that explicit SSH/SCP always keeps the effective fetch on SSH and that Git failure never triggers REST; implementation allows safe same-host SSH-to-HTTPS insteadOf rewrites, after which exhausted effective HTTPS can authorize REST.
  2. [DevX UX Expert] Fold the packaged apm-usage dependency guidance update before ship. -- packages/apm-guide/.apm/skills/apm-usage/dependencies.md still says GitLab path files are fetched over Git, not REST, which contradicts the new Git-first plus restricted REST fallback contract that downstream agents will quote to users.
  3. [DevX UX Expert] Fold the dead fallback docs link fix before ship. -- The custom-port recovery warning points users at a route/anchor that does not exist in this tree; a broken recovery link undercuts the failure-mode UX for the exact transport scenario this PR repairs.
  4. [CLI Logging Expert] Fold an actual protocol-switch warning for GitLab sparse fallback. -- When opt-in fallback changes the attempted sparse transport, users should see the same kind of yellow transition signal clone fallback provides, especially because an effective HTTPS attempt can affect REST eligibility.
  5. [Supply Chain Security] Do not fold the validation auth redesign into this PR; capture only as an out-of-scope follow-up if the maintainer wants it later. -- The credential-resolution probe is real but pre-existing at the merge base, not a regression from this sparse-fetch fix. Folding it now would broaden the patch into validation/provider auth behavior that the maintainer explicitly excluded.

Architecture

classDiagram
    direction LR
    class GitHubPackageDownloader {
      <<FacadeOwner>>
      +_download_github_file(dep_ref, file_path, ref) bytes
      +_build_repo_url(repo_url_base, use_ssh, dep_ref, token) str
    }
    class DownloadDelegate {
      <<Delegate>>
      +download_gitlab_file(dep_ref, file_path, ref) bytes
      +_download_gitlab_file_via_git(dep_ref, file_path, ref, requested_url, effective_url, git_env, auth_mode) bytes
      +_git_file_transport_key(dep_ref, ref, requested_url, effective_url, auth_mode) tuple
      +_gitlab_rest_eligible(effective_url, api_base) bool
    }
    class TransportSelector {
      <<Strategy>>
      +select(dep_ref, cli_pref, allow_fallback, has_token, candidate_url) TransportPlan
    }
    class TransportAttempt {
      <<ValueObject>>
      +scheme str
      +use_token bool
      +label str
      +requested_url str
      +effective_url str
    }
    class TransportPlan {
      <<ValueObject>>
      +attempts list
      +strict bool
      +fallback_hint str
    }
    class InsteadOfResolver {
      <<Protocol>>
      +resolve(candidate_url) str
      +has_exact_rule(candidate_url) bool
    }
    class GitConfigInsteadOfResolver {
      <<Adapter>>
      +resolve(candidate_url) str
      +has_exact_rule(candidate_url) bool
    }
    class AuthResolver {
      <<Strategy>>
      +resolve_for_remote(host, remote_url, org) AuthContext
      +git_env_for_remote(ctx, remote_url) dict
      +build_native_git_credential_env(host_info, remote_url) dict
    }
    class AuthContext {
      <<ValueObject>>
      +token str
      +source str
      +host_info HostInfo
      +auth_scheme str
    }
    class HostInfo {
      <<ValueObject>>
      +kind str
      +host str
      +port int
      +api_base str
    }
    class GitSparseFileTransport {
      <<ReusableResource>>
      +fetch_file(file_path) bytes
      +fetch_file_with_commit(file_path) GitFileFetchResult
      +close() None
    }
    class GitFileTransportError {
      <<TypedFailure>>
    }
    class GitTransportPolicy {
      <<ValueObject>>
      +use_resolved_credentials bool
      +allow_native_credential_lookup bool
      +reject_https_downgrade bool
    }
    GitHubPackageDownloader *-- DownloadDelegate : owns
    GitHubPackageDownloader *-- TransportSelector : shared selector
    DownloadDelegate ..> TransportSelector : consumes plan
    TransportSelector o-- InsteadOfResolver : rewrite policy seam
    InsteadOfResolver <|.. GitConfigInsteadOfResolver
    TransportSelector ..> TransportPlan : returns
    TransportPlan *-- TransportAttempt : ordered attempts
    DownloadDelegate ..> AuthResolver : per-attempt auth
    AuthResolver ..> AuthContext : returns
    AuthContext *-- HostInfo : classifies
    AuthResolver ..> GitTransportPolicy : applies
    DownloadDelegate *-- GitSparseFileTransport : live checkout cache
    GitSparseFileTransport ..> GitFileTransportError : raises typed retry signal
    note for TransportSelector "Single owner for initial scheme and ordered fallback plan"
    note for AuthResolver "Chain of Responsibility: env token -> gh/CLI where eligible -> native credential helper where policy allows"
    class DownloadDelegate:::touched
    class TransportSelector:::touched
    class TransportAttempt:::touched
    class TransportPlan:::touched
    class AuthResolver:::touched
    classDef touched fill:#fff3b0,stroke:#d47600
Loading
flowchart TD
    A["CLI install path -> GitHubPackageDownloader._download_github_file"] --> B["src/apm_cli/deps/download_strategies.py: DownloadDelegate.download_gitlab_file"]
    B --> C["initial_transport_scheme(dep_ref, self._host._protocol_pref)"]
    C --> D["build_repo_url(..., token='') creates requested candidate_url"]
    D --> E["[EXEC] TransportSelector.select(..., has_token=False, candidate_url=candidate_url) probes git insteadOf via GitConfigInsteadOfResolver"]
    E --> F["AuthResolver.resolve_for_remote(host, initial.effective_url or requested_url)"]
    F --> G["TransportSelector.select(..., has_token=bool(initial_ctx.token)) returns final TransportPlan"]
    G --> H{"for attempt in plan.attempts"}
    H --> I["requested_url = attempt.requested_url or build_repo_url(..., token='')"]
    I --> J["effective_url = attempt.effective_url or requested_url"]
    J --> K["AuthResolver.resolve_for_remote(host, effective_url, owner)"]
    K --> L{"attempt.use_token?"}
    L -->|"yes"| M["AuthResolver.git_env_for_remote(attempt_ctx, effective_url)"]
    L -->|"no"| N["AuthResolver.build_native_git_credential_env(host_info, effective_url)"]
    M --> O["validate_git_url_rewrite_safety(requested_url, git_env) revalidates before cache reuse"]
    N --> O
    O --> P["_git_file_transport_key(provider.kind, ref, normalize_repo_url(requested_url), normalize_repo_url(effective_url), auth_mode)"]
    P --> Q["[LOCK] _git_file_transports_lock gets or creates GitSparseFileTransport"]
    Q --> R["[EXEC][FS][NET] GitSparseFileTransport.fetch_file(file_path): git init, remote add, sparse-checkout, fetch, checkout, read_bytes"]
    R -->|"success"| S["return bytes; verbose_callback('Fetched file via git transport: ...')"]
    R -->|"GitFileTransportError only"| T["[LOCK] _discard_git_file_transport(key, failed_transport) evicts exact failed instance"]
    T --> U["rest_eligible = rest_eligible or _gitlab_rest_eligible(effective_url, host_info.api_base)"]
    U --> H
    R -->|"security/local I/O/rewrite/non-transport exception"| V["terminal propagation; no retry and no REST"]
    H -->|"plan exhausted"| W{"rest_eligible true?"}
    W -->|"yes: executed same-origin effective HTTPS"| X["[NET] _download_gitlab_file_via_rest(dep_ref, file_path, ref, verbose_callback)"]
    W -->|"no"| Y["RuntimeError: Git transport failed; REST is not authorized by selected plan"]
Loading

Recommendation

Advisory recommendation: fold the four in-scope docs/UX corrections now, then re-run the shepherd's exact-head validation path. Do not expand into validation auth redesign, provider-wide cache/auth cleanup, README positioning, or docs restructuring; after the bounded folds and refreshed evidence, this should be ready to ship as a trust-preserving GitLab sparse transport fix.


Full per-persona findings

Python Architect

  • [nit] Design pattern inventory for the architecture artifact.
    Design patterns
  • Used in this PR: Strategy / selector -- TransportSelector.select owns ordered transport attempts, while DownloadDelegate.download_gitlab_file consumes the plan instead of recomputing scheme/fallback decisions.
  • Used in this PR: Dataclass-as-value-object -- frozen TransportAttempt and TransportPlan carry requested/effective URL identity, token intent, labels, and strictness through the flow.
  • Used in this PR: Facade/Delegate -- DownloadDelegate remains the backend-specific boundary under GitHubPackageDownloader, with the GitLab sparse path folded into the existing delegate surface.
  • Used in this PR: Chain of Responsibility -- AuthResolver.resolve_for_remote retains one credential-resolution chain per effective remote and routes child Git env creation through git_env_for_remote or build_native_git_credential_env.
  • Pragmatic suggestion: none -- the current shape is the simplest correct design at this bounded scope; further provider-wide auth/cache redesign would cross the maintainer's no-scope-creep reservation.

CLI Logging Expert

  • [recommended] Emit a protocol-switch warning when GitLab sparse fallback actually changes transport. at src/apm_cli/deps/download_strategies.py:1058
    CloneEngine warns at the moment an admitted fallback changes protocol, but the new GitLab sparse loop only emits the custom-port prewarning and then advances from a failed attempt to the next attempt silently. For default human output, a cross-protocol retry is a yellow event: the user opted into it, but should know APM is now trying a different transport and potentially unlocking REST eligibility after an effective HTTPS attempt.
    Suggested: Track the previous failed attempt label/scheme in download_gitlab_file and, when plan.strict is false and the next attempt uses a different scheme, emit the same shape as CloneEngine: Protocol fallback: <prev label> GitLab sparse fetch of <project_path> failed; retrying with <attempt.label>. Keep credentials redacted.
  • [nit] Make the custom-port warning fix match all fallback sources. at src/apm_cli/deps/transport_selection.py:154
    The warning says to drop --allow-protocol-fallback, but fallback can also come from APM_ALLOW_PROTOCOL_FALLBACK=1 or saved config; for an explicit SSH URL, Pin the URL scheme may also not stop fallback while the escape hatch remains enabled.
    Suggested: Prefer wording like: Disable protocol fallback to fail fast, or declare the endpoint each protocol should use.

DevX UX Expert

  • [recommended] Custom-port fallback warning links to a docs route that does not exist in this tree. at src/apm_cli/deps/transport_selection.py:42
    The warning is part of the failure/recovery UX for the new GitLab sparse transport path. APM's failure-mode standard is one concrete next action; a dead See: URL breaks that recovery path. I checked the docs tree for guides/dependencies and restoring-the-legacy-permissive-chain and found no matching page or anchor, while the updated in-scope guidance now lives under consumer/manage-dependencies transport selection.
    Suggested: Point _PROTOCOL_FALLBACK_DOCS_URL at the existing transport-selection section, e.g. https://microsoft.github.io/apm/consumer/manage-dependencies/#transport-selection, or add the referenced anchor in the docs as part of this bounded docs update.
  • [recommended] Shipped apm-usage dependency guidance still says GitLab path files are fetched over git, not REST. at packages/apm-guide/.apm/skills/apm-usage/dependencies.md:68
    The PR intentionally changes the user contract to Git-first with restricted REST fallback after an eligible effective HTTPS attempt. The distributed apm-usage resource still says GitLab path files are fetched over git transport, not the REST API, which conflicts with the updated consumer docs and can make agents give users obsolete troubleshooting advice.
    Suggested: Mirror the bounded wording used in docs/src/content/docs/consumer/manage-dependencies.md: GitLab path: files use Git first, with restricted REST fallback, then link to the GitLab authentication/fetch policy section.

Supply Chain Security

  • [blocking] GitLab SSH validation still performs host-scoped credential resolution before the transport attempt is known. at src/apm_cli/install/validation.py:454
    The PR's security contract says GitLab non-HTTPS attempts must not resolve HTTPS credentials and that authentication is selected per effective attempt. However validation builds a GitLab SSH candidate, then immediately calls auth_resolver.resolve_for_dep(dep_ref) before iterating the selected attempts. With an explicit GitLab SSH/SCP dependency this path can read GITLAB_APM_PAT/GITLAB_TOKEN and invoke git credential fill even though the eventual ls-remote is SSH and git_env_for_remote later strips those credentials. That violates least privilege and the documented 'credential helper when the remote transport permits lookup' invariant, and it leaves the validation surface out of sync with the fixed sparse-fetch path.
    Suggested: Mirror download_gitlab_file's per-attempt flow in validation: select an anonymous initial plan from the requested URL, resolve credentials with resolve_for_remote(policy_url) only for attempts whose effective transport permits it, and avoid resolve_for_dep for GitLab SSH, SCP, file, and HTTP attempts. Add a regression test for _validate_package_exists('git@gitlab.com:owner/repo.git#main') with a recording token manager asserting resolve_credential_from_git is not called.
    Proof (manual only): (no test ref) -- proves: An explicit GitLab SSH validation path can consult HTTPS credential sources before running the SSH probe. [secure-by-default,governed-by-policy]
    dep_ctx = None if is_generic else auth_resolver.resolve_for_dep(dep_ref)

OSS Growth Hacker

No findings.

Auth Expert

No findings.

Doc Writer

  • [recommended] Qualify the strict-SSH guarantee for safe Git rewrites at docs/src/content/docs/consumer/authentication.md:68
    The new paragraph says explicit SSH/SCP and SSH preference keep fetches on SSH and Git failure never triggers REST. That is stronger than the implementation: TransportSelector._rewrite_attempt preserves the requested URL but adopts the effective rewrite scheme, including in strict mode. A safe same-host SSH-to-HTTPS rewrite can therefore execute effective HTTPS, and download_gitlab_file authorizes REST after that attempt fails and the plan is exhausted. This also contradicts manage-dependencies.md, which correctly says an explicit scheme does not disable insteadOf configuration. The packaged authentication guidance repeats the unconditional preservation claim. This is a bounded documentation correction, not a request to change transport policy.
    Suggested: State that strict mode passes the selected SSH/SCP URL to Git without APM choosing another protocol; safe Git insteadOf rules still apply. Replace 'Git failure never triggers REST' with 'If the effective transport remains SSH, failure never triggers REST, even with a PAT available.' Apply the same distinction in packages/apm-guide/.apm/skills/apm-usage/authentication.md and retain the existing effective-HTTPS eligibility paragraph.

Test Coverage

No findings.

Performance Expert

No findings.

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

Deferred (out-of-scope follow-ups)

  • Validation credential-order redesign: scope_boundary_crossed = unchanged merge-base validation behavior; this PR preserves validation behavior and fixes per-attempt auth for GitLab sparse fetch. No issue or project was created.

Copilot signals reviewed

No Copilot review or inline comments exist in the first fetch round.

Address CEO docs and diagnostics follow-ups on PR #2939: distinguish requested SSH from effective Git rewrites, correct packaged REST guidance, repair fallback recovery advice, and expose admitted protocol switches. Add a warning regression and isolated mutation proof without changing validation auth or other provider policy.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Fix both legacy integration REST fixtures to execute a real HTTPS selector plan and raise only typed Git failures. Probe symlink capability rather than skipping Windows unconditionally, and qualify the canonical sparse-fetch credential documentation. Addresses Copilot review 5167472317 without changing validation auth or shared selector dedup semantics.

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

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CI recovery 1: the shared custom-port warning now correctly explains disabling all fallback configuration sources and points at the live docs route. Update its older clone consumer assertion to this reviewed wording without changing runtime behavior.

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

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

Copy link
Copy Markdown
Collaborator Author

APM Review Panel: ship_now

GitLab sparse fetch now preserves the selected transport plan, auth boundary, and fallback contract without broadening into provider-wide redesign.

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

The nine-persona panel converges: no substantive in-scope gap remains on the reviewed GitLab sparse-fetch transport work. The final recovery commit at 81bbad6 changed only stale test expectations for the reviewed warning text and live docs URL; the supplied diff from 246eb0a..81bbad6 over src/ and uv.lock is empty. Exact-head evidence is load-bearing here: 457 targeted tests passed in 170.96s with zero skips, 14 behavioral mutants plus static M9 passed, owner tests passed separately, canonical lint including GNU Linux guards passed, and an actual Linux amd64 binary built from 81bbad6/version 0.30.0 passed 5 acceptance tests in 40.33s. Current CI is still being observed by the orchestrator, so publication should wait for final observed-green checks rather than claiming CI is already green.

Mandatory reservation: Maintainer explicitly requires no scope creep. Preserve GitLab sparse-fetch transport and its directly coupled authentication, rewrite, REST eligibility, live checkout identity, error, regression-test, documentation and ownership-guard contracts. Do not broaden this into provider-wide auth/cache redesign, new user features, unrelated cleanup, documentation restructuring or project positioning work. Full scope applies.

Dissent disposition is straightforward. The python-architect return contains only an informational pattern inventory, not an action item, and every other active specialist reports no remaining finding. The two deferred baseline themes--validation pre-attempt resolve_for_dep and shared _dedup_attempts scheme/use_token identity dropping distinct alternate mirror targets--are explicitly not introduced by this diff; changing either would cross the stated scope into validation or all-provider fallback redesign. The three Copilot fixes and the earlier panel correction themes were folded, and no unresolved foldable finding remains.

Dissent. There is no substantive panel dissent. I side with the scope-bound disposition: the architecture nit is informational, and the two baseline themes are not PR-introduced defects and would require broader provider or validation redesign.

Aligned with: Portable by manifest, Secure by default, Governed by policy, Multi-harness / multi-host, OSS community-driven, Pragmatic as npm

Growth signal. No README, positioning, or growth-strategy change is warranted. The adoption signal is trust through restraint: a focused transport correctness fix with strong evidence, not a broader narrative or feature expansion.

Panel summary

Persona B R N Takeaway
Python Architect 0 0 1 No blocking architecture concern; terminal folds route GitLab sparse fetch through selector/AuthResolver with behavioral plus static guards.
CLI Logging Expert 0 0 0 CLI warning folds are verified; no remaining CLI logging issues within the reserved GitLab sparse-fetch scope.
DevX UX Expert 0 0 0 GitLab sparse transport UX, warnings, and docs are scoped and actionable; no DevX findings remain.
Supply Chain Security 0 0 0 No introduced supply-chain blocker remains in the bounded GitLab sparse-fetch transport scope.
OSS Growth Hacker 0 0 0 No growth finding: final docs and changelog stay inside the bounded GitLab sparse-fetch scope; no README, strategy, or positioning work needed.
Auth Expert 0 0 0 GitLab sparse auth, credential isolation, rewrite checks, and REST eligibility match the bounded transport contract.
Doc Writer 0 0 0 Docs now match the bounded GitLab sparse-fetch transport contract; no doc-writer findings remain.
Test Coverage 0 0 0 GitLab sparse transport contracts have exact-head unit, integration, mutation, owner, conformance, and binary e2e coverage.
Performance Expert 0 0 0 No introduced performance regression; GitLab sparse plan stays cached/reused, adds no raw ls-remote, and exact-head evidence passes.

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

Architecture

classDiagram
    direction LR
    class GitHubPackageDownloader { <<FacadeOwner>> +_download_github_file() }
    class DownloadDelegate { <<Delegate>> +download_gitlab_file() +_download_gitlab_file_via_git() }
    class TransportSelector { <<Strategy>> +select() TransportPlan }
    class TransportPlan { <<ValueObject>> +attempts +strict }
    class TransportAttempt { <<ValueObject>> +requested_url +effective_url +use_token }
    class AuthResolver { <<Strategy>> +resolve_for_remote() +git_env_for_remote() }
    class GitSparseFileTransport { <<ReusableResource>> +fetch_file() }
    GitHubPackageDownloader *-- DownloadDelegate : owns
    DownloadDelegate ..> TransportSelector : consumes plan
    TransportSelector ..> TransportPlan : returns
    TransportPlan *-- TransportAttempt : ordered attempts
    DownloadDelegate ..> AuthResolver : per-attempt auth/env
    DownloadDelegate *-- GitSparseFileTransport : keyed requested+effective+auth
    note for TransportSelector "Single owner: initial scheme plus ordered fallback plan"
    note for AuthResolver "Chain of Responsibility: env/CLI/helper only where policy allows"
    class DownloadDelegate:::touched
    class TransportSelector:::touched
    class AuthResolver:::touched
    classDef touched fill:#fff3b0,stroke:#d47600
Loading
flowchart TD
    A["CLI install -> GitHubPackageDownloader._download_github_file"] --> B["download_strategies.py: DownloadDelegate.download_gitlab_file"]
    B --> C["transport_selection.py: initial_transport_scheme(dep_ref, pref)"]
    C --> D["build_repo_url(..., token='') creates requested candidate_url"]
    D --> E["[EXEC] TransportSelector.select(..., has_token=False, candidate_url)"]
    E --> F["AuthResolver.resolve_for_remote(host, initial effective/requested URL)"]
    F --> G["TransportSelector.select(..., has_token=bool(initial_ctx.token))"]
    G --> H{"for attempt in plan.attempts"}
    H --> I["AuthResolver.resolve_for_remote(host, effective_url) then git_env_for_remote/build_native_git_credential_env"]
    I --> J["validate_git_url_rewrite_safety(requested_url, git_env)"]
    J --> K["[LOCK] _git_file_transport_key(provider, ref, requested, effective, auth_mode)"]
    K --> L["[EXEC][FS][NET] _download_gitlab_file_via_git -> GitSparseFileTransport.fetch_file"]
    L -->|"success"| M["return bytes; verbose_callback fetched via git transport"]
    L -->|"GitFileTransportError only"| N["[LOCK] _discard_git_file_transport(exact failed instance); maybe warn protocol fallback; maybe mark REST eligible"]
    N --> H
    L -->|"security/rewrite/local/non-transport failure"| O["terminal exception; no retry and no REST"]
    H -->|"exhausted"| P{"executed same-origin effective HTTPS?"}
    P -->|"yes"| Q["[NET] _download_gitlab_file_via_rest"]
    P -->|"no"| R["RuntimeError: REST not authorized by selected transport plan"]
Loading

Recommendation

Ship the reviewed scoped code once the orchestrator observes the still-running CI checks finish green. There are no remaining panel follow-ups to fold in this PR, and the deferred baseline themes should stay outside this PR unless a future maintainer explicitly opens a provider-wide validation or fallback-identity redesign.


Full per-persona findings

Python Architect

  • [nit] Architecture pattern inventory for the panel synthesis.
    Design patterns
  • Used in this PR: Strategy / selector -- TransportSelector.select remains the single owner for initial scheme and ordered fallback attempts, while DownloadDelegate.download_gitlab_file consumes the selected plan instead of recomputing it.
  • Used in this PR: Dataclass-as-value-object -- frozen TransportAttempt and TransportPlan carry requested URL, effective URL, token intent, label, and strictness through the GitLab sparse path.
  • Used in this PR: Facade/Delegate -- GitHubPackageDownloader keeps backend orchestration delegated to DownloadDelegate, with GitLab sparse-fetch folded into that existing boundary.
  • Used in this PR: Chain of Responsibility -- AuthResolver.resolve_for_remote applies the credential chain per effective remote, and the GitLab sparse loop then selects managed or native Git env per attempt.
  • Pragmatic suggestion: none -- the current shape is the simplest correct design at this bounded scope; broad provider-wide auth/cache redesign would cross the maintainer's no-scope-creep reservation.

CLI Logging Expert

No findings.

DevX UX Expert

No findings.

Supply Chain Security

No findings.

OSS Growth Hacker

No findings.

Auth Expert

No findings.

Doc Writer

No findings.

Test Coverage

No findings.

Performance Expert

No findings.

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

Reservations carried from strategic-alignment

  • Maintainer explicitly requires no scope creep. Preserve GitLab sparse-fetch transport and its directly coupled authentication, rewrite, REST eligibility, live checkout identity, error, regression-test, documentation and ownership-guard contracts. Do not broaden this into provider-wide auth/cache redesign, new user features, unrelated cleanup, documentation restructuring or project positioning work. -- Addressed by the bounded folds and the two explicit baseline deferrals below.

Folded in this run

  • (panel) Qualify strict SSH docs with requested versus effective safe Git rewrite semantics. -- resolved in 246eb0a.
  • (panel) Correct packaged dependency guidance to Git-first with restricted REST recovery. -- resolved in 246eb0a.
  • (panel) Point the shared custom-port warning at the live transport-selection documentation. -- resolved in 246eb0a.
  • (panel) Show actual opt-in cross-protocol GitLab sparse retry transitions, with eight regression cases and M10 mutation proof. -- resolved in 246eb0a.
  • (panel) Make fallback-disable advice cover CLI flags, environment and saved configuration. -- resolved in 246eb0a.
  • (copilot) Repair both existing integration REST fixtures with real HTTPS selection, prepared auth and typed Git failures. -- resolved in 7479322.
  • (copilot) Replace unconditional Windows symlink-test skip with a capability probe. -- resolved in 7479322.
  • (copilot) Qualify the canonical sparse-fetch credential-chain prose and package-source table. -- resolved in 7479322.
  • (panel) Update the older clone warning regression to the reviewed remediation and parsed live docs URL after CI exposed its stale expectation. -- resolved in 81bbad6.

Copilot signals reviewed

  • 3979380427 -- LEGIT: Both overlooked integration REST fixtures failed: their MagicMock plans executed no attempt, and their sparse mock raised an untyped RuntimeError. Real HTTPS plans and typed errors restore their intended contract.
  • 3979380480 -- LEGIT: An OS-only symlink skip discarded supported Windows coverage. The test now attempts symlink creation and skips only when the filesystem reports unsupported capability.
  • 5167472317-suppressed-auth-docs -- LEGIT: The canonical auth page needed the sparse-fetch effective-HTTPS qualifier. Added bounded prose and source-table guidance without claiming validation credential ordering changed.
  • 5167472317-suppressed-rewrite-dedup -- LEGIT: Distinct mirror targets can share the existing scheme/auth dedup key. Its AST is unchanged from merge base; changing this all-provider selector behavior crosses the explicit preservation boundary, so it is deferred.

Both inline threads were answered and resolved using threaded replies. Two fetch rounds consumed; no additional review round was requested.

Deferred (out-of-scope follow-ups)

  • (panel) Move pre-existing validation credential resolution behind per-effective-attempt policy. -- scope_boundary_crossed: The resolve_for_dep call is identical at merge base. Changing validation credential ordering exceeds the sparse-fetch repair and contradicts the explicit requirement to preserve existing validation behavior.
  • (copilot) Make shared transport-selector dedup distinguish alternate mirror URL identities. -- scope_boundary_crossed: The shared _dedup_attempts AST is identical at merge base. URL-aware dedup changes all-provider clone/ref/validation fallback semantics, beyond executing the existing selector plan in GitLab sparse fetch.

No follow-up issue/project was created; no README, policy baseline, or network configuration changed.

Regression-trap evidence (mutation-break gate)

457 exact-head tests passed in 170.96s with zero skips. All 14 behavioral mutation drivers required a passing baseline and the intended child AssertionError; the separate M9 static suite rejected owner bypasses. The new protocol-switch warning test was also demonstrated failing before the implementation.

Lint contract

Exact HEAD 81bbad6: uv run --frozen --no-sync ruff check src/ tests/ scripts/lint_architecture_boundaries.py scripts/architecture_linter/ exited 0; ruff format --check for the same paths exited 0 (1849 files already formatted). Pylint R0801, auth-signals, architecture boundaries, unchanged GNU Linux YAML/2100-line/relative_to guards, assertion and duplicate ratchets, and conformance orphan check all exited 0. Evidence: shepherd-81bbad6-lint.log and shepherd-81bbad6-linux-guards.log. origin/main was freshly merged (already up to date) before each mirror/push.

CI

The CEO's pending-CI condition above is now satisfied. Observed final HEAD 81bbad6: all 18 check-rollup entries complete (16 SUCCESS, 1 NEUTRAL, 1 SKIPPED). Main CI https://github.com/microsoft/apm/actions/runs/34484187161 and merge gate https://github.com/microsoft/apm/actions/runs/34484187284 succeeded, as did spec, docs, CodeQL, NOTICE and CLA. Recovery count 1: earlier run 34483012748 exposed one stale clone-warning assertion, corrected in 81bbad6 without production changes. Actual exact-head Linux binary acceptance separately passed 5 tests in 40.33s; binary SHA256 e7f73b82b11050daa220a25df7b6914c0bec40f27ebac11fa40464a62600cbdf. GitHub remains MERGEABLE/BLOCKED, not a conflict; no merge or auto-merge was performed.

The actual final Linux amd64 binary reports 0.30.0 (81bbad6); five SSH-semver acceptance tests passed in 40.33s. This is separate executed binary evidence, not an inference from generic CI.

Mergeability status

PR head SHA CEO stance iters folds defers Copilot rounds CI mergeable mergeStateStatus notes
#2939 81bbad6 ship_now 2 9 2 2 green MERGEABLE BLOCKED awaiting maintainer review

Convergence

Two full synchronous nine-persona advisory passes and separate CEO syntheses; one CI recovery. Final stance: ship_now. Canonical-owner completion v2 passed JSON schema and deterministic exact-head semantic verification for all three touched owners. No merge, auto-merge, queue, closure or superseding PR was performed.

@danielmeppiel
Daniel Meppiel (danielmeppiel) merged commit 146b24f into main Sep 11, 2026
18 checks passed
@danielmeppiel
Daniel Meppiel (danielmeppiel) deleted the danielmeppiel-gitlab-sparse-transport branch September 11, 2026 20:01
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.

Specification: preserve GitLab sparse-fetch transport [BUG] GitLab sparse-fetch rewrites ssh:// URL with custom port to HTTPS on same host

2 participants