Log tabs with a daemon I/O view, and Playwright UI tests

The log view splits into All / Daemon I/O / Scans / HTTP. Daemon I/O is
the control protocol itself, logged where every command funnels through
so it covers socket clients, the CLI and the web UI alike. stderr and the
in-app buffer now have separate filters, so the UI can keep debug detail
the terminal should not carry.

Playwright drives a real browser against a daemon on fixture feeds. Eight
tests, each mapping to a bug that reached a user -- the Rust tests and the
stub-DOM smoke test cannot see a wrong selector or a dead handler.

It immediately found one: OPML folders rendered expanded by default,
because the code stored closed groups, so any folder never toggled counted
as open.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AdXho5tTkjFLeUXKbEjKBh
This commit is contained in:
2026-09-10 17:07:19 +00:00
parent 4429f1119c
commit 8b21d19365
18 changed files with 457 additions and 20 deletions

View File

@@ -56,6 +56,39 @@ and until now nothing set them.
---
## 2026-09-10 — Daemon I/O log tab, and Playwright
**Log tabs.** All / Daemon I/O / Scans / HTTP. "Daemon I/O" is the control protocol itself: every
command arriving (`-> {"cmd":...}`) and every event leaving (`<- {"ev":...}`), logged under
`ipx::io` at the one point every command funnels through, so it catches socket clients, the CLI
proxying and the web UI alike. Rendered in that tab as in/out rather than level+target.
The two logging destinations now have **separate filters**: stderr follows `IPX_LOG` (default
`ipx=info`), the in-app buffer follows `IPX_UI_LOG` (default `ipx=debug`) and holds 5000 lines. The
UI can therefore show protocol traffic and routine skips that would be noise on a terminal.
**Playwright.** `npm install` plus `npx playwright install --with-deps chromium`, added to
`/src/install.sh`. `playwright.config.js` starts a fixture feed server and a daemon on a scratch
config, and eight tests drive a real browser. Each maps to a bug that actually reached Ray.
Two things the harness taught, both worth keeping:
- **Playwright starts `webServer` before `globalSetup`.** Writing the scratch config in globalSetup
meant the daemon launched with no config, fell back to defaults, and tried to seize the live
daemon's socket. The config is written at config-load time instead.
- Ports 8097/8771 looked free but `ss` showed 8097 held by a process outside this container (shared
host network). Moved to 8791/8792.
**The suite found a real bug on its first green-ish run:** OPML folders rendered *expanded* by
default. The code stored the set of closed groups, so a folder never toggled — every folder in a
fresh browser — counted as open, the exact opposite of the comment above it. It stores the open ones
now. Two other failures were my tests' fault, not the code's: asserting `S1E1` on `.ep.first()` when
newest-first put a different episode there, and an unscoped `getByText` matching both the sidebar
entry and the page heading. The `SE` in that first failure was the artwork placeholder's initials,
which I nearly misread as a broken chip.
---
## 2026-09-10 — Regression: derived feeds looked "unsubscribed" to half the code
Reported as `error: enclosure 235 belongs to unsubscribed feed "abort-retry-fail"`. Moving OPML