Files
jason.woltjeandClaude Opus 5.5 20c0ee1c6f agents: Dewey row 151 round 1 packet, launch-notice routing and talk waiting/reply tests (#1645)
Candidate on 686fc1cb6: candidate-manifest.sha256 cb831847...78ed, 2 test files, +44.
Mutants 4/4 killed; owner's gate rc 0 (bus 265, cli 213, every scripts/test-*.sh).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 19:18:15 -05:00

67 lines
3.7 KiB
Diff

diff --git a/packages/bus/tests/launch-notice.test.mjs b/packages/bus/tests/launch-notice.test.mjs
index 3ac83e702..0bf1703a2 100644
--- a/packages/bus/tests/launch-notice.test.mjs
+++ b/packages/bus/tests/launch-notice.test.mjs
@@ -92,6 +92,12 @@ test('a message to a stopped role sends launch.by one notice, and the result say
launcherRunning: true,
});
assert.equal(ctx.notices().length, 2);
+ // Row 151 (#1645): the notice is routed to launch.by's holder, which the Console's waiting lists read.
+ // message.receive selects on to_role, so only this row shows a notice routed anywhere else.
+ assert.equal(
+ ctx.store().get("SELECT holder_run FROM deliveries WHERE message=? AND op='routed'", ctx.notices().at(-1).id).holder_run,
+ 'chief-run',
+ );
// A role's message to a stopped role tells launch.by too.
ctx.launch('coder', 'coder');
ctx.claim('coder');
diff --git a/packages/cli/tests/verbs.test.mjs b/packages/cli/tests/verbs.test.mjs
index f29922e0e..c9d0b9d17 100644
--- a/packages/cli/tests/verbs.test.mjs
+++ b/packages/cli/tests/verbs.test.mjs
@@ -118,6 +118,44 @@ test("talk says when the role isn't running, whether launch.by was told, and how
assert.equal(await text(s, "coder"), "");
});
+// Row 151 (#1645): rows 146 and 142 in one talk. The waiting line prints once after "sent", a
+// message that isn't the reply arrives first and stays unread, then the reply prints and talk exits 0.
+test("talk to a stopped role prints the waiting line once, skips a non-reply and prints the reply", async (t) => {
+ const s = setup(t, {}, { demo: { ...BUSINESSES.demo, launch: { by: "pm", instances: ["cto", "coder"], models: {}, limits: {} } } });
+ const pm = s.b.bindLaunch({ business: "demo", role: "pm", run: "pm-run", harness: "pi" });
+ call(s.b, pm, "role.claim");
+ // pm's note arrives on the first poll; cto comes up and answers on the second.
+ let polls = 0;
+ let note;
+ let reply;
+ const transport = (business) => {
+ const inner = s.transport(business);
+ return async (verb, args) => {
+ if (verb === "message.receive" && ++polls === 1) note = call(s.b, pm, "message.send", { to: "human", body: "launching cto", class: "INFO" });
+ if (verb === "message.receive" && polls === 2) {
+ const cto = s.b.bindLaunch({ business: "demo", role: "cto", run: "cto-run", harness: "pi" });
+ call(s.b, cto, "role.claim");
+ const [m] = call(s.b, cto, "message.receive");
+ reply = call(s.b, cto, "message.send", { to: m.from_role, body: "on it", class: "RESULT", in_reply_to: m.id });
+ }
+ return inner(verb, args);
+ };
+ };
+ const r = await s.run(["talk", "cto", "review the plan", "--wait", "5", "--business", "demo"], io(), { transport });
+ assert.equal(r.code, 0, r.error);
+ const sent = s.store.get("SELECT id FROM messages WHERE from_role='human' ORDER BY seq DESC LIMIT 1");
+ assert.equal(
+ r.io.out.text,
+ `sent ${sent.id.slice(0, 8)} to cto\ncto isn't running, pm has been told\ncto (RESULT, re ${sent.id.slice(0, 8)}) ${reply.id.slice(0, 8)}:\non it\n`,
+ );
+ assert.equal(polls, 2, "the note came first");
+ assert.deepEqual(s.calls.filter((c) => c.verb === "message.read").map((c) => c.args.id), [reply.id], "only the reply was marked read");
+ const inbox = io();
+ await main(["inbox", "--business", "demo"], inbox, { system: { dataRoot: s.dataRoot }, transport: s.transport });
+ assert.match(inbox.out.text, /messages: 1 unread\n/);
+ assert.match(inbox.out.text, new RegExp(`pm \\(INFO\\) ${note.id.slice(0, 8)}:\nlaunching cto\n`));
+});
+
test("talk, stop and launches off/on refuse inside an agent run; bad arguments are usage errors", async (t) => {
const s = setup(t);
const agent = () => io({ ...io().env, CLAUDECODE: "1" });