Name no controller verb in hints and drop its handover and paging reads, since the controller ended (issue 1126 B19)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
@@ -40,15 +40,15 @@ async function aMeshWithTools() {
|
||||
await shop.handle("price", async (body: { of?: string }) => ({ of: body.of ?? "nothing", cost: 12 }));
|
||||
// Every role's tools, from the console on this machine (ADR 0571), the method beside MCP's; the controller's
|
||||
// verb answers a call of its seat and is never asked for the roles.
|
||||
const controller = await connectNats({ url: url!, module: "mesh-controller" });
|
||||
const controller = await connectNats({ url: url!, module: "mesh-placement" });
|
||||
let controllerToolsAsked = 0;
|
||||
await controller.handle("seat:mesh-controller.tools", async () => {
|
||||
await controller.handle("seat:mesh-placement.tools", async () => {
|
||||
controllerToolsAsked++;
|
||||
return { seats: [] };
|
||||
});
|
||||
await controller.handle("seat:mesh-controller.status", async () => ({ output: "all quiet", ok: true }));
|
||||
await controller.handle("seat:mesh-placement.status", async () => ({ output: "all quiet", ok: true }));
|
||||
const local = await aConsoleWithSeats([
|
||||
{ seat: "mesh-controller", scope: "mesh", tools: [{ name: "status", description: "what is wrong", input: {} }] },
|
||||
{ seat: "mesh-placement", scope: "mesh", tools: [{ name: "status", description: "what is wrong", input: {} }] },
|
||||
{ seat: "node-dns-resolver", scope: "node", tools: [{ name: "lookup", description: "one machine's", input: {} }] },
|
||||
]);
|
||||
return {
|
||||
@@ -71,12 +71,12 @@ test("a person sees what the running modules answer, sorted, and who did not ans
|
||||
const have = await toolsOn(person);
|
||||
assert.deepEqual(
|
||||
have.tools.map((x) => `${x.module}.${x.name}`),
|
||||
["mesh-catalog.catalog_modules", "mesh-controller.status", "node-dns-resolver.lookup", "shop.price"],
|
||||
["mesh-catalog.catalog_modules", "mesh-placement.status", "node-dns-resolver.lookup", "shop.price"],
|
||||
"the list is what the modules answered plus every role's tools, in a stable order",
|
||||
);
|
||||
// A role's tool is marked as one; a node-scoped seat's carries its scope, so a caller names
|
||||
// the machine and the verb resolves as the seat's (design 33 §4, ADR 0170).
|
||||
assert.ok(have.tools.find((x) => x.module === "mesh-controller")!.seat);
|
||||
assert.ok(have.tools.find((x) => x.module === "mesh-placement")!.seat);
|
||||
const lookup = have.tools.find((x) => x.module === "node-dns-resolver")!;
|
||||
assert.ok(lookup.seat && lookup.scope === "node");
|
||||
assert.equal(toolKey("node-dns-resolver.lookup", seatsIn(have)), "seat:node-dns-resolver.lookup");
|
||||
@@ -146,12 +146,12 @@ test("a role's tool is reached through the seat, and a module's own name is neve
|
||||
const person = await connectNats({ url, module: "person.ada" });
|
||||
try {
|
||||
const roles = seatsIn(await toolsOn(person));
|
||||
assert.equal(toolKey("mesh-controller.status", roles), "seat:mesh-controller.status");
|
||||
assert.equal(toolKey("mesh-controller.other", roles), "mesh-controller.other", "a verb the seat does not declare is a module's");
|
||||
assert.equal(toolKey("mesh-placement.status", roles), "seat:mesh-placement.status");
|
||||
assert.equal(toolKey("mesh-placement.other", roles), "mesh-placement.other", "a verb the seat does not declare is a module's");
|
||||
assert.equal(toolKey("shop.price", roles), "shop.price");
|
||||
const answer = await callTool(person, "mesh-controller.status", {}, roles);
|
||||
const answer = await callTool(person, "mesh-placement.status", {}, roles);
|
||||
assert.deepEqual(answer.result, { output: "all quiet", ok: true });
|
||||
const direct = await callTool(person, "seat:mesh-controller.status", {});
|
||||
const direct = await callTool(person, "seat:mesh-placement.status", {});
|
||||
assert.deepEqual(direct.result, { output: "all quiet", ok: true });
|
||||
} finally {
|
||||
await person.close();
|
||||
|
||||
@@ -30,10 +30,10 @@ async function aMeshAndACredential(t: { after: (fn: () => Promise<void> | void)
|
||||
tools: [{ name: "price", description: "what something costs", input: { of: { type: "string" } } }],
|
||||
}));
|
||||
await shop.handle("price", async (body: { of?: string }) => ({ of: body.of ?? "nothing", cost: 12 }));
|
||||
const controller = await connectNats({ url: url!, module: "mesh-controller" });
|
||||
const controller = await connectNats({ url: url!, module: "mesh-placement" });
|
||||
// Every role's tools, from the console on this machine (ADR 0571), which the spawned `mesh` asks through MESH_CONSOLE.
|
||||
const local = await aConsoleWithSeats([
|
||||
{ seat: "mesh-controller", scope: "mesh", tools: [
|
||||
{ seat: "mesh-placement", scope: "mesh", tools: [
|
||||
{ name: "status", description: "what is wrong", input: {} },
|
||||
{ name: "push", description: "tell a machine", input: { node: { type: "string" } } },
|
||||
] },
|
||||
@@ -41,8 +41,8 @@ async function aMeshAndACredential(t: { after: (fn: () => Promise<void> | void)
|
||||
{ seat: "node-dns-resolver", scope: "node", tools: [{ name: "lookup", description: "one machine's", input: {} }] },
|
||||
]);
|
||||
await controller.handle("seat:node-dns-resolver.lookup@anchor", async () => ({ machine: "anchor", answered: true }));
|
||||
await controller.handle("seat:mesh-controller.status", async () => ({ output: "all quiet", ok: true }));
|
||||
await controller.handle("seat:mesh-controller.push", async (body: { node?: string }) => ({ told: body.node ?? "nobody" }));
|
||||
await controller.handle("seat:mesh-placement.status", async () => ({ output: "all quiet", ok: true }));
|
||||
await controller.handle("seat:mesh-placement.push", async (body: { node?: string }) => ({ told: body.node ?? "nobody" }));
|
||||
t.after(async () => {
|
||||
await catalogue.close();
|
||||
await shop.close();
|
||||
@@ -107,7 +107,7 @@ test("a host initialises, lists the mesh's tools and calls one", async (t) => {
|
||||
|
||||
const listed = byId.get(2)!.result.tools;
|
||||
assert.deepEqual(listed.map((x: { name: string }) => x.name),
|
||||
["mesh-controller.push", "mesh-controller.status", "node-dns-resolver.lookup", "shop.price"],
|
||||
["mesh-placement.push", "mesh-placement.status", "node-dns-resolver.lookup", "shop.price"],
|
||||
"the modules' tools and the roles', named the way a person names them");
|
||||
// A node-scoped seat's verb takes the machine, and requires it (ADR 0170).
|
||||
const lookup = listed[2];
|
||||
@@ -164,7 +164,7 @@ test("a seat's verb keeps a node of its own; only a module's tool gives it to th
|
||||
if (!url) return t.skip("MESH_TEST_NATS unset");
|
||||
const credential = await aMeshAndACredential(t);
|
||||
const replies = await driving(credential, [
|
||||
{ jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "mesh-controller.push", arguments: { node: "anchor" } } },
|
||||
{ jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "mesh-placement.push", arguments: { node: "anchor" } } },
|
||||
]);
|
||||
const result = replies[0].result;
|
||||
assert.ok(!result.isError, JSON.stringify(replies[0]));
|
||||
@@ -175,7 +175,7 @@ test("a host calls the mesh's own verb through the seat", async (t) => {
|
||||
if (!url) return t.skip("MESH_TEST_NATS unset");
|
||||
const credential = await aMeshAndACredential(t);
|
||||
const replies = await driving(credential, [
|
||||
{ jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "mesh-controller.status", arguments: {} } },
|
||||
{ jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "mesh-placement.status", arguments: {} } },
|
||||
]);
|
||||
const result = replies[0].result;
|
||||
assert.ok(!result.isError, JSON.stringify(replies[0]));
|
||||
|
||||
@@ -69,12 +69,12 @@ test("preparing with nothing named fails rather than passing quietly", async ()
|
||||
// therefore pure and tested as such: what a message is for is decided by the patterns registered, not
|
||||
// by which reader happened to fetch it.
|
||||
test("a message is for every pattern that matches it, and nothing else", () => {
|
||||
const registered = ["mesh-build-machine.built", "mesh-controller.built-before"];
|
||||
const registered = ["mesh-build-machine.built", "mesh-placement.built-before"];
|
||||
const matched = (key: string) => registered.filter((p) => topicMatches(p, key));
|
||||
assert.deepEqual(matched("mesh-build-machine.built"), ["mesh-build-machine.built"]);
|
||||
assert.deepEqual(matched("mesh-controller.built-before"), ["mesh-controller.built-before"]);
|
||||
assert.deepEqual(matched("mesh-placement.built-before"), ["mesh-placement.built-before"]);
|
||||
// Nothing registered for it: the consumer's filter is the controller's and may be wider.
|
||||
assert.deepEqual(matched("mesh-catalog.upgraded"), []);
|
||||
// And a handler that asked for everything gets both, which is what the audit logger does.
|
||||
assert.deepEqual(["#"].filter((p) => topicMatches(p, "mesh-controller.built-before")), ["#"]);
|
||||
assert.deepEqual(["#"].filter((p) => topicMatches(p, "mesh-placement.built-before")), ["#"]);
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@ import { connectNats } from "../dist/broker-nats.js";
|
||||
|
||||
const URL = "nats://127.0.0.1:14222";
|
||||
|
||||
// The controller's job, done by hand here: the stream and the module's durable consumer.
|
||||
// The mesh's job, done by hand here: the stream and the module's durable consumer.
|
||||
const admin = await connect({ servers: URL });
|
||||
const jsm = await admin.jetstreamManager();
|
||||
await jsm.streams.add({ name: "EVENTS", subjects: ["mesh.mod.*.event.>", "mesh.seat.*.event.>"] });
|
||||
|
||||
Reference in New Issue
Block a user