Skip to content
This repository was archived by the owner on May 14, 2026. It is now read-only.

feat(git-fetcher): warm prefetch for git-hosted snapshots (#436 follow-up)#454

Merged
zkochan merged 3 commits into
mainfrom
feat/436-warm-git-prefetch
May 13, 2026
Merged

feat(git-fetcher): warm prefetch for git-hosted snapshots (#436 follow-up)#454
zkochan merged 3 commits into
mainfrom
feat/436-warm-git-prefetch

Conversation

@zkochan

@zkochan zkochan commented May 13, 2026

Copy link
Copy Markdown
Member

Summary

Wires git-hosted installs into pacquet's existing index.db warm-cache path. Before this PR every git install cold-pathed, even when the snapshot was already in the store from a previous install. Now the second install of an unchanged lockfile reuses the previous install's clone + checkout + prepare + packlist work and skips the fetcher entirely — same as registry-tarball installs do today.

Three pieces

  1. Both fetchers now write PackageFilesIndex rows. cas_io::import_into_cas returns the full HashMap<String, CafsFileInfo> payload alongside the existing cas_paths map; the fetchers wrap that into a PackageFilesIndex and queue it on the shared StoreIndexWriter at the cache key the dispatcher passed in.
    • algo: "sha512", manifest: None, requires_build reflects should_be_built.
    • add_files_from_dir's file_mode_from is duplicated locally so the cache row encodes the same POSIX-mode shape pnpm's tarball entries use.
  2. create_virtual_store::snapshot_cache_key routes git arms through the warm batch. For Tarball { gitHosted: true } and Git resolutions it now returns Some(gitHostedStoreIndexKey(pkg_id, built=true)) (matches upstream's pickStoreIndexKey shape) instead of the previous None cold-batch shortcut. built = true matches the dispatcher's ignore_scripts: false default; both sites will flip together when an ignore-scripts config knob lands.
  3. Dispatcher threads writer + key into both fetchers. install_package_by_snapshot.rs builds the same gitHostedStoreIndexKey(package_id, built=true) the warm path reads and hands it to GitFetcher.files_index_file / GitHostedTarballFetcher.files_index_file, plus the shared store_index_writer clone.

The warm prefetch's existing file-verification (check_pkg_files_integrity) runs unchanged — if any CAS file referenced by the row has been pruned, the snapshot falls through to cold and the fetcher re-runs.

Out of scope

Test plan

  • cargo nextest run -p pacquet-git-fetcher — 42 tests, including a new writes_index_row_when_writer_provided round-trip that spawns a real StoreIndexWriter, runs the tarball fetcher with it, awaits the writer's join handle, and re-opens the store to confirm the row landed at pkg_id\tbuilt with the right algo, requires_build, and a non-empty files map.
  • cargo nextest run -p pacquet-package-manager — 245 tests still green after snapshot_cache_key is rewritten.
  • just ready — 765 tests, 765 pass.
  • RUSTDOCFLAGS=-D warnings cargo doc --no-deps --workspace --document-private-items — clean.
  • taplo format --check
  • just dylint

Written by an agent (Claude Code, claude-opus-4-7).

Summary by CodeRabbit

  • Performance Improvements
    • Faster installs for git-hosted packages via caching of per-package file indexes and reuse of prior checkout/packlist work (warm prefetch), reducing repeated prepare/pack steps.
  • New Features
    • Optional recording of package file indexes into the store index to enable future prefetch/skip-materialize behavior.
    • Per-file metadata (digest, size, POSIX mode/executability) included in recorded indexes.
  • Tests
    • Updated tests and a new integration test validating index-writing and per-file metadata round-trip.

Review Change Stack

…w-up)

Wires git-hosted installs into pacquet's existing `index.db` warm-cache
path so the second install of an unchanged lockfile reuses the previous
install's clone+checkout+prepare+packlist work instead of redoing it
from scratch.

Three pieces:

- **Both fetchers now write `PackageFilesIndex` rows.** `cas_io::
  import_into_cas` returns the full
  `HashMap<String, CafsFileInfo>` payload alongside the existing
  `cas_paths` map; the fetchers wrap that into a `PackageFilesIndex`
  and queue it on the shared `StoreIndexWriter` at the cache key the
  dispatcher passed in. `algo: "sha512"`, `manifest: None`,
  `requires_build` reflects `should_be_built`. `add_files_from_dir`'s
  `file_mode_from` is duplicated locally so the cache row encodes
  the same POSIX-mode shape pnpm's tarball entries use.
- **`create_virtual_store::snapshot_cache_key` routes git arms
  through the warm batch.** For `Tarball { gitHosted: true }` and
  `Git` resolutions it now returns
  `Some(gitHostedStoreIndexKey(pkg_id, built=true))` (matches
  upstream's `pickStoreIndexKey` shape) instead of the previous
  `None` cold-batch shortcut. `built = true` matches the dispatcher's
  `ignore_scripts: false` default; both sites will flip together
  when an `ignore-scripts` config knob lands.
- **Dispatcher threads writer + key into both fetchers.**
  `install_package_by_snapshot.rs` builds the same
  `gitHostedStoreIndexKey(package_id, built=true)` the warm path
  reads and hands it to `GitFetcher.files_index_file` /
  `GitHostedTarballFetcher.files_index_file`, plus the shared
  `store_index_writer` clone.

New test: `tarball_fetcher::tests::writes_index_row_when_writer_provided`
spawns a real `StoreIndexWriter`, runs the fetcher with it, awaits
the writer's join handle, and re-opens the store to confirm the row
landed at the expected `pkg_id\tbuilt` key with the right `algo`,
`requires_build`, and a non-empty `files` map.

The warm prefetch's existing file-verification (`check_pkg_files_integrity`)
runs unchanged — if any CAS file referenced by the row has been
pruned, the snapshot falls through to cold and the fetcher re-runs.
Copilot AI review requested due to automatic review settings May 13, 2026 13:48
@coderabbitai

coderabbitai Bot commented May 13, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ba3d4d78-a786-4c6a-ac28-108b90087ef3

📥 Commits

Reviewing files that changed from the base of the PR and between b07616d and ce6d40b.

📒 Files selected for processing (2)
  • crates/git-fetcher/src/tarball_fetcher/tests.rs
  • crates/package-manager/src/create_virtual_store/tests.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/git-fetcher/src/tarball_fetcher/tests.rs
📜 Recent review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Upload results
  • GitHub Check: Run benchmark on ubuntu-latest
  • GitHub Check: Lint and Test (macos-latest)
  • GitHub Check: Lint and Test (windows-latest)
  • GitHub Check: Lint and Test (ubuntu-latest)
  • GitHub Check: Code Coverage
  • GitHub Check: Run benchmark on ubuntu-latest
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Preserve existing method chains and pipe-trait chains; do not break them into intermediate let bindings unless there is a concrete justification such as a compilation failure, borrow checker rejection, meaningful performance improvement, or other technical necessity. Refactoring for style alone is not sufficient justification.
Choose owned vs. borrowed parameters to minimize copies; prefer borrowed types (&Path over &PathBuf, &str over &String) when it does not force extra copies.
Prefer Arc::clone(&x) and Rc::clone(&x) over x.clone() for reference-counted types to make the cost visible at the call site.
Do not use star imports inside module bodies. Write use super::{Foo, bar} instead of use super::*; for any glob whose target is a module you control. External-crate preludes (e.g., use rayon::prelude::*;) and root-of-module re-exports (e.g., pub use submodule::*; in lib.rs) are exceptions.
Follow Rust API Guidelines for naming, as documented in https://rust-lang.github.io/api-guidelines/naming.html.
Declare a newtype wrapper for any branded string type being ported from TypeScript pnpm. Do not collapse the brand into a plain String or &str; give the type its own struct so misuse is a type error.
When porting branded string types where upstream TypeScript always validates before construction, validate in the Rust port too. Construct the wrapper only via TryFrom<String> and/or FromStr; do not provide an infallible public constructor that takes an arbitrary string.
For branded string types where upstream TypeScript never validates (used purely for type-safety to prevent confusion between string slots), expose an infallible From<String> and From<&str> constructor in the Rust wrapper.
When upstream TypeScript occasionally constructs a branded type without validation (via bare as assertion), add a from_str_unchecked (or similarly named) constructor on the Rust side. Keep the validating constructor as well; `from_str_u...

Files:

  • crates/package-manager/src/create_virtual_store/tests.rs
🧠 Learnings (2)
📚 Learning: 2026-05-01T10:01:33.766Z
Learnt from: zkochan
Repo: pnpm/pacquet PR: 349
File: crates/reporter/src/tests.rs:121-121
Timestamp: 2026-05-01T10:01:33.766Z
Learning: In Rust test code, follow the repo’s CODE_STYLE_GUIDE test-logging rule: add logging (e.g., `eprintln!`/`eprintln!(...)`) so that useful diagnostic values are printed when a test fails, unless the assertion is `assert_eq!` (where the differing values are already included). Concretely, if you use assertions like `assert!`, `assert_ne!`, etc., ensure the test logs the relevant actual/expected values (or context) before/around the assertion so failures can be diagnosed without rerunning.

Applied to files:

  • crates/package-manager/src/create_virtual_store/tests.rs
📚 Learning: 2026-05-07T23:19:08.272Z
Learnt from: KSXGitHub
Repo: pnpm/pacquet PR: 401
File: tasks/integrated-benchmark/src/work_env.rs:343-344
Timestamp: 2026-05-07T23:19:08.272Z
Learning: When reviewing Rust code in pnpm/pacquet for deprecated API usage, do not automatically treat `serde_saphyr::to_string` as deprecated. In `serde-saphyr` v0.0.25, `serde_saphyr::to_string` has no `#[deprecated]` attribute (the `#[deprecated]` later in `serde-saphyr-0.0.25/src/lib.rs` applies to a different function). Only flag `serde_saphyr::to_string` as deprecated if the resolved dependency version’s source shows `#[deprecated]` on that specific function.

Applied to files:

  • crates/package-manager/src/create_virtual_store/tests.rs
🔇 Additional comments (1)
crates/package-manager/src/create_virtual_store/tests.rs (1)

1-3: LGTM!

Also applies to: 5-7, 173-220, 222-255


📝 Walkthrough

Walkthrough

This PR extends CAS import to produce file metadata (files_index), wires git and tarball fetchers to optionally write PackageFilesIndex rows, enables warm-cache key derivation for git-hosted resolutions, and threads the writer and cache key through fetcher instantiation to support reusing prior fetch/checkout work.

Changes

Git-hosted warm prefetch

Layer / File(s) Summary
CAS import contract and file metadata
crates/git-fetcher/src/cas_io.rs
import_into_cas now returns ImportedFiles struct with both cas_paths and computed files_index (mapping relative-path to CafsFileInfo). New file_mode_from helper derives executable bits from filesystem metadata on Unix or returns fixed default on non-Unix, replacing prior is_file_executable.
GitFetcher struct and index-write wiring
crates/git-fetcher/src/fetcher.rs, crates/git-fetcher/src/fetcher/tests.rs
Extended GitFetcher with optional store_index_writer and files_index_file fields. Updated run_sync to destructure ImportedFiles and conditionally queue PackageFilesIndex rows (with requires_build, algo: sha512, and files from index) when writer is present. Updated three test initializers to populate new fields.
GitHostedTarballFetcher struct and index-write test
crates/git-fetcher/src/tarball_fetcher.rs, crates/git-fetcher/src/tarball_fetcher/tests.rs
Extended GitHostedTarballFetcher with optional store_index_writer and files_index_file fields. Updated re-import logic to destructure ImportedFiles and conditionally queue PackageFilesIndex. Added new test that spawns StoreIndexWriter, runs fetcher with writer enabled, and verifies index row presence with correct algorithm, requires_build, and file entries. Updated existing test initializers.
Warm-cache key support for git-hosted resolutions
crates/package-manager/src/create_virtual_store.rs
Import git_hosted_store_index_key helper. Refactor snapshot_cache_key to return warm-cache keys for git-hosted tarballs and LockfileResolution::Git(_) variants (instead of forcing cold batch), while preserving unsupported errors for directories and integrity-based keys for non-git-hosted cases.
Fetcher instantiation with writer and index-file key
crates/package-manager/src/install_package_by_snapshot.rs
Refactor imports and thread store_index_writer and files_index_file (computed via git_hosted_store_index_key) into both tarball and git fetcher constructors. Add comments documenting cache-key alignment rationale.
Test fixture updates
crates/package-manager/src/installability/tests.rs, crates/package-manager/src/create_virtual_store/tests.rs
Update synthetic_metadata helper to explicitly set path: None in TarballResolution. Add tests and helpers validating git-hosted cache-key behavior.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Possibly related PRs

  • pnpm/pacquet#440: Introduced TarballResolution.git_hosted and git_hosted_store_index_key helpers this PR uses for warm-cache key derivation.
  • pnpm/pacquet#451: Modified CAS import helpers; this PR refactors import_into_cas to return ImportedFiles with file metadata.
  • pnpm/pacquet#446: Related git-fetcher CAS-import flow that this PR builds upon to produce and persist file index entries.

Poem

🐇 I hopped through checkouts, hashing every file,
Stashing bits in CAS with a careful smile.
An index row queued for warm prefetch delight,
So next time I fetch, the paths are light.
Cache warmed, I twitch my nose—builds take flight!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(git-fetcher): warm prefetch for git-hosted snapshots (#436 follow-up)' directly summarizes the main change—enabling warm-cache prefetch for git-hosted installs to reuse prior work.
Description check ✅ Passed The description comprehensively covers the summary, three implementation pieces, test plan, and out-of-scope items, exceeding the template's core requirements.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/436-warm-git-prefetch

Comment @coderabbitai help to get the list of available commands and usage tips.

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 encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@codecov

codecov Bot commented May 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 65.11628% with 30 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.06%. Comparing base (34be005) to head (ce6d40b).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
crates/package-manager/src/create_virtual_store.rs 46.15% 14 Missing ⚠️
crates/git-fetcher/src/fetcher.rs 28.57% 10 Missing ⚠️
...package-manager/src/install_package_by_snapshot.rs 0.00% 6 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #454      +/-   ##
==========================================
- Coverage   87.20%   87.06%   -0.14%     
==========================================
  Files         105      113       +8     
  Lines        8493     9240     +747     
==========================================
+ Hits         7406     8045     +639     
- Misses       1087     1195     +108     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/git-fetcher/src/tarball_fetcher/tests.rs`:
- Around line 325-326: The two assertions using
assert!(row.files.contains_key("package.json")) and
assert!(row.files.contains_key("index.js")) lack failure context; update these
to include diagnostic messages that print which keys are present when the
assertion fails (e.g., include row.files.keys() or row.files itself in the
message) so failures show the actual contents of row.files for easier CI triage;
locate the checks in the test in tarball_fetcher/tests.rs and replace the bare
assert! calls with assert! that include a formatted message referencing
row.files or row.files.keys().
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8d921631-a5c9-48dd-80b6-6dcec788ff23

📥 Commits

Reviewing files that changed from the base of the PR and between cf125b1 and 7f8ac61.

📒 Files selected for processing (8)
  • crates/git-fetcher/src/cas_io.rs
  • crates/git-fetcher/src/fetcher.rs
  • crates/git-fetcher/src/fetcher/tests.rs
  • crates/git-fetcher/src/tarball_fetcher.rs
  • crates/git-fetcher/src/tarball_fetcher/tests.rs
  • crates/package-manager/src/create_virtual_store.rs
  • crates/package-manager/src/install_package_by_snapshot.rs
  • crates/package-manager/src/installability/tests.rs
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run benchmark on ubuntu-latest
  • GitHub Check: Run benchmark on ubuntu-latest
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Preserve existing method chains and pipe-trait chains; do not break them into intermediate let bindings unless there is a concrete justification such as a compilation failure, borrow checker rejection, meaningful performance improvement, or other technical necessity. Refactoring for style alone is not sufficient justification.
Choose owned vs. borrowed parameters to minimize copies; prefer borrowed types (&Path over &PathBuf, &str over &String) when it does not force extra copies.
Prefer Arc::clone(&x) and Rc::clone(&x) over x.clone() for reference-counted types to make the cost visible at the call site.
Do not use star imports inside module bodies. Write use super::{Foo, bar} instead of use super::*; for any glob whose target is a module you control. External-crate preludes (e.g., use rayon::prelude::*;) and root-of-module re-exports (e.g., pub use submodule::*; in lib.rs) are exceptions.
Follow Rust API Guidelines for naming, as documented in https://rust-lang.github.io/api-guidelines/naming.html.
Declare a newtype wrapper for any branded string type being ported from TypeScript pnpm. Do not collapse the brand into a plain String or &str; give the type its own struct so misuse is a type error.
When porting branded string types where upstream TypeScript always validates before construction, validate in the Rust port too. Construct the wrapper only via TryFrom<String> and/or FromStr; do not provide an infallible public constructor that takes an arbitrary string.
For branded string types where upstream TypeScript never validates (used purely for type-safety to prevent confusion between string slots), expose an infallible From<String> and From<&str> constructor in the Rust wrapper.
When upstream TypeScript occasionally constructs a branded type without validation (via bare as assertion), add a from_str_unchecked (or similarly named) constructor on the Rust side. Keep the validating constructor as well; `from_str_u...

Files:

  • crates/git-fetcher/src/fetcher/tests.rs
  • crates/package-manager/src/installability/tests.rs
  • crates/package-manager/src/install_package_by_snapshot.rs
  • crates/package-manager/src/create_virtual_store.rs
  • crates/git-fetcher/src/fetcher.rs
  • crates/git-fetcher/src/tarball_fetcher.rs
  • crates/git-fetcher/src/cas_io.rs
  • crates/git-fetcher/src/tarball_fetcher/tests.rs
🧠 Learnings (2)
📚 Learning: 2026-05-01T10:01:33.766Z
Learnt from: zkochan
Repo: pnpm/pacquet PR: 349
File: crates/reporter/src/tests.rs:121-121
Timestamp: 2026-05-01T10:01:33.766Z
Learning: In Rust test code, follow the repo’s CODE_STYLE_GUIDE test-logging rule: add logging (e.g., `eprintln!`/`eprintln!(...)`) so that useful diagnostic values are printed when a test fails, unless the assertion is `assert_eq!` (where the differing values are already included). Concretely, if you use assertions like `assert!`, `assert_ne!`, etc., ensure the test logs the relevant actual/expected values (or context) before/around the assertion so failures can be diagnosed without rerunning.

Applied to files:

  • crates/git-fetcher/src/fetcher/tests.rs
  • crates/package-manager/src/installability/tests.rs
  • crates/git-fetcher/src/tarball_fetcher/tests.rs
📚 Learning: 2026-05-07T23:19:08.272Z
Learnt from: KSXGitHub
Repo: pnpm/pacquet PR: 401
File: tasks/integrated-benchmark/src/work_env.rs:343-344
Timestamp: 2026-05-07T23:19:08.272Z
Learning: When reviewing Rust code in pnpm/pacquet for deprecated API usage, do not automatically treat `serde_saphyr::to_string` as deprecated. In `serde-saphyr` v0.0.25, `serde_saphyr::to_string` has no `#[deprecated]` attribute (the `#[deprecated]` later in `serde-saphyr-0.0.25/src/lib.rs` applies to a different function). Only flag `serde_saphyr::to_string` as deprecated if the resolved dependency version’s source shows `#[deprecated]` on that specific function.

Applied to files:

  • crates/git-fetcher/src/fetcher/tests.rs
  • crates/package-manager/src/installability/tests.rs
  • crates/package-manager/src/install_package_by_snapshot.rs
  • crates/package-manager/src/create_virtual_store.rs
  • crates/git-fetcher/src/fetcher.rs
  • crates/git-fetcher/src/tarball_fetcher.rs
  • crates/git-fetcher/src/cas_io.rs
  • crates/git-fetcher/src/tarball_fetcher/tests.rs
🔇 Additional comments (8)
crates/package-manager/src/installability/tests.rs (1)

56-61: LGTM!

crates/package-manager/src/create_virtual_store.rs (1)

17-20: LGTM!

Also applies to: 667-715

crates/git-fetcher/src/cas_io.rs (1)

25-32: LGTM!

Also applies to: 121-167, 169-181

crates/git-fetcher/src/fetcher.rs (1)

60-74: LGTM!

Also applies to: 172-191

crates/git-fetcher/src/fetcher/tests.rs (1)

97-98: LGTM!

Also applies to: 144-145, 205-206

crates/package-manager/src/install_package_by_snapshot.rs (1)

13-16: LGTM!

Also applies to: 169-176, 191-193, 209-212, 229-230

crates/git-fetcher/src/tarball_fetcher/tests.rs (1)

5-7: LGTM!

Also applies to: 56-57, 105-107, 150-152, 208-210, 261-263, 275-324, 327-327

crates/git-fetcher/src/tarball_fetcher.rs (1)

77-83: LGTM!

Also applies to: 156-177

Comment thread crates/git-fetcher/src/tarball_fetcher/tests.rs Outdated
@github-actions

github-actions Bot commented May 13, 2026

Copy link
Copy Markdown

Micro-Benchmark Results

Linux

group                          main                                   pr
-----                          ----                                   --
tarball/download_dependency    1.01     17.4±0.58ms   249.8 KB/sec    1.00     17.2±0.24ms   252.1 KB/sec

…#454 review)

CodeRabbit pointed out that the two `assert!(row.files.contains_key(...))`
calls in `writes_index_row_when_writer_provided` fail without showing
what keys were actually present, making CI triage slower. Collect the
keys once and pass them through to the assertion message so a failure
points at the discrepancy directly.

Pure test-readability change; no production code touched.
@github-actions

github-actions Bot commented May 13, 2026

Copy link
Copy Markdown

Integrated-Benchmark Report (Linux)

Scenario: Frozen Lockfile

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 2.662 ± 0.140 2.544 2.920 1.03 ± 0.06
pacquet@main 2.575 ± 0.068 2.471 2.695 1.00
pnpm 6.383 ± 0.126 6.196 6.568 2.48 ± 0.08
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 2.66226748482,
      "stddev": 0.14044020100462554,
      "median": 2.6316269642199996,
      "user": 2.6175296799999996,
      "system": 3.6408581999999994,
      "min": 2.54357902272,
      "max": 2.9203348177199997,
      "times": [
        2.89861999572,
        2.54357902272,
        2.55694870472,
        2.65694569472,
        2.9203348177199997,
        2.55030160272,
        2.6885711947199997,
        2.54411988672,
        2.6513461557199998,
        2.61190777272
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 2.57459651332,
      "stddev": 0.06838677331987975,
      "median": 2.5671595637199998,
      "user": 2.56309838,
      "system": 3.625521,
      "min": 2.47114686972,
      "max": 2.69468806672,
      "times": [
        2.54209228972,
        2.5243061937199998,
        2.66715994572,
        2.56768449572,
        2.52357325772,
        2.56865710172,
        2.47114686972,
        2.69468806672,
        2.56663463172,
        2.6200222807199998
      ]
    },
    {
      "command": "pnpm",
      "mean": 6.382924709019999,
      "stddev": 0.12566620585257687,
      "median": 6.38275426372,
      "user": 9.546950980000002,
      "system": 4.6046641,
      "min": 6.19614376872,
      "max": 6.56793153872,
      "times": [
        6.33575752472,
        6.23269475972,
        6.409010654719999,
        6.39950356272,
        6.47107280972,
        6.56793153872,
        6.19614376872,
        6.36600496472,
        6.29220023972,
        6.55892726672
      ]
    }
  ]
}

Scenario: Frozen Lockfile (Hot Cache)

Command Mean [ms] Min [ms] Max [ms] Relative
pacquet@HEAD 709.2 ± 30.7 685.1 792.7 1.00
pacquet@main 769.9 ± 41.1 724.6 860.3 1.09 ± 0.07
pnpm 2752.5 ± 64.3 2676.0 2885.1 3.88 ± 0.19
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 0.7092117118400001,
      "stddev": 0.030683466981246226,
      "median": 0.7004896066400002,
      "user": 0.39636678000000003,
      "system": 1.49606138,
      "min": 0.6851064926400001,
      "max": 0.7927312456400001,
      "times": [
        0.7927312456400001,
        0.6998627356400001,
        0.71281212264,
        0.7059392606400001,
        0.6851064926400001,
        0.6980966016400001,
        0.6889658946400001,
        0.7011164776400001,
        0.7123118326400001,
        0.69517445464
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 0.7699402479399999,
      "stddev": 0.04107972452039447,
      "median": 0.7553922926400001,
      "user": 0.38176457999999996,
      "system": 1.5161572799999998,
      "min": 0.7246431016400001,
      "max": 0.8603070556400001,
      "times": [
        0.8603070556400001,
        0.7660648446400001,
        0.7584395796400001,
        0.7373242036400001,
        0.7523450056400001,
        0.7909169746400001,
        0.7466808286400001,
        0.7481119386400001,
        0.7246431016400001,
        0.8145689466400001
      ]
    },
    {
      "command": "pnpm",
      "mean": 2.75247403574,
      "stddev": 0.06431999187280807,
      "median": 2.7381536961400004,
      "user": 3.3506833800000004,
      "system": 2.30865878,
      "min": 2.67596523064,
      "max": 2.88511119564,
      "times": [
        2.76444152364,
        2.88511119564,
        2.70995197564,
        2.8371002616400003,
        2.74011589464,
        2.67596523064,
        2.69557179764,
        2.73619149764,
        2.7212418026400003,
        2.75904917764
      ]
    }
  ]
}

…for warm git prefetch (#454 coverage)

Closes the two coverage gaps identified during PR review:

- **`snapshot_cache_key` unit tests for git arms.** Two new tests
  in `create_virtual_store::tests` build a `PackageMetadata` with
  `LockfileResolution::Git` and `LockfileResolution::Tarball {
  gitHosted: true }` respectively and assert that
  `snapshot_cache_key` returns `Some("<pkg_id>\tbuilt")` — the same
  `gitHostedStoreIndexKey(pkg_id, built=true)` shape both fetchers
  write at install time. Drift between the read and write sides
  would silently degrade every git-hosted re-install to the cold
  path; pinning the shape on both sides keeps them in lock-step.
- **Richer `CafsFileInfo` round-trip assertions.** The existing
  `writes_index_row_when_writer_provided` round-trip checked only
  `algo`, `requires_build`, and key presence. Now it also asserts
  the per-file entry's `digest` is non-empty + ASCII-hex, `mode`
  rounds to `0o644` on a non-executable regular file, `size`
  matches the source bytes, and `checked_at` is `None` (matches
  `add_files_from_dir`'s convention — the first integrity-check
  pass stamps it). Drift in any of these would surface as a warm-
  prefetch cache miss instead of a clean fail, so explicit
  assertions catch the regression at unit-test time.

No production code changes; pure coverage. Existing tests still
green at 767 (765 + the 2 new `snapshot_cache_key` tests).
Copilot AI review requested due to automatic review settings May 13, 2026 14:31

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 encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@zkochan zkochan merged commit 99d708a into main May 13, 2026
17 of 20 checks passed
@zkochan zkochan deleted the feat/436-warm-git-prefetch branch May 13, 2026 15:01
KSXGitHub pushed a commit that referenced this pull request May 13, 2026
Pull in 28 commits from upstream main, including the
`pacquet-npmrc` → `pacquet-config` rename (#420) plus features:
- supportedArchitectures + --cpu/--os/--libc (#456)
- frozen-lockfile (#442, #443, #447, #450)
- git-fetcher (#436 / #446, #451, #454)
- side-effects cache (#421 / #422, #423, #424)
- real-hoist + global-virtual-store (#432 / #438 / #444, #449, #452)
- patchedDependencies + allow-builds (#425, #427, #428)
- engine/platform installability (#434 / #439)

Conflicts resolved:
- `crates/npmrc/` files migrated under the renamed
  `crates/config/` directory; `Npmrc` → `Config` everywhere
  except `NpmrcAuth` (which keeps the `.npmrc`-domain name).
- `Config::current` reads the env-var DI generic `Api: EnvVar`
  for ${VAR}-substitution in `.npmrc`. Production turbofish in
  `cli_args.rs` is `Config::current::<RealApi, _, _, _, _>(...)`.
- Two-phase `NpmrcAuth::apply_*` retained so default-registry
  creds key at the yaml-resolved registry URL.
- New `Config::auth_headers` field plumbed through
  `install_package_by_snapshot`'s `DownloadTarballToStore`.
- Tests under `crates/config/src/workspace_yaml/tests.rs`
  pick up the new ParseYaml unit test added on this branch.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants