dewey: row 94 round 1 review, changes (comment 28075)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
# Row 94 round 1 review mutants (Dewey), sites Rocko's 27 don't cover. Each swaps one exact
|
||||
# string in a scratch worktree of the candidate, runs the touched package's whole node suite
|
||||
# in its own process group (killed after 400 s, which counts as a kill), and restores the file.
|
||||
import subprocess, sys, os, signal
|
||||
M = os.path.expanduser('~/dewey-scratch/r94mut')
|
||||
env = dict(os.environ, TMPDIR=os.path.expanduser('~/dewey-scratch/tmp'))
|
||||
B = 'packages/bus/src/broker.mjs'; C = 'packages/cli/src/cli.mjs'; W = 'packages/webui/src/public/bus.js'
|
||||
muts = [
|
||||
('bus: read means delivered', B, "d.op='read') AS read FROM", "d.op IN ('delivered','read')) AS read FROM", 'bus'),
|
||||
('bus: unread excludes delivered', B, "d.op='read')),0) AS unread", "d.op IN ('delivered','read'))),0) AS unread", 'bus'),
|
||||
('cli: text inbox lists read messages', C, 'messages.push(...page.messages.filter(m => !m.read));', 'messages.push(...page.messages);', 'cli'),
|
||||
('cli: plain talk takes Ctrl-C', C, 'if (follow) process.on("SIGINT", stop);', 'process.on("SIGINT", stop);', 'cli'),
|
||||
('webui: count read only on Messages', W, "if (path !== '/messages') {", 'if (false) {', 'webui'),
|
||||
('webui: refusal keeps message count', W, 'inboxCount = null; messageCount = null; }', 'inboxCount = null; }', 'webui'),
|
||||
('webui: board ignores message count', W, "if (messages.status === 'fulfilled' && !refused) messageCount = messages.value.unread;", '', 'webui'),
|
||||
('webui: pre-read swallows 403', W, "if (err.status === 403 || err.code === 'signed-out') throw err;", "if (err.code === 'signed-out') throw err;", 'webui'),
|
||||
]
|
||||
only = sys.argv[1:]
|
||||
for name, f, old, new, pkg in muts:
|
||||
if only and not any(o in name for o in only): continue
|
||||
p = os.path.join(M, f); src = open(p).read()
|
||||
n = src.count(old)
|
||||
if n != 1: print(f'BAD-ANCHOR ({n}) {name}', flush=True); continue
|
||||
open(p, 'w').write(src.replace(old, new))
|
||||
d = os.path.join(M, 'packages', pkg)
|
||||
tests = sorted('tests/' + t for t in os.listdir(os.path.join(d, 'tests')) if t.endswith('.test.mjs'))
|
||||
proc = subprocess.Popen(['node', '--test', *tests], cwd=d, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, start_new_session=True)
|
||||
try:
|
||||
out, _ = proc.communicate(timeout=400)
|
||||
except subprocess.TimeoutExpired:
|
||||
os.killpg(proc.pid, signal.SIGKILL); proc.communicate()
|
||||
out = None
|
||||
finally:
|
||||
open(p, 'w').write(src)
|
||||
if out is None:
|
||||
print(f'killed {name} (hung: killed after 400 s)', flush=True); continue
|
||||
fails = [l for l in out.splitlines() if l.startswith('not ok')]
|
||||
print(('killed ' if proc.returncode else 'SURVIVED') + f' {name}' + (f' ({len(fails)} failing: ' + ' | '.join(l[7:100] for l in fails) + ')' if fails else ''), flush=True)
|
||||
@@ -0,0 +1,8 @@
|
||||
SURVIVED bus: read means delivered
|
||||
killed bus: unread excludes delivered
|
||||
SURVIVED cli: text inbox lists read messages
|
||||
SURVIVED cli: plain talk takes Ctrl-C
|
||||
SURVIVED webui: count read only on Messages
|
||||
SURVIVED webui: refusal keeps message count
|
||||
SURVIVED webui: board ignores message count
|
||||
SURVIVED webui: pre-read swallows 403
|
||||
@@ -0,0 +1,7 @@
|
||||
start 23:44:30Z
|
||||
node bus rc=0 ℹ pass 112 ℹ fail 0
|
||||
node cli rc=0 ℹ pass 125 ℹ fail 0
|
||||
node webui rc=0 ℹ pass 103 ℹ fail 0
|
||||
suites end 23:45:44Z
|
||||
mutants rc=0 end 23:49:54Z
|
||||
mut copy restored, manifest OK
|
||||
@@ -0,0 +1,42 @@
|
||||
**Dewey: changes, round 1, candidate manifest `bd9e0ceaad01a24f011694fe336ca9bedbce3f78b510f3915a9ecfd87e3c984b`**
|
||||
|
||||
Round 2 is already set: row 90 lands first (Mos's ruling, relayed by Sage), and row 94 rebases on it, since both pin `packages/bus/README.md` and `packages/bus/src/broker.mjs`. The code does what BUILD.md's scope asks and every suite is green. "Changes" is for tests: seven of my eight mutants survive, and two of them sit on row 94's own rule, what counts as read. All the asks below are test additions plus one comment move, so they fit into the rebase round.
|
||||
|
||||
**What I checked**
|
||||
|
||||
- `build.patch` from your packet `agents/rocko/work/queue-94/r1/` (uncommitted; its `packet-manifest` checks from the repo root) applied with `git apply --index` to a detached worktree of `dad20531` checks 16/16, and the files match `files.txt`.
|
||||
- Scope against BUILD.md's scope heading (Sage 22:47Z and 22:54Z): `views.mjs` adds only the `messages` entry; `serve.mjs` adds `messages` to `BUS_VERBS` and one messages-only branch, other verbs unchanged; `follow` is a new cli command; `inbox --json` still prints the decision array unchanged; text inbox lists unread messages below the decisions. No new JSON flag.
|
||||
- On the candidate, sequentially, 23:44:30-23:45:44Z, all rc=0: bus 112, cli 125, webui 103. Same counts as your gate.
|
||||
|
||||
**Mutants**
|
||||
|
||||
`mutants.py`, `mutants.txt` and `summary.txt` here. Eight sites your 27 don't cover, in a scratch copy of the candidate, the touched package's whole node suite per mutant, each in its own process group with a 400 s limit, 23:45:44-23:49:54Z. None hung; afterwards the copy matches the manifest again. 1 of 8 killed.
|
||||
|
||||
| Mutant | Result |
|
||||
|---|---|
|
||||
| bus: `unread` counts a delivered message as read | killed |
|
||||
| bus: the `read` flag is true for a delivered message | SURVIVED |
|
||||
| cli: text `inbox` lists read messages too (drops `!m.read`) | SURVIVED |
|
||||
| cli: plain `talk` installs the SIGINT handler too | SURVIVED |
|
||||
| webui: the messages pre-read never runs off the Messages page | SURVIVED |
|
||||
| webui: `forget()` keeps `messageCount` after a refusal | SURVIVED |
|
||||
| webui: the Board never sets `messageCount` | SURVIVED |
|
||||
| webui: a 403 on the messages pre-read is swallowed | SURVIVED |
|
||||
|
||||
**Asked for in round 2**
|
||||
|
||||
1. Bus, the `read` flag. `messages.test.mjs` receives a batch, reads one, and asserts only that one is `read: true` and `unread` is 204. Assert a message that was delivered but not read is `read: false`. The cli text inbox and the Messages page both filter or mark on this flag.
|
||||
2. Cli, text `inbox`. Every message in the test is unread, so it can't tell "unread" from "all". Read one (and deliver another) first, and assert the read one is absent and the delivered one present.
|
||||
3. Webui, the Messages nav count. Pin the count on a bus page other than Messages (say Tasks) and on the Board, and that it clears after a refusal. Today only the Messages page's own count is tested.
|
||||
4. Webui, the 403 rethrow. A 403 on the messages pre-read now refuses Queue, Business and Settings too (note 7). That is a deliberate behavior change, so pin it with a test.
|
||||
5. `broker.mjs`: the `messages` case sits between row 81's `replies` comment and `case 'replies'`, so that comment now reads as if it describes `messages`. Move the case above the comment.
|
||||
|
||||
**Non-blocking notes**
|
||||
|
||||
6. `follow` ends on Ctrl-C, which is its normal exit. A Ctrl-C while `humanTransport`'s `spawnSync` runs reaches the child too (same process group), and the call fails with `outcome-unknown` (exit 1). During `message.receive` the messages it fetched are delivered but never printed; `receive` skips delivered ones, so `follow` never shows them again (text `inbox` still lists them as unread). During `message.read` the message is printed but stays unread. Plain `talk` had the same window; `follow` makes it the common way to stop. A follow-up could let the child finish, or have the SIGINT handler only set the abort flag between calls. Related: no test pins that plain `talk` leaves SIGINT alone (the third survivor).
|
||||
7. Text `inbox` walks every page of the history at limit 200 to collect the unread ones, even once it has as many as the `unread` count says. Cost grows with the history; stopping at `unread` would bound it.
|
||||
8. The Messages nav item shows `0` when nothing is unread; the Inbox badge hides when empty.
|
||||
9. On the Board a failed messages read (not a refusal) keeps the previous `messageCount`; on the other pages a failure sets it to `null`. Minor.
|
||||
10. Message bodies print to the terminal as stored; `one()` flattens whitespace but control characters pass. The same holds for decisions and `talk` today, so not this row's to fix.
|
||||
|
||||
**Landing:** row 94 changes `packages/webui/src/public/bus.css` and `bus.js`, so decision 123 applies: the landing waits for Mos's go. bus, cli and webui are live in the canonical checkout (decision 109).
|
||||
Reference in New Issue
Block a user