Files
stack/agents/darkwing/work/s6-review/probe/desync.mjs
T
jason.woltjeandClaude Opus 5.5 f19787ee60 docs(review): row 41 slice 1 S6 round 1 review, changes (darkwing)
R1 and R3 block: a late broker reply shifts later replies on the host IPC
channel, and a dangling workspace symlink passes the gate. R2 (close waits
on an open launch.sock client) should be fixed in the same round. Probes,
mutants and suite outputs alongside. Comment 26995 on #1523, queue rev 247.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 20:21:28 -05:00

70 lines
3.9 KiB
JavaScript

// R1: host->broker IPC replies carry no id. Stall the broker past the 10 s
// firstReply wait with two requests queued, resume it, and see which reply
// each request gets. Imports from the review worktree by absolute path.
import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
import { join } from "node:path";
import { tmpdir } from "node:os";
const WT = process.env.WT;
const { Client } = await import(`${WT}/packages/bus/src/client.mjs`);
const { bootConfig, loadSystem } = await import(`${WT}/packages/cli/src/config.mjs`);
const { startHost, startTimeOf } = await import(`${WT}/packages/cli/src/host.mjs`);
const { fixture } = await import(`${WT}/packages/cli/tests/helpers.mjs`);
const root = mkdtempSync(join(tmpdir(), "r41-desync-"));
const f = fixture(root, "acme", (doc) => {
doc.launch = { by: "pm", instances: ["coder", "reviewer", "cto"], max: { opus: 1, sonnet: 1 } };
return doc;
});
mkdirSync(f.dataRoot, { mode: 0o700 });
const system = loadSystem({ env: f.env });
const boot = bootConfig({ system, businessId: "acme", env: f.env });
const host = await startHost({ boot, business: "acme", log: (l) => console.log("host:", l) });
const bind = async (role, run) => {
const b = await host.bindLaunch({ business: "acme", role, run, harness: "pi", pid: process.pid, startTime: startTimeOf(process.pid) });
await new Client({ path: host.path, cap: b.cap }).call("role.claim");
return b.cap;
};
const pm = await bind("pm", "pm-run");
const cto = await bind("cto", "cto-run");
const show = (p) => p.then((v) => ({ ok: v }), (e) => ({ err: e.code ?? e.message }));
console.log("baseline, no stall:");
console.log(" authorizeLaunch(pm, coder):", JSON.stringify(await show(host.op({ op: "authorizeLaunch", cap: pm, instance: "coder" }))));
console.log(" authorizeLaunch(cto, coder):", JSON.stringify(await show(host.op({ op: "authorizeLaunch", cap: cto, instance: "coder" }))));
console.log(" identity(bogus):", JSON.stringify(await show(host.op({ op: "identity", cap: "bogus" }))));
console.log(`SIGSTOP broker ${host.pids.broker}`);
process.kill(host.pids.broker, "SIGSTOP");
const t0 = Date.now();
const p1 = show(host.op({ op: "authorizeLaunch", cap: pm, instance: "coder" }));
const p2 = show(host.op({ op: "authorizeLaunch", cap: cto, instance: "coder" }));
const p3 = show(host.op({ op: "identity", cap: "bogus" }));
const r1 = await p1;
console.log(`p1 authorizeLaunch(pm) after ${Date.now() - t0} ms:`, JSON.stringify(r1));
process.kill(host.pids.broker, "SIGCONT");
console.log("SIGCONT");
console.log("p2 authorizeLaunch(cto), should refuse:", JSON.stringify(await p2));
console.log("p3 identity(bogus), should refuse:", JSON.stringify(await p3));
const p4 = await show(host.op({ op: "identity", cap: cto }));
console.log("p4 identity(cto), should be cto:", JSON.stringify(p4));
await new Promise((r) => setTimeout(r, 500));
console.log("--- bind case: two binds queued during a stall");
process.kill(host.pids.broker, "SIGSTOP");
const rec = (run) => ({ business: "acme", role: "coder", run, harness: "pi", pid: process.pid, startTime: startTimeOf(process.pid) });
const b1 = show(host.bindLaunch(rec("run-x")));
const b2 = show(host.bindLaunch({ ...rec("run-y"), role: "reviewer" }));
console.log("b1 bind run-x:", JSON.stringify(await b1));
process.kill(host.pids.broker, "SIGCONT");
const r2 = await b2;
console.log("b2 bind run-y (reviewer) got:", r2.ok ? JSON.stringify({ run: r2.ok.run, role: r2.ok.role }) : JSON.stringify(r2));
if (r2.ok?.cap) {
const who = await show(new Client({ path: host.path, cap: r2.ok.cap }).call("role.claim"));
console.log("claim with b2's cap:", JSON.stringify(who));
}
await new Promise((r) => setTimeout(r, 3000));
console.log("after 3 s idle, identity(pm), should be pm:", JSON.stringify(await show(host.op({ op: "identity", cap: pm }))));
console.log("then identity(pm) again:", JSON.stringify(await show(host.op({ op: "identity", cap: pm }))));
await host.close(0);
rmSync(root, { recursive: true, force: true });