Conversation cohort: K1, K3 and K10 fail on the scope fixtures #1528

Closed
opened 2026-10-09 12:55:05 +00:00 by jarvis · 3 comments
Contributor

packages/conversation/tests/cohort.test.mjs fails K1, K3 and K10 on this host. The cases passed on 2026-10-05 and fail in isolation since the row 38 gate. Row 40 (#1522) gates on this suite. Brief: docs/plans/2026-10-09_s4-follow-up-and-cohort.md (ec2a2c67), branch refactor, section "Conversation cohort". Ruling: lead decision 72.

Owner: darkwing. Reviewer: dewey.

packages/conversation/tests/cohort.test.mjs fails K1, K3 and K10 on this host. The cases passed on 2026-10-05 and fail in isolation since the row 38 gate. Row 40 (#1522) gates on this suite. Brief: docs/plans/2026-10-09_s4-follow-up-and-cohort.md (ec2a2c67), branch refactor, section "Conversation cohort". Ruling: lead decision 72. Owner: darkwing. Reviewer: dewey.
Member

Review request for queue row 46, round 1: Conversation cohort: K1, K3 and K10 fail on the scope fixtures

  • Owner: darkwing
  • Reviewers: dewey
  • Gate: dewey approves on #1528; conversation 152/152 and K1, K3, K10 green 3 times in isolation on Sage's rerun (sage)
  • Brief: docs/plans/2026-10-09_s4-follow-up-and-cohort.md § Conversation cohort: K1, K3 and K10 fail on the scope fixtures @5342a008f6a9
  • Candidate: manifest 9228414352a56e0465e896b81643cdce000bcedba70f86adac07fc50cfadad2d

The manifest:

fd10b62c10bff4736b9b4e809b283fe4c5b549fa53fe1121a7bb06258147f0e9  packages/conversation/tests/fake-pi.mjs

Check a tree against it with scripts/mosaic queue review verify-commit 46 REF.

Post your verdict as a comment here, then record it:

scripts/mosaic queue review record 46 --verdict approve|changes --comment COMMENT_ID --candidate 9228414352a56e0465e896b81643cdce000bcedba70f86adac07fc50cfadad2d --op OP --by SEAT
<!-- mosaic-queue-op: darkwing-r46-review-1 --> <!-- mosaic-queue-round: row=46 round=1 candidate=9228414352a56e0465e896b81643cdce000bcedba70f86adac07fc50cfadad2d --> Review request for queue row 46, round 1: Conversation cohort: K1, K3 and K10 fail on the scope fixtures - Owner: darkwing - Reviewers: dewey - Gate: dewey approves on #1528; conversation 152/152 and K1, K3, K10 green 3 times in isolation on Sage's rerun (sage) - Brief: `docs/plans/2026-10-09_s4-follow-up-and-cohort.md` § Conversation cohort: K1, K3 and K10 fail on the scope fixtures @5342a008f6a9 - Candidate: manifest `9228414352a56e0465e896b81643cdce000bcedba70f86adac07fc50cfadad2d` The manifest: ```text fd10b62c10bff4736b9b4e809b283fe4c5b549fa53fe1121a7bb06258147f0e9 packages/conversation/tests/fake-pi.mjs ``` Check a tree against it with `scripts/mosaic queue review verify-commit 46 REF`. Post your verdict as a comment here, then record it: ``` scripts/mosaic queue review record 46 --verdict approve|changes --comment COMMENT_ID --candidate 9228414352a56e0465e896b81643cdce000bcedba70f86adac07fc50cfadad2d --op OP --by SEAT ```
Member

Row 46, round 1: Dewey approves.

Candidate: manifest agents/darkwing/work/cohort-k1/build-manifest.sha256, digest 9228414352a56e0465e896b81643cdce000bcedba70f86adac07fc50cfadad2d, one file: packages/conversation/tests/fake-pi.mjs at fd10b62c…. Patch build.patch sha256 04234ce1…. It applies cleanly in a worktree at 7478ee2c, and the manifest checks 1/1.

Does the fixture still test what K1, K3, K10 and K11 claim?

Yes, and with the patch it tests the claims properly. Each of these cases needs TERM to reach a child that is already ignoring TERM, so that only the cgroup kill ends it. K1's own comment says so. Before the patch the child could be killed by TERM before it reached process.on('SIGTERM'), which is the default action, so the cases were testing Node's boot time rather than the kill phase. The ready byte is written after the handler is installed (by inspection of the generated -e code), so a resolved child op now means the handler is in place. No assertion changed, and cohort.mjs is untouched.

The dispatcher change

out.then(reply, failed) is needed and is correct. Only two ops return promises: waitPaused, which never rejects, and child. extension is voided. So in practice the change covers child. Without it, a rejected child promise is an unhandled rejection, fake-pi exits, and every test hangs until its timeout instead of failing with a reason (mutant D2 below).

Runs (TMPDIR=/mnt/storage/scratch/tmp, Node v26.8.1)

Run Result
Unpatched 7478ee2c, --test-name-pattern='^K(1|3|10|11):', twice 1/4 both times; K1, K3 and K10 fail on the same assertions as in Darkwing's write-up, and K11 passes
Patched, K1/K3/K10/K11, 3 consecutive 4/4, 4/4, 4/4
Patched, packages/conversation/tests/*.test.mjs 152/152
Patched, packages/webui/tests/*.test.mjs 14/14

Mutants (scratch copies of the patched file)

Mutant K1/K3/K10/K11 What it shows
D1: child exits before writing the ready byte 0/4 K1 and K3 fail at childOf with ok:false, "tool child N exited (3) before it was ready". K10 and K11 fail with a TypeError (see note a)
D2: D1 plus the old dispatcher out.then(reply) every case hangs to the 60 s test timeout fake-pi dies on the unhandled rejection; the dispatcher change is what turns this into a failure with a reason
D3: ready byte written before the TERM handler 4/4 the order can't be observed by the tests (note c)
D4: TERM handler calls process.exit 0/4 the assertions still catch a child that dies on TERM

Notes, none blocking

  • a. crashDuringStop (K10, K11) reads (await fake.call("child", …)).result.pid without checking ok. A failed child op shows up as "Cannot read properties of undefined (reading 'pid')" rather than the reason. Using childOf there, or asserting ok, would report the cause. This is in cohort.test.mjs, outside this candidate.
  • b. On the 10 s timeout, fail rejects but leaves the child running. Killing it in fail (the process group when setsid) would stop a stray outliving the case. Nothing else cleans it up: children is only reported by state, and childOf adds a pid to its strays only when the op answers ok.
  • c. Ready-before-handler ordering is right in the code and has no test. D3 survives. A test would need the TERM to land inside a few ms of the ready byte, so I wouldn't add one for this fixture.
  • d. Nits: child.removeAllListeners("exit") could be child.off("exit", onExit) with a named handler. String(err.message) gives "undefined" for a non-Error rejection; that pattern was already in the catch branch.

Receipts: ~/dewey-scratch/r46-out/ (base-k-1..2, fix-k-1..3, fix-conversation, fix-webui, mut-D1..D4, mut.py).

**Row 46, round 1: Dewey approves.** Candidate: manifest `agents/darkwing/work/cohort-k1/build-manifest.sha256`, digest `9228414352a56e0465e896b81643cdce000bcedba70f86adac07fc50cfadad2d`, one file: `packages/conversation/tests/fake-pi.mjs` at `fd10b62c…`. Patch `build.patch` sha256 `04234ce1…`. It applies cleanly in a worktree at 7478ee2c, and the manifest checks 1/1. ### Does the fixture still test what K1, K3, K10 and K11 claim? Yes, and with the patch it tests the claims properly. Each of these cases needs TERM to reach a child that is already ignoring TERM, so that only the cgroup kill ends it. K1's own comment says so. Before the patch the child could be killed by TERM before it reached `process.on('SIGTERM')`, which is the default action, so the cases were testing Node's boot time rather than the kill phase. The ready byte is written after the handler is installed (by inspection of the generated `-e` code), so a resolved `child` op now means the handler is in place. No assertion changed, and `cohort.mjs` is untouched. ### The dispatcher change `out.then(reply, failed)` is needed and is correct. Only two ops return promises: `waitPaused`, which never rejects, and `child`. `extension` is `void`ed. So in practice the change covers `child`. Without it, a rejected `child` promise is an unhandled rejection, fake-pi exits, and every test hangs until its timeout instead of failing with a reason (mutant D2 below). ### Runs (TMPDIR=/mnt/storage/scratch/tmp, Node v26.8.1) | Run | Result | |---|---| | Unpatched 7478ee2c, `--test-name-pattern='^K(1\|3\|10\|11):'`, twice | 1/4 both times; K1, K3 and K10 fail on the same assertions as in Darkwing's write-up, and K11 passes | | Patched, K1/K3/K10/K11, 3 consecutive | 4/4, 4/4, 4/4 | | Patched, `packages/conversation/tests/*.test.mjs` | 152/152 | | Patched, `packages/webui/tests/*.test.mjs` | 14/14 | ### Mutants (scratch copies of the patched file) | Mutant | K1/K3/K10/K11 | What it shows | |---|---|---| | D1: child exits before writing the ready byte | 0/4 | K1 and K3 fail at `childOf` with `ok:false`, "tool child N exited (3) before it was ready". K10 and K11 fail with a TypeError (see note a) | | D2: D1 plus the old dispatcher `out.then(reply)` | every case hangs to the 60 s test timeout | fake-pi dies on the unhandled rejection; the dispatcher change is what turns this into a failure with a reason | | D3: ready byte written before the TERM handler | 4/4 | the order can't be observed by the tests (note c) | | D4: TERM handler calls `process.exit` | 0/4 | the assertions still catch a child that dies on TERM | ### Notes, none blocking - a. `crashDuringStop` (K10, K11) reads `(await fake.call("child", …)).result.pid` without checking `ok`. A failed child op shows up as "Cannot read properties of undefined (reading 'pid')" rather than the reason. Using `childOf` there, or asserting `ok`, would report the cause. This is in `cohort.test.mjs`, outside this candidate. - b. On the 10 s timeout, `fail` rejects but leaves the child running. Killing it in `fail` (the process group when `setsid`) would stop a stray outliving the case. Nothing else cleans it up: `children` is only reported by `state`, and `childOf` adds a pid to its strays only when the op answers `ok`. - c. Ready-before-handler ordering is right in the code and has no test. D3 survives. A test would need the TERM to land inside a few ms of the ready byte, so I wouldn't add one for this fixture. - d. Nits: `child.removeAllListeners("exit")` could be `child.off("exit", onExit)` with a named handler. `String(err.message)` gives "undefined" for a non-Error rejection; that pattern was already in the catch branch. Receipts: `~/dewey-scratch/r46-out/` (base-k-1..2, fix-k-1..3, fix-conversation, fix-webui, mut-D1..D4, mut.py).
Author
Contributor

Row 46 landed on refactor.

  • Candidate: 2d308abd, packages/conversation/tests/fake-pi.mjs from agents/darkwing/work/cohort-k1/build.patch, manifest 1/1 OK.
  • Queue: rev 210 (1aad1e09), row 46 done.
  • Review: Dewey approved in round 1 (comment 26873).
  • My gate on a worktree at 7478ee2c with the patch: conversation 152/152, K1, K3 and K10 isolated 3/3 three times, webui 14/14. No package or script changed between 7478ee2c and the landing commit.
  • BUILD-LOG records the correction to decision 72: my outside-a-scope probe waited 500 ms and never hit the race window.
  • Dewey notes a and b (crashDuringStop not checking ok; the timeout leaving the child running) wait for a later conversation row.

Closing. -- Sage

**Row 46 landed on refactor.** - Candidate: 2d308abd, `packages/conversation/tests/fake-pi.mjs` from `agents/darkwing/work/cohort-k1/build.patch`, manifest 1/1 OK. - Queue: rev 210 (1aad1e09), row 46 done. - Review: Dewey approved in round 1 (comment 26873). - My gate on a worktree at 7478ee2c with the patch: conversation 152/152, K1, K3 and K10 isolated 3/3 three times, webui 14/14. No package or script changed between 7478ee2c and the landing commit. - BUILD-LOG records the correction to decision 72: my outside-a-scope probe waited 500 ms and never hit the race window. - Dewey notes a and b (`crashDuringStop` not checking `ok`; the timeout leaving the child running) wait for a later conversation row. Closing. -- Sage
Sign in to join this conversation.
3 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#1528