diff --git a/scripts/serve-voidbot-swarm-cultmesh.cjs b/scripts/serve-voidbot-swarm-cultmesh.cjs index f03b6b7..2e8ba80 100644 --- a/scripts/serve-voidbot-swarm-cultmesh.cjs +++ b/scripts/serve-voidbot-swarm-cultmesh.cjs @@ -26,6 +26,7 @@ const documentDefinitions = Object.values(documents); const bindings = { provider: defineCultNetDocumentBinding({ definition: documents.provider }), surface: defineCultNetDocumentBinding({ definition: documents.surface }), + commandReceipt: defineCultNetDocumentBinding({ definition: documents.commandReceipt }), }; const documentRegistry = new CultNetDocumentRegistry( documentDefinitions.map((definition) => defineCultNetDocumentBinding({ definition })), @@ -101,6 +102,28 @@ async function applySwarmCommand(document) { await node.put(documents.swarmControl, "voidbot-swarm", control); await node.flush?.(true); console.log(`Applied swarm heat ${control.globalHeat} from ${control.commandId}`); + return { + binding: bindings.commandReceipt, + recordKey: control.commandId, + value: { + schema: "gamecult.eve.command_receipt.v1", + receiptId: `voidbot.swarm:${control.commandId}`, + commandId: control.commandId, + command: command.command, + state: "reconciled", + ownerRepo: "VoidBot", + authority: "voidbot.swarm_control_state.v1:voidbot-swarm", + providerId: command.providerId, + surfaceId: command.surfaceId || "voidbot.swarm.surface", + sourceVersion: Date.parse(control.updatedAt), + issuedAtUtc: control.updatedAt, + message: `Swarm heat applied at ${control.globalHeat}.`, + diagnostics: [{ binding: "voidbot.swarm.globalHeat", value: control.globalHeat }], + }, + sourceRuntimeId: "voidbot-swarm-cultmesh", + sourceRole: "swarm-control-owner", + tags: ["eve", "command-receipt", "swarm-control"], + }; } async function announceToOdin() { @@ -223,6 +246,15 @@ function defineDocuments(defineDocumentType) { name: (value) => value?.commandId || value?.command_id || "swarm-command", schema: objectSchema("Eve command"), }), + commandReceipt: defineDocumentType({ + type: "gamecult.eve.command_receipt", + schemaName: "gamecult.eve.command_receipt", + schemaId: "gamecult.eve.command_receipt.v1", + schemaVersion: "gamecult.eve.command_receipt.v1", + global: false, + name: (value) => value?.commandId || value?.receiptId || "command-receipt", + schema: objectSchema("Eve command receipt"), + }), swarmControl: defineDocumentType({ type: "voidbot.swarm_control_state", schemaName: "voidbot.swarm_control_state", diff --git a/scripts/smoke-swarm-heat-command.mjs b/scripts/smoke-swarm-heat-command.mjs index a2e1942..671927e 100644 --- a/scripts/smoke-swarm-heat-command.mjs +++ b/scripts/smoke-swarm-heat-command.mjs @@ -18,14 +18,19 @@ const controlDefinition = defineDocumentType({ type: "voidbot.swarm_control_state", schemaName: "voidbot.swarm_control_state", schemaId: "voidbot.swarm_control_state.v1", schemaVersion: "voidbot.swarm_control_state.v1", global: false, name: () => "voidbot-swarm", schema: objectSchema, }); +const receiptDefinition = defineDocumentType({ + type: "gamecult.eve.command_receipt", schemaName: "gamecult.eve.command_receipt", schemaId: "gamecult.eve.command_receipt.v1", schemaVersion: "gamecult.eve.command_receipt.v1", + global: false, name: input => input?.commandId || "smoke-receipt", schema: objectSchema, +}); const commandId = `smoke:swarm-heat:${Date.now()}`; -await CultMesh.publishRudpDocumentOnce("voidbot-swarm-heat-smoke", 0x43554c54, "rudp://127.0.0.1:17873", { definition: commandDefinition }, commandId, { +const receipt = await CultMesh.publishRudpDocumentAndWaitForReceipt("voidbot-swarm-heat-smoke", 0x43554c54, "rudp://127.0.0.1:17873", { definition: commandDefinition }, commandId, { schema: "gamecult.eve.command.v1", commandId, providerId: "voidbot.swarm", surfaceId: "voidbot.swarm.surface", command: "swarm.set_heat", payload: { value, transport: "cultmesh-binding" }, clientId: "voidbot.smoke", issuedAt: new Date().toISOString(), -}, { sourceRuntimeId: "voidbot-swarm-heat-smoke", sourceRole: "verification", tags: ["smoke", "command"] }); +}, { definition: receiptDefinition }, { sourceRuntimeId: "voidbot-swarm-heat-smoke", sourceRole: "verification", tags: ["smoke", "command"], receiptTimeoutMs: 5_000 }); +if (receipt.commandId !== commandId || receipt.state !== "reconciled") throw new Error(`Receipt did not confirm application: ${JSON.stringify(receipt)}`); await new Promise(resolvePromise => setTimeout(resolvePromise, 500)); const node = await CultMesh.createNode(resolve(repoRoot, ".voidbot", "private", "swarm-controls.cc"), { documents: [controlDefinition] }); const control = node.get(controlDefinition, "voidbot-swarm"); if (Number(control?.globalHeat) !== value || control?.commandId !== commandId) throw new Error(`Applied control did not match command: ${JSON.stringify(control)}`); -console.log(JSON.stringify({ ok: true, commandId, globalHeat: control.globalHeat, controlStore: resolve(repoRoot, ".voidbot", "private", "swarm-controls.cc") })); +console.log(JSON.stringify({ ok: true, commandId, globalHeat: control.globalHeat, receipt, controlStore: resolve(repoRoot, ".voidbot", "private", "swarm-controls.cc") }));