diff --git a/scripts/render-voidbot-swarm-dashboard.mjs b/scripts/render-voidbot-swarm-dashboard.mjs index 6ba8f4f..e84097a 100644 --- a/scripts/render-voidbot-swarm-dashboard.mjs +++ b/scripts/render-voidbot-swarm-dashboard.mjs @@ -12,6 +12,7 @@ const defaultSnapshotPath = resolve(defaultStatusDir, "swarm-state.json"); const defaultDashboardPath = resolve(defaultStatusDir, "swarm-dashboard.html"); const defaultCultMeshStorePath = resolve(defaultStatusDir, "cultmesh", "voidbot-swarm-state.cc"); const providerId = "voidbot.swarm"; +const operatorDmProviderId = "voidbot.operator-dm"; const verseId = "voidbot.local"; const snapshotDocumentType = "voidbot.swarm_state_snapshot"; const snapshotSchemaId = "voidbot.swarm_state_snapshot.v1"; @@ -745,10 +746,16 @@ async function writeCultMeshPublication(snapshot, eveState, storePath) { }); const providerAdvertisement = buildProviderAdvertisement(snapshot); const eveBinding = buildEveInterfaceBinding(snapshot, eveState); + const operatorDmState = buildOperatorDmProviderState(snapshot); + const operatorDmAdvertisement = buildOperatorDmProviderAdvertisement(snapshot); + const operatorDmBinding = buildOperatorDmInterfaceBinding(snapshot, operatorDmState); await node.put(snapshotDefinition, "voidbot-swarm", snapshot); await node.put(providerDefinition, providerId, providerAdvertisement); + await node.put(providerDefinition, operatorDmProviderId, operatorDmAdvertisement); await node.put(surfaceDefinition, providerId, eveState); + await node.put(surfaceDefinition, operatorDmProviderId, operatorDmState); await node.put(eveBindingDefinition, providerId, eveBinding); + await node.put(eveBindingDefinition, operatorDmProviderId, operatorDmBinding); await node.flush?.(true); return { writeStatus: "ok", @@ -937,6 +944,207 @@ function providerManifest() { }; } +function buildOperatorDmProviderAdvertisement(snapshot) { + return { + schemaVersion: providerAdvertisementSchemaId, + providerId: operatorDmProviderId, + verseId, + title: "VoidBot Operator DM", + description: "VoidBot-owned operator notification boundary for service alerts that need an owner DM.", + version: String(snapshot.summary?.initiativeClock ?? Date.now()), + status: ownerDmStatus(snapshot), + updatedAt: snapshot.generatedAt, + endpoints: [operatorDmCultMeshEndpoint()], + provider: operatorDmProviderManifest(snapshot), + commandSurface: { + primary: operatorDmCultMeshEndpoint(), + transport: "cultmesh-interface-binding", + commands: [ownerDmCommandDescriptor()], + executionOwner: "VoidBot Discord owner-DM helper", + executionNote: "Command delivery must be drained by VoidBot; Odin only discovers and lowers this provider-owned interface.", + }, + documents: [{ + type: providerAdvertisementDocumentType, + schemaId: providerAdvertisementSchemaId, + key: operatorDmProviderId, + }, { + type: surfaceDocumentType, + schemaId: surfaceSchemaId, + key: operatorDmProviderId, + }, { + type: eveBindingDocumentType, + schemaId: eveBindingSchemaId, + key: operatorDmProviderId, + }], + }; +} + +function buildOperatorDmInterfaceBinding(snapshot, eveState) { + return { + schemaVersion: eveBindingSchemaId, + bindingId: operatorDmProviderId, + providerId: operatorDmProviderId, + verseId, + title: "VoidBot Operator DM", + kind: "eve-cultui-command-surface", + updatedAt: snapshot.generatedAt, + authority: { + owner: "VoidBot Discord owner-DM helper", + commandOwner: "VoidBot worker notify_owner path", + sourceDocuments: [{ + type: providerAdvertisementDocumentType, + schemaId: providerAdvertisementSchemaId, + key: operatorDmProviderId, + }], + surfaceDocument: { + type: surfaceDocumentType, + schemaId: surfaceSchemaId, + key: operatorDmProviderId, + }, + forbiddenWriters: ["Mimir Well", "Odin", "Eve renderers"], + }, + surface: eveState.surface, + stateSummary: { + state: ownerDmStatus(snapshot), + botTokenConfigured: Boolean(env.DISCORD_BOT_TOKEN), + ownerConfigured: Boolean(env.DISCORD_OWNER_ID), + command: "owner.dm.send", + }, + controls: [ownerDmCommandDescriptor()], + commands: [ownerDmCommandDescriptor()], + rendererHints: { + preferredLowerings: ["nightwing-tui", "eve-native"], + viewportMode: "operator-alerts", + tileId: "voidbot-operator-dm", + minWidth: 44, + minHeight: 8, + preferredWidth: 72, + preferredHeight: 14, + priority: -80, + density: "compact", + }, + }; +} + +function buildOperatorDmProviderState(snapshot) { + const state = ownerDmStatus(snapshot); + return { + schema: surfaceSchemaId, + providerId: operatorDmProviderId, + title: "VoidBot Operator DM", + version: Number(snapshot.summary?.initiativeClock ?? Date.now()), + updatedAt: snapshot.generatedAt, + surface: { + schema: "gamecult.eve.surface.v1", + id: "voidbot.operator-dm.surface", + title: "VoidBot Operator DM", + commands: [ownerDmCommandDescriptor()], + root: { + id: "voidbot-operator-dm-root", + kind: "pane", + props: { + title: "Operator DM", + providerId: operatorDmProviderId, + status: state, + summary: state === "active" + ? "owner.dm.send is advertised; VoidBot owns delivery." + : "owner.dm.send is advertised but Discord credentials are incomplete.", + command: "owner.dm.send", + }, + children: [{ + id: "voidbot-operator-dm-status", + kind: "metric", + props: { + label: "Owner DM", + value: state, + tone: state === "active" ? "ok" : "warn", + }, + children: [], + }, { + id: "voidbot-operator-dm-command", + kind: "button", + props: { + label: "Send owner DM", + command: "owner.dm.send", + action: ownerDmCommandAction(), + status: state, + }, + children: [], + }, { + id: "voidbot-operator-dm-contract", + kind: "text", + props: { + text: "Mimir may request operator intervention through gamecult.operator_dm_request.v1; VoidBot owns Discord delivery.", + }, + children: [], + }], + }, + assets: [], + }, + commands: [ownerDmCommandDescriptor()], + }; +} + +function ownerDmCommandDescriptor() { + return { + id: "owner.dm.send", + label: "Send owner DM", + command: "owner.dm.send", + transport: "cultmesh-command", + payloadSchema: { + schemaId: "gamecult.operator_dm_request.v1", + type: "object", + required: ["message"], + properties: { + message: { type: "string", maxLength: 1800 }, + severity: { type: "string" }, + service: { type: "string" }, + sourceId: { type: "string" }, + reason: { type: "string" }, + }, + }, + writes: { + documentType: "gamecult.operator_dm_request", + schemaId: "gamecult.operator_dm_request.v1", + owner: operatorDmProviderId, + }, + action: ownerDmCommandAction(), + }; +} + +function ownerDmCommandAction() { + return { + type: "owner.dm.send", + command: "owner.dm.send", + transport: "cultmesh-command", + providerId: operatorDmProviderId, + payloadSchemaId: "gamecult.operator_dm_request.v1", + }; +} + +function operatorDmProviderManifest(snapshot) { + return { + id: operatorDmProviderId, + title: "VoidBot Operator DM", + description: "Provider-owned owner-DM command boundary for service intervention alerts.", + version: "1", + endpoint: operatorDmCultMeshEndpoint(), + capabilities: ["operator-alerts", "owner-dm", "cultmesh-command"], + usesCultMesh: true, + status: ownerDmStatus(snapshot), + transport: "CultMesh Eve interface binding.", + commands: [ownerDmCommandDescriptor()], + }; +} + +function ownerDmStatus() { + return env.DISCORD_BOT_TOKEN && env.DISCORD_OWNER_ID ? "active" : "degraded"; +} + +function operatorDmCultMeshEndpoint() { + return `cultmesh://${verseId}/eve/providers/${operatorDmProviderId}`; +} + function buildEveProviderState(snapshot) { const summary = snapshot.summary ?? {}; const participants = Array.isArray(snapshot.participants) ? snapshot.participants : []; diff --git a/state/evidence.jsonl b/state/evidence.jsonl index 1a54e4d..3c05ca1 100644 --- a/state/evidence.jsonl +++ b/state/evidence.jsonl @@ -331,3 +331,4 @@ {"ts":"2026-06-03T02:14:18Z","type":"documentation","status":"ok","note":"Documented VoidBot Verse service contract: VoidBot owns Discord cognition/archive/source/typed self-state/repo Face compatibility; Huginn owns Persona/.cc runtime inspection stewardship; Bifrost owns governed work transport; Odin discovers provider-owned CultMesh surfaces; Eve/CultUI is the presentation contract. Current provider is cultmesh://voidbot.local/eve/providers/voidbot.swarm with .cc witness at .voidbot/status/cultmesh/voidbot-swarm-state.cc."} {"ts":"2026-06-04T10:51:11Z","type":"implementation","status":"ok","note":"Fixed repo Face active-stack fault: worker now claims one runnable job per poll, and heartbeat reconciliation treats running jobs as active while approved jobs only block duplicate queueing. Reset eight over-claimed running jobs to approved and restarted worker PID 36736; visible active count dropped to one."} {"ts":"2026-06-04T12:24:21Z","type":"implementation","status":"ok","note":"Watchdog backup discovery now ignores non-timestamped restorepoint directories when choosing latest local backups, so ad hoc Face-state restorepoints under .voidbot/backups no longer masquerade as incomplete scheduled backups. The 2026-06-04 ops smoke still fails only on Raven/192.168.1.84 reachability for remote Ollama and offsite SSH."} +{"ts":"2026-06-04T12:52:27Z","type":"implementation","status":"ok","note":"VoidBot now publishes a provider-owned CultMesh/Eve command surface as voidbot.operator-dm in .voidbot/status/cultmesh/voidbot-swarm-state.cc. It advertises owner.dm.send with payload schema gamecult.operator_dm_request.v1 for Mimir Well intervention alerts; Odin discovers it as an active command-surface provider."}