carlos / platform Public

Root CLI tests read the operator's real ~/.carlos/config when TMPDIR is under HOME open

About 22 root-package tests fail with "not logged in to https://console.carloku.com" when a developer runs go test . with TMPDIR under $HOME. That is the default for sandboxed agent sessions.

Why it happens. The tests pin HOME, but they work in a t.TempDir() under TMPDIR. The .carlos/config lookup walks up from the working directory and finds the operator's real ~/.carlos/config. TestResolveModeExpiredOffersRelogin also hangs for about 10 minutes.

Where it is fixed already. CI has no ~/.carlos and the full gate's texec wrapper puts TMPDIR at /var/tmp/ck, so both pass. Commit 6fff5358 on moves-phase1-4 fixes TestShipVerbAPIMode alone with t.Setenv("CARLOS_CONFIG", ...).

Why there is no blanket fix yet. Pinning CARLOS_CONFIG in the shared helper breaks the tests that write a .carlos/config in their working directory, for example TestResolveModeNonTTYErrorsUnchanged.

Suggested fix. Either stop the config walk at the pinned HOME, or have the shared credentials helper chdir to a directory outside the operator's home.

The full list of failing tests is in platform-moves-p4/.superpowers/sdd/2026-10-06-moves-phase1-4-rollback-return-declare-lost/task-1-report.md, under "Fix round 1".

Fixed on branch test-suite-debt (35a97d09), CI green on the tip ce7b9441, Astra review running, not merged. This covers #226 too.

Cause. useTempCredentials pins HOME to one t.TempDir() and chdirs to another. Config discovery walks up from the working directory by path, so with TMPDIR under a logged-in home it reaches the real ~/.carlos/config whatever HOME says.

Fix. A guard in the root package's TestMain (isolateTempRoot). Before any test runs it checks every ancestor of os.TempDir() for a .carlos/config:

  • none above (every CI run): nothing changes;
  • one above: it moves TMPDIR to a fresh /var/tmp/carlos-test-* for the binary and its children, and removes it at exit;
  • no clear base writable: the binary exits 1 and says why, rather than run against the real config.

Neither fix suggested here would have worked alone: the scratch cwd is a sibling of the pinned HOME, not under it, so stopping the walk at HOME changes nothing (and would change behaviour for real users); pinning CARLOS_CONFIG breaks the tests that write their own config.

Measured, TMPDIR under $HOME on a logged-in machine. Before: TestResolveModeNonTTYErrorsUnchanged and TestConfigDefaultWalk fail, the *VerbAPIMode tests abort the binary. After: the 37 tests on this thread's list pass, TestResolveModeExpiredOffersRelogin passes in 2s, and the whole root package passes in 440s with no non-loopback connection seen.

Limits. A test that leaves its cwd in the checkout and runs discovery without CARLOS_CONFIG would still leak; none does today. If the binary dies by log.Fatal or a timeout, its /var/tmp/carlos-test-* directory is left behind (the path is printed).

Review update. Astra's first pass on test-suite-debt found four P2s in the guard; three are fixed in 8c5587c9 and the second pass says MERGE-READY with no new findings. CI is green on 8c5587c9.

  • The guard now checks GOTMPDIR as well as TMPDIR (t.TempDir() prefers it).
  • TestMain unsets an inherited CARLOS_CONFIG and CARLOS_DESTINATION. Either one, exported in a developer's shell, steered an in-process test before.
  • /tmp is no longer a fallback; without /var/tmp the binary refuses and names the variable to set.
  • Declined: cleanup after log.Fatal, a panic or a timeout. An aborted run leaves one /var/tmp/carlos-test-* directory; its path is printed and AGENTS.md says to remove it by name.

CARLOS_CONSOLE and CARLOS_CREDENTIALS are not cleared in TestMain: the fixtures (useTempCredentials, useIsolatedConfig, carlosTestEnv) override both. That isolation is the fixtures', not TestMain's.

Waiting on Paul to merge.