diff --git a/agents/darkwing/work/queue-77-review/r3/mine.py b/agents/darkwing/work/queue-77-review/r3/mine.py new file mode 100644 index 000000000..1170ee419 --- /dev/null +++ b/agents/darkwing/work/queue-77-review/r3/mine.py @@ -0,0 +1,47 @@ +# Row 77 round 3 review (Darkwing): sites Rocko's r2/mutants.py doesn't name. +# Same loop as theirs: each edit alone on , the service and +# pinned-copy tests run, the file restored. Usage: python3 -I mine.py +import subprocess, shutil, sys +W=sys.argv[1] +S='scripts/webui-service.sh' +ORDER_A=''' bus_on_copy "$COPIES/$COPY_ID" + # N1: a running unit would take the new ExecStart at its next restart. + local state + state="$(running)" || exit 1 + [ -z "$state" ] || die "refusing: $UNIT_NAME is running ($state); Jason or Mos stops it first (systemctl --user stop mosaic-webui), then install, then start it" +''' +ORDER_B=''' # N1: a running unit would take the new ExecStart at its next restart. + local state + state="$(running)" || exit 1 + [ -z "$state" ] || die "refusing: $UNIT_NAME is running ($state); Jason or Mos stops it first (systemctl --user stop mosaic-webui), then install, then start it" + bus_on_copy "$COPIES/$COPY_ID" +''' +M=[ + ('bus checked after the state read', S, ORDER_A, ORDER_B), + ('any copy will do for the bus', S, ' bus_on_copy "$COPIES/$COPY_ID"\n', ' bus_on_copy "$COPIES"\n'), + ('bus check before render', S, ' unit="$(render)" || exit 1\n bus_on_copy "$COPIES/$COPY_ID"\n', ' bus_on_copy "$COPIES/$COPY_ID"\n unit="$(render)" || exit 1\n'), + ('only the first ExecStart path checked', S, "sed 's/^path=//' || true)\"", "sed 's/^path=//' | head -1 || true)\""), + ('a non-bus unit name accepted', S, "awk '$1 ~ /^mosaic-bus@.+\\.service$/ {print $1}'", "awk '{print $1}'"), + ('uninstall takes --copy', S, '[ "$SERVE_SET" = 0 ] && [ "$COPY_FLAGS" = 0 ] && [ "$ALLOW_WORKTREE" = 0 ] || usage\n local target="$UNIT_DIR/$UNIT_NAME"\n # N3', '[ "$SERVE_SET" = 0 ] || usage\n local target="$UNIT_DIR/$UNIT_NAME"\n # N3'), + ('status takes --copy', S, '[ "$SERVE_SET" = 0 ] && [ "$RELOAD" = 1 ] && [ "$COPY_FLAGS" = 0 ] && [ "$ALLOW_WORKTREE" = 0 ] || usage', '[ "$SERVE_SET" = 0 ] && [ "$RELOAD" = 1 ] || usage'), + ('render takes --copies alone', S, '{ [ -n "$COPY_ID" ] || [ "$COPY_FLAGS" = 0 ]; } || usage', 'true || usage'), + ('render takes --allow-worktree', S, '[ "$DIR_FLAGS" = 0 ] && [ "$ALLOW_WORKTREE" = 0 ] || usage\n { [', '[ "$DIR_FLAGS" = 0 ] || usage\n { ['), + ('copies dir may be relative', S, 'case "$COPIES" in /*) ;; *) die', 'case "$COPIES" in *) ;; /*) die'), + ('short copy id accepted', S, '[ "${#COPY_ID}" -eq 40 ] ||', 'true ||'), + ('copy takes --dir', S, '[ "$SERVE_SET" = 0 ] && [ "$DIR_FLAGS" = 0 ] && [ -z "$COPY_ID" ] || usage\n canonical', '[ "$SERVE_SET" = 0 ] && [ -z "$COPY_ID" ] || usage\n canonical'), + ('copy without mv -T fallback check', S, ' [ -e "$target" ] || die "could not rename the copy into $target"\n', ''), + ('dirs 0755', S, '-type d -exec chmod 0555 {} +', '-type d -exec chmod 0755 {} +'), + ('status copy not anchored', S, 'if [[ "$copy" =~ ^[0-9a-f]{40}$ ]]; then', 'if [[ "$copy" =~ [0-9a-f]{40} ]]; then'), +] +res=[] +for name,f,a,b in M: + p=f'{W}/{f}'; s=open(p).read() + if s.count(a)!=1: res.append(f'NOMATCH({s.count(a)}) {name}'); continue + shutil.copy(p,p+'.bak'); open(p,'w').write(s.replace(a,b)) + try: + r=subprocess.run(['node','--test','tests/service.test.mjs','tests/pinned-copy.test.mjs'],cwd=f'{W}/packages/webui',capture_output=True,text=True,timeout=300,start_new_session=True) + fails=sorted({l[:110] for l in r.stdout.splitlines() if l.startswith('✖') and 'failing tests' not in l}) + res.append(f"{'KILLED' if r.returncode else 'SURVIVED'} {name}: {len(fails)} failing{' | '+fails[0] if fails else ''}") + print(res[-1], flush=True) + finally: + shutil.move(p+'.bak',p) diff --git a/agents/darkwing/work/queue-77-review/r3/mine.txt b/agents/darkwing/work/queue-77-review/r3/mine.txt new file mode 100644 index 000000000..240581bfe --- /dev/null +++ b/agents/darkwing/work/queue-77-review/r3/mine.txt @@ -0,0 +1,15 @@ +KILLED bus checked after the state read: 2 failing | ✖ install refuses unless the bus unit runs from the same copy, naming both paths, and changes nothing (23.7942 +KILLED any copy will do for the bus: 1 failing | ✖ install refuses unless the bus unit runs from the same copy, naming both paths, and changes nothing (18.3771 +KILLED bus check before render: 1 failing | ✖ install needs --copy naming a copy that exists, and refuses outside the canonical checkout without --allow-w +SURVIVED only the first ExecStart path checked: 0 failing +SURVIVED a non-bus unit name accepted: 0 failing +KILLED uninstall takes --copy: 1 failing | ✖ usage: an unknown verb or flag, and start or enable, exit 4 (71.839775ms) +KILLED status takes --copy: 1 failing | ✖ usage: an unknown verb or flag, and start or enable, exit 4 (230.450977ms) +KILLED render takes --copies alone: 1 failing | ✖ usage: an unknown verb or flag, and start or enable, exit 4 (47.370502ms) +KILLED render takes --allow-worktree: 1 failing | ✖ usage: an unknown verb or flag, and start or enable, exit 4 (48.089967ms) +KILLED copies dir may be relative: 2 failing | ✖ copy refuses a commit off refactor, an unknown one, a worktree and a bad copies directory (303.666576ms) +KILLED short copy id accepted: 1 failing | ✖ install needs --copy naming a copy that exists, and refuses outside the canonical checkout without --allow-w +KILLED copy takes --dir: 1 failing | ✖ copy refuses a commit off refactor, an unknown one, a worktree and a bad copies directory (341.554495ms) +SURVIVED copy without mv -T fallback check: 0 failing +KILLED dirs 0755: 1 failing | ✖ copy writes the Console paths at the full id, read-only, and leaves an existing copy alone (230.947689ms) +SURVIVED status copy not anchored: 0 failing diff --git a/agents/darkwing/work/queue-77-review/r3/rocko-mutants.txt b/agents/darkwing/work/queue-77-review/r3/rocko-mutants.txt new file mode 100644 index 000000000..53b6e7a0f --- /dev/null +++ b/agents/darkwing/work/queue-77-review/r3/rocko-mutants.txt @@ -0,0 +1,34 @@ +KILLED failed counts as running: 2 failing +KILLED reloading not running: 2 failing +KILLED list-units failure ignored: 2 failing +KILLED install skips state read: 3 failing +KILLED uninstall skips state read with --no-reload: 1 failing +KILLED uid header case-sensitive: 1 failing +KILLED uid allows upper hex: 1 failing +KILLED allowed user may start with -: 2 failing +KILLED no --checkout in unit: 4 failing +KILLED canonical always passes: 2 failing +KILLED install without --copy: 1 failing +KILLED missing copy accepted: 2 failing +KILLED status drift wrong: 1 failing +KILLED copy misses seat: 1 failing +KILLED copy misses README: 1 failing +KILLED commit may start with a dot: 1 failing +KILLED copy misses tasks src: 1 failing +KILLED copy misses fields table: 2 failing +KILLED copy stays writable: 1 failing +KILLED copy dirs stay writable: 1 failing +KILLED copy subdirs stay writable: 1 failing +KILLED trap leaves read-only temp: 1 failing +KILLED copy off-branch accepted: 5 failing +KILLED queue reads root: 2 failing +KILLED --checkout relative ok: 1 failing +KILLED install skips bus check: 2 failing +KILLED bus path prefix only: 1 failing +KILLED bus not-found accepted: 1 failing +KILLED bus show failure ignored: 1 failing +KILLED bus list failure ignored: 1 failing +KILLED no loaded bus accepted: 1 failing +KILLED no ExecStart path accepted: 1 failing +KILLED only first bus unit checked: 1 failing +KILLED --business ignored for bus: 1 failing diff --git a/agents/darkwing/work/queue-77-review/r3/webui.txt b/agents/darkwing/work/queue-77-review/r3/webui.txt new file mode 100644 index 000000000..4c2222ede --- /dev/null +++ b/agents/darkwing/work/queue-77-review/r3/webui.txt @@ -0,0 +1,110 @@ +✔ 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 (5246.340359ms) +✔ 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 (8043.177372ms) +✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3726.689933ms) +Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286} +✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4706.991221ms) +✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (8382.707487ms) +✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (323.456971ms) +✔ decisions route serves the roles and recent lists from the reader view (1096.303702ms) +✔ credentials route serves the broker credential status from the reader view (317.931688ms) +✔ replies route serves the replies to one message from the reader view (884.713617ms) +✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (297.792716ms) +✔ bus refusals map to statuses and never carry a path or a stack (64.166767ms) +✔ humanCall speaks the human transport protocol without blocking the server (2210.495047ms) +✔ busReader takes --business, else the live bus host, else refuses (315.941209ms) +✔ humanCall kills a transport that runs past its timeout (530.449694ms) +✔ serve refuses a --business value that is not a business id (522.658339ms) +✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3557.989402ms) +✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3912.41627ms) +✔ conversation view: a newer session with no readable history keeps the marker (2933.838118ms) +✔ conversation view: seats without history say so and offer no reply (3034.330267ms) +✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3609.836993ms) +✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55315.797987ms) +✔ copy writes the Console paths at the full id, read-only, and leaves an existing copy alone (2655.519953ms) +✔ copy refuses a commit off refactor, an unknown one, a worktree and a bad copies directory (6055.554965ms) +✔ copy writes nothing when an archived path is missing at the commit (1170.349266ms) +✔ a copy that loses the rename race leaves no read-only temp directory behind (709.330276ms) +✔ serve runs from a copy: /healthz 200, the config check, roles and RELEASE from the copy, the queue from --checkout (878.260235ms) +✔ serve refuses a --checkout that is not an absolute directory path (314.710615ms) +✔ consoleReads runs the queue read under queueRoot and reads RELEASE under root (67.76214ms) +✔ preview options are all three or none, and each is checked (8.61513ms) +✔ the CLI refuses a partial or invalid preview flag set at start (3076.4302ms) +✔ the CLI with all three flags serves the preview mode (464.324715ms) +✔ a request for the public name needs the header equal to the user id, else 401 and nothing read (124.845938ms) +✔ the header name matches without regard to case, and the value compare is exact (13.265548ms) +✔ the public name with the header reads the bus views, and the Host and Origin are checked first (104.233254ms) +✔ in preview mode every write answers 403 read-only, from the public name and loopback, and reaches nothing (24.819042ms) +✔ loopback requests are unchanged in preview mode, and the bus views work with the board down (17.563724ms) +✔ a proxy that rewrites Host to the loopback port gets 403 on its first request, whatever the user (65.459904ms) +✔ a repeated Host is 400 before any other check, in preview mode and without it (17.881223ms) +✔ without the preview flags the public name is refused and writes still reach the board (9.180667ms) +✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.298093ms) +✔ credential state from the date alone, as the bus states it (0.269819ms) +✔ queue: rows, one row, ids and methods, notes; no path or id from a row (3151.761892ms) +✔ queue: a refused read fails closed with the store's text, redacted (1278.808265ms) +✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (3209.438282ms) +✔ business: names, vars on the allowlist, authority, credential metadata only (16.340087ms) +✔ business: missing or invalid file refuses with the module's text, redacted (28.449878ms) +✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (16.726096ms) +✔ settings: release, board, loopback address, notifier binding name only (1057.576043ms) +✔ task fields: the committed table with no reads configured, GET only; a bad table refuses with no path (36.254751ms) +✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3790.39541ms) +✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22689.487515ms) +✔ 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 (7507.670672ms) +✔ a sent receipt lists its replies as the page refreshes, oldest first and capped, and marks none delivered or read (14059.646761ms) +✔ startServer takes send only as a function and only with the preview mode (9.217262ms) +✔ the CLI refuses --send without the preview flags, and without a configured bus (2743.43295ms) +✔ the CLI with --send and a configured bus sends through the human transport and logs one line (408.168711ms) +✔ without --send the route is read-only like every other write, and config says no send (50.290066ms) +✔ with --send every other write is still 403 read-only, and nothing reaches the board or bus (41.235693ms) +✔ config says send only on the public name with --send on (8.593181ms) +✔ a send without the signed-in user is 401 and is neither sent nor logged, loopback included (9.968292ms) +✔ each request check refuses before anything is sent, and logs the refusal without the body (25.493371ms) +✔ a send passes exactly to, body and in_reply_to, answers the id, and logs it (10.614458ms) +✔ a crafted role name cannot forge a log line (13.398697ms) +✔ a bus refusal is not sent, with its status; an outcome the bus could not confirm is unknown and never retried (17.281702ms) +✔ through the human transport the request is exactly the business, message.send and the keys (343.311896ms) +✔ against a real broker: the message is written once as a human send, and its refusals map (899.335015ms) +✔ loopback host and board origin fail closed (14.312218ms) +✔ real board fixture passes through WebUI; assets and isolated seen/reply work (402.2579ms) +✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (117.091304ms) +✔ unreachable board reports URL; CLI rejects unsupported options (821.685449ms) +✔ render prints the unit without the preview flags (69.919496ms) +✔ render carries every serve flag, the preview set included (32.084132ms) +✔ a partial preview set is refused (89.608557ms) +✔ a value the unit cannot carry is refused, for every flag (1067.614185ms) +✔ the rendered unit names none of the human proof agent markers and sets no agent variable (44.477898ms) +✔ install into --dir with --no-reload writes the file whole, then reports unchanged (100.523327ms) +✔ install without --no-reload runs daemon-reload and nothing that starts the unit (50.633966ms) +✔ uninstall refuses while the unit is running, with or without --no-reload, and removes it once it is not (326.600559ms) +✔ status prints the unit state and GET /healthz on the installed port (321.076049ms) +✔ usage: an unknown verb or flag, and start or enable, exit 4 (210.880571ms) +✔ without --send, render is the template with its placeholders filled, byte for byte (122.784085ms) +✔ render --send with the preview set ends ExecStart with --send and changes nothing else (135.945051ms) +✔ --send without the whole preview set is refused and writes nothing (272.670945ms) +✔ install --send into --dir with --no-reload writes the sending unit, and status says whether it sends (398.199578ms) +✔ uninstall and status take no --send (54.721363ms) +✔ install refuses while the unit is running, in any state but inactive or failed, and changes nothing (668.108531ms) +✔ when the state read fails, install and uninstall refuse with one line and change nothing (102.963366ms) +✔ install needs --copy naming a copy that exists, and refuses outside the canonical checkout without --allow-worktree (85.768549ms) +✔ install refuses unless the bus unit runs from the same copy, naming both paths, and changes nothing (339.81128ms) +✔ render --copy runs the copy and reads the queue in the checkout; without --copy the unit is unchanged (23.223938ms) +✔ the uid header takes only an Authentik uid; the username header still takes a username (510.24577ms) +✔ a preview value cannot start with - or . (70.422379ms) +✔ status names the copy and whether refactor has moved past it, and says when the unit runs from no copy (336.814296ms) +✔ tokens.css and icons.svg are byte copies of docs/design (2.252492ms) +✔ JetBrains Mono files match the digests in their sources file, with the OFL text (4.405207ms) +✔ shipped strings are unchanged, and the web keeps its write boundary (3.797284ms) +✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (8464.346516ms) +✔ five states on Board, Inbox, Tasks, Agents and Trail (3227.665035ms) +✔ Queue, Business and Settings in their five states, with no move button (5265.458085ms) +✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (3568.77358ms) +✔ the server log names no config path when the system config refuses (373.609649ms) +ℹ tests 101 +ℹ suites 0 +ℹ pass 101 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 55978.270074 diff --git a/agents/darkwing/work/queue-77-review/review-r3.md b/agents/darkwing/work/queue-77-review/review-r3.md new file mode 100644 index 000000000..d2531dcef --- /dev/null +++ b/agents/darkwing/work/queue-77-review/review-r3.md @@ -0,0 +1,45 @@ +## Row 77 round 3 review (Darkwing): approve + +**Verdict: approve**, candidate manifest `d9880f4c92d1d976027562a03cc5dd07512954e86f49f1da0ebb34e642a955f5`. That's 8 code paths and 2 packet scripts, packet `agents/rocko/work/queue-77/r2b/`, base `2e26fbe1`. My round 1 findings F1 to F3 (27801) are fixed. Nothing blocks. The notes below can go on the follow-up list with Filbert's. + +### What I checked + +- **Tree.** I applied `r2b/build.patch` with `--index` to a detached worktree at `bd3dbaf9`. The manifest checks 10/10, and it still did after both mutant runs. `git diff --stat 2e26fbe1..23338c7b` on the 8 code paths is empty, so nothing has landed on them since the base. +- **The round 3 delta.** I pulled the `packages/webui/README.md` lines the r2 and r2b patches add and remove: 19 each, byte-identical (`cmp`). Round 3 moves only the README's context lines onto row 95, as Sage said. +- **Suite.** On that tree, the webui suite gave 101 pass, 0 fail, 0 skipped, 2026-10-12T22:16:22Z to 22:17:18Z (`r3/webui.txt`). I didn't rerun the other suites. Round 3 touches no path they read, and Filbert's round 2 gate covers them. +- **Read.** The whole `scripts/webui-service.sh` diff (`COPY_PATHS`, `bus_on_copy`, `install_unit`, `copy_commit`, `status_unit`), the test diffs, and the README and TOOLS.md hunks. + +### Against my round 1 + +- **F1, the bus refuses human proofs from a copy.** Fixed. `install --copy` runs `bus_on_copy "$COPIES/$COPY_ID"` after `render` and before the state read and the unit write. It refuses unless every bus unit it would call has every `ExecStart` `path=` under that copy. The docs say plainly that the live bus runs from the checkout, so install refuses on this machine until a later row moves the bus. I read the live unit's `ExecStart` form once (names only, nothing changed). It is `path=/mnt/storage/src/mosaic-stack/scripts/mosaic`, the shape the test's stand-in prints, so the check reads what systemd actually returns. +- **F2, row 61's tasks import is missing.** Fixed. `packages/tasks/src` and `packages/tasks/fields.json` are in `COPY_PATHS`. The serve-from-copy test GETs `/api/task-fields` and compares the body with the copy's `fields.json`. +- **F3, directories are writable.** Fixed. Directories are 0555 and files `a-w`. The tests show `sed -i` and a new file both refused with `EACCES`. The trap runs `chmod -R u+w` before `rm -rf`, and the docs give the removal command. +- My N1 to N3 are taken up as Filbert's round 2 lists them. + +### Mutants + +- **Rocko's `r2/mutants.py`**, all 34, 22:17:58Z to 22:19:47Z: **34 of 34 killed** (`r3/rocko-mutants.txt`). +- **My own**, `r3/mine.py`, 15 edits at sites Rocko's 34 don't name, each alone against `service.test.mjs` and `pinned-copy.test.mjs`, 22:21:09Z to 22:21:52Z (`r3/mine.txt`). 11 were killed: + - the bus check moved after the state read; + - the bus check moved before `render`; + - the bus accepted on any copy (`"$COPIES"` for `"$COPIES/$COPY_ID"`); + - `uninstall` and `status` taking `--copy`; + - `render` taking `--copies` alone or `--allow-worktree`; + - a relative copies directory; + - a short copy id; + - `copy` taking `--dir`; + - directories at 0755. + + 4 survived. Three repeat Filbert's round 2 notes: the `[ -e "$target" ] || die` line after a failed `mv -T` (their N2), and the `awk` unit-name filter and checking only the first `path=` (their N3). I agree with their reading of all three. The fourth is new, N1 below. + + Every file went back to its original afterwards, and no `.bak` was left behind. + +### Notes (non-blocking) + +N1. **`status` accepts an unanchored copy id.** If `^[0-9a-f]{40}$` loses its anchors, every test still passes. A `WorkingDirectory` whose last part only contains 40 hex characters would then print as `copy: ` and get compared with `refactor`. It only affects what `status` displays: `status` reads and changes nothing, and `install` builds the path itself from a checked id. One stand-in unit with a `WorkingDirectory` like `/<40 hex>x` pins it. + +N2. **The two-unit case checks only acceptance.** The test at `service.test.mjs:536` loads `mosaic-bus@other` on the copy, checks that install succeeds, and checks that `show mosaic-bus@other.service` was called. Nothing loads two units and puts the second one off the copy. That is the case F1 exists for when two businesses run. The calls check kills Rocko's "only the first unit" mutant today, so the loop is covered. An explicit refusal case would say what the calls check only implies. + +N3. **My process.** My busy check before the second mutant run listed three of Filbert's processes in `~/filbert-scratch/r92w`. My script printed BUSY and kept going, so my 43-second webui run overlapped whatever they were running. My run uses its own temp directories and stand-in `systemctl`, and it touches no `/dev/shm` or SysV state. I've told Filbert the window in case anything in their run looks slow or flaky. The busy check stops the run from now on. + +Approve for the candidate above.