Fix TUI GPU image selection, load compose overrides, bump stack
Fixes four bugs reported from the field. One commit per issue.
QUI-895 — GPU toggle started the CPU image (TUI only)
Enabling a GPU in the TUI wrote [gpu]/[cuda.0] into config.toml but still started the CPU container, so the miner ran the CPU image against a CUDA config.
The ticket attributes this to COMPOSE_PROFILES. This app never reads that variable; it selects the image with --profile from AppSettings.image_tag. The real cause is that FormState.image_tag was written once, in from_settings() at TuiApp::new(), and never again. Both save paths copied that startup value forward while the GPU toggle mutated node_config directly, so the two diverged as soon as the user touched the toggle. The GUI re-derives image_tag on every Apply and was never affected.
The field is now gone and the tag is derived, matching the GUI rule at src/app.js:713-715. TuiApp retains survey.gpu_backend, which was previously fetched and discarded, because GpuDeviceConfig records only index and enabled and cannot tell an NVIDIA card from an Apple Metal GPU.
The CPU fallback is not silent: enabling a GPU without a CUDA backend surfaces a warning, since config.toml still declares [cuda.*] and the mismatch is otherwise invisible. Suppressed in Native mode, which starts no miner container.
The stack status panel read the same stale field, so the TUI also reported health for the wrong container.
QUI-824, QUI-898 — miner memory cap and log rotation
Both were already fixed upstream in nodes.quip.network d221968, so this bumps the submodule rather than patching the staged compose. quip-cpu is capped at ${QUIP_MINER_MEM_LIMIT:-16g}, and all seven services get 32m x 5 log rotation.
QUI-866 — validator pruning: documented, not changed
Archive mode stays the default. Pruning breaks the dashboard descriptor worker, which scans from genesis and fails with State already discarded, and likely reduces SNAG point awards. The reporter's ask to ship a pruned default is declined for those reasons; see the issue comment.
Documenting the override surfaced that there was no working override path at all:
sync_stack_assetsrewrites the staged docker-compose.yml on every Start and Apply, so hand-edits are erased.- Upstream documents
docker-compose.override.yml, but compose auto-discovers that filename only when it also discovers the compose file. Every invocation here passes-f, which disables discovery.
The override file is now passed explicitly when present, after the base file so its values win. This also unblocks the upstream instructions for DNS-01 certificates and custom --bootnodes, which route through the same file. The log streamer's hand-rolled compose prefix now reuses compose_cmd so log streaming resolves the same model.
Miner memory ceiling as a setting
.env is regenerated by write_env_file on every Start, so QUIP_MINER_MEM_LIMIT could not be set there either. Added AppSettings.miner_mem_limit_gb, rendered into .env when set. Unset stays unset so the compose :-16g default remains the single source of truth.
Verification
147 tests pass, up from 138. Clippy clean. Vale clean on the new README prose.
The QUI-895 fix is covered by five new unit tests and was mutation-tested: removing the cuda branch fails exactly the two assertions covering it. It has not been verified on real CUDA hardware. Worth asking @cryptogemfunds to confirm on rc11 before closing QUI-895.
miner_mem_limit_gb has no UI control and is set by editing app-settings.json. Documented as such.