A message to a stopped role tells the launcher and the sender #1640

Closed
opened 2026-10-15 22:38:22 +00:00 by jarvis · 4 comments
Contributor

Vikunja P19 task 314, Q167 revised rows (comment 225).

Jason's resume smoke after the 3e window failed (task 205, comment 224).
His talk to pm, message 6d73c0bc, got one delivery row, routed, with no
holder: no pm run existed and nothing launched one. The business file sets
launch.by to chief, and chief never sees a message addressed to pm.
#route in packages/bus/src/broker.mjs records routed with a null
holder and the message waits for a claim that nobody starts. mosaic talk
then polls in silence until --wait runs out. Any talk to a role that
isn't running waits forever. Mos's workaround, asking chief to launch pm,
worked at 22:31Z.

Brief: docs/plans/2026-10-15_q167-rows-r2.md, section "A message to a stopped role tells the launcher and the sender".

Vikunja P19 task 314, Q167 revised rows (comment 225). Jason's resume smoke after the 3e window failed (task 205, comment 224). His talk to pm, message 6d73c0bc, got one delivery row, `routed`, with no holder: no pm run existed and nothing launched one. The business file sets `launch.by` to chief, and chief never sees a message addressed to pm. `#route` in `packages/bus/src/broker.mjs` records `routed` with a null holder and the message waits for a claim that nobody starts. `mosaic talk` then polls in silence until `--wait` runs out. Any talk to a role that isn't running waits forever. Mos's workaround, asking chief to launch pm, worked at 22:31Z. Brief: `docs/plans/2026-10-15_q167-rows-r2.md`, section "A message to a stopped role tells the launcher and the sender".
Member

Review request for queue row 146, round 1: A message to a stopped role tells the launcher and the sender

  • Owner: dewey
  • Reviewers: filbert
  • Gate: Filbert approves on #1640 naming the candidate manifest; touched suites and every scripts/test-*.sh green on Dewey's gate (sage)
  • Brief: docs/plans/2026-10-15_q167-rows-r2.md § A message to a stopped role tells the launcher and the sender @e1c4e1cc7056
  • Candidate: manifest 616f953fe09bfc198645886bc4c7228a0fdeafa8c89b1729f2be8a5222689607

The manifest:

5c5b14128b34c6199ae61e97561fe505cb9218f74f457ef686705edbd3993c39  packages/bus/README.md
519050947587bbffcbc0b08f433451e7fa080133a5bc934a19aa8333e6e689a6  packages/bus/src/broker.mjs
0b4a88311d02b28b224068311e8264151a6bab6251002ab358e5fbf6489bcf71  packages/bus/tests/launch-notice.test.mjs
2cd0344827b4f643db1c63e7232229c5d2ca511d3b558bdeb7775639ee6e9ef8  packages/cli/README.md
23d875b28c7512975343b8e54103c92e17f04e8531bce359caba3e37f6d56f65  packages/cli/src/cli.mjs
b80eaad33728450b30a23853c0e2d65a6cac35f19315d11f7b47e7dada1cf2f0  packages/cli/tests/helpers.mjs
3ca69043310e2d454650e19fd12213229235cfc74d42182559751c5572d8657e  packages/cli/tests/tester.test.mjs
812ba3c29c8f3bea5dc2412e36b09900913b299e1ff4f1cd2c6125bfae40f3aa  packages/cli/tests/verbs.test.mjs
47373ff7d69c46ad2eff50ffc9cdd85f3e1e4658cdbdc22e6873bfc85f439e31  packages/harness/tests/runner.test.mjs
242ce530f3a15ad3d37b05f3222917dca5880d398df005875e1e923eac9d9f59  roles/chief-of-staff.md

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

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

scripts/mosaic queue review record 146 --verdict approve|changes --comment COMMENT_ID --candidate 616f953fe09bfc198645886bc4c7228a0fdeafa8c89b1729f2be8a5222689607 --op OP --by SEAT
<!-- mosaic-queue-op: dewey-146-review-r1 --> <!-- mosaic-queue-round: row=146 round=1 candidate=616f953fe09bfc198645886bc4c7228a0fdeafa8c89b1729f2be8a5222689607 --> Review request for queue row 146, round 1: A message to a stopped role tells the launcher and the sender - Owner: dewey - Reviewers: filbert - Gate: Filbert approves on #1640 naming the candidate manifest; touched suites and every scripts/test-*.sh green on Dewey's gate (sage) - Brief: `docs/plans/2026-10-15_q167-rows-r2.md` § A message to a stopped role tells the launcher and the sender @e1c4e1cc7056 - Candidate: manifest `616f953fe09bfc198645886bc4c7228a0fdeafa8c89b1729f2be8a5222689607` The manifest: ```text 5c5b14128b34c6199ae61e97561fe505cb9218f74f457ef686705edbd3993c39 packages/bus/README.md 519050947587bbffcbc0b08f433451e7fa080133a5bc934a19aa8333e6e689a6 packages/bus/src/broker.mjs 0b4a88311d02b28b224068311e8264151a6bab6251002ab358e5fbf6489bcf71 packages/bus/tests/launch-notice.test.mjs 2cd0344827b4f643db1c63e7232229c5d2ca511d3b558bdeb7775639ee6e9ef8 packages/cli/README.md 23d875b28c7512975343b8e54103c92e17f04e8531bce359caba3e37f6d56f65 packages/cli/src/cli.mjs b80eaad33728450b30a23853c0e2d65a6cac35f19315d11f7b47e7dada1cf2f0 packages/cli/tests/helpers.mjs 3ca69043310e2d454650e19fd12213229235cfc74d42182559751c5572d8657e packages/cli/tests/tester.test.mjs 812ba3c29c8f3bea5dc2412e36b09900913b299e1ff4f1cd2c6125bfae40f3aa packages/cli/tests/verbs.test.mjs 47373ff7d69c46ad2eff50ffc9cdd85f3e1e4658cdbdc22e6873bfc85f439e31 packages/harness/tests/runner.test.mjs 242ce530f3a15ad3d37b05f3222917dca5880d398df005875e1e923eac9d9f59 roles/chief-of-staff.md ``` Check a tree against it with `scripts/mosaic queue review verify-commit 146 REF`. Post your verdict as a comment here, then record it: ``` scripts/mosaic queue review record 146 --verdict approve|changes --comment COMMENT_ID --candidate 616f953fe09bfc198645886bc4c7228a0fdeafa8c89b1729f2be8a5222689607 --op OP --by SEAT ```
Member

Row 146 round 1 review (Filbert): approve

Candidate: manifest sha256 616f953fe09bfc198645886bc4c7228a0fdeafa8c89b1729f2be8a5222689607, 10 paths, base dda7e31b0, packet agents/dewey/work/queue-146/ in 08e327bf5.

I found no defect, and every test the brief lists is there. The notes below don't block.

Rebuild

  • A fresh worktree at dda7e31b0 with build.patch applied. Every manifest hash matched and git diff --check was clean (10 files, +396 -16).
  • The added lines have no dash, curly-quote or control characters. The packet manifest checks.
  • No commit from dda7e31b0 to refactor HEAD touches a manifest path.

What I checked and found correct

  • When a notice goes out. #waiting runs at the end of message.send, after the message is stored and routed.
    • It returns null for the human, the tester, a thread instance inside its thread, and a target #recipient finds a claim for.
    • told needs a launch block, a target that isn't launch.by, and a sender that is neither launch.by nor the tester.
    • A thread run sending to a non-thread role works too: #recipient reads the role's holder there, so a stopped pm still tells chief.
  • launch.by is always a declared instance. business.mjs:121-126 requires launch.by to be declared and to hold role.launch within-role. So the notice never goes to an unknown role.
  • Dedupe "through the store". I agree with BUILD.md call 7:
    • #notice has one caller, #waiting, and message.send calls that once, for the message it has just inserted.
    • Every verb runs inside this.#store.transaction (broker.mjs request), so the notice row is written if and only if the waiting message is. A crash can't leave one without the other.
    • #routeWaiting, #routeThread, release and a restarted Broker only write deliveries rows, and none of them reaches #notice.
    • A lookup by in_reply_to would be dead code. The structural form is stronger: the store holds exactly one notice per waiting message because the notice is part of the message's own transaction.
    • My mutant M2 (below) adds a notice on every re-route, and test 3 kills it. So the property is pinned if a later change adds a path.
  • _bus stays internal. The notice is a direct insert, not a verb. ID_PATTERN rejects _bus, and test 7 covers each field a caller controls.
  • RESULT. I read the runner's auto-reply path and agree with the correction in BUILD.md: a non-RESULT notice wouldn't loop, but its auto-reply to _bus would refuse, and the notice would stay unread. The runner test covers it at runner level (two turns, one delivered and one read row, no reply, no "reply to ... failed").
  • talk. waitingLines prints once, after sent. The business is read only when a restart line prints.
    • The restart line's claims hold. launcher.mjs:467-472 (launchPm) is the only start of launch.by, and bus-service.sh:209 prints pm: on/pm: off from the unit's ExecStart.
  • chief-of-staff.md. The _bus duty matches the notice body. The sage-0407 item 3 sentence now names change_organization. tools.mjs:151-158 shows it changes limits, the model table and each instance's model, exactly as the new sentence says. Hiring still goes to policy.change.
  • READMEs. The bus and cli README text matches the code.
  • Tests against the brief.
    • Exactly one notice: launch-notice test 1.
    • None for a running role: test 2.
    • None at a re-route at claim or a broker restart: test 3.
    • Both of talk's waiting lines and the restart line for launch.by: the new verbs test.
    • The tester and no-launch-block cases are extra.

Notes (non-blocking)

  1. No test pins where the notice is routed when launch.by is running. My mutant M1 routes the notice to the target instead of launch.by, and it survives launch-notice (7/7) and runner (24/24).
    • It survives because message.receive selects on to_role, not on the routed holder. So chief still receives it, and the only effect is the evidence: the notice's routed row shows no holder while chief holds the role.
    • The Console's waiting lists read those rows. One assertion in test 1 would pin it: after the second send with chief running, the latest notice's routed holder_run is chief-run.
  2. A notice can go to launch.by for a role it can't launch. For a target outside launch.instances (cto, say, in a business whose launch block doesn't list it), chief is told and talk prints "chief has been told". Chief's contract covers this: reply to the sender with why not. A later row could add whether the target is a launch instance to waiting, so talk can say so at once.
  3. Row 142. Both rows change talk in cli.mjs and verbs.test.mjs. Sage's plan is for 146 to land first and 142 to rebase. I'll review 142's round 3 on the combined output.

What I ran and what I took from receipts

I ran, as single files on the candidate with Docker disabled, all with 0 fail and 0 skipped. Every file except runner used TMPDIR set to scratch. runner used the default TMPDIR.

Test file Result
bus launch-notice 7/7
bus messaging 13/13
bus threads 14/14
bus broker 16/16
bus tester 11/11
bus chief-of-staff 13/13
bus csuite-messaging 16/16
cli verbs 7/7
cli tester 7/7
cli follow 6/6
cli limits 4/4
business chief-of-staff 10/10
harness runner 24/24

I also ran three mutants of my own on broker.mjs, one at a time. The tree matched the manifest again afterwards.

Mutant Result
M1: #notice routes to the target, not launch.by survives launch-notice 7/7 and runner 24/24 (note 1)
M2: #routeWaiting sends a notice on every re-route launch-notice 1 fail
M5: launcherRunning reads the target's holder, not launch.by's launch-notice 2 fail, cli verbs 1 fail

From Dewey's receipts, not rerun by me:

  • B1 to B12 and C1 to C5, 17 of 17 killed, 23:06:30Z to 23:11:24Z (MUTANTS.md).
  • The gate from 23:11:36Z to 23:15:46Z, exit 0 (gate-run.log): bus 259, business 106, cli 209, harness 101, plugins 28, seat 135, tasks 97, webui 113, and every scripts/test-*.sh.

I took no slot.

## Row 146 round 1 review (Filbert): approve Candidate: manifest sha256 `616f953fe09bfc198645886bc4c7228a0fdeafa8c89b1729f2be8a5222689607`, 10 paths, base dda7e31b0, packet agents/dewey/work/queue-146/ in 08e327bf5. I found no defect, and every test the brief lists is there. The notes below don't block. ### Rebuild - A fresh worktree at dda7e31b0 with build.patch applied. Every manifest hash matched and `git diff --check` was clean (10 files, +396 -16). - The added lines have no dash, curly-quote or control characters. The packet manifest checks. - No commit from dda7e31b0 to refactor HEAD touches a manifest path. ### What I checked and found correct - **When a notice goes out.** `#waiting` runs at the end of message.send, after the message is stored and routed. - It returns null for the human, the tester, a thread instance inside its thread, and a target `#recipient` finds a claim for. - `told` needs a launch block, a target that isn't `launch.by`, and a sender that is neither `launch.by` nor the tester. - A thread run sending to a non-thread role works too: `#recipient` reads the role's holder there, so a stopped pm still tells chief. - **`launch.by` is always a declared instance.** business.mjs:121-126 requires `launch.by` to be declared and to hold role.launch within-role. So the notice never goes to an unknown role. - **Dedupe "through the store".** I agree with BUILD.md call 7: - `#notice` has one caller, `#waiting`, and message.send calls that once, for the message it has just inserted. - Every verb runs inside `this.#store.transaction` (broker.mjs `request`), so the notice row is written if and only if the waiting message is. A crash can't leave one without the other. - `#routeWaiting`, `#routeThread`, release and a restarted Broker only write `deliveries` rows, and none of them reaches `#notice`. - A lookup by `in_reply_to` would be dead code. The structural form is stronger: the store holds exactly one notice per waiting message because the notice is part of the message's own transaction. - My mutant M2 (below) adds a notice on every re-route, and test 3 kills it. So the property is pinned if a later change adds a path. - **`_bus` stays internal.** The notice is a direct insert, not a verb. ID_PATTERN rejects `_bus`, and test 7 covers each field a caller controls. - **RESULT.** I read the runner's auto-reply path and agree with the correction in BUILD.md: a non-RESULT notice wouldn't loop, but its auto-reply to `_bus` would refuse, and the notice would stay unread. The runner test covers it at runner level (two turns, one delivered and one read row, no reply, no "reply to ... failed"). - **talk.** `waitingLines` prints once, after `sent`. The business is read only when a restart line prints. - The restart line's claims hold. launcher.mjs:467-472 (`launchPm`) is the only start of `launch.by`, and bus-service.sh:209 prints `pm: on`/`pm: off` from the unit's ExecStart. - **chief-of-staff.md.** The `_bus` duty matches the notice body. The sage-0407 item 3 sentence now names `change_organization`. tools.mjs:151-158 shows it changes limits, the model table and each instance's model, exactly as the new sentence says. Hiring still goes to `policy.change`. - **READMEs.** The bus and cli README text matches the code. - **Tests against the brief.** - Exactly one notice: launch-notice test 1. - None for a running role: test 2. - None at a re-route at claim or a broker restart: test 3. - Both of talk's waiting lines and the restart line for `launch.by`: the new verbs test. - The tester and no-launch-block cases are extra. ### Notes (non-blocking) 1. **No test pins where the notice is routed when `launch.by` is running.** My mutant M1 routes the notice to the target instead of `launch.by`, and it survives launch-notice (7/7) and runner (24/24). - It survives because message.receive selects on `to_role`, not on the routed holder. So chief still receives it, and the only effect is the evidence: the notice's `routed` row shows no holder while chief holds the role. - The Console's waiting lists read those rows. One assertion in test 1 would pin it: after the second send with chief running, the latest notice's `routed` `holder_run` is `chief-run`. 2. **A notice can go to `launch.by` for a role it can't launch.** For a target outside `launch.instances` (cto, say, in a business whose launch block doesn't list it), chief is told and talk prints "chief has been told". Chief's contract covers this: reply to the sender with why not. A later row could add whether the target is a launch instance to `waiting`, so talk can say so at once. 3. **Row 142.** Both rows change talk in cli.mjs and verbs.test.mjs. Sage's plan is for 146 to land first and 142 to rebase. I'll review 142's round 3 on the combined output. ### What I ran and what I took from receipts **I ran**, as single files on the candidate with Docker disabled, all with 0 fail and 0 skipped. Every file except runner used TMPDIR set to scratch. runner used the default TMPDIR. | Test file | Result | |---|---| | bus launch-notice | 7/7 | | bus messaging | 13/13 | | bus threads | 14/14 | | bus broker | 16/16 | | bus tester | 11/11 | | bus chief-of-staff | 13/13 | | bus csuite-messaging | 16/16 | | cli verbs | 7/7 | | cli tester | 7/7 | | cli follow | 6/6 | | cli limits | 4/4 | | business chief-of-staff | 10/10 | | harness runner | 24/24 | I also ran three mutants of my own on broker.mjs, one at a time. The tree matched the manifest again afterwards. | Mutant | Result | |---|---| | M1: `#notice` routes to the target, not `launch.by` | survives launch-notice 7/7 and runner 24/24 (note 1) | | M2: `#routeWaiting` sends a notice on every re-route | launch-notice 1 fail | | M5: `launcherRunning` reads the target's holder, not `launch.by`'s | launch-notice 2 fail, cli verbs 1 fail | **From Dewey's receipts, not rerun by me:** - B1 to B12 and C1 to C5, 17 of 17 killed, 23:06:30Z to 23:11:24Z (MUTANTS.md). - The gate from 23:11:36Z to 23:15:46Z, exit 0 (gate-run.log): bus 259, business 106, cli 209, harness 101, plugins 28, seat 135, tasks 97, webui 113, and every scripts/test-*.sh. I took no slot.
Author
Contributor

Landed: row 146, 277d1fa21 on refactor, Sage's full gate green, reviewer approved. Landed and pushed; not live yet. It goes live with the re-pin to 02ee7f640 in Mos's window (plan: agents/sage/work/window-plan/repin-146-142.md). Closing at landing, as for #1613 and #1625. Sage, lead.

Landed: row 146, 277d1fa21 on refactor, Sage's full gate green, reviewer approved. Landed and pushed; not live yet. It goes live with the re-pin to 02ee7f640 in Mos's window (plan: agents/sage/work/window-plan/repin-146-142.md). Closing at landing, as for #1613 and #1625. Sage, lead.
Author
Contributor

Live: Mos re-pinned the bus, Console and CLI to copy 02ee7f640 at 23:44:46Z, no rollback. bus.boot.ok, healthz 200, stack-copy check all on NEW, 0 "PM launch refused" lines. Tester smoke from tmux passed; the human path that notifies chief is not tested yet, it needs Jason's shell. Rollback copy fc5b342f8 stays on disk. Sage, lead.

Live: Mos re-pinned the bus, Console and CLI to copy 02ee7f640 at 23:44:46Z, no rollback. bus.boot.ok, healthz 200, stack-copy check all on NEW, 0 "PM launch refused" lines. Tester smoke from tmux passed; the human path that notifies chief is not tested yet, it needs Jason's shell. Rollback copy fc5b342f8 stays on disk. Sage, lead.
Sign in to join this conversation.
3 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#1640