dewey: row 63 round 2 review packet, approve, candidate 2a22fd3f (#1556)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
# Row 63 round 2 review mutants (Dewey), round 1's ten unchanged, now with a process group per run: each swaps one exact string in the scratch copy, runs the tasks
|
||||
# verbs and tree tests, restores the file, and reports killed (tests fail) or SURVIVED.
|
||||
import subprocess, sys, os, signal
|
||||
M = os.path.expanduser('~/dewey-scratch/r63mut2')
|
||||
env = dict(os.environ, TMPDIR=os.path.expanduser('~/dewey-scratch/tmp'))
|
||||
V = 'packages/tasks/src/verbs.mjs'
|
||||
T = ['packages/tasks/tests/verbs.test.mjs', 'packages/tasks/tests/tree.test.mjs']
|
||||
ALL = ['packages/tasks/tests/*.test.mjs']
|
||||
muts = [
|
||||
('located: first relation only', V, " for (const r of list) {", " for (const r of list.slice(0, 1)) {", ALL),
|
||||
('located: a missing task passes', V, "const x = await ctx.sync('GET', `/tasks/${r.other}`);\n if (outcome(x) !== 'ok') fail(refusal(x));", "const x = await ctx.sync('GET', `/tasks/${r.other}`);\n if (outcome(x) !== 'ok') continue;", ALL),
|
||||
('relation: keeps the root project', V, "return { kind: x.kind, other: other.task, project: other.project };", "return { kind: x.kind, other: other.task, project: ctx.project };", ALL),
|
||||
('step: re-read on the root board', V, " after = await read(at);\n } catch {\n // the re-read failed too", " after = await read({ ...at, project: ctx.project, view: ctx.view });\n } catch {\n // the re-read failed too", ALL),
|
||||
('handle: final read on the root board', V, " after = await read(at);\n } catch {\n // handled below: a failed", " after = await read({ ...at, project: ctx.project, view: ctx.view });\n } catch {\n // handled below: a failed", ALL),
|
||||
('create: final read on the root board', V, " after = await read(at);\n } catch {\n // handled below, as in handle()", " after = await read({ ...at, project: ctx.project, view: ctx.view });\n } catch {\n // handled below, as in handle()", ALL),
|
||||
('create: fallback todo of the root', V, "taskFields(r.json, at.view.ids.todo)", "taskFields(r.json, ctx.view.ids.todo)", ALL),
|
||||
('create: authorize before located', V, " await located(plan.others);\n ctx.broker.authorize(cap, 'task.create', { decision: plan.decision, target: null });", " ctx.broker.authorize(cap, 'task.create', { decision: plan.decision, target: null });\n await located(plan.others);", ALL),
|
||||
('installed: outside is not-installed', V, " if (!ctx.tree.has(project)) fail('task-project');\n", "", ALL),
|
||||
('create: ref under the root', V, "const taskRef = ref(at.project, id);", "const taskRef = ref(ctx.project, id);", ALL),
|
||||
]
|
||||
only = sys.argv[1:]
|
||||
for name, f, old, new, tests 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))
|
||||
proc = subprocess.Popen(['node', '--test', *tests], cwd=M, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, start_new_session=True)
|
||||
try:
|
||||
out, _ = proc.communicate(timeout=300)
|
||||
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 300 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)' if fails else ''), flush=True)
|
||||
@@ -0,0 +1,10 @@
|
||||
killed located: first relation only
|
||||
killed located: a missing task passes
|
||||
killed relation: keeps the root project
|
||||
killed step: re-read on the root board
|
||||
killed handle: final read on the root board
|
||||
killed create: final read on the root board
|
||||
killed create: fallback todo of the root
|
||||
killed create: authorize before located
|
||||
killed installed: outside is not-installed
|
||||
killed create: ref under the root
|
||||
@@ -0,0 +1,4 @@
|
||||
start 23:01:50Z
|
||||
node tasks rc=0 ℹ pass 84 ℹ fail 0
|
||||
node harness rc=0 ℹ pass 65 ℹ fail 0
|
||||
mutants rc=0 end 23:02:57Z
|
||||
@@ -0,0 +1,26 @@
|
||||
**Dewey: approve, round 2, candidate manifest `2a22fd3f0a04c38e65d40d215b95516c07a9eeff6b2199accc49c39422e22859`**
|
||||
|
||||
My round 1 finding is closed. Each of the five sites that had no test now has one that fails if the site goes back to the root, and the code is unchanged from round 1.
|
||||
|
||||
**What I checked**
|
||||
|
||||
- `build.patch` (packet `557b28fb`) applied with `git apply --index` to a detached worktree of `cca5f23e` checks 6/6. None of the six paths, and not `fake.mjs`, changed between `cca5f23e` and `refactor` `16d5310b`.
|
||||
- `verbs.mjs` and the harness lines are byte-identical to round 1's. The README change rewraps the `task.create` paragraph with the same words, which closes my round 1 non-blocking note.
|
||||
- On the candidate, sequentially, 23:01:50-23:02:57Z, all rc=0: tasks 84, harness 65. Same counts as your gate.
|
||||
- The test changes: `thenFailRead` takes the task id to fault, and `tree()` passes an `edit` to `world()` after `TREE`. The `edit` that `thenFailRead` returns holds only `fake` and `fetch`, so the spread can't replace `TREE`'s `tracker` or `scopes`.
|
||||
|
||||
**The four new tests against my round 1 mutants**
|
||||
|
||||
`mutants.py` and `mutants.txt` here: round 1's ten mutants, the same swaps, run in a scratch copy of the round 2 candidate against every `packages/tasks` test. The runner now gives each run its own process group and kills it after 300 s; none hung. All 10 are killed, the five round 1 survivors included. After the run the scratch copy's six files are byte-identical to the candidate again.
|
||||
|
||||
| Round 1 survivor | Killed by |
|
||||
|---|---|
|
||||
| `handle`'s final read uses the root's project and view | a verb in the child records the final read (non-null etag after `task.assign`) |
|
||||
| `create`'s final read uses the root's project and view | the same test, after the create |
|
||||
| `create`'s fallback uses `ctx.view.ids.todo` | a child create whose final read fails records the child's todo bucket |
|
||||
| `step`'s re-read uses the root's project and view | a lost answer in the child is settled by a re-read (in-progress and done, exactly two PUTs) |
|
||||
| `located` reads only the first relation | every related task is read before authority (the second relation names the child with a root task id: `task-moved`, no write) |
|
||||
|
||||
The failed-read test also checks that, after a tick, the only `task.changed.external` is the grandchild's task 1, so a create whose final read failed is not later recorded as an external change.
|
||||
|
||||
**Merge note.** Rows 63 and 90 share five paths. Stacked on `a0005d62` in either order, the second patch fails on `packages/tasks/README.md` and `packages/tasks/tests/verbs.test.mjs`. Whichever row lands second needs a rebase and a new candidate manifest. I've told Sage.
|
||||
Reference in New Issue
Block a user