Skip to content

Commit 27d23e8

Browse files
author
David Noble
committed
test(plan): close the runtime environments the plan tests mint
The first windows measurement on this PR came back 48 -> 45, not the predicted ~30. Fifteen failures still carried the leak signature, and none of them were the production paths #393 diagnosed: they are test environments that are never closed. Whoever constructs a runtime environment closes it; these tests never did -- before this branch or after -- so relocating the mint could not fix them. A natural experiment inside the plan package proves it. lifecycle_e2e, lifecycle_starlark, and result_flow_starlark each close their environment and none of their tests appear in the failure list; provider_test (2 mints), lifecycle_api (10), gather_api (1), git_resume (1), and receipt_integration (1) close nothing and all of their tests do. Fifteen unclosed mint sites, fifteen leak failures. Add t.Cleanup(func() { _ = environment.Close() }) at all fifteen, matching the idiom the three passing helpers already use. Verified by enumeration rather than assumption: repo-wide, every test file that sets a root now closes it. The files that mint without a root hold no handle, and TestNewRuntimeEnvironment_BadAnchorFails mints nothing because the mint is what fails. The plan document records the miss rather than quietly restating the target: the option-4 verification asked whether anything dereferences spec.Root between spec construction and Run (it does not, and that holds), but never asked who closes the environment in the failing tests. The -18 estimate was inherited from the #393 diagnosis and repeated without its own enumeration. Expected windows effect: 45 -> ~30. Unlike the first estimate, this one names its mechanism -- fifteen unclosed handles, fifteen cleanups. Assisted-by: Claude Signed-off-by: David Noble <[email protected]>
1 parent dd72af4 commit 27d23e8

6 files changed

Lines changed: 61 additions & 2 deletions

File tree

‎cmd/writ/writ/migrate/receipt_integration_test.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ func TestExecutionTrace_SerializesAsMigrationReceipt(t *testing.T) {
3434
if err != nil {
3535
t.Fatalf("op.NewRuntimeEnvironment: %v", err)
3636
}
37+
t.Cleanup(func() { _ = environment.Close() })
3738

3839
planProvider := plan.NewProvider(environment)
3940

‎docs/plans/env-minted-root.md‎

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,51 @@ shape — no error return to join).
206206
x.Close() }()` to named-return + `defer iox.Close(&err, x)`, so close failures join the
207207
result instead of being discarded under a nolint. No production defer-lambda close remains
208208
in `cmd/writ`. (`snapshot_test.go:152` is test-side with no error return — left as is.)
209-
- [ ] PR; `test (windows-latest)` expected at ~30 failures (48 − 18). Count measured against the
210-
head commit's check-runs, uncapped.
209+
- [x] PR [#402](https://github.com/NobleFactor/devlore-cli/pull/402) opened; every leg green
210+
except `test (windows-latest)`, as expected.
211+
212+
#### First windows measurement — 48 → 45, NOT the predicted ~30 (2026-08-13)
213+
214+
Measured against head `dd72af41`'s check-runs, uncapped: **45** `--- FAIL` lines, zero
215+
`[build failed]`. The framework change delivered **−3, not −18**, and the estimate this plan
216+
carried was wrong.
217+
218+
**Why.** 15 of the 45 still carry the leak signature (`TempDir RemoveAll cleanup: … being used by
219+
another process`), and every one of them is a **test-side runtime environment that is never
220+
closed** — not a production path. The framework contract is that whoever constructs an
221+
environment closes it; these tests construct one and never do. That was true before this change
222+
(the environment held `spec.Root`, unclosed) and remained true after (the environment mints its
223+
own Root, unclosed). Relocating the mint could not fix it.
224+
225+
The proof is a natural experiment inside one package: `lifecycle_e2e_test.go`,
226+
`lifecycle_starlark_test.go`, and `result_flow_starlark_test.go` each close their environment and
227+
**none of their tests appear in the failure list**; `provider_test.go` (2 mints),
228+
`lifecycle_api_test.go` (10), `gather_api_test.go` (1), `git_resume_test.go` (1), and
229+
`receipt_integration_test.go` (1) close nothing and **all of their tests do**. 15 unclosed mint
230+
sites, 15 leak failures.
231+
232+
**The estimate's error, named:** the #393 diagnosis attributed these 18 failures to production
233+
pre-Run error paths and the resume family. Those production leaks were real and are fixed (lore's
234+
closed-Root loop, verify/readback never closing, devlore-test's aliasing) — but they were not what
235+
these tests were failing on. The verification that preceded the option-4 ruling asked whether
236+
anything dereferences `spec.Root` between spec construction and Run (it does not, and that holds);
237+
it never asked who closes the environment in the failing tests. Inherited estimates get their own
238+
enumeration — see [[verify-ledger-facts-by-enumeration]].
239+
240+
**No regressions:** the other 30 failures are the standing write/path/chown grind plus the known
241+
singles, unchanged, and no failure anywhere traces to the new mint or preflight code paths.
242+
243+
### Phase 6: close the 15 unclosed test environments — status: complete (2026-08-13)
244+
245+
- [x] `t.Cleanup(func() { _ = environment.Close() })` at all 15 mint sites across the five files
246+
above, matching the idiom the three already-passing helpers use.
247+
- [x] Verified by enumeration rather than assumption: repo-wide, every test file that sets a root
248+
now closes it. The files that mint without a root (`pkg/op` internals, `instance_test.go`,
249+
`builder_test.go`) hold no handle; `TestNewRuntimeEnvironment_BadAnchorFails` mints nothing
250+
because the mint is what fails.
251+
- [x] `make test` zero failures; `make lint-all` 0 issues per GOOS; gofmt clean.
252+
- [ ] Second windows measurement. Expected 45 → ~30 — and unlike the first estimate, this one
253+
names its mechanism: 15 unclosed handles, 15 cleanups.
211254

212255
**Known non-blocker:** `make check` fails its `complexity` step on two pre-existing functions this
213256
branch never touched — `git guessDirName` (27) and `cli runSelfInstall` (22). The gate is

‎pkg/op/provider/plan/gather_api_test.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ func TestGatherFailureUnwind_ViaPublicAPI(t *testing.T) {
3737
if err != nil {
3838
t.Fatalf("op.NewRuntimeEnvironment: %v", err)
3939
}
40+
t.Cleanup(func() { _ = environment.Close() })
4041

4142
planProvider := plan.NewProvider(environment)
4243

‎pkg/op/provider/plan/git_resume_test.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ func gitCloneResumeThenFail(t *testing.T, format string) {
5757
if err != nil {
5858
t.Fatalf("op.NewRuntimeEnvironment: %v", err)
5959
}
60+
t.Cleanup(func() { _ = environment.Close() })
6061
planProvider := plan.NewProvider(environment)
6162

6263
dirA := filepath.Join(tmp, "a")

‎pkg/op/provider/plan/lifecycle_api_test.go‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ func TestGraphSaveLoadExecuteTrace_ViaPublicAPI(t *testing.T) {
4949
if err != nil {
5050
t.Fatalf("op.NewRuntimeEnvironment: %v", err)
5151
}
52+
t.Cleanup(func() { _ = environment.Close() })
5253

5354
planProvider := plan.NewProvider(environment)
5455

@@ -131,6 +132,7 @@ func TestGraphPauseResume_ViaPublicAPI(t *testing.T) {
131132
if err != nil {
132133
t.Fatalf("op.NewRuntimeEnvironment: %v", err)
133134
}
135+
t.Cleanup(func() { _ = environment.Close() })
134136

135137
planProvider := plan.NewProvider(environment)
136138

@@ -213,6 +215,7 @@ func TestGraphPauseResumeNested_ViaPublicAPI(t *testing.T) {
213215
if err != nil {
214216
t.Fatalf("op.NewRuntimeEnvironment: %v", err)
215217
}
218+
t.Cleanup(func() { _ = environment.Close() })
216219

217220
planProvider := plan.NewProvider(environment)
218221

@@ -291,6 +294,7 @@ func TestGraphSaveLoadResume_ViaPublicAPI(t *testing.T) {
291294
if err != nil {
292295
t.Fatalf("op.NewRuntimeEnvironment: %v", err)
293296
}
297+
t.Cleanup(func() { _ = environment.Close() })
294298

295299
planProvider := plan.NewProvider(environment)
296300

@@ -401,6 +405,7 @@ func resumeThenFailRollsBack(t *testing.T, format string) {
401405
if err != nil {
402406
t.Fatalf("op.NewRuntimeEnvironment: %v", err)
403407
}
408+
t.Cleanup(func() { _ = environment.Close() })
404409
planProvider := plan.NewProvider(environment)
405410

406411
dirA := filepath.Join(tmp, "a")
@@ -501,6 +506,7 @@ func resumePromiseFidelity(t *testing.T, format string) {
501506
if err != nil {
502507
t.Fatalf("op.NewRuntimeEnvironment: %v", err)
503508
}
509+
t.Cleanup(func() { _ = environment.Close() })
504510
planProvider := plan.NewProvider(environment)
505511

506512
dir := filepath.Join(tmp, "d")
@@ -606,6 +612,8 @@ func TestResourceLedgerRehydrate_PreservesIDs(t *testing.T) {
606612
if err != nil {
607613
t.Fatalf("op.NewRuntimeEnvironment: %v", err)
608614
}
615+
t.Cleanup(func() { _ = environment.Close() })
616+
609617
resource, err := file.NewRegular(environment, "", filepath.Join(tmp, "x"))
610618
if err != nil {
611619
t.Fatalf("file.NewRegular: %v", err)
@@ -620,6 +628,7 @@ func TestResourceLedgerRehydrate_PreservesIDs(t *testing.T) {
620628
if err != nil {
621629
t.Fatalf("op.NewRuntimeEnvironment(resume): %v", err)
622630
}
631+
t.Cleanup(func() { _ = resumeEnvironment.Close() })
623632
restored, err := snapshot.Rehydrate(resumeEnvironment)
624633
if err != nil {
625634
t.Fatalf("Rehydrate: %v", err)
@@ -651,6 +660,7 @@ func TestGraphSaveLoad_ContentTransport(t *testing.T) {
651660
if err != nil {
652661
t.Fatalf("op.NewRuntimeEnvironment(%s): %v", dir, err)
653662
}
663+
t.Cleanup(func() { _ = host.Close() })
654664
return host
655665
}
656666

@@ -796,6 +806,7 @@ func TestGraphStop_UnwindsToStopped_ViaPublicAPI(t *testing.T) {
796806
if err != nil {
797807
t.Fatalf("op.NewRuntimeEnvironment: %v", err)
798808
}
809+
t.Cleanup(func() { _ = environment.Close() })
799810

800811
planProvider := plan.NewProvider(environment)
801812

‎pkg/op/provider/plan/provider_test.go‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ func plannedEnvironmentAt(t *testing.T, rootPath string) *op.RuntimeEnvironment
8080
if err != nil {
8181
t.Fatalf("op.NewRuntimeEnvironment: %v", err)
8282
}
83+
t.Cleanup(func() { _ = environment.Close() })
8384

8485
return environment
8586
}
@@ -329,6 +330,7 @@ func TestProvider_Spec_DefaultsFromPlanningEnvironment(t *testing.T) {
329330
if err != nil {
330331
t.Fatalf("op.NewRuntimeEnvironment: %v", err)
331332
}
333+
t.Cleanup(func() { _ = environment.Close() })
332334
p := NewProvider(environment)
333335

334336
spec, err := p.Spec("", "", nil)

0 commit comments

Comments
 (0)