GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Fix Go replace target missed in go 1.16 graphs (#1840) by mikolalysenko · Pull Request #1843 · SocketDev/socket-patch

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Oct 29, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Collaborator

LLM Description written by Claude Code:claude-opus-5-5

Fixes #1840

What was wrong

Take a go 1.16 module (or one with no go directive) where a dependency imports other.com/fork, and the root go.mod has replace other.com/fork => example.com/upstream v1.0.0. A patch for example.com/upstream v1.0.0 was reported as applied. apply --check said it was in sync, and vex attested not_affected. Meanwhile go build linked the unpatched code, because go doesn't chain replaces.

Root cause

replaced_module (crates/socket-patch-core/src/vendor/go_build_list.rs) decides whether a module-to-module replace explains the upstream's go.sum zip line. It only counted the replace when the root go.mod's require listed the fork. In an unpruned graph (go < 1.17), the root go.mod leaves out modules that only a dependency requires. The root's replace still applies to them. Its require versions are also only lower bounds. So go.mod alone can't tell whether the fork is in the build, or at which version.

Fix

  • Read the module graph. When go.mod is unpruned and has a user-authored module-to-module replace, ModuleGraph reads it from GOMODCACHE. It reuses GoWorkspace::read_graph_from, the walker go.work projects already use (as a one-member workspace). The fork's MVS-selected version then decides whether the replace is in effect. That covers a fork only a dependency requires, a fork a dependency raises past go.mod's require, a versioned replace shadowing a version-less one, and a stale replace that go mod tidy kept but nothing uses.
  • One rule for every caller. select_in / selection_in take the graph. All callers use it: project_selection (agent apply, apply --check, CLI vex), the hosted rewriter (through HostedGoWorkspace.module_graph, read by the hosted engine), and VEX lockfile discovery. The graph is only read when the go.mod needs it. The files it reads in the project (root go.mod, directory replacements' go.mod) are declared to the snapshot view, so cached discovery stays valid.
  • Safe fallback. Without a complete graph (the module cache lacks a needed go.mod, or an in-memory view), a replace that may be in effect counts as in effect, so no patch is reported built that isn't. The one exception is when go.mod requires module@version itself: go refuses one module@version under two paths. That guess only refuses a new hosted pin. It never removes an existing one (proves_replace_target). Only a pruned go.mod or a complete graph removes a stale pin.

Tests (red → green)

Issue / case Test Without fix With fix
#1840 repro: fork required only by a dep, versionless and versioned replace e2e_golang_build::fork_required_only_by_a_dependency_is_a_replace_target apply --check exit 0 "Patches are in sync" (build prints PRISTINE) refuses / exit 1 / not attested
#1840 unit: transitive fork in go 1.16 / no directive go_build_list::tests::unpruned_replace_of_a_transitive_fork_is_a_replace_target fails (Selected) passes
Stale fork replace must not block a working patch (review finding) e2e_golang_build::stale_fork_replace_keeps_a_transitive_patch fails on the first (guess-only) commit: apply refused apply 0, build PATCHED, check 0
Graph settles raised/shadowed/stale forks; incomplete graph falls back safely go_build_list::tests::unpruned_module_graph_settles_the_fork_replace, unpruned_root_require_of_another_version_keeps_the_fork_replace, only_a_pruned_go_mod_or_a_complete_graph_proves_a_replace_target, module_graph_is_needed_only_for_an_unpruned_fork_replace n/a (new API) pass
Graph walk reaches a dep-only fork go_workspace::tests::module_graph_reaches_a_fork_only_a_dependency_requires n/a pass
Hosted rewriter uses the graph; an unproven guess keeps an existing pin redirect::tests::golang_go116_module_graph_decides_the_fork_replace, golang_go116_suspected_replace_target_keeps_the_hosted_pin n/a pass

One existing assertion changed meaning. In inactive_replace_target_keeps_the_zip_as_evidence, the go 1.16 cases "fork not required" and "version-less replace shadowed at go.mod's required version" encoded the bug: go.mod alone can't show the fork is absent, or that a dependency didn't raise it. They now run only for go 1.21. The go 1.16 behavior is covered with and without a graph in unpruned_module_graph_settles_the_fork_replace.

Commands run locally (Linux, go 1.24.7):

  • cargo fmt --all -- --check: clean
  • cargo clippy --workspace --all-features -- -D warnings: clean
  • cargo test -p socket-patch-core --all-features: 6999 passed, 4 failed. The 4 failures are chmod-based write-failure tests (copy_tree, vlt_heal, pypi_poetry, pypi_requirements) that can't fail as root (this container runs as uid 0). They're unrelated to Go, and none of their modules is touched by this PR.
  • cargo test -p socket-patch-cli --all-features --lib plus every Go-touching suite with SOCKET_PATCH_GO_E2E_REQUIRED=1: e2e_golang_build (33), e2e_golang_hosted_build (25), e2e_golang_workspace_build (31), e2e_vendor_golang_build (44), e2e_golang, e2e_golang_hosted_state, e2e_vex, e2e_vex_redirect, e2e_vex_vendor, ecosystem_dispatch_e2e, in_process_*, cli_apply_silent: all pass.
  • The full cargo test --workspace (189 CLI test binaries) doesn't fit this container's disk, so CI covers the rest.

/code-review high findings not fixed

  • Module-cache reads aren't in the snapshot read set. If the cache gains a missing go.mod between two passes of one command, a reused discovery keeps its safe verdict ("not built") until the next run. Cached module versions are immutable, and the stale answer is always the conservative one.
  • exclude directives aren't applied by the graph walker. That's the same pre-existing limitation as go.work mode. An excluded fork version can only make a patch refused, never falsely attested.
  • "Graph rebuilt per patched module" in project_selection: the graph is read only for a module that an unpruned go.mod's fork replace targets (needs_module_graph), not for every patch.
  • let in_effect = …; in_effect in replaced_module is required: the iterator borrows entries, a local, so it can't be the tail expression (E0597). Clippy is clean.

Out of scope: GoWorkspace::selection never returns ReplaceTarget, so the #1823/#1840 shapes inside a go.work workspace aren't detected. That's pre-existing and not part of #1840.

https://claude.ai/code/session_011f9aeNyyce3TLFE4vQrpsJ


Generated by Claude Code

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@claude
Start fix for #1840
7d67e26
Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko Mikola Lysenko (mikolalysenko) mentioned this pull request Oct 29, 2026
Go apply, apply --check and vex still treat a replace-target module as built when the replaced fork is required only by a dependency (go 1.16 module), so the patch is attested while the build stays unpatched #1840
Closed
Claude (claude) added 4 commits October 29, 2026 18:35
@claude
Refuse Go patches built only via a dep's fork
3e22f8d
In a go 1.16 module, when a dependency imports a fork that the root
go.mod replaces with the patched upstream module, apply reported the
patch applied, apply --check said in sync and vex attested
not_affected, while go build linked the unpatched code.

The root go.mod of an unpruned graph does not list modules that only
a dependency requires, but its replace still applies to them. The
replace-target check only trusted a fork listed in the root require,
so it missed this shape. Now, in an unpruned graph, such a replace
counts as in effect unless go.mod requires the patched module itself.

Fixes #1840

Assisted-by: Claude Code:claude-opus-5-5
@claude
Read the go 1.16 module graph for fork replaces
208eaa6
The first fix guessed that a fork replace in a go 1.16 module is in
effect whenever go.mod doesn't list the fork. That refused working
patches when go mod tidy kept a replace nothing uses, and the hosted
rewriter then removed a working Socket replace. It also missed a fork
a dependency raises to the replaced version.

Now, when an unpruned go.mod has a module-to-module replace, the
module graph is read from the module cache, the same walk go.work
projects use, and the fork's selected version decides. apply,
apply --check, the hosted rewriter and VEX discovery all use it. When
the cache lacks go.mod files, the safe answer stays: the patch is not
reported built.

Refs #1840

Assisted-by: Claude Code:claude-opus-5-5
@claude
Declare the go.mod files the graph walk reads
ed91ca0
Reading the go 1.16 module graph went around the project view, which
ended read recording for every project: VEX discovery asked for the
raw disk root even with no Go files, so discovery could no longer be
reused between passes. Now the graph is read only when go.mod needs
it, and the root go.mod and any directory replacement's go.mod are
declared as reads, so a change to them still invalidates the cache.

Refs #1840

Assisted-by: Claude Code:claude-opus-5-5
@claude
Keep a hosted Go pin on an unproven fork replace
4e9eebf
Without every go.mod of a go 1.16 module graph in the module cache,
a fork replace is only suspected of bypassing the Socket pin. That
guess now refuses a new pin but no longer removes an existing one
that may be working. A partial graph is no longer trusted as proof
that the fork is at a lower version. Socket's own hosted replaces no
longer trigger a graph walk.

Refs #1840

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

Mikola Lysenko (mikolalysenko) commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

cursor[bot] reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

cursor[bot]
cursor Bot reviewed Oct 29, 2026
View reviewed changes

@cursor cursor Bot left a comment •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

There was a problem hiding this comment.

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.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
This was referenced Oct 29, 2026
CI performance dashboard #1182
Open
Swarm health manager log #1382
Open
@mikolalysenko Mikola Lysenko (mikolalysenko) added the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 29, 2026
@mikolalysenko

Mikola Lysenko (mikolalysenko) commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator Author

Burn-down agent: labeled Ready for review at 4e9eebf6171c934fc22bd3f3672e94156ff33cd4.

  • CI: 32/32 run checks green (50 skipped), no failures.
  • Bugbot: reviewed 4e9eebf with no findings; no open review threads.
  • CHANGELOG.md: untouched.
  • For the reviewer: the core change is replaced_module / ModuleGraph in crates/socket-patch-core/src/vendor/go_build_list.rs — unpruned (go < 1.17) go.mods now read the module graph from GOMODCACHE to decide whether a fork replace is in effect, falling back to "in effect" when the graph is incomplete. One existing test's go 1.16 cases changed meaning (see the PR body).

Generated by Claude Code

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@mikolalysenko

Mikola Lysenko (mikolalysenko) commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator Author

Final review brief

What it does. In a go 1.16 (unpruned) module, a root replace other.com/fork => example.com/upstream v1.0.0 applies even when only a dependency requires the fork, so go build links the fork path and our patch for example.com/upstream never lands. We used to report that patch as applied/in sync/not_affected. Now, for unpruned go.mods with a module-to-module replace, we read the module graph from GOMODCACHE to see whether the fork is really selected. If the graph is incomplete, we treat the replace as in effect, so we refuse rather than falsely attest.

Risk: medium. It changes the selection rule that apply, apply --check, vex and the hosted rewriter share. The only behavior change is for go < 1.17 projects with a fork replace, and every fallback errs toward "not built". The graph is only read in that case, so other projects pay nothing.

Look here

  • replaced_module decision table:`` Selected / Absent / AtLeast / Unknown, and the built_itself exception
  • proves_replace_target and its use in the hosted rewriter: a guess never removes an existing hosted pin
  • module_graph: reuses the go.work graph walker as a one-member workspace
  • Hosted engine read set: root go.mod and directory-replacement go.mods are declared to the snapshot
  • e2e: fork_required_only_by_a_dependency_is_a_replace_target, stale_fork_replace_keeps_a_transitive_patch

Verified

  • Read the full non-test diff (go_build_list, go_workspace, redirect, hosted engine, vex discovery). All three callers go through select_in with the same graph.
  • Checked that Loaded::None plus module_graph is handled downstream like the existing modfile-only case (selection and host_conflict are no-ops).
  • Checked that a missing GOMODCACHE gives an incomplete graph whenever the root requires anything, so the fallback is the conservative one.
  • Windows: module_graph_reads normalizes \\ to / and joins onto the root.
  • CI 32/32 green on 4e9eebf. Bugbot reviewed 4e9eebf with no findings. No open threads. CHANGELOG.md untouched.

Changes I made: none.

Open questions: none blocking. The PR body lists known non-goals: exclude directives, module-cache reads outside the snapshot read set, and go.work workspaces not detecting this shape.

Auto-merge (squash) is armed, so approving sends it straight to the merge queue.


Generated by Claude Code

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@mikolalysenko
Mikola Lysenko (mikolalysenko) enabled auto-merge October 29, 2026 21:50
This was referenced Oct 29, 2026
On a go 1.16 go.mod, Go apply, apply --check and vex trust a stale go.sum zip line left by a downgrade, so they report an inert replace as applied (or refuse a selected version) #1862
Closed
Fix Cargo member globs with [..] classes (#1833) #1866
Merged
Fix Go stale go.sum line after a downgrade (#1862) #1893
Merged
@mikolalysenko

Mikola Lysenko (mikolalysenko) commented Oct 30, 2026

Copy link
Copy Markdown
Collaborator Author

[agent] Heads-up: #1893 (fix for #1862) is stacked on this branch; it reuses the ModuleGraph reader added here. Please merge this one first.


Generated by Claude Code

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@mikolalysenko Mikola Lysenko (mikolalysenko) mentioned this pull request Nov 2, 2026
Sweep core utils/python_{lock,script}.rs #1990
Merged
Wenxin-Jiang
Wenxin Jiang (Wenxin-Jiang) approved these changes Nov 2, 2026
View reviewed changes
@mikolalysenko
Mikola Lysenko (mikolalysenko) added this pull request to the merge queue Nov 2, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Nov 2, 2026
Wenxin-Jiang
Wenxin Jiang (Wenxin-Jiang) approved these changes Nov 2, 2026
View reviewed changes

@Wenxin-Jiang Wenxin Jiang (Wenxin-Jiang) left a comment

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

[agent] Reviewed 4e9eebf6171c934fc22bd3f3672e94156ff33cd4. Traced unpruned Go replacement targets through cached dependency graphs, hosted rewriting, local apply and VEX, including incomplete-graph conservatism. No major correctness issues found. Validation: source and regression-test review plus the available exact-head CI checks; no local test pass is claimed.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@claude
Merge remote-tracking branch 'origin/main' into agent/fix-go-unpruned…
9e958e5
…-replace-target

# Conflicts:
#	crates/socket-patch-cli/tests/e2e_golang_build.rs
@mikolalysenko

Mikola Lysenko (mikolalysenko) commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

cursor[bot] reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

cursor[bot]
cursor Bot reviewed Nov 2, 2026
View reviewed changes

@cursor cursor Bot left a comment

Copy link
Copy Markdown

There was a problem hiding this comment.

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 9e958e5. Configure here.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@mikolalysenko

Mikola Lysenko (mikolalysenko) commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator Author

[agent] The merge queue dropped this PR on a conflict in e2e_golang_build.rs: main (#1698) changed the stage helper to take its go directive from the toolchain matrix. I merged main in at 9e958e5. The upstream go directive is now a bare version, and stage still defaults to the matrix value. CI on 9e958e5 is green (31 success, 19 skipped), Bugbot is clean, and both approvals still stand. Auto-merge was turned off when the PR left the queue, so it needs re-queuing.


Generated by Claude Code

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@mikolalysenko
Mikola Lysenko (mikolalysenko) added this pull request to the merge queue Nov 2, 2026
@mikolalysenko

Mikola Lysenko (mikolalysenko) commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator Author

[final reviewer] Re-added to the merge queue at 9e958e5. The queue dropped it at 15:57 UTC on a conflict in e2e_golang_build.rs; main was merged in at 9e958e5, and ci-ok and clippy are green on that head. It is still approved, mergeable and has no open threads.


Generated by Claude Code

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Merged via the queue into main with commit cf9fd1f Nov 2, 2026
50 checks passed

Uh oh!

There was an error while loading. Please reload this page.

@mikolalysenko
Mikola Lysenko (mikolalysenko) deleted the agent/fix-go-unpruned-replace-target branch November 2, 2026 17:36
Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Nov 2, 2026
@claude
Merge main into agent/fix-go-unpruned-stale-zip
222e467
#1843 landed on main with the module-graph plumbing this branch was
built on, so the four Go files conflicted. Keep this branch's go.sum
aware may_need_module_graph/needs_module_graph and the moved module
graph read in vex discover; take main's stage_with(tmp, "1.16")
signature in the e2e test and update this branch's two new callers.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@cursor cursor[bot] cursor[bot] left review comments
@Wenxin-Jiang Wenxin Jiang (Wenxin-Jiang) Wenxin-Jiang approved these changes

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Go apply, apply --check and vex still treat a replace-target module as built when the replaced fork is required only by a dependency (go 1.16 module), so the patch is attested while the build stays unpatched

Uh oh!

There was an error while loading. Please reload this page.

3 participants

@mikolalysenko @Wenxin-Jiang @claude
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402520 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.