The four share files (index.ts, module.json, guard.test.ts), so they land as one commit. GIT1: seat.ts serves pull, open-pulls, tags, trees, file, contains, statuses, status, comment, view, note, merge-pull as tools on the seat, each answering the contract's shape and an error of kind refused, not-found or failed; a comment, view or note whose act id is held is answered held (note matched by id). merge-pull merges the head the caller names (its from-G judgements are GIT6, GIT7, GIT9). GIT2: seatevents.ts says pull-updated, pull-merged, pull-closed, forge-pull, forge-status, forge-delete and repo-writable on mesh.seat.git.event.<event>; the module's emits drop them. GIT3: merge.ts, check.ts, the acting tools, the owed watch, the gate's source read and check-gates-merge are gone. GIT10: catalog.ts follows the seat mesh-catalog (revision, catalog-revision); no write is granted before the first view. Builds: design 47 G §310, §311, §312, §313, §314, §163, §317, §278, §321, §196 Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
90 lines
5.5 KiB
TypeScript
90 lines
5.5 KiB
TypeScript
// **The bundle's import ends while a bind is refused** (novox/hq issue 1793). The launcher imports index.js and then answers
|
|
// node-tools' handshake, which has 10 s before the bundle is killed and never started again. A bind awaited at the top
|
|
// of index.ts, refused for longer, ended the bundle at 00:28:37 on 2026-10-22. Run in a child: the polls' timers keep a
|
|
// process alive, and the child says "imported" and exits as soon as the import resolves.
|
|
|
|
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { spawn } from "node:child_process";
|
|
import { dirname, join } from "node:path";
|
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
|
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
|
|
test("importing the bundle resolves while the broker keeps refusing the binds", async () => {
|
|
const index = pathToFileURL(join(here, "..", "dist", "index.js")).href;
|
|
const script = `
|
|
const { useBroker } = await import("@novox/mesh-sdk/messaging");
|
|
useBroker(() => ({
|
|
subscribe: async () => { throw new Error("binding gitea's consumer novox_gitea: nats: no responders available for request"); },
|
|
publish: async () => {}, request: async () => { throw new Error("no responders"); },
|
|
handle: async () => () => {}, close: async () => {},
|
|
}));
|
|
await import(${JSON.stringify(index)});
|
|
console.log("imported");
|
|
process.exit(0);`;
|
|
const env = { ...process.env, GITEA_TOKEN: "x", MESH_GITEA_URL: "http://127.0.0.1:1" };
|
|
for (const k of ["MESH_GITEA_STATE_DIR", "MESH_GITEA_ADMIN_USER", "MESH_GITEA_ADMIN_PASSWORD", "MESH_SERVED_MODULE"]) delete env[k];
|
|
const child = spawn(process.execPath, ["--input-type=module", "-e", script], { cwd: join(here, ".."), env, stdio: ["ignore", "pipe", "pipe"] });
|
|
let out = "";
|
|
let err = "";
|
|
child.stdout.on("data", (d) => (out += d));
|
|
child.stderr.on("data", (d) => (err += d));
|
|
const ended = new Promise<void>((r) => child.on("exit", () => r()));
|
|
const timer = setTimeout(() => child.kill("SIGKILL"), 3000);
|
|
await ended;
|
|
clearTimeout(timer);
|
|
assert.match(out, /imported/, `the import did not resolve within 3 s while a bind was refused; stderr: ${err.slice(0, 600)}`);
|
|
});
|
|
|
|
// **The bundle's import does not wait on the forge** (novox/hq issue 1726, 2026-10-24 12:37). The webhook receiver's set-up
|
|
// asks the forge's admin API for its system webhook; awaited at the top of index.ts, a forge slow to answer (busy while
|
|
// every bundle of the node starts) held the import past node-tools' 10 s handshake, the bundle was killed and never
|
|
// started again, and gitea_events was served nowhere and no new pull request was announced. Here the forge stand-in takes
|
|
// every request and never answers: the import resolves at once all the same, with the bundle's tools registered. The
|
|
// bundle serves no merge and no status of its own: those are the seat git's acts (design 47 *The seat git*).
|
|
test("importing the bundle resolves, its tools registered, while the forge never answers the webhook's set-up", async () => {
|
|
const index = pathToFileURL(join(here, "..", "dist", "index.js")).href;
|
|
const script = `
|
|
const http = await import("node:http");
|
|
const fs = await import("node:fs");
|
|
const os = await import("node:os");
|
|
const path = await import("node:path");
|
|
const forge = http.createServer(() => {});
|
|
await new Promise((r) => forge.listen(0, "127.0.0.1", r));
|
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "gitea-1726-"));
|
|
fs.writeFileSync(path.join(dir, "admin.password"), "pw");
|
|
process.env.MESH_GITEA_URL = "http://127.0.0.1:" + forge.address().port;
|
|
process.env.MESH_GITEA_STATE_DIR = dir;
|
|
process.env.MESH_GITEA_ADMIN_PASSWORD_FILE = path.join(dir, "admin.password");
|
|
process.env.MESH_GITEA_HOOK_PORT = "0";
|
|
process.env.MESH_GITEA_HOOK_HOST = "127.0.0.1";
|
|
const { useBroker } = await import("@novox/mesh-sdk/messaging");
|
|
useBroker(() => ({
|
|
subscribe: async () => () => {}, publish: async () => {}, request: async () => { throw new Error("no responders"); },
|
|
handle: async () => () => {}, close: async () => {},
|
|
}));
|
|
await import(${JSON.stringify(index)});
|
|
const { collectTools } = await import("@novox/mesh-sdk/tools");
|
|
console.log(JSON.stringify({ imported: true, tools: collectTools().flatMap((r) => r.tools.map((t) => t.name)) }));
|
|
process.exit(0);`;
|
|
const env = { ...process.env, GITEA_TOKEN: "x", MESH_GITEA_ADMIN_USER: "admin", MESH_GITEA_HOOK_URL: "http://127.0.0.1:1" };
|
|
for (const k of ["MESH_SERVED_MODULE", "MESH_GITEA_ADMIN_PASSWORD", "MESH_GITEA_CONFIG_FILE"]) delete env[k];
|
|
const child = spawn(process.execPath, ["--input-type=module", "-e", script], { cwd: join(here, ".."), env, stdio: ["ignore", "pipe", "pipe"] });
|
|
let out = "";
|
|
let err = "";
|
|
child.stdout.on("data", (d) => (out += d));
|
|
child.stderr.on("data", (d) => (err += d));
|
|
const ended = new Promise<void>((r) => child.on("exit", () => r()));
|
|
const timer = setTimeout(() => child.kill("SIGKILL"), 3000);
|
|
await ended;
|
|
clearTimeout(timer);
|
|
const line = out.split("\n").find((l) => l.startsWith("{"));
|
|
assert.ok(line, `the import did not resolve within 3 s while the forge did not answer; stdout: ${out.slice(0, 400)} stderr: ${err.slice(0, 600)}`);
|
|
const got = JSON.parse(line!) as { tools: string[] };
|
|
assert.ok(got.tools.includes("gitea_events"), `gitea_events is registered: ${got.tools}`);
|
|
for (const t of ["gitea_commit_status", "gitea_merge_pull_request"]) {
|
|
assert.ok(!got.tools.includes(t), `${t} is retired: ${got.tools}`);
|
|
}
|
|
});
|