# Goal-1 (clean, layered host architecture) — staged execution plan The design is in [`windows-host-rewrite.md`](windows-host-rewrite.md) §2.2–2.4. This file is the **ordered, independently-shippable execution plan**, because the host is **live-validated** (GameStream + punktfunk/1, NVENC + IDD-push on-glass) and Goal-1 rewires its session/config/dispatch flow — so every stage must **preserve behavior**, compile + box-verify on its own, and be committed before the next starts. The plan's own §14 makes the §1 preservation checklist a mandatory per-module assert contract; honour it. ## Why staged (not one big rewrite) `main` is at parity and shipping. A monolithic rewrite would put the validated host in a broken intermediate state for a long window and make a regression impossible to bisect. Each stage below is a behaviour-preserving transform with its own verification, so a regression is caught at the stage that introduced it. ## Stages (ordered; each = goal · files · risk · verify) **Stage 1 — `HostConfig` foundation. ✅ DONE (this commit).** `config.rs`: typed `HostConfig` parsed ONCE from env (`idd_push`/`encoder_pref`/`no_helper`/`force_helper`). Migrated the two highest-churn dispatch reads onto it (`encode::windows_resolved_backend`, `punktfunk1::should_use_helper`). Risk: low (env constant at runtime → identical behaviour). Verify: box `cargo check --features nvenc`. **Stage 2 — finish `HostConfig` + resolve-once. ✅ DONE (this commit).** Migrated **31** genuinely-constant operator/dispatch sites onto `HostConfig`: `idd_push` ×7 (the capture/topology disagreement knob), `no_wgc`, `capture_backend`, `render_adapter`, `encoder_pref` (Linux), the Windows vdisplay-backend select, plus the plan-named `secure_dda`/`idd_depth`/`zerocopy`/`ten_bit` and the multi-site `perf` ×4 / `compositor` ×5 / `video_source` ×3 / `gamepad`. Each `HostConfig` field's parser is **byte-identical** to the read it replaced, so `old == new` by construction (the §1 "flipped bool" guard). **Scope correction (the plan's "~64 sites / Linux XDG+compositor / grep→0" was unsafe as written):** two classes of `env::var` read are deliberately **kept live** and documented in `config.rs`: - **Runtime-mutated session vars.** On Linux, `vdisplay::apply_session_env` *rewrites the process env on every connect* (the Bazzite Gaming↔Desktop follow): `WAYLAND_DISPLAY`, `XDG_CURRENT_DESKTOP`, `XDG_RUNTIME_DIR`, `DBUS_SESSION_BUS_ADDRESS`, and the derived `PUNKTFUNK_INPUT_BACKEND`, `PUNKTFUNK_GAMESCOPE_SESSION/NODE`, `PUNKTFUNK_KWIN/MUTTER_VIRTUAL_PRIMARY`, `PUNKTFUNK_FORCE_SHM`. Parse-once would freeze them at startup → silent session-following regression. They are NOT constant. - **Single-use local tuning** (no resolve-once benefit, call-site-local default/clamp, and `FEC_PCT` even has *two different* semantics): `FEC_PCT`, `VIDEO_DROP`, `VBV_FRAMES`, `SPLIT_ENCODE`, `PACE_BURST_KB`, the `capture/dxgi.rs` timing knobs, the `*_LIVE`/test gates, plus path/dynamic reads (config-dir, `PATH` search, env-forward-to-child). `PUNKTFUNK_ZEROCOPY` is split on purpose: Windows presence-semantics moved to the field; Linux keeps its own truthy parser. Risk: medium (semantics-preservation). Verify: Linux `cargo check`/`clippy`/`fmt` green (the Windows-only edits are 1:1 substitutions, compile-verified on the box as part of Stage 3's build). **Stage 3 — `SessionPlan` (the single biggest clarity lever, plan §2.4).** Resolve `display/capture/topology/encoder/format/hdr/bit_depth` ONCE from `HostConfig` into a typed `SessionPlan`; replace the 3-place dispatch (`capture_virtual_output`, `should_use_helper`/`virtual_stream`, `windows_resolved_backend`) with reads off the plan. Fixes the capture/encode backend-disagreement bug class. Risk: medium-high (rewires the deployed decision). Verify: box build + on-glass re-test (NVENC + IDD-push + a mode switch). **Stage 4 — `SessionContext` + `SessionFactory`/`Session`.** Bundle the 12–13-arg `#[allow(too_many_arguments)]` signatures into `SessionContext`; `SessionFactory.build()` owns the RAII chain `vdm.lease(mode) → open_capturer(vout, fmt) → open_encoder(plan) → spawn pipeline`, with `Session::drop` the ONLY teardown path. Risk: high (teardown ordering — the §1 RAII asserts are mandatory). Verify: box build + on-glass (connect/disconnect/reconnect, no leaked monitors/threads). **Stage 5 — seam-trait tightenings (plan §2.3).** `Capturer::open_capturer(vout, want: OutputFormat)` takes the format IN (kills the `capture → encode::windows_resolved_backend()` back-reference recomputed in `dxgi.rs`); HDR/release become `VirtualLease` methods (session glue names no concrete backend, contains no `unsafe`); optional encoder features move to `EncoderCaps`. Risk: medium. Verify: box build + on-glass. **Stage 6 — `src/windows/` tree (cfg-sprawl confinement, plan §2.2).** Move the Windows backends under `src/windows/` + `capture/windows/`, `encode/windows/`, `inject/windows/`, `audio/windows/`, `vdisplay/windows.rs` behind one `#[cfg(windows)] mod windows;` seam. Pure file move + mod/use-path updates — behaviour-identical. Risk: low-but-huge (dozens of files; compile-verify catches all). Do LAST so the earlier semantic stages don't fight path churn. Verify: Linux + box build. ## Guardrails (mandatory, plan §14) - Each stage is its own commit; box-verify before moving on. - Stages 3–5 touch the deployed path → **on-glass re-test** (NVENC + IDD-push, a mode switch, a connect/disconnect cycle) before the next stage. - Preserve every `PUNKTFUNK_*` var's exact semantics; when in doubt, assert old==new at the call site.