diff --git a/.agents/phases/todo/01_keyboard_kiosk_control/00_phase.md b/.agents/phases/complete/01_keyboard_kiosk_control/00_phase.md similarity index 100% rename from .agents/phases/todo/01_keyboard_kiosk_control/00_phase.md rename to .agents/phases/complete/01_keyboard_kiosk_control/00_phase.md diff --git a/.agents/phases/todo/01_keyboard_kiosk_control/01_keyboard_command_router.md b/.agents/phases/complete/01_keyboard_kiosk_control/01_keyboard_command_router.md similarity index 100% rename from .agents/phases/todo/01_keyboard_kiosk_control/01_keyboard_command_router.md rename to .agents/phases/complete/01_keyboard_kiosk_control/01_keyboard_command_router.md diff --git a/.agents/phases/todo/01_keyboard_kiosk_control/02_wall_navigation_commands.md b/.agents/phases/complete/01_keyboard_kiosk_control/02_wall_navigation_commands.md similarity index 100% rename from .agents/phases/todo/01_keyboard_kiosk_control/02_wall_navigation_commands.md rename to .agents/phases/complete/01_keyboard_kiosk_control/02_wall_navigation_commands.md diff --git a/.agents/phases/todo/01_keyboard_kiosk_control/03_keyboard_reachable_tile_controls.md b/.agents/phases/complete/01_keyboard_kiosk_control/03_keyboard_reachable_tile_controls.md similarity index 100% rename from .agents/phases/todo/01_keyboard_kiosk_control/03_keyboard_reachable_tile_controls.md rename to .agents/phases/complete/01_keyboard_kiosk_control/03_keyboard_reachable_tile_controls.md diff --git a/.agents/phases/todo/01_keyboard_kiosk_control/04_help_overlay_and_keyboard_e2e.md b/.agents/phases/complete/01_keyboard_kiosk_control/04_help_overlay_and_keyboard_e2e.md similarity index 100% rename from .agents/phases/todo/01_keyboard_kiosk_control/04_help_overlay_and_keyboard_e2e.md rename to .agents/phases/complete/01_keyboard_kiosk_control/04_help_overlay_and_keyboard_e2e.md diff --git a/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__00_phase.a1.err b/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__00_phase.a1.err new file mode 100644 index 0000000..e69de29 diff --git a/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__00_phase.a1.md b/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__00_phase.a1.md new file mode 100644 index 0000000..3766b0d --- /dev/null +++ b/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__00_phase.a1.md @@ -0,0 +1,9 @@ +**Phase 01 final verification pass — complete, 100% green** + +- Verified all 4 tasks done (files in `complete/`); implementation review: key router in `watch.js`, share-page router, CSS focus/selection affordances, `?` overlay, pointer-free E2E suite — all sound. +- Gates: `ruff check` + `ruff format --check` ✅ · `pyright` 0 errors ✅ · `uv run pytest` → 223 passed, 2 skipped (realdisplay), 1 pre-existing FastAPI deprecation warning · `MIRROR_REALDISPLAY=1 pytest -m realdisplay` → 2 passed ✅ · `pytest -m e2e` → 51 passed (incl. `test_keyboard_wall.py`, which a meta-test keeps pointer-free) · `node --check` on all static JS ✅. +- Completion criteria: all 6 ✅ (j/k+Enter, f/g/n/p/1–9/m/?/Esc keyboard-only E2E; no hover-only controls via CSS guard + focus-ring E2E; `#name` text-safe; e2e/realdisplay green; key map documented in README "Kiosk client" and `docs/kiosk-silverblue.md`). +- README fixed: added missing env vars `MIRROR_MAX_BITRATE`, `MIRROR_PING_INTERVAL`, `DEBUGPY_WAIT` to the config table; added the one-time `rpm-ostree` layering note before the kiosk install snippet. +- AGENTS.md fixed: removed stale "roadmap is intentionally empty" parenthetical (3 phases pending); coverage ~94% → ~95%. No content added. +- **Unresolved, needs your call:** README/SECURITY.md say `mirror.reeseapps.com`; `docs/kiosk-silverblue.md` (reference-client naming table with real IPs) says `mirror.reeselink.com`. I couldn't verify which is the live deployment domain, so I changed neither — say the word and I'll unify. +- Next pending phase: `02_sharp_capture`. diff --git a/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__00_phase.a1.md.status b/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__00_phase.a1.md.status new file mode 100644 index 0000000..9766475 --- /dev/null +++ b/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__00_phase.a1.md.status @@ -0,0 +1 @@ +ok diff --git a/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__00_phase.a1.validate b/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__00_phase.a1.validate new file mode 100644 index 0000000..6de2384 --- /dev/null +++ b/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__00_phase.a1.validate @@ -0,0 +1,32 @@ +...................................................ss................... [ 32%] +........................................................................ [ 64%] +........................................................................ [ 96%] +......... [100%] +=============================== warnings summary =============================== +.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1 + /home/ducoterra/Projects/Personal/mirror/.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead. + from starlette.testclient import TestClient as TestClient # noqa + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +================================ tests coverage ================================ +_______________ coverage: platform linux, python 3.14.8-final-0 ________________ + +Name Stmts Miss Cover Missing +------------------------------------------------------ +src/mirror/__init__.py 3 0 100% +src/mirror/__main__.py 72 3 96% 129, 145, 156 +src/mirror/access.py 60 0 100% +src/mirror/app.py 185 11 94% 108, 113, 117, 182-183, 212-216, 245 +src/mirror/config.py 68 5 93% 110-111, 118-120 +src/mirror/debug.py 15 10 33% 19-28 +src/mirror/protocol.py 59 0 100% +src/mirror/registry.py 191 3 98% 166, 180, 355 +------------------------------------------------------ +TOTAL 653 32 95% +=========================== short test summary info ============================ +SKIPPED [1] tests/e2e/test_x11_capture.py:220: set MIRROR_REALDISPLAY=1 to exercise the real X11 capture backend +SKIPPED [1] tests/e2e/test_x11_capture.py:257: set MIRROR_REALDISPLAY=1 to exercise the real X11 capture backend +223 passed, 2 skipped, 1 warning in 35.82s +All checks passed! +26 files already formatted +0 errors, 0 warnings, 0 informations diff --git a/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__01_keyboard_command_router.a1.err b/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__01_keyboard_command_router.a1.err new file mode 100644 index 0000000..e69de29 diff --git a/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__01_keyboard_command_router.a1.md b/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__01_keyboard_command_router.a1.md new file mode 100644 index 0000000..c4e81eb --- /dev/null +++ b/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__01_keyboard_command_router.a1.md @@ -0,0 +1,14 @@ +All green. Final report: + +**Implemented (task 01 — keyboard router + cursor on `/watch`)** +- `watch.js`: removed the per-tile `Enter`/`Space` and global `Escape` listeners; added `state.cursor` (stream-id), `orderedTiles()`, `selectedTile()`, `setSelected()` (focus-follows-selection, `preventScroll`), `moveSelection(delta)` (wraps), and one `onWallKey` router (bails on `defaultPrevented`/Ctrl/Cmd/Alt chords/fields; `j`/`ArrowDown`, `k`/`ArrowUp`, `Escape`→`unstage`; `preventDefault` only for handled keys). Cursor re-seats on `dropTile` (nearest remaining tile, wraps) and tracks the staged tile. +- `app.css`: `.tile[data-selected]` border at live-tile weight; `.tile:focus-visible` accent ring. +- `tests/e2e/test_keyboard_wall.py`: 6 mouse-free tests (j selects first + focus follows, j/k wrap both ends, arrows, `Control+j/k/ArrowDown` no-ops, Esc unstages, Esc no-op). + +**Gates** — `ruff check` + `ruff format --check` + `pyright`: clean; `uv run pytest`: 193 passed, 2 skipped (realdisplay off); `MIRROR_REALDISPLAY=1 uv run pytest -m realdisplay`: 2 passed; `grep -c 'addEventListener("keydown"' watch.js` = 1; `pytest -m e2e -k keyboard`: 6 passed; `pytest tests/test_app.py`: 61 passed. Coverage 95% (no new Python). + +**Docs** — no README/AGENTS.md changes (keymap docs are task 04's scope; no statements went stale). + +**Notable** — the committed phase-04 task file failed `ruff format` (ruff 0.17 now formats markdown code blocks); since I may not edit `todo/` files, I added `extend-exclude = [".agents"]` to `[tool.ruff]` in `pyproject.toml` with a comment explaining why the harness-owned planning tree is outside the code gate. + +**Next pending task** — `02_wall_navigation_commands.md` (Enter/f, g, n/p, 1–9, m in the router). diff --git a/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__01_keyboard_command_router.a1.md.status b/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__01_keyboard_command_router.a1.md.status new file mode 100644 index 0000000..9766475 --- /dev/null +++ b/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__01_keyboard_command_router.a1.md.status @@ -0,0 +1 @@ +ok diff --git a/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__01_keyboard_command_router.a1.validate b/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__01_keyboard_command_router.a1.validate new file mode 100644 index 0000000..28e649d --- /dev/null +++ b/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__01_keyboard_command_router.a1.validate @@ -0,0 +1,8 @@ +ImportError while loading conftest '/home/ducoterra/Projects/Personal/mirror/tests/conftest.py'. +tests/conftest.py:8: in + from mirror.app import create_app +E ModuleNotFoundError: No module named 'mirror' +All checks passed! +repo hygiene: TRACKED files look like log drops / key material (untrack with 'git rm --cached ' and add a .gitignore rule): + .env.example +validation FAILED (see output above) diff --git a/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__01_keyboard_command_router.a2.err b/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__01_keyboard_command_router.a2.err new file mode 100644 index 0000000..e69de29 diff --git a/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__01_keyboard_command_router.a2.md b/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__01_keyboard_command_router.a2.md new file mode 100644 index 0000000..5b5ae3b --- /dev/null +++ b/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__01_keyboard_command_router.a2.md @@ -0,0 +1,14 @@ +100% green. Report: + +**Fixed (validator failures)** +- `.agents/validate.sh` ran bare `python3 -m pytest`/`ruff` — in this uv-managed project the package and dev tools live in `.venv`, so `mirror` was unimportable. Adapted the script (as its own header instructs) to the project's real gate: `uv run pytest -q` + `uv run ruff check .` + `uv run ruff format --check .` + `uv run pyright`. +- Validator's generic key-material regex over-matched the tracked `.env.example` — a committed config *template* with default/empty values, deliberately re-allowed by `.gitignore` (`!.env.example`). Added a commented per-project allow-list in `validate.sh` (the script's stated customization point) instead of `git rm --cached`, which my rules reserve for files my task file explicitly names. A real `.env`/`.env.local` still fails the gate. +- No application code changes from the previous attempt (router, cursor, CSS, E2E suite) were touched — they were already green. + +**Gates** — `bash .agents/validate.sh`: **exit 0** (193 passed / 2 realdisplay-skipped, `ruff check` + `ruff format --check` clean, `pyright` 0 errors, all hygiene checks clean); `MIRROR_REALDISPLAY=1 uv run pytest -m realdisplay`: 2 passed; `pytest -m e2e -k keyboard`: 6 passed; `pytest tests/test_app.py`: 61 passed; coverage 95% (no new Python). + +**Docs** — no README/AGENTS.md changes needed (not the phase's final pass; no statements went stale). + +**Notable** — `validate.sh` is untracked harness scaffolding; its two adaptations are the only changes this round. + +**Next pending task** — `02_wall_navigation_commands.md` (Enter/f, g, n/p, 1–9, m in the router). diff --git a/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__01_keyboard_command_router.a2.md.status b/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__01_keyboard_command_router.a2.md.status new file mode 100644 index 0000000..9766475 --- /dev/null +++ b/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__01_keyboard_command_router.a2.md.status @@ -0,0 +1 @@ +ok diff --git a/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__01_keyboard_command_router.a2.validate b/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__01_keyboard_command_router.a2.validate new file mode 100644 index 0000000..7216f57 --- /dev/null +++ b/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__01_keyboard_command_router.a2.validate @@ -0,0 +1,31 @@ +......................ss................................................ [ 36%] +........................................................................ [ 73%] +................................................... [100%] +=============================== warnings summary =============================== +.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1 + /home/ducoterra/Projects/Personal/mirror/.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead. + from starlette.testclient import TestClient as TestClient # noqa + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +================================ tests coverage ================================ +_______________ coverage: platform linux, python 3.14.8-final-0 ________________ + +Name Stmts Miss Cover Missing +------------------------------------------------------ +src/mirror/__init__.py 3 0 100% +src/mirror/__main__.py 72 3 96% 129, 145, 156 +src/mirror/access.py 60 0 100% +src/mirror/app.py 185 12 94% 108, 113, 116-117, 182-183, 212-216, 245 +src/mirror/config.py 68 5 93% 110-111, 118-120 +src/mirror/debug.py 15 10 33% 19-28 +src/mirror/protocol.py 59 0 100% +src/mirror/registry.py 191 3 98% 166, 180, 355 +------------------------------------------------------ +TOTAL 653 33 95% +=========================== short test summary info ============================ +SKIPPED [1] tests/e2e/test_x11_capture.py:220: set MIRROR_REALDISPLAY=1 to exercise the real X11 capture backend +SKIPPED [1] tests/e2e/test_x11_capture.py:257: set MIRROR_REALDISPLAY=1 to exercise the real X11 capture backend +193 passed, 2 skipped, 1 warning in 25.27s +All checks passed! +26 files already formatted +0 errors, 0 warnings, 0 informations diff --git a/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__02_wall_navigation_commands.a1.err b/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__02_wall_navigation_commands.a1.err new file mode 100644 index 0000000..e69de29 diff --git a/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__02_wall_navigation_commands.a1.md b/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__02_wall_navigation_commands.a1.md new file mode 100644 index 0000000..174f438 --- /dev/null +++ b/.agents/reports/01_keyboard_kiosk_control/01_keyboard_kiosk_control__02_wall_navigation_commands.a1.md @@ -0,0 +1,13 @@ +All gates green. Task complete. + +**Report — Task 02 (wall navigation commands)** + +- `watch.js`: extracted `setMuted(tile, muted)` (audio button is now a two-liner calling it); added `Enter`/`f`, `g`, `n`/`p`, `1`–`9`, `m` to the single keydown router (case-insensitive matching; `?` left for task 04); no-op toasts (`no mirror N`, `no mirrors to switch to`, `no mirrors to stage`, `no mirror to mute`, `this mirror has no audio`); `f`-branch comment cites the `--kiosk`/`window-config.ini` reality; `n`/`p` **stage** the neighbour per the task assumption. +- **Defect fixed in previously completed work**: staging a *replacement* tile (`n`/`p`, digits, or clicking another tile) unstaged the replacement — `unstage()`'s `exitFullscreen()` fired `fullscreenchange` after `state.staged` was already the new tile, and the handler unstaged it. Reproduced deterministically (6/6) via a throwaway script; fixed with `unstage({ keepFullscreen })` on the replace path (the stage container itself stays fullscreen; no exit, no event). +- `tests/e2e/test_keyboard_wall.py`: +10 mouse-free tests — Enter (selected & unselected), `f` both states, `g` both states, `n`/`p` step with same-`