Repository navigation
Description
[agent] Filed by the scheduled architecture audit (audit-core). Register: register.
Kind: refactor. Register ID: A74.
Problem
crates/socket-patch-core/src/hosted/governing_root.rs (3,580 lines, ~1,265 of them production code) holds the npm-family workspace-membership model, but most of it has nothing to do with hosted mode:
member_stray_lock:`` its own doc says it is "shared by the hosted refusal, the vendored refusal (vendor_lockfile_missing) and VEX discovery". It is called from `vendor/npm_flavor.rs` and `vex/discover/mod.rs` (with `member_stray_lock_own_files`).nested_npm_projectandhas_own_npm_family_lock: called only by the CLI'scommands/scan/discovery.rs, for both modes.workspace_patterns:```` called bycrawlers/npm_crawler.rsand `patch/redirect/yarnrc.rs`.- The machinery they all depend on:
package_json_workspace_root,vlt_workspace_root/vlt_workspace_groups/vlt_lists_member,vlt_project_root, the Bun dialect sniff (lock_dialect,bun_workspace_dirs,bun_lockb_config_version) andlisting_workspace/lockless_npm_project.
Only refusal and its message builders (pnpm_settings_elsewhere, cargo_member_refusal, the *_refusal wrappers, HOSTED_CARGO_ROOT_HINT) are hosted policy. Open PRs are pushing more callers into it: #1928 makes vendor call a new public npm_family_governing_root, and #1971 changes workspace_patterns for the crawler and yarnrc. So the vendored flow now depends on hosted::, and vex and the crawler depend on it too.
Proposed change
Once #1928 and #1971 have landed, move the membership model into a module named for what it is, for example formats/npm_workspace.rs (beside #1971's formats::package_json::Workspaces) or utils/npm_workspace.rs (beside utils::pnpm_workspace). That covers member_stray_lock (+ MemberStrayLock, member_stray_lock_own_files), nested_npm_project, has_own_npm_family_lock, the package.json/vlt/Bun root walks and dialect sniffs, and npm_family_governing_root from #1928. Keep hosted/governing_root.rs as the refusal: it maps a governing root to a Refusal and does the cargo, uv and pnpm-settings checks. Update the imports in vendor/npm_flavor.rs, vex/discover/mod.rs, crawlers/npm_crawler.rs, patch/redirect/yarnrc.rs and the CLI's scan/discovery.rs. While moving the code, fold the small copies inside the module:
- the deeper-root-wins choice is written twice (
nearer_rootand thematchinvlt_lock_root); - "path components to
Vec<String>" is written 3× (L644, L739, L997); - the
/-split normalize is written 2× (L842, L1142); canonicalize(..).unwrap_or_else(|_| root.to_path_buf())appears 5× besidecanonical_or_absolute.
Size
3–7 files; mostly moved lines, about +60/−60 net once the copies are folded. The tests (~2,300 lines) move with the code.
Acceptance criteria
- Behavior is unchanged. The moved tests (
nested_npm_project_*,*_stray_*,vlt_*,bun_*,package_json_workspace*) and the remainingrefusaltests (pnpm_*,cargo_workspace_member_is_refused) pass unmodified apart from their paths. vendor,vexandcrawlersno longer import anything fromcrate::hosted::governing_root.
Activity
audit-core: hosted/ audited, A74 (#1974), A75, 5 carried rows
mikolalysenko commentedon Nov 2, 2026
[agent] Triaged as
priority:p1(npm-family workspace model). Not a duplicate and not yet fixed onmain. Its proposed change depends on #1928 and #1971 landing first, so I am leaving it unclaimed until both have merged.Generated by Claude Code