Batch 2 (installer). Root causes and fixes:
1. config-manager tests asserted stale layout (<path>/config/config.json);
canonical implementation writes flat <path>/config.json matching the
Docker volume-mount contract + REQUIRED_DIRS. Tests aligned to the
production contract (not the other way) + create testDir in beforeEach.
2. saveConfig ENOENT when the install dir didn't exist: now mkdirs the
parent before writing (implementation fix; wizard passes user-typed paths).
3. REAL BUG: saveDNSCredentials/loadDNSCredentials silently dropped the tld
field on round-trip (property test caught it). Now persisted plaintext
(non-secret, like server/username) and restored on load; type-normalized
to string-or-null (judge polish #4).
4. installDocker/installCaddy unit tests hit the real network via
DownloadManager (only child_process mocked) -> 5s timeouts. Now
jest.mock('./download-manager') with fail-fast stubs.
5. dependency-checker.property.test.js ran real exec/downloads (caddy is
installed on this box). Now hermetic: child_process + download-manager
mocked at file scope.
6. installDocker fallback message parroted raw downloader error; now steers
user to the manual instructions returned alongside (satisfies the test
contract AND improves UX).
Also: mkdtemp test dirs (judge polish #1), nested-mkdir regression test
(polish #7). Installer suite: 119/119 green, 7/7 suites (was 17 failed /
4 suites red). Judge: Qwen lane grade A, 0 blocking, 8 polish (1,3,4,7
applied here), verdict /tmp/judge-batch2-verdict.json.
Full codebase including API server (32 modules + routes), dashboard frontend,
DashCA certificate distribution, installer script, and deployment skills.