feat(discord): connector pilot for the Sage seat, reviewed candidate (#1509)

Zero-dependency Discord connector under packages/discord: binding
validation, REST and gateway clients, pi engine adapter, journal with
append-only inbox, outbox, admissions and notices, and a run.lock
ownership record {pid, start, boot} whose identity is checked three ways
and whose cleanup is gated by STOP. CLI check|run|stop|unlock via
scripts/discord.sh; offline suite scripts/test-discord.sh (28 checks,
87 node tests).

Reviewed by rev-code-02 on #1509 over nine rounds; approved exact tree
4e0feb6758c0a7e4a71483912a8e0d3e3ec95aef at comment 26170. Corrections
(1) to (12) recorded in BUILD-LOG. No listener started, no token read,
no Discord write; the live pilot follows this commit per the brief.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
2026-09-13 01:15:37 -05:00
co-authored by Claude Fable 5.1
parent b023841c8a
commit 786e379c49
34 changed files with 4641 additions and 1 deletions
+199
View File
@@ -0,0 +1,199 @@
import { test } from "node:test";
import assert from "node:assert/strict";
import { createGateway, OP, CONNECTOR_INTENTS, INTENT } from "../src/gateway.mjs";
import { fakeTimers } from "./helpers.mjs";
// A fake WebSocket: the test is the server. `sockets` records every instance.
function fakeSocketClass() {
const sockets = [];
class FakeWebSocket {
constructor(url) {
this.url = url;
this.readyState = 0;
this.sent = [];
this.closeCalls = [];
sockets.push(this);
}
send(data) {
this.sent.push(JSON.parse(data));
}
close(code, reason) {
this.closeCalls.push({ code, reason });
this.readyState = 2;
// The server-side close event follows.
this.readyState = 3;
if (this.onclose) this.onclose({ code, reason });
}
// test helpers acting as the server
open() {
this.readyState = 1;
if (this.onopen) this.onopen({});
}
receive(payload) {
this.onmessage({ data: JSON.stringify(payload) });
}
serverClose(code, reason = "") {
this.readyState = 3;
this.onclose({ code, reason });
}
sentOps(op) {
return this.sent.filter((p) => p.op === op);
}
}
return { FakeWebSocket, sockets };
}
function setup({ random = () => 0.5 } = {}) {
const t = fakeTimers();
const { FakeWebSocket, sockets } = fakeSocketClass();
const events = [];
const gw = createGateway({
url: "wss://gateway.example", token: "T0KEN-not-real-1234567890", WebSocketImpl: FakeWebSocket,
setTimeoutImpl: t.setTimeout, clearTimeoutImpl: t.clearTimeout, random,
});
for (const name of ["ready", "resumed", "dispatch", "closed", "fatal", "log"]) gw.on(name, (p) => events.push([name, p]));
return { gw, t, sockets, events };
}
function hello(ws, interval = 1000) {
ws.open();
ws.receive({ op: OP.HELLO, d: { heartbeat_interval: interval } });
}
test("gateway: hello -> identify with intents, ready, heartbeat with jitter, ack", () => {
const { gw, t, sockets, events } = setup();
gw.connect();
const ws = sockets[0];
assert.equal(ws.url, "wss://gateway.example/?v=10&encoding=json");
hello(ws, 1000);
const id = ws.sentOps(OP.IDENTIFY);
assert.equal(id.length, 1);
assert.equal(id[0].d.intents, CONNECTOR_INTENTS);
assert.equal(CONNECTOR_INTENTS, INTENT.GUILDS | INTENT.GUILD_MESSAGES | INTENT.MESSAGE_CONTENT);
assert.equal(id[0].d.token, "T0KEN-not-real-1234567890");
ws.receive({ op: OP.DISPATCH, t: "READY", s: 1, d: { session_id: "sess1", resume_gateway_url: "wss://resume.example", user: { id: "1" }, guilds: [{ id: "g" }] } });
assert.equal(events.filter((e) => e[0] === "ready").length, 1);
assert.equal(gw.sessionId, "sess1");
// First heartbeat after interval * jitter (0.5 -> 500 ms), carrying the sequence.
t.advance(499);
assert.equal(ws.sentOps(OP.HEARTBEAT).length, 0);
t.advance(1);
assert.equal(ws.sentOps(OP.HEARTBEAT).length, 1);
assert.equal(ws.sentOps(OP.HEARTBEAT)[0].d, 1);
ws.receive({ op: OP.HEARTBEAT_ACK });
t.advance(1000);
assert.equal(ws.sentOps(OP.HEARTBEAT).length, 2);
// Server-requested heartbeat is answered immediately.
ws.receive({ op: OP.HEARTBEAT });
assert.equal(ws.sentOps(OP.HEARTBEAT).length, 3);
// No token in any emitted event or log.
assert.ok(!JSON.stringify(events).includes("T0KEN"));
});
test("gateway: missed ack closes the socket and resumes with the last sequence", () => {
const { gw, t, sockets, events } = setup();
gw.connect();
const ws = sockets[0];
hello(ws, 1000);
ws.receive({ op: OP.DISPATCH, t: "READY", s: 1, d: { session_id: "sess1", resume_gateway_url: "wss://resume.example", user: { id: "1" } } });
ws.receive({ op: OP.DISPATCH, t: "MESSAGE_CREATE", s: 7, d: { id: "x" } });
t.advance(500); // first heartbeat
t.advance(1000); // second beat, ack still pending -> close
assert.equal(ws.closeCalls.length, 1);
const closed = events.find((e) => e[0] === "closed");
assert.equal(closed[1].willReconnect, true);
t.advance(2000); // backoff (1000 + 500 jitter)
assert.equal(sockets.length, 2);
const ws2 = sockets[1];
assert.equal(ws2.url, "wss://resume.example/?v=10&encoding=json");
hello(ws2, 1000);
const resume = ws2.sentOps(OP.RESUME);
assert.equal(resume.length, 1);
assert.deepEqual(resume[0].d, { token: "T0KEN-not-real-1234567890", session_id: "sess1", seq: 7 });
assert.equal(ws2.sentOps(OP.IDENTIFY).length, 0);
ws2.receive({ op: OP.DISPATCH, t: "RESUMED", s: 8, d: {} });
assert.equal(events.filter((e) => e[0] === "resumed").length, 1);
});
test("gateway: op 7 reconnect resumes; op 9 non-resumable re-identifies", () => {
const { gw, t, sockets } = setup();
gw.connect();
const ws = sockets[0];
hello(ws);
ws.receive({ op: OP.DISPATCH, t: "READY", s: 3, d: { session_id: "s", resume_gateway_url: "wss://r.example", user: { id: "1" } } });
ws.receive({ op: OP.RECONNECT });
assert.equal(ws.closeCalls.length, 1);
t.advance(2000);
const ws2 = sockets[1];
hello(ws2);
assert.equal(ws2.sentOps(OP.RESUME).length, 1);
ws2.receive({ op: OP.INVALID_SESSION, d: false });
t.advance(3000);
const ws3 = sockets[2];
assert.equal(ws3.url, "wss://gateway.example/?v=10&encoding=json");
hello(ws3);
assert.equal(ws3.sentOps(OP.IDENTIFY).length, 1);
assert.equal(ws3.sentOps(OP.RESUME).length, 0);
});
test("gateway: op 9 resumable resumes", () => {
const { gw, t, sockets } = setup();
gw.connect();
const ws = sockets[0];
hello(ws);
ws.receive({ op: OP.DISPATCH, t: "READY", s: 3, d: { session_id: "s", resume_gateway_url: "wss://r.example", user: { id: "1" } } });
ws.receive({ op: OP.INVALID_SESSION, d: true });
t.advance(3000);
const ws2 = sockets[1];
hello(ws2);
assert.equal(ws2.sentOps(OP.RESUME).length, 1);
});
test("gateway: close 4014 is fatal, reports the missing intent, never reconnects", () => {
const { gw, t, sockets, events } = setup();
gw.connect();
const ws = sockets[0];
hello(ws);
ws.serverClose(4014, "Disallowed intent(s).");
const fatal = events.find((e) => e[0] === "fatal");
assert.ok(fatal);
assert.equal(fatal[1].code, 4014);
assert.match(fatal[1].reason, /message content/);
t.advance(120000);
assert.equal(sockets.length, 1);
assert.throws(() => gw.connect(), /already stopped/);
});
test("gateway: 4004 and 4013 are fatal too; 1006 reconnects with identify when no session", () => {
for (const code of [4004, 4013]) {
const { gw, t, sockets, events } = setup();
gw.connect();
hello(sockets[0]);
sockets[0].serverClose(code);
t.advance(120000);
assert.equal(sockets.length, 1, `code ${code}`);
assert.equal(events.filter((e) => e[0] === "fatal").length, 1);
}
const { gw, t, sockets } = setup();
gw.connect();
hello(sockets[0]);
sockets[0].serverClose(1006);
t.advance(2000);
assert.equal(sockets.length, 2);
hello(sockets[1]);
assert.equal(sockets[1].sentOps(OP.IDENTIFY).length, 1);
});
test("gateway: close() is final and unparseable frames are ignored", () => {
const { gw, t, sockets, events } = setup();
gw.connect();
const ws = sockets[0];
hello(ws);
ws.onmessage({ data: "{nope" });
assert.ok(events.some((e) => e[0] === "log" && /unparseable/.test(e[1])));
gw.close();
assert.equal(ws.closeCalls[0].code, 1000);
t.advance(120000);
assert.equal(sockets.length, 1);
assert.equal(t.pending, 0);
});