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

chore(reporter): backfill missing log events in already-ported code #347

@zkochan

Description

@zkochan

Summary

Pacquet has ported a fair amount of code from pnpm without ever emitting
log events, since the reporter didn't exist. With the reporting engine
(#345) and the porting convention (#346) in place, every existing call
site that should emit needs to be wired up. This issue tracks the sweep.

The "Channels in scope" section below is the concrete audit that
replaces the rough "Likely targets" list this issue originally
contained. Each PR ticks a box.

Per-PR conventions

Each backfill PR lands one channel, end-to-end:

  1. Add the variant. Extend the LogEvent enum in crates/reporter
    with the channel and its payload. Mirror the upstream TS shape from
    @pnpm/core-loggers byte-for-byte (camelCase field names, status
    string sets, etc.) so @pnpm/cli.default-reporter accepts it.
  2. Reference the upstream permalink (pinned SHA per the rule in
    AGENTS.md) for the emit site you're matching, in code comments,
    the commit message, and the PR description.
  3. Wire the emit site. Find the pacquet location that mirrors the
    upstream call. Thread R: Reporter through the call chain if it
    doesn't already reach there; the trait monomorphises away, so the
    ergonomic cost is a turbofish at the production entry point and a
    generic on intermediate fns. See the engine PR (feat(reporter): implement the reporting engine (NDJSON + Reporter trait) #345) for the
    pattern.
  4. Throttle on the emit side for high-volume channels
    (pnpm:fetching-progress, pnpm:progress) — pnpm's reporter
    coalesces per-package events to 200ms, so emitting every byte of
    progress wastes serialization and pipe cost.
  5. Test. Use the recording-fake DI pattern from Refactor and document the dependency injection pattern for tests #339: a unit-struct
    reporter declared inside the #[test] body, recording into a
    static Mutex<Vec<LogEvent>> declared in the same body. Assert the
    captured sequence.
  6. Verify the test catches the regression. Temporarily delete the
    emit site, run the test, confirm it fails for the right reason, then
    restore. Same discipline plans/TEST_PORTING.md calls for.
  7. Check off the item below as part of the PR.

Upstream paths in this audit are relative to the pnpm/pnpm repo.
Permalinks pin commit 086c5e91e8 so they don't drift; resolve a fresh
SHA with git ls-remote https://github.com/pnpm/pnpm.git refs/heads/main
and update this issue when re-auditing.

Channels in scope

Ordered roughly by user-visible value: download progress and per-package
status are what users see first when running pacquet install, so they
land before counts and summary.

pnpm:fetching-progress — tarball download progress

pnpm:progress — per-package status transitions

  • Upstream type:
    core/core-loggers/src/progressLogger.ts.
    Status values: resolved, fetched, found_in_store, imported.
    The first three carry { packageId, requester }; imported carries
    { method, requester, to }.
  • Upstream emits:
  • Pacquet sites (as shipped in feat(reporter): emit pnpm:progress and pnpm:fetching-progress #372):
    • resolvedinstall_package_from_registry::run (no-lockfile
      path, after the registry picks a version) and
      install_package_by_snapshot::run + create_virtual_store::run's
      warm batch (frozen-lockfile path, per snapshot — the lockfile
      is the resolution).
    • fetchedfetch_and_extract_with_retry on the successful
      Ok branch.
    • found_in_store — three sites in crates/tarball/src/lib.rs:
      the prefetched-cas hit and load_cached_cas_paths hit in
      run_without_mem_cache, plus the in-process mem_cache dedup
      hit in run_with_mem_cache (so the second requester of a
      shared URL also ticks the per-package counter), and from
      create_virtual_store::run's warm batch.
    • imported — after create_cas_files returns Ok in
      create_virtual_dir_by_snapshot::run and
      install_package_from_registry::install_package_version.
  • Notes: requester is the project root, threaded down from
    Install::run as &'a str on every install-layer struct
    (matches the prefix in pnpm:stage). imported.method is
    best-effort — pacquet doesn't surface the per-package resolved
    method past link_file's install-scoped atomic, so the optimistic
    configured-method mapping (Auto/CloneOrCopyclone,
    explicit settings as-is) is what's emitted today. Refining to
    per-package resolution would require threading the resolved
    method back from link_file; tracked as a separate follow-up
    here.
  • Backfill pnpm:progress (resolved, fetched,
    found_in_store, imported) (landed in feat(reporter): emit pnpm:progress and pnpm:fetching-progress #372).

pnpm:stageresolution_started / resolution_done (deferred)

  • Upstream type:
    core/core-loggers/src/stageLogger.ts.
    importing_started / importing_done already wired in feat(reporter): implement the reporting engine (NDJSON + Reporter trait) #345.
  • Upstream emits:
  • Pacquet target: would live in
    crates/package-manager/src/install_without_lockfile.rs. Deferred —
    pacquet's no-lockfile path interleaves resolution and import per
    dependency (each async move block fetches metadata, downloads, and
    links in one pass), so there is no clean phase boundary to bracket.
    Emitting resolution_done after import work has already happened
    would interleave with pnpm:progress fetched/imported events for
    individual packages and confuse the reporter's per-package state.
    Frozen-lockfile installs (pacquet's primary path today) don't need
    these events — pnpm itself skips them in deps-restorer, where the
    lockfile is the resolution.
  • Pick this back up once pacquet's no-lockfile flow gains a separate
    resolution pass (resolve every dependency first, then import). Until
    then, pnpm:stage is importing_started / importing_done only.
  • Backfill pnpm:stage resolution_started / resolution_done
    (blocked on phase-separated resolution).

pnpm:context — install start metadata

pnpm:stats — added / removed counts

  • Upstream type:
    core/core-loggers/src/statsLogger.ts.
    Two payloads: { prefix, added } and { prefix, removed }.
  • Upstream emits:
    installing/deps-installer/src/install/link.ts
    and
    installing/deps-restorer/src/index.ts
    emit after the link phase completes.
  • Pacquet target:
    crates/package-manager/src/create_virtual_store.rs — count packages
    newly linked vs. removed in the cleanup pass.
  • Notes: removed is currently always 0 because pacquet doesn't prune
    yet. Emit removed: 0 to keep the wire shape stable; revisit when
    pruning lands.
  • Backfill pnpm:stats (landed prior; Added + Removed emits at crates/package-manager/src/create_virtual_store.rs:435-445, recording-fake assertions in crates/package-manager/src/install/tests.rs:493-513).

pnpm:summary — end-of-install summary

pnpm:package-import-method — clone / hardlink / copy decision

  • Upstream type:
    core/core-loggers/src/packageImportMethodLogger.ts.
    Payload: { method: 'clone' | 'hardlink' | 'copy' }.
  • Upstream emit:
    fs/indexed-pkg-importer/src/index.ts:32
    fires once when the importer is constructed; the auto branch fires
    the actual method that succeeded.
  • Pacquet site (as shipped): crates/package-manager/src/link_file.rs's
    log_method_once helper. Fires the first time each resolved method
    succeeds during an install, so for Auto and CloneOrCopy the wire
    value is the post-fallback method, not the optimistic configured
    one. Up to three events per install (one per resolved method),
    gated by an install-scoped AtomicU8 threaded down from
    Install::run.
  • Backfill pnpm:package-import-method (landed in feat(reporter): emit pnpm:package-import-method from link_file #356).

pnpm:request-retry — HTTP retry events

  • Upstream type:
    core/core-loggers/src/requestRetryLogger.ts.
    Payload: { attempt, error, maxRetries, method, timeout, url }.
  • Upstream emits:
  • Pacquet targets:
    • Tarball — crates/tarball/src/lib.rs's retry driver
      (fetch_and_extract_once callers, around the existing
      RetryOpts).
    • Registry / generic — crates/registry/src/package_distribution.rs
      and crates/registry/src/package_version.rs. Pacquet currently
      has no retry loop in those paths; the channel only fires when
      retries happen, so wire it once retries land. Track the retry-loop
      work separately if needed.
  • Notes: error is a structured object, not a string. Mirror the field
    set (name, message, status, code, errno) — the reporter
    decides which to display.
  • Backfill pnpm:request-retry (tarball path) (landed prior; emit at crates/tarball/src/lib.rs:1690 mirroring upstream remoteTarballFetcher.ts:106, recording-fake test at crates/tarball/src/tests.rs::request_retry_event_fires_per_retried_attempt).
  • Backfill pnpm:request-retry (registry path; gated on retry-loop
    parity).

pnpm:lifecycle — script execution

  • Upstream type:
    core/core-loggers/src/lifecycleLogger.ts.
    Three payload shapes: stdio ({ depPath, stage, wd, line, stdio }),
    exit ({ depPath, stage, wd, exitCode, optional }), script
    ({ depPath, stage, wd, script, optional }).
  • Upstream emit:
    exec/lifecycle/src/runLifecycleHook.ts
    emits script at start, streams stdio per output line, and emits
    exit at the end.
  • Pacquet target: crates/executor/src/lib.rs's execute_shell is
    the current entry point but it's too thin for streaming output. This
    channel needs the executor to grow line-by-line capture — track
    separately as part of the lifecycle-scripts feature.
  • Notes: stage here means the npm-script name (preinstall,
    postinstall, …), not the install pipeline phase. Don't confuse
    with pnpm:stage.
  • Backfill pnpm:lifecycle (gated on streaming-executor work).

pnpm:root — top-level dependency add / remove

  • Upstream type:
    core/core-loggers/src/rootLogger.ts.
    Payload: { prefix, added: { id, name, realName, version, dependencyType, latest, linkedFrom } }
    or { prefix, removed: { name, version, dependencyType } }.
  • Upstream emit:
    installing/linking/direct-dep-linker/src/linkDirectDeps.ts.
  • Pacquet target:
    crates/package-manager/src/symlink_direct_dependencies.rs fires
    after each direct symlink is created.
  • Notes: powers the "+N -M" output the user sees at the end of an
    install, paired with pnpm:summary.
  • Backfill pnpm:root (landed prior; per-direct-dep emit at crates/package-manager/src/symlink_direct_dependencies.rs:417, dedicated test file at crates/package-manager/src/symlink_direct_dependencies/tests.rs).

pnpm:link — symlink to store

pnpm:package-manifest — manifest read / write

  • Upstream type:
    core/core-loggers/src/packageManifestLogger.ts.
    Two payloads: { prefix, initial } and { prefix, updated }.
  • Upstream emit:
    installing/context/src/index.ts
    emits initial at install start; deps-installer / restorer emit
    updated after pacquet add mutates the manifest.
  • Pacquet targets:
    • initialInstall::run entry, alongside the pnpm:context
      emit.
    • updatedAdd::run after the manifest has been re-saved.
  • Backfill pnpm:package-manifest (landed prior; initial emit in crates/package-manager/src/install.rs:185 alongside pnpm:context, updated emit in crates/package-manager/src/add.rs:120).

Channels gated on feature work

These channels exist in @pnpm/core-loggers but only fire from code
paths pacquet hasn't built yet. They land alongside the feature, not
as part of this sweep:

  • pnpm:install-check — fires from
    config/package-is-installable;
    pacquet doesn't enforce engine / cpu / os filters yet.
  • pnpm:peer-dependency-issues — peer-dep resolution.
  • pnpm:deprecation — fires when the resolver sees a deprecated
    package; pacquet doesn't surface deprecation metadata yet.
  • pnpm:hook.pnpmfile.cjs hook execution. No pnpmfile
    support yet.
  • pnpm:scope — workspace scope. No workspace support yet.
  • pnpm:ignored-scripts--ignore-scripts and the
    build-allow-list. Gated on lifecycle-script support.
  • pnpm:installing-config-deps — config-dep installation;
    pacquet doesn't have a config-deps feature.
  • pnpm:update-check — version-update notifier; pacquet has no
    self-update path.
  • pnpm:execution-time — overall install timing; nice-to-have,
    no hard dependency, but not a parity blocker.
  • pnpm:registry — never directly emitted upstream;
    @pnpm/logger exposes the channel but no live caller writes to it.
    Skip.

Out of scope

  • Channels for code paths pacquet doesn't yet implement
    (pnpm:peer-dependency-issues, pnpm:deprecation, pnpm:hook,
    pnpm:ignored-scripts, pnpm:installing-config-deps,
    pnpm:update-check, etc.). Those land alongside the features they
    belong to, not as part of this sweep — see "Channels gated on
    feature work" above.

Depends on

Parent

Design: #344.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions