fix: preserve workspace specs on update#12140
Conversation
|
💖 Thanks for opening this pull request! 💖 |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughFixes workspace: local-path dependency specifiers being incorrectly rewritten during ChangesWorkspace Protocol Preservation
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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
ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration. 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. Comment |
Review Summary by QodoPreserve workspace protocol specs on manifest update
WalkthroughsDescription• Preserve workspace protocol specs when updating project manifest • Extract bare specifier logic into dedicated function • Prioritize catalog specifiers over resolver-normalized specs • Add comprehensive test coverage for workspace spec preservation Diagramflowchart LR
A["updateProjectManifest"] -- "calls" --> B["getBareSpecifierToSave"]
B -- "checks catalog lookup" --> C["Return user specified spec"]
B -- "checks workspace protocol" --> D["Return workspace spec"]
B -- "fallback" --> E["Return normalized spec"]
F["Test: preserveWorkspaceProtocol=true"] -- "expects" --> G["workspace: spec saved"]
H["Test: preserveWorkspaceProtocol=false"] -- "expects" --> I["link: spec saved"]
File Changes1. installing/deps-resolver/src/updateProjectManifest.ts
|
There was a problem hiding this comment.
🧹 Nitpick comments (1)
installing/deps-resolver/test/updateProjectManifest.test.ts (1)
8-26: ⚡ Quick winAdd a catalog-precedence test case.
The new suite only covers the workspace-vs-link branches.
getBareSpecifierToSave()also givesresolvedDep.catalogLookup.userSpecifiedBareSpecifierhighest priority, and that behavior is part of this PR’s contract. A focused fixture withcatalogLookupset would keep that branch from regressing if the precedence order changes later.Also applies to: 53-66
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@installing/deps-resolver/test/updateProjectManifest.test.ts` around lines 8 - 26, Add a test to cover the catalog-precedence branch by exercising getBareSpecifierToSave via updateProjectManifest: create a fixture/direct dependency where the resolved dependency includes resolvedDep.catalogLookup.userSpecifiedBareSpecifier (e.g., set on the object returned by createDirectDependency or the resolvedDep mock used by updateProjectManifest), call updateProjectManifest with preserveWorkspaceProtocol true/false as appropriate, and assert that manifest.dependencies.foo equals the userSpecifiedBareSpecifier value; this ensures the catalogLookup.userSpecifiedBareSpecifier branch in getBareSpecifierToSave is covered and won't regress.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@installing/deps-resolver/test/updateProjectManifest.test.ts`:
- Around line 8-26: Add a test to cover the catalog-precedence branch by
exercising getBareSpecifierToSave via updateProjectManifest: create a
fixture/direct dependency where the resolved dependency includes
resolvedDep.catalogLookup.userSpecifiedBareSpecifier (e.g., set on the object
returned by createDirectDependency or the resolvedDep mock used by
updateProjectManifest), call updateProjectManifest with
preserveWorkspaceProtocol true/false as appropriate, and assert that
manifest.dependencies.foo equals the userSpecifiedBareSpecifier value; this
ensures the catalogLookup.userSpecifiedBareSpecifier branch in
getBareSpecifierToSave is covered and won't regress.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 30781d69-23ca-41e0-84ac-adec924e1430
📒 Files selected for processing (2)
installing/deps-resolver/src/updateProjectManifest.tsinstalling/deps-resolver/test/updateProjectManifest.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Compile & Lint
- GitHub Check: Analyze (javascript)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Follow Standard Style with trailing commas, preferring functions over classes, and declaring functions after they are used (relying on hoisting)
Use a single options object instead of multiple parameters when a function needs more than two or three arguments
Follow Import Order: Standard libraries first, then external dependencies (alphabetically), then relative imports
Write self-documenting code where function names, parameters, and types explain what a function does without requiring prose comments
Do not write comments that restate what the code already says; refactor via renaming, splitting helpers, or restructuring instead
Do not repeat documentation at call sites that already exists in JSDoc on the callee; update JSDoc once for all call sites to benefit
Use JSDoc only for a function's contract (preconditions, postconditions, edge cases, why the function exists), not for re-narrating the body
Do not record past implementation shape, refactor history, or 'the previous code did X' framing in code; use git log and git blame instead
Write comments only when: the reason for code is non-obvious (hidden invariant, workaround for known bug, deliberate exception), or the right name doesn't fit (temporary technical constraint)
Files:
installing/deps-resolver/test/updateProjectManifest.test.tsinstalling/deps-resolver/src/updateProjectManifest.ts
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use util.types.isNativeError() instead of instanceof Error for error type checking in Jest tests
Files:
installing/deps-resolver/test/updateProjectManifest.test.ts
🧠 Learnings (1)
📚 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:
installing/deps-resolver/test/updateProjectManifest.test.tsinstalling/deps-resolver/src/updateProjectManifest.ts
198a987 to
5e581e2
Compare
5e581e2 to
fddb8a4
Compare
Code Review by Qodo
Context used 1. Backslash workspace path missed
|
fddb8a4 to
79f83e8
Compare
|
Code review by qodo was updated up to the latest commit 79f83e8 |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #12140 +/- ##
=======================================
Coverage 88.08% 88.08%
=======================================
Files 310 310
Lines 41847 41855 +8
=======================================
+ Hits 36862 36870 +8
Misses 4985 4985 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…hangeset Mirror the pnpm-side `getBareSpecifierToSave` fix in pacquet's `update` command so `pacquet update --latest` no longer rewrites a `workspace:` local-path dependency (e.g. `workspace:../packages/foo/dist`) into a registry version. Both `--latest` rewrite sites now skip registry resolution for such specs via `is_workspace_local_path_specifier`, a faithful port of pnpm's `isWorkspaceLocalPathSpecifier`. Also adds the missing changeset for the published `@pnpm/installing.deps-resolver` change. --- Written by an agent (Claude Code, claude-opus-4-8).
79f83e8 to
1f58673
Compare
|
Code review by qodo was updated up to the latest commit 1f58673 |
| function isWorkspaceLocalPathSpecifier (bareSpecifier: string): boolean { | ||
| if (!bareSpecifier.startsWith('workspace:')) return false | ||
| const pref = bareSpecifier.slice('workspace:'.length) | ||
| return pref.startsWith('.') || pref.startsWith('/') || pref.startsWith('~/') || /^[A-Z]:/i.test(pref) | ||
| } |
There was a problem hiding this comment.
1. Backslash workspace path missed 🐞 Bug ≡ Correctness
isWorkspaceLocalPathSpecifier()/is_workspace_local_path_specifier() fails to recognize Windows backslash-root workspace specs like workspace:\foo\bar as local-path dependencies, so update flows fall back to registry/normalized spec handling and can rewrite manifests even when preserveWorkspaceProtocol is enabled. This violates the intended guarantee that workspace local-path specifiers— including Windows-authored backslash paths—are preserved verbatim during updates.
Agent Prompt
## Issue description
The workspace-local-path detection helper (`isWorkspaceLocalPathSpecifier()` / `is_workspace_local_path_specifier()`) does not recognize Windows backslash-root paths after `workspace:` (e.g. `workspace:\\foo\\bar`) as local workspace paths. Because update/save logic is gated on this predicate, a false negative causes the updater to take the registry/normalized-spec path (e.g. `fetch_latest()` / saving `resolvedDep.normalizedBareSpecifier`) and rewrite the manifest even when `preserveWorkspaceProtocol` is enabled; these Windows-authored backslash workspace paths should instead be treated as local and preserved.
## Issue Context
- The decision of whether to preserve the existing dependency spec or rewrite it during updates depends on the workspace-local-path predicate.
- pnpm’s local scheme parsing normalizes backslashes to `/`, indicating that backslash-authored `workspace:` paths are valid local-path inputs and resolve as local dependencies.
- pacquet’s local bare-spec parsing explicitly treats `\\` as a valid filespec/path prefix on Windows-host inputs, so `workspace:\\...` forms are plausible and should be excluded from registry resolution/rewriting.
## Fix Focus Areas
- installing/deps-resolver/src/updateProjectManifest.ts[58-76]
- pacquet/crates/package-manager/src/update.rs[287-296]
- pacquet/crates/package-manager/src/update.rs[716-725]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
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
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 4.23089331874,
"stddev": 0.10116526122339824,
"median": 4.19723895544,
"user": 4.113932559999999,
"system": 3.4701284599999993,
"min": 4.10907628544,
"max": 4.40833499644,
"times": [
4.25095880444,
4.10907628544,
4.1863405364399995,
4.26022831544,
4.39675254244,
4.15055693244,
4.40833499644,
4.20487532644,
4.15220686344,
4.189602584439999
]
},
{
"command": "pacquet@main",
"mean": 4.23197203824,
"stddev": 0.19287780409771055,
"median": 4.20946907644,
"user": 4.054297859999999,
"system": 3.4205866599999992,
"min": 3.98052352344,
"max": 4.5889700724399995,
"times": [
4.5889700724399995,
4.117222970439999,
4.30062261644,
4.0461278714399995,
4.19801830344,
4.43992869344,
4.06115609344,
4.2209198494399995,
4.36623038844,
3.98052352344
]
},
{
"command": "pnpr@HEAD",
"mean": 2.18178215874,
"stddev": 0.11053646472005757,
"median": 2.16010811644,
"user": 2.7009714599999994,
"system": 2.9373769599999995,
"min": 2.0272342944400004,
"max": 2.3388523674400004,
"times": [
2.3169527644400003,
2.12256470244,
2.07395999144,
2.2970809244400003,
2.0272342944400004,
2.0838405684400003,
2.18374221444,
2.3388523674400004,
2.13647401844,
2.2371197414400004
]
},
{
"command": "pnpr@main",
"mean": 2.06906477364,
"stddev": 0.12844746938281967,
"median": 2.05440726094,
"user": 2.72936556,
"system": 2.8820641599999997,
"min": 1.8888966764400001,
"max": 2.29234637544,
"times": [
2.10018358144,
2.29234637544,
2.20167826244,
1.93352433544,
1.98800392844,
1.98698488144,
1.8888966764400001,
2.0086309404400002,
2.1265653594400002,
2.16383339544
]
}
]
}Scenario: Isolated linker: fresh restore, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 0.63667739288,
"stddev": 0.01117062980186702,
"median": 0.63810889648,
"user": 0.37254692,
"system": 1.3255773200000003,
"min": 0.61313586498,
"max": 0.65129120098,
"times": [
0.63789825398,
0.62367819798,
0.63789922598,
0.6414469939799999,
0.64812129098,
0.65129120098,
0.63831856698,
0.63391555498,
0.64106877798,
0.61313586498
]
},
{
"command": "pacquet@main",
"mean": 0.6381763151800001,
"stddev": 0.01288915645949533,
"median": 0.63911952648,
"user": 0.39098862,
"system": 1.3282018199999999,
"min": 0.61977959498,
"max": 0.65730383998,
"times": [
0.65730383998,
0.63069935698,
0.61977959498,
0.62207976298,
0.62895593598,
0.63801911698,
0.64648301798,
0.64021993598,
0.65537637298,
0.64284621698
]
},
{
"command": "pnpr@HEAD",
"mean": 0.6814439734800001,
"stddev": 0.015835730168625443,
"median": 0.68372370248,
"user": 0.38799771999999993,
"system": 1.35771002,
"min": 0.65654944398,
"max": 0.70684523098,
"times": [
0.68415877198,
0.69667626898,
0.66622332698,
0.65654944398,
0.68006073698,
0.69210432998,
0.68328863298,
0.70684523098,
0.66204113398,
0.68649185798
]
},
{
"command": "pnpr@main",
"mean": 0.68578244968,
"stddev": 0.017949889189942045,
"median": 0.68292605098,
"user": 0.39389031999999996,
"system": 1.3567570199999996,
"min": 0.65577611098,
"max": 0.72244110898,
"times": [
0.70065772798,
0.69417953698,
0.72244110898,
0.65577611098,
0.6807686039799999,
0.68065460998,
0.68338634598,
0.66883262198,
0.68246575598,
0.68866207398
]
}
]
}Scenario: Isolated linker: fresh install, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 4.277545605039999,
"stddev": 0.07003039808260346,
"median": 4.25320628044,
"user": 3.84938104,
"system": 3.318024,
"min": 4.23051554244,
"max": 4.46384908644,
"times": [
4.46384908644,
4.26141773144,
4.23108579944,
4.24180289444,
4.23051554244,
4.25453987344,
4.24085884044,
4.29932233844,
4.25187268744,
4.30019125644
]
},
{
"command": "pacquet@main",
"mean": 4.25222874824,
"stddev": 0.07619050543042295,
"median": 4.23895752994,
"user": 3.87456064,
"system": 3.3002009,
"min": 4.11660505044,
"max": 4.37207131944,
"times": [
4.303391245439999,
4.24471225644,
4.11660505044,
4.23320280344,
4.20114379944,
4.2504093554399995,
4.208970852439999,
4.2320315084399995,
4.35974929144,
4.37207131944
]
},
{
"command": "pnpr@HEAD",
"mean": 2.1589483194399994,
"stddev": 0.1271747984605788,
"median": 2.1465098439399997,
"user": 2.58513934,
"system": 2.8408594,
"min": 1.97892903944,
"max": 2.3485919684399996,
"times": [
2.3485919684399996,
2.0586522494399997,
2.1815665324399998,
1.97892903944,
2.3194669904399996,
2.13818593144,
2.04104815944,
2.0717700024399996,
2.29643856444,
2.15483375644
]
},
{
"command": "pnpr@main",
"mean": 2.0850699161399997,
"stddev": 0.09400826935105654,
"median": 2.07966266494,
"user": 2.5505356399999997,
"system": 2.8352712999999996,
"min": 1.97498896644,
"max": 2.29820830344,
"times": [
2.1178899424399997,
1.99028882344,
2.01903185744,
2.14882555644,
2.1096670774399997,
1.97498896644,
2.03247330444,
2.0735028124399997,
2.29820830344,
2.0858225174399996
]
}
]
}Scenario: Isolated linker: fresh install, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 1.3445011324399998,
"stddev": 0.01402422903908496,
"median": 1.3457932065399998,
"user": 1.3255103,
"system": 1.7028575400000001,
"min": 1.31883450004,
"max": 1.36363421704,
"times": [
1.35177648904,
1.34969206204,
1.33420841204,
1.3383403940399998,
1.36363421704,
1.3418943510399999,
1.36234409304,
1.33252040004,
1.31883450004,
1.3517664060399999
]
},
{
"command": "pacquet@main",
"mean": 1.3736260468400001,
"stddev": 0.02700693088015784,
"median": 1.36161706304,
"user": 1.3690604,
"system": 1.72454114,
"min": 1.35302879904,
"max": 1.43765567804,
"times": [
1.36139747804,
1.43765567804,
1.36839804504,
1.36063193104,
1.3578778490399999,
1.40635050904,
1.35302879904,
1.37052830004,
1.36183664804,
1.35855523104
]
},
{
"command": "pnpr@HEAD",
"mean": 0.65320104264,
"stddev": 0.027155867442142052,
"median": 0.64649483304,
"user": 0.3288849,
"system": 1.2776648400000001,
"min": 0.62626235604,
"max": 0.7218971320400001,
"times": [
0.63422920104,
0.62626235604,
0.6404374610400001,
0.64532215104,
0.7218971320400001,
0.64840214504,
0.6710426740400001,
0.65808703804,
0.6476675150400001,
0.6386627530400001
]
},
{
"command": "pnpr@main",
"mean": 0.6508371794400001,
"stddev": 0.022974661275112118,
"median": 0.64336086504,
"user": 0.3363094,
"system": 1.2843951399999998,
"min": 0.6335056490400001,
"max": 0.7135281960400001,
"times": [
0.64063036104,
0.64825417904,
0.6401445440400001,
0.64280954704,
0.6439121830400001,
0.65880661104,
0.6453856440400001,
0.6335056490400001,
0.7135281960400001,
0.64139488004
]
}
]
}Scenario: Isolated linker: fresh install, cold cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 3.03286467224,
"stddev": 0.06199991427814319,
"median": 3.01277683064,
"user": 1.76653616,
"system": 2.00169972,
"min": 2.97778838864,
"max": 3.19169278864,
"times": [
3.0656085606400003,
3.04315698464,
3.00058608364,
3.01281792864,
3.01273573264,
3.03719814264,
2.9836304566400003,
3.19169278864,
2.97778838864,
3.00343165564
]
},
{
"command": "pacquet@main",
"mean": 3.1037916231400002,
"stddev": 0.07765484860962729,
"median": 3.08108702614,
"user": 1.8602231599999999,
"system": 2.0231171199999998,
"min": 3.02886147164,
"max": 3.26531164764,
"times": [
3.03064639264,
3.06000944264,
3.0357489276400003,
3.02886147164,
3.18874543564,
3.14665261264,
3.09396958064,
3.26531164764,
3.11976624864,
3.06820447164
]
},
{
"command": "pnpr@HEAD",
"mean": 0.6756841827400001,
"stddev": 0.016209705350704016,
"median": 0.67336116814,
"user": 0.33082196,
"system": 1.33899992,
"min": 0.65396622064,
"max": 0.70173369864,
"times": [
0.68721488564,
0.6637727426400001,
0.6714406806400001,
0.66897054164,
0.67528165564,
0.69960116064,
0.67660020864,
0.65826003264,
0.65396622064,
0.70173369864
]
},
{
"command": "pnpr@main",
"mean": 0.6940260319400001,
"stddev": 0.08631268460466239,
"median": 0.66698110564,
"user": 0.34727036,
"system": 1.2985485200000002,
"min": 0.64678313564,
"max": 0.93664970264,
"times": [
0.70024740364,
0.66289661864,
0.64678313564,
0.66747313064,
0.65773203664,
0.66652633364,
0.66376505464,
0.67075102564,
0.93664970264,
0.66743587764
]
}
]
} |
|
| Branch | pr/12140 |
| Testbed | pacquet |
Click to view all benchmark results
| Benchmark | Latency | Benchmark Result milliseconds (ms) (Result Δ%) | Upper Boundary milliseconds (ms) (Limit %) |
|---|---|---|---|
| isolated-linker.fresh-install.cold-cache.cold-store | 📈 view plot 🚷 view threshold | 4,277.55 ms(+2.42%)Baseline: 4,176.66 ms | 5,011.99 ms (85.35%) |
| isolated-linker.fresh-install.cold-cache.hot-store | 📈 view plot 🚷 view threshold | 3,032.86 ms(+1.44%)Baseline: 2,989.69 ms | 3,587.63 ms (84.54%) |
| isolated-linker.fresh-install.hot-cache.hot-store | 📈 view plot 🚷 view threshold | 1,344.50 ms(+2.54%)Baseline: 1,311.17 ms | 1,573.41 ms (85.45%) |
| isolated-linker.fresh-restore.cold-cache.cold-store | 📈 view plot 🚷 view threshold | 4,230.89 ms(+4.55%)Baseline: 4,046.70 ms | 4,856.04 ms (87.13%) |
| isolated-linker.fresh-restore.hot-cache.hot-store | 📈 view plot 🚷 view threshold | 636.68 ms(+3.81%)Baseline: 613.33 ms | 736.00 ms (86.51%) |
|
| Branch | pr/12140 |
| Testbed | pnpr |
⚠️ 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-thresholdsflag.
Click to view all benchmark results
| Benchmark | Latency | milliseconds (ms) |
|---|---|---|
| isolated-linker.fresh-install.cold-cache.cold-store | 📈 view plot | 2,158.95 ms |
| isolated-linker.fresh-install.cold-cache.hot-store | 📈 view plot | 675.68 ms |
| isolated-linker.fresh-install.hot-cache.hot-store | 📈 view plot | 653.20 ms |
| isolated-linker.fresh-restore.cold-cache.cold-store | 📈 view plot | 2,181.78 ms |
| isolated-linker.fresh-restore.hot-cache.hot-store | 📈 view plot | 681.44 ms |
What
workspace:dependency specifiers whenupdateProjectManifestsaves updated direct dependencies andpreserveWorkspaceProtocolis enabled@pnpm/installing.deps-resolverchangepacquet parity
Ported the same fix to pacquet's
updatecommand. Previouslypacquet update --latestrouted every direct dependency through a registrylatestlookup, so aworkspace:local-path dependency (e.g.workspace:../packages/foo/dist) was rewritten into a registry version — corrupting the manifest (in the regression test it became0.0.1-security). Both--latestrewrite sites now skip registry resolution for such specs viais_workspace_local_path_specifier, a faithful port of pnpm'sisWorkspaceLocalPathSpecifier. The gate is unconditional in the--latestpath becausepreserveWorkspaceProtocolis always on there (its only override derives fromlinkWorkspacePackagesunder--workspace, which cannot be combined with--latest).Fixes #3902
Testing
pnpm side:
pnpm --dir installing/deps-resolver exec tsgo --buildenv NODE_OPTIONS="--experimental-vm-modules --disable-warning=ExperimentalWarning --disable-warning=DEP0169" pnpm --dir installing/deps-resolver exec jest updateProjectManifest.test.tspnpm --dir installing/deps-resolver exec eslint src/updateProjectManifest.ts test/updateProjectManifest.test.tspacquet side:
cargo nextest run -p pacquet-package-manager workspace— helper unit testscargo nextest run -E 'test(update_latest_preserves_workspace_local_path_specifier)'— integration regression test (verified it fails without the guard: the manifest spec was rewritten to0.0.1-security)cargo clippy -p pacquet-package-manager -p pacquet-cli --all-targets -- --deny warningscargo fmt --checkWritten by an agent (Claude Code, claude-opus-4-8).
Summary by CodeRabbit
Refactor
workspace:preservation vs normalization logic.Bug Fixes
pnpm update/pacquet update --latestrewritingworkspace:specifiers that point to local paths, keeping them as-is when appropriate.Tests