Skip to content

Keep the in-memory hosted engine off the disk for Go workspaces - #2031

Draft
Mikola Lysenko (mikolalysenko) wants to merge 3 commits into
fix/go-discovery-workspacefrom
fix/hosted-memory-go-pathspace
Draft

Mikola Lysenko (mikolalysenko) wants to merge 3 commits into
fix/go-discovery-workspacefrom
fix/hosted-memory-go-pathspace

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Nov 5, 2026 •

Copy link
Copy Markdown
Collaborator

Stacked on #2011 (fix/go-discovery-workspace).

Problem

The hosted engine hands the golang rewriter the Go workspace it read (HostedGoWorkspace). In memory (ProjectView::Memory) its paths are root-relative with an empty root. The rewriter still judged them with the disk checks: replace_host, rooted_at, selection and host_conflict resolved paths against the process's working directory and probed <cwd>/go.mod. The in-memory engine's answer therefore depended on where the server process was started.

Example: an in-memory project with a root go.mod and a go.work that only has use ./b. If the cwd had no go.mod, the rewriter refused with "nowhere to go: … holds neither go.work nor a go.mod". If the cwd had one, it gave the correct "does not use this module" answer.

Fix

  • The workspace knows where its paths live. GoWorkspace now has files: Option<BTreeSet<PathBuf>>. In memory it lists the root-relative files that exist; on disk it is None. Every method goes through GoWorkspace::paths() (PathSpace::from_files). The _in(PathSpace, ..) variants from Read the governing go.work in Go discovery #2011 and their Disk-default wrappers are removed, so no caller can judge an in-memory workspace against the disk. The rewriter's code is unchanged from the base.
  • One in-memory builder. go_workspace::read_memory builds the in-memory workspace for both the hosted engine and discovery, through a small MemoryFiles reader trait. The engine reads through read_advisory; discovery uses its guarded reads, which diagnose an unparseable member go.mod. A file is in files when it exists, whether readable, a link, an entry without content, or a go.mod go cannot parse. Existence is checked without reading the file.
  • GoWorkspace::selection no longer shows a main-module member through the disk display_path.

I checked the rest of the in-memory engine's Go path: the memory branch of read_go_workspace reads only through the view, and go_build_list::select_in is pure. No other disk, cwd or env probes remain on that path.

Review findings

  1. files counted readable, not existing, files; engine and discovery built it differently. Confirmed, fixed. The engine skipped a symlinked or content-less root go.mod. Discovery skipped an unparseable one. Both now go through read_memory, which uses existence. The new tests hosted::engine::tests::memory_go_workspace_never_probes_the_cwd and vex::discover::golang::tests::in_memory_workspace_files_are_the_files_that_exist use the same three projects: a valid, an unparseable and a symlinked root go.mod, with use ./b. Both expect the same files and the "not a member" verdict. Under the old engine rule the symlinked case loses go.mod and fails. Note: in discovery the root go.mod's presence is only consulted for a parsed go.mod, so its old rule never changed a verdict; it is aligned anyway.
  2. members_within counted ../out as in-root in memory. Confirmed, fixed. Only all-Normal relative paths count as within the root, so absolute members are excluded too. memory_members_within_excludes_members_outside_the_root fails without the fix (["a", "../out", "/abs", ""]) and also checks the NoModule member hint.
  3. Test helper memory_go_files ignored a root go.mod outside the members. Fixed. go_workspace_key and memory_go_files now take root_go_mod. with_root_go_work passes whether the fixture has one.
  4. Store the PathSpace on the workspace. Done. See Fix above.
  5. Shared in-memory reader. Done. read_memory returns the workspace and its in-root members. Discovery derives its sum files from those members.
  6. PathSpace::from_files. Added, used by GoWorkspace::paths. Discovery's Workspace::paths and HostedGoWorkspace::paths are gone.
  7. Root go.mod read only for .is_some(). Fixed. It is now an existence check. The engine's check also counts unreadable entries.
  8. read_go_workspace pub(crate) only for a test. Fixed. The test moved into hosted::engine's tests and the function is private again.

Tests

  • cargo test -p socket-patch-core --lib -- hosted:: vex::discover:: vendor::go golang: 912 passed.
  • CLI --test hosted_memory_engine: 49 passed. --test hosted_memory_parity: 42 passed.
  • cargo clippy --locked --workspace --all-features -- -D warnings and cargo fmt --all -- --check are clean.

Generated by Claude Code

The hosted engine hands the golang rewriter the Go workspace it read
(HostedGoWorkspace). In memory its paths are root-relative, but the
rewriter still judged them with the disk checks: replace_host,
rooted_at, selection and host_conflict canonicalized against the
process's cwd and probed `<cwd>/go.mod`. So an in-memory project whose
root module go.work does not `use` got "nowhere to go" (no go.mod)
when the server's cwd had none, and the member answer when it did.

HostedGoWorkspace now carries `files`: in memory, the root-relative
files that exist (go.work, a readable root go.mod, each in-root
member's readable go.mod), filled by the engine as discovery lists
them. The rewriter routes every workspace check through
`PathSpace::Memory` when it is set, via new `_in` variants of
`replace_host`, `selection`, `host_conflict` and `conflict` on
GoWorkspace / Loaded. Disk behaviour is unchanged (the old names
delegate with `PathSpace::Disk`).

`GoWorkspace::selection` also displayed a main-module member through
the disk display_path; discovery's build-list graph now carries the
workspace's PathSpace so in-memory discovery uses `selection_in` too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- GoWorkspace carries its own `files` (in memory: the root-relative
  files that exist; `None` on disk) and every method routes through
  `GoWorkspace::paths()`. The `_in(PathSpace, ..)` variants and their
  Disk-default wrappers are gone, so no caller can judge an in-memory
  workspace against the disk or the cwd. HostedGoWorkspace::files and
  its paths() go with them; the rewriter's code is back to the base's.
- One builder for the in-memory workspace, go_workspace::read_memory,
  over a MemoryFiles reader: the hosted engine reads through
  read_advisory, discovery through its guarded reads (diagnosing an
  unparseable member go.mod). Both list a file in `files` when it
  exists (a link, an entry without content, a go.mod go cannot parse
  all count), checked without reading it, and both drop an unparseable
  member go.mod's text.
- members_within no longer counts `../out` or an absolute member as
  inside the empty in-memory root (strip_prefix("") matched them), so
  the NoModule hint names in-root members only.
- PathSpace::from_files builds the space from an optional file list.
- The cwd test moved to hosted::engine's tests; read_go_workspace is
  private again. The redirect fixtures list the root go.mod whenever
  the project has one, as read_memory does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant