# AGENTS.md Guidance for coding agents working in this repository. ## Mission Build `left4me` according to the two implementation plans: - `docs/superpowers/plans/2026-04-22-l4d2-host-lib-v1.md` - `docs/superpowers/plans/2026-04-23-l4d2-web-app-v1.md` Do not invent architecture outside these plans unless explicitly requested. ## Current Project State - `l4d2host/` and `l4d2web/` implementation directories exist. - Implementation plans remain the source of truth for contract changes and task sequencing. ## Non-Negotiable Constraints ### Workspace and tools - Do not use git worktrees. - Repo is a uv workspace; Python is pinned to 3.13 via `.python-version`. After fresh checkout: install `uv` (`brew install uv` / `curl -LsSf https://astral.sh/uv/install.sh | sh`), then `direnv allow` (or `uv sync` directly). See README **Local development** for details. ### Planning artifacts - Design specs live in `docs/superpowers/specs/` as `YYYY-MM-DD--design.md`. - Implementation plans live in `docs/superpowers/plans/` as `YYYY-MM-DD-.md` (suffix the topic with `-v1`/`-v2`/etc. if a plan is versioned). - Commit both to git as soon as the user approves them. - Do not leave specs or plans outside this repo. The `~/.claude/plans/.md` plan-mode scratch file is acceptable while plan mode is open; the persisted artifact must end up under `docs/superpowers/` and be committed. ### Naming and boundaries - Use `l4d2` naming consistently. - Keep host library and web app as separate components. - Do not collapse them into one package. ### Host library (`l4d2host` / `l4d2ctl`) - Exposed CLI write command set is fixed: - `install` - `initialize -f ` - `start ` - `stop ` - `delete ` - CLI read commands are allowed for web/host boundary consistency: - `status --json` - `logs --lines --follow/--no-follow` - Runtime paths are rooted at `LEFT4ME_ROOT`, defaulting to `/var/lib/left4me`. - Deployment/config management owns global units under `/usr/local/lib/systemd/system` and privileged helpers under `/usr/local/libexec/left4me`. - Overlay directories are populated by the web app (workshop downloads, managed-global refresh). The host library only mounts them. - Fail-fast subprocess behavior; pass raw stderr; propagate return code. - No lock manager, no rollback, no preflight runtime checks. - Delete missing instance/runtime dirs must succeed (no-op). - Read APIs required for web app integration: - `get_instance_status(name)` - `stream_instance_logs(name, lines=200, follow=True)` ### Web app (`l4d2-web-app`) - Flask + server-rendered templates + vendored HTMX. - No external frontend framework/dependencies. - Custom CSS with tokenized, consistent link and accent colors. - Local username/password auth and `admin` flag. - Persist command logs in `job_logs` table (retain indefinitely). - Desired vs actual server state model. - Live logs in UI for both jobs and servers. - Web app host operations go through `l4d2ctl` via a host command client, not direct `l4d2host` imports. - Blueprint semantics (locked): - private per user in v1 - live-linked to servers - no server-level overrides - deleting in-use blueprint is blocked - updates apply on next action - servers can reassign blueprint anytime ## Delivery Workflow 1. Read both plan files fully before coding. 2. Execute plan tasks in order. 3. Keep changes scoped to one task at a time. 4. Run task-level tests before moving forward. 5. Do not claim completion without command evidence. 6. Keep docs updated when behavior/contracts change. ## Verification Expectations Before claiming success on any step, run the relevant command and report actual output status. Typical commands (once components exist): - `pytest l4d2host/tests -q` - `pytest l4d2web/tests -q` ## Change Control - If a requested change conflicts with this file, follow explicit user instruction. - If plans and code diverge, update plans or flag the mismatch clearly. ## End-to-end tests The Playwright-based browser tests under `l4d2web/tests/e2e/` need a chromium binary, fetched on first setup: ```bash uv run playwright install chromium ``` Run with `uv run pytest -m e2e`. Excluded from the default fast suite via the `e2e` marker. **Sandbox note:** Chromium needs Mach-port IPC on macOS, which the Claude Code sandbox blocks. When running e2e tests from a sandboxed agent session, pass `dangerouslyDisableSandbox: true` on the `uv run pytest -m e2e` invocation (the symptom of a sandboxed run is a `FATAL` Chromium crash with `Permission denied (1100)` on Mach port rendezvous, not a missing-binary or network error).