chore(l4d2): flatten component layout

This commit is contained in:
mwiegand 2026-05-05 23:47:06 +02:00
parent 7bac34c886
commit 288eda7c37
No known key found for this signature in database
81 changed files with 221 additions and 205 deletions

5
.gitignore vendored
View file

@ -1,2 +1,7 @@
.worktrees/ .worktrees/
.venv/
.pytest_cache/
__pycache__/
*.pyc
*.egg-info/
l4d2web.db* l4d2web.db*

View file

@ -74,8 +74,8 @@ Before claiming success on any step, run the relevant command and report actual
Typical commands (once components exist): Typical commands (once components exist):
- `pytest components/l4d2-host-lib/tests -q` - `pytest l4d2host/tests -q`
- `pytest components/l4d2-web-app/tests -q` - `pytest l4d2web/tests -q`
## Change Control ## Change Control

View file

@ -38,8 +38,8 @@ Implementation plans are the source of truth:
## Planned Repository Layout ## Planned Repository Layout
- `components/l4d2-host-lib/` - `l4d2host/`
- `components/l4d2-web-app/` - `l4d2web/`
- `docs/superpowers/plans/` - `docs/superpowers/plans/`
## Tech Stack (planned) ## Tech Stack (planned)
@ -52,8 +52,8 @@ Implementation plans are the source of truth:
## Recommended Implementation Order ## Recommended Implementation Order
1. Implement `components/l4d2-host-lib` plan first. 1. Implement `l4d2host` plan first.
2. Implement `components/l4d2-web-app` plan second. 2. Implement `l4d2web` plan second.
3. Keep tests green task-by-task (TDD flow from plans). 3. Keep tests green task-by-task (TDD flow from plans).
4. Keep commits small and aligned with plan tasks. 4. Keep commits small and aligned with plan tasks.

View file

@ -39,35 +39,35 @@
## Planned File Layout ## Planned File Layout
- `components/l4d2-host-lib/pyproject.toml` - `l4d2host/pyproject.toml`
- `components/l4d2-host-lib/src/l4d2host/cli.py` - `l4d2host/cli.py`
- `components/l4d2-host-lib/src/l4d2host/spec.py` - `l4d2host/spec.py`
- `components/l4d2-host-lib/src/l4d2host/process.py` - `l4d2host/process.py`
- `components/l4d2-host-lib/src/l4d2host/steam_install.py` - `l4d2host/steam_install.py`
- `components/l4d2-host-lib/src/l4d2host/systemd_user.py` - `l4d2host/systemd_user.py`
- `components/l4d2-host-lib/src/l4d2host/fs/base.py` - `l4d2host/fs/base.py`
- `components/l4d2-host-lib/src/l4d2host/fs/fuse_overlayfs.py` - `l4d2host/fs/fuse_overlayfs.py`
- `components/l4d2-host-lib/src/l4d2host/instances.py` - `l4d2host/instances.py`
- `components/l4d2-host-lib/src/l4d2host/status.py` - `l4d2host/status.py`
- `components/l4d2-host-lib/src/l4d2host/logs.py` - `l4d2host/logs.py`
- `components/l4d2-host-lib/src/l4d2host/templates/l4d2@.service` - `l4d2host/templates/l4d2@.service`
- `components/l4d2-host-lib/tests/test_cli.py` - `l4d2host/tests/test_cli.py`
- `components/l4d2-host-lib/tests/test_spec.py` - `l4d2host/tests/test_spec.py`
- `components/l4d2-host-lib/tests/test_process.py` - `l4d2host/tests/test_process.py`
- `components/l4d2-host-lib/tests/test_install.py` - `l4d2host/tests/test_install.py`
- `components/l4d2-host-lib/tests/test_initialize.py` - `l4d2host/tests/test_initialize.py`
- `components/l4d2-host-lib/tests/test_lifecycle.py` - `l4d2host/tests/test_lifecycle.py`
- `components/l4d2-host-lib/tests/test_status.py` - `l4d2host/tests/test_status.py`
- `components/l4d2-host-lib/tests/test_logs.py` - `l4d2host/tests/test_logs.py`
- `components/l4d2-host-lib/README.md` - `l4d2host/README.md`
### Task 1: Scaffold package and CLI entrypoint ### Task 1: Scaffold package and CLI entrypoint
**Files:** **Files:**
- Create: `components/l4d2-host-lib/pyproject.toml` - Create: `l4d2host/pyproject.toml`
- Create: `components/l4d2-host-lib/src/l4d2host/__init__.py` - Create: `l4d2host/__init__.py`
- Create: `components/l4d2-host-lib/src/l4d2host/cli.py` - Create: `l4d2host/cli.py`
- Test: `components/l4d2-host-lib/tests/test_cli.py` - Test: `l4d2host/tests/test_cli.py`
- [ ] **Step 1: Write failing CLI help test** - [ ] **Step 1: Write failing CLI help test**
@ -85,7 +85,7 @@ def test_help_lists_v1_commands():
- [ ] **Step 2: Run test and verify failure** - [ ] **Step 2: Run test and verify failure**
Run: `pytest components/l4d2-host-lib/tests/test_cli.py -q` Run: `pytest l4d2host/tests/test_cli.py -q`
Expected: FAIL with missing package/app. Expected: FAIL with missing package/app.
- [ ] **Step 3: Implement pyproject script and minimal CLI app** - [ ] **Step 3: Implement pyproject script and minimal CLI app**
@ -132,21 +132,21 @@ def delete(name: str) -> None:
- [ ] **Step 4: Run tests and verify pass** - [ ] **Step 4: Run tests and verify pass**
Run: `pytest components/l4d2-host-lib/tests/test_cli.py -q` Run: `pytest l4d2host/tests/test_cli.py -q`
Expected: PASS. Expected: PASS.
- [ ] **Step 5: Commit scaffold** - [ ] **Step 5: Commit scaffold**
```bash ```bash
git add components/l4d2-host-lib git add l4d2host
git commit -m "feat(l4d2): scaffold package and v1 CLI entrypoint" git commit -m "feat(l4d2): scaffold package and v1 CLI entrypoint"
``` ```
### Task 2: Implement YAML spec parser ### Task 2: Implement YAML spec parser
**Files:** **Files:**
- Create: `components/l4d2-host-lib/src/l4d2host/spec.py` - Create: `l4d2host/spec.py`
- Test: `components/l4d2-host-lib/tests/test_spec.py` - Test: `l4d2host/tests/test_spec.py`
- [ ] **Step 1: Write failing parser tests** - [ ] **Step 1: Write failing parser tests**
@ -189,7 +189,7 @@ def test_unknown_keys_ignored(tmp_path: Path):
- [ ] **Step 2: Run tests and verify failure** - [ ] **Step 2: Run tests and verify failure**
Run: `pytest components/l4d2-host-lib/tests/test_spec.py -q` Run: `pytest l4d2host/tests/test_spec.py -q`
Expected: FAIL. Expected: FAIL.
- [ ] **Step 3: Implement parser and dataclass** - [ ] **Step 3: Implement parser and dataclass**
@ -220,21 +220,21 @@ def load_spec(path: Path) -> InstanceSpec:
- [ ] **Step 4: Run tests and verify pass** - [ ] **Step 4: Run tests and verify pass**
Run: `pytest components/l4d2-host-lib/tests/test_spec.py -q` Run: `pytest l4d2host/tests/test_spec.py -q`
Expected: PASS. Expected: PASS.
- [ ] **Step 5: Commit parser** - [ ] **Step 5: Commit parser**
```bash ```bash
git add components/l4d2-host-lib/src/l4d2host/spec.py components/l4d2-host-lib/tests/test_spec.py git add l4d2host/spec.py l4d2host/tests/test_spec.py
git commit -m "feat(l4d2): add spec parser with required port and permissive fields" git commit -m "feat(l4d2): add spec parser with required port and permissive fields"
``` ```
### Task 3: Build streaming process runner with callbacks ### Task 3: Build streaming process runner with callbacks
**Files:** **Files:**
- Create: `components/l4d2-host-lib/src/l4d2host/process.py` - Create: `l4d2host/process.py`
- Test: `components/l4d2-host-lib/tests/test_process.py` - Test: `l4d2host/tests/test_process.py`
- [ ] **Step 1: Write failing process tests** - [ ] **Step 1: Write failing process tests**
@ -257,7 +257,7 @@ def test_nonzero_exit_raises():
- [ ] **Step 2: Run tests and verify failure** - [ ] **Step 2: Run tests and verify failure**
Run: `pytest components/l4d2-host-lib/tests/test_process.py -q` Run: `pytest l4d2host/tests/test_process.py -q`
Expected: FAIL. Expected: FAIL.
- [ ] **Step 3: Implement process runner** - [ ] **Step 3: Implement process runner**
@ -304,21 +304,21 @@ def run_command(cmd, *, on_stdout=None, on_stderr=None, passthrough=False):
- [ ] **Step 4: Run tests and verify pass** - [ ] **Step 4: Run tests and verify pass**
Run: `pytest components/l4d2-host-lib/tests/test_process.py -q` Run: `pytest l4d2host/tests/test_process.py -q`
Expected: PASS. Expected: PASS.
- [ ] **Step 5: Commit process runner** - [ ] **Step 5: Commit process runner**
```bash ```bash
git add components/l4d2-host-lib/src/l4d2host/process.py components/l4d2-host-lib/tests/test_process.py git add l4d2host/process.py l4d2host/tests/test_process.py
git commit -m "feat(l4d2): add callback-capable streaming process runner" git commit -m "feat(l4d2): add callback-capable streaming process runner"
``` ```
### Task 4: Implement install command with callback passthrough ### Task 4: Implement install command with callback passthrough
**Files:** **Files:**
- Create: `components/l4d2-host-lib/src/l4d2host/steam_install.py` - Create: `l4d2host/steam_install.py`
- Test: `components/l4d2-host-lib/tests/test_install.py` - Test: `l4d2host/tests/test_install.py`
- [ ] **Step 1: Write failing install tests** - [ ] **Step 1: Write failing install tests**
@ -352,7 +352,7 @@ def test_fail_fast_on_first_failure(monkeypatch):
- [ ] **Step 2: Run tests and verify failure** - [ ] **Step 2: Run tests and verify failure**
Run: `pytest components/l4d2-host-lib/tests/test_install.py -q` Run: `pytest l4d2host/tests/test_install.py -q`
Expected: FAIL. Expected: FAIL.
- [ ] **Step 3: Implement installer** - [ ] **Step 3: Implement installer**
@ -381,23 +381,23 @@ class SteamInstaller:
- [ ] **Step 4: Run tests and verify pass** - [ ] **Step 4: Run tests and verify pass**
Run: `pytest components/l4d2-host-lib/tests/test_install.py -q` Run: `pytest l4d2host/tests/test_install.py -q`
Expected: PASS. Expected: PASS.
- [ ] **Step 5: Commit install command** - [ ] **Step 5: Commit install command**
```bash ```bash
git add components/l4d2-host-lib/src/l4d2host/steam_install.py components/l4d2-host-lib/tests/test_install.py git add l4d2host/steam_install.py l4d2host/tests/test_install.py
git commit -m "feat(l4d2): implement callback-aware install command" git commit -m "feat(l4d2): implement callback-aware install command"
``` ```
### Task 5: Implement initialize and systemd template management ### Task 5: Implement initialize and systemd template management
**Files:** **Files:**
- Create: `components/l4d2-host-lib/src/l4d2host/systemd_user.py` - Create: `l4d2host/systemd_user.py`
- Create: `components/l4d2-host-lib/src/l4d2host/instances.py` - Create: `l4d2host/instances.py`
- Create: `components/l4d2-host-lib/src/l4d2host/templates/l4d2@.service` - Create: `l4d2host/templates/l4d2@.service`
- Test: `components/l4d2-host-lib/tests/test_initialize.py` - Test: `l4d2host/tests/test_initialize.py`
- [ ] **Step 1: Write failing initialize tests** - [ ] **Step 1: Write failing initialize tests**
@ -423,7 +423,7 @@ def test_empty_config_writes_empty_server_cfg(tmp_path: Path):
- [ ] **Step 2: Run tests and verify failure** - [ ] **Step 2: Run tests and verify failure**
Run: `pytest components/l4d2-host-lib/tests/test_initialize.py -q` Run: `pytest l4d2host/tests/test_initialize.py -q`
Expected: FAIL. Expected: FAIL.
- [ ] **Step 3: Implement initialize flow** - [ ] **Step 3: Implement initialize flow**
@ -458,23 +458,23 @@ def initialize_instance(name: str, spec_path: Path, *, root: Path = Path("/opt/l
- [ ] **Step 4: Run tests and verify pass** - [ ] **Step 4: Run tests and verify pass**
Run: `pytest components/l4d2-host-lib/tests/test_initialize.py -q` Run: `pytest l4d2host/tests/test_initialize.py -q`
Expected: PASS. Expected: PASS.
- [ ] **Step 5: Commit initialize logic** - [ ] **Step 5: Commit initialize logic**
```bash ```bash
git add components/l4d2-host-lib/src/l4d2host/{instances.py,systemd_user.py,templates/l4d2@.service} components/l4d2-host-lib/tests/test_initialize.py git add l4d2host/{instances.py,systemd_user.py,templates/l4d2@.service} l4d2host/tests/test_initialize.py
git commit -m "feat(l4d2): implement initialize flow and systemd user template management" git commit -m "feat(l4d2): implement initialize flow and systemd user template management"
``` ```
### Task 6: Implement start/stop/delete lifecycle commands ### Task 6: Implement start/stop/delete lifecycle commands
**Files:** **Files:**
- Create: `components/l4d2-host-lib/src/l4d2host/fs/base.py` - Create: `l4d2host/fs/base.py`
- Create: `components/l4d2-host-lib/src/l4d2host/fs/fuse_overlayfs.py` - Create: `l4d2host/fs/fuse_overlayfs.py`
- Modify: `components/l4d2-host-lib/src/l4d2host/instances.py` - Modify: `l4d2host/instances.py`
- Test: `components/l4d2-host-lib/tests/test_lifecycle.py` - Test: `l4d2host/tests/test_lifecycle.py`
- [ ] **Step 1: Write failing lifecycle tests** - [ ] **Step 1: Write failing lifecycle tests**
@ -498,7 +498,7 @@ def test_delete_missing_is_noop(tmp_path: Path):
- [ ] **Step 2: Run tests and verify failure** - [ ] **Step 2: Run tests and verify failure**
Run: `pytest components/l4d2-host-lib/tests/test_lifecycle.py -q` Run: `pytest l4d2host/tests/test_lifecycle.py -q`
Expected: FAIL. Expected: FAIL.
- [ ] **Step 3: Implement lifecycle methods with callbacks** - [ ] **Step 3: Implement lifecycle methods with callbacks**
@ -552,23 +552,23 @@ def delete_instance(name: str, *, root: Path = Path("/opt/l4d2"), on_stdout=None
- [ ] **Step 4: Run tests and verify pass** - [ ] **Step 4: Run tests and verify pass**
Run: `pytest components/l4d2-host-lib/tests/test_lifecycle.py -q` Run: `pytest l4d2host/tests/test_lifecycle.py -q`
Expected: PASS. Expected: PASS.
- [ ] **Step 5: Commit lifecycle implementation** - [ ] **Step 5: Commit lifecycle implementation**
```bash ```bash
git add components/l4d2-host-lib/src/l4d2host/{fs/base.py,fs/fuse_overlayfs.py,instances.py} components/l4d2-host-lib/tests/test_lifecycle.py git add l4d2host/{fs/base.py,fs/fuse_overlayfs.py,instances.py} l4d2host/tests/test_lifecycle.py
git commit -m "feat(l4d2): implement start stop delete lifecycle with callback support" git commit -m "feat(l4d2): implement start stop delete lifecycle with callback support"
``` ```
### Task 7: Add status and log read APIs ### Task 7: Add status and log read APIs
**Files:** **Files:**
- Create: `components/l4d2-host-lib/src/l4d2host/status.py` - Create: `l4d2host/status.py`
- Create: `components/l4d2-host-lib/src/l4d2host/logs.py` - Create: `l4d2host/logs.py`
- Test: `components/l4d2-host-lib/tests/test_status.py` - Test: `l4d2host/tests/test_status.py`
- Test: `components/l4d2-host-lib/tests/test_logs.py` - Test: `l4d2host/tests/test_logs.py`
- [ ] **Step 1: Write failing read-API tests** - [ ] **Step 1: Write failing read-API tests**
@ -584,7 +584,7 @@ def test_status_mapping():
- [ ] **Step 2: Run tests and verify failure** - [ ] **Step 2: Run tests and verify failure**
Run: `pytest components/l4d2-host-lib/tests/test_status.py components/l4d2-host-lib/tests/test_logs.py -q` Run: `pytest l4d2host/tests/test_status.py l4d2host/tests/test_logs.py -q`
Expected: FAIL. Expected: FAIL.
- [ ] **Step 3: Implement status/log readers** - [ ] **Step 3: Implement status/log readers**
@ -660,22 +660,22 @@ def stream_instance_logs(name: str, *, lines: int = 200, follow: bool = True):
- [ ] **Step 4: Run tests and verify pass** - [ ] **Step 4: Run tests and verify pass**
Run: `pytest components/l4d2-host-lib/tests/test_status.py components/l4d2-host-lib/tests/test_logs.py -q` Run: `pytest l4d2host/tests/test_status.py l4d2host/tests/test_logs.py -q`
Expected: PASS. Expected: PASS.
- [ ] **Step 5: Commit read APIs** - [ ] **Step 5: Commit read APIs**
```bash ```bash
git add components/l4d2-host-lib/src/l4d2host/{status.py,logs.py} components/l4d2-host-lib/tests/test_{status,logs}.py git add l4d2host/{status.py,logs.py} l4d2host/tests/test_{status,logs}.py
git commit -m "feat(l4d2): add status and journald log read APIs" git commit -m "feat(l4d2): add status and journald log read APIs"
``` ```
### Task 8: Wire CLI to implementations and finalize docs ### Task 8: Wire CLI to implementations and finalize docs
**Files:** **Files:**
- Modify: `components/l4d2-host-lib/src/l4d2host/cli.py` - Modify: `l4d2host/cli.py`
- Create: `components/l4d2-host-lib/README.md` - Create: `l4d2host/README.md`
- Modify: tests under `components/l4d2-host-lib/tests/` - Modify: tests under `l4d2host/tests/`
- [ ] **Step 1: Write failing CLI exit-code test** - [ ] **Step 1: Write failing CLI exit-code test**
@ -696,7 +696,7 @@ def test_cli_propagates_subprocess_return_code(monkeypatch):
- [ ] **Step 2: Run tests and verify failure** - [ ] **Step 2: Run tests and verify failure**
Run: `pytest components/l4d2-host-lib/tests/test_cli.py -q` Run: `pytest l4d2host/tests/test_cli.py -q`
Expected: FAIL. Expected: FAIL.
- [ ] **Step 3: Implement CLI handlers** - [ ] **Step 3: Implement CLI handlers**
@ -720,13 +720,13 @@ def start(name: str) -> None:
- [ ] **Step 4: Final test run** - [ ] **Step 4: Final test run**
Run: `pytest components/l4d2-host-lib/tests -q` Run: `pytest l4d2host/tests -q`
Expected: PASS. Expected: PASS.
- [ ] **Step 5: Commit finalization** - [ ] **Step 5: Commit finalization**
```bash ```bash
git add components/l4d2-host-lib git add l4d2host
git commit -m "docs(l4d2): finalize v1 CLI contracts and web-facing read APIs" git commit -m "docs(l4d2): finalize v1 CLI contracts and web-facing read APIs"
``` ```

View file

@ -42,41 +42,41 @@
## Planned File Layout ## Planned File Layout
- `components/l4d2-web-app/pyproject.toml` - `l4d2web/pyproject.toml`
- `components/l4d2-web-app/src/l4d2web/app.py` - `l4d2web/app.py`
- `components/l4d2-web-app/src/l4d2web/config.py` - `l4d2web/config.py`
- `components/l4d2-web-app/src/l4d2web/db.py` - `l4d2web/db.py`
- `components/l4d2-web-app/src/l4d2web/models.py` - `l4d2web/models.py`
- `components/l4d2-web-app/src/l4d2web/auth.py` - `l4d2web/auth.py`
- `components/l4d2-web-app/src/l4d2web/cli.py` - `l4d2web/cli.py`
- `components/l4d2-web-app/src/l4d2web/services/l4d2_facade.py` - `l4d2web/services/l4d2_facade.py`
- `components/l4d2-web-app/src/l4d2web/services/spec_yaml.py` - `l4d2web/services/spec_yaml.py`
- `components/l4d2-web-app/src/l4d2web/services/job_worker.py` - `l4d2web/services/job_worker.py`
- `components/l4d2-web-app/src/l4d2web/services/status.py` - `l4d2web/services/status.py`
- `components/l4d2-web-app/src/l4d2web/services/security.py` - `l4d2web/services/security.py`
- `components/l4d2-web-app/src/l4d2web/routes/auth_routes.py` - `l4d2web/routes/auth_routes.py`
- `components/l4d2-web-app/src/l4d2web/routes/overlay_routes.py` - `l4d2web/routes/overlay_routes.py`
- `components/l4d2-web-app/src/l4d2web/routes/blueprint_routes.py` - `l4d2web/routes/blueprint_routes.py`
- `components/l4d2-web-app/src/l4d2web/routes/server_routes.py` - `l4d2web/routes/server_routes.py`
- `components/l4d2-web-app/src/l4d2web/routes/job_routes.py` - `l4d2web/routes/job_routes.py`
- `components/l4d2-web-app/src/l4d2web/routes/log_routes.py` - `l4d2web/routes/log_routes.py`
- `components/l4d2-web-app/src/l4d2web/templates/*.html` - `l4d2web/templates/*.html`
- `components/l4d2-web-app/src/l4d2web/static/vendor/htmx.min.js` - `l4d2web/static/vendor/htmx.min.js`
- `components/l4d2-web-app/src/l4d2web/static/css/{tokens,layout,components,logs}.css` - `l4d2web/static/css/{tokens,layout,components,logs}.css`
- `components/l4d2-web-app/src/l4d2web/static/js/{sse,csrf}.js` - `l4d2web/static/js/{sse,csrf}.js`
- `components/l4d2-web-app/alembic.ini` - `l4d2web/alembic.ini`
- `components/l4d2-web-app/alembic/env.py` - `l4d2web/alembic/env.py`
- `components/l4d2-web-app/alembic/versions/0001_initial.py` - `l4d2web/alembic/versions/0001_initial.py`
- `components/l4d2-web-app/tests/*.py` - `l4d2web/tests/*.py`
- `components/l4d2-web-app/README.md` - `l4d2web/README.md`
### Task 1: Scaffold Flask app and base wiring ### Task 1: Scaffold Flask app and base wiring
**Files:** **Files:**
- Create: `components/l4d2-web-app/pyproject.toml` - Create: `l4d2web/pyproject.toml`
- Create: `components/l4d2-web-app/src/l4d2web/app.py` - Create: `l4d2web/app.py`
- Create: `components/l4d2-web-app/src/l4d2web/config.py` - Create: `l4d2web/config.py`
- Test: `components/l4d2-web-app/tests/test_health.py` - Test: `l4d2web/tests/test_health.py`
- [ ] **Step 1: Write failing health test** - [ ] **Step 1: Write failing health test**
@ -94,7 +94,7 @@ def test_health_endpoint():
- [ ] **Step 2: Run test and verify failure** - [ ] **Step 2: Run test and verify failure**
Run: `pytest components/l4d2-web-app/tests/test_health.py -q` Run: `pytest l4d2web/tests/test_health.py -q`
Expected: FAIL. Expected: FAIL.
- [ ] **Step 3: Implement app factory** - [ ] **Step 3: Implement app factory**
@ -125,25 +125,25 @@ def create_app(test_config=None):
- [ ] **Step 4: Run test and verify pass** - [ ] **Step 4: Run test and verify pass**
Run: `pytest components/l4d2-web-app/tests/test_health.py -q` Run: `pytest l4d2web/tests/test_health.py -q`
Expected: PASS. Expected: PASS.
- [ ] **Step 5: Commit scaffold** - [ ] **Step 5: Commit scaffold**
```bash ```bash
git add components/l4d2-web-app git add l4d2web
git commit -m "feat(l4d2-web): scaffold flask app and health endpoint" git commit -m "feat(l4d2-web): scaffold flask app and health endpoint"
``` ```
### Task 2: Add database models and migration baseline ### Task 2: Add database models and migration baseline
**Files:** **Files:**
- Create: `components/l4d2-web-app/src/l4d2web/db.py` - Create: `l4d2web/db.py`
- Create: `components/l4d2-web-app/src/l4d2web/models.py` - Create: `l4d2web/models.py`
- Create: `components/l4d2-web-app/alembic.ini` - Create: `l4d2web/alembic.ini`
- Create: `components/l4d2-web-app/alembic/env.py` - Create: `l4d2web/alembic/env.py`
- Create: `components/l4d2-web-app/alembic/versions/0001_initial.py` - Create: `l4d2web/alembic/versions/0001_initial.py`
- Test: `components/l4d2-web-app/tests/test_models.py` - Test: `l4d2web/tests/test_models.py`
- [ ] **Step 1: Write failing model tests** - [ ] **Step 1: Write failing model tests**
@ -168,7 +168,7 @@ def test_create_user_and_blueprint(tmp_path, monkeypatch):
- [ ] **Step 2: Run test and verify failure** - [ ] **Step 2: Run test and verify failure**
Run: `pytest components/l4d2-web-app/tests/test_models.py -q` Run: `pytest l4d2web/tests/test_models.py -q`
Expected: FAIL. Expected: FAIL.
- [ ] **Step 3: Implement schema** - [ ] **Step 3: Implement schema**
@ -255,24 +255,24 @@ class JobLog(Base):
- [ ] **Step 4: Run tests and verify pass** - [ ] **Step 4: Run tests and verify pass**
Run: `pytest components/l4d2-web-app/tests/test_models.py -q` Run: `pytest l4d2web/tests/test_models.py -q`
Expected: PASS. Expected: PASS.
- [ ] **Step 5: Commit models and migration** - [ ] **Step 5: Commit models and migration**
```bash ```bash
git add components/l4d2-web-app/src/l4d2web/{db.py,models.py} components/l4d2-web-app/alembic* git add l4d2web/{db.py,models.py} l4d2web/alembic*
git commit -m "feat(l4d2-web): add sqlite schema including blueprints and job logs" git commit -m "feat(l4d2-web): add sqlite schema including blueprints and job logs"
``` ```
### Task 3: Implement auth and admin bootstrap ### Task 3: Implement auth and admin bootstrap
**Files:** **Files:**
- Create: `components/l4d2-web-app/src/l4d2web/auth.py` - Create: `l4d2web/auth.py`
- Create: `components/l4d2-web-app/src/l4d2web/routes/auth_routes.py` - Create: `l4d2web/routes/auth_routes.py`
- Create: `components/l4d2-web-app/src/l4d2web/cli.py` - Create: `l4d2web/cli.py`
- Modify: `components/l4d2-web-app/src/l4d2web/app.py` - Modify: `l4d2web/app.py`
- Test: `components/l4d2-web-app/tests/test_auth.py` - Test: `l4d2web/tests/test_auth.py`
- [ ] **Step 1: Write failing auth tests** - [ ] **Step 1: Write failing auth tests**
@ -291,7 +291,7 @@ def test_login_sets_session(client, seed_user):
- [ ] **Step 2: Run tests and verify failure** - [ ] **Step 2: Run tests and verify failure**
Run: `pytest components/l4d2-web-app/tests/test_auth.py -q` Run: `pytest l4d2web/tests/test_auth.py -q`
Expected: FAIL. Expected: FAIL.
- [ ] **Step 3: Implement auth and CLI command** - [ ] **Step 3: Implement auth and CLI command**
@ -322,22 +322,22 @@ def promote_admin(username: str):
- [ ] **Step 4: Run tests and verify pass** - [ ] **Step 4: Run tests and verify pass**
Run: `pytest components/l4d2-web-app/tests/test_auth.py -q` Run: `pytest l4d2web/tests/test_auth.py -q`
Expected: PASS. Expected: PASS.
- [ ] **Step 5: Commit auth features** - [ ] **Step 5: Commit auth features**
```bash ```bash
git add components/l4d2-web-app/src/l4d2web/{auth.py,cli.py,app.py} components/l4d2-web-app/src/l4d2web/routes/auth_routes.py components/l4d2-web-app/tests/test_auth.py git add l4d2web/{auth.py,cli.py,app.py} l4d2web/routes/auth_routes.py l4d2web/tests/test_auth.py
git commit -m "feat(l4d2-web): add public auth and admin bootstrap command" git commit -m "feat(l4d2-web): add public auth and admin bootstrap command"
``` ```
### Task 4: Implement admin overlay catalog CRUD with path safety ### Task 4: Implement admin overlay catalog CRUD with path safety
**Files:** **Files:**
- Create: `components/l4d2-web-app/src/l4d2web/routes/overlay_routes.py` - Create: `l4d2web/routes/overlay_routes.py`
- Create: `components/l4d2-web-app/src/l4d2web/services/security.py` - Create: `l4d2web/services/security.py`
- Test: `components/l4d2-web-app/tests/test_overlays.py` - Test: `l4d2web/tests/test_overlays.py`
- [ ] **Step 1: Write failing overlay tests** - [ ] **Step 1: Write failing overlay tests**
@ -354,7 +354,7 @@ def test_overlay_path_must_be_under_root(admin_client):
- [ ] **Step 2: Run tests and verify failure** - [ ] **Step 2: Run tests and verify failure**
Run: `pytest components/l4d2-web-app/tests/test_overlays.py -q` Run: `pytest l4d2web/tests/test_overlays.py -q`
Expected: FAIL. Expected: FAIL.
- [ ] **Step 3: Implement route and validator** - [ ] **Step 3: Implement route and validator**
@ -372,21 +372,21 @@ def validate_overlay_path(raw: str) -> Path:
- [ ] **Step 4: Run tests and verify pass** - [ ] **Step 4: Run tests and verify pass**
Run: `pytest components/l4d2-web-app/tests/test_overlays.py -q` Run: `pytest l4d2web/tests/test_overlays.py -q`
Expected: PASS. Expected: PASS.
- [ ] **Step 5: Commit overlay CRUD** - [ ] **Step 5: Commit overlay CRUD**
```bash ```bash
git add components/l4d2-web-app/src/l4d2web/{routes/overlay_routes.py,services/security.py} components/l4d2-web-app/tests/test_overlays.py git add l4d2web/{routes/overlay_routes.py,services/security.py} l4d2web/tests/test_overlays.py
git commit -m "feat(l4d2-web): add admin overlay catalog CRUD with path validation" git commit -m "feat(l4d2-web): add admin overlay catalog CRUD with path validation"
``` ```
### Task 5: Implement blueprint CRUD and linkage rules ### Task 5: Implement blueprint CRUD and linkage rules
**Files:** **Files:**
- Create: `components/l4d2-web-app/src/l4d2web/routes/blueprint_routes.py` - Create: `l4d2web/routes/blueprint_routes.py`
- Test: `components/l4d2-web-app/tests/test_blueprints.py` - Test: `l4d2web/tests/test_blueprints.py`
- [ ] **Step 1: Write failing blueprint tests** - [ ] **Step 1: Write failing blueprint tests**
@ -409,7 +409,7 @@ def test_delete_blueprint_blocked_when_in_use(user_client, linked_blueprint):
- [ ] **Step 2: Run tests and verify failure** - [ ] **Step 2: Run tests and verify failure**
Run: `pytest components/l4d2-web-app/tests/test_blueprints.py -q` Run: `pytest l4d2web/tests/test_blueprints.py -q`
Expected: FAIL. Expected: FAIL.
- [ ] **Step 3: Implement blueprint routes** - [ ] **Step 3: Implement blueprint routes**
@ -436,21 +436,21 @@ def create_blueprint():
- [ ] **Step 4: Run tests and verify pass** - [ ] **Step 4: Run tests and verify pass**
Run: `pytest components/l4d2-web-app/tests/test_blueprints.py -q` Run: `pytest l4d2web/tests/test_blueprints.py -q`
Expected: PASS. Expected: PASS.
- [ ] **Step 5: Commit blueprint feature** - [ ] **Step 5: Commit blueprint feature**
```bash ```bash
git add components/l4d2-web-app/src/l4d2web/routes/blueprint_routes.py components/l4d2-web-app/tests/test_blueprints.py git add l4d2web/routes/blueprint_routes.py l4d2web/tests/test_blueprints.py
git commit -m "feat(l4d2-web): add private blueprint CRUD with in-use deletion guard" git commit -m "feat(l4d2-web): add private blueprint CRUD with in-use deletion guard"
``` ```
### Task 6: Implement server CRUD from blueprints (no overrides) ### Task 6: Implement server CRUD from blueprints (no overrides)
**Files:** **Files:**
- Create: `components/l4d2-web-app/src/l4d2web/routes/server_routes.py` - Create: `l4d2web/routes/server_routes.py`
- Test: `components/l4d2-web-app/tests/test_servers.py` - Test: `l4d2web/tests/test_servers.py`
- [ ] **Step 1: Write failing server tests** - [ ] **Step 1: Write failing server tests**
@ -468,7 +468,7 @@ def test_reassign_blueprint_anytime(user_client, server_and_two_blueprints):
- [ ] **Step 2: Run tests and verify failure** - [ ] **Step 2: Run tests and verify failure**
Run: `pytest components/l4d2-web-app/tests/test_servers.py -q` Run: `pytest l4d2web/tests/test_servers.py -q`
Expected: FAIL. Expected: FAIL.
- [ ] **Step 3: Implement server routes** - [ ] **Step 3: Implement server routes**
@ -495,22 +495,22 @@ def create_server():
- [ ] **Step 4: Run tests and verify pass** - [ ] **Step 4: Run tests and verify pass**
Run: `pytest components/l4d2-web-app/tests/test_servers.py -q` Run: `pytest l4d2web/tests/test_servers.py -q`
Expected: PASS. Expected: PASS.
- [ ] **Step 5: Commit server routes** - [ ] **Step 5: Commit server routes**
```bash ```bash
git add components/l4d2-web-app/src/l4d2web/routes/server_routes.py components/l4d2-web-app/tests/test_servers.py git add l4d2web/routes/server_routes.py l4d2web/tests/test_servers.py
git commit -m "feat(l4d2-web): add server creation and blueprint reassignment routes" git commit -m "feat(l4d2-web): add server creation and blueprint reassignment routes"
``` ```
### Task 7: Add direct `l4d2host` facade and blueprint-to-spec generation ### Task 7: Add direct `l4d2host` facade and blueprint-to-spec generation
**Files:** **Files:**
- Create: `components/l4d2-web-app/src/l4d2web/services/spec_yaml.py` - Create: `l4d2web/services/spec_yaml.py`
- Create: `components/l4d2-web-app/src/l4d2web/services/l4d2_facade.py` - Create: `l4d2web/services/l4d2_facade.py`
- Test: `components/l4d2-web-app/tests/test_l4d2_facade.py` - Test: `l4d2web/tests/test_l4d2_facade.py`
- [ ] **Step 1: Write failing facade tests** - [ ] **Step 1: Write failing facade tests**
@ -531,7 +531,7 @@ def test_initialize_uses_latest_blueprint_data(monkeypatch, server_with_blueprin
- [ ] **Step 2: Run tests and verify failure** - [ ] **Step 2: Run tests and verify failure**
Run: `pytest components/l4d2-web-app/tests/test_l4d2_facade.py -q` Run: `pytest l4d2web/tests/test_l4d2_facade.py -q`
Expected: FAIL. Expected: FAIL.
- [ ] **Step 3: Implement facade/spec generation** - [ ] **Step 3: Implement facade/spec generation**
@ -555,22 +555,22 @@ def initialize_server(server_id: int, on_stdout=None, on_stderr=None):
- [ ] **Step 4: Run tests and verify pass** - [ ] **Step 4: Run tests and verify pass**
Run: `pytest components/l4d2-web-app/tests/test_l4d2_facade.py -q` Run: `pytest l4d2web/tests/test_l4d2_facade.py -q`
Expected: PASS. Expected: PASS.
- [ ] **Step 5: Commit facade** - [ ] **Step 5: Commit facade**
```bash ```bash
git add components/l4d2-web-app/src/l4d2web/services/{spec_yaml.py,l4d2_facade.py} components/l4d2-web-app/tests/test_l4d2_facade.py git add l4d2web/services/{spec_yaml.py,l4d2_facade.py} l4d2web/tests/test_l4d2_facade.py
git commit -m "feat(l4d2-web): resolve live-linked blueprints to runtime specs via l4d2host" git commit -m "feat(l4d2-web): resolve live-linked blueprints to runtime specs via l4d2host"
``` ```
### Task 8: Implement scheduler, lock rules, and startup recovery ### Task 8: Implement scheduler, lock rules, and startup recovery
**Files:** **Files:**
- Create: `components/l4d2-web-app/src/l4d2web/services/job_worker.py` - Create: `l4d2web/services/job_worker.py`
- Modify: `components/l4d2-web-app/src/l4d2web/app.py` - Modify: `l4d2web/app.py`
- Test: `components/l4d2-web-app/tests/test_job_worker.py` - Test: `l4d2web/tests/test_job_worker.py`
- [ ] **Step 1: Write failing worker tests** - [ ] **Step 1: Write failing worker tests**
@ -597,7 +597,7 @@ def test_recover_stale_running_jobs(worker_fixture):
- [ ] **Step 2: Run tests and verify failure** - [ ] **Step 2: Run tests and verify failure**
Run: `pytest components/l4d2-web-app/tests/test_job_worker.py -q` Run: `pytest l4d2web/tests/test_job_worker.py -q`
Expected: FAIL. Expected: FAIL.
- [ ] **Step 3: Implement scheduler with in-memory guards** - [ ] **Step 3: Implement scheduler with in-memory guards**
@ -619,22 +619,22 @@ def can_start(job, state: SchedulerState) -> bool:
- [ ] **Step 4: Implement single-process guard + stale recovery** - [ ] **Step 4: Implement single-process guard + stale recovery**
Run: `pytest components/l4d2-web-app/tests/test_job_worker.py -q` Run: `pytest l4d2web/tests/test_job_worker.py -q`
Expected: PASS. Expected: PASS.
- [ ] **Step 5: Commit scheduler** - [ ] **Step 5: Commit scheduler**
```bash ```bash
git add components/l4d2-web-app/src/l4d2web/{services/job_worker.py,app.py} components/l4d2-web-app/tests/test_job_worker.py git add l4d2web/{services/job_worker.py,app.py} l4d2web/tests/test_job_worker.py
git commit -m "feat(l4d2-web): add async scheduler with lock rules and crash recovery" git commit -m "feat(l4d2-web): add async scheduler with lock rules and crash recovery"
``` ```
### Task 9: Persist command logs and add SSE job stream ### Task 9: Persist command logs and add SSE job stream
**Files:** **Files:**
- Create: `components/l4d2-web-app/src/l4d2web/routes/job_routes.py` - Create: `l4d2web/routes/job_routes.py`
- Modify: `components/l4d2-web-app/src/l4d2web/services/job_worker.py` - Modify: `l4d2web/services/job_worker.py`
- Test: `components/l4d2-web-app/tests/test_job_logs.py` - Test: `l4d2web/tests/test_job_logs.py`
- [ ] **Step 1: Write failing job-log tests** - [ ] **Step 1: Write failing job-log tests**
@ -652,7 +652,7 @@ def test_sse_resume_from_last_seq(client, seeded_job_logs):
- [ ] **Step 2: Run tests and verify failure** - [ ] **Step 2: Run tests and verify failure**
Run: `pytest components/l4d2-web-app/tests/test_job_logs.py -q` Run: `pytest l4d2web/tests/test_job_logs.py -q`
Expected: FAIL. Expected: FAIL.
- [ ] **Step 3: Implement persistence and SSE route** - [ ] **Step 3: Implement persistence and SSE route**
@ -689,23 +689,23 @@ def stream_job(job_id: int):
- [ ] **Step 4: Run tests and verify pass** - [ ] **Step 4: Run tests and verify pass**
Run: `pytest components/l4d2-web-app/tests/test_job_logs.py -q` Run: `pytest l4d2web/tests/test_job_logs.py -q`
Expected: PASS. Expected: PASS.
- [ ] **Step 5: Commit job log streaming** - [ ] **Step 5: Commit job log streaming**
```bash ```bash
git add components/l4d2-web-app/src/l4d2web/{routes/job_routes.py,services/job_worker.py} components/l4d2-web-app/tests/test_job_logs.py git add l4d2web/{routes/job_routes.py,services/job_worker.py} l4d2web/tests/test_job_logs.py
git commit -m "feat(l4d2-web): persist command logs and stream them with sse" git commit -m "feat(l4d2-web): persist command logs and stream them with sse"
``` ```
### Task 10: Add runtime server log stream and status model ### Task 10: Add runtime server log stream and status model
**Files:** **Files:**
- Create: `components/l4d2-web-app/src/l4d2web/routes/log_routes.py` - Create: `l4d2web/routes/log_routes.py`
- Create: `components/l4d2-web-app/src/l4d2web/services/status.py` - Create: `l4d2web/services/status.py`
- Modify: `components/l4d2-web-app/src/l4d2web/services/job_worker.py` - Modify: `l4d2web/services/job_worker.py`
- Test: `components/l4d2-web-app/tests/test_status_and_server_logs.py` - Test: `l4d2web/tests/test_status_and_server_logs.py`
- [ ] **Step 1: Write failing tests** - [ ] **Step 1: Write failing tests**
@ -722,7 +722,7 @@ def test_status_precedence():
- [ ] **Step 2: Run tests and verify failure** - [ ] **Step 2: Run tests and verify failure**
Run: `pytest components/l4d2-web-app/tests/test_status_and_server_logs.py -q` Run: `pytest l4d2web/tests/test_status_and_server_logs.py -q`
Expected: FAIL. Expected: FAIL.
- [ ] **Step 3: Implement log stream and status computation** - [ ] **Step 3: Implement log stream and status computation**
@ -759,23 +759,23 @@ def stream_server_logs(server_id: int):
- [ ] **Step 4: Add actual-state refresh updates** - [ ] **Step 4: Add actual-state refresh updates**
Run: `pytest components/l4d2-web-app/tests/test_status_and_server_logs.py -q` Run: `pytest l4d2web/tests/test_status_and_server_logs.py -q`
Expected: PASS. Expected: PASS.
- [ ] **Step 5: Commit status/log features** - [ ] **Step 5: Commit status/log features**
```bash ```bash
git add components/l4d2-web-app/src/l4d2web/{routes/log_routes.py,services/status.py,services/job_worker.py} components/l4d2-web-app/tests/test_status_and_server_logs.py git add l4d2web/{routes/log_routes.py,services/status.py,services/job_worker.py} l4d2web/tests/test_status_and_server_logs.py
git commit -m "feat(l4d2-web): add live server logs and desired-vs-actual status model" git commit -m "feat(l4d2-web): add live server logs and desired-vs-actual status model"
``` ```
### Task 11: Apply security and reliability hardening ### Task 11: Apply security and reliability hardening
**Files:** **Files:**
- Modify: `components/l4d2-web-app/src/l4d2web/app.py` - Modify: `l4d2web/app.py`
- Modify: `components/l4d2-web-app/src/l4d2web/db.py` - Modify: `l4d2web/db.py`
- Modify: `components/l4d2-web-app/src/l4d2web/routes/auth_routes.py` - Modify: `l4d2web/routes/auth_routes.py`
- Create: `components/l4d2-web-app/tests/test_security.py` - Create: `l4d2web/tests/test_security.py`
- [ ] **Step 1: Write failing hardening tests** - [ ] **Step 1: Write failing hardening tests**
@ -794,7 +794,7 @@ def test_login_rate_limit(client):
- [ ] **Step 2: Run tests and verify failure** - [ ] **Step 2: Run tests and verify failure**
Run: `pytest components/l4d2-web-app/tests/test_security.py -q` Run: `pytest l4d2web/tests/test_security.py -q`
Expected: FAIL. Expected: FAIL.
- [ ] **Step 3: Implement hardening measures** - [ ] **Step 3: Implement hardening measures**
@ -815,33 +815,33 @@ with engine.connect() as conn:
- [ ] **Step 4: Run tests and verify pass** - [ ] **Step 4: Run tests and verify pass**
Run: `pytest components/l4d2-web-app/tests/test_security.py -q` Run: `pytest l4d2web/tests/test_security.py -q`
Expected: PASS. Expected: PASS.
- [ ] **Step 5: Commit hardening** - [ ] **Step 5: Commit hardening**
```bash ```bash
git add components/l4d2-web-app/src/l4d2web/{app.py,db.py} components/l4d2-web-app/src/l4d2web/routes/auth_routes.py components/l4d2-web-app/tests/test_security.py git add l4d2web/{app.py,db.py} l4d2web/routes/auth_routes.py l4d2web/tests/test_security.py
git commit -m "feat(l4d2-web): add csrf, rate limiting, and sqlite reliability settings" git commit -m "feat(l4d2-web): add csrf, rate limiting, and sqlite reliability settings"
``` ```
### Task 12: Build UI and finalize docs ### Task 12: Build UI and finalize docs
**Files:** **Files:**
- Create: `components/l4d2-web-app/src/l4d2web/templates/base.html` - Create: `l4d2web/templates/base.html`
- Create: `components/l4d2-web-app/src/l4d2web/templates/dashboard.html` - Create: `l4d2web/templates/dashboard.html`
- Create: `components/l4d2-web-app/src/l4d2web/templates/blueprints.html` - Create: `l4d2web/templates/blueprints.html`
- Create: `components/l4d2-web-app/src/l4d2web/templates/server_detail.html` - Create: `l4d2web/templates/server_detail.html`
- Create: `components/l4d2-web-app/src/l4d2web/templates/admin_overlays.html` - Create: `l4d2web/templates/admin_overlays.html`
- Create: `components/l4d2-web-app/src/l4d2web/static/vendor/htmx.min.js` - Create: `l4d2web/static/vendor/htmx.min.js`
- Create: `components/l4d2-web-app/src/l4d2web/static/css/tokens.css` - Create: `l4d2web/static/css/tokens.css`
- Create: `components/l4d2-web-app/src/l4d2web/static/css/layout.css` - Create: `l4d2web/static/css/layout.css`
- Create: `components/l4d2-web-app/src/l4d2web/static/css/components.css` - Create: `l4d2web/static/css/components.css`
- Create: `components/l4d2-web-app/src/l4d2web/static/css/logs.css` - Create: `l4d2web/static/css/logs.css`
- Create: `components/l4d2-web-app/src/l4d2web/static/js/sse.js` - Create: `l4d2web/static/js/sse.js`
- Create: `components/l4d2-web-app/src/l4d2web/static/js/csrf.js` - Create: `l4d2web/static/js/csrf.js`
- Create: `components/l4d2-web-app/README.md` - Create: `l4d2web/README.md`
- Test: `components/l4d2-web-app/tests/test_pages.py` - Test: `l4d2web/tests/test_pages.py`
- [ ] **Step 1: Write failing page tests** - [ ] **Step 1: Write failing page tests**
@ -860,7 +860,7 @@ def test_blueprint_page_private_visibility(user_client, other_users_blueprint):
- [ ] **Step 2: Run tests and verify failure** - [ ] **Step 2: Run tests and verify failure**
Run: `pytest components/l4d2-web-app/tests/test_pages.py -q` Run: `pytest l4d2web/tests/test_pages.py -q`
Expected: FAIL. Expected: FAIL.
- [ ] **Step 3: Implement templates and style tokens** - [ ] **Step 3: Implement templates and style tokens**
@ -883,13 +883,13 @@ a {
- [ ] **Step 4: Run tests and full suite** - [ ] **Step 4: Run tests and full suite**
Run: `pytest components/l4d2-web-app/tests -q` Run: `pytest l4d2web/tests -q`
Expected: PASS. Expected: PASS.
- [ ] **Step 5: Commit UI and docs** - [ ] **Step 5: Commit UI and docs**
```bash ```bash
git add components/l4d2-web-app git add l4d2web
git commit -m "docs(l4d2-web): finalize blueprint-driven ui and deployment contracts" git commit -m "docs(l4d2-web): finalize blueprint-driven ui and deployment contracts"
``` ```

View file

@ -39,8 +39,8 @@ Do not perform cleanup after a failure unless the user approves cleanup.
## Files And Runtime Locations ## Files And Runtime Locations
- Read: `docs/superpowers/specs/2026-05-05-l4d2-host-smoke-test-design.md` - Read: `docs/superpowers/specs/2026-05-05-l4d2-host-smoke-test-design.md`
- Read: `components/l4d2-host-lib/pyproject.toml` - Read: `l4d2host/pyproject.toml`
- Read: `components/l4d2-host-lib/src/l4d2host/**` - Read: `l4d2host/**`
- Remote create: `~/l4d2host-smoke/` - Remote create: `~/l4d2host-smoke/`
- Remote create: `~/l4d2host-smoke/.venv/` - Remote create: `~/l4d2host-smoke/.venv/`
- Remote create: `~/l4d2host-smoke/specs/smoke.yaml` - Remote create: `~/l4d2host-smoke/specs/smoke.yaml`
@ -238,7 +238,7 @@ Expected: user explicitly approves before commands are run.
Run from repository root: Run from repository root:
```bash ```bash
tar --exclude='*.pyc' --exclude='__pycache__' --exclude='.pytest_cache' --exclude='*.egg-info' -C components/l4d2-host-lib -czf /var/folders/h4/nnvk2kxs2sv7nr32kmb_4dm40000gn/T/opencode/l4d2-host-lib-smoke.tar.gz . tar --exclude='*.pyc' --exclude='__pycache__' --exclude='.pytest_cache' --exclude='*.egg-info' -C l4d2host -czf /var/folders/h4/nnvk2kxs2sv7nr32kmb_4dm40000gn/T/opencode/l4d2-host-lib-smoke.tar.gz .
``` ```
Expected: command exits 0 and archive exists at `/var/folders/h4/nnvk2kxs2sv7nr32kmb_4dm40000gn/T/opencode/l4d2-host-lib-smoke.tar.gz`. Expected: command exits 0 and archive exists at `/var/folders/h4/nnvk2kxs2sv7nr32kmb_4dm40000gn/T/opencode/l4d2-host-lib-smoke.tar.gz`.
@ -264,7 +264,7 @@ Expected: archive copies successfully.
Run: Run:
```bash ```bash
ssh ckn@10.0.4.128 'set -eu; tar -xzf "$HOME/l4d2host-smoke/l4d2-host-lib-smoke.tar.gz" -C "$HOME/l4d2host-smoke/src"; test -f "$HOME/l4d2host-smoke/src/pyproject.toml"; test -f "$HOME/l4d2host-smoke/src/src/l4d2host/cli.py"' ssh ckn@10.0.4.128 'set -eu; tar -xzf "$HOME/l4d2host-smoke/l4d2-host-lib-smoke.tar.gz" -C "$HOME/l4d2host-smoke/src"; test -f "$HOME/l4d2host-smoke/src/pyproject.toml"; test -f "$HOME/l4d2host-smoke/src/cli.py"'
``` ```
Expected: source tree unpacks and expected files exist. Expected: source tree unpacks and expected files exist.

View file

@ -12,8 +12,8 @@
The repository now contains both planned components: The repository now contains both planned components:
- `components/l4d2-host-lib`: Python host library and `l4d2ctl` CLI. - `l4d2host`: Python host library and `l4d2ctl` CLI.
- `components/l4d2-web-app`: Flask app for users, blueprints, servers, jobs, and logs. - `l4d2web`: Flask app for users, blueprints, servers, jobs, and logs.
The web app depends on the host library for real lifecycle behavior. Before wiring web lifecycle jobs end-to-end, the host contract should be proven on an actual Linux machine with `steamcmd`, `fuse-overlayfs`, systemd user services, and journald available. The web app depends on the host library for real lifecycle behavior. Before wiring web lifecycle jobs end-to-end, the host contract should be proven on an actual Linux machine with `steamcmd`, `fuse-overlayfs`, systemd user services, and journald available.
@ -81,7 +81,7 @@ Purpose: install the current repository implementation on the target host withou
Allowed actions after approval: Allowed actions after approval:
- Copy or archive the current `components/l4d2-host-lib` source to the server. - Copy or archive the current `l4d2host` source to the server.
- Install it using its existing `pyproject.toml`, preferably into an isolated virtual environment. - Install it using its existing `pyproject.toml`, preferably into an isolated virtual environment.
- Verify that `l4d2ctl --help` exposes the fixed v1 command surface. - Verify that `l4d2ctl --help` exposes the fixed v1 command surface.

View file

@ -17,7 +17,10 @@ dependencies = [
l4d2ctl = "l4d2host.cli:app" l4d2ctl = "l4d2host.cli:app"
[tool.setuptools] [tool.setuptools]
package-dir = {"" = "src"} packages = ["l4d2host", "l4d2host.fs", "l4d2host.templates"]
[tool.setuptools.packages.find] [tool.setuptools.package-dir]
where = ["src"] l4d2host = "."
[tool.setuptools.package-data]
"l4d2host.templates" = ["*.service"]

View file

@ -16,7 +16,15 @@ dependencies = [
] ]
[tool.setuptools] [tool.setuptools]
package-dir = {"" = "src"} packages = ["l4d2web", "l4d2web.routes", "l4d2web.services"]
[tool.setuptools.packages.find] [tool.setuptools.package-dir]
where = ["src"] l4d2web = "."
[tool.setuptools.package-data]
l4d2web = [
"templates/*.html",
"static/css/*.css",
"static/js/*.js",
"static/vendor/*.js",
]