Skip to content

test(cmd/gc): add requireNoLeakedDoltAfter helper for dolt-leak detection#1795

Open
quad341 wants to merge 5 commits intogastownhall:mainfrom
quad341:builder/ga-b4gug-2
Open

test(cmd/gc): add requireNoLeakedDoltAfter helper for dolt-leak detection#1795
quad341 wants to merge 5 commits intogastownhall:mainfrom
quad341:builder/ga-b4gug-2

Conversation

@quad341
Copy link
Copy Markdown
Collaborator

@quad341 quad341 commented May 7, 2026

What this changes

Adds a test-only requireNoLeakedDoltAfter helper that snapshots live
dolt sql-server PIDs (via /proc) at registration time and re-scans
in t.Cleanup. Any new PID present at cleanup is reported via
t.Errorf with PID + argv, surfacing the dolt-leak class of bugs that
have repeatedly caused OOMs and identity-drift incidents in the past
(see parent ga-de27g, related ga-3ski1).

The helper is wired into writeCityRuntimeConfig* writers in
city_runtime_test.go, so all 14 callers — including the 13
TestCityRuntimeReload* tests — get auto-coverage without the bead
having to enumerate them.

⚠️ Stacked on the identity contract chain (PR #1791#1793
#1794 → this). The dolt-leak helper itself is not semantically
dependent on the identity work — the builder happened to author both
on the same branch. Will show four commits while the lower three PRs
are open.

Review notes

  • Test-only diff: cmd/gc/path_helpers_test.go (+59) and
    cmd/gc/city_runtime_test.go (+4) for the writer integration.
  • The helper is package-private to cmd/gc — no public API.
  • clearInheritedBeadsEnv gap filled in
    writeCityRuntimeConfigWithShutdownTimeout; this was uncovered
    during integration.
  • The failure-mode test (synthetic-leak injection) is deferred to
    follow-up ga-vux42u (validator's responsibility per role boundary
    — builders don't author new tests against tests they wrote).

Test plan

🤖 Deployed by actual-factory (stacked on #1794)


View in Codesmith
Need help on this PR? Tag @codesmith with what you need.

  • Let Codesmith autofix CI failures and bot reviews

Claude Code (gascity/builder) and others added 5 commits May 6, 2026 22:30
Child 1 of 4 from ga-ich5z (parent ga-3ski1) — establish the canonical
L1 path helper and the strict-on-parse, lenient-on-read accessor for
project identity.

- ProjectIdentityPath(scopeRoot) centralizes "<scopeRoot>/.beads/identity.toml"
  so doctor, error messages, and reconcile name the file consistently.
- ReadProjectIdentity loads the L1 file with BurntSushi/toml strict
  parsing (md.Undecoded() rejects unknown top-level keys and unknown
  keys inside [project]). Returns ("", false, nil) for: missing file,
  missing [project] section, empty/whitespace-only project.id —
  callers must treat all four as "L1 not yet populated" (legacy rig).
  Returns wrapped error for malformed TOML, unknown keys, and IO
  failures (errors.Is(err, os.ErrNotExist) does not match — IsNotExist
  is the file-absent short-circuit, not an error path).

Tests cover designer §5 subtests A1-A12 + C1-C2: 12 read behaviors
(missing, valid, trim, empty/whitespace id, missing section, malformed
TOML, extra top-level/project keys, permission error, comments, UTF-8
round-trip) plus 2 path behaviors (joins scope root, canonicalizes
trailing slash). Permission-error subtest is skipped under root.

Out of scope here, per ga-401s4 acceptance:
- WriteProjectIdentity (child 2, ga-7o5mb)
- TestNoExternalIdentityWriters lint guard (child 3, ga-b4gug)
- gitignore !.beads/identity.toml entries (child 4, ga-4tg3j — closed)
Adds WriteProjectIdentity to the contract package — slice 2 of 4 of
the identity contract foundation (parent ga-ich5z). Built on top of
slice 1 (ga-401s4) which introduced the read path.

WriteProjectIdentity validates the id (rejects empty, whitespace-only,
and ids containing newline/CR/quote/backslash), creates the .beads/
directory if missing, and writes via fsys.WriteFileIfChangedAtomic so
the operation is atomic and idempotent.

Tests B1-B10 from designer §5 cover body byte-equality, idempotent
writes (no inode churn), overwrite-changes-inode, all four input
rejections, dotbeads dir creation, round-trip through ReadProject-
Identity, and concurrent same-id safety. Existing A and C subtests
unchanged. -count=1 -race passes; go vet and golangci-lint clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
TestNoExternalIdentityWriters walks the repo and asserts no .go file
outside internal/beads/contract/ contains the literal "identity.toml".
This is subtest D1 from designer §5 (ga-ich5z) and the third of four
slices implementing the identity-contract foundation.

V1 implementation is a coarse byte-level grep — the bead spec
explicitly defers AST-level filtering. False positives are absorbed
through identityWriterAllowlist (currently empty by design). Skip list
covers vendor/.git/node_modules/_test.go (per spec) plus repo-local
defensive entries (.gc/.claude/worktrees) for performance.

Verified the test catches violations with a temporary probe file
outside the contract package; all current production code is clean
because every existing mention lives in identity.go (the writer)
or identity_test.go (test fixtures, exempt as a _test.go file).

Runs in ~30ms with race detector clean. golangci-lint 0 issues.
Adds a dolt-leak detector that snapshots live `dolt sql-server` PIDs
via /proc at registration time and re-scans in t.Cleanup. Any new PID
present at cleanup is reported via t.Errorf with PID and argv so the
spawn site is traceable. The helper degrades to a no-op on hosts
without /proc.

Wired into the three leaf city.toml writers in city_runtime_test.go:
writeCityRuntimeConfigNamed, writeCityRuntimeConfigWithShutdownTimeout,
writeCityRuntimeConfigWithIncludes. Every test that writes a city
runtime config now gets the leak check automatically.

Pairs with the existing clearInheritedBeadsEnv: that helper prevents
the leak by stripping inherited GC_BEADS=bd before the city.toml is
written; this helper catches any leak that slips through.

Also fixes a gap in writeCityRuntimeConfigWithShutdownTimeout, which
was missing the clearInheritedBeadsEnv call that its three siblings
already had.

Verified against TestCityRuntimeReload* (the original ga-de27g
regression target): all 13 tests pass with no false-positive leak
reports. Helper reuses the existing discoverDoltProcesses() reaper
discovery — no duplicated /proc-walking logic.

Out of scope:
- Regression test that deliberately spawns a leaked dolt to verify
  the helper t.Errorf's. Filed as a needs-tests bead for the
  validator.
- Race in TestCityRuntimeManualReloadPanicAfterReloadKeepsReloadReplyAndClears
  (order_dispatch.go:81 logDispatchError concurrent bytes.Buffer
  writes); pre-existing, unrelated to this change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Verdict: PASS at HEAD 79b3e64 on quad341:builder/ga-b4gug-1.
Test-only addition; not semantically dependent on identity chain
but stacked on PR gastownhall#1794 because the builder authored both.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@quad341 quad341 requested a review from julianknutsen as a code owner May 7, 2026 10:20
@github-actions github-actions Bot added the status/needs-triage Inbox — we haven't looked at it yet label May 7, 2026
@codecov-commenter
Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.00000% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/beads/contract/identity.go 90.00% 2 Missing and 2 partials ⚠️

📢 Thoughts on this report? Let us know!

@randy-release-manager randy-release-manager Bot added kind/chore Internal improvement (refactor, tests, CI, tooling) priority/p2 Medium — real problem, workaround exists and removed status/needs-triage Inbox — we haven't looked at it yet labels May 7, 2026
@julianknutsen julianknutsen added status/needs-review-auto PR review requested with auto approval status/reviewing and removed status/needs-review-auto PR review requested with auto approval labels May 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/chore Internal improvement (refactor, tests, CI, tooling) priority/p2 Medium — real problem, workaround exists status/reviewing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants