docs(ocaml): STATUS: review fixes live at a24ca85

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Paul Campbell pushed by claude agent, operated by paul@keymail.dev d803224001571c109f7430e8139614e6c9184132 parent 25c96be
1 file changed, +38 −16
  • apps/ocaml/STATUS.md +38 −16
diff --git a/apps/ocaml/STATUS.md b/apps/ocaml/STATUS.md
index 0684abf..4e9ea01 100644
--- a/apps/ocaml/STATUS.md
+++ b/apps/ocaml/STATUS.md
@@ -1,28 +1,50 @@
-LIVE https://todo-ocaml.bfj.oncarlos.com b927326
+LIVE https://todo-ocaml.bfj.oncarlos.com a24ca85
## Checks
-- `make test`: 15 Alcotest tests pass (form parsing, escaping, and the
- request handler against an in-memory database).
+- `make test`: 16 Alcotest tests pass (form parsing, Unicode trimming,
+ escaping, and the request handler against an in-memory database).
- Shared suite against `dist/app --listen 127.0.0.1`: 67 passed, 3 skipped
(the two JS-only edit tests with JS off, and the screenshot with JS off).
- `scripts/boot-check.sh dist/app-linux-arm64`: /healthz and /api/version
200, clean exit on SIGTERM.
-- Deployed with `make deploy`; `/api/version` and `X-Carlos-Version` both
- report `b927326`.
+- Deployed with `make deploy`; `/api/version` reports `a24ca85`.
- Shared suite against https://todo-ocaml.bfj.oncarlos.com: 67 passed,
3 skipped.
+## Review fixes (a24ca85)
+
+1. **Unbounded request heads.** `bin/main.ml` now runs cohttp-eio's
+ per-connection loop itself instead of `Cohttp_eio.Server.run` (which
+ hard-codes `max_size:max_int`). Each connection reads through a byte
+ budget: 64 KiB for a request line plus headers, then 1 MiB + 64 KiB
+ per request after that, with at most 256 connections. Checked locally:
+ a 10 MB header line and 100,000 small headers both get
+ `431 Request Header Fields Too Large`, and the process stays at about
+ 17 MB RSS.
+2. **Edits lost on re-render.** `app.js` keeps an open editor across a
+ response: the same item stays in edit mode with its unsaved text,
+ focus and selection, and losing focus during the swap doesn't count
+ as a save. Checked in Chromium with a toggle response held back while
+ another item was being edited.
+3. **Unicode whitespace.** `Form.trim` strips Unicode White_Space (plus
+ U+FEFF) and is used for both adding and editing. A title of only
+ non-breaking spaces adds nothing, and editing to one deletes the item.
+4. **SQLite from source.** `tools/sqlite.sh` downloads the SQLite 3.54.1
+ amalgamation once into `.cache/src` (checked against sqlite.org's
+ SHA3-256) and compiles it into `.cache/sqlite-<arch>`. The `sqlite3`
+ opam package finds it through `pkg-config`. `make clean-deps` deletes
+ those builds and keeps the tarball. The shipped binary contains SQLite
+ 3.54.1 (source id 2026-10-16), not the arm64 root's 3.46.1.
+
## Notes
-- The shipped binary is static glibc (aarch64, 8.6 MB), built in the
- emulated arm64 root with Debian's OCaml 5.3.0 as `ocaml-system`. The
- linker warns about `getpwnam`, `gethostbyname` and the like (pulled in
- by OCaml's `unix` library). The app never calls them.
-- The first `make build-arm64` after `make clean-deps` compiles every
- library under qemu. That took about 35 minutes here, including opam
- setting up the switch. After that, a rebuild of the app alone takes
- about 15 seconds.
-- docs-t1 has no SQLite headers or `pkg-config`, so the local x86 build
- unpacks Debian's `libsqlite3-dev` and `pkgconf` into `.cache/` with
- `apt-get download` (see README). The arm64 root already has both.
+- The shipped binary is static glibc (aarch64), built in the emulated
+ arm64 root with Debian's OCaml 5.3.0 as `ocaml-system`. The linker
+ warns about `getpwnam`, `gethostbyname` and the like (pulled in by
+ OCaml's `unix` library). The app never calls them.
+- `make clean-deps && make build-arm64` took 20.5 minutes here: SQLite
+ and every OCaml library compiled under qemu. After that, a rebuild of
+ the app alone takes about 15 seconds.
+- docs-t1 has no `pkg-config`, so the x86 build unpacks Debian's
+ `pkgconf` into `.cache/` with `apt-get download` (see README).