LIVE https://todo-haskell.bfj.oncarlos.com 7d40789

# todo-haskell: status

Deployed build `7d40789` to `todo-haskell.bfj.oncarlos.com` (channel
`edge`). `/api/version` reports `7d40789`. The first deploy was
`7881a7e`; `7d40789` fixes the review findings below.

## Checks

| Check | Result |
|---|---|
| `make test` (hspec, 18 examples) | pass |
| Shared suite, local x86 build | 67 passed, 3 skipped (the suite's own JS-off skips) |
| `scripts/boot-check.sh dist/app-linux-arm64` | `/healthz` 200, `/api/version` 200, clean exit on SIGTERM |
| Shared suite against https://todo-haskell.bfj.oncarlos.com | 67 passed, 3 skipped |

## Notes

- Stack: warp + wai, lucid2, sqlite-simple, cookie, file-embed. Reasons
  are in `README.md`.
- The arm64 binary is built inside the arm64 root with Debian's GHC
  9.6.6, statically linked against glibc, stripped: 28 MB. The x86 build
  uses ghcup's GHC 9.10.3. `cabal.project` pins the Hackage index so both
  resolve the same library versions.
- A cold arm64 dependency build (about fifty libraries, `-j8`, emulated)
  took roughly 40 minutes on a busy docs-t1. After that, rebuilding the
  app takes about 90 seconds.
- docs-t1 has `libgmp.so.10` but no `libgmp.so`, which ghcup's x86 GHC
  needs to link. The Makefile makes a link to it in `.cache/x86-lib` and
  points `LIBRARY_PATH` there. No system change.
- Warp's `x509` flag is off (it would pull in a crypto library for TLS
  client certificates, which the edge already handles).
- Static glibc warns about `getaddrinfo`, `dlopen` and `getpw*`. The app
  calls none of them at runtime.

## Review findings fixed (7d40789)

1. **An edit in progress was lost when a response arrived.** `app.js`
   replaced the whole list with each action's answer, which dropped any
   editor opened while that request was pending. It now records the open
   edit (item, typed value, selection, focus) before swapping in the new
   list and puts it back on the same item afterwards. It also ignores the
   `focusout` caused by the swap, so the old input isn't saved by
   mistake. Checked in Chromium with a toggle response delayed 1.5 s:
   double-clicking another item and typing during the delay leaves the
   editor open, focused and holding the typed text, and Enter saves it.
2. **`--listen` accepted any `127.x.x.x` address.** It now accepts exactly
   `127.0.0.1:<port>`. `127.0.0.2:8080` and `0.0.0.0:8080` are refused
   with a usage error.

After the fixes: `make test` passes, the shared suite passes locally and
against https://todo-haskell.bfj.oncarlos.com (67 passed, 3 skipped),
and `boot-check.sh` passes.

## Review findings not fixed

None.
