# L4D2 CLI Host Client 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:** Make `l4d2web` manage the local host through `l4d2ctl` instead of importing `l4d2host` internals, so the same execution boundary can later be transported over SSH.

**Architecture:** `l4d2host` remains the host-local implementation behind `l4d2ctl`. `l4d2web` gains a small local command runner that streams CLI stdout/stderr into jobs, supports cancellation, and parses status JSON. Hosts and overlay sync remain out of this change; the current machine is the implicit local host.

**Tech Stack:** Python 3.12+, Typer, subprocess, Flask, SQLAlchemy, pytest.

---

## File Map

- `l4d2host/cli.py`: add read commands for status and logs.
- `l4d2host/tests/test_cli.py`: cover the expanded CLI contract.
- `l4d2web/services/host_commands.py`: new subprocess-based host command runner and cancellation exception.
- `l4d2web/services/l4d2_facade.py`: call `l4d2ctl` through `host_commands` instead of importing `l4d2host` internals.
- `l4d2web/services/job_worker.py`: catch the web-side cancellation exception.
- `l4d2web/tests/test_host_commands.py`: cover callback streaming, failures, and cancellation.
- `l4d2web/tests/test_l4d2_facade.py`: verify facade emits CLI commands and parses status.
- `l4d2web/tests/test_job_worker.py`: update cancellation imports.
- `l4d2host/README.md`, `l4d2web/README.md`, existing implementation plans: document the relaxed CLI boundary.

## Tasks

### Task 1: Add host CLI read commands

- [x] Write failing tests for `l4d2ctl status <name> --json` and `l4d2ctl logs <name> --no-follow`.
- [x] Run `pytest l4d2host/tests/test_cli.py -q` and confirm the new tests fail because commands do not exist.
- [x] Add the `status` and `logs` commands to `l4d2host/cli.py` using existing `get_instance_status` and `stream_instance_logs` APIs.
- [x] Run `pytest l4d2host/tests/test_cli.py -q` and confirm it passes.

### Task 2: Add web host command runner

- [x] Write failing tests for streaming stdout/stderr callbacks, non-zero exit propagation, and cancellation.
- [x] Run `pytest l4d2web/tests/test_host_commands.py -q` and confirm failures are for the missing module.
- [x] Implement `l4d2web/services/host_commands.py` with `run_command`, `HostCommandError`, and `CommandCancelledError`.
- [x] Run `pytest l4d2web/tests/test_host_commands.py -q` and confirm it passes.

### Task 3: Switch web facade to CLI calls

- [x] Update facade tests so they monkeypatch `host_commands.run_command` and assert emitted `l4d2ctl` commands.
- [x] Run `pytest l4d2web/tests/test_l4d2_facade.py -q` and confirm failures show the facade still imports/calls `l4d2host` internals.
- [x] Replace direct `l4d2host` imports in `l4d2web/services/l4d2_facade.py` with CLI command calls.
- [x] Run `pytest l4d2web/tests/test_l4d2_facade.py -q` and confirm it passes.

### Task 4: Update worker cancellation boundary

- [x] Update job worker tests to import `CommandCancelledError` from `l4d2web.services.host_commands`.
- [x] Run `pytest l4d2web/tests/test_job_worker.py -q` and confirm failures identify the old boundary.
- [x] Update `l4d2web/services/job_worker.py` to catch the web-side cancellation exception.
- [x] Run `pytest l4d2web/tests/test_job_worker.py -q` and confirm it passes.

### Task 5: Update docs and verify

- [x] Update README/plan language from “fixed write commands only” to “fixed write commands plus read commands”.
- [x] Run `pytest l4d2host/tests -q` and confirm pass.
- [x] Run `pytest l4d2web/tests -q` and confirm pass.
- [x] Run `ccc index` if available so the code index reflects the boundary change.

## Self-Review

- Spec coverage: covers CLI read commands, web-side CLI execution, status/log parsing, cancellation, docs, and verification.
- Scope: hosts table, SSH transport, and overlay sync are explicitly excluded from this change.
- Type consistency: the web-side cancellation type is `l4d2web.services.host_commands.CommandCancelledError`; the host-side process type remains internal to `l4d2host`.
