Tests that found things reading did not

The testing pass: 2140 tests to 2283, and four bugs that no amount of
reading had turned up. Three came from driving the JavaScript under a
Node DOM stub, which is the practice CLAUDE.md sets out and this is the
reason it does.

The terminal dropped every keystroke after a reconnect. `onclose` closed
over the module-level socket rather than its own, and close() queues its
event -- so the old socket's close arrived after a new one was assigned
and nulled the live one. Output kept coming, because onmessage is bound
to the object, while every send gates on the variable. It also announced
"Disconnected" about a shell that had just reconnected.

Two scripts were loaded twice on /messages, once by base.html and again
by the page. Each is an IIFE with its own state, so four keyboard
shortcuts toggled their panel twice and therefore did nothing, /help
opened two dialogs, and an @ mention attached its file twice. A sweep
refuses any template re-loading what base.html has.

The microphone had no guard while the permission prompt was up, so each
click opened another stream and only the last was ever stopped. And a
skill shared with you took its name out of your own library: create
checked uniqueness against what is *visible* rather than what is owned,
against a (owner_id, name) constraint, and told you to edit a row you
cannot edit.

--ink-faint failed the contrast minimum in both themes -- 3.85 and 3.19
against 4.5 -- so the smallest text on every screen was the hardest to
read. Measured in a headless browser rather than judged by eye.

And the suite runs on 3.11 and 3.12 now as well as 3.14. It had only ever
run on 3.14 while the image ships 3.12 and the packaging claimed 3.11:
the interpreter most people would run was the one nothing had tested.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-07 14:41:45 +02:00
parent 25fe81a224
commit 0514568df0
33 changed files with 2859 additions and 13 deletions
+42 -1
View File
@@ -360,7 +360,48 @@ Questions raised before the security stage, answered by it:
button press. pip's `only-if-needed` default limits the blast radius, which is
why this is a note rather than an emergency.
## For the testing stage
## Testing — *Stage 4*
2140 tests became **2283**, and writing them found four bugs that reading had
not — three of them by driving the JavaScript under a Node DOM stub, which is
the practice `CLAUDE.md` sets out and the reason it does.
- **The terminal dropped every keystroke after a reconnect.** `socket.onclose`
closed over the module-level variable rather than its own connection, and
`close()` queues its event: both reconnect paths close the old socket and
immediately open a new one, so the old one's `close` arrived afterwards and
nulled the *live* socket. Output kept coming, because `onmessage` is bound to
the object; every send gates on `socket && readyState === OPEN`. It also said
"Disconnected" about a shell that had just reconnected, because
`closedOnPurpose` had been cleared for the new connection.
- **Two scripts were loaded twice on `/messages`** — once by `base.html` and
again by the page. Each is an IIFE with its own state, so four keyboard
shortcuts toggled their panel twice and therefore did nothing, `/help` opened
two dialogs, and an `@` mention attached its file twice. A sweep now refuses
any template re-loading what `base.html` already has.
- **The microphone had no guard while the permission prompt was up.** State only
left `idle` after `getUserMedia` resolved, so every click in that window
opened another stream; only the last was stopped.
- **A shared skill took its name out of your own library.** `create` checked
uniqueness with `by_name`, which is scoped to what is *visible* — owned or
shared — against a `(owner_id, name)` constraint. `documents.create_base` next
door asks it correctly. Found by writing HTTP tests for `api/library.py`.
Also: `--ink-faint` failed the 4.5:1 contrast minimum in **both** themes (3.85
in Moria, 3.19 in Shire), measured by computing the ratios in a headless browser
rather than by eye. Raised in both.
**Verified rather than assumed:** the suite now runs on Python **3.11, 3.12 and
3.14**. It had only ever run on 3.14, while `Dockerfile` ships 3.12 and
`requires-python` claimed 3.11 — the interpreter most people would actually run
was the one nothing had tested. Both pass.
One measurement worth keeping honest: the `slow` marker splits 365 tests that
stand up a real server, shell, PTY or git repository, but the fast subset is
111s against 140s for everything. The split is for CI, not for a dramatic
saving.
## Still open after the testing stage
- `api/library.py`: 28 routes, effectively zero HTTP coverage. Highest exposure
— auth, ownership and permission gates all live at that boundary.