Skip to content

fence: go.sum completeness across the multi-module tree (#976) - #985

Merged
c03rad0r merged 6 commits into
mainfrom
fix/976-gosum-fence
Oct 26, 2026
Merged

c03rad0r merged 6 commits into
mainfrom
fix/976-gosum-fence

Conversation

@Amperstrand

Copy link
Copy Markdown
Collaborator

What

tests/contract/check-gosum-completeness.sh — the fence for the #975 class: a merge can change one module's import surface (e.g. #938 adding nip19 to package config_manager) and leave every importer's go.sum incomplete. go build then fails with missing go.sum entry, but only for that module — and no gate owned that beat (check-deps-sync.py = go.mod Require sync; per-PR verification builds the PR's touched modules, not every importer). Only the 2026-10-16 night battery caught it, hours after merge.

Shape (per #976's proposal, and the repo's fence patterns)

  • Probe = the exact failing command: go build ./... from inside each module dir (ci: go test fails in git worktrees — 'main module does not contain package' error #961 discipline), with GOWORK=off GOFLAGS=-mod=readonly — no workspace rescue, no re-tidy rescue; each module resolves against its own go.sum.
  • Glob discovery, never enumeration: every go.mod under src/ (18 today; a new module must not need a fence edit).
  • Loud, categorized failure: names the modules, quotes the missing go.sum entry lines, prints the go mod tidy fix hint to stderr; non-gosum build failures are reported separately (still red — a fence that hides a red behind a category is decoration).
  • Fast: ~2 s warm for all 18 modules (test-only import gaps stay with the battery's go test legs — that boundary is stated in the header).
  • Wired: release-check.sh deps leg (the per-push shape) + both test.yml twins' deps-and-imports jobs, one identical step each.

Negative controls — both proven

  1. Hermetic (--selftest): temp-copies a real module, strips the go.sum lines of a dependency chosen from that module's own go list -deps output (so the fixture cannot rot with dep churn), and requires the probe to go red. Verified: selftest ok: stripping 'github.com/ImVexed' go.sum lines in a temp copy of config_manager fails the probe. (A naive "delete the last go.sum line" fixture was tried first and did not fail — not every sum line is build-required; the graph-derived victim is the fix.)
  2. Live (the battery rule): on current main the fence goes RED on exactly src/merchant_types, src/upstream_detector, src/upstream_session_manager — the fix(deps): restore battery-green main — bech32 go.sum entries for #938's nip19 import surface #975 breakage, named, with the btcsuite/btcd/btcutil/bech32 missing-entry lines quoted, exit 1. Full transcript in the verification section below. This is the fence working as designed: main stays red until fix(deps): restore battery-green main — bech32 go.sum entries for #938's nip19 import surface #975 lands — sequencing note for the owner: land fix(deps): restore battery-green main — bech32 go.sum entries for #938's nip19 import surface #975 first and this PR's lanes go green, or land this first and accept the temporary red (either order proves the fence).

Evidence (this branch)

$ bash tests/contract/check-gosum-completeness.sh
ok   src … ok src/merchant
RED  src/merchant_types — missing go.sum entries:
       …/go-nostr@v0.51.12/nip19/nip19.go:9:2: missing go.sum entry for module providing
       package github.com/btcsuite/btcd/btcutil/bech32 (imported by github.com/nbd-wtf/go-nostr/nip19)
RED  src/upstream_detector — missing go.sum entries: (same class)
RED  src/upstream_session_manager — missing go.sum entries: (same class)
ok   (remaining 12 modules)
== gosum fence: 18 modules probed, 3 go.sum-incomplete, 0 other build failures
== fix: run 'go mod tidy' (or 'go get <dep>') inside: src/merchant_types src/upstream_detector src/upstream_session_manager
(exit 1)

$ bash tests/contract/check-gosum-completeness.sh --selftest
selftest ok: stripping 'github.com/ImVexed' go.sum lines in a temp copy of config_manager fails the probe
(exit 0)

$ time bash tests/contract/check-gosum-completeness.sh >/dev/null 2>&1   # warm
real 0m1.884s

bash -n clean; release-check.sh syntax-clean with the new leg.

Notes

  • Pre-existing drift observed, not touched: the test.yml twins are documented byte-identical (.ngit/README.md) but have substantially diverged on main (the .github twin carries a happy-path job and more). This PR added the identical step to both; reconciling the twins is its own change.
  • No merges; 4 files, one logical change; no secrets.

The #975 class: a merge changed one module's import surface (#938's
nip19 in package config_manager) and left three importers' go.sum
incomplete; every gate that ran stayed green because go.sum
completeness was nobody's beat — check-deps-sync.py covers go.mod
Require sync only, and per-PR verification builds the PR's touched
modules, not every importer. Only the 2026-10-16 night battery caught
it, hours after merge.

tests/contract/check-gosum-completeness.sh probes every module under
src/ (glob discovery, never enumeration) with the exact failing
command — go build ./... from inside the module dir (#961), GOWORK=off
and -mod=readonly so nothing rescues an incomplete go.sum — and fails
loudly naming the modules, quoting the missing-entry lines, with the
go-mod-tidy fix hint. ~2s warm for all 18 modules; test files are the
battery's beat, keeping the fence per-push cheap.

Negative controls, both proven: the hermetic --selftest strips the
go.sum lines of a dependency chosen from the module's own dep graph
(rot-proof) and requires the probe to go red; and the live one — on
current main the fence is RED on exactly merchant_types,
upstream_detector and upstream_session_manager until #975 lands (the
documented sequencing: #975 first, or accept the temporary red).

Wired into release-check's deps leg and both test.yml twins' deps-and-
imports jobs (the twins have drifted on main — pre-existing, noted in
the PR, not touched here).
@Amperstrand

Copy link
Copy Markdown
Collaborator Author

Evidence review (#976 fence — independently verified on x280, live negative control included):

Ready for approving review; tracked on #965.

@Amperstrand

Copy link
Copy Markdown
Collaborator Author

Local CI gate — PR #985: fence: go.sum completeness across the multi-module tree (#976)

Run of scripts/pr-verify.sh (the local CI gate: GitHub Actions is
org-blocked and the ngit lane fires only on maintainer mirror pushes,
so a PR ref gets no CI runs — this comment is the offline verification
evidence, produced on the pinned head SHA).

  • Head: fix/976-gosum-fence @ 5eb29a79b7
  • Base: main @ 9e4702e83b
  • Gate: pr-verify `` on ai-legion, started 2026-10-17 21:18 UTC, wall 371s
  • Logs: /tmp/tollgate-pr-verify/985-20261017T211155Z (on the verifying host)
Leg Result Detail
hygiene PASS no conflict markers in the tracked tree (0s)
contract-fences FAIL red: doc-facts contract dependency sync — see contract-fences.log; 12 checks green; 1 skipped (absent from tree: entry-UI marker path fence) (3s)
packaging-sequential PASS 13 suites (7s)
shell-suites PASS 17 suites (uci-defaults) (40s)
packaging-concurrent PASS 13 suites, all at once (2s)
go-battery FAIL 18 modules — see go-battery.log (319s)

Verdict: 2 leg(s) RED — 4 green.

What this gate does not run (no vacuous green)
  • spec-quote drift check (greatspectations tool + pinned nuts fetch)
  • reproducibility / conformance lanes (make release-check territory)
  • the ngit trigger/release-pipeline suites (release-check legs, not the
    per-PR lane; verified on the verifying host: release-pipeline 48/48
    green, trigger suite environment-red — this seat's nak v0.16.2 does
    not accept nsec-string args to key public, identical on main)
  • router-visible behaviour (PRTA / bench — unit green ≠ router works)
failing-leg log tails
contract-fences — tail
   root: v0.48.0
   tollgate-cvm: v0.31.0
   tollgate_protocol: v0.48.0
   tollwallet: v0.48.0
   upstream_detector: v0.48.0
   upstream_session_manager: v0.48.0
   valve: v0.48.0
   wireless_gateway_manager: v0.48.0

Fix: update all go.mod files to the same version.
     Run 'go mod tidy' in each sub-module after updating.
== gonuts pin fence (offline identity)
ok   an aligned tree (block + single-line carriers) passes
ok   a block-form carrier on a different version
ok   a single-line carrier left behind (the token-recovery shape)
ok   a go.sum that never recorded the truth version (half-update)
ok   a self-consistent pseudo-version integration pin passes OFFLINE (the --latest leg is the release gate for it)
ok   a manifest without the .gonuts key
ok   a tree whose manifest names a pin no carrier has

7 cases, 0 failed
== gonuts pin carriers
  manifest pin (packaging/build-inputs.json .gonuts.version): v0.13.0
  carriers discovered by glob: 5 module(s)
✅ one pin, everywhere: github.com/OpenTollGate/gonuts-tollgate v0.13.0 across 5 carrier(s).
== mirror/relay sync
ok    BLOSSOM_SERVERS: 13 copies identical (https://blossom.primal.net https://blossom.psbt.me https://blossom2.orangesync.tech https://drive.cashu.email)
ok    relay list: 13 copies identical (wss://relay.damus.io wss://nos.lol wss://nostr.mom wss://relay1.orangesync.tech wss://relay2.orangesync.tech)
== stale import paths
✅ All 287 Go source files use correct import paths.
go-battery — tail
=== module: src
ok  	github.com/OpenTollGate/tollgate-module-basic-go	12.667s
?   	github.com/OpenTollGate/tollgate-module-basic-go/cmd/tg-vendor-ie-demo	[no test files]
=== module: src/cli
ok  	github.com/OpenTollGate/tollgate-module-basic-go/src/cli	1.476s
=== module: src/cmd/tollgate-cli
ok  	tollgate-cli	1.855s
=== module: src/cmd/tollgate-cvm
ok  	github.com/OpenTollGate/tollgate-module-basic-go/src/cmd/tollgate-cvm	1.345s
=== module: src/config_manager
ok  	github.com/OpenTollGate/tollgate-module-basic-go/src/config_manager	1.317s
=== module: src/identity
ok  	github.com/OpenTollGate/tollgate-module-basic-go/src/identity	3.056s
=== module: src/lightning
ok  	github.com/OpenTollGate/tollgate-module-basic-go/src/lightning	1.008s
=== module: src/merchant
ok  	github.com/OpenTollGate/tollgate-module-basic-go/src/merchant	282.770s
=== module: src/merchant_types
/root/go/pkg/mod/github.com/nbd-wtf/go-nostr@v0.51.12/nip19/nip19.go:9:2: missing go.sum entry for module providing package github.com/btcsuite/btcd/btcutil/bech32 (imported by github.com/nbd-wtf/go-nostr/nip19); to add:
	go get github.com/nbd-wtf/go-nostr/nip19@v0.51.12
make: *** [Makefile:7: go-battery] Error 1

@Amperstrand

Copy link
Copy Markdown
Collaborator Author

Stamp context (this seat): both reds are the documented tree-age classes — battery = the pre-#975 go.sum trio (this branch adds the fence for that class; the fix is #975, so its own base carries the breakage by design), contract-fences = the pre-#1000 doc-facts/desync drift. Fittingly, the fence this PR ships goes green the moment #975 lands (proven in the release-prep dry-run: 18/18 inside release-check with both merged).

@Amperstrand

Copy link
Copy Markdown
Collaborator Author

Local CI gate — PR #985: fence: go.sum completeness across the multi-module tree (#976)

Run of scripts/pr-verify.sh (the local CI gate: GitHub Actions is
org-blocked and the ngit lane fires only on maintainer mirror pushes,
so a PR ref gets no CI runs — this comment is the offline verification
evidence, produced on the pinned head SHA).

  • Head: fix/976-gosum-fence @ 5eb29a79b7
  • Base: main @ 9e4702e83b
  • Gate: pr-verify 5a703563168f on ai-legion, started 2026-10-18 17:21 UTC, wall 373s
  • Logs: /tmp/tollgate-pr-verify/985-20261018T171523Z (on the verifying host)
Leg Result Detail
hygiene PASS no conflict markers in the tracked tree (0s)
contract-fences FAIL red: doc-facts contract dependency sync — see contract-fences.log; 12 checks green; 1 skipped (absent from tree: entry-UI marker path fence) (4s)
packaging-sequential PASS 13 suites (6s)
shell-suites PASS 17 suites (uci-defaults) (40s)
packaging-concurrent PASS 13 suites, all at once (3s)
go-battery FAIL 18 modules — see go-battery.log (320s)

Verdict: 2 leg(s) RED — 4 green.

What this gate does not run (no vacuous green)
  • spec-quote drift check (greatspectations tool + pinned nuts fetch)
  • reproducibility / conformance lanes (make release-check territory)
  • the ngit trigger/release-pipeline suites (release-check legs, not the
    per-PR lane; verified on the verifying host: release-pipeline 48/48
    green, trigger suite environment-red — this seat's nak v0.16.2 does
    not accept nsec-string args to key public, identical on main)
  • router-visible behaviour (PRTA / bench — unit green ≠ router works)
failing-leg log tails
contract-fences — tail
   root: v0.48.0
   tollgate-cvm: v0.31.0
   tollgate_protocol: v0.48.0
   tollwallet: v0.48.0
   upstream_detector: v0.48.0
   upstream_session_manager: v0.48.0
   valve: v0.48.0
   wireless_gateway_manager: v0.48.0

Fix: update all go.mod files to the same version.
     Run 'go mod tidy' in each sub-module after updating.
== gonuts pin fence (offline identity)
ok   an aligned tree (block + single-line carriers) passes
ok   a block-form carrier on a different version
ok   a single-line carrier left behind (the token-recovery shape)
ok   a go.sum that never recorded the truth version (half-update)
ok   a self-consistent pseudo-version integration pin passes OFFLINE (the --latest leg is the release gate for it)
ok   a manifest without the .gonuts key
ok   a tree whose manifest names a pin no carrier has

7 cases, 0 failed
== gonuts pin carriers
  manifest pin (packaging/build-inputs.json .gonuts.version): v0.13.0
  carriers discovered by glob: 5 module(s)
✅ one pin, everywhere: github.com/OpenTollGate/gonuts-tollgate v0.13.0 across 5 carrier(s).
== mirror/relay sync
ok    BLOSSOM_SERVERS: 13 copies identical (https://blossom.primal.net https://blossom.psbt.me https://blossom2.orangesync.tech https://drive.cashu.email)
ok    relay list: 13 copies identical (wss://relay.damus.io wss://nos.lol wss://nostr.mom wss://relay1.orangesync.tech wss://relay2.orangesync.tech)
== stale import paths
✅ All 287 Go source files use correct import paths.
go-battery — tail
=== module: src
ok  	github.com/OpenTollGate/tollgate-module-basic-go	12.952s
?   	github.com/OpenTollGate/tollgate-module-basic-go/cmd/tg-vendor-ie-demo	[no test files]
=== module: src/cli
ok  	github.com/OpenTollGate/tollgate-module-basic-go/src/cli	1.492s
=== module: src/cmd/tollgate-cli
ok  	tollgate-cli	1.987s
=== module: src/cmd/tollgate-cvm
ok  	github.com/OpenTollGate/tollgate-module-basic-go/src/cmd/tollgate-cvm	1.344s
=== module: src/config_manager
ok  	github.com/OpenTollGate/tollgate-module-basic-go/src/config_manager	1.343s
=== module: src/identity
ok  	github.com/OpenTollGate/tollgate-module-basic-go/src/identity	3.253s
=== module: src/lightning
ok  	github.com/OpenTollGate/tollgate-module-basic-go/src/lightning	1.008s
=== module: src/merchant
ok  	github.com/OpenTollGate/tollgate-module-basic-go/src/merchant	283.296s
=== module: src/merchant_types
/root/go/pkg/mod/github.com/nbd-wtf/go-nostr@v0.51.12/nip19/nip19.go:9:2: missing go.sum entry for module providing package github.com/btcsuite/btcd/btcutil/bech32 (imported by github.com/nbd-wtf/go-nostr/nip19); to add:
	go get github.com/nbd-wtf/go-nostr/nip19@v0.51.12
make: *** [Makefile:7: go-battery] Error 1

@Amperstrand

Copy link
Copy Markdown
Collaborator Author

Evidence review — 13-criteria pass (verified at head, with a bonus live negative)

Fence mechanism verified twice over at this head:

  1. On its own (Oct-17) tree it is correctly RED — 18 modules probed, 3 go.sum-incomplete naming merchant_types / upstream_detector / upstream_session_manager with the tidy fix hint: that is the fix(deps): restore battery-green main — bech32 go.sum entries for #938's nip19 import surface #975-class carriers before main's fixes, i.e. the fence refusing a genuinely broken tree (a live negative control the calendar provided for free).
  2. Mutation: stripping the btcutil entry from merchant_types' go.sum at head → RED with the exact categorized output — module named, missing go.sum entry … bech32 … nip19 quoted, remedy printed. Restored → back to the base state.

Shape review: probe = the exact failing command (go build ./... per-module, GOWORK=off GOFLAGS=-mod=readonly — no workspace/rescue paths, the #961 discipline); glob discovery (18 today, zero fence edits for a 19th); non-gosum build failures stay red in their own category (a fence that hides reds behind categories is decoration — this one doesn't); ~2s warm. Wired into release-check + both test.yml twins identically.

Merge note (same class as #1039): rebase onto current main first — the three carriers are fixed there (#1013/#1032/#1030), so the fence goes green on its own tree; without the rebase it ships red-on-self. After merge: this is the fence the #975/#1013 history justifies, and the natural negative-control home for the cold-cache class #1013's diagnosis named (warm-cache CI passing while cold vet fails — worth one seeded-failure test in the follow-up the body's boundary statement already scopes).

Verdict: mechanically ready post-rebase. Human gate.

@c03rad0r c03rad0r left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Technical review — head 5eb29a7. The fence is right and it has already earned its keep; approve with a sequencing condition.

The shape is exactly the check-ssid-format pattern done well: probe the real failing command (GOWORK=off GOFLAGS=-mod=readonly go build ./...) from inside each glob-discovered module, categorize the failure, and refuse to hide a non-go.sum red behind the category. The hermetic --selftest is the part most fences skip: deleting the last go.sum line did not fail (not every sum is build-required), so the fixture derives its victim from go list -deps — the control is load-bearing.

Verified here:

bash tests/contract/check-gosum-completeness.sh --selftest
selftest ok: stripping 'github.com/ImVexed' go.sum lines in a temp copy of config_manager fails the probe

It also caught a real main break, which I fixed: current main still fails the probe in src/upstream_detector and src/upstream_session_manager (missing btcsuite/btcd/btcec/v2 resolution from go-nostr v0.51.12; #975 was closed unmerged). I opened #1120 with go mod tidy in both modules — all 18 modules now build with GOWORK=off -mod=readonly, so the fence lands green.

Condition: the branch still points at an older main, so at its head the fence is red (3 modules, incl. merchant_types). Rebase onto current main after #1120 lands and re-run the fence before merge — otherwise the PR that adds the fence is the one that turns the lane red.

Disposition: land (after #1120 + rebase).

@c03rad0r
c03rad0r merged commit 7371c7d into main Oct 26, 2026
@c03rad0r
c03rad0r deleted the fix/976-gosum-fence branch October 26, 2026 14:39
c03rad0r added a commit that referenced this pull request Oct 26, 2026
* fix(deps): complete go.sum for the two go-nostr carriers (#975)

`GOWORK=off GOFLAGS=-mod=readonly go build ./...` fails in
src/upstream_detector and src/upstream_session_manager: go-nostr v0.51.12
imports btcsuite/btcd/btcec/v2 and the carriers' go.sum lacks the entries
that resolve it (btcutil v1.1.6). The night battery caught it; no per-PR
gate did, which is what #985's fence is for.

`go mod tidy` in both modules adds the missing sums and drops requires the
tidy graph no longer needs. Verified all 18 module builds green with
GOWORK=off and -mod=readonly, and deps-sync + gonuts-pin fences still pass.

* changelog: fragment for #1120

---------

Co-authored-by: felixfelix-bot <felixfelix-bot@users.noreply.github.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.

2 participants