Skip to content

feat: announce reused lockfile-verification verdicts#12326

Merged
zkochan merged 1 commit into
mainfrom
lockfile-verification-cached-message
Jun 11, 2026
Merged

feat: announce reused lockfile-verification verdicts#12326
zkochan merged 1 commit into
mainfrom
lockfile-verification-cached-message

Conversation

@zkochan

@zkochan zkochan commented Jun 11, 2026

Copy link
Copy Markdown
Member

Summary

Closes #12324

When the lockfile-verification gate short-circuits on a cached verdict, it stayed completely silent — with trustLockfile/trustPolicy/minimumReleaseAge active, a repeat pnpm update or pnpm install looked like the supply-chain policy gate never ran. This PR makes the reused verdict visible:

✓ Lockfile passes supply-chain policies (verified 2h ago)

Changes

pnpm (TypeScript)

  • @pnpm/core-loggers: new cached status on LockfileVerificationMessage, carrying the reused record's verifiedAt (ISO-8601) and lockfilePath. No entries count — the short-circuit happens before candidates are collected, keeping the fast path stat-only.
  • @pnpm/installing.deps-installer: the cache-hit return in verifyLockfileResolutions emits the event, but only when policy verifiers are active — the offline shape-only check every install performs stays quiet. tryLockfileVerificationCache now surfaces verifiedAt from both the stat-shortcut and content-hash hit paths.
  • @pnpm/cli.default-reporter: renders the relative age via pretty-ms (clamped at zero against clock skew), falling back to (previously verified) for cache records that predate the timestamp field.

pacquet (Rust port, same change)

  • Cached variant on the reporter's LockfileVerificationMessage with the matching camelCase wire shape (status: "cached", verifiedAt, lockfilePath; absent fields omitted), emitted from the same cache-hit point in verify_lockfile_resolutions. CacheLookupResult gained verified_at.

Behavior notes

  • The non-cached path is unchanged: ? Verifying lockfile… / ✓ … (N entries in Xms).
  • Installs without any active policy verifier print nothing, as before.
  • One existing pacquet test (fresh_install_records_lockfile_verification_for_mtime_bypassed_noop) asserted the cache hit emits no verification events; it now pins "exactly one Cached, no fan-out events".

Testing

  • Unit tests on both stacks: cached emission with timestamp, silent shape-only hit, reporter rendering (relative + fallback), NDJSON wire shape (present + omitted verifiedAt).
  • Verified end-to-end with the bundled CLI: pnpm update with minimumReleaseAge set prints the cached line on repeat runs; after wiping <cacheDir>/lockfile-verified.jsonl the started/done pair still renders.

Written by an agent (Claude Code, claude-fable-5).

Summary by CodeRabbit

  • New Features
    • Lockfile verification now displays a message when verification results are cached, showing the lockfile passed supply-chain policies and including a timestamp indicating when verification was last performed (e.g., "verified 2 hours ago").

When the lockfile-verification gate short-circuits on a cached verdict,
it used to stay completely silent, which made it look like the
supply-chain policy gate never ran (#12324). Emit a new
`cached` status on the pnpm:lockfile-verification channel carrying the
reused record's verifiedAt timestamp, and render it in the default
reporter as "Lockfile passes supply-chain policies (verified 2h ago)"
(falling back to "previously verified" for records that predate the
timestamp). The event fires only when policy verifiers are active, so
the shape-only check every install performs stays quiet.

Ported to pacquet in the same change: a `Cached` variant on the
reporter's LockfileVerificationMessage with the matching camelCase wire
shape, emitted from the same cache-hit point in
verify_lockfile_resolutions.
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

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: 905947a4-c368-4e7a-a152-949a77b1ca5f

📥 Commits

Reviewing files that changed from the base of the PR and between cb18695 and 7e54dd7.

📒 Files selected for processing (13)
  • .changeset/lockfile-verification-cached-message.md
  • cli/default-reporter/src/reporterForClient/reportLockfileVerification.ts
  • cli/default-reporter/test/reportingLockfileVerification.ts
  • core/core-loggers/src/lockfileVerificationLogger.ts
  • installing/deps-installer/src/install/verifyLockfileResolutions.ts
  • installing/deps-installer/src/install/verifyLockfileResolutionsCache.ts
  • installing/deps-installer/test/install/verifyLockfileResolutions.ts
  • pacquet/crates/lockfile-verification/src/cache.rs
  • pacquet/crates/lockfile-verification/src/verify_lockfile_resolutions.rs
  • pacquet/crates/lockfile-verification/src/verify_lockfile_resolutions/tests.rs
  • pacquet/crates/package-manager/src/install/tests.rs
  • pacquet/crates/reporter/src/lib.rs
  • pacquet/crates/reporter/src/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). (1)
  • GitHub Check: windows-latest / Node.js 22.13.0 / Test
🧰 Additional context used
📓 Path-based instructions (2)
pacquet/**/*.rs

📄 CodeRabbit inference engine (pacquet/AGENTS.md)

pacquet/**/*.rs: Log emissions are part of matching pnpm — when porting a function that fires pnpm:<channel> events through globalLogger, logger.debug(...), or streamParser.write(...), mirror the call site, payload, and ordering so @pnpm/cli.default-reporter parses pacquet's NDJSON the same way
Declare a newtype wrapper for branded string types instead of collapsing the brand into a plain String or &str in Rust
If upstream TypeScript always validates before construction of a branded string, validate in the Rust wrapper too via TryFrom<String> and/or FromStr and do not provide an infallible public constructor
If upstream TypeScript never validates a branded string, just brand for type-safety in Rust by exposing an infallible From<String> constructor
If upstream TypeScript occasionally constructs a branded string without validation, expose from_str_unchecked in Rust as an escape hatch alongside the validating constructor
Match upstream serde behavior for branded strings crossing JSON, YAML, or INI boundaries by using #[serde(try_from = "String")] for deserialization and #[serde(into = "String")] for serialization
Derive simple conversions for branded strings using #[derive(derive_more::From)] and #[derive(derive_more::Into)] instead of handwriting impl blocks; use manual impl only when conversion needs custom logic
Model TypeScript string literal unions (like 'auto' | 'always' | 'never') as Rust enums instead of newtype wrappers, since the set of valid values is closed
Treat TypeScript string template literal types (like `${string}@${string}`) the same as branded string types in Rust, using a newtype wrapper with validation
Follow the code style guide in CODE_STYLE_GUIDE.md — imports, modules, naming, ownership and borrowing, parameter type selection, trait bounds, pattern matching, pipe-trait, error handling, test layout, and cloning of Arc and Rc
Choose owned vs. borrowed parameters to minimize copies; widen to t...

Files:

  • pacquet/crates/reporter/src/tests.rs
  • pacquet/crates/lockfile-verification/src/verify_lockfile_resolutions/tests.rs
  • pacquet/crates/lockfile-verification/src/verify_lockfile_resolutions.rs
  • pacquet/crates/package-manager/src/install/tests.rs
  • pacquet/crates/lockfile-verification/src/cache.rs
  • pacquet/crates/reporter/src/lib.rs
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use Standard Style with trailing commas, prefer functions over classes, declare functions after they are used (relying on hoisting), limit functions to no more than two or three arguments, and use a single options object for functions needing more parameters
Follow import order: standard libraries first, then external dependencies (sorted alphabetically), then relative imports
Do not write comments that restate what the code already says; rename variables, split helpers, or move checks to more obvious places instead
Do not repeat documentation at call sites that already lives on the callee; update the JSDoc once and let every call site benefit
Use JSDoc for the function's contract (preconditions, postconditions, edge cases, why the function exists), not for re-narrating the function body
Do not record past implementation shape, refactor history, or removed code in comments; use git log and git blame for that information instead
Write comments only when the reason for code is non-obvious, a hidden invariant exists, a workaround for a known bug is needed, or an exception to surrounding pattern is deliberate

Files:

  • core/core-loggers/src/lockfileVerificationLogger.ts
  • installing/deps-installer/src/install/verifyLockfileResolutions.ts
  • cli/default-reporter/src/reporterForClient/reportLockfileVerification.ts
  • cli/default-reporter/test/reportingLockfileVerification.ts
  • installing/deps-installer/test/install/verifyLockfileResolutions.ts
  • installing/deps-installer/src/install/verifyLockfileResolutionsCache.ts
🧠 Learnings (8)
📚 Learning: 2026-05-26T21:01:06.666Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11966
File: .changeset/require-tarball-integrity.md:6-6
Timestamp: 2026-05-26T21:01:06.666Z
Learning: In pnpm lockfile-related release notes/docs (especially changeset markdown), preserve URL hostnames exactly as they appear in pnpm-lock.yaml tarball resolution entries—keep hosts like `codeload.github.com`, `bitbucket.org`, and `gitlab.com` in lowercase. Do not “correct” them to title-case/preserve brand capitalization (e.g., LanguageTool rules like `GITHUB` capitalization) because these are literal URL fragments, not platform brand names.

Applied to files:

  • .changeset/lockfile-verification-cached-message.md
📚 Learning: 2026-05-20T19:40:55.051Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11774
File: pacquet/crates/resolving-deps-resolver/src/resolve_peers.rs:0-0
Timestamp: 2026-05-20T19:40:55.051Z
Learning: In the pacquet Rust code, ensure the semver implementation uses the `node-semver` crate (not `nodejs-semver`). `node-semver`’s public API does not include a `satisfies_with_prerelease`-style method; prerelease-tolerant matching should be implemented inline by first calling `Range::satisfies`, and when it rejects a prerelease version, retry matching against a stripped `MAJOR.MINOR.PATCH` base of the prerelease version.

Applied to files:

  • pacquet/crates/reporter/src/tests.rs
  • pacquet/crates/lockfile-verification/src/verify_lockfile_resolutions/tests.rs
  • pacquet/crates/lockfile-verification/src/verify_lockfile_resolutions.rs
  • pacquet/crates/package-manager/src/install/tests.rs
  • pacquet/crates/lockfile-verification/src/cache.rs
  • pacquet/crates/reporter/src/lib.rs
📚 Learning: 2026-05-22T00:08:44.646Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11837
File: pacquet/crates/resolving-npm-resolver/src/pick_package.rs:33-51
Timestamp: 2026-05-22T00:08:44.646Z
Learning: In the pnpm/pnpm repo’s pacquet Rust crates, do not flag Unicode ellipsis characters (U+2026, `…`) in Rust doc comments (`///` / `/** */`) as a lint violation. The pacquet crate’s `dylint.toml` only enables `perfectionist::derive_ordering`, and the Dylint `unicode-ellipsis` rule is not enabled for this project—so `…` in doc comments is an intentional, repo-consistent style.

Applied to files:

  • pacquet/crates/reporter/src/tests.rs
  • pacquet/crates/lockfile-verification/src/verify_lockfile_resolutions/tests.rs
  • pacquet/crates/lockfile-verification/src/verify_lockfile_resolutions.rs
  • pacquet/crates/package-manager/src/install/tests.rs
  • pacquet/crates/lockfile-verification/src/cache.rs
  • pacquet/crates/reporter/src/lib.rs
📚 Learning: 2026-05-20T23:07:58.444Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11784
File: pacquet/crates/resolving-deps-resolver/src/hoist_peers.rs:120-133
Timestamp: 2026-05-20T23:07:58.444Z
Learning: When reviewing code in this pacquet Rust port, follow the upstream pnpm compatibility rule: only match pnpm’s behavior exactly. Do not propose review changes that intentionally deviate from pnpm’s documented/observed behavior, even if pnpm appears buggy. If you identify a real bug in pnpm behavior, the review should prioritize fixing it upstream in pnpm first, and avoid implementing a pnpm-behavior workaround here unless the same fix has already landed upstream.

Applied to files:

  • pacquet/crates/reporter/src/tests.rs
  • pacquet/crates/lockfile-verification/src/verify_lockfile_resolutions/tests.rs
  • pacquet/crates/lockfile-verification/src/verify_lockfile_resolutions.rs
  • pacquet/crates/package-manager/src/install/tests.rs
  • pacquet/crates/lockfile-verification/src/cache.rs
  • pacquet/crates/reporter/src/lib.rs
📚 Learning: 2026-06-06T18:58:37.156Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 12243
File: pacquet/crates/package-manager/src/install_package_by_snapshot.rs:319-322
Timestamp: 2026-06-06T18:58:37.156Z
Learning: When reviewing Rust code, do not assume `matches!(expr, Pattern(_))` will move out of `expr` if `Pattern(_)` contains no by-value bindings. `matches!` desugars to a `match` that auto-borrows the scrutinee for discrimination, so even if `expr` is a non-`Copy` value behind a shared reference (e.g., `&T`), the macro should not move-out of the borrowed data purely due to `matches!`. Treat `matches!(&expr, Pattern(_))` as a readability/clarity improvement, not a correctness requirement. Only flag potential move-out-of-borrow risks when the pattern includes by-value bindings that would require moving the matched value.

Applied to files:

  • pacquet/crates/reporter/src/tests.rs
  • pacquet/crates/lockfile-verification/src/verify_lockfile_resolutions/tests.rs
  • pacquet/crates/lockfile-verification/src/verify_lockfile_resolutions.rs
  • pacquet/crates/package-manager/src/install/tests.rs
  • pacquet/crates/lockfile-verification/src/cache.rs
  • pacquet/crates/reporter/src/lib.rs
📚 Learning: 2026-05-14T09:04:00.133Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11622
File: resolving/npm-resolver/test/publishedBy.test.ts:350-354
Timestamp: 2026-05-14T09:04:00.133Z
Learning: In the pnpm/pnpm repository, ESLint is the authoritative style linter. Do not raise review findings for missing trailing commas in multiline function calls (e.g., `fs.writeFileSync(...)`) when this repo’s ESLint configuration does not report them and lint passes. Prefer deferring to the ESLint results for this specific trailing-comma rule rather than enforcing it manually in code review.

Applied to files:

  • core/core-loggers/src/lockfileVerificationLogger.ts
  • installing/deps-installer/src/install/verifyLockfileResolutions.ts
  • cli/default-reporter/src/reporterForClient/reportLockfileVerification.ts
  • cli/default-reporter/test/reportingLockfileVerification.ts
  • installing/deps-installer/test/install/verifyLockfileResolutions.ts
  • installing/deps-installer/src/install/verifyLockfileResolutionsCache.ts
📚 Learning: 2026-06-05T13:47:26.046Z
Learnt from: vsumner
Repo: pnpm/pnpm PR: 12190
File: installing/deps-installer/src/install/index.ts:2337-2343
Timestamp: 2026-06-05T13:47:26.046Z
Learning: In the pnpm/pnpm codebase, `PnpmError` automatically prefixes `err.code` with `ERR_PNPM_` when you pass a code that does not already start with `ERR_PNPM_` (it normalizes `this.code` via `code.startsWith('ERR_PNPM_') ? code : `ERR_PNPM_${code}``). Therefore, during code review you should NOT flag `new PnpmError(...)` call sites for passing a bare error code (e.g., `new PnpmError('FROZEN_STORE_INCOMPATIBLE_WITH_PNPR', ...)`); the resulting `err.code` will still be `ERR_PNPM_FROZEN_STORE_INCOMPATIBLE_WITH_PNPR`.

Applied to files:

  • core/core-loggers/src/lockfileVerificationLogger.ts
  • installing/deps-installer/src/install/verifyLockfileResolutions.ts
  • cli/default-reporter/src/reporterForClient/reportLockfileVerification.ts
  • cli/default-reporter/test/reportingLockfileVerification.ts
  • installing/deps-installer/test/install/verifyLockfileResolutions.ts
  • installing/deps-installer/src/install/verifyLockfileResolutionsCache.ts
📚 Learning: 2026-06-05T13:47:05.929Z
Learnt from: vsumner
Repo: pnpm/pnpm PR: 12190
File: installing/deps-installer/test/install/frozenStore.ts:2-17
Timestamp: 2026-06-05T13:47:05.929Z
Learning: In the pnpm/pnpm repository, the shared Jest preset keeps `injectGlobals` at its default (`true`), so `test` and `expect` are available as Jest globals. Therefore, reviewers should not flag (or treat as TypeScript/compilation errors) missing `import { test, expect } from 'jest/globals'` when a test file uses `test`/`expect` without importing them. Importing from `jest/globals` may still be used for consistency with sibling files, but it is not required for execution in this repo unless a Jest preset is explicitly configured with `injectGlobals: false`.

Applied to files:

  • cli/default-reporter/test/reportingLockfileVerification.ts
  • installing/deps-installer/test/install/verifyLockfileResolutions.ts
🔇 Additional comments (14)
pacquet/crates/lockfile-verification/src/verify_lockfile_resolutions.rs (1)

79-81: LGTM!

Also applies to: 114-115, 125-137

pacquet/crates/lockfile-verification/src/verify_lockfile_resolutions/tests.rs (1)

516-573: LGTM!

Also applies to: 575-614

pacquet/crates/package-manager/src/install/tests.rs (2)

15-17: LGTM!


6109-6118: LGTM!

core/core-loggers/src/lockfileVerificationLogger.ts (1)

9-9: LGTM!

Also applies to: 48-62, 68-68

pacquet/crates/reporter/src/lib.rs (1)

743-746: LGTM!

Also applies to: 775-783

installing/deps-installer/src/install/verifyLockfileResolutionsCache.ts (1)

109-115: LGTM!

Also applies to: 264-267, 297-297

pacquet/crates/lockfile-verification/src/cache.rs (1)

109-112: LGTM!

Also applies to: 178-182, 194-194, 201-201, 225-225

pacquet/crates/reporter/src/tests.rs (1)

939-987: LGTM!

.changeset/lockfile-verification-cached-message.md (1)

1-9: LGTM!

installing/deps-installer/src/install/verifyLockfileResolutions.ts (1)

133-146: LGTM!

Also applies to: 152-154

cli/default-reporter/src/reporterForClient/reportLockfileVerification.ts (1)

32-36: LGTM!

Also applies to: 59-68

cli/default-reporter/test/reportingLockfileVerification.ts (1)

132-166: LGTM!

installing/deps-installer/test/install/verifyLockfileResolutions.ts (1)

5-7: LGTM!

Also applies to: 266-321


📝 Walkthrough

Walkthrough

This PR extends lockfile verification to expose cached verdicts instead of silently reusing them. When verification is short-circuited by the cache, a "cached" message is now emitted with an optional timestamp of when the lockfile was previously verified, displayed to users with human-readable durations.

Changes

Lockfile Verification Cached Status and Messages

Layer / File(s) Summary
Message type contracts and reporter shape
core/core-loggers/src/lockfileVerificationLogger.ts, pacquet/crates/reporter/src/lib.rs
LockfileVerificationMessageBase.status union extends to include 'cached', and new LockfileVerificationCachedMessage interface with optional verifiedAt field is added. Rust reporter gains Cached { verified_at?, lockfile_path? } variant.
Cache lookup result contracts exposing verified timestamp
installing/deps-installer/src/install/verifyLockfileResolutionsCache.ts, pacquet/crates/lockfile-verification/src/cache.rs
CacheLookupResult adds optional verifiedAt/verified_at field populated only on successful cache hits with usable timestamps.
TypeScript cache hydration paths
installing/deps-installer/src/install/verifyLockfileResolutionsCache.ts
Stat-shortcut and content-hash hit paths populate verifiedAt from the cached record's timestamp.
Rust cache hydration paths
pacquet/crates/lockfile-verification/src/cache.rs
Stat-shortcut, untrusted-record, and final refresh-hit paths populate verified_at from cached records when timestamps are non-empty.
TypeScript verification runner cache-hit event emission
installing/deps-installer/src/install/verifyLockfileResolutions.ts
Cache-hit fast path now emits lockfileVerificationLogger.debug event with status: 'cached', verifiedAt, and lockfilePath when policy verifiers are present.
Rust verification runner cache-hit event emission
pacquet/crates/lockfile-verification/src/verify_lockfile_resolutions.rs
Cache-hit path emits LockfileVerificationMessage::Cached event with verified_at and lockfile_path when policy verifiers are present; documentation updated to reflect this behavior.
TypeScript reporter formatting for cached verdicts
cli/default-reporter/src/reporterForClient/reportLockfileVerification.ts
reportLockfileVerification handles status: 'cached' by emitting a green success message with a new formatCachedVerdict helper that renders "previously verified ago" or a timeless fallback.
Rust wire format tests for cached serialization
pacquet/crates/reporter/src/tests.rs
Validate JSON serialization of Cached variant with camelCase verifiedAt/lockfilePath fields and verify omission of verifiedAt when the underlying verified_at is None.
TypeScript reporter test coverage for cached verdicts
cli/default-reporter/test/reportingLockfileVerification.ts
Test cached verdict output with and without verifiedAt timestamp, verifying humanized duration formatting and fallback to "previously verified".
TypeScript cache logging integration tests
installing/deps-installer/test/install/verifyLockfileResolutions.ts
Verify cache-hit paths emit lockfileVerificationLogger.debug events with status: 'cached' and timestamp, and remain silent when no policy verifiers are active.
Rust cache event integration tests
pacquet/crates/lockfile-verification/src/verify_lockfile_resolutions/tests.rs
Test cache hits emit a single Cached event with the first run's verified_at timestamp, and remain silent when no policy verifiers are present.
Package manager integration test update
pacquet/crates/package-manager/src/install/tests.rs
Update install test to expect LockfileVerificationMessage::Cached on verification cache hits, replacing the prior expectation of no events.
Release notes and changelog
.changeset/lockfile-verification-cached-message.md
Document the behavioral change and version bumps for @pnpm/core-loggers, @pnpm/cli.default-reporter, @pnpm/installing.deps-installer, and pnpm.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • pnpm/pnpm#11722: Directly implements the cached lockfile-verification messages, cache timestamp fields, and reporter events proposed in this issue.

Possibly related PRs

  • pnpm/pnpm#11712: Extends the lockfile-verification logging and reporter pipeline introduced in that PR by adding a new cached status and cached-verified-time messaging.
  • pnpm/pnpm#12260: Both PRs update the lockfile-verification caching flow—this one adds the cached outcome for reused verdicts while that one records the verification results for cache reuse.

Poem

🐰 A cached verdict hops forth with glee,
No more silent returns—now all can see!
"Verified 2h ago," the message rings clear,
A timestamp of trust for all far and near.
The cache speaks its truth with a verdant green gleam! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and concisely summarizes the main change: making cached lockfile-verification verdicts visible to users through announcements/messages.
Linked Issues check ✅ Passed The PR addresses issue #12324 by implementing visibility for cached lockfile-verification verdicts with timestamps, ensuring policy gates produce user-visible output, though the link between the two is indirect.
Out of Scope Changes check ✅ Passed All changes directly support the PR objective of announcing cached lockfile-verification verdicts; no unrelated modifications detected across TypeScript and Rust implementations.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ 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 lockfile-verification-cached-message

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

Copy link
Copy Markdown
Contributor

Micro-Benchmark Results

Linux

group                          main                                   pr
-----                          ----                                   --
tarball/download_dependency    1.00      7.8±0.13ms   552.7 KB/sec    1.06      8.3±0.05ms   522.5 KB/sec

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.11765% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 87.73%. Comparing base (cb18695) to head (7e54dd7).

Files with missing lines Patch % Lines
pacquet/crates/lockfile-verification/src/cache.rs 85.71% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main   #12326   +/-   ##
=======================================
  Coverage   87.73%   87.73%           
=======================================
  Files         291      291           
  Lines       36034    36044   +10     
=======================================
+ Hits        31613    31622    +9     
- Misses       4421     4422    +1     

☔ View full report in Codecov by Harness.
📢 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.

@github-actions

Copy link
Copy Markdown
Contributor

Integrated-Benchmark Report (Linux)

Each scenario reports direct installs and pnpr installs. Bencher consumes pacquet@HEAD and pnpr@HEAD.

Scenario: Isolated linker: fresh restore, cold cache + cold store

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 10.322 ± 0.092 10.205 10.517 1.99 ± 0.04
pacquet@main 10.284 ± 0.056 10.215 10.394 1.98 ± 0.03
pnpr@HEAD 5.221 ± 0.149 5.104 5.621 1.01 ± 0.03
pnpr@main 5.185 ± 0.083 5.126 5.372 1.00
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 10.321633290640001,
      "stddev": 0.09246384135628427,
      "median": 10.31884307294,
      "user": 3.8106472599999988,
      "system": 2.14301474,
      "min": 10.20515459894,
      "max": 10.51674737394,
      "times": [
        10.36494973594,
        10.51674737394,
        10.38262087594,
        10.36157075394,
        10.26109133994,
        10.31651105294,
        10.32117509294,
        10.27597227494,
        10.20515459894,
        10.21053980694
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 10.284321675940001,
      "stddev": 0.056187172603647195,
      "median": 10.26641675944,
      "user": 3.7601576599999995,
      "system": 2.16284774,
      "min": 10.21516078894,
      "max": 10.39436375894,
      "times": [
        10.39436375894,
        10.26670376494,
        10.26612975394,
        10.36444559094,
        10.21516078894,
        10.25758276594,
        10.30652411594,
        10.23704197394,
        10.25248372094,
        10.28278052494
      ]
    },
    {
      "command": "pnpr@HEAD",
      "mean": 5.220947227840001,
      "stddev": 0.1491757767313957,
      "median": 5.18054783194,
      "user": 2.7528888599999997,
      "system": 1.8440864399999999,
      "min": 5.10388374494,
      "max": 5.62123384494,
      "times": [
        5.13713560894,
        5.18719618194,
        5.10388374494,
        5.62123384494,
        5.18692151994,
        5.13663381694,
        5.2294232139400005,
        5.17417414394,
        5.15472135294,
        5.27814884994
      ]
    },
    {
      "command": "pnpr@main",
      "mean": 5.18492499984,
      "stddev": 0.08334373162516463,
      "median": 5.14727614244,
      "user": 2.73596446,
      "system": 1.8318604399999994,
      "min": 5.12588020094,
      "max": 5.3719712279400005,
      "times": [
        5.3719712279400005,
        5.13943564394,
        5.15511664094,
        5.22978724994,
        5.13004840794,
        5.12814643794,
        5.2798607229400005,
        5.12588020094,
        5.12782151294,
        5.16118195294
      ]
    }
  ]
}

Scenario: Isolated linker: fresh restore, hot cache + hot store

Command Mean [ms] Min [ms] Max [ms] Relative
pacquet@HEAD 475.4 ± 11.7 460.4 491.7 1.00 ± 0.03
pacquet@main 474.8 ± 7.7 461.6 490.8 1.00
pnpr@HEAD 585.5 ± 17.3 563.5 618.5 1.23 ± 0.04
pnpr@main 580.1 ± 27.2 555.5 648.2 1.22 ± 0.06
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 0.4754310596,
      "stddev": 0.011658042717783822,
      "median": 0.47068201640000007,
      "user": 0.3777297,
      "system": 0.7703764,
      "min": 0.46037816240000007,
      "max": 0.49169700940000005,
      "times": [
        0.46370402540000005,
        0.46762096740000003,
        0.48704259340000006,
        0.4702470174,
        0.46794887240000005,
        0.47111701540000006,
        0.49015210640000006,
        0.48440282640000004,
        0.49169700940000005,
        0.46037816240000007
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 0.47478433100000006,
      "stddev": 0.0077098531229636506,
      "median": 0.47274151490000005,
      "user": 0.37218819999999997,
      "system": 0.7839913999999999,
      "min": 0.46160519740000006,
      "max": 0.4907853394,
      "times": [
        0.47638395340000006,
        0.4758207494,
        0.47210535140000004,
        0.46160519740000006,
        0.47129852940000005,
        0.47190050540000006,
        0.47337767840000006,
        0.48273791540000005,
        0.47182809040000007,
        0.4907853394
      ]
    },
    {
      "command": "pnpr@HEAD",
      "mean": 0.5854582467,
      "stddev": 0.01734355359663726,
      "median": 0.5809292129000001,
      "user": 0.3848217,
      "system": 0.7942684999999999,
      "min": 0.5634538274,
      "max": 0.6185002754000001,
      "times": [
        0.6185002754000001,
        0.6035775874,
        0.5973911454,
        0.5932235844,
        0.5709727784,
        0.5723854314000001,
        0.5771428024,
        0.5847156234,
        0.5732194114000001,
        0.5634538274
      ]
    },
    {
      "command": "pnpr@main",
      "mean": 0.5801106596000001,
      "stddev": 0.02723103229750047,
      "median": 0.5717542054,
      "user": 0.3905674,
      "system": 0.7806403999999999,
      "min": 0.5555244864000001,
      "max": 0.6481742874,
      "times": [
        0.5766116304000001,
        0.6481742874,
        0.5774737714,
        0.6033380064,
        0.5781361644,
        0.5620934154,
        0.5659717284000001,
        0.5668967804,
        0.5668863254000001,
        0.5555244864000001
      ]
    }
  ]
}

Scenario: Isolated linker: fresh install, cold cache + cold store

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 8.443 ± 0.045 8.376 8.528 1.70 ± 0.05
pacquet@main 8.470 ± 0.044 8.369 8.512 1.71 ± 0.05
pnpr@HEAD 4.997 ± 0.135 4.862 5.236 1.01 ± 0.04
pnpr@main 4.966 ± 0.154 4.860 5.374 1.00
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 8.44257299212,
      "stddev": 0.04548567258544682,
      "median": 8.44347679002,
      "user": 3.76306602,
      "system": 2.0749866000000003,
      "min": 8.37585149552,
      "max": 8.52807351252,
      "times": [
        8.37585149552,
        8.44153750652,
        8.44261323752,
        8.44434034252,
        8.38033836852,
        8.45632867852,
        8.48280505252,
        8.52807351252,
        8.41336938652,
        8.460472340519999
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 8.47002705562,
      "stddev": 0.0439457518500649,
      "median": 8.48042834852,
      "user": 3.7628470199999997,
      "system": 2.0683372,
      "min": 8.368825296519999,
      "max": 8.51228067552,
      "times": [
        8.50109150252,
        8.45514532152,
        8.368825296519999,
        8.51228067552,
        8.432426272519999,
        8.51220514552,
        8.49247773252,
        8.49111851052,
        8.469738186519999,
        8.46496191252
      ]
    },
    {
      "command": "pnpr@HEAD",
      "mean": 4.99716597702,
      "stddev": 0.13508784946113978,
      "median": 4.932009692519999,
      "user": 2.5459312199999995,
      "system": 1.7719706000000002,
      "min": 4.862251512519999,
      "max": 5.236142549519999,
      "times": [
        5.236142549519999,
        4.91398035552,
        4.90830146352,
        4.90848627552,
        5.20798817652,
        4.96440003552,
        4.91838519352,
        4.94563419152,
        4.862251512519999,
        5.10609001652
      ]
    },
    {
      "command": "pnpr@main",
      "mean": 4.965971531719999,
      "stddev": 0.1542564087815126,
      "median": 4.89999903802,
      "user": 2.56940422,
      "system": 1.7683042999999998,
      "min": 4.8600676395199995,
      "max": 5.37438342552,
      "times": [
        4.94669727452,
        4.89973300452,
        5.06699912852,
        5.37438342552,
        4.89312230852,
        4.897974340519999,
        4.92911899752,
        4.90026507152,
        4.89135412652,
        4.8600676395199995
      ]
    }
  ]
}

Scenario: Isolated linker: fresh install, hot cache + hot store

Command Mean [ms] Min [ms] Max [ms] Relative
pacquet@HEAD 940.2 ± 13.7 923.0 966.4 1.88 ± 0.04
pacquet@main 954.8 ± 22.0 935.8 1003.6 1.91 ± 0.05
pnpr@HEAD 518.3 ± 50.3 487.0 655.2 1.04 ± 0.10
pnpr@main 500.2 ± 7.2 487.2 511.1 1.00
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 0.9402329691600002,
      "stddev": 0.013660156299153639,
      "median": 0.9345915342600001,
      "user": 1.1209460799999997,
      "system": 1.0010175,
      "min": 0.9230298017600002,
      "max": 0.9663624557600001,
      "times": [
        0.9336982697600001,
        0.9336017847600001,
        0.9354513227600001,
        0.9615601307600001,
        0.9230298017600002,
        0.9387241827600001,
        0.9337317457600001,
        0.9316607077600001,
        0.9445092897600001,
        0.9663624557600001
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 0.9547978451600001,
      "stddev": 0.022000419902687397,
      "median": 0.9490964202600001,
      "user": 1.13677868,
      "system": 1.0014173999999998,
      "min": 0.9358182247600001,
      "max": 1.00360855076,
      "times": [
        0.9531810757600001,
        0.9413393597600002,
        0.9559011287600001,
        0.9829940767600001,
        0.9530744147600001,
        0.9398338957600001,
        0.9371092987600002,
        1.00360855076,
        0.9358182247600001,
        0.9451184257600002
      ]
    },
    {
      "command": "pnpr@HEAD",
      "mean": 0.5183191953600002,
      "stddev": 0.050296205196286074,
      "median": 0.50014782276,
      "user": 0.34068358,
      "system": 0.7567370999999998,
      "min": 0.48702047276000004,
      "max": 0.6551543657600001,
      "times": [
        0.5407601247600001,
        0.49467326676,
        0.49844459776000005,
        0.48702047276000004,
        0.5018510477600001,
        0.49255285176,
        0.6551543657600001,
        0.5069042717600001,
        0.50866967776,
        0.49716127676000005
      ]
    },
    {
      "command": "pnpr@main",
      "mean": 0.5002288044600001,
      "stddev": 0.007198036011321011,
      "median": 0.49930343326000004,
      "user": 0.33890748,
      "system": 0.751375,
      "min": 0.48722304676,
      "max": 0.5111264097600001,
      "times": [
        0.5111264097600001,
        0.49649218376000004,
        0.49345848776,
        0.5082663697600001,
        0.48722304676,
        0.5060538497600001,
        0.49767147676,
        0.5002582147600001,
        0.49834865176000004,
        0.50338935376
      ]
    }
  ]
}

Scenario: Isolated linker: fresh install, cold cache + hot store

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 4.700 ± 0.015 4.672 4.718 9.38 ± 0.16
pacquet@main 4.695 ± 0.012 4.681 4.719 9.37 ± 0.16
pnpr@HEAD 0.501 ± 0.008 0.497 0.524 1.00
pnpr@main 0.502 ± 0.014 0.489 0.540 1.00 ± 0.03
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 4.699605027980001,
      "stddev": 0.015344482929571699,
      "median": 4.701206429380001,
      "user": 1.6220023600000002,
      "system": 1.19980618,
      "min": 4.672187360880001,
      "max": 4.7180461108800005,
      "times": [
        4.7180461108800005,
        4.695075177880001,
        4.69991721488,
        4.672187360880001,
        4.714963277880001,
        4.67571800388,
        4.708996931880001,
        4.698890732880001,
        4.702495643880001,
        4.709759824880001
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 4.695332178180001,
      "stddev": 0.011713476546600163,
      "median": 4.6922056883800005,
      "user": 1.60349596,
      "system": 1.2045819800000002,
      "min": 4.68069381088,
      "max": 4.71939497788,
      "times": [
        4.688368119880001,
        4.71939497788,
        4.69558186588,
        4.688991375880001,
        4.709860007880001,
        4.686612360880001,
        4.69981734188,
        4.688581919880001,
        4.69542000088,
        4.68069381088
      ]
    },
    {
      "command": "pnpr@HEAD",
      "mean": 0.50119142768,
      "stddev": 0.008286558507852712,
      "median": 0.49797917988000007,
      "user": 0.33949156,
      "system": 0.7657788799999999,
      "min": 0.49694215188,
      "max": 0.5241977238800001,
      "times": [
        0.5241977238800001,
        0.49786497488000003,
        0.49809338488000005,
        0.49729440488000004,
        0.5002654118800001,
        0.49694215188,
        0.49965981787999997,
        0.50281498288,
        0.49758739288000003,
        0.49719403088
      ]
    },
    {
      "command": "pnpr@main",
      "mean": 0.50216709938,
      "stddev": 0.014238655522782417,
      "median": 0.49808956688,
      "user": 0.3433136600000001,
      "system": 0.74918478,
      "min": 0.48897060088,
      "max": 0.53963284988,
      "times": [
        0.53963284988,
        0.49367693088000003,
        0.49827266488,
        0.49790646888,
        0.49536156688000005,
        0.49563586788,
        0.5049579408800001,
        0.48897060088,
        0.49913408888,
        0.5081220138800001
      ]
    }
  ]
}

@github-actions

Copy link
Copy Markdown
Contributor

🐰 Bencher Report

Branchpr/12326
Testbedpacquet
Click to view all benchmark results
BenchmarkLatencyBenchmark Result
milliseconds (ms)
(Result Δ%)
Upper Boundary
milliseconds (ms)
(Limit %)
isolated-linker.fresh-install.cold-cache.cold-store📈 view plot
🚷 view threshold
8,442.57 ms
(-8.39%)Baseline: 9,215.58 ms
11,058.69 ms
(76.34%)
isolated-linker.fresh-install.cold-cache.hot-store📈 view plot
🚷 view threshold
4,699.61 ms
(-6.06%)Baseline: 5,002.55 ms
6,003.06 ms
(78.29%)
isolated-linker.fresh-install.hot-cache.hot-store📈 view plot
🚷 view threshold
940.23 ms
(-33.59%)Baseline: 1,415.73 ms
1,698.88 ms
(55.34%)
isolated-linker.fresh-restore.cold-cache.cold-store📈 view plot
🚷 view threshold
10,321.63 ms
(+2.25%)Baseline: 10,094.88 ms
12,113.86 ms
(85.21%)
isolated-linker.fresh-restore.hot-cache.hot-store📈 view plot
🚷 view threshold
475.43 ms
(-27.28%)Baseline: 653.80 ms
784.56 ms
(60.60%)
🐰 View full continuous benchmarking report in Bencher

@github-actions

Copy link
Copy Markdown
Contributor

🐰 Bencher Report

Branchpr/12326
Testbedpnpr

⚠️ WARNING: No Threshold found!

Without a Threshold, no Alerts will ever be generated.

Click here to create a new Threshold
For more information, see the Threshold documentation.
To only post results if a Threshold exists, set the --ci-only-thresholds flag.

Click to view all benchmark results
BenchmarkLatencymilliseconds (ms)
isolated-linker.fresh-install.cold-cache.cold-store📈 view plot
⚠️ NO THRESHOLD
4,997.17 ms
isolated-linker.fresh-install.cold-cache.hot-store📈 view plot
⚠️ NO THRESHOLD
501.19 ms
isolated-linker.fresh-install.hot-cache.hot-store📈 view plot
⚠️ NO THRESHOLD
518.32 ms
isolated-linker.fresh-restore.cold-cache.cold-store📈 view plot
⚠️ NO THRESHOLD
5,220.95 ms
isolated-linker.fresh-restore.hot-cache.hot-store📈 view plot
⚠️ NO THRESHOLD
585.46 ms
🐰 View full continuous benchmarking report in Bencher

@zkochan zkochan marked this pull request as ready for review June 11, 2026 13:58
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 11, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0)

Context used

Grey Divider


Remediation recommended

1. Legacy cache records skipped 🐞 Bug ☼ Reliability
Description
pacquet deserializes JSONL cache lines into a CacheRecord that requires verifiedAt, so cache entries
written before that field existed will fail to parse and be ignored, turning what should be cache
hits (with a timeless cached message) into cache misses and re-verification.
Code

pacquet/crates/lockfile-verification/src/cache.rs[R106-113]

#[derive(Debug, Default, Clone)]
pub struct CacheLookupResult {
    pub hit: bool,
+    /// ISO-8601 timestamp of the verification run the hit is reusing.
+    /// `Some` only on a hit, and only when the record carries a
+    /// non-empty timestamp.
+    pub verified_at: Option<String>,
    pub precomputed: CachePrecomputed,
Evidence
TypeScript’s cache reader explicitly supports cache records where verifiedAt is absent by
normalizing it to an empty string. pacquet’s reader, however, requires verifiedAt during
deserialization and skips lines that fail to parse, so a legacy record without verifiedAt will
never reach the lookup logic that interprets empty timestamps as "absent".

installing/deps-installer/src/install/verifyLockfileResolutionsCache.ts[107-115]
installing/deps-installer/src/install/verifyLockfileResolutionsCache.ts[186-198]
pacquet/crates/lockfile-verification/src/cache.rs[60-76]
pacquet/crates/lockfile-verification/src/cache.rs[273-305]
pacquet/crates/lockfile-verification/src/cache.rs[106-114]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The Rust cache reader (`read_cache`) deserializes each JSONL line into `CacheRecord`, where `verified_at` is a required `String`. If an existing cache file contains records written before the `verifiedAt` field existed, `serde_json::from_str` will fail and the line will be skipped, preventing cache hits and making the newly-added "cached" message fallback for missing timestamps unreachable.

### Issue Context
TypeScript explicitly normalizes missing `verifiedAt` to an empty string and treats it as absence when surfacing `CacheLookupResult.verifiedAt`. pacquet should mirror that behavior for cross-stack cache compatibility.

### Fix Focus Areas
- pacquet/crates/lockfile-verification/src/cache.rs[60-76]
- pacquet/crates/lockfile-verification/src/cache.rs[273-305]

### Suggested fix
- Make `CacheRecord::verified_at` tolerant to missing JSON field by adding a serde default:
 - `#[serde(default, rename = "verifiedAt")] pub verified_at: String`
 - (or) change it to `Option<String>` with appropriate normalization.
- Keep the existing `is_empty()` checks so empty/missing timestamps surface as `None` in `CacheLookupResult` and produce the timeless cached message.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Announce reused lockfile-verification verdicts with a new cached status
✨ Enhancement 🧪 Tests 🕐 20-40 Minutes

Grey Divider

Walkthroughs

Description
• Emit a cached lockfile-verification event when a passing verdict is reused.
• Render a user-visible "verified 2h ago" line in the default reporter.
• Add TS + Rust tests for cache-hit emission, silence rules, and NDJSON shape.
Diagram
graph TD
  A["pnpm: verifyLockfileResolutions"] --> B[("lockfile-verified cache")] --> C["LockfileVerificationMessage: cached"] --> D["pnpm default reporter"]
  E["pacquet: verify_lockfile_resolutions"] --> F[("lockfile-verified cache")] --> C --> G["pacquet reporter (NDJSON)"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Reuse `done` event shape with a new `cached: true` flag
  • ➕ Avoids adding a new discriminator/status in consumers that switch on status
  • ➕ Could allow displaying existing done formatting paths
  • ➖ Ambiguous semantics (a 'done' that did no work) and encourages fake entries/elapsedMs
  • ➖ Harder to keep wire shape stable across TS + Rust without accidental field combinations
2. Always emit cached events (even with no policy verifiers) and let reporters decide
  • ➕ Simplifies emission logic (single behavior for cache hits)
  • ➕ Keeps signal available to custom reporters
  • ➖ Noisy/misleading: shape-only verification runs every install and would look like configured policy checks
  • ➖ Pushes policy-awareness into every reporter/consumer
3. Log cached reuse at `info` level instead of `debug`
  • ➕ Stronger guarantee the message is shown even under selective log pipelines
  • ➖ Potentially breaks expectations for existing pnpm:lockfile-verification consumers that treat it as debug-only telemetry
  • ➖ Increases verbosity for CI/ndjson users without opt-in

Recommendation: Current approach is the best tradeoff: introduce an explicit status: &quot;cached&quot; variant (clear semantics, no synthetic entries/elapsedMs), carry an optional verifiedAt for human-friendly age rendering, and emit only when policy verifiers are active to avoid misleading noise from the always-on shape-only check.

Grey Divider

File Changes

Enhancement (7)
reportLockfileVerification.ts Render 'cached' lockfile-verification events with relative age +16/-0

Render 'cached' lockfile-verification events with relative age

• Adds handling for 'status: "cached"' events, printing a success line that includes a formatted cached-verdict age. Implements 'formatCachedVerdict()' using 'pretty-ms', clamping negative elapsed time and falling back when the timestamp is missing/invalid.

cli/default-reporter/src/reporterForClient/reportLockfileVerification.ts


lockfileVerificationLogger.ts Add 'cached' variant to lockfile-verification logger message type +18/-1

Add 'cached' variant to lockfile-verification logger message type

• Extends 'LockfileVerificationMessage' with a 'cached' status variant carrying optional 'verifiedAt'. Documents semantics: emitted instead of started/done when a prior passing verdict is reused, with no 'entries' count.

core/core-loggers/src/lockfileVerificationLogger.ts


verifyLockfileResolutions.ts Emit 'cached' event on verification cache hits when policies are active +17/-3

Emit 'cached' event on verification cache hits when policies are active

• On cache-hit short-circuit, emits a 'pnpm:lockfile-verification' debug event with 'status: "cached"', optional 'verifiedAt', and 'lockfilePath'. Keeps the always-on shape-only verification silent by only emitting when policy verifiers are present.

installing/deps-installer/src/install/verifyLockfileResolutions.ts


verifyLockfileResolutionsCache.ts Surface 'verifiedAt' on cache hits from both lookup paths +11/-2

Surface 'verifiedAt' on cache hits from both lookup paths

• Extends 'CacheLookupResult' with optional 'verifiedAt'. Populates it for stat-shortcut hits and content-hash hits, normalizing pre-field records to 'undefined' rather than an empty string.

installing/deps-installer/src/install/verifyLockfileResolutionsCache.ts


cache.rs Add 'verified_at' to Rust cache lookup result +11/-1

Add 'verified_at' to Rust cache lookup result

• Extends 'CacheLookupResult' with 'verified_at: Option<String>' and threads it through both stat-shortcut and hash-based lookup paths, omitting empty timestamps.

pacquet/crates/lockfile-verification/src/cache.rs


verify_lockfile_resolutions.rs Emit 'Cached' lockfile-verification message on cache-hit short-circuit +18/-2

Emit 'Cached' lockfile-verification message on cache-hit short-circuit

• When the verification cache hits and policy verifiers are present, emits a single 'LockfileVerificationMessage::Cached' event (with 'verified_at' and optional 'lockfile_path') and returns early instead of emitting Started/Done.

pacquet/crates/lockfile-verification/src/verify_lockfile_resolutions.rs


lib.rs Add 'Cached' variant to pacquet reporter wire message +13/-1

Add 'Cached' variant to pacquet reporter wire message

• Introduces 'LockfileVerificationMessage::Cached' with camelCase 'verifiedAt' and 'lockfilePath', both omitted when 'None'. Documents that cached events replace Started/Done and intentionally omit 'entries'/'elapsedMs'.

pacquet/crates/reporter/src/lib.rs


Tests (5)
reportingLockfileVerification.ts Test cached-verdict reporter output and fallback behavior +36/-0

Test cached-verdict reporter output and fallback behavior

• Adds tests asserting that cached events render as "verified 2h ago" when 'verifiedAt' is present, and as "previously verified" when it is absent.

cli/default-reporter/test/reportingLockfileVerification.ts


verifyLockfileResolutions.ts Add tests for cached-event emission and silence rule +59/-1

Add tests for cached-event emission and silence rule

• Adds Jest coverage verifying that a cache short-circuit emits exactly one 'cached' event (with a timestamp) when policy verifiers are active, and emits nothing when no policy verifiers are provided.

installing/deps-installer/test/install/verifyLockfileResolutions.ts


tests.rs Test 'Cached' event emission and silence when no verifiers +100/-0

Test 'Cached' event emission and silence when no verifiers

• Adds tokio tests ensuring a cache hit with active verifiers emits exactly one 'Cached' event carrying a timestamp, and that cache hits remain silent when verifiers are empty.

pacquet/crates/lockfile-verification/src/verify_lockfile_resolutions/tests.rs


tests.rs Update install test to expect a single 'Cached' verification message +12/-5

Update install test to expect a single 'Cached' verification message

• Adjusts an existing install test that previously asserted no lockfile-verification events on cache hit. It now asserts the presence of exactly one 'LockfileVerificationMessage::Cached' and no fan-out events.

pacquet/crates/package-manager/src/install/tests.rs


tests.rs Add NDJSON wire-shape tests for cached lockfile-verification events +49/-0

Add NDJSON wire-shape tests for cached lockfile-verification events

• Adds serialization tests asserting that 'cached' emits 'status', 'verifiedAt', and 'lockfilePath' with no 'entries/elapsedMs', and that 'verifiedAt' is omitted (not 'null') when absent.

pacquet/crates/reporter/src/tests.rs


Other (1)
lockfile-verification-cached-message.md Add changeset documenting cached-verdict message +8/-0

Add changeset documenting cached-verdict message

• Introduces a changeset describing the new user-facing line shown when a cached lockfile-verification verdict is reused. Bumps affected packages appropriately.

.changeset/lockfile-verification-cached-message.md


Grey Divider

Qodo Logo

@zkochan zkochan merged commit f11b4fc into main Jun 11, 2026
27 of 28 checks passed
@zkochan zkochan deleted the lockfile-verification-cached-message branch June 11, 2026 15:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

pnpm update should test trustPolicy

2 participants