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]>
70 lines
3.9 KiB
JavaScript
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 });
|