chore(l4d2): flatten component layout
This commit is contained in:
parent
7bac34c886
commit
288eda7c37
81 changed files with 221 additions and 205 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -1,2 +1,7 @@
|
||||||
.worktrees/
|
.worktrees/
|
||||||
|
.venv/
|
||||||
|
.pytest_cache/
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
*.egg-info/
|
||||||
l4d2web.db*
|
l4d2web.db*
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
|
|
@ -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",
|
||||||
|
]
|
||||||
Loading…
Reference in a new issue