From f265840892582e93dd9e0df794b041fa44a65cc9 Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Fri, 23 Oct 2026 00:36:41 -0500 Subject: [PATCH] launchpad: row 294 round 3 (#1800): G5's baseline is coalesce(max(seq), 0), since max(seq) is NULL on the fixture's empty events table and run 2 read 0 of 22 refusal events (sage-3212, Mos ruling pending sage-3211); manifest round 3, run 2 receipts; unrun --- agents/launchpad/work/queue-294/BUILD.md | 25 ++ agents/launchpad/work/queue-294/MANIFEST.md | 10 +- .../launchpad/work/queue-294/MANIFEST.sha256 | 2 +- agents/launchpad/work/queue-294/run2/gate.sh | 35 ++ .../launchpad/work/queue-294/run2/mutants.py | 81 ++++ .../launchpad/work/queue-294/run2/summary.txt | 5 + .../launchpad/work/queue-294/run2/webui.log | 418 ++++++++++++++++++ packages/webui/tests/granted-user.test.mjs | 2 +- 8 files changed, 574 insertions(+), 4 deletions(-) create mode 100755 agents/launchpad/work/queue-294/run2/gate.sh create mode 100644 agents/launchpad/work/queue-294/run2/mutants.py create mode 100644 agents/launchpad/work/queue-294/run2/summary.txt create mode 100644 agents/launchpad/work/queue-294/run2/webui.log diff --git a/agents/launchpad/work/queue-294/BUILD.md b/agents/launchpad/work/queue-294/BUILD.md index 8a2f8f935..ff554157f 100644 --- a/agents/launchpad/work/queue-294/BUILD.md +++ b/agents/launchpad/work/queue-294/BUILD.md @@ -324,6 +324,13 @@ product change. The manifest adds packages/webui/tests/browser.test.mjs (18 path is `refusals.length`, not a literal. The verb check is not possible, because the event names no verb; the test pins the whole body instead. +Round 3 (sage-3212; goes to Mos as the row's second red, sage-3211, before any push or run): one +changed line, in new test G5. Its baseline was `SELECT max(seq) n FROM events`. The G5 fixture has +an empty events table, so that is NULL and `seq > NULL` matches no row: run 2 read 0 events added +where run 1's count(*) had shown 22. The line is now `SELECT coalesce(max(seq), 0) n FROM events`. +Nothing else in G5 and no product file changes. The manifest stays 18 paths; only +granted-user.test.mjs's hash moves. My round 2 check missed the empty-table case. + ## Runs ### Run 1, 2026-10-23 04:57Z (sage-3187 CLEAR), head 685ff053b, manifest 0b3f9c9d…94f3 @@ -350,3 +357,21 @@ returned (launchpad-0060). 1 skipped, "the esbuild 0.28.1 editor rebuild" (gizmoduck queue-264 BUILD.md:516-518, DEFERRED 264a N2). Row 270's gate ran only webui pinned-copy.test. The fresh 294 gate still runs npm ci in packages/webui first, so the test should run here. If it skips again, the run names it and says why. + +### Run 2, 2026-10-23 05:33Z (sage-3199 CLEAR), head 66c181717, manifest 7ec7b1ea…315b + +Duckworth's round 2 approve was recorded first (duckworth-0253, rev 3455, comment 31029). Drivers +and logs are in run2/. TMPDIR was under /mnt/storage/scratch/tmp. + +| Step | Result | Time | Limit | +|---|---|---|---| +| npm ci --ignore-scripts, root | ok, esbuild 0.28.1 | 2.2 s | 900 s | +| npm ci --ignore-scripts, packages/webui | ok, esbuild 0.28.2 | 1.2 s | 900 s | +| webui suite | 365 tests: 364 pass, 1 fail, 0 skipped | 58.2 s | 600 s | + +The driver stopped at the first red. The slot was returned (launchpad-0070), and I made no edit. + +- The editor rebuild check ran and passed, and browser.test.mjs passed with the round 2 pin. +- G5:269 failed `0 !== 22`, "one event per refused verb". Every verb was still refused with + `access-refused`. Placed as a test defect (the NULL baseline, round 3 above); as the row's + second red it went to Mos through Sage (sage-3211) before the fix runs. diff --git a/agents/launchpad/work/queue-294/MANIFEST.md b/agents/launchpad/work/queue-294/MANIFEST.md index 7ee2b51a8..10cf8e84c 100644 --- a/agents/launchpad/work/queue-294/MANIFEST.md +++ b/agents/launchpad/work/queue-294/MANIFEST.md @@ -1,7 +1,13 @@ -# Row 294 (FED R6, #1800) candidate manifest, round 2 +# Row 294 (FED R6, #1800) candidate manifest, round 3 + +Round 3 (sage-3212, Mos ruling pending in sage-3211): one line in granted-user.test.mjs G5. The +baseline is `SELECT coalesce(max(seq), 0) n FROM events`, because max(seq) is NULL on the fixture's +empty events table and run 2 read 0 events added. Only that file's hash moves; still 18 paths. Round 2 +(7ec7b1ea…315b) was approved by Duckworth (rev 3455). Its text follows unchanged. + Branch launchpad/row294, base c902a767d (the round 1 head ee77b3635 rebased with range-diff all "=" -and stable patch-ids unchanged; receipts in run1/). The round 2 head is the commit that adds this +and stable patch-ids unchanged; receipts in run1/). The round 3 head is the commit that adds this file. MANIFEST.sha256 lists the 18 code and test paths changed from the base, hashed from that commit's blobs (`sha256sum -c` passes on it). BUILD.md, ASSERTION-PRESERVATION.json and run1/ are the row note and are not in the manifest. webui/src/bus.mjs is byte-identical to the base diff --git a/agents/launchpad/work/queue-294/MANIFEST.sha256 b/agents/launchpad/work/queue-294/MANIFEST.sha256 index b4b58e2dc..51bc96e12 100644 --- a/agents/launchpad/work/queue-294/MANIFEST.sha256 +++ b/agents/launchpad/work/queue-294/MANIFEST.sha256 @@ -15,4 +15,4 @@ e7673188c1d76ecc63cdc24e7d1470922b1d69bedeb994a862e94924edfc27d2 packages/webui 70b243ed9a83ff822a4c7a148528ff5153574cc87d5c776fd2e12f340762ed76 packages/webui/src/sso.mjs 8cc1185beda9085ced62470f1b9e39d95dcca2dcc8bf9325c18c2f0b2da618dc packages/webui/src/user-bus.mjs 58f022643839b78c0bd149927157d1f7b36977212975a4fe549c9661c31c4e40 packages/webui/tests/browser.test.mjs -8bf20e514817d21356e59ca21a8a99ee25609f084f4a26c5a409d71a90b6f6a4 packages/webui/tests/granted-user.test.mjs +673f09560b7cf4515856ab1181fce8fcc2f70d59fb01bda9d687cc3d9b259e95 packages/webui/tests/granted-user.test.mjs diff --git a/agents/launchpad/work/queue-294/run2/gate.sh b/agents/launchpad/work/queue-294/run2/gate.sh new file mode 100755 index 000000000..02b2665cc --- /dev/null +++ b/agents/launchpad/work/queue-294/run2/gate.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# Row 294 gate run 2, sage-3199: npm ci (root and packages/webui), suites whole, every +# scripts/test-*.sh, then the mutants. Stops at the first red. FROM= resumes at a step +# (the mos-1411 single cli rerun uses FROM=cli TAG=-rerun). +set -u -o pipefail +WT=/mnt/storage/scratch/launchpad-r294/wt +OUT=/mnt/storage/scratch/launchpad-r294/run2 +FROM=${FROM:-npm-root}; TAG=${TAG:-} +export TMPDIR=$(mktemp -d /mnt/storage/scratch/tmp/gXXXXXX) +echo "start $(date -u +%H:%M:%SZ) FROM=$FROM TAG=$TAG TMPDIR $TMPDIR head $(git -C $WT rev-parse HEAD)" >> $OUT/summary.txt +[ -z "$(git -C $WT status --porcelain)" ] || { echo "STOP worktree not clean" | tee -a $OUT/summary.txt; exit 9; } +on=0 +step() { # name limit dir cmd... + local name=$1 limit=$2 dir=$3; shift 3 + [ $on = 1 ] || [ $name = $FROM ] || return 0; on=1 + local t0=$(date +%s%N) + ( cd $dir && timeout -k 10 $limit "$@" ) > $OUT/$name$TAG.log 2>&1 + local rc=$? + local ms=$(( ($(date +%s%N) - t0) / 1000000 )); local dt=$((ms/1000)).$(printf %03d $((ms%1000))) + echo "$name$TAG rc=$rc seconds=$dt limit=$limit $(date -u +%H:%M:%SZ)" | tee -a $OUT/summary.txt + [ $rc -eq 0 ] || { echo "STOP at $name$TAG" | tee -a $OUT/summary.txt; exit $rc; } +} +step npm-root 900 $WT npm ci --ignore-scripts +step npm-webui 900 $WT/packages/webui npm ci --ignore-scripts +step webui 600 $WT/packages/webui node --test tests/ +step cli 2100 $WT/packages/cli node --test tests/*.test.mjs +step bus 900 $WT/packages/bus node --test tests/*.test.mjs +declare -A L=([auth]=126 [conductor]=145 [config]=130 [discord]=147 [extension-package]=130 [foundation]=269 [land-work]=171 [queue]=226 [release]=157 [task]=231) +for s in auth conductor config discord extension-package foundation land-work queue release task; do + step test-$s ${L[$s]} $WT bash scripts/test-$s.sh +done +[ -z "$(git -C $WT status --porcelain)" ] || { echo "STOP worktree dirty after the scripts" | tee -a $OUT/summary.txt; git -C $WT status --porcelain >> $OUT/summary.txt; exit 9; } +echo "SUITES GREEN $(date -u +%H:%M:%SZ)" | tee -a $OUT/summary.txt +python3 -B $OUT/mutants.py +rc=$?; echo "MUTANTS rc=$rc $(date -u +%H:%M:%SZ)" | tee -a $OUT/summary.txt; exit $rc diff --git a/agents/launchpad/work/queue-294/run2/mutants.py b/agents/launchpad/work/queue-294/run2/mutants.py new file mode 100644 index 000000000..90f8d3550 --- /dev/null +++ b/agents/launchpad/work/queue-294/run2/mutants.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +# Row 294 mutants M1-M11 (BUILD.md "Mutants"). Each mutant edits the worktree (every old text must +# match exactly once), runs its killing file alone under timeout -k 10 300 with the TAP reporter, +# then restores with git checkout and checks the tree is clean. Killed = the named killing test(s) +# report "not ok". M1 also needs G5 "ok" in the same run (Duckworth C1). +import json, os, re, subprocess, sys, time + +WT = '/mnt/storage/scratch/launchpad-r294/wt' +OUT = '/mnt/storage/scratch/launchpad-r294/run2' +GU = ('packages/webui', 'tests/granted-user.test.mjs') +AB = ('packages/cli', 'tests/access-boot.test.mjs') + +CHAT_CALL = "verb === 'plugins' ? owner('plugins', {}) : call(verb, args)" +MUTANTS = [ + ('M1', [('packages/webui/src/serve.mjs', "if (!userRoute(path, req.method)) return json(res, 403, { error: 'owner-only'", + "if (false) return json(res, 403, { error: 'owner-only'")], GU, ['R6 G4'], ['R6 G5']), + ('M2', [('packages/webui/src/sso.mjs', "if (s.user !== undefined && !users?.check(s)) {", "if (s.user !== undefined && false) {")], GU, ['R6 G3'], []), + ('M3', [('packages/webui/src/public/user.html', "but not your messages.", "but not your replies.")], GU, ['R6 G6'], []), + ('M4', [('packages/cli/src/access.mjs', "const peers = (system.federation?.peers ?? []).map((p) => p.id);", "const peers = [];")], AB, + ['R6 a configured peer id', 'R6 B1'], []), + ('M5', [('packages/webui/src/access.mjs', "return user && live(doc, user.id, b) ? user.id : null;", "return user ? user.id : null;")], GU, ['R6 G2'], []), + ('M6', [('packages/webui/src/user-bus.mjs', "env: { ...rest, MOSAIC_BUS_USER_NONCE", "env: { ...env, MOSAIC_BUS_USER_NONCE")], GU, ['R6 G8'], []), + ('M7', [('packages/cli/src/config.mjs', " if (access) config.access = access;\n", "")], AB, ['R6 B1'], []), + ('M8', [('packages/webui/src/chat.mjs', CHAT_CALL, + "verb === 'plugins' ? owner('plugins', {}) : verb === 'thread.list' ? owner(verb, args) : call(verb, args)")], GU, ['R6 G9'], []), + ('M9', [('packages/cli/src/config.mjs', " warn(`access file refused at boot (${code}), no granted user binds until it is fixed: ${file}`);\n", "")], + AB, ['R6 B3'], []), + ('M10', [('packages/webui/src/console-bus.mjs', "return session => consoleBus(transport(session.user), { send, owner });", + "return session => consoleBus(transport(session.user), { send, owner: (v, a) => owner(v, { ...a, sub: session.sub }) });")], GU, ['R6 G9'], []), + ('M11', [('packages/webui/src/chat.mjs', "const dispatch = createChannelCaller({ name: 'webui', call: owner === null ? call : (verb, args) => " + CHAT_CALL + " });", + "let last = {};\n const dispatch = createChannelCaller({ name: 'webui', call: owner === null ? call : (verb, args) => verb === 'plugins' ? owner('plugins', last) : call(verb, args) });"), + ('packages/webui/src/chat.mjs', " try { return await dispatch(verb, args); }", " try { last = args; return await dispatch(verb, args); }")], GU, ['R6 G9'], []), +] + +def git(*a): + return subprocess.run(['git', '-C', WT, *a], capture_output=True, text=True, check=True).stdout + +def restore(files): + if files: git('checkout', '--', *sorted(files)) + if git('status', '--porcelain'): raise SystemExit('worktree not clean after restore') + +def results(tap): + # Top-level TAP test lines: "ok N - name" / "not ok N - name" at no indentation. + return {m.group(2): m.group(1) == 'ok' for m in re.finditer(r'^(ok|not ok) \d+ - (.*)$', tap, re.M)} + +def main(): + only = sys.argv[1:] + if git('status', '--porcelain'): raise SystemExit('worktree not clean at start') + env = dict(os.environ) + summary, bad = [], 0 + for mid, edits, (pkg, test), killers, greens in MUTANTS: + if only and mid not in only: continue + files = set() + try: + for path, old, new in edits: + p = os.path.join(WT, path); s = open(p).read() + if s.count(old) != 1: raise SystemExit(f'{mid}: anchor count {s.count(old)} in {path}') + open(p, 'w').write(s.replace(old, new)); files.add(path) + diff = git('diff') + t0 = time.time() + r = subprocess.run(['timeout', '-k', '10', '300', 'node', '--test', '--test-reporter=tap', test], + cwd=os.path.join(WT, pkg), capture_output=True, text=True, env=env) + dt = round(time.time() - t0, 1) + finally: + restore(files) + open(f'{OUT}/{mid}.diff', 'w').write(diff) + open(f'{OUT}/{mid}.tap', 'w').write(r.stdout + '\n# stderr\n' + r.stderr) + res = results(r.stdout) + hit = lambda pre: [n for n, ok in res.items() if n.startswith(pre)] + red = [n for n, ok in res.items() if not ok] + killed = r.returncode not in (0, 124, 137) and all(any(not res[n] for n in hit(k)) for k in killers) \ + and all(hit(g) and all(res[n] for n in hit(g)) for g in greens) + bad += not killed + line = f'{mid} rc={r.returncode} seconds={dt} killed={killed} red={red}' + print(line, flush=True) + summary.append({'mutant': mid, 'rc': r.returncode, 'seconds': dt, 'killed': killed, 'red': red, + 'killers': killers, 'greens': greens}) + json.dump(summary, open(f'{OUT}/mutants.json', 'w'), indent=1) + sys.exit(1 if bad else 0) + +main() diff --git a/agents/launchpad/work/queue-294/run2/summary.txt b/agents/launchpad/work/queue-294/run2/summary.txt new file mode 100644 index 000000000..3505ee384 --- /dev/null +++ b/agents/launchpad/work/queue-294/run2/summary.txt @@ -0,0 +1,5 @@ +start 05:33:44Z FROM=npm-root TAG= TMPDIR /mnt/storage/scratch/tmp/gbuNdnK head 66c18171766ec35d2e84440bd4e62b13cc494dc9 +npm-root rc=0 seconds=2.179 limit=900 05:33:47Z +npm-webui rc=0 seconds=1.204 limit=900 05:33:48Z +webui rc=1 seconds=58.219 limit=600 05:34:46Z +STOP at webui diff --git a/agents/launchpad/work/queue-294/run2/webui.log b/agents/launchpad/work/queue-294/run2/webui.log new file mode 100644 index 000000000..896c906e3 --- /dev/null +++ b/agents/launchpad/work/queue-294/run2/webui.log @@ -0,0 +1,418 @@ +✔ row 194 T16 Settings Agents: unavailable read-only, launch follows its request, confirms, reason and refusal (21200.447968ms) +✔ row 256 T7 T8 the human page offers Delete on a creative instance, the tester page never does (3851.801754ms) +✔ row 194 T16: each agent action makes one call with only its keys, logs the user and answers the request (47.103306ms) +✔ row 194 T16: the boundary refuses before any bus call (21.672718ms) +✔ row 194 T16: read-only when send is off or no writer is wired; no route has a --send exception (10.39829ms) +✔ row 194 T16: a bus refusal names its reason and nothing changed; an unknown outcome or a bad shape is never retried (12.273549ms) +✔ row 194 T16: the request read takes one id, adds the refusal reason and refuses an unexpected shape (12.546557ms) +✔ row 194 T16: delete joins add and change on /api/instances with only revision, instance and op (4.813888ms) +✔ row 194 T16 D4.1: the tester listener launches, terminates and reads requests; regenerate, veto and delete are refused with no call (16.913793ms) +✔ row 194 N5: agents.js is served as JavaScript and index.html loads it once (5.167141ms) +✔ Q169 the signed-in human names a seat and stops the assistant, one bus call each, logged with the user (56.448865ms) +✔ Q169 the name and stop routes need send on, the public name, the user, the exact Origin and same-origin Fetch Metadata (30.24353ms) +✔ Q169 the name and stop routes take JSON within 4 KiB and only their own keys, all text (13.456376ms) +✔ Row 167 a null body on the name and stop routes is 400 invalid-request with no write, and the next request is answered (7.904021ms) +✔ Q169 a bus refusal says nothing changed; a lost or odd answer says the change may have been made (15.513711ms) +✔ Q169 the names read is a bus read like the others (4.61894ms) +✔ the Board: held roles with no machine sessions, a claim with no address, the toggle kept across a reload, and Message addressed to the role (9226.01783ms) +✔ a Seen click while a Board read is in flight waits for that read, then posts (3864.427337ms) +✔ a redirect answer on a Board read, a bus read, a conversation read, a board POST, a module read and a send signs the page out: one line, nothing stale, no further reads (12005.864452ms) +✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (6607.304173ms) +Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286} +✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (8415.467371ms) +✔ the README says a refused field table names the file or the key at fault, never a path (2.547364ms) +✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (13566.719029ms) +✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (200.737611ms) +✔ decisions route serves the roles and recent lists from the reader view (125.181042ms) +✔ credentials route serves the broker credential status from the reader view (35.913347ms) +✔ replies route serves the replies to one message from the reader view (74.493573ms) +✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (117.272719ms) +✔ bus refusals map to statuses and never carry a path or a stack (84.725063ms) +✔ humanCall speaks the human transport protocol without blocking the server (2756.823825ms) +✔ busReader takes --business, else the live bus host, else refuses (744.665939ms) +✔ humanCall kills a transport that runs past its timeout (590.725094ms) +✔ serve refuses a --business value that is not a business id (1190.080637ms) +✔ WebUI channel off returns 503 and named recovery for every chat route without a chat operation (77.716579ms) +✔ WebUI only selection keeps web chat enabled while retaining the old read envelope (5.930279ms) +✔ WebUI channel-disabled recovery text is fixed and never echoes transport text (3.049595ms) +✔ chat browser: list/create named projects and threads, hostile labels inert and no implicit writes (3800.342351ms) +✔ chat browser: thread drafts, receipts and cursor history stay isolated; refresh never writes (4591.830173ms) +✔ chat browser: uncertain writes retain draft, ownership refusal clears it, read-only cannot send (4127.562754ms) +✔ chat browser: a late read from A cannot paint B; sign-out clears private state and stops reads (3736.559168ms) +✔ chat browser: page forward and deduplicate replies; pending and byte-limit submissions do not duplicate (2856.733288ms) +✔ chat browser: scrolled history stays put, New messages and an own send return to the tail (2725.878046ms) +✔ chat browser: automatic polling follows a reply without a POST or a lost draft (12630.063778ms) +✔ chat browser: display stays bounded while every forward page remains reachable (2518.810917ms) +✔ chat browser: a confirmed visible message replaces its pending receipt; malformed receipts stay unknown (2860.187009ms) +✔ chat browser: a refusal elsewhere forgets private chat drafts (1658.755391ms) +✔ chat browser: a late project creation receipt cannot navigate away from another thread (1330.247015ms) +✔ chat reads map only fixed verbs, strict forward paging and pure GET (72.670606ms) +✔ chat refuses malformed and repeated query/id values before transport (20.974981ms) +✔ chat project.create: one verified write, fixed identity/class and private log (9.42558ms) +✔ chat project.create: each existing Console write fence makes zero bus calls (20.315573ms) +✔ chat project.create: refusal or lost/malformed receipt never retries or invents success (16.582154ms) +✔ chat thread.create: one verified write, fixed identity/class and private log (5.075296ms) +✔ chat thread.create: each existing Console write fence makes zero bus calls (20.803577ms) +✔ chat thread.create: refusal or lost/malformed receipt never retries or invents success (18.74415ms) +✔ chat thread.send: one verified write, fixed identity/class and private log (9.869431ms) +✔ chat thread.send: each existing Console write fence makes zero bus calls (21.141103ms) +✔ chat thread.send: refusal or lost/malformed receipt never retries or invents success (17.273069ms) +✔ chat thread.open: one verified write, fixed identity/class and private log (2.908672ms) +✔ chat thread.open: each existing Console write fence makes zero bus calls (42.171683ms) +✔ chat thread.open: refusal or lost/malformed receipt never retries or invents success (16.741319ms) +✔ chat createThread receipt requires an instance even when its id is valid (4.654023ms) +✔ chat write receipts expose only the fields belonging to their operation (9.245546ms) +✔ chat validates names, body, project and strict empty open payload (9.689665ms) +✔ chat read failures redact ownership and refuse invalid transport arrays (10.834021ms) +✔ REQ-USER-2 real broker: owned catalog, create/name/filter, isolated history and 403/404 without mutations (259.430605ms) +✔ chat repeats every POST boundary even when called behind an admitted request (2.308805ms) +✔ configured Console process injects chat through its existing human transport (901.59636ms) +✔ chat assets, GET purity, method and unknown-route refusals are explicit (206.409025ms) +✔ the chat seam names the transport it is given: the human one and the tester one both pass, anything else is a TypeError (1.199765ms) +✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5146.976274ms) +✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (5150.763373ms) +✔ conversation view: a newer session with no readable history keeps the marker (2848.192209ms) +✔ conversation view: seats without history say so and offer no reply (3518.976325ms) +✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4400.65459ms) +✔ the editor: the list, hostile markdown that runs nothing, the draft, and each save receipt (9331.57255ms) +✔ the Console's CSP stands and the editor runs under it; hostile markdown on the load path runs and loads nothing (6067.385422ms) +✔ hostile content pasted into the editor runs and loads nothing, and a bad link target is refused in the link bar (4845.559933ms) +✔ a pasted image span is kept only for a target the link gate accepts, and a save writes only those (3491.829799ms) +✔ a switch of view reads nothing; a reload, and a trip away and back, each read the file exactly once (5866.549827ms) +✔ an edit rewrites only the block touched; a save sends the rest byte for byte; the Source view carries the draft (3788.939927ms) +✔ a file the editor cannot write back byte for byte opens as Source, says so, cannot save, and the Visual button works once it can (4234.458458ms) +✔ a link title pasted with line breaks in it is written on one line, so no HTML block comes of it (2605.728378ms) +✔ an image alt pasted with line breaks in it is written on one line, so no HTML block comes of it (1999.78511ms) +✔ an image title pasted with line breaks in it is written on one line, so no HTML block comes of it (2077.446601ms) +✔ a code block info string pasted with line breaks in it is written on one line, so no HTML block comes of it (3718.637005ms) +✔ a link title holding a quote after a backslash then a tag is written with the backslash escaped, so the title stays closed (1909.173557ms) +✔ an image title holding a quote after a backslash then a tag is written with the backslash escaped, so the title stays closed (1433.734236ms) +✔ a link title holding a trailing backslash is written with the backslash escaped, so the title stays closed (1056.073164ms) +✔ an image title holding a trailing backslash is written with the backslash escaped, so the title stays closed (1029.588894ms) +✔ a code block info string with a backtick, pasted, is dropped on Save, so the fence stays closed (987.373566ms) +✔ a code block info string with a backtick, as a tilde fence can have, is kept as source, not rewritten without it (691.874734ms) +✔ the imported source is the tagged upstream source plus the recorded local changes, and nothing else (161.636186ms) +✔ every package names its origin and license, and NOTICE lists each (1.293126ms) +✔ the build reads only imported source and the editor's own files; the bundle is the recorded build (13.549655ms) +✔ MOSAIC_REQUIRE_EDITOR_BUILD: a missing or wrong esbuild skips without it and fails with it; a wrong binary always fails (7.776306ms) +✔ the toolchain check, run as a program, exits non-zero without esbuild when the build is required (350.656359ms) +▲ [WARNING] Ignoring this import because "third_party/editor/prosemirror-transform/src/mark.ts" was marked as having no side effects [ignored-bare-import] + + third_party/editor/prosemirror-transform/src/index.ts:10:7: + 10 │ import "./mark" + ╵ ~~~~~~~~ + + "sideEffects" is false in the enclosing "package.json" file: + + third_party/editor/prosemirror-transform/package.json:13:2: + 13 │ "sideEffects": false, + ╵ ~~~~~~~~~~~~~ + +✔ rebuilding from the source gives the same bytes (needs the pinned esbuild; MOSAIC_REQUIRE_EDITOR_BUILD=1 makes its absence a failure) (555.743104ms) +✔ the editor routes serve the built files with their types and no cache (100.21958ms) +✔ open and save with no edit writes the same bytes: the goals review and a doc with every element (36.982058ms) +✔ open and save with no edit writes the same bytes: every repository doc under docs/ and spec/ (8452.885873ms) +✔ the editor source builds markup only with DOM calls, and the bundle has no eval (4.009583ms) +✔ the link gate and the image rule are the preview's (2.055746ms) +✔ R6 G1 a granted user signs in, gets the user page and reaches the broker as itself (330.267727ms) +▶ R6 G2 a subject with no live grant in this business is refused subject-unknown + ✔ a listed user with no grant (903.4736ms) + ✔ a grant in another business (237.185135ms) + ✔ an expired grant (890.423789ms) + ✔ another user's grant (453.888332ms) + ✔ a sub in no file (449.315611ms) + ✔ a file the loader refuses (388.859391ms) +✔ R6 G2 a subject with no live grant in this business is refused subject-unknown (3334.806179ms) +▶ R6 G3 a revoke counts on the next request: 401 and the session is gone + ✔ grant removed (268.650779ms) + ✔ subject removed (267.295293ms) + ✔ grant expired (665.836412ms) + ✔ file now refused (500.82568ms) +✔ R6 G3 a revoke counts on the next request: 401 and the session is gone (1704.325802ms) +✔ R6 G4 every owner route answers 403 owner-only before any bus call (340.702221ms) +✖ R6 G5 the bus refuses every owner verb over the user transport on its own (485.568404ms) +✔ R6 G9 a user chat sends only the plugins read over the owner transport (612.749445ms) +✔ R6 G6 the user page holds the notice word for word (324.783385ms) +✔ R6 G7 the hub label and plain peer links render from config on both pages (4356.409923ms) +✔ R6 G8 userCall spawns user-cli directly with the user nonce, no human nonce, and the user request (269.315168ms) +✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55038.290223ms) +✔ row 165 Settings: role instances read, add with a restart named, change without one, a refusal keeps the draft (8162.45209ms) +✔ row 165 Console: the signed-in human adds an instance through a real broker; validate exits 0, the log names the human and no restart is needed (row 194) (1333.106508ms) +✔ row 165 Console: the instance write needs send on, the public name, the user, the exact Origin and same-origin Fetch Metadata (19.101403ms) +✔ row 165 Console: the instance write takes JSON within 16 KiB and only its own keys; holder, credentials and an actor are refused (36.91494ms) +✔ row 165 Console: a bus refusal says nothing changed; a lost or odd answer says the change may have been made (34.62602ms) +✔ row 165 Console: the instances read takes no arguments, needs a bus and refuses an answer without a revision (24.175094ms) +✔ C2-04 CLI: both listener callback pairs dispatch library verbs with their own transport (518.811424ms) +✔ row 254: each library sync code maps to its status (1.180458ms) +✔ REQ-LIMIT-3 REQ-LIMIT-4 Settings shows token limits counted, not enforced and check failed; Reset posts one path; a save keeps the token limits (6298.863107ms) +✔ Row 167 with an assistant draft open, a token limit reset gives the assistant submit its revision (4276.44007ms) +✔ REQ-LIMIT-3 limits read returns only the token rows; no query or write is accepted (145.506945ms) +✔ REQ-LIMIT-3 a limits answer without a token list is refused, not shown as no limits (49.973437ms) +2026-10-23T05:33:56.191Z settings user="jason" changed +✔ REQ-LIMIT-3 a Settings reset reaches the human write with its paths; a reset alone is enough (17.215891ms) +✔ REQ-LIMIT-3 the Console reader asks the bus limits verb with no arguments (0.883912ms) +✔ the preview source names none of the four string-to-markup sinks (5.976009ms) +✔ headings, paragraphs, emphasis and inline code render (2.996825ms) +✔ lists, block quotes and code blocks render (1.216917ms) +✔ a link is made for http, https and relative targets, with rel and the target shown (5.548947ms) +✔ a link with any other scheme, or an evasion of the scheme, stays source text (2.805299ms) +✔ an image shows alt text and URL, is never an img element and never loads (0.339927ms) +✔ raw HTML, entities and unsupported blocks show as source text, never as elements (3.919101ms) +✔ markup inside code and inside link text stays text (0.450973ms) +✔ hostile and pathological input stays within its look-ahead budget and under the nesting cap (128.726332ms) +✔ trailing blanks and closing hashes are cut by a walk from the end, not a regex that retries inside a blank run (84.266204ms) +✔ a link opener with no closer searches at most SPAN characters ahead (559.485811ms) +✔ the look-ahead work of one render is a fixed multiple of the text, whatever the opener (2605.970638ms) +✔ past the budget an opener is source text, through the path all other text takes (80.262453ms) +✔ a normal 200 KiB document renders exactly as it did without a budget, and spends little of it (66.666568ms) +✔ a line separator inside a heading, a list item or a fence line does not end the marker line (0.96462ms) +✔ a thematic-break line is a paragraph, every other marker line opens a list (5.448705ms) +✔ every short line over space, tab, - * _ and a letter is a break exactly when the old regex said so (204.427802ms) +✔ one line of marks does not throw, at 4 MiB, whatever the mark or the spacing (22559.426214ms) +✔ CRLF and tabs are read like LF and spaces; empty input renders nothing (0.262399ms) +✔ messages HTTP read pages and preserves metadata; bad arguments refuse before a read; other reads unchanged (504.206798ms) +✔ Console Messages lists late unsolicited messages on polling with sender class time read state and trail; nav count includes beyond the page; reads never deliver (5038.67421ms) +✔ Messages nav count polls on Tasks and Board and clears on a Board refusal; messages 403 refuses local pages (5981.767963ms) +✔ copy writes the Console paths at the full id, read-only, and leaves an existing copy alone (9096.497016ms) +✔ copy refuses a commit off refactor, an unknown one, a worktree and a bad copies directory (7347.744628ms) +✔ copy writes nothing when an archived path is missing at the commit (1868.63621ms) +✔ a copy that loses the rename race leaves no read-only temp directory behind (3311.544636ms) +✔ a copy whose rename fails with no target refuses, writes no copy and leaves no temp directory (2312.433164ms) +✔ a copy that meets a target holding files says exists: and moves nothing into it (2364.990749ms) +✔ a copy that meets an empty target directory replaces it by rename, not by moving into it (4067.081185ms) +✔ serve runs from a copy: /healthz 200, the config check, roles and RELEASE from the copy, the queue from --checkout (2851.087141ms) +✔ serve refuses a --checkout that is not an absolute directory path (670.341187ms) +✔ consoleReads runs the queue read under queueRoot and reads RELEASE under root (90.16485ms) +✔ preview options are all three or none, and each is checked (13.591058ms) +✔ the CLI refuses a partial or invalid preview flag set at start (2803.4313ms) +✔ the CLI with all three flags serves the preview mode (415.465059ms) +✔ a request for the public name needs the header equal to the user id, else 401 and nothing read (110.760312ms) +✔ the header name matches without regard to case, and the value compare is exact (10.083712ms) +✔ the public name with the header reads the bus views, and the Host and Origin are checked first (112.090681ms) +✔ in preview mode every write answers 403 read-only, from the public name and loopback, and reaches nothing (44.457086ms) +✔ loopback requests are unchanged in preview mode, and the bus views work with the board down (10.641941ms) +✔ a proxy that rewrites Host to the loopback port gets 403 on its first request, whatever the user (35.919573ms) +✔ a repeated Host is 400 before any other check, in preview mode and without it (74.970474ms) +✔ without the preview flags the public name is refused and writes still reach the board (23.308441ms) +✔ processing browser: keyboard Stop, confirmed Resume, unknown outcome and persistent banner (3245.719335ms) +✔ processing browser: Q175 read-only interim keeps both writes disabled and names CLI recovery at 320px (3800.114751ms) +✔ processing browser: sign-out from config redirect blocks later refresh and writes (2745.133274ms) +✔ processing browser: sign-out from state redirect blocks later refresh and writes (3220.693284ms) +✔ processing browser: sign-out from write redirect blocks later refresh and writes (2763.829672ms) +✔ processing browser: sign-out from config sign-in required blocks later refresh and writes (2580.25615ms) +✔ processing browser: sign-out from state sign-in required blocks later refresh and writes (2291.250621ms) +✔ processing browser: sign-out from write sign-in required blocks later refresh and writes (2322.675396ms) +ROW191_LAYOUT {"width":400,"height":1000,"controls":97,"tree":324.0625,"main":1450.703125,"top":181.5,"bottom":217.5,"barBottom":129,"projectsTop":352.59375,"order":"0","visible":true} +ROW191_LAYOUT {"width":1440,"height":1000,"controls":127,"tree":914,"main":914,"top":135.5,"bottom":171.5,"barBottom":53,"projectsTop":636.59375,"order":"0","visible":true} +ROW191_LAYOUT {"width":1440,"height":420,"controls":127,"tree":334,"main":907.734375,"top":135.5,"bottom":171.5,"barBottom":53,"projectsTop":636.59375,"order":"0","visible":true} +ROW191_LAYOUT {"width":800,"height":420,"controls":127,"tree":334,"main":1131.65625,"top":135.5,"bottom":171.5,"barBottom":53,"projectsTop":618.59375,"order":"0","visible":true} +✔ processing browser: static nav controls keep Stop visible, ordered and keyboard reachable (1993.447545ms) +✔ interim read-only controls show CLI recovery, disable both writes and retain a halted banner across views (11.88827ms) +✔ Stop sends one empty halt and Resume confirms before sending the displayed generation (6.530287ms) +✔ a cancelled resume never calls the writer (2.767886ms) +✔ unknown writes are not retried or displayed as success and partial stops remain explicit (4.664918ms) +✔ the persistent shell loads the control outside route-specific content with accessible status and native buttons (0.871493ms) +✔ read-only Console can read processing state but neither human control writes without --send (56.737865ms) +✔ Console halt and generation-bound resume use only the management writer and keep partial stops honest (14.881178ms) +✔ processing write fences refuse cross-site, wrong origin, unnamed host, duplicate identity and bad payload before writer (17.485528ms) +✔ tester cannot invoke either control writer and failures never echo backend text (42.124054ms) +✔ processing writes use the existing SSO signer and still require --send and same-origin (39.331986ms) +✔ processing handler independently refuses wrong origin and cross-site before its writer (3.45994ms) +✔ a signed-in same-origin save reaches exactly one human operation and returns the new commit (41.25163ms) +✔ a save needs send, the named Host, the user, the exact Origin and same-origin Fetch Metadata (13.607509ms) +✔ a save validates JSON, strict keys, text values and the 1 MiB body before the bus is asked (33.994683ms) +✔ a body that is JSON but not an object is refused 400, not thrown on (91.175327ms) +✔ the 1 MiB limit is this route only; Settings and messages keep 16 KiB (4.985896ms) +✔ a bus refusal is reported as not saved with its own status; stale-base tells the user to reload (66.286554ms) +✔ a save whose answer is lost or unusable is reported unknown and is never retried (8.306276ms) +✔ the file list and one file read take strict queries and arrive in the expected shape (13.677685ms) +✔ reads work without --send; an unusable bus answer is a bad response, a refusal keeps its status (11.604929ms) +✔ /api/config says edit only where a save can pass (3.953644ms) +✔ a repository-name project refusal is an HTTP conflict (2.625569ms) +✔ proof startup refuses partial flags non-preview and invalid fixture file metadata or bytes (5672.724613ms) +✔ serve argv carries only fixture proof path and header; startup explicitly reports proof on or off (945.898363ms) +✔ CLI applies proof on the main listener and rejects that header on its tester listener (1278.393646ms) +✔ proxy proof admits named reads and sends, reads its fixture once, and never exposes the value (92.673688ms) +✔ proxy proof refuses missing wrong short or repeated proof before auth and before any read (28.6526ms) +✔ proxy proof header is refused on other Hosts and on the tester listener (26.562191ms) +✔ proof options reject unsafe or colliding header names without disclosing file contents (6.908814ms) +✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (3.570818ms) +✔ credential state from the date alone, as the bus states it (0.347595ms) +✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2617.257384ms) +✔ queue: a refused read fails closed with the store's text, redacted (1351.235521ms) +✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (2758.552888ms) +✔ business: names, vars on the allowlist, authority, credential metadata only (42.302368ms) +✔ business: missing or invalid file refuses with the module's text, redacted (44.173504ms) +✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (78.521237ms) +✔ settings: release, board, loopback address, notifier binding name only (855.233449ms) +✔ task fields: the committed table with no reads configured, GET only; a bad table refuses with no path (301.327869ms) +✔ bus-status: a refused boot shows reason, file and last good load; a live host or an ok boot clears it (26.372421ms) +✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4998.546596ms) +✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (24159.990938ms) +organization: {"event":"reload-needs-restart","business":"acme"} +organization: {"event":"reload-needs-restart","business":"acme"} +✔ row 65 Settings: the routing table lists every instance, saves a harness, model, thinking and fallback to the file, and keeps the draft on each refusal (8132.127507ms) +organization: {"event":"reload-needs-restart","business":"acme"} +✔ row 65 Console: the signed-in human saves a route through a real broker; each refusal says nothing changed and leaves the file and the log (553.361055ms) +✔ row 65 Console: the routes write needs send on, a writer, the public name, the user, the exact Origin and same-origin Fetch Metadata (16.864535ms) +✔ row 65 Console: the routes write takes JSON within 16 KiB with a revision and routes only; an actor is refused and routes are never logged (22.570214ms) +✔ row 65 Console: a bus refusal maps to its status and says nothing changed; a lost or odd answer says the change may have been made (55.26176ms) +✔ row 65 Console: the routes read takes no arguments, needs a bus and refuses an odd answer (12.571836ms) +✔ the send box: held roles, the draft and caret across a refresh, each receipt, one POST per Send, and the trail link; the board Reply on a 4xx and a 5xx (7748.726069ms) +✔ a sent receipt lists its replies as the page refreshes, oldest first and capped, and marks none delivered or read (14185.480824ms) +✔ startServer takes send only as a function and only with the preview mode (10.695212ms) +✔ the CLI refuses --send without the preview flags, and without a configured bus (1879.80579ms) +✔ the CLI with --send and a configured bus sends through the human transport and logs one line (971.515355ms) +✔ without --send the route is read-only like every other write, and config says no send (39.763344ms) +✔ with --send every other write is still 403 read-only, and nothing reaches the board or bus (35.328273ms) +✔ config says send only on the public name with --send on (4.531841ms) +✔ a send without the signed-in user is 401 and is neither sent nor logged, loopback included (12.334012ms) +✔ each request check refuses before anything is sent, and logs the refusal without the body (53.220094ms) +✔ a send passes exactly to, body and in_reply_to, answers the id, and logs it (4.98961ms) +✔ a crafted role name cannot forge a log line (4.285004ms) +✔ a bus refusal is not sent, with its status; an outcome the bus could not confirm is unknown and never retried (23.523423ms) +✔ through the human transport the request is exactly the business, message.send and the keys (323.904475ms) +✔ against a real broker: the message is written once as a human send, and its refusals map (297.534568ms) +✔ loopback host and board origin fail closed (10.372634ms) +✔ real board fixture passes through WebUI; assets and isolated seen/reply work (227.895539ms) +✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (151.79035ms) +✔ unreachable board reports URL; CLI rejects unsupported options (2346.124164ms) +✔ tester service render carries the tester port alongside existing flags (927.685029ms) +✔ tester service refuses forwarded 7332 even with another main port (684.618276ms) +✔ tester service invalid ports refuse render and install with exit 4 and leave the unit alone (4181.817131ms) +✔ tester service counts the digits as written before it strips leading zeros (319.721177ms) +✔ tester service install and status show the tester port without changing send reporting (2075.300401ms) +✔ render prints the unit without the preview flags (168.909905ms) +✔ render carries every serve flag, the preview set included (180.372548ms) +✔ a partial preview set is refused (423.124536ms) +✔ a value the unit cannot carry is refused, for every flag (8912.939939ms) +✔ the rendered unit names none of the human proof agent markers and sets no agent variable (430.705298ms) +✔ install into --dir with --no-reload writes the file whole, then reports unchanged (856.949864ms) +✔ install without --no-reload runs daemon-reload and nothing that starts the unit (136.133629ms) +✔ uninstall refuses while the unit is running, with or without --no-reload, and removes it once it is not (1608.019156ms) +✔ status prints the unit state and GET /healthz on the installed port (664.89403ms) +✔ usage: an unknown verb or flag, and start or enable, exit 4 (1125.418813ms) +✔ without --send, render is the template with its placeholders filled, byte for byte (453.815489ms) +✔ render --send with the preview set ends ExecStart with --send and changes nothing else (615.369315ms) +✔ --send without the whole preview set is refused and writes nothing (1116.0282ms) +✔ install --send into --dir with --no-reload writes the sending unit, and status says whether it sends (481.528636ms) +✔ uninstall and status take no --send (149.39612ms) +✔ install refuses while the unit is running, in any state but inactive or failed, and changes nothing (658.273346ms) +✔ when the state read fails, install and uninstall refuse with one line and change nothing (215.709624ms) +✔ install needs --copy naming a copy that exists, and refuses outside the canonical checkout without --allow-worktree (331.559972ms) +✔ install refuses unless the bus unit runs from the same copy, naming both paths, and changes nothing (680.106866ms) +✔ render --copy runs the copy and reads the queue in the checkout; without --copy the unit is unchanged (115.093975ms) +✔ the uid header takes only an Authentik uid; the username header still takes a username (1862.395215ms) +✔ a preview value cannot start with - or . (208.286301ms) +✔ status names the copy and whether refactor has moved past it, and says when the unit runs from no copy (584.456927ms) +✔ proxy proof render carries both flags but never opens the fixture file (46.254649ms) +✔ proxy proof render refuses incomplete non-preview missing unsafe-mode and repo/copy paths (401.932358ms) +✔ proxy proof status retains send and tester reporting with both proof flags (147.108399ms) +✔ row196 socket flag renders only for supporting targets and refuses mixed transports (96.901062ms) +✔ Settings form shows every scope and held role; gated save, exact payload, retained draft and no automatic retry (4689.944901ms) +✔ Q169 Assistant section: gated, changed fields only, name and stop posts, retained draft and no automatic retry (4298.630309ms) +✔ Row 167 a refused names read gives the refused usage page; another names failure keeps the Assistant section (2381.347611ms) +✔ Row 167 with a limits draft open, an assistant save gives the limits submit its revision and the typed limit stays (3422.5705ms) +✔ Row 167 with an assistant draft open, a launch save gives the assistant submit its revision, and reload reads the current one (3167.495309ms) +2026-10-23T05:34:12.156Z settings user="jason" changed +✔ REQ-WEB-1 signed-in same-origin Settings write reaches exactly one human operation (50.41002ms) +✔ REQ-WEB-1 Settings writes require enabled send, named Host, identity, exact Origin and same-origin Fetch Metadata (40.80252ms) +✔ REQ-WEB-1 Settings validates JSON, size, revision and strict writable keys before broker call (38.254838ms) +✔ Q169 a Console change of the assistant reaches the human operation unchanged, and the log names the signed-in user (15.531946ms) +✔ tokens.css and icons.svg are byte copies of docs/design (3.198341ms) +✔ JetBrains Mono files match the digests in their sources file, with the OFL text (15.351971ms) +✔ shipped strings are unchanged, and the web keeps its write boundary (26.807319ms) +ROW191_LAYOUT {"width":1440,"height":1000,"controls":212.5,"tree":914,"main":914} +✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (11440.609596ms) +✔ five states on Board, Inbox, Tasks, Agents and Trail (6836.575873ms) +✔ a 401 sign-in required on each fetch path signs the page out with a sign-in link and stops the refresh; another 401 stays an error (10800.915863ms) +✔ the Sign out form shows only when /api/config says sso, and posts to /auth/logout (4684.860781ms) +✔ positive: an RS256 provider signs in and sends one message, and the session reads (252.249989ms) +✔ positive: an ES256 provider signs in and sends (66.12706ms) +✔ positive: two providers each sign in their own subjects, and one provider's subject is unknown to the other (277.673504ms) +✔ 1: a forged x-authentik header without a cookie is 401 on the API and a sign-in redirect on a page (96.809454ms) +✔ 2: an HS256 ID token keyed with the client secret and a real RSA kid is refused (127.831239ms) +✔ 3: a wrong aud, and an aud list holding another client with no azp, are refused (308.312945ms) +✔ 4: a token that expired 10 minutes ago is refused (126.926096ms) +✔ 5: another issuer, and the issuer without its trailing slash, are refused (187.553334ms) +✔ 6: a nonce mismatch and a missing nonce are refused (176.286164ms) +✔ 7: state mismatch, no login cookie, a replay, an expired record and two login cookies are refused (117.997966ms) +✔ 8: the token request carries the PKCE verifier, and the OP refuses a wrong one (259.026782ms) +✔ 9: alg none, a changed payload under a real kid, and an unknown kid even after the refetch are refused (324.624454ms) +✔ 10: a valid token for a subject not configured is 403 with one subject-unknown line (292.839143ms) +✔ 11: start refusals: HS or none algs, reserved names, HS-only discovery, an empty JWKS, no S256, an issuer mismatch, a short RSA key (818.587961ms) +✔ 11: slot files: a symlink, mode 0644, mode 0640 and a file under the repository are refused, and no message holds the value (4.916667ms) +✔ 11: the CLI refuses --sso with the old flags and without console.sso, and starts with a good one (3619.965965ms) +✔ 12: under --sso the tester listener refuses each kind of sign-in, has no /auth, and startServer refuses tester with sso (123.335369ms) +✔ 13: idle and absolute expiry, logout then reuse, a cross-origin logout, and background reads that don't keep a session (184.084176ms) +✔ 13: the CSP lets the Sign out form reach the provider's end-session origin, and only under --sso (123.933217ms) +✔ 14: open redirects land on /, and a path on the Console lands there (694.674531ms) +✔ 15: a callback for A carrying B's code and iss, and a provider= query, is refused (231.062943ms) +✔ 16: a provider with RS256 and ES256 takes either, and refuses PS256 with a usable PS256 key in the JWKS (401.501434ms) +✔ 17: 33 started logins leave 32 records, and the first one's callback finds none (180.860007ms) +✔ 18: after a key rotation a sign-in works through one refetch, and the removed kid is refused (237.107167ms) +✔ 20 and 21: the store holds no raw session id, and the cookie strings are exact (339.228051ms) +✔ 22: a loopback Host is 403 on a page, a read and a write; /healthz on loopback is 200; a redirect_uri never comes from the Host (185.956183ms) +✔ 23: a provider down at start leaves the Console up and 503, and the retry makes sign-in work (793.068219ms) +✔ 19: no captured log line holds a token, code, state, nonce, verifier, client secret or session id (12.154889ms) +✔ tester Console banner exposes only instance route and name editors and saves a route as tester (4052.176134ms) +✔ row 177: the tester Console chat composer is enabled and a send shows in its own thread (2058.798981ms) +✔ tester-port validates range tunnel main port and requires configured bus before listening (3636.182442ms) +✔ CLI tester listener binds a private Unix socket and spawns TESTER_CLI for every Console bus read (11007.753465ms) +✔ main listener refuses x-mosaic-tester before any board or bus read for every Host (90.164888ms) +✔ tester listener rejects preview binding and requires its fixed logical Host and no proxy auth header (391.197032ms) +✔ tester listener writes only instances routes and names with tester receipts and actor (478.315366ms) +✔ tester listener refuses every human-only write with no bus call (437.60685ms) +✔ tester-off and tester-required reads return HTTP 403 (330.027803ms) +✔ tester credentials routes and committed project reads keep secrets and paths bounded (885.561947ms) +✔ row 177 W1: the tester chat makes, sends to, opens and reads its own thread as tester (136.597127ms) +✔ row 177 W2: tester chat writes refuse a missing or foreign Origin, cross-site, text/plain and the proxy header with no bus call (59.629769ms) +✔ row 177 W3: the human thread through the tester chat is thread-not-owned, and bus.tester off answers tester-off (273.894801ms) +✔ row 177 W4: the main listener refuses x-mosaic-tester on chat reads and writes before the chat (5.513509ms) +✔ private tester HTTP serves on 0600 socket in 0700 parent and fixed origin (150.811311ms) +✔ private tester refuses symlink, unsafe parent and long path without repairs (2.151814ms) +✔ tester cleanup must leave a replacement inode untouched (3.172069ms) +✔ row278 browser lists held claims and release preserves role rows and totals (3219.347343ms) +✔ row278 browser claims 403 stays local and role section remains byte-equal (2905.608034ms) +✔ row278 browser renders only four escaped claim fields and never instance or other extras (1443.497625ms) +✔ row278 browser sign-out removes claims and prevents reuse of cached answers (1144.811802ms) +✔ row278 tester browser keeps role rows without requesting the owner claims section (955.86179ms) +✔ row278 browser discards a late claims answer after sign-out (1083.591626ms) +✔ row278 HTTP read returns held claims without send and refuses arguments before the bus (117.459434ms) +✔ row278 HTTP read preserves bus refusal and rejects a malformed result (87.806387ms) +✔ row278 tester socket refuses thread claims explicitly with zero bus calls (36.301855ms) +✔ row278 Console adapter calls argument-free thread.claims through the normal transport (631.785506ms) +✔ REQ-LIMIT-4 Settings shows all token kinds and unknown turns beside every total, as a read-only separate section (3398.492293ms) +✔ REQ-WEB-1 usage read keeps unknown counts and each token kind; no query or write is accepted (82.33328ms) +✔ REQ-WEB-1 malformed usage is refused instead of shown as zero (51.183905ms) +✔ Queue, Business and Settings in their five states, with no move button (6719.50364ms) +✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2114.005252ms) +✔ the server log names no config path when the system config refuses (426.110951ms) +ℹ tests 365 +ℹ suites 0 +ℹ pass 364 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 58189.687162 + +✖ failing tests: + +test at tests/granted-user.test.mjs:250:1 +✖ R6 G5 the bus refuses every owner verb over the user transport on its own (485.568404ms) + AssertionError [ERR_ASSERTION]: one event per refused verb + + 0 !== 22 + + at TestContext. (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:269:10) + at process.processTicksAndRejections (node:internal/process/task_queues:104:5) + at async Test.run (node:internal/test_runner/test:1409:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: 0, + expected: 22, + operator: 'strictEqual', + diff: 'simple' + } diff --git a/packages/webui/tests/granted-user.test.mjs b/packages/webui/tests/granted-user.test.mjs index 4edf30412..7c1a902af 100644 --- a/packages/webui/tests/granted-user.test.mjs +++ b/packages/webui/tests/granted-user.test.mjs @@ -250,7 +250,7 @@ test('R6 G4 every owner route answers 403 owner-only before any bus call', async test('R6 G5 the bus refuses every owner verb over the user transport on its own', async t => { const env = await setup(t); const o = consoleBus(env.userTransport('alice'), { send: true, owner: env.ownerCall }); - const before = env.store.get('SELECT max(seq) n FROM events').n, rest = tables(env); + const before = env.store.get('SELECT coalesce(max(seq), 0) n FROM events').n, rest = tables(env); const refusals = [ ['inbox', () => o.bus.inbox()], ['tasks', () => o.bus.tasks()], ['agents', () => o.bus.agents()], ['trail', () => o.bus.trail()], ['decisions', () => o.bus.decisions()], ['instances', () => o.bus.instances()], ['routes', () => o.bus.routes()], ['thread.claims', () => o.bus['thread.claims']()],