Skip to content

fix(pacquet/modules-yaml): write GVS-aware virtualStoreDir to match pnpm#11896

Merged
zkochan merged 2 commits into
mainfrom
fix/pacquet-modules-yaml-virtual-store-dir-gvs
May 24, 2026
Merged

fix(pacquet/modules-yaml): write GVS-aware virtualStoreDir to match pnpm#11896
zkochan merged 2 commits into
mainfrom
fix/pacquet-modules-yaml-virtual-store-dir-gvs

Conversation

@zkochan

@zkochan zkochan commented May 24, 2026

Copy link
Copy Markdown
Member

Summary

Under `enableGlobalVirtualStore: true`, upstream pnpm mutates `virtualStoreDir` in place at `extendInstallOptions.ts:419-422` so every consumer that reads `ctx.virtualStoreDir` — including `writeModulesManifest` and the `pnpm:context` debug log — sees the GVS-derived `/v11/links` path.

Pacquet kept `Config::virtual_store_dir` at its project-local value (deliberately, see `apply_global_virtual_store_derivation`'s rationale) and wrote that field straight into `.modules.yaml` and the context log. With `pnpm install` delegating to pacquet via `configDependencies: @pnpm/pacquet`, every run came back through pnpm's `checkCompatibility`, the recorded project-local `virtualStoreDir` didn't match the GVS-mutated value pnpm computed, and per-importer purges fired the "modules directories will be reinstalled from scratch" prompt on every install.

This was reproducible in this repo: `pnpm-workspace.yaml` declares both `enableGlobalVirtualStore: true` and `configDependencies: '@pnpm/pacquet': 0.2.7`. Aborting the prompt (e.g. in CI without a TTY) failed with `ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY`.

Fix

Route both externally-visible consumers — `build_modules_manifest` and the `pnpm:context` emission — through a new `Config::effective_virtual_store_dir` helper that returns `global_virtual_store_dir` when GVS is on (which already encodes "user pinned or fall back to `/links`" via `apply_global_virtual_store_derivation`) and the project-local `virtual_store_dir` otherwise.

Pacquet's internal layout consumers still read the field directly — the deliberate divergence the helper documents lives only at the parity boundary.

Test plan

  • New `gvs_persists_global_virtual_store_dir_in_modules_yaml_and_context_log` pins both halves: `.modules.yaml` round-trips to `/v11/links` under GVS, and the `pnpm:context` event reports the same path.
  • Verified the test fails when either consumer is reverted to the buggy form (the assertion fires on each leg in turn).
  • `cargo nextest run -p pacquet-package-manager` — 2007/2007 pass.
  • `just check` / `cargo fmt --check` / `just lint` clean.

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

Summary by CodeRabbit

  • Bug Fixes

    • Fixed virtual store directory reporting so installs now record the effective virtual store path consistently in .modules.yaml and context logs when global virtual store mode is enabled.
  • Tests

    • Added/updated integration tests to verify the effective virtual store path is persisted and canonicalized consistently across manifests and emitted logs.

Review Change Stack

Under `enableGlobalVirtualStore: true`, upstream pnpm mutates
`virtualStoreDir` in place at `extendInstallOptions.ts:419-422` so
every consumer that reads `ctx.virtualStoreDir` — including
`writeModulesManifest` and the `pnpm:context` debug log — sees the
GVS-derived `<storeDir>/v11/links` path.

Pacquet kept `Config::virtual_store_dir` at its project-local value
(deliberately, see `apply_global_virtual_store_derivation`'s rationale)
and wrote that field straight into `.modules.yaml` and the context
log. With `pnpm install` delegating to pacquet via `configDependencies`,
every run came back through pnpm's `checkCompatibility`, the recorded
project-local `virtualStoreDir` didn't match the GVS-mutated value
pnpm computed, and per-importer purges fired the
"modules directories will be reinstalled from scratch" prompt on
every install.

Route both externally-visible consumers through a new
`Config::effective_virtual_store_dir` helper that returns
`global_virtual_store_dir` when GVS is on (which already encodes
"user pinned or fall back to `<storeDir>/links`" via
`apply_global_virtual_store_derivation`) and the project-local
`virtual_store_dir` otherwise. Pacquet's internal layout consumers
still read the field directly — the divergence the helper bridges
is only at the parity boundary.

Test pins both halves: `.modules.yaml` round-trips to
`<storeDir>/v11/links` under GVS, and the `pnpm:context` event
reports the same path.
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@coderabbitai

coderabbitai Bot commented May 24, 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: f1b99dcc-dfae-4f0a-8e94-c404d03d6b22

📥 Commits

Reviewing files that changed from the base of the PR and between 3d8ce4a and b018277.

📒 Files selected for processing (1)
  • pacquet/crates/store-dir/src/store_dir/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: Run benchmark on ubuntu-latest
  • GitHub Check: Run benchmark on ubuntu-latest
  • GitHub Check: Code Coverage
  • GitHub Check: Lint and Test (ubuntu-latest)
  • GitHub Check: Lint and Test (windows-latest)
  • GitHub Check: Lint and Test (macos-latest)
  • GitHub Check: Compile & Lint
🧰 Additional context used
📓 Path-based instructions (1)
pacquet/**/*.rs

📄 CodeRabbit inference engine (pacquet/AGENTS.md)

pacquet/**/*.rs: When porting a function that fires pnpm:<channel> events through globalLogger, logger.debug(), or streamParser.write(), mirror the call site, payload, and ordering so the reporter parses pacquet's NDJSON the same way it parses pnpm's.
Declare a newtype wrapper for branded string types. Do not collapse the brand into a plain String or &str.
If upstream always validates before construction, validate in pacquet's wrapper too. The wrapper must construct only via TryFrom<String> and/or FromStr. Do not provide an infallible public constructor.
If upstream never validates, just brand for type-safety. Expose an infallible From<String> (and From<&str> when convenient).
If upstream occasionally constructs without validation, expose from_str_unchecked as an escape hatch alongside the validating constructor.
Match upstream serde behavior for branded types that cross JSON, YAML, or INI boundaries. Use #[serde(try_from = "String")] for deserialization and #[serde(into = "String")] for serialization.
Use #[derive(derive_more::From)] and #[derive(derive_more::Into)] for mechanical conversion impls. Fall back to manual impl only when conversion needs custom logic.
String-literal unions should become enums, not newtype wrappers. Model closed sets of valid string values as enums.
Template literal types should be treated as branded strings with validation discipline from rules 2-5.
Choose owned vs. borrowed parameters to minimize copies. Widen to the most encompassing type (&Path over &PathBuf, &str over &String) when it doesn't force extra copies.
Prefer Arc::clone(&x) / Rc::clone(&x) over x.clone() for reference-counted types, so the cost is visible at the call site.
Follow Rust API Guidelines for naming conventions.
Do not use star imports inside module bodies. Write use super::{Foo, bar} instead of use super::*;. Two forms stay allowed: external-crate preludes like use rayon::prelude::*; and root-of-module re-...

Files:

  • pacquet/crates/store-dir/src/store_dir/tests.rs
🧠 Learnings (3)
📚 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/store-dir/src/store_dir/tests.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/store-dir/src/store_dir/tests.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/store-dir/src/store_dir/tests.rs
🔇 Additional comments (1)
pacquet/crates/store-dir/src/store_dir/tests.rs (1)

48-56: LGTM!

Also applies to: 61-62


📝 Walkthrough

Walkthrough

Adds Config::effective_virtual_store_dir(), switches install serialization/logging to use it for virtualStoreDir, and adds tests asserting .modules.yaml and pnpm:context report the effective (global when enabled) virtual-store path.

Changes

Global Virtual Store Directory Reporting

Layer / File(s) Summary
Config public helper method
pacquet/crates/config/src/lib.rs
Imports Path and adds Config::effective_virtual_store_dir(&self) -> &Path, returning global_virtual_store_dir when GVS is enabled, otherwise virtual_store_dir.
Install serialization using effective directory
pacquet/crates/package-manager/src/install.rs
Switches pnpm:context ContextLog emission and build_modules_manifest to use config.effective_virtual_store_dir() instead of config.virtual_store_dir, so persisted .modules.yaml and logged virtualStoreDir reflect the effective path.
GVS integration and serialization test
pacquet/crates/package-manager/src/install/tests.rs, pacquet/crates/store-dir/src/store_dir/tests.rs
Adds gvs_persists_global_virtual_store_dir_in_modules_yaml_and_context_log to validate parity between .modules.yaml and pnpm:context when GVS is enabled; updates a StoreDir test to construct the expected store path portably.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

Poem

🐰 I hopped through configs, soft and spry,
To find where virtual links may lie.
When global blooms, the links take lead,
Tests and logs now both concede.
A tiny hop — the paths comply! 🥕

🚥 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 title clearly and specifically describes the main change: adding GVS-aware virtualStoreDir handling to match pnpm's behavior in the modules.yaml file.
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 fix/pacquet-modules-yaml-virtual-store-dir-gvs

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

github-actions Bot commented May 24, 2026

Copy link
Copy Markdown
Contributor

Micro-Benchmark Results

Linux

group                          main                                   pr
-----                          ----                                   --
tarball/download_dependency    1.00      8.0±0.30ms   545.1 KB/sec    1.02      8.1±0.38ms   535.9 KB/sec

@codecov-commenter

codecov-commenter commented May 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.84%. Comparing base (3209c25) to head (b018277).
⚠️ Report is 4 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main   #11896   +/-   ##
=======================================
  Coverage   87.83%   87.84%           
=======================================
  Files         206      206           
  Lines       24520    24525    +5     
=======================================
+ Hits        21538    21544    +6     
+ Misses       2982     2981    -1     

☔ 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.

@github-actions

Copy link
Copy Markdown
Contributor

Integrated-Benchmark Report (Linux)

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

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 2.469 ± 0.080 2.346 2.581 1.02 ± 0.04
pacquet@main 2.410 ± 0.060 2.330 2.514 1.00
pnpm 4.903 ± 0.041 4.827 4.962 2.03 ± 0.05
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 2.4694117675599996,
      "stddev": 0.08048096703064878,
      "median": 2.4761091340599997,
      "user": 2.6639608399999997,
      "system": 3.5377732199999996,
      "min": 2.34562417756,
      "max": 2.5811444775599997,
      "times": [
        2.57102357156,
        2.5811444775599997,
        2.41165734256,
        2.50395079756,
        2.53370708956,
        2.4704514075599997,
        2.34562417756,
        2.4194579955599997,
        2.48176686056,
        2.37533395556
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 2.41020830836,
      "stddev": 0.05971350587167937,
      "median": 2.39438894556,
      "user": 2.65375814,
      "system": 3.5793070200000003,
      "min": 2.3301536445599997,
      "max": 2.51412307756,
      "times": [
        2.3301536445599997,
        2.4023182095599998,
        2.48293484656,
        2.44164856056,
        2.34424918156,
        2.44638450656,
        2.38645968156,
        2.36973093956,
        2.38408043556,
        2.51412307756
      ]
    },
    {
      "command": "pnpm",
      "mean": 4.90301671406,
      "stddev": 0.0405340448544853,
      "median": 4.9016154285599995,
      "user": 8.26207984,
      "system": 4.24188952,
      "min": 4.82669438756,
      "max": 4.96156048156,
      "times": [
        4.96156048156,
        4.95107520156,
        4.93802477356,
        4.89674871556,
        4.8992682165599994,
        4.87804285256,
        4.90780839856,
        4.86698147256,
        4.82669438756,
        4.90396264056
      ]
    }
  ]
}

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

Command Mean [ms] Min [ms] Max [ms] Relative
pacquet@HEAD 673.3 ± 46.3 642.3 799.3 1.00
pacquet@main 686.3 ± 37.6 655.2 778.0 1.02 ± 0.09
pnpm 2675.2 ± 65.4 2619.1 2801.6 3.97 ± 0.29
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 0.67328842764,
      "stddev": 0.04634444249611974,
      "median": 0.65522626154,
      "user": 0.37704170000000004,
      "system": 1.4331746,
      "min": 0.64231825454,
      "max": 0.7993274195400001,
      "times": [
        0.7993274195400001,
        0.6754184885400001,
        0.6535063425400001,
        0.68325989554,
        0.65394120154,
        0.6466925975400001,
        0.6736549015400001,
        0.64825385354,
        0.64231825454,
        0.65651132154
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 0.68626488084,
      "stddev": 0.037616780069496175,
      "median": 0.6739882635400001,
      "user": 0.3832122,
      "system": 1.4507614,
      "min": 0.6551600215400001,
      "max": 0.77798138054,
      "times": [
        0.66751310154,
        0.71219303254,
        0.65915694654,
        0.6551600215400001,
        0.66442776254,
        0.7030340635400001,
        0.68046342554,
        0.65792503554,
        0.68479403854,
        0.77798138054
      ]
    },
    {
      "command": "pnpm",
      "mean": 2.67519293094,
      "stddev": 0.06543419047216763,
      "median": 2.6611197860400004,
      "user": 3.3186014999999998,
      "system": 2.2681955,
      "min": 2.61912811554,
      "max": 2.8015819515400002,
      "times": [
        2.64125234254,
        2.62479420954,
        2.8015819515400002,
        2.78516435054,
        2.6641221285400003,
        2.61943643754,
        2.61912811554,
        2.66236916754,
        2.67421020154,
        2.6598704045400003
      ]
    }
  ]
}

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

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 4.891 ± 0.173 4.589 5.129 1.00
pacquet@main 4.913 ± 0.231 4.665 5.399 1.00 ± 0.06
pnpm 6.659 ± 0.124 6.416 6.800 1.36 ± 0.05
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 4.89075011138,
      "stddev": 0.17312066521695407,
      "median": 4.89615646838,
      "user": 6.590735180000001,
      "system": 3.5660129000000005,
      "min": 4.58900732038,
      "max": 5.12855977938,
      "times": [
        5.01345426438,
        5.11749220138,
        4.58900732038,
        4.7873501993800005,
        5.12855977938,
        4.77137732738,
        4.95586348538,
        4.75191547238,
        4.95603161238,
        4.83644945138
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 4.912526767779999,
      "stddev": 0.2314974295539865,
      "median": 4.8515618183800004,
      "user": 6.605044479999999,
      "system": 3.5911537000000004,
      "min": 4.66451040238,
      "max": 5.39898795338,
      "times": [
        4.66451040238,
        4.7200338223800005,
        4.83331013138,
        4.76832143938,
        4.87708935038,
        5.18858145738,
        4.86981350538,
        5.39898795338,
        5.04468914738,
        4.75993046838
      ]
    },
    {
      "command": "pnpm",
      "mean": 6.658628504880001,
      "stddev": 0.12430944920311156,
      "median": 6.70365707088,
      "user": 10.820982279999999,
      "system": 4.4367168,
      "min": 6.415849021380001,
      "max": 6.799641379380001,
      "times": [
        6.47233332738,
        6.415849021380001,
        6.7091831193800004,
        6.75175028738,
        6.799641379380001,
        6.73534050938,
        6.60202009338,
        6.69285316938,
        6.70364248938,
        6.703671652380001
      ]
    }
  ]
}

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

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 3.710 ± 0.053 3.627 3.799 1.00
pacquet@main 3.779 ± 0.129 3.644 4.006 1.02 ± 0.04
pnpm 4.336 ± 0.043 4.290 4.416 1.17 ± 0.02
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 3.7100478128800005,
      "stddev": 0.05268690525337999,
      "median": 3.7160912917799998,
      "user": 4.0698515,
      "system": 2.16070948,
      "min": 3.62701513428,
      "max": 3.7994858052800002,
      "times": [
        3.73050767428,
        3.72244126828,
        3.7994858052800002,
        3.62701513428,
        3.73050961728,
        3.77114870728,
        3.6579278252800003,
        3.66340276328,
        3.6882980182800003,
        3.70974131528
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 3.77876552858,
      "stddev": 0.12946190488700438,
      "median": 3.74607022728,
      "user": 4.093991399999999,
      "system": 2.23586568,
      "min": 3.64351746028,
      "max": 4.00566913228,
      "times": [
        3.7346298202800003,
        3.65394175528,
        3.6618577392800002,
        3.64351746028,
        3.91415845728,
        3.70435423228,
        3.93852150428,
        3.75751063428,
        3.77349455028,
        4.00566913228
      ]
    },
    {
      "command": "pnpm",
      "mean": 4.3362929684800005,
      "stddev": 0.04288091495045665,
      "median": 4.32324476078,
      "user": 5.508751200000001,
      "system": 2.5689145799999995,
      "min": 4.28950686028,
      "max": 4.41597068128,
      "times": [
        4.30235901528,
        4.28950686028,
        4.31086246228,
        4.3063403322800005,
        4.32486978828,
        4.40046891428,
        4.41597068128,
        4.36395191228,
        4.32697998528,
        4.32161973328
      ]
    }
  ]
}

@github-actions

Copy link
Copy Markdown
Contributor

🐰 Bencher Report

Branchpr/11896
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
4,890.75 ms
(-1.97%)Baseline: 4,988.94 ms
5,986.73 ms
(81.69%)
isolated-linker.fresh-install.hot-cache.hot-store📈 view plot
🚷 view threshold
3,710.05 ms
(-5.57%)Baseline: 3,928.69 ms
4,714.42 ms
(78.70%)
isolated-linker.fresh-restore.cold-cache.cold-store📈 view plot
🚷 view threshold
2,469.41 ms
(+1.23%)Baseline: 2,439.40 ms
2,927.29 ms
(84.36%)
isolated-linker.fresh-restore.hot-cache.hot-store📈 view plot
🚷 view threshold
673.29 ms
(-0.79%)Baseline: 678.63 ms
814.35 ms
(82.68%)
🐰 View full continuous benchmarking report in Bencher

…oin so the test passes on Windows

`modules_yaml_serialized_store_dir_carries_store_version` (added in
3209c25) hardcoded `/tmp/.pnpm-store/v11` on the right-hand side
while the left-hand side flows through `StoreDir::from`'s
`PathBuf::join`. On Windows that join emits `\v11`, so the test
panicked with `\v11` vs `/v11` — and has been failing in the
post-merge `Pacquet CI` run for #11891 since it landed.

Pnpm itself uses Node's `path.join` (via `getStorePath` →
`path.join(storePath, STORE_VERSION)`), which is also
backslash-joined on Windows. Building the expected value through
`Path::join` here mirrors that path and keeps the test asserting the
real parity contract on every platform.
@zkochan zkochan merged commit 74a219e into main May 24, 2026
28 checks passed
@zkochan zkochan deleted the fix/pacquet-modules-yaml-virtual-store-dir-gvs branch May 24, 2026 08:31
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.

2 participants