4.0 KiB
Row 300 build notes (Launchpad): R6-N1, an HTTP-level test of the user-session option swap (#1807)
Brief: docs/plans/2026-10-23_r6-followup-rows.md § Brief: R6-N1. Reviewer Duckworth, gate owner Sage.
Base c13c337e1 (refactor, after row 294 landed at d3059c2c3). Branch launchpad/row300.
Change
Tests only. One new file, packages/webui/tests/user-option-swap.test.mjs. No product change and no existing assertion or fixture touched.
- Gate stubbed open. The file registers an in-thread load hook (
registerHooks, as thread-claims.test and library-cli.test do through--import) keyed on the one URLserve.mjs?gate=open. On that URL alone it prependsreturn true;touserRoute's body, then importsstartServerfrom it. Every other import of serve.mjs, and every relative import inside the copy, is the real module. If the gate's signature line is not in serve.mjs exactly once, the hook throws. A renamed gate then fails loudly, instead of quietly testing the closed gate. The test also asserts that the copy'suserRouteanswers true forPOST /api/bus/message. - Setup. This is granted-user.test.mjs's setup, cut to what the test needs: the fake OP, the
in-process broker for
demo, createSso with the access hook, anduserOptionsForover alice's transport. Every owner option is a stub that records its own name. The owner transport records['transport', verb, args]. One list,owner, holds both kinds of owner call. - Positive control. The owner signs in and posts
/api/bus/message. Exactly['option', 'send']must be recorded, so the route reachessendand the count works. - The check. Alice signs in and walks G4's owner routes, copied, plus two chat reads. No answer
may say
owner-only, so the gate really is open. After each route, every owner record must be['transport', 'plugins', {}], the read mos-1394 allowed. The message route must reach the user transport (message.send), and every transport opened is alice's.
Why a new file: granted-user.test.mjs's setup imports startServer statically. Passing the gate-open copy into it would change that fixture, and a fixture change goes to Mos. The route list and setup are copied, with a comment naming their source.
Mutant
M12: serve.mjs:689 drops send from the user-session destructure, so a user session keeps the
owner's send. Killed when "R6-N1" reports not ok. Expected failure: on POST /api/bus/message,
the owner record holds ['option', 'send']. run1/mutants.py applies it (anchor exactly once), runs
the new file alone, restores with git checkout and checks the tree is clean.
Gate
Duckworth approves the manifest. On Sage's CLEAR: run1/gate.sh does npm ci (root and webui), the new file whole, the webui suite whole, then M12. Logs go to /mnt/storage/scratch/launchpad-r300/run1, outside the tree. Not run yet.
MANIFEST.sha256 lists the one new path.
Run 1 (sage-3408 CLEAR, 12:05Z)
npm ci root and webui were green. The new file was red on its first run (mos-1272, placed by me):
TypeError: Cannot read properties of undefined (reading 'set-cookie') in signIn. When I cut down
granted-user.test.mjs's call, I dropped headers: res.headers from what it resolves, so the
sign-in found no cookies. The fix puts headers back, the same as granted-user.test.mjs:40. No
assertion changed. The new file then passed alone (1/0). The fix is a new commit, and the manifest
is re-hashed. The gate resumes at new-file on the new head.
Run 1 resumed (TAG -r2) on db5122920, manifest db8be52ce895603e687985acfae29e53e3e8782ae945c3cded6cca36141bfa3b:
- new file whole: 1 pass, 0 fail.
- webui suite whole: 375 pass, 0 fail, 0 skipped, 0 cancelled, 58.2 s (limit 600).
- M12 killed. "R6-N1" reports not ok at :151:
POST /api/bus/message: ["option","send"], where["transport","plugins",{}]is expected. The restore left the tree clean.
Duckworth N1 (duckworth-0263, non-blocking): the setup passes no upstream, so the swap's
upstream = null has no recorder here. The test proves the swap for the options it stubs. It
proves nothing about upstream.