Skip to content

Resolve vlt registry bases through one shared function (#562) - #574

Open
Mikola Lysenko (mikolalysenko) wants to merge 8 commits into
mainfrom
arch-refactor/562-vlt-registry-base
Open

Mikola Lysenko (mikolalysenko) wants to merge 8 commits into
mainfrom
arch-refactor/562-vlt-registry-base

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Oct 2, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #562.

vlt lock inventory and hosted rollback/remove now use one registry-base resolver. A lock resolves to the same registry in both paths, including scoped packages and modern empty-tilde DepIDs. Hosted rewrite and restore retain compatible admission and tuple-layout rules.

Behavior and implementation

  • vlt_lock_text::registry_base(era, segment, name, options) replaces both private resolvers. Inventory also uses the shared npm tarball URL builder; duplicate default-alias and registry constants are removed.
  • Modern ~~ resolves through the literal npm alias, matching fresh-process checks against published vlt 1.3.5. A mapped alias takes precedence over options.registry; a recognized default falls back to the configured registry and then the npm default. Unknown unmapped aliases are refused.
  • After the segment resolves, a matching scoped-registries entry supplies the package's base. Explicit URL and named-alias segments keep their existing treatment.
  • Legacy ·· empty segments retain the previous compatibility policy. Historical Node and browser vlt releases differ here; this is not a claim that every old runtime hydrated them identically.
  • Registry URL resolution normalizes the modern empty segment. Restore admission, sibling selection, and URL-slot convention use the raw segment, matching forward rewrite, heal, and vendoring. This also lets restore recover hosted pins created before this change.
  • The branch incorporates main 203e092b and the isolated Berry benchmark correction from Stream ZIP member hashes during vendored verification #587. The fixture strictly requires both package.json and yarn.lock, matching Berry's descriptor and lockfile rewrites.

Validation

  • The scoped-registry and modern empty-segment rules were checked against published vlt packages in fresh processes to avoid their hydration caches.
  • The new custom-default-alias forward rewrite/restore regression fails on d03ae6b6 and passes with ae7a0353. Both the no-sibling and same-empty-sibling cases recover the original lockfile bytes, including registry B's tarball URL when the configured default is registry A.
  • 17 distinct focused tests pass (20 executions across hosted restore, shared resolver, and inventory filters), including direct hosted-pin restoration, sibling conventions, scoped registries, and explicit URL/alias controls. Formatting and diff checks pass.
  • The final commit merges cleanly with checked main b1f9818a. The benchmark correction is identical to the already validated fixture from Stream ZIP member hashes during vendored verification #587. No full local workspace or timing run was repeated.
  • Final-head CI is complete: 485 successful checks, 6 skipped; no failures or pending checks. Bugbot passed, there are no unresolved review threads, and the PR is mergeable.

Risk and scope

Registry precedence changes affect locks with non-default registry configuration. Unknown bases still fail closed. JSON layout, forward admission, error codes, and exit codes are unchanged. #521 remains outside this change.


Note

Medium Risk
Changes registry precedence for vlt locks with custom registry, aliases, and scoped registries; unknown aliases still fail closed, but mis-resolved bases could affect inventory and restore slot [3] URLs.

Overview
Unifies vlt DepID → registry URL resolution so lock inventory and hosted upstream restore agree, including scoped packages and modern empty-tilde (~~) segments.

The duplicate local resolvers in vlt_lock_text are replaced by shared registry_base(era, segment, name, options), with default_registry_alias, tilde empty-segment normalization, scoped-registries overrides, and legacy-era empty-segment policy kept distinct from vlt 1.3.5 behavior. Lock inventory now derives inferred tarball URLs via that helper and npm_tarball_url. Upstream restore uses the same base for slot [3], refuses when a segment maps to no registry, and keeps admission/sibling slot-3 rules on the raw DepID segment (matching forward rewrite). Mock registry paths now URL-encode scoped package names.

The Yarn Berry bench fixture expects rewrites on package.json and yarn.lock, not lockfile alone. Coverage adds REGISTRY_BASE_CASES, scoped-registry cases, and a forward-rewrite → restore round-trip for custom default aliases.

Reviewed by Cursor Bugbot for commit ae7a035. Configure here.

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko Mikola Lysenko (mikolalysenko) added refactor Structural change: duplicated code or logic, missing abstraction, layering, dead code arch-refactor PR opened by the scheduled architecture refactor routine labels Oct 2, 2026
vlt lock inventory (vendored fetch, VEX) and hosted rollback/remove
each turned a DepID registry segment into a registry URL with their
own precedence, so one lock could resolve to two registries. Both now
call vlt_lock_text::registry_base, which follows vlt's DepID
hydration: a mapped alias is its registries URL, the default registry
is options.registry, then the default alias's URL, then npmjs.

The private copies, the inline tarball URL, restore's NPM_REGISTRY
and its default_alias helper are deleted.

Fixes #562

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 2, 2026 16:26
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 2, 2026
Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

[agent] e2e (ubuntu-latest, e2e_vendor_jvm_build, 3.8.9, --ignored maven_reactor) failed on 1bc4388. The failure isn't from this PR: Maven itself failed to resolve maven-resources-plugin:2.6 because repo.maven.apache.org returned 502 Bad Gateway for org/apache/maven/maven/2.0.6/maven-2.0.6.pom (job log). This PR only changes vlt registry resolution (vlt_lock_text.rs, lock_inventory/vlt.rs, redirect/upstream/vlt.rs), and the JVM vendor path doesn't call it. There's no fix to port, because this was an upstream outage. I'll re-run the failed job once, after its workflow run finishes.


Generated by Claude Code

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

bugbot run


Generated by Claude Code

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@mikolalysenko Mikola Lysenko (mikolalysenko) added the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 2, 2026
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

[burn-down agent] Labeled Ready for review at 1bc43883d11d789f5e359d1eb733166e21a8b107.

  • CI: 484/490 check runs green on this head (rest skipped by path/matrix filters), 0 failing. Mergeable; 14 commits behind main; lock_inventory/tests.rs overlaps — merged main locally: clean merge, crate builds, 276 vlt/lock-inventory unit tests pass (one unrelated permission test fails only because the sandbox runs as root).
  • Bugbot: re-requested (the earlier run was cancelled) and it reviewed 1bc43883d1 with no issues found; no unresolved review threads.
  • Reviewer focus: the precedence order in the shared vlt_lock_text::registry_base (URL segment → mapped alias → default registry), which now governs both inventory and hosted restore.

Generated by Claude Code

@mikolalysenko

Mikola Lysenko (mikolalysenko) commented Oct 2, 2026 •

Copy link
Copy Markdown
Collaborator Author

Reviewed final head ae7a03533870a7088eec798e9ee2e893c9eb1e03.

The scoped-registry and modern ~~ base-resolution corrections are retained. The later Bugbot admission finding is also fixed: restore now uses the same raw-segment admission and sibling conventions as forward rewriting, while the shared resolver alone normalizes ~~ to npm. The URL-slot decision also preserves the raw convention, preventing loss of the restored tarball URL.

The full forward rewrite/restore regression failed before this correction and now recovers the original bytes with and without an empty-segment sibling. Direct hosted pins and sibling layout are covered too. 17 distinct focused tests pass (20 executions across restore, resolver, and inventory filters); formatting and diff checks pass. The final commit merges cleanly with checked main b1f9818a. The branch also contains the previously validated Berry benchmark correction.

Ready to merge as-is from this review. Final-head CI is complete: 485 successful checks, 6 skipped; no failures or pending checks. Bugbot passed, there are no unresolved review threads, and the PR is mergeable. Legacy empty segments retain the prior compatibility policy; no full local workspace or timing run was repeated.

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

[agent] I confirmed the scoped-registries finding against @vltpkg/dep-id and @vltpkg/spec 1.3.5, and found a second divergence in my own table that the fix should cover. Since you said a fix is in progress, I won't push to this branch until it lands, to avoid two competing fixes.

How I checked: hydrate memoizes on the DepID alone (cacheKey = name + id), and Spec also caches. So several cases run in one process return stale answers. Each row below is a fresh node process calling hydrateTuple(splitDepID(id), undefined, options) and printing final.registry:

DepID options vlt 1.3.5 this PR
~npm~@s+a@1.0.0 registry a, registries.npm b, scoped-registries.@s a a b ✗
~npm~@s+a@1.0.0 same, but scoped-registries.@s c c b ✗
~npm~@s+a@1.0.0 scoped-registries.@s c only c npmjs ✗
~corp~@s+a@1.0.0 registries.corp d, scoped-registries.@s c c d ✗
~npm~@s+a@1.0.0 registry a, registries.npm b (no scope entry) b b ✓
~~a@1.0.0 registry a, registries.npm b b a ✗
~npm~a@1.0.0 registry a, registries.npm b b b ✓
~npm~a@1.0.0 registry a a a ✓

What this shows:

  1. Scoped registries win for every registry segment. A scoped name with a scoped-registries entry resolves there whatever the segment is, a named alias such as corp included. The npm: / <alias>: subspec is re-parsed, and its scopeReg ?? registry ?? … wins in final. So registry_base needs the package name (or its scope) and has to check options["scoped-registries"][scope] first. Neither copy on main read it either. main's inventory returned the right answer in your example only because the scope URL happened to equal registry.
  2. The empty tilde segment behaves like the default alias. splitDepID("~~a@1.0.0") returns ["registry","npm","a@1.0.0"], so ~~ resolves exactly like ~npm~: registries.npm before registry. My table row ("", registry a + registries.npm b) → a follows Spec's plain-spec order, not the DepID path. Restore on main had this case right. The legacy ·· era may differ: vlt 1.3.5's splitDepID rejects ··, and those locks come from older vlt releases, so that row needs a check against the vlt release that wrote it, not 1.3.5.

Proposed fix:

  • registry_base(segment, name, options) returns the scoped-registries[scope] URL first.
  • On the tilde era, treat '' as the default alias, keeping today's resolution for legacy ·· ids.
  • Add these rows to REGISTRY_BASE_CASES so the inventory and restore caller tests cover them.

I'm marking the PR blocked until your correction lands.


Generated by Claude Code

Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 2, 2026
Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko Mikola Lysenko (mikolalysenko) removed the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 2, 2026
clippy -D warnings rejected the registry_base doc comment
(doc_lazy_continuation), failing CI on cb35d5a. Separate the
scoped-registry paragraph from the list, and rustfmt the three touched
files that were rustfmt-clean on main. No behavior change.

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

[agent] clippy failed on cb35d5a. The cause was doc_lazy_continuation on the registry_base doc comment, where the scoped-registry paragraph ran straight on from the list. I pushed d9f5278, which adds the blank doc line and rustfmts the three touched files, which were rustfmt-clean on main. There is no behavior change. With it, cargo clippy --workspace --all-features -- -D warnings is clean. cargo test -p socket-patch-core --lib vlt gives 173 passed and 1 failed; the failure is the known root-only permission test vlt_heal::an_unremovable_hidden_lock_keeps_every_store_entry.

One item from my earlier comment is still open on this head. With registry a and registries.npm b, the empty tilde segment (~~a@1.0.0) resolves to a here. vlt 1.3.5 splits ~~ to the npm alias and resolves it to b; restore on main agreed with vlt. This is an edge case: it needs a lock where registry and registries.npm differ, plus ~~ ids. If you judged it out of scope, say so and I'll file it separately rather than widen this PR.


Generated by Claude Code

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 2, 2026
Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Follow-up to the empty-segment finding: the scoped correction is pushed as cb35d5a, and I have incorporated your formatting/doc-lint follow-up d9f52781 as the base for the next fix.

An agent in this review is now implementing and testing the ~~ correction on top of d9f52781; another is verifying the legacy ·· behavior against the older published releases. Please leave the functional follow-up to this work to avoid competing branch updates. I will push the verified correction and update the review here. The PR stays on hold until final checks pass.

Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 2, 2026
Assisted-by: Claude Code:claude-opus-5-5

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

[agent] e2e (windows-latest, e2e_vendor_jvm_build, 8.14.3, 17, --ignored gradle_multi_project) failed on d9f5278. The failure isn't from this PR. Maven's plugin resolution failed because Maven Central couldn't serve dozens of standard plugin dependencies, among them plexus-utils:3.5.1, commons-io:2.13.0 and asm:9.5 (job log). This is the same Central outage as the earlier 502 on 1bc4388. This PR changes only vlt registry resolution, and the JVM vendor path doesn't call it. There's no fix to port. The reviewer's ~~ commit will re-trigger CI; if the head hasn't moved by the time this run finishes, I'll re-run the failed job once.


Generated by Claude Code

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review

Please review final commit faade961f6fdf133677a0089aaf8ebb060ffcd69, which fixes scoped-registry precedence and modern empty-tilde normalization. The PR description and review comment now reflect the final implementation and focused validation.

Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 2, 2026
Assisted-by: Claude Code:claude-opus-5-5

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread crates/socket-patch-core/src/patch/redirect/upstream/vlt.rs Outdated
Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 2, 2026
Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review

Please review final head ae7a03533870a7088eec798e9ee2e893c9eb1e03. The restore admission/sibling/URL-slot mismatch is fixed with a failing-before/passing-after full rewrite/restore regression. The PR description and review comment reflect the final behavior and evidence.

Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 2, 2026
Assisted-by: Claude Code:claude-opus-5-5

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit ae7a035. Configure here.

@mikolalysenko Mikola Lysenko (mikolalysenko) added the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 2, 2026
Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 2, 2026
Assisted-by: Claude Code:claude-opus-5-5

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arch-refactor PR opened by the scheduled architecture refactor routine Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review refactor Structural change: duplicated code or logic, missing abstraction, layering, dead code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

vlt lock inventory and hosted restore resolve a node's registry differently

2 participants