fix(ado): preserve lock coordinates for outdated and update#2226
Conversation
Signed-off-by: King Star <mcxin.y@gmail.com>
…t-tool-scope-warning
Co-authored-by: King Star <54024410+jstar0@users.noreply.github.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: b3ef1ddd-3e6e-4516-b1ef-147d6d3b7381
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: b3ef1ddd-3e6e-4516-b1ef-147d6d3b7381
Persist canonical Azure DevOps coordinates across lock serialization and reconstruction, route semver ref checks through the canonical ADO URL, and add real Consume lifecycle coverage for outdated, update, audit, and convergence.\n\nCo-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>\nCopilot-Session: 1117b9a1-68ed-4d08-8f91-04694a98ce5f
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>\nCopilot-Session: 1117b9a1-68ed-4d08-8f91-04694a98ce5f
There was a problem hiding this comment.
Pull request overview
This PR restores full Azure DevOps (ADO) dependency coordinate fidelity across lockfile write/read/reconstruction, and threads the canonical ADO remote URL into semver ref enumeration so apm outdated / bounded apm update can resolve and materialize real tags instead of degrading to unknown.
Changes:
- Persist
ado_organization/ado_project/ado_repoinLockedDependency, validate them fail-closed, and reconstruct legacy locks viaDependencyReference.canonical_ado_coordinates(). - Thread a canonical
remote_urlthrough_maybe_resolve_git_semver->GitSemverResolver->RefResolver, with safety checks and cache-keying by effective remote identity. - Add unit + real-binary integration contracts, plus an architecture-boundary lint guard, to prevent lock reconstruction from reintroducing local ADO parsing.
Show a summary per file
| File | Description |
|---|---|
src/apm_cli/models/dependency/reference.py |
Adds canonical ADO coordinate derivation and enforces coordinate/path consistency when building ADO URLs. |
src/apm_cli/deps/lockfile.py |
Persists ADO coordinates, validates partial/mismatched states, and reconstructs ADO refs deterministically. |
src/apm_cli/install/phases/resolve.py |
Passes canonical ADO remote URL into semver resolution. |
src/apm_cli/deps/git_semver_resolver.py |
Accepts optional remote_url and forwards it to the ref resolver when present. |
src/apm_cli/marketplace/ref_resolver.py |
Accepts an optional canonical ADO URL, validates it, and includes it in cache identity + bearer retry path. |
scripts/lint-architecture-boundaries.sh |
Adds a static guard enforcing lock reconstruction routes through DependencyReference and forbids repo_url.split(...) in lockfile reconstruction. |
tests/unit/deps/test_lockfile_consumer_contract.py |
Extends lock consumer contracts to cover ADO fields and URL-consumer projection. |
tests/unit/install/test_git_semver_wiring.py |
Verifies ADO semver wiring passes the canonical ADO URL. |
tests/unit/install/phases/test_resolve_ref_resolver_reuse.py |
Uses a production-shaped ADO semver dep fixture for auth/cache wiring reuse. |
tests/unit/marketplace/test_ref_resolver.py |
Adds tests for canonical ADO URL usage, unsafe URL rejection, and cache identity. |
tests/unit/test_outdated_command.py |
Regression test: lock coordinates must reach the real remote-ref consumer and form the expected parsed ADO URL. |
tests/integration/test_ado_lock_consumer_contract.py |
New real-binary end-to-end ADO lock/outdated/update/audit/convergence contract using hermetic local Git + insteadOf rewrite. |
tests/integration/test_architecture_authorities.py |
Ensures the ADO coordinate canonical owner is used and guarded by static lint. |
tests/quality/critical_suite.toml |
Adds the new ADO integration contract to the finite critical suite. |
tests/quality/test_test_taxonomy.py |
Updates expected finite manifest module count. |
docs/src/content/docs/reference/lockfile-spec.md |
Documents the three additive persisted ADO-only lock fields. |
CHANGELOG.md |
Adds an Unreleased/Fixed entry for restored ADO outdated/bounded-update behavior. |
Review details
- Files reviewed: 17/17 changed files
- Comments generated: 4
- Review effort level: Low
| if remote_url is not None: | ||
| parsed_remote = urllib.parse.urlparse(remote_url) | ||
| if ( | ||
| not ado_host | ||
| or parsed_remote.scheme != "https" | ||
| or parsed_remote.hostname != self._host | ||
| or parsed_remote.username is not None | ||
| or parsed_remote.password is not None | ||
| or parsed_remote.query | ||
| or parsed_remote.fragment | ||
| ): |
| cache_key = remote_url or owner_repo | ||
| lock = self._remote_lock(cache_key) | ||
| with lock: | ||
| # Check cache first | ||
| cached = self._cache.get(owner_repo) | ||
| cached = self._cache.get(cache_key) | ||
| if cached is not None: | ||
| return cached | ||
|
|
||
| if self._offline: | ||
| raise OfflineMissError(package="", remote=owner_repo) | ||
|
|
| canonical_ado = DependencyReference.canonical_ado_coordinates(host, dep_ref.repo_url) | ||
| if isinstance(dep_ref, DependencyReference): | ||
| supplied_ado = (dep_ref.ado_organization, dep_ref.ado_project, dep_ref.ado_repo) | ||
| if supplied_ado != canonical_ado: | ||
| raise ValueError( | ||
| "Azure DevOps reference coordinates do not match the canonical repository path" | ||
| ) | ||
| ado_organization, ado_project, ado_repo = supplied_ado | ||
| else: | ||
| ado_organization, ado_project, ado_repo = canonical_ado |
| - Azure DevOps lock entries now preserve complete organization, project, and | ||
| repository coordinates, so `apm outdated` reports real tag status and bounded | ||
| `apm update` runs resolve and materialize the matching tag. (closes #2197) |
Resolve the critical-suite count at eleven after combining #2223 with the new ADO lifecycle contract.\n\nCo-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>\nCopilot-Session: 1117b9a1-68ed-4d08-8f91-04694a98ce5f
Co-authored-by: King Star <54024410+jstar0@users.noreply.github.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: b3ef1ddd-3e6e-4516-b1ef-147d6d3b7381
Route git-source semver tag enumeration through the canonical transport selector and preserve the chosen transport across install and update. Add a real-binary lifecycle contract with one-protocol rewrites, transport tracing, strict failure, lock provenance, and convergence evidence. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 324c57ea-aa78-4f0e-a656-d12f966cedff
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 324c57ea-aa78-4f0e-a656-d12f966cedff
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 324c57ea-aa78-4f0e-a656-d12f966cedff
APM Review Panel:
|
| Persona | B | R | N | Takeaway |
|---|---|---|---|---|
| Python Architect | 1 | 2 | 2 | Regression: canonical_ado_coordinates bypasses non-ADO validation; dead isinstance; hostname case gap |
| CLI Logging Expert | 0 | 1 | 1 | OfflineMissError reports wrong identity; error hint not actionable |
| DevX UX Expert | 0 | 1 | 2 | Upgrade path seamless; mismatch error needs host/value context |
| Supply Chain Security Expert | 0 | 0 | 2 | Fail-closed design is sound; no exploit vectors |
| OSS Growth Hacker | 0 | 0 | 2 | ADO fix is a strong enterprise-trust signal; CHANGELOG solid |
| Auth Expert | 0 | 0 | 2 | Bearer path correct; hostname nit; path scope advisory |
| Doc Writer | 0 | 0 | 1 | CHANGELOG and lockfile-spec.md are ship-ready |
| Test Coverage Expert | 1 | 0 | 2 | CI regression blocking; ADO integration contract is well-covered |
| Performance Expert | 0 | 0 | 3 | All costs O(1), negligible vs subprocess; cache semantics correct |
B = blocking-severity findings, R = recommended, N = nits.
Counts are signal strength, not gates. The maintainer ships.
Top 5 follow-ups
- [Python Architect] (blocking-severity) Restore
_validate_final_repo_fieldsinparse()for non-ADO hosts -- CI shard 2 FAILED: test_ssh_nested_group_with_virtual_ext_rejected. Call_validate_final_repo_fieldsunconditionally in parse() Phase 3; usecanonical_ado_coordinatesonly for ADO coordinate extraction after validation passes. - [Test Coverage Expert] (blocking-severity) Address spec-conformance failure: 51 uncited critical-path lines -- CI spec-conformance job FAILED. Add spec anchors + manifest rows + conformance tests for each cited line, or file explicit waivers per CONFORMANCE.md.
- [Python Architect] Normalize hostname comparison to case-insensitive in
ref_resolver.py:215-- Consensus across 4 panelists. Fails closed but violates RFC 4343. Fix:.lower()on both sides. - [CLI Logging Expert] Make error messages actionable: include actual vs expected values -- Users cannot act on "Rebuild the dependency reference from its declared source" without knowing which reference or what mismatched.
- [Python Architect] Remove dead
isinstanceguard inlockfile.py:551--dep_refis alwaysDependencyReferenceat that point; else branch is unreachable dead code.
Per-persona findings detail
Python Architect
- PA-1 (blocking)
reference.py:1960: Non-ADO validation silently dropped --canonical_ado_coordinatesreturns (None,None,None) for non-ADO hosts without running_validate_final_repo_fields. CI-confirmed. - PA-2 (recommended)
lockfile.py:551: Dead isinstance guard -- dep_ref is always DependencyReference; else branch is unreachable. - PA-3 (recommended)
ref_resolver.py:216: Hostname comparison case-sensitive -- urlparse lowercases but self._host may not. Use.lower(). - PA-4 (nit)
ref_resolver.py:291: OfflineMissError.remote uses owner_repo but cache_key may be remote_url. - PA-5 (nit)
reference.py:2055: canonical_ado_coordinates re-invoked in to_github_url -- O(1) cost, acceptable guard.
Auth Expert
- (nit)
ref_resolver.py:215: Hostname case sensitivity -- same as PA-3. Fails closed. - (nit)
ref_resolver.py:226: remote_url path not validated against owner_repo -- safe today, future risk if callers bypass to_github_url.
Test Coverage Expert
- (blocking) CI regression: test_ssh_nested_group_with_virtual_ext_rejected FAILS -- root cause is PA-1.
- (nit) No test for mixed-case hostname comparison in ref_resolver.
- (nit) No test for offline miss with remote_url reporting canonical URL.
Supply Chain Security Expert
- (nit) Hostname case sensitivity -- same as PA-3. Fails closed, not exploitable.
- (nit) remote_url path not validated vs owner_repo -- same as Auth-2. Future defense.
CLI Logging Expert
- (recommended) OfflineMissError remote=owner_repo when cache_key is remote_url -- user action points to wrong identity.
- (nit) GitLsRemoteError hint "Rebuild the dependency reference from its declared source" is not actionable.
DevX UX Expert
- (recommended) Error message "The canonical remote URL does not match the configured host." gives no values to diff -- include parsed hostname and self._host in message.
- (nit) OfflineMissError identity inconsistency.
- (nit) Partial-coordinates error lacks repair guidance.
OSS Growth Hacker
- (nit) CHANGELOG entry could lead with the pain ("ADO packages previously showed unknown") before the fix.
- (nit) Future ADO quickstart section opportunity -- not this PR.
Doc Writer
- CHANGELOG format is consistent and correct -- (closes [BUG]
apm outdated/apm updatereportunknownfor Azure DevOps deps —LockedDependency.to_dependency_ref()dropsado_*fields (distinct from #2150 / #2119) #2197; fix(ado): preserve lock coordinates for outdated and update #2226) matches established pattern. - lockfile-spec.md ado_project and ado_repo Notes could add persistence rationale -- optional, non-bloat.
Performance Expert
- canonical_ado_coordinates in to_github_url: ~1.4us per call, amortized by cache, noise vs subprocess.
- urlparse in _git_url_and_env: ~1.9us per call, guarded by cache.
- Cache key divergence (remote_url vs owner_repo): correct semantic split; no double-fetch today.
- No O(n^2) patterns introduced.
Signed-off-by: King Star <mcxin.y@gmail.com>
…, error messages) - reference.py parse() Phase 3: restore _validate_final_repo_fields so non-ADO virtual-extension validation still runs; canonical_ado_coordinates is for consumers with already-validated input only (lockfile, outdated) - lockfile.py: remove dead isinstance(dep_ref, DependencyReference) guard; function signature already requires DependencyReference, and tests now provide proper Mocks with ado_* attributes set to None - ref_resolver.py: normalize hostname comparison to .lower() per RFC 3986 3.2.2 (urlparse always lowercases); OfflineMissError now uses cache_key instead of owner_repo to match the actual cache lookup key; error message now shows expected vs actual hostname with actionable repair hint - test_dev_dependencies.py: fix Mock to set ado_organization/project/repo=None so the test double correctly represents a non-ADO DependencyReference apm-spec-waiver: restores ADO lock coordinate fidelity lost in prior impl; no new spec semantics Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 324c57ea-aa78-4f0e-a656-d12f966cedff
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 324c57ea-aa78-4f0e-a656-d12f966cedff
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fold Copilot review by carrying the selected plain-HTTP scheme into semver enumeration, centralizing the URL builder, suppressing all auth channels on HTTP, and tightening cache-key typing. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 324c57ea-aa78-4f0e-a656-d12f966cedff
Co-authored-by: King Star <54024410+jstar0@users.noreply.github.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: b3ef1ddd-3e6e-4516-b1ef-147d6d3b7381
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: b3ef1ddd-3e6e-4516-b1ef-147d6d3b7381
apm-spec-waiver: bug fix restores existing transport preference semantics; no new normative requirement Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 324c57ea-aa78-4f0e-a656-d12f966cedff
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: b3ef1ddd-3e6e-4516-b1ef-147d6d3b7381
Remove the post-review explicit-HTTP expansion while retaining precise cache typing and concise changelog coverage. Issue #2184 remains bounded to explicit SSH/prefer-ssh behavior. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 324c57ea-aa78-4f0e-a656-d12f966cedff
APM Review Panel:
|
| Persona | B | R | N | Takeaway |
|---|---|---|---|---|
| Python Architect | 0 | 1 | 1 | Single-owner rule, lock validation, and AC4 guard are correct; track the 2099-line module ceiling. |
| CLI Logging Expert | 0 | 0 | 2 | Current errors are actionable; two hypothetical wording nits remain. |
| DevX UX Expert | 0 | 0 | 0 | The real ADO install, outdated, update, audit, and convergence promises are restored. |
| Supply Chain Security Expert | 0 | 0 | 2 | Fail-closed design is sound; two defense-in-depth test nits remain. |
| OSS Growth Hacker | 0 | 0 | 0 | The fix is a clear enterprise ADO trust signal. |
| Auth Expert | 0 | 0 | 0 | AuthResolver ownership, token isolation, and PAT/bearer fallback remain intact. |
| Doc Writer | 0 | 0 | 1 | Lock schema and changelog are accurate; one touched-page ordering nit is optional. |
| Test Coverage Expert | 0 | 1 | 1 | Critical surfaces meet the evidence floor; the PR body can map one more URL scenario. |
| Performance Expert | 0 | 1 | 2 | No runtime regression; document the canonical URL cache-key discipline for future callers. |
B = blocking-severity findings, R = recommended, N = nits.
Counts are signal strength, not gates. The maintainer ships.
Top 3 follow-ups
- [Test Coverage Expert] Add a PR-body scenario row for the explicit ADO semver/outdated URL handoff -- coverage is already green, but the mapping improves reviewer and contributor discoverability.
- [Python Architect] Track a future extraction for
reference.py, now 2099/2100 lines -- planned separation is preferable to an emergency split. - [Performance Expert] Document that ADO callers must supply
remote_urlto share the canonical cache slot -- no current caller regresses, but the API invariant should be explicit.
Architecture
classDiagram
direction LR
class DependencyReference {
+ado_organization str
+ado_project str
+ado_repo str
+canonical_ado_coordinates host repo_url
+to_github_url
+parse
}
class LockedDependency {
+ado_organization str
+ado_project str
+ado_repo str
+from_dependency_ref
+to_dependency_ref
+to_dict
+from_dict
}
class RefResolver {
+list_remote_refs
-_git_url_and_env
-_retry_rejected_ado_pat
}
class GitSemverResolver {
+resolve
}
class LockFile {
+read
+write
}
LockedDependency ..> DependencyReference : reconstructs through canonical owner
GitSemverResolver ..> RefResolver : passes canonical remote URL
LockFile *-- LockedDependency : persists
flowchart TD
L[LockFile.read] --> D[LockedDependency.to_dependency_ref]
D --> C{ADO coordinates}
C -->|all absent| B[Derive through DependencyReference]
C -->|all present| V[Validate against canonical owner]
C -->|partial or mismatched| F[Fail closed]
B --> U[DependencyReference.to_github_url]
V --> U
U --> O[apm outdated]
U --> S[GitSemverResolver]
S --> R[RefResolver]
R --> X[apm update]
Recommendation
Recommend shipping once required human approval lands. Exact head f430787c0aa5bb3888c5306c2bea7d413ee65e6f is green, mergeable, and protected by real lifecycle plus field-drop mutation evidence. None of the three follow-ups needs to land in this PR.
Full per-persona findings
Python Architect
- [recommended]
reference.pyis 2099/2100 lines. Track a follow-up extraction of the ADO-coordinate subsystem; no change is needed for this PR. - [nit] The all-fields round-trip fixture intentionally pairs an ADO host with
host_type="gitlab"; a comment could make that independence test clearer.
CLI Logging Expert
- [nit] The ADO-only repair hint could show the concrete production URL shape instead of
<source>. - [nit] A malformed future caller could render
got 'None'; the current canonical caller cannot produce that state.
DevX UX Expert
No findings.
Supply Chain Security Expert
- [nit] The canonical supplied URL guard does not separately reject a non-default port; current
DependencyReferencevalidation owns that input. - [nit] A generic-host
RefResolverreceivingremote_urllacks a direct defense-in-depth unit test; production only supplies it for ADO.
OSS Growth Hacker
No findings.
Auth Expert
No findings. AuthResolver remains the credential owner; no token reaches a URL; PAT and bearer fallback use the same canonical URL.
Doc Writer
- [nit] In the touched lockfile reference table, the
claude_skillversion exception could follow the git-semver case for slightly smoother type progression. The schema claims themselves are verified.
Test Coverage Expert
- [recommended] Add one Scenario Evidence row for the explicit ADO semver and
outdatedURL handoff. The tests already pass at unit and E2E tiers. - [nit] No action: the YAML reload to URL assertion is already explicit in the unit contract and repeated in the real lifecycle test.
Performance Expert
- [recommended] Document the
remote_urlcache-key invariant for future ADO callers. - [nit] Canonical coordinate revalidation is linear in a short repository path and negligible next to Git subprocess work.
- [nit] Non-ADO lock reconstruction performs one constant-time host classification per dependency.
This panel is advisory. It does not block merge. Re-apply the
panel-review label after addressing feedback to re-run.
APM Review Panel:
|
| Persona | B | R | N | Takeaway |
|---|---|---|---|---|
| Python Architect | 0 | 1 | 2 | Canonical owners are preserved; diagnostic category consolidation is a separate semantic choice. |
| CLI Logging Expert | 0 | 2 | 1 | Both consensus wording recommendations are folded. |
| DevX UX Expert | 0 | 1 | 2 | Default agent-conversion wording is now package-manager friendly. |
| Supply Chain Security Expert | 0 | 0 | 3 | No exploit or auth bypass; three defense-in-depth nits remain. |
| OSS Growth Hacker | 0 | 0 | 1 | CHANGELOG style is folded; enterprise reliability is the release angle. |
| Auth Expert | 0 | 0 | 2 | SSH/HTTPS credential boundaries remain correct. |
| Doc Writer | 0 | 0 | 0 | Docs are accurate, in-place resolved, and have no README drift. |
| Test Coverage Expert | 0 | 0 | 0 | Every critical user promise has evidence at or above its floor. |
| Performance Expert | 0 | 0 | 0 | Schema failure; grounded pre-return analysis found no current regression. |
B = high-severity findings, R = recommended, N = nits.
Counts are advisory signal. The maintainer ships.
Follow-ups
- [Coordinator] Merge fix(cli): warn when Codex agent tool scope is dropped #2186 and fix(deps): honor SSH for semver ref resolution #2229 before reconsidering fix(ado): preserve lock coordinates for outdated and update #2226, then resolve only residual tree differences.
- [Python Architect] Weigh diagnostic-category consolidation separately; it changes semantics rather than wording.
- [Performance Expert] Re-run the persona after its schema issue is fixed and evaluate
annotate_update_plan_refson the predecessor branch. - [Supply Chain Security] Consider eliminating the ADO SSH coordinate split parser when the transport owner can receive validated coordinates directly.
Recommendation
Freeze as prepared-waiting-on-#2186/#2229. Do not treat the green hosted state as independent landing permission. After both predecessors merge, fetch exact main, preserve all 16 critical rows and AC1-AC14, resolve only residual differences, and rerun hosted CI plus the two panels.
Evidence and deferred findings
- ADO legacy all-absent coordinates reconstruct; any partial coordinate set fails closed.
- Dropping
ado_repoon the stacked head fails the round-trip regression test. - SSH and HTTPS resolver instances use distinct transport-aware cache identities.
- ADO PAT/bearer fallback remains disabled for SSH and preserves the canonical URL for HTTPS.
- Broad category, parallelization, and owner-extraction refactors are deferred to predecessor-specific work.
This panel is advisory. Re-apply the panel-review label after predecessor integration to re-run.
Release Layer 3 against exact predecessor 7d23159, which contains #2186 3d2828c. Preserve the 15-row predecessor suite and AC10-AC13 unchanged; add only the ADO critical row and AC14 plus HTTPS composition through canonical owners.\n\napm-spec-waiver: ADO Layer 3 preserves the immutable predecessor spec union and adds no normative semantics\n\nCo-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>\nCopilot-Session: 1117b9a1-68ed-4d08-8f91-04694a98ce5f
APM Spec Guardian:
|
APM Review Panel:
|
| Lens | Result |
|---|---|
| Architecture | Canonical owner union preserved; predecessor-relative diff contains no duplicate transport/cache/coordinate authority. |
| Auth and supply chain | SSH clears HTTP auth; ADO PAT/bearer retry remains HTTPS-only; hostile canonical URLs fail closed. |
| DevX and logging | ADO status/update behavior and actionable errors remain covered; predecessor diagnostics are unchanged. |
| Docs | Lockfile reference is the only non-predecessor docs delta; README has no drift. |
| Test coverage | Every ADO promise has unit plus real-binary lifecycle evidence; mutation receipt is current. |
| Performance | Resolver/cache topology matches immutable #2229; ADO adds no extra SSH round-trip. |
Recommendation
Freeze as predecessor-wait. Do not merge #2226 before #2229. Once #2229 lands, fetch exact main and resolve only residual tree differences; preserve the 15->16 proof and AC1-AC14, then rerun exact-head hosted CI.
This panel is advisory. Re-apply the panel-review label after predecessor landing if residual differences remain.
Prepare Layer 3 on main 147fa04 while retaining inherited SSH AC13, ADO AC14, and the 14+SSH+ADO taxonomy union at 16.\n\nCo-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>\nCopilot-Session: 1117b9a1-68ed-4d08-8f91-04694a98ce5f
Keep the post-#2186 PR diff scoped to the SSH semver fix. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 324c57ea-aa78-4f0e-a656-d12f966cedff
…-semver-transport
…ppiel-fix-ado-lock-consumer-contract
…-semver-transport
Addresses shepherd panel follow-ups by removing the ADO SSH owner_repo split, validating supplied ADO remote URLs against canonical dependency coordinates, and making corrupted ADO lock guidance actionable. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Tighten the SSH semver transport path by removing unrelated spec edits, polishing changelog attribution, typing the resolver cache boundary, scrubbing SSH askpass, and adding a regression trap for the ADO SSH bearer-retry guard. Addresses apm-review-panel follow-ups for PR #2229. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Shortens the ADO coordinate recovery message introduced by the shepherd fold so the CI file-length guard remains green without changing behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
APM Review Panel:
|
| Persona | B | R | N | Takeaway |
|---|---|---|---|---|
| Python Architect | 0 | 2 | 1 | Canonical ADO coordinate authority is now used across lock and ref resolution. |
| CLI Logging Expert | 0 | 0 | 2 | Error messages now include actionable ADO lock recovery guidance. |
| DevX UX Expert | 0 | 1 | 1 | ADO outdated/update user promise is restored; corrupt-lock guidance improved. |
| Supply Chain Security Expert | 0 | 0 | 2 | ADO remote URLs are host/path validated and credential-free. |
| OSS Growth Hacker | 0 | 0 | 1 | ADO enterprise support story is stronger; docs are adequate. |
| Auth Expert | 0 | 1 | 2 | ADO auth remains delegated to AuthResolver/git env; no token leakage. |
| Doc Writer | 0 | 0 | 1 | Lockfile spec and changelog are accurate and scoped. |
| Test Coverage Expert | 0 | 0 | 3 | Critical ADO lifecycle and URL-safety surfaces are covered. |
| Performance Expert | 0 | 1 | 3 | No measured perf regression; one future-proofing refactor can wait. |
B = blocking-severity findings, R = recommended, N = nits. Counts are signal strength, not gates. The maintainer ships.
Top follow-ups
- [Performance Expert] Thread
TransportPlanonce per resolution pass instead of rebuilding per dependency -- future-proofing only; no measurable regression today. - [Docs / Growth] Optional changelog voice polish to lead with the user-facing ADO
outdated/updateoutcome -- cosmetic only.
Folded in this run
- (panel) Routed ADO SSH coordinate extraction through
DependencyReference.canonical_ado_coordinatesand extended AC14 static/architecture guards -- resolved in32daed93418a6e348e19ce573a0fc673dce6bb73. - (panel) Validated supplied ADO HTTPS
remote_urlpaths against canonical dependency coordinates beforegit ls-remote-- resolved in32daed93418a6e348e19ce573a0fc673dce6bb73. - (panel) Documented that ADO HTTPS keeps credentials out of the URL and uses git
http.extraheaderauth -- resolved in32daed93418a6e348e19ce573a0fc673dce6bb73. - (panel) Made corrupted ADO lock/reference coordinate errors include concrete recovery guidance -- resolved in
32daed93418a6e348e19ce573a0fc673dce6bb73. - (panel) Kept the dependency reference module within the CI file-length guard after the shepherd fold -- resolved in
017fca75aa6e1a5776e1f7243606567f242777b9.
Copilot signals reviewed
No inline comments from copilot-pull-request-reviewer[bot] were present when fetched during this run.
Deferred (out-of-scope follow-ups)
- (panel) Thread
TransportPlanonce per resolution pass -- scope boundary: PR scope is ADO lock coordinate correctness; this is a future-proofing performance refactor with no measured regression. - (panel) Polish changelog wording -- scope boundary: current changelog entry is accurate; this is release-note voice polish outside the correctness fix.
Regression-trap evidence (mutation-break gate)
tests/unit/marketplace/test_ref_resolver.py::TestRefResolver::test_ado_remote_url_rejects_unsafe_variants-- deletedparsed_remote.path != expected_ado_path; test FAILED as expected; 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, auth-signal lint, and scripts/lint-architecture-boundaries.sh also passed.
CI
gh pr checks 2226 --repo microsoft/apm reports all checks passing on 017fca75aa6e1a5776e1f7243606567f242777b9 after 1 CI recovery iteration (initial file-length guard failure fixed by 017fca75).
Mergeability status
Captured from gh pr view 2226 --repo microsoft/apm --json number,headRefOid,mergeable,mergeStateStatus,statusCheckRollup immediately after the last push of this run.
| PR | head SHA | CEO stance | iters | folds | defers | Copilot rounds | CI | mergeable | mergeStateStatus | notes |
|---|---|---|---|---|---|---|---|---|---|---|
| #2226 | 017fca7 |
ship_now | 1 | 5 | 2 | 2 | green | MERGEABLE | BLOCKED | pending required review |
Convergence
1 outer iteration; 2 Copilot rounds. Final panel stance: ship_now.
Ready for maintainer review.
This panel is advisory. It does not block merge.
Route frozen-binary HTTP metadata probes through a closed loopback proxy and disable persistent cache so the one-transport Git rewrite contract cannot reach shared or external state. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 324c57ea-aa78-4f0e-a656-d12f966cedff
Keep the attributed #2229 entry as the single Unreleased record surfaced by the advisory panel. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 324c57ea-aa78-4f0e-a656-d12f966cedff
Keep the issue reference in prose and reserve the trailing parenthetical for PR #2229. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 324c57ea-aa78-4f0e-a656-d12f966cedff
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e86c834c-e4a2-4354-825f-2a51cfb99e0e
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e86c834c-e4a2-4354-825f-2a51cfb99e0e
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e86c834c-e4a2-4354-825f-2a51cfb99e0e
Integrate exact predecessor ce38770 on main 6a03f3f. Preserve predecessor SSH/spec/taxonomy/AC1-AC13 exactly and retain only bounded ADO code, docs, AC14, and row 16 above it.\n\napm-spec-waiver: ADO Layer 3 composes existing immutable predecessor requirements and adds no normative semantics\n\nCo-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>\nCopilot-Session: 1117b9a1-68ed-4d08-8f91-04694a98ce5f
Keep provider-specific Azure DevOps coordinates transient under DependencyReference and persist only generic host plus repository identity in the lockfile. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1117b9a1-68ed-4d08-8f91-04694a98ce5f
Keep DependencyReference under the source length guard while retaining canonical provider-coordinate derivation behind its model interface. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1117b9a1-68ed-4d08-8f91-04694a98ce5f
Fold the final review findings by attaching repository identity to ADO resolver errors and removing the duplicate inherited SSH changelog entry. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1117b9a1-68ed-4d08-8f91-04694a98ce5f
APM Review Panel:
|
| Persona | B | R | N | Takeaway |
|---|---|---|---|---|
| Python Architect | 0 | 1 | 2 | Clean single-owner extraction; mixin is bounded and AC14 supplies the dual guardrail. |
| CLI Logging Expert | 0 | 1 | 1 | ADO errors should name the dependency; folded at exact head. |
| DevX UX Expert | 0 | 0 | 2 | Provider-neutral lock semantics match package-manager expectations. |
| Supply Chain Security Expert | 0 | 0 | 1 | Generic identity, URL validation, credential handling, and commit integrity remain sound. |
| OSS Growth Hacker | 0 | 0 | 2 | The provider-neutral story is accurate and concise. |
| Auth Expert | 0 | 0 | 2 | Bearer/basic fallback and credential-free URL handling remain intact. |
| Doc Writer | 0 | 1 | 0 | Documentation is accurate; duplicate inherited changelog entry was folded. |
| Test Coverage Expert | 0 | 1 | 1 | Unit, architecture, and real-binary tiers cover all stated promises. |
| Performance Expert | 0 | 0 | 0 | New work is O(1) per dependency and network-neutral. |
B = blocking-severity findings, R = recommended, N = nits.
Counts are signal strength, not gates. The maintainer ships.
Top 4 follow-ups
- [OSS Growth Hacker] Lead release communication with the restored user outcome -- this is a useful multi-forge proof point.
- [Python Architect] Split
maybe_resolve_git_semveronly if it gains callers or complexity -- avoid speculative refactoring now. - [Test Coverage Expert] Consider a committed mutation fixture -- the current regression assertion and manual mutation already catch the exact omission.
- [Supply Chain Security Expert] Quote the ADO organization segment for symmetry if its accepted character set ever widens -- current validation makes it URL-safe.
Architecture
classDiagram
direction LR
class ProviderCoordinateMixin {
+validate_provider_coordinates()
+is_transient_provider_field(field_name)
+with_derived_provider_coordinates()
}
class DependencyReference {
+repo_url
+host
+canonical_ado_coordinates(host, repo_url)
+to_github_url()
}
class LockedDependency {
+repo_url
+host
+to_dependency_ref()
+from_dict(data)
}
DependencyReference --|> ProviderCoordinateMixin : inherits
LockedDependency ..> DependencyReference : reconstructs
flowchart TD
Y["apm.lock.yaml: host + repo_url"] --> L[LockedDependency]
L --> D[DependencyReference]
D --> C["derive transient ADO coordinates"]
C --> V["validate canonical identity"]
V --> U["build credential-free /_git/ URL"]
U --> O["outdated and update"]
Recommendation
All panel lenses found the provider-neutral redesign sound, and the actionable review items were folded at exact head 8f820cc496. Ship after exact-head CI completes; track only the bounded follow-ups that earn separate scope.
Full per-persona findings
Python Architect
- [recommended] Split
maybe_resolve_git_semverif it grows or gains another caller. The current function is tested and has one caller, so no speculative split is needed in this PR. - [nit] The mixin uses
NotImplementedErrorinstead of an ABC/Protocol. One concrete inheritor makes this acceptable today. - [nit] Provider coordinates are validated at lock creation and URL rendering. These protect different transitions and remain O(1).
CLI Logging Expert
- [recommended, folded] Newly added ADO
GitLsRemoteErrorpaths used an empty package identity. Exact head now passesowner_repo. - [nit, folded] Provider-coordinate errors should name
repo_url. Exact head now does.
DevX UX Expert
- [nit, folded] Name the dependency in provider-coordinate recovery guidance.
- [nit] Existing lockfile-spec link repairs are unrelated housekeeping inherited by the cumulative branch.
Supply Chain Security Expert
- [nit] The organization path segment is not quoted while project and repository are. Current validation restricts it to URL-safe characters.
OSS Growth Hacker
- [nit] Release communication can lead with the user outcome rather than transport implementation.
- [nit] A short portability hook may help readers connect the lock design to multi-forge use.
Auth Expert
- [nit] A non-ADO caller supplying
remote_urlreceives an ADO-specific rejection message; it still fails closed. - [nit] Quote the organization path segment for symmetry if validation ever widens.
Doc Writer
- [recommended, folded] Remove the duplicate inherited fix(deps): honor SSH for semver ref resolution #2229 changelog entry.
Test Coverage Expert
- [recommended] The real-binary lifecycle satisfies the integration floor for lock determinism; no coverage gap remains.
- [nit] A committed mutation fixture could make the manual mutation receipt self-running, but the exact regression assertion already fails when derivation is removed.
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.
Address panel follow-ups for PR #2226 by tightening the provider-coordinate mixin contract, making the mismatch recovery text command-shaped, documenting the RefResolver remote_url cache contract, and adding a regression trap for mismatched transient ADO coordinates. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
APM Review Panel:
|
| Persona | B | R | N | Takeaway |
|---|---|---|---|---|
| Python Architect | 0 | 0 | 2 | Single-owner ADO coordinate derivation is clean and guarded. |
| CLI Logging Expert | 0 | 0 | 1 | Recovery text is now concrete and actionable. |
| DevX UX Expert | 0 | 0 | 1 | No CLI surface change; ADO users get expected behavior back. |
| Supply Chain Security Expert | 0 | 0 | 1 | URL validation remains fail-closed and credential-safe. |
| OSS Growth Hacker | 0 | 0 | 1 | ADO parity strengthens the vendor-neutral lockfile story. |
| Auth Expert | 0 | 0 | 1 | ADO credential injection remains on the existing safe path. |
| Doc Writer | 0 | 0 | 0 | Docs, changelog, and PR body now align with the current head. |
| Test Coverage Expert | 0 | 0 | 0 | Lockfile, resolver, architecture, and mismatch paths are covered. |
| Performance Expert | 0 | 0 | 1 | No material hot-path regression; added checks are O(1) per dep. |
Folded in this run
- (panel) Add unit regression test for
validate_provider_coordinates()mismatched ADO fields -- resolved in3697a6e42a43d850a266cd71fd0c1cea1f88cdcd. - (panel) Promote
ProviderCoordinateMixin.canonical_ado_coordinatestoabc.abstractmethod-- resolved in3697a6e42a43d850a266cd71fd0c1cea1f88cdcd. - (panel) Make provider-coordinate mismatch recovery text include an
apm installcommand -- resolved in3697a6e42a43d850a266cd71fd0c1cea1f88cdcd. - (panel) Sharpen the CHANGELOG ADO entry around
apm outdated/apm updateuser benefit -- resolved in3697a6e42a43d850a266cd71fd0c1cea1f88cdcd. - (panel) Document the
RefResolverremote_urlcache-identity consistency contract -- resolved in3697a6e42a43d850a266cd71fd0c1cea1f88cdcd. - (panel) Refresh the PR body exact-head notice and hosted-run evidence after shepherd folds -- resolved in
3697a6e42a43d850a266cd71fd0c1cea1f88cdcd.
Copilot signals reviewed
review-4705343839-- NOT-LEGIT: overview-only review was stale after folds; no inline Copilot comments were returned by the GitHub API.
Regression-trap evidence (mutation-break gate)
tests/unit/deps/test_lockfile_consumer_contract.py::test_validate_provider_coordinates_rejects_mismatched_ado_fields-- deletedprovider_coordinates.pymismatch guard; test FAILED as expected; guard restored.
Lint contract
uv run --extra dev ruff check src/ tests/ and uv run --extra dev ruff format --check src/ tests/ both silent. The pylint R0801, auth-signal, remaining grep-equivalent guards, and architecture boundary lint also passed.
CI
gh pr checks 2226 --repo microsoft/apm is green on head 3697a6e42a43d850a266cd71fd0c1cea1f88cdcd (CI 29479999500, CodeQL 29479999474, Merge Gate 29479999532, Spec 29479999465, Docs 29479999462, NOTICE 29479999478) after 0 CI fix iterations.
Mergeability status
Captured from gh pr view 2226 --json mergeable,mergeStateStatus,statusCheckRollup immediately after the last push of this run.
| PR | head SHA | CEO stance | iters | folds | defers | Copilot rounds | CI | mergeable | mergeStateStatus | notes |
|---|---|---|---|---|---|---|---|---|---|---|
| #2226 | 3697a6e |
ship_now | 1 | 6 | 0 | 2 | green | MERGEABLE | BLOCKED | pending required review |
Convergence
1 outer iteration; 2 Copilot rounds. Final panel stance: ship_now.
Ready for maintainer review.
fix(ado): reconstruct lock consumers from generic repository identity
TL;DR
Azure DevOps dependencies now survive lock reload,
apm outdated, boundedapm update, audit, and convergence without provider-specific lockfile fields.The lock persists the same generic
hostandrepo_urlidentity used forGitHub, GitLab, and other Git providers;
DependencyReferencederives transientADO transport coordinates when reconstructing the consumer.
Important
Exact head
3697a6e42a43d850a266cd71fd0c1cea1f88cdcdis frozen for human review. This PR must not be auto-merged.Problem (WHY)
apm outdated/apm updatereportunknownfor Azure DevOps deps —LockedDependency.to_dependency_ref()dropsado_*fields (distinct from #2150 / #2119) #2197: showed that lock reconstruction dropped transient ADOcoordinates, causing canonical URL construction to fail and
apm outdatedto report
unknown.apm updatecould not resolve an ADO semver range after lock reload, eventhough direct
apm view ... versionsandgit ls-remoteworked for the samerepository.
ado_organization,ado_project, andado_repowould repairthe symptom but duplicate information already present in
host + repo_url.and generic-host behavior and create two values that must remain consistent.
The implementation follows the existing-owner rule rather than adding another
parser: "Synthesize from existing project artifacts".
Approach (WHAT)
LockedDependencyandapm.lock.yamlprovider-neutral.DependencyReference, then derive transient ADOorganization/project/repository coordinates from canonical
host + repo_url./_git/transport URL.unknown fields for forward compatibility.
Implementation (HOW)
src/apm_cli/deps/lockfile.pyado_*dataclass, YAML, and reconstruction fields. Generic lock state is converted through theDependencyReferencemodel boundary.src/apm_cli/models/dependency/reference.pysrc/apm_cli/models/dependency/provider_coordinates.pyDependencyReference.src/apm_cli/install/helpers/ref_reuse.py,src/apm_cli/deps/git_semver_resolver.py,src/apm_cli/marketplace/ref_resolver.pytests/unit/deps/test_lockfile_consumer_contract.pytests/integration/test_ado_lock_consumer_contract.pyscripts/lint-architecture-boundaries.sh,tests/integration/test_architecture_authorities.pytests/quality/critical_suite.toml,tests/quality/test_test_taxonomy.pydocs/src/content/docs/reference/lockfile-spec.md,CHANGELOG.mdDiagrams
Legend: persisted state stays generic; only the in-memory reference derives the
ADO transport shape consumed by outdated and update.
flowchart LR subgraph Persist["Generic lock state"] I["host + repo_url"] Y["apm.lock.yaml"] I --> Y end subgraph Reconstruct["Canonical model boundary"] L[LockedDependency] D[DependencyReference] C["Transient ADO coordinates"] Y --> L L --> D D --> C end subgraph Consume["Real consumers"] U["Canonical /_git/ URL"] O["apm outdated"] P["apm update"] C --> U U --> O U --> P end classDef new stroke-dasharray: 5 5; class C,U new;Trade-offs
redundant provider fields and their mismatch states.
consume named coordinates, so this PR derives them in memory rather than
widening the change into a downloader rewrite.
on
main; preserving them as unknown metadata would violate the final schema.Other unknown fields continue to round-trip.
canonical parser must remain below the 2,100-line CI guardrail.
Benefits
ado_*fields.host + repo_urlreconstructs all 3 transient ADO coordinates.v1.0.0reportsv1.1.0,outdated, andgit tags.^1.0.0update changes the commit, ref, and deployed bytes once,then converges byte-for-byte.
Validation
APM_BINARY_PATH=dist/apm-darwin-arm64/apm APM_E2E_TESTS=1 APM_TIERED_RESOLVER=0 pytest <Codex+SSH+ADO>:pytest tests/unit tests/test_console.py tests/red_team -n 2 --dist worksteal:pytest tests/quality tests/spec_conformance tests/integration/test_architecture_authorities.py:apm audit --ci --no-policy --format json:Exact-head and mutation evidence
Hosted exact-head runs are green with 0 red and 0 pending:
CI
29479999500, CodeQL29479999474, Merge Gate29479999532,Spec
29479999465, Docs29479999462, NOTICE29479999478.Scenario Evidence
unknown.tests/integration/test_ado_lock_consumer_contract.py::test_ado_lock_replay_drives_outdated_update_audit_and_convergencetests/integration/test_ado_lock_consumer_contract.py::test_ado_lock_replay_drives_outdated_update_audit_and_convergencetests/unit/deps/test_lockfile_consumer_contract.py::test_ado_coordinates_are_derived_after_generic_lock_round_triptests/unit/deps/test_lockfile_consumer_contract.py::test_retired_ado_lock_fields_are_dropped_without_losing_unknown_fieldstests/integration/test_architecture_authorities.py::test_ado_lock_coordinates_have_single_ownerHow to test
ado_*keys in its generated lock.tests/unit/deps/test_lockfile_consumer_contract.py; expect 8 passes.bash scripts/lint-architecture-boundaries.sh; expect AC1-AC14 clean.apm audit --ci --no-policy --format json; expect 9/9 checks.Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com