# SteamCMD Install Logging & Buffering Fix Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Improve live feedback during the `install` operation by adding step markers to `SteamInstaller` and fixing Python subprocess buffering so output streams immediately.

**Architecture:** Modifies `l4d2host/process.py` and `l4d2web/services/host_commands.py` to add `flush=True` to `print()` statements for immediate pipeline throughput. Modifies `l4d2host/steam_install.py` to use `_emit_step` to log platform payload downloads.

**Tech Stack:** Python, subprocess

---

### Task 1: Fix process output buffering

**Files:**
- Modify: `l4d2host/process.py`
- Modify: `l4d2web/services/host_commands.py`

- [ ] **Step 1: Check existing test suite**
No failing test is required here because we are only modifying the `flush` parameter of `print()` inside existing pass-through functions, which are thoroughly covered by integration tests but unit-testing the buffering of `print` is notoriously flaky across OSes. We will just modify the code and run the existing suite.

- [ ] **Step 2: Add flush to l4d2host process**

Modify `emit_stderr_message` and `pump` inside `run_command` in `l4d2host/process.py`:

```python
    def emit_stderr_message(line: str) -> None:
        stderr_lines.append(line)
        if on_stderr is not None:
            on_stderr(line)
        if passthrough:
            print(line, file=sys.stderr, flush=True)
            
    # ... inside pump ...
            if passthrough:
                print(line, file=output_stream, flush=True)
```

- [ ] **Step 3: Add flush to l4d2web host_commands**

Modify `emit_stderr_message` and `pump` inside `run_command` in `l4d2web/services/host_commands.py`:

```python
    def emit_stderr_message(line: str) -> None:
        stderr_lines.append(line)
        if on_stderr is not None:
            on_stderr(line)
        if passthrough:
            print(line, file=sys.stderr, flush=True)
            
    # ... inside pump ...
            if passthrough:
                print(line, file=output_stream, flush=True)
```

- [ ] **Step 4: Run test to verify it passes**

Run: `pytest l4d2host/tests l4d2web/tests -q`
Expected: PASS

- [ ] **Step 5: Commit**

```bash
git add l4d2host/process.py l4d2web/services/host_commands.py
git commit -m "fix(host): enforce flush=True to prevent pipeline block buffering"
```

---

### Task 2: Add step logging to SteamInstaller

**Files:**
- Modify: `l4d2host/steam_install.py`
- Modify: `l4d2host/tests/test_install.py`

- [ ] **Step 1: Write failing test**

In `l4d2host/tests/test_install.py`, add a test to verify `SteamInstaller` logs steps:

```python
def test_steam_installer_emits_steps(tmp_path: Path, monkeypatch) -> None:
    monkeypatch.setenv("LEFT4ME_ROOT", str(tmp_path))
    monkeypatch.setattr("l4d2host.steam_install.run_command", lambda cmd, **kwargs: None)

    steps: list[str] = []
    
    from l4d2host.steam_install import SteamInstaller
    SteamInstaller().install_or_update(on_stdout=steps.append)

    assert steps == [
        "Step: downloading windows platform payload...",
        "Step: downloading linux platform payload...",
        "Step: installation complete."
    ]
```

- [ ] **Step 2: Run test to verify it fails**

Run: `pytest l4d2host/tests/test_install.py -k test_steam_installer_emits_steps -q`
Expected: FAIL because `steps` is empty.

- [ ] **Step 3: Add step logs to SteamInstaller**

In `l4d2host/steam_install.py`, import `_emit_step`:

```python
from l4d2host.instances import _emit_step
```

Modify `install_or_update`:

```python
    def install_or_update(
        self,
        *,
        on_stdout: Callable[[str], None] | None = None,
        on_stderr: Callable[[str], None] | None = None,
        passthrough: bool = False,
        should_cancel: Callable[[], bool] | None = None,
    ) -> None:
        for platform in ("windows", "linux"):
            _emit_step(f"downloading {platform} platform payload...", on_stdout, passthrough)
            run_command(
                [
                    self.steamcmd,
                    "+force_install_dir",
                    str(self.install_dir),
                    "+login",
                    "anonymous",
                    "+@sSteamCmdForcePlatformType",
                    platform,
                    "+app_update",
                    "222860",
                    "validate",
                    "+quit",
                ],
                on_stdout=on_stdout,
                on_stderr=on_stderr,
                passthrough=passthrough,
                should_cancel=should_cancel,
            )
        _emit_step("installation complete.", on_stdout, passthrough)
```

- [ ] **Step 4: Run test to verify it passes**

Run: `pytest l4d2host/tests/test_install.py -k test_steam_installer_emits_steps -q`
Expected: PASS

- [ ] **Step 5: Run full suite**

Run: `pytest l4d2host/tests l4d2web/tests -q`
Expected: PASS

- [ ] **Step 6: Commit**

```bash
git add l4d2host/steam_install.py l4d2host/tests/test_install.py
git commit -m "feat(host): add step logging to steam_install"
```
