You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on May 14, 2026. It is now read-only.
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:
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.
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.
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.
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.
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.
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.
Pacquet site (as shipped): crates/tarball/src/lib.rs's fetch_and_extract_once. started fires once per HTTP attempt
(around send().await, so DNS / connect / timeout failures still
emit one). size is the response's Content-Length when a head
arrives, JSON null otherwise. in_progress is gated to match
upstream pnpm exactly
(remoteTarballFetcher.ts:143):
skip when Content-Length is unknown or the tarball is smaller
than BIG_TARBALL_SIZE = 5 MB; otherwise throttle at 500ms with
leading + trailing edges (the original audit's 200ms note was
wrong).
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 }.
resolved — install_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).
fetched — fetch_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/CloneOrCopy → clone,
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.
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:stageresolution_started / resolution_done (blocked on phase-separated resolution).
Pacquet target: Install::run in crates/package-manager/src/install.rs, immediately before the
existing importing_started emit.
Notes: currentLockfileExists should reflect node_modules/.pnpm/lock.yaml once that's being read/written; until
then, hard-code false and add a TODO so the emit doesn't go stale.
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
Upstream type: core/core-loggers/src/summaryLogger.ts.
Payload: { prefix }. The reporter combines this with the
accumulated pnpm:root events to render the final "+N -M" block.
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.
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).
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).
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).
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:
initial — Install::run entry, alongside the pnpm:context
emit.
updated — Add::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: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.
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:
LogEventenum incrates/reporterwith the channel and its payload. Mirror the upstream TS shape from
@pnpm/core-loggersbyte-for-byte (camelCase field names,statusstring sets, etc.) so
@pnpm/cli.default-reporteraccepts it.AGENTS.md) for the emit site you're matching, in code comments,the commit message, and the PR description.
upstream call. Thread
R: Reporterthrough the call chain if itdoesn'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.
(
pnpm:fetching-progress,pnpm:progress) — pnpm's reportercoalesces per-package events to 200ms, so emitting every byte of
progress wastes serialization and pipe cost.
reporter declared inside the
#[test]body, recording into astatic Mutex<Vec<LogEvent>>declared in the same body. Assert thecaptured sequence.
emit site, run the test, confirm it fails for the right reason, then
restore. Same discipline
plans/TEST_PORTING.mdcalls for.Upstream paths in this audit are relative to the
pnpm/pnpmrepo.Permalinks pin commit
086c5e91e8so they don't drift; resolve a freshSHA with
git ls-remote https://github.com/pnpm/pnpm.git refs/heads/mainand 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 theyland before counts and summary.
pnpm:fetching-progress— tarball download progresscore/core-loggers/src/fetchingProgressLogger.ts.Two payloads:
{ status: 'started', attempt, packageId, size }and{ status: 'in_progress', downloaded, packageId }.installing/package-requester/src/packageRequester.ts:560fires
startedonce per fetch attempt andin_progressfor eachchunk.
crates/tarball/src/lib.rs'sfetch_and_extract_once.startedfires once per HTTP attempt(around
send().await, so DNS / connect / timeout failures stillemit one).
sizeis the response'sContent-Lengthwhen a headarrives, JSON
nullotherwise.in_progressis gated to matchupstream pnpm exactly
(
remoteTarballFetcher.ts:143):skip when
Content-Lengthis unknown or the tarball is smallerthan
BIG_TARBALL_SIZE = 5 MB; otherwise throttle at 500ms withleading + trailing edges (the original audit's 200ms note was
wrong).
pnpm:fetching-progress(landed in feat(reporter): emit pnpm:progress and pnpm:fetching-progress #372).pnpm:progress— per-package status transitionscore/core-loggers/src/progressLogger.ts.Status values:
resolved,fetched,found_in_store,imported.The first three carry
{ packageId, requester };importedcarries{ method, requester, to }.resolved—installing/deps-resolver/src/resolveDependencies.ts:1586.fetched/found_in_store—installing/package-requester/src/packageRequester.ts:435.imported—installing/deps-installer/src/install/link.ts:498.resolved—install_package_from_registry::run(no-lockfilepath, after the registry picks a version) and
install_package_by_snapshot::run+create_virtual_store::run'swarm batch (frozen-lockfile path, per snapshot — the lockfile
is the resolution).
fetched—fetch_and_extract_with_retryon the successfulOkbranch.found_in_store— three sites incrates/tarball/src/lib.rs:the prefetched-cas hit and
load_cached_cas_pathshit inrun_without_mem_cache, plus the in-processmem_cachededuphit in
run_with_mem_cache(so the second requester of ashared URL also ticks the per-package counter), and from
create_virtual_store::run's warm batch.imported— aftercreate_cas_filesreturns Ok increate_virtual_dir_by_snapshot::runandinstall_package_from_registry::install_package_version.requesteris the project root, threaded down fromInstall::runas&'a stron every install-layer struct(matches the
prefixinpnpm:stage).imported.methodisbest-effort — pacquet doesn't surface the per-package resolved
method past
link_file's install-scoped atomic, so the optimisticconfigured-method mapping (
Auto/CloneOrCopy→clone,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-uphere.
pnpm:progress(resolved,fetched,found_in_store,imported) (landed in feat(reporter): emit pnpm:progress and pnpm:fetching-progress #372).pnpm:stage—resolution_started/resolution_done(deferred)core/core-loggers/src/stageLogger.ts.importing_started/importing_donealready wired in feat(reporter): implement the reporting engine (NDJSON + Reporter trait) #345.resolution_started—installing/deps-installer/src/install/index.ts:1232.resolution_done—installing/deps-installer/src/install/index.ts:1375.crates/package-manager/src/install_without_lockfile.rs. Deferred —pacquet's no-lockfile path interleaves resolution and import per
dependency (each
async moveblock fetches metadata, downloads, andlinks in one pass), so there is no clean phase boundary to bracket.
Emitting
resolution_doneafter import work has already happenedwould interleave with
pnpm:progress fetched/importedevents forindividual 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 thelockfile is the resolution.
resolution pass (resolve every dependency first, then import). Until
then,
pnpm:stageisimporting_started/importing_doneonly.pnpm:stageresolution_started/resolution_done(blocked on phase-separated resolution).
pnpm:context— install start metadatacore/core-loggers/src/contextLogger.ts.Payload:
{ currentLockfileExists, storeDir, virtualStoreDir }.installing/context/src/index.ts:196(also fires from a second site in the same file at
:359).Install::runincrates/package-manager/src/install.rs, immediately before theexisting
importing_startedemit.currentLockfileExistsshould reflectnode_modules/.pnpm/lock.yamlonce that's being read/written; untilthen, hard-code
falseand add a TODO so the emit doesn't go stale.pnpm:context(landed in feat(reporter): emit pnpm:context at install start #354).pnpm:stats— added / removed countscore/core-loggers/src/statsLogger.ts.Two payloads:
{ prefix, added }and{ prefix, removed }.installing/deps-installer/src/install/link.tsand
installing/deps-restorer/src/index.tsemit after the link phase completes.
crates/package-manager/src/create_virtual_store.rs— count packagesnewly linked vs. removed in the cleanup pass.
removedis currently always 0 because pacquet doesn't pruneyet. Emit
removed: 0to keep the wire shape stable; revisit whenpruning lands.
pnpm:stats(landed prior;Added+Removedemits atcrates/package-manager/src/create_virtual_store.rs:435-445, recording-fake assertions incrates/package-manager/src/install/tests.rs:493-513).pnpm:summary— end-of-install summarycore/core-loggers/src/summaryLogger.ts.Payload:
{ prefix }. The reporter combines this with theaccumulated
pnpm:rootevents to render the final "+N -M" block.installing/deps-installer/src/install/index.tsand
installing/deps-restorer/src/index.ts.Install::runafter the existingimporting_doneemit.
pnpm:rootemits so the reporter can renderthe diff block.
pnpm:summary(landed in feat(reporter): emit pnpm:summary at install end #355).pnpm:package-import-method— clone / hardlink / copy decisioncore/core-loggers/src/packageImportMethodLogger.ts.Payload:
{ method: 'clone' | 'hardlink' | 'copy' }.fs/indexed-pkg-importer/src/index.ts:32fires once when the importer is constructed; the
autobranch firesthe actual method that succeeded.
crates/package-manager/src/link_file.rs'slog_method_oncehelper. Fires the first time each resolved methodsucceeds during an install, so for
AutoandCloneOrCopythe wirevalue 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
AtomicU8threaded down fromInstall::run.pnpm:package-import-method(landed in feat(reporter): emit pnpm:package-import-method from link_file #356).pnpm:request-retry— HTTP retry eventscore/core-loggers/src/requestRetryLogger.ts.Payload:
{ attempt, error, maxRetries, method, timeout, url }.fetching/tarball-fetcher/src/remoteTarballFetcher.ts:106.network/fetch/src/fetch.ts:90.resolving/npm-resolver/src/fetch.ts.crates/tarball/src/lib.rs's retry driver(
fetch_and_extract_oncecallers, around the existingRetryOpts).crates/registry/src/package_distribution.rsand
crates/registry/src/package_version.rs. Pacquet currentlyhas 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.
erroris a structured object, not a string. Mirror the fieldset (
name,message,status,code,errno) — the reporterdecides which to display.
pnpm:request-retry(tarball path) (landed prior; emit atcrates/tarball/src/lib.rs:1690mirroring upstreamremoteTarballFetcher.ts:106, recording-fake test atcrates/tarball/src/tests.rs::request_retry_event_fires_per_retried_attempt).pnpm:request-retry(registry path; gated on retry-loopparity).
pnpm:lifecycle— script executioncore/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 }).exec/lifecycle/src/runLifecycleHook.tsemits
scriptat start, streamsstdioper output line, and emitsexitat the end.crates/executor/src/lib.rs'sexecute_shellisthe 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.
stagehere means the npm-script name (preinstall,postinstall, …), not the install pipeline phase. Don't confusewith
pnpm:stage.pnpm:lifecycle(gated on streaming-executor work).pnpm:root— top-level dependency add / removecore/core-loggers/src/rootLogger.ts.Payload:
{ prefix, added: { id, name, realName, version, dependencyType, latest, linkedFrom } }or
{ prefix, removed: { name, version, dependencyType } }.installing/linking/direct-dep-linker/src/linkDirectDeps.ts.crates/package-manager/src/symlink_direct_dependencies.rsfiresafter each direct symlink is created.
install, paired with
pnpm:summary.pnpm:root(landed prior; per-direct-dep emit atcrates/package-manager/src/symlink_direct_dependencies.rs:417, dedicated test file atcrates/package-manager/src/symlink_direct_dependencies/tests.rs).pnpm:link— symlink to storecore/core-loggers/src/linkLogger.ts.Payload:
{ target, link }.fs/symlink-dependency/src/index.ts.crates/package-manager/src/symlink_package.rsandlink_file.rs.default in pnpm. Backfill last.
pnpm:link.pnpm:package-manifest— manifest read / writecore/core-loggers/src/packageManifestLogger.ts.Two payloads:
{ prefix, initial }and{ prefix, updated }.installing/context/src/index.tsemits
initialat install start; deps-installer / restorer emitupdatedafterpacquet addmutates the manifest.initial—Install::runentry, alongside thepnpm:contextemit.
updated—Add::runafter the manifest has been re-saved.pnpm:package-manifest(landed prior;initialemit incrates/package-manager/src/install.rs:185alongsidepnpm:context,updatedemit incrates/package-manager/src/add.rs:120).Channels gated on feature work
These channels exist in
@pnpm/core-loggersbut only fire from codepaths pacquet hasn't built yet. They land alongside the feature, not
as part of this sweep:
pnpm:install-check— fires fromconfig/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 deprecatedpackage; pacquet doesn't surface deprecation metadata yet.
pnpm:hook—.pnpmfile.cjshook execution. No pnpmfilesupport yet.
pnpm:scope— workspace scope. No workspace support yet.pnpm:ignored-scripts—--ignore-scriptsand thebuild-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 noself-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/loggerexposes the channel but no live caller writes to it.Skip.
Out of scope
(
pnpm:peer-dependency-issues,pnpm:deprecation,pnpm:hook,pnpm:ignored-scripts,pnpm:installing-config-deps,pnpm:update-check, etc.). Those land alongside the features theybelong to, not as part of this sweep — see "Channels gated on
feature work" above.
Depends on
Parent
Design: #344.