carlos / platform Public

Root package tests read the operator's real ~/.carlos/config (TestShipVerbAPIMode aborts the package) open

Found on 2026-10-04 by several agents running make test on main 78fc8d40 from a logged-in machine.

TestShipVerbAPIMode in the root package reads the operator's real ~/.carlos/config, so on a laptop logged in to console.carloku.com it fails with "not logged in to console.carloku.com" and takes the rest of the root package down with it. With HOME pointed at a scratch directory, the root e2e, secrets and migrate tests pass. TestDefaultAppConfigPairing also fails on main when run with an isolated config.

That goes against AGENTS.md's rule that a test shelling out to the carlos binary builds its env through carlosTestEnv and runs from a scratch cmd.Dir. The rule exists because a logged-in laptop once minted more than 40 real accounts on the production console. CI passes because CI has no config, which is how this went unnoticed. It is the same failure mode "a known-failing test is a defect in its own right" warns about.

Fix: route the offending tests through carlosTestEnv and a scratch dir. Better, make the root test binary refuse to start when HOME is the real one.

Related, seen 2026-10-06 while rebasing tenant-iso-domains: when several packages run in one go test call, something deletes the TMPDIR directory partway through the run, and the console tests then fail in bulk. Each package passes when run alone. Some test is removing a directory it doesn't own, possibly os.TempDir() itself, which on a shared machine could take other processes' files with it. Not tracked down yet.

Tracked under #247 (tenant isolation before Carloku takes outside tenants).

The config leak is fixed on branch test-suite-debt (35a97d09); details on #258. The root test binary now refuses to run, or moves its temp root, when any ancestor of its temp directory holds a .carlos/config.

The TMPDIR deleter from the 2026-10-06 reply was not found. Every RemoveAll in test files (102) and production (46) was read, plus os.Remove of parent directories, upward-walking loops and the TestMain cleanups. Each removes something it created or a named child of a configured directory. Ruled out by name: newGeoipPublisher's RemoveAll(tmpRoot), safefile.Reap, breakGenerationsFile, pruneEmptyDirs, claimCensusCreate.

Unverified hypothesis: it is not code. Sessions on the shared machine use fixed temp roots (/tmp/g, /tmp/k, /var/tmp/ck) and the gate recipe says to empty them afterwards, so one session's cleanup removes another's live root. Private roots per run (mktemp -d) would rule that out.