perf(resolve): honour locked commits without re-resolving refs over the network#1973
Conversation
…he network Two coupled fixes so a lockfile actually short-circuits ref resolution: 1. Seed the TieredRefResolver's L0 per-run cache from the lockfile (resolved_ref -> resolved_commit) at the start of the resolve phase, before any download runs. On a machine that has a committed lockfile but a cold cache, the first resolve() for each dep previously hit the network to turn the manifest ref (tag/branch) into a SHA the lockfile already held. Verified: a 3-dep locked install made 3 real commits-API calls before, 0 after; the seeding extends to branch pins (e.g. #main) that the semver replay path does not cover. Skipped under --update/--refresh (those intentionally re-resolve). Safe: the seeded SHA is the lockfile's trust anchor, and drift detection still runs against the manifest ref so changed pins re-resolve. 2. Count an already-concrete SHA ref as a distinct `sha_passthrough` stat instead of `commits_api`. The commits-API tier short-circuits on a SHA-regex match with no HTTP call but still incremented the commits-API counter, so a warm install making zero network calls reported e.g. `commits_api=40` -- misleading anyone profiling install cost. Adds TieredRefResolver.seed(), the sha_passthrough stat, and the seeding step. The step lives in install/helpers/ref_seed.py so phases/resolve.py stays within its per-module LOC budget (test_architecture_invariants). Unit tests cover the resolver methods, passthrough counting, and the seeding wiring (guards, entry filtering, case-normalization).
|
@microsoft-github-policy-service agree |
There was a problem hiding this comment.
Pull request overview
This PR optimizes install-time git ref resolution by seeding the tiered ref resolver’s per-run (L0) cache from apm.lock so branch-pinned/tagless locked dependencies can reuse resolved_commit without re-resolving refs over the network. It also improves resolver diagnostics by tracking already-concrete SHA refs as sha_passthrough rather than inflating commits_api stats.
Changes:
- Seed
TieredRefResolverL0 cache from the existing lockfile early in the resolve phase (skipped under--update/--refresh). - Add
sha_passthroughstats path for SHA refs to keep tier counters aligned with real network I/O. - Add unit tests covering seeding behavior and SHA passthrough stats.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/install/phases/test_resolve_seed_from_lockfile.py | Adds resolve-phase unit tests for lockfile-to-resolver seeding behavior and guardrails. |
| tests/unit/deps/test_tiered_ref_resolver.py | Adds unit tests for TieredRefResolver.seed() and sha_passthrough stats behavior. |
| src/apm_cli/install/phases/resolve.py | Wires the lockfile seeding step into the resolve phase execution flow. |
| src/apm_cli/install/helpers/ref_seed.py | Introduces helper that extracts lockfile entries and seeds the resolver cache (with update/refresh guards). |
| src/apm_cli/deps/tiered_ref_resolver.py | Implements seed() and sha_passthrough stats handling in the tiered resolver. |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
- Clarify seed() docstring: `ref` is the resolved_ref (branch OR tag name), the exact string resolve() will look up -- not specifically a tag. - Make the test-double guard actually validate a 40-char hex SHA (was length-only) so it truly mirrors TieredRefResolver.seed, and add a case proving a same-length non-hex commit is rejected.
|
Note on the commit trail: the middle commit ("Potential fix for pull request finding") is a Copilot Autofix suggestion that landed automatically on the review findings. The final commit ( |
- Add CHANGELOG [Unreleased] entry crediting @srobroek for the lockfile-seeded L0 cache optimization (addresses CEO follow-up: CHANGELOG entry required per fold-vs-defer rubric). - Add cache-key invariant comment in seed_ref_resolver_from_lockfile documenting that locked.repo_url must match dep_ref.repo_url at resolve() call sites; future URL canonicalization must be symmetric (addresses panel recommendation: document invariant assumption). - Fix verbose log status symbol [*] -> [i] for the seeding message; [i] is the correct INFO/setup symbol per STATUS_SYMBOLS convention (addresses panel nit: symbol consistency). - Fix seed() docstring: 'lockfile-verified' -> 'lockfile-recorded' to avoid overstating the trust level of the lockfile-held SHA (addresses panel nit: docstring accuracy). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Merge origin/main into perf/lockfile-seed-resolver-cache to keep the branch up to date. Preserved the [Unreleased] Performance entry for this PR alongside the Fixed entries from main. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
APM Review Panel:
|
| Persona | B | R | N | Takeaway |
|---|---|---|---|---|
| Python Architect | 0 | 0 | 2 | Clean perf optimization: lockfile-seeded L0 cache + SHA passthrough short-circuit. Correct sequencing, appropriate extraction, thread-safe. |
| CLI Logging Expert | 0 | 0 | 2 | Verbose-detail level correct; [*] symbol changed to [i] (folded); sha_passthrough already rendered by tier_summary. |
| DevX UX Expert | 0 | 0 | 2 | Solid perf UX: no default-output change, verbose messages informative, stats more honest. |
| Supply Chain Security Expert | 0 | 0 | 2 | Lockfile seeding is safe -- caches same trust anchor download phase already uses; content-hash integrity unaffected. |
| OSS Growth Hacker | 0 | 1 | 1 | High-signal community perf PR targeting CI/CD locked installs; quotable "2->0 API calls" story deserves a release beat. |
| Auth Expert | -- | -- | -- | INACTIVE: no auth surface touched (no auth.py, token_manager.py, azure_cli.py, github_downloader.py, or marketplace client.py changed). |
| Doc Writer | -- | -- | -- | INACTIVE: no user-facing behavior change; no doc update required. |
| Test Coverage Expert | 0 | 1 | 1 | Unit coverage thorough (10 tests); recommend one integration-level test asserting seeding avoids network calls end-to-end. |
| Performance Expert | 0 | 2 | 2 | Correct high-leverage optimization: eliminates 1 real HTTP RTT per unique (repo, branch) locked dep; sha_passthrough saves misleading stat inflation. |
B = blocking-severity findings, R = recommended, N = nits.
Counts are signal strength, not gates. The maintainer ships.
Top 4 follow-ups
- [performance-expert] Document cache-key invariant in ref_seed.py -- folded: comment added documenting that
locked.repo_urlmust matchdep_ref.repo_urlat allresolve()call sites; future URL canonicalisation must be symmetric. - [test-coverage-expert] Add integration test asserting lockfile-seeded resolve avoids network tier end-to-end -- deferred: would require a new integration test fixture; the existing unit test
test_seed_populates_l0_and_avoids_network_tierprovides mock-level proof; a follow-up integration test is recommended but non-gating. - [cli-logging-expert] Change [*] status symbol to [i] for setup/info messages -- folded: symbol updated in this run.
- [oss-growth-hacker] Amplify trilogy in release notes with contributor attribution and before/after metric -- CHANGELOG entry added in this run crediting @srobroek with "2 calls -> 0" metric.
Folded in this run
- cache-key invariant comment added to
src/apm_cli/install/helpers/ref_seed.py(commit 8f3bf53) [*]symbol changed to[i]in verbose seeding message (commit 8f3bf53)- docstring corrected: "lockfile-verified" -> "lockfile-recorded" in
src/apm_cli/deps/tiered_ref_resolver.py(commit 8f3bf53) - CHANGELOG
[Unreleased]Performance entry added (commit 8f3bf53) - Merged origin/main to resolve CHANGELOG conflict (commit 1194141)
Copilot signals reviewed
- ID 4606125113 (review body, 2 inline comments generated):
Lint evidence
ruff check src/ tests/: all checks passed (exit 0)
ruff format --check src/ tests/: 1388 files already formatted (exit 0)
CI evidence
CI check status: action_required for external fork workflows (CI, Merge Gate, CodeQL, Spec conformance). These require maintainer approval to run. The most recent PR event (2026-07-04) triggered Docs Sync Advisory and PR Review Panel workflows -- both completed SKIPPED. license/cla: SUCCESS. The main test suite (CI workflow) has NOT run; awaiting maintainer approval of the workflow run for this external fork.
Mergeability
| #PR | SHA | CEO stance | Iterations | Folds | Deferrals | Copilot rounds | CI | Mergeable | MergeState | Notes |
|---|---|---|---|---|---|---|---|---|---|---|
| #1973 | 1194141 | ship_with_followups | 1 | 4 | 1 | 1 | blocked | MERGEABLE | BLOCKED | awaiting CI approval + maintainer review; trilogy order: 1973->1974->1975 |
Full per-persona findings
Python Architect
- nit: Stats dict mutations are not thread-safe (pre-existing pattern; diagnostic-only counters; not introduced by this PR)
- nit: getattr duck-typing is pragmatic at one call site; if a second consumer of seed() appears, consider a Seedable protocol
CLI Logging Expert
- nit: [*] symbol changed to [i] for seeding message (FOLDED)
- nit: sha_passthrough stat rendered automatically by tier_summary (no extra output needed)
DevX UX Expert
- nit: sha_passthrough could be renamed lock_hit for user-friendliness in verbose output (consistent with existing naming, not blocking)
- nit: [*] vs [i] prefix (FOLDED)
Supply Chain Security Expert
- nit: Docstring corrected: "lockfile-verified" -> "lockfile-recorded" (FOLDED)
- nit: repo_url not normalized in seed() -- a key mismatch degrades gracefully to the pre-PR resolve path (not a security issue)
OSS Growth Hacker
- recommended: Amplify trilogy in release notes with contributor attribution (CHANGELOG entry added, FOLDED)
- nit: Before/after timing snippet in CHANGELOG if wall-clock data available (non-blocking)
Test Coverage Expert
- recommended: Add integration test asserting lockfile-seeded resolve avoids network tier end-to-end (DEFERRED -- scope boundary: existing unit tests prove mechanism; e2e integration test is a follow-up)
- nit: Parametrize duplicate-seed/overwrite-key scenario in unit tests
Performance Expert
- recommended: Document cache-key invariant (FOLDED -- comment added)
- recommended: Add integration-level stat assertion to detect future bypass regression (DEFERRED -- same as test-coverage-expert integration test recommendation)
- nit: sha_passthrough saves ~5-10us per dep (quantified, not blocking)
- nit: Seed iterates local deps that always no-op (minimal overhead, not worth fixing)
… end-to-end Add an integration test that drives the seed optimization through the REAL production wiring -- seed_ref_resolver_from_lockfile, a real LockFile, the real build_tiered_ref_resolver tier stack, and the real GitHubPackageDownloader.resolve_git_reference facade -- rather than hand-built tier mocks. Only the innermost legacy GitReferenceResolver (downloader._refs) is faked, so no live network runs. It asserts commits_api == 0 on a warm, seeded resolve of a branch pin and returns the lockfile's own commit; a control test proves the same facade WOULD hit the commits-API tier without the seed (regression trap), and an --update test proves drift re-resolution is preserved. Co-authored-by: srobroek <srobroek@users.noreply.github.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Codify the network-free lockfile-replay guarantee the tiered ref
resolver relies on as OpenAPM req-rs-015 (consumer MUST, Section 7.5):
a non-update install MUST replay a lockfile entry that records a
resolved_commit by reusing that commit without any network
ref-resolution (no commits-API, ls-remote, or clone), absent drift
against the manifest ref or an explicit apm update/--refresh.
This closes the Mode-B silent-extension gate on the lockfile-seeded
resolver cache: the net-new resolver behaviour under critical paths
now lands with a spec anchor, manifest row, Appendix C row, and a
@pytest.mark.req("req-rs-015") conformance test. Statement count
95 -> 96 (91 MUST, 5 SHOULD); CONFORMANCE.{json,md} regenerated.
Co-authored-by: Sjors Robroek <s.robroek@vxsan.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Apply the six fold_now findings from the apm-spec-guardian editorial panel to the req-rs-015 amendment (network-free lockfile replay): - F1 scope the replay trigger to entries recording a resolved_commit WITHOUT a resolved_tag (git-literal + untagged-branch per req-rs-003), so semver-entry freshness checks (req-rs-004) are not wrongly forbidden. - F2 define "drift" locally (manifest ref not character-equal to lockfile resolved_ref, or entry removed); semver-range drift stays under req-rs-004. - F3 correct the anchor cross-ref: resolved_commit is the resolution anchor (req-lk-003), not a trust anchor (req-lk-006 was frozen-mode); content integrity remains under tree_sha256 (req-lk-015) and resolved_hash (req-lk-013). - F4 scope "no clone" to ref discovery (illustrative, not exhaustive) and clarify object fetch to materialise already-resolved content is not constrained. - F5 move the non-normative rationale into a NOTE block. - F6 lowercase "provided" (not an RFC 2119 keyword). Prose-only: no new anchors/reqs; orphan_check 96 aligned, conformance suite 129 passed, CONFORMANCE artifacts unchanged. Co-authored-by: Sjors Robroek <s.robroek@vxsan.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
APM Spec Guardian:
|
| Panel | Verdict | Shocked | New B | New R | New N |
|---|---|---|---|---|---|
| Swagger / OpenAPI Editor | ship_with_followups | 8/10 | 0 | 1 | 2 |
| OCI Distribution Editor | ship_with_followups | 8/10 | 0 | 2 | 1 |
| Pkgmgr Registry-Contract Editor | ship_with_followups | 7/10 | 0 | 3 | 1 |
| W3C TAG Architect | ship_with_followups | 8/10 | 0 | 2 | 1 |
B = new blocking findings, R = new recommended, N = new nits.
Counts are signal strength, not gates. The maintainer ships.
Convergent themes (flagged by 2+ panels)
- T1 -- drift predicate underspecified (supporting: sw-rec-r1-1, pkg-rec-r1-2)
- T2 -- wrong anchor cross-reference (req-lk-006 vs recording anchor) (supporting: sw-nit-r1-2, oci-rec-r1-1, pkg-nit-r1-1)
- T3 -- normative text mixed with rationale (supporting: oci-nit-r1-1, tag-rec-r1-3)
- T4 -- "no clone" over-broad vs content fetch (supporting: oci-rec-r1-2, tag-rec-r1-1)
Fold now (6 item(s)) -- APPLIED in 0c0efc075
- [F1 / over-scope] Section 7.5 req-rs-015 -- scope the replay trigger to entries recording a
resolved_commitWITHOUT aresolved_tag(git-literal and untagged-branch entries per req-rs-003), so semver-entry freshness checks under req-rs-004 are not wrongly forbidden.
Success criterion:req-rs-015 names the resolved_commit-without-resolved_tag scope - [F2 / T1] Section 7.5 req-rs-015 -- replace bare "(drift)" with a local definition: manifest
refnot character-equal to lockfileresolved_ref, or entry removed; semver-range drift governed by req-rs-004.
Success criterion:drift is defined in-place, not left to the reader - [F3 / T2] Section 7.5 req-rs-015 -- correct cross-ref:
resolved_commitis the resolution anchor (req-lk-003), not a trust anchor (req-lk-006 is frozen-mode); content integrity remains under tree_sha256 (req-lk-015) and resolved_hash (req-lk-013).
Success criterion:no reference to req-lk-006 as the recording anchor - [F4 / T4] Section 7.5 req-rs-015 -- scope "no clone" to ref discovery (illustrative, not exhaustive) and state object fetch to materialise already-resolved content is not constrained.
Success criterion:content fetch at the resolved commit is explicitly out of scope - [F5 / T3] Section 7.5 req-rs-015 -- move the trailing rationale sentence into a NOTE (non-normative) block, preserving "network-free at the resolution step".
Success criterion:normative body contains only the MUST + its conditions - [F6 / hygiene] Section 7.5 req-rs-015 -- lowercase "provided" (not an RFC 2119 keyword).
Success criterion:no non-keyword all-caps in req-rs-015
Defer to v0.1.1
- [F7 / conformance-fixture] tests/fixtures/spec-conformance -- add a behavioral fixture exercising the network-free replay path end-to-end. Impl-level behavioral tests already exist in this PR; the text-pinning marker follows the req-rs-cluster convention, so this is a convention upgrade, not a gap.
Rejected findings
- pkg-rec-r1-3 -- split req-rs-015 into two MUSTs (replay + re-resolve): the pkgmgr-registry lens is deprioritized for this amendment and the split would force a renumber of the req-rs cluster for no conformance gain; the single MUST with explicit conditions is testable as-is.
- tag-nit-r1-1 -- abstract the CLI verbs ("or equivalent"): req-rs-011/012 already carry the CLI-abstraction caveat; the parenthetical here aids implementers without over-constraining.
Linter handoff: orphan_check 96 aligned; conformance suite 129 passed; Mode-B short-circuits green (spec-concurrent). CONFORMANCE.{json,md} unchanged (prose-only folds). The three conformance-pinned phrases ("non-update install", "WITHOUT issuing a network ref-resolution", "network-free at the resolution step") survive the rewrite contiguous on single lines.
This panel is advisory. It does not block merge. Re-apply the spec-review label after addressing feedback to re-run.
danielmeppiel
left a comment
There was a problem hiding this comment.
Approving as maintainer: triaged via apm-issue-autopilot, driven to green CI, advisory review panels clean. Batch-merge authorized.
Resolve CHANGELOG.md conflict: keep both the Performance entry (microsoft#1973) and the Changed entry (microsoft#1974). resolve.py auto-merged cleanly against the microsoft#1975 ls-remote dedup that landed first; spec-conformance 129 pass, resolver suite 2092 pass. Co-authored-by: Sjors Robroek <s.robroek@vxsan.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Description
With a complete
apm.lock, direct semver deps already replay the locked tag without a network call. But deps whose lockfile entry has aresolved_commitwithout aresolved_tag— notably branch pins (e.g.#main) and transitive deps tracking a branch — fall through the semver-replay path and re-resolve their ref over the network on every install, even though the lockfile already holds the exact commit.This PR:
Seeds the
TieredRefResolver's L0 per-run cache from the lockfile (resolved_ref -> resolved_commit) at the start of the resolve phase, before any download runs. Any laterresolve_git_reference()for a locked ref then hits L0 and never fires the commits-API tier or a clone. Skipped under--update/--refresh(those intentionally re-resolve). Safe: the seeded SHA is the lockfile's own trust anchor, and drift detection still runs against the manifest ref so a changed pin is re-resolved.Counts an already-concrete SHA ref as a distinct
sha_passthroughstat instead ofcommits_api. The commits-API tier short-circuits on a SHA-regex match and returns with no HTTP call, but still incremented the commits-API counter — so a warm install that made zero network calls reported e.g.commits_api=40, misleading anyone profiling install cost. Verbose tier stats now reflect real round-trips.Measured: on a machine with a committed lockfile but cold cache, a 1-direct + 1-transitive (branch-pinned) install made 2 real commits-API calls before, 0 after (call-level instrumentation).
The seeding step lives in
install/helpers/ref_seed.pysophases/resolve.pystays within its per-module LOC budget (test_architecture_invariants).Fixes # (issue)
Type of change
Testing
Added unit tests for
TieredRefResolver.seed(), thesha_passthroughcounting, and the resolve-phase seeding wiring (update/refresh guards, entry filtering, case-normalization). Fulltests/unit/install+tests/unit/depssuites pass (2882 passed).Spec conformance (OpenAPM v0.1)