From 387afe4941759f059a697b37c59c3a77a8079ecd Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 23 Aug 2026 01:51:24 +0200 Subject: [PATCH] Remove parallel executable control planes --- Cargo.toml | 6 - README.md | 25 +- epiphany-core/Cargo.toml | 124 - .../src/bin/epiphany-agent-memory-store.rs | 284 - .../src/bin/epiphany-agent-telemetry.rs | 526 -- .../src/bin/epiphany-character-loop.rs | 918 -- .../src/bin/epiphany-cluster-daemon.rs | 299 - .../src/bin/epiphany-cultmesh-smoke.rs | 96 - .../src/bin/epiphany-daemon-supervisor.rs | 465 +- ...piphany-daemon-survival-rehearsal-smoke.rs | 479 - .../src/bin/epiphany-hands-action-smoke.rs | 152 - .../src/bin/epiphany-mvp-coordinator-smoke.rs | 324 - .../src/bin/epiphany-operator-run.rs | 486 - .../src/bin/epiphany-operator-snapshot.rs | 167 - ...iphany-persona-discord-crossing-fixture.rs | 48 - ...any-persona-discord-rudp-client-fixture.rs | 82 - .../src/bin/epiphany-persona-discord.rs | 1530 ---- .../epiphany-persona-feedback-causal-smoke.rs | 184 - .../epiphany-persona-memory-recall-smoke.rs | 103 - .../src/bin/epiphany-persona-other.rs | 1290 --- .../src/bin/epiphany-persona-reddit.rs | 1364 --- .../bin/epiphany-public-source-gate-probe.rs | 320 - .../src/bin/epiphany-rider-bridge-smoke.rs | 253 - .../src/bin/epiphany-rider-bridge.rs | 656 -- .../bin/epiphany-runtime-store-benchmark.rs | 143 - .../bin/epiphany-semantic-recovery-smoke.rs | 641 -- .../src/bin/epiphany-state-ledger-store.rs | 136 - .../src/bin/epiphany-unity-bridge-smoke.rs | 490 -- .../src/bin/epiphany-unity-bridge.rs | 1035 --- epiphany-core/src/bin/epiphany-verse-query.rs | 7814 ----------------- epiphany-core/src/bin/epiphany-void-memory.rs | 620 -- .../bin/epiphany-weksa-interlingua-smoke.rs | 130 - epiphany-core/src/bin/epiphany-work.rs | 106 - ...phany-workspace-coverage-recovery-smoke.rs | 522 -- epiphany-core/src/lib.rs | 9 - .../src/memory_graph/semantic_projector.rs | 95 - epiphany-core/src/runtime_spine.rs | 1 - epiphany-core/src/runtime_store_migration.rs | 178 - .../workspace_coverage_projector_service.rs | 4 - epiphany-core/tests/cultnet_backend.rs | 136 - epiphany-release-construction/src/lib.rs | 2 - integrations/rider/README.md | 47 - integrations/rider/build.gradle.kts | 33 - integrations/rider/settings.gradle.kts | 15 - .../epiphany/rider/EpiphanyBridgeClient.kt | 75 - .../rider/EpiphanyCaptureContextAction.kt | 34 - .../rider/EpiphanyToolWindowFactory.kt | 33 - .../src/main/resources/META-INF/plugin.xml | 25 - notes/epiphany-verse-architecture.md | 211 - notes/fresh-workspace-handoff.md | 20 +- notes/hands-cultnet-contracts.md | 17 +- ...graph-semantic-projection-authority-map.md | 11 +- notes/model-atlas-vertical-slice.md | 9 +- schemas/README.md | 5 - schemas/canonical-agent-state-schema.md | 204 - schemas/organ-state-profiles.md | 158 - state/ledgers.msgpack | Bin 651790 -> 652347 bytes state/map.yaml | 8 +- state/scratch.md | 23 +- tools/epiphany_local_run.ps1 | 2244 ----- 60 files changed, 49 insertions(+), 25366 deletions(-) delete mode 100644 epiphany-core/src/bin/epiphany-agent-memory-store.rs delete mode 100644 epiphany-core/src/bin/epiphany-agent-telemetry.rs delete mode 100644 epiphany-core/src/bin/epiphany-character-loop.rs delete mode 100644 epiphany-core/src/bin/epiphany-cluster-daemon.rs delete mode 100644 epiphany-core/src/bin/epiphany-cultmesh-smoke.rs delete mode 100644 epiphany-core/src/bin/epiphany-daemon-survival-rehearsal-smoke.rs delete mode 100644 epiphany-core/src/bin/epiphany-hands-action-smoke.rs delete mode 100644 epiphany-core/src/bin/epiphany-mvp-coordinator-smoke.rs delete mode 100644 epiphany-core/src/bin/epiphany-operator-run.rs delete mode 100644 epiphany-core/src/bin/epiphany-operator-snapshot.rs delete mode 100644 epiphany-core/src/bin/epiphany-persona-discord-crossing-fixture.rs delete mode 100644 epiphany-core/src/bin/epiphany-persona-discord-rudp-client-fixture.rs delete mode 100644 epiphany-core/src/bin/epiphany-persona-discord.rs delete mode 100644 epiphany-core/src/bin/epiphany-persona-feedback-causal-smoke.rs delete mode 100644 epiphany-core/src/bin/epiphany-persona-memory-recall-smoke.rs delete mode 100644 epiphany-core/src/bin/epiphany-persona-other.rs delete mode 100644 epiphany-core/src/bin/epiphany-persona-reddit.rs delete mode 100644 epiphany-core/src/bin/epiphany-public-source-gate-probe.rs delete mode 100644 epiphany-core/src/bin/epiphany-rider-bridge-smoke.rs delete mode 100644 epiphany-core/src/bin/epiphany-rider-bridge.rs delete mode 100644 epiphany-core/src/bin/epiphany-runtime-store-benchmark.rs delete mode 100644 epiphany-core/src/bin/epiphany-semantic-recovery-smoke.rs delete mode 100644 epiphany-core/src/bin/epiphany-state-ledger-store.rs delete mode 100644 epiphany-core/src/bin/epiphany-unity-bridge-smoke.rs delete mode 100644 epiphany-core/src/bin/epiphany-unity-bridge.rs delete mode 100644 epiphany-core/src/bin/epiphany-verse-query.rs delete mode 100644 epiphany-core/src/bin/epiphany-void-memory.rs delete mode 100644 epiphany-core/src/bin/epiphany-weksa-interlingua-smoke.rs delete mode 100644 epiphany-core/src/bin/epiphany-work.rs delete mode 100644 epiphany-core/src/bin/epiphany-workspace-coverage-recovery-smoke.rs delete mode 100644 epiphany-core/src/runtime_store_migration.rs delete mode 100644 epiphany-core/tests/cultnet_backend.rs delete mode 100644 integrations/rider/README.md delete mode 100644 integrations/rider/build.gradle.kts delete mode 100644 integrations/rider/settings.gradle.kts delete mode 100644 integrations/rider/src/main/kotlin/dev/epiphany/rider/EpiphanyBridgeClient.kt delete mode 100644 integrations/rider/src/main/kotlin/dev/epiphany/rider/EpiphanyCaptureContextAction.kt delete mode 100644 integrations/rider/src/main/kotlin/dev/epiphany/rider/EpiphanyToolWindowFactory.kt delete mode 100644 integrations/rider/src/main/resources/META-INF/plugin.xml delete mode 100644 notes/epiphany-verse-architecture.md delete mode 100644 schemas/canonical-agent-state-schema.md delete mode 100644 schemas/organ-state-profiles.md delete mode 100644 tools/epiphany_local_run.ps1 diff --git a/Cargo.toml b/Cargo.toml index 0bd3b471..4fade533 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,8 +59,6 @@ uuid = { version = "1", features = ["serde", "v4"] } zeroize = "1" [features] -semantic-recovery-smoke = [] -workspace-coverage-recovery-smoke = [] coordinator-runtime = ["dep:epiphany-self-policy"] openai-runtime = ["dep:epiphany-openai-runtime"] tool-mcp-runtime = ["dep:epiphany-tool-mcp-runtime"] @@ -121,10 +119,6 @@ path = "epiphany-core/src/bin/epiphany-workspace-coverage-projector.rs" name = "epiphany-memory-semantic" path = "epiphany-core/src/bin/epiphany-memory-semantic.rs" -[[bin]] -name = "epiphany-verse-query" -path = "epiphany-core/src/bin/epiphany-verse-query.rs" - [[bin]] name = "epiphany-repository-body" path = "epiphany-core/src/bin/epiphany-repository-body.rs" diff --git a/README.md b/README.md index e248e99c..5c05bc1e 100644 --- a/README.md +++ b/README.md @@ -241,26 +241,13 @@ Read that before touching the machine. It contains the operating law, re-entry rites, and enough anti-Jenga doctrine to keep the next clever patch from becoming tomorrow's cleanup sermon. -## Run Locally +## Operator Surfaces -The current local operator wrapper is: - -```powershell -.\tools\epiphany_local_run.ps1 -``` - -Useful modes: - -```powershell -.\tools\epiphany_local_run.ps1 -Mode status -.\tools\epiphany_local_run.ps1 -Mode plan -.\tools\epiphany_local_run.ps1 -Mode smoke -.\tools\epiphany_local_run.ps1 -Mode run -MaxSteps 4 -.\tools\epiphany_local_run.ps1 -Mode mvp -PersonaInput "Wake the local swarm and report." -``` - -`status`, `plan`, and `smoke` are operator-safe and do not spend model calls. -`run` and `mvp` can use the retained OpenAI/Codex model transport. +Eve/CultMesh projections are the normal human interface. Repository surgery +uses the narrow native binaries owned by the packaged runtime; there is no +umbrella script that assembles a second local control plane. For direct Mind +inspection during development, use the shared-target `epiphany-state` command +documented in [AGENTS.md](AGENTS.md). ## License diff --git a/epiphany-core/Cargo.toml b/epiphany-core/Cargo.toml index 53557844..295e192d 100644 --- a/epiphany-core/Cargo.toml +++ b/epiphany-core/Cargo.toml @@ -10,134 +10,10 @@ license-file = "../LICENSE" name = "epiphany_core" path = "src/lib.rs" -[features] -public-source-gate-probe = [] -semantic-recovery-smoke = [] -workspace-coverage-recovery-smoke = [] - -[[bin]] -name = "epiphany-semantic-recovery-smoke" -path = "src/bin/epiphany-semantic-recovery-smoke.rs" -required-features = ["semantic-recovery-smoke"] - -[[bin]] -name = "epiphany-workspace-coverage-recovery-smoke" -path = "src/bin/epiphany-workspace-coverage-recovery-smoke.rs" -required-features = ["workspace-coverage-recovery-smoke"] - -[[bin]] -name = "epiphany-agent-memory-store" -path = "src/bin/epiphany-agent-memory-store.rs" - -[[bin]] -name = "epiphany-agent-telemetry" -path = "src/bin/epiphany-agent-telemetry.rs" - -[[bin]] -name = "epiphany-character-loop" -path = "src/bin/epiphany-character-loop.rs" - -[[bin]] -name = "epiphany-cluster-daemon" -path = "src/bin/epiphany-cluster-daemon.rs" - -[[bin]] -name = "epiphany-cultmesh-smoke" -path = "src/bin/epiphany-cultmesh-smoke.rs" - -[[bin]] -name = "epiphany-daemon-survival-rehearsal-smoke" -path = "src/bin/epiphany-daemon-survival-rehearsal-smoke.rs" - -[[bin]] -name = "epiphany-hands-action-smoke" -path = "src/bin/epiphany-hands-action-smoke.rs" - -[[bin]] -name = "epiphany-mvp-coordinator-smoke" -path = "src/bin/epiphany-mvp-coordinator-smoke.rs" - -[[bin]] -name = "epiphany-operator-run" -path = "src/bin/epiphany-operator-run.rs" - -[[bin]] -name = "epiphany-operator-snapshot" -path = "src/bin/epiphany-operator-snapshot.rs" - -[[bin]] -name = "epiphany-persona-discord" -path = "src/bin/epiphany-persona-discord.rs" - -[[bin]] -name = "epiphany-persona-discord-crossing-fixture" -path = "src/bin/epiphany-persona-discord-crossing-fixture.rs" - -[[bin]] -name = "epiphany-persona-discord-rudp-client-fixture" -path = "src/bin/epiphany-persona-discord-rudp-client-fixture.rs" - -[[bin]] -name = "epiphany-persona-feedback-causal-smoke" -path = "src/bin/epiphany-persona-feedback-causal-smoke.rs" - -[[bin]] -name = "epiphany-persona-memory-recall-smoke" -path = "src/bin/epiphany-persona-memory-recall-smoke.rs" - -[[bin]] -name = "epiphany-public-source-gate-probe" -path = "src/bin/epiphany-public-source-gate-probe.rs" -required-features = ["public-source-gate-probe"] - -[[bin]] -name = "epiphany-persona-other" -path = "src/bin/epiphany-persona-other.rs" - -[[bin]] -name = "epiphany-persona-reddit" -path = "src/bin/epiphany-persona-reddit.rs" - [[bin]] name = "epiphany-prepare-compaction" path = "src/bin/epiphany-prepare-compaction.rs" -[[bin]] -name = "epiphany-rider-bridge" -path = "src/bin/epiphany-rider-bridge.rs" - -[[bin]] -name = "epiphany-rider-bridge-smoke" -path = "src/bin/epiphany-rider-bridge-smoke.rs" - -[[bin]] -name = "epiphany-runtime-store-benchmark" -path = "src/bin/epiphany-runtime-store-benchmark.rs" - -[[bin]] -name = "epiphany-state-ledger-store" -path = "src/bin/epiphany-state-ledger-store.rs" - -[[bin]] -name = "epiphany-unity-bridge" -path = "src/bin/epiphany-unity-bridge.rs" - -[[bin]] -name = "epiphany-unity-bridge-smoke" -path = "src/bin/epiphany-unity-bridge-smoke.rs" - -[[bin]] -name = "epiphany-void-memory" -path = "src/bin/epiphany-void-memory.rs" - -[[bin]] -name = "epiphany-weksa-interlingua-smoke" -path = "src/bin/epiphany-weksa-interlingua-smoke.rs" - -[[bin]] -name = "epiphany-work" -path = "src/bin/epiphany-work.rs" - [dependencies] anyhow = "1" bm25 = "2.3.2" diff --git a/epiphany-core/src/bin/epiphany-agent-memory-store.rs b/epiphany-core/src/bin/epiphany-agent-memory-store.rs deleted file mode 100644 index 1c66fce6..00000000 --- a/epiphany-core/src/bin/epiphany-agent-memory-store.rs +++ /dev/null @@ -1,284 +0,0 @@ -use anyhow::Context; -use anyhow::Result; -use epiphany_core::AgentMemoryLifecycleOperation; -use epiphany_core::AgentSelfPatch; -use epiphany_core::admit_legacy_agent_memory_generation; -use epiphany_core::agent_memory_status; -use epiphany_core::apply_agent_memory_lifecycle_operation; -use epiphany_core::apply_agent_self_patch_document; -use epiphany_core::ensure_agent_memory_swarm_identity; -use epiphany_core::migrate_agent_memory_json_dir_to_cultcache; -use epiphany_core::project_agent_memory_to_json_dir; -use epiphany_core::project_persona_state_for_role; -use epiphany_core::refresh_agent_state_soa; -use epiphany_core::repair_agent_memory_store; -use epiphany_core::review_agent_memory_lifecycle_operation; -use epiphany_core::review_agent_self_patch_document; -use epiphany_core::validate_agent_memory_store; -use std::env; -use std::fs; -use std::path::Path; -use std::path::PathBuf; -use uuid::Uuid; - -fn main() -> Result<()> { - let mut args = env::args().skip(1); - let Some(command) = args.next() else { - print_usage(); - std::process::exit(2); - }; - match command.as_str() { - "migrate-json-dir" => { - let agent_dir = require_path_arg(&mut args, "--agent-dir")?; - let store = require_path_arg(&mut args, "--store")?; - print_json(&migrate_agent_memory_json_dir_to_cultcache( - agent_dir, store, - )?)?; - } - "project-json-dir" => { - let store = require_path_arg(&mut args, "--store")?; - let output_dir = require_path_arg(&mut args, "--output-dir")?; - print_json(&project_agent_memory_to_json_dir(store, output_dir)?)?; - } - "project-persona" => { - let store = require_path_arg(&mut args, "--store")?; - let role_id = require_string_arg(&mut args, "--role-id")?; - print_json(&project_persona_state_for_role(store, &role_id)?)?; - } - "status" => { - let store = require_path_arg(&mut args, "--store")?; - print_json(&agent_memory_status(store)?)?; - } - "set-swarm-identity" => { - let store = require_path_arg(&mut args, "--store")?; - let swarm_id = require_string_arg(&mut args, "--swarm-id")?; - print_json(&ensure_agent_memory_swarm_identity(store, &swarm_id)?)?; - } - "admit-legacy-mind" => { - let store = require_path_arg(&mut args, "--store")?; - print_json(&admit_legacy_agent_memory_generation(store)?)?; - } - "refresh-soa" => { - let store = require_path_arg(&mut args, "--store")?; - print_json(&refresh_agent_state_soa(store)?)?; - } - "repair" => { - let store = require_path_arg(&mut args, "--store")?; - let result = repair_agent_memory_store(store)?; - let ok = result["ok"].as_bool().unwrap_or(false); - print_json(&result)?; - if !ok { - std::process::exit(1); - } - } - "validate" => { - let store = require_path_arg(&mut args, "--store")?; - let errors = validate_agent_memory_store(&store)?; - print_json(&serde_json::json!({ - "ok": errors.is_empty(), - "store": store, - "errors": errors, - }))?; - if !errors.is_empty() { - std::process::exit(1); - } - } - "review-patch" => { - let store = require_path_arg(&mut args, "--store")?; - let role_id = require_string_arg(&mut args, "--role-id")?; - let patch = read_patch_arg(&require_string_arg(&mut args, "--patch")?)?; - print_json(&review_agent_self_patch_document(&role_id, &patch, store))?; - } - "apply-patch" => { - let store = require_path_arg(&mut args, "--store")?; - let role_id = require_string_arg(&mut args, "--role-id")?; - let patch = read_patch_arg(&require_string_arg(&mut args, "--patch")?)?; - let result = apply_agent_self_patch_document(&role_id, patch, store)?; - let accepted = result.status == "accepted"; - print_json(&result)?; - if !accepted { - std::process::exit(1); - } - } - "review-lifecycle" => { - let store = require_path_arg(&mut args, "--store")?; - let role_id = require_string_arg(&mut args, "--role-id")?; - let operation = read_lifecycle_arg(&require_string_arg(&mut args, "--operation")?)?; - print_json(&review_agent_memory_lifecycle_operation( - &role_id, &operation, store, - ))?; - } - "apply-lifecycle" => { - let store = require_path_arg(&mut args, "--store")?; - let role_id = require_string_arg(&mut args, "--role-id")?; - let operation = read_lifecycle_arg(&require_string_arg(&mut args, "--operation")?)?; - let result = apply_agent_memory_lifecycle_operation(&role_id, operation, store)?; - let accepted = result.status == "accepted"; - print_json(&result)?; - if !accepted { - std::process::exit(1); - } - } - "smoke" => { - let store = optional_path_arg(&mut args, "--store")? - .unwrap_or_else(|| PathBuf::from("state/agents.msgpack")); - let result = run_smoke(&store)?; - let ok = result["ok"].as_bool().unwrap_or(false); - print_json(&result)?; - if !ok { - std::process::exit(1); - } - } - _ => { - print_usage(); - std::process::exit(2); - } - } - Ok(()) -} - -fn require_path_arg(args: &mut impl Iterator, name: &str) -> Result { - Ok(PathBuf::from(require_string_arg(args, name)?)) -} - -fn require_string_arg(args: &mut impl Iterator, name: &str) -> Result { - let Some(flag) = args.next() else { - anyhow::bail!("missing {name}"); - }; - if flag != name { - anyhow::bail!("expected {name}, got {flag}"); - } - args.next() - .with_context(|| format!("missing value for {name}")) -} - -fn optional_path_arg( - args: &mut impl Iterator, - name: &str, -) -> Result> { - let values: Vec = args.collect(); - if values.is_empty() { - return Ok(None); - } - if values.len() != 2 || values[0] != name { - anyhow::bail!("expected optional {name} , got {values:?}"); - } - Ok(Some(PathBuf::from(&values[1]))) -} - -fn read_patch_arg(value: &str) -> Result { - let path = PathBuf::from(value); - if path.exists() { - let raw = read_json_file_lossless(&path, "patch")?; - return serde_json::from_str(&raw) - .with_context(|| format!("failed to decode typed selfPatch {}", path.display())); - } - serde_json::from_str(value).context("failed to decode typed selfPatch JSON") -} - -fn read_lifecycle_arg(value: &str) -> Result { - let path = PathBuf::from(value); - if path.exists() { - let raw = read_json_file_lossless(&path, "lifecycle operation")?; - return serde_json::from_str(&raw).with_context(|| { - format!( - "failed to decode typed memory lifecycle operation {}", - path.display() - ) - }); - } - serde_json::from_str(value).context("failed to decode typed memory lifecycle operation JSON") -} - -fn read_json_file_lossless(path: &Path, label: &str) -> Result { - let raw = fs::read_to_string(path) - .with_context(|| format!("failed to read {label} {}", path.display()))?; - Ok(raw.trim_start_matches('\u{feff}').to_string()) -} - -fn print_json(value: &T) -> Result<()> { - println!("{}", serde_json::to_string_pretty(value)?); - Ok(()) -} - -fn print_usage() { - eprintln!( - "usage: epiphany-agent-memory-store ..." - ); -} - -fn run_smoke(store: &Path) -> Result { - let errors = validate_agent_memory_store(store)?; - if !errors.is_empty() { - return Ok(serde_json::json!({ - "ok": false, - "phase": "validate", - "errors": errors, - })); - } - let accepted_patch: AgentSelfPatch = serde_json::from_value(serde_json::json!({ - "agentId": "epiphany.modeling", - "reason": "Modeling should remember accepted graph growth must be source-grounded and bounded.", - "semanticMemories": [{ - "memoryId": "mem-body-smoke-source-grounding", - "summary": "A modeling self-memory request is acceptable when it improves future graph/checkpoint judgment without smuggling project truth.", - "salience": 0.74, - "confidence": 0.86, - }], - }))?; - let accepted = review_agent_self_patch_document("modeling", &accepted_patch, store); - let wrong_role = review_agent_self_patch_document("verification", &accepted_patch, store); - let forbidden_patch: AgentSelfPatch = serde_json::from_value(serde_json::json!({ - "agentId": "epiphany.modeling", - "reason": "This tries to put project state in lane memory, which should be refused.", - "graphs": {}, - "semanticMemories": [{ - "memoryId": "mem-body-bad-project-truth", - "summary": "Bad patch.", - "salience": 0.5, - "confidence": 0.5, - }], - }))?; - let forbidden = review_agent_self_patch_document("modeling", &forbidden_patch, store); - - let temp_dir = scoped_temp_dir("epiphany-agent-memory-smoke")?; - let temp_store = temp_dir.join("agents.msgpack"); - fs::copy(store, &temp_store).with_context(|| { - format!( - "failed to copy {} to {}", - store.display(), - temp_store.display() - ) - })?; - let applied = apply_agent_self_patch_document("modeling", accepted_patch, &temp_store)?; - let soa_refresh = refresh_agent_state_soa(&temp_store)?; - let soa_status = agent_memory_status(&temp_store)?; - let temp_validation_errors = validate_agent_memory_store(&temp_store)?; - let _ = fs::remove_dir_all(&temp_dir); - - let ok = accepted.status == "accepted" - && wrong_role.status == "rejected" - && forbidden.status == "rejected" - && applied.status == "accepted" - && soa_refresh["rowCount"] == 7 - && soa_status["persistedAgentStateSoa"]["rowCount"] == 7 - && temp_validation_errors.is_empty() - && project_persona_state_for_role(store, "Persona")?["schemaVersion"] - == epiphany_core::PERSONA_STATE_SCHEMA_VERSION; - Ok(serde_json::json!({ - "ok": ok, - "accepted": accepted, - "wrongRole": wrong_role, - "forbidden": forbidden, - "applied": applied, - "soaRefresh": soa_refresh, - "soaStatus": soa_status["persistedAgentStateSoa"].clone(), - "tempValidationErrors": temp_validation_errors, - })) -} - -fn scoped_temp_dir(prefix: &str) -> Result { - let path = env::temp_dir().join(format!("{prefix}-{}", Uuid::new_v4())); - fs::create_dir(&path).with_context(|| format!("failed to claim {}", path.display()))?; - Ok(path) -} diff --git a/epiphany-core/src/bin/epiphany-agent-telemetry.rs b/epiphany-core/src/bin/epiphany-agent-telemetry.rs deleted file mode 100644 index e1a62a7d..00000000 --- a/epiphany-core/src/bin/epiphany-agent-telemetry.rs +++ /dev/null @@ -1,526 +0,0 @@ -use anyhow::Context; -use anyhow::Result; -use anyhow::anyhow; -use chrono::Utc; -use serde_json::Map; -use serde_json::Value; -use serde_json::json; -use std::collections::BTreeMap; -use std::collections::BTreeSet; -use std::env; -use std::fs; -use std::path::{Path, PathBuf}; - -const TEXTLIKE_KEYS: &[&str] = &[ - "activeTranscript", - "body", - "content", - "input", - "inputTranscript", - "items", - "message", - "note", - "output", - "prompt", - "raw", - "rawResult", - "reasoning", - "result", - "summary", - "text", - "turns", -]; - -const SAFE_KEYS: &[&str] = &[ - "action", - "artifactPath", - "backendJobId", - "bindingId", - "changedFields", - "cwd", - "ephemeral", - "expectedRevision", - "id", - "jobId", - "kind", - "level", - "maxRuntimeSeconds", - "method", - "mode", - "path", - "recommendedAction", - "recommendedSceneAction", - "revision", - "roleId", - "source", - "stateStatus", - "status", - "targetRole", - "threadId", - "turnId", - "type", - "verdict", -]; - -const COMMAND_VERBS: &[&str] = &[ - "rg", - "git", - "Get-Content", - "Get-ChildItem", - "Select-String", - "Test-Path", - "New-Item", - "Set-Content", - "python", - "cargo", - "dotnet", - "npm", - "node", - "apply_patch", -]; - -fn main() -> Result<()> { - let args = parse_args()?; - let telemetry = build_telemetry(&args.transcript)?; - if let Some(output) = args.output { - if let Some(parent) = output.parent() { - fs::create_dir_all(parent) - .with_context(|| format!("failed to create {}", parent.display()))?; - } - fs::write( - &output, - format!("{}\n", serde_json::to_string_pretty(&telemetry)?), - ) - .with_context(|| format!("failed to write {}", output.display()))?; - } - println!("{}", serde_json::to_string_pretty(&telemetry)?); - Ok(()) -} - -struct Args { - transcript: PathBuf, - output: Option, -} - -fn parse_args() -> Result { - let mut args = env::args().skip(1); - let Some(transcript) = args.next() else { - return Err(anyhow!( - "usage: epiphany-agent-telemetry [--output ]" - )); - }; - let mut output = None; - while let Some(arg) = args.next() { - match arg.as_str() { - "--output" => { - output = Some(PathBuf::from( - args.next() - .ok_or_else(|| anyhow!("--output requires a path"))?, - )); - } - _ => return Err(anyhow!("unknown argument: {arg}")), - } - } - Ok(Args { - transcript: PathBuf::from(transcript), - output, - }) -} - -fn build_telemetry(transcript_path: &Path) -> Result { - if !transcript_path.exists() { - return Ok(json!({ - "transcriptPath": transcript_path, - "generatedAt": Utc::now().to_rfc3339(), - "status": "missing", - "events": [], - "counts": {}, - })); - } - - let raw = fs::read_to_string(transcript_path) - .with_context(|| format!("failed to read {}", transcript_path.display()))?; - let mut events = Vec::new(); - let mut method_counts: BTreeMap = BTreeMap::new(); - let mut direction_counts: BTreeMap = BTreeMap::new(); - let mut function_counts: BTreeMap = BTreeMap::new(); - let mut decode_errors = Vec::new(); - - for (index, line) in raw.lines().enumerate() { - if line.trim().is_empty() { - continue; - } - let record: Value = match serde_json::from_str(line) { - Ok(value) => value, - Err(error) => { - decode_errors.push(json!({ - "index": index, - "error": error.to_string(), - "chars": line.chars().count(), - })); - continue; - } - }; - let event = match record { - Value::Object(map) => telemetry_event(index, &map), - other => json!({ - "index": index, - "payload": sealed_summary("record", &other), - }), - }; - let direction = event - .get("direction") - .and_then(Value::as_str) - .unwrap_or("unknown") - .to_string(); - *direction_counts.entry(direction).or_default() += 1; - if let Some(method) = event.get("method").and_then(Value::as_str) { - *method_counts.entry(method.to_string()).or_default() += 1; - } - if let Some(names) = event.get("functionNames").and_then(Value::as_array) { - for name in names.iter().filter_map(Value::as_str) { - *function_counts.entry(name.to_string()).or_default() += 1; - } - } - events.push(event); - } - - let mut request_shape: BTreeMap> = BTreeMap::new(); - for event in &events { - let Some(event_id) = event.get("id") else { - continue; - }; - let Some(direction) = event.get("direction").and_then(Value::as_str) else { - continue; - }; - if direction != "sent" && direction != "received" { - continue; - } - let id = value_key(event_id); - let entry = request_shape.entry(id).or_insert_with(|| { - BTreeMap::from([("sent".to_string(), 0), ("received".to_string(), 0)]) - }); - *entry.entry(direction.to_string()).or_default() += 1; - } - - Ok(json!({ - "transcriptPath": transcript_path, - "generatedAt": Utc::now().to_rfc3339(), - "status": "ok", - "policy": { - "directThoughtsSealed": true, - "note": "Telemetry summarizes function/API shape only; text, raw results, and transcript payloads are sealed.", - }, - "counts": { - "events": events.len(), - "directions": direction_counts, - "methods": method_counts, - "functionNames": function_counts, - "decodeErrors": decode_errors.len(), - }, - "requestShape": request_shape, - "decodeErrors": decode_errors, - "events": events, - })) -} - -fn telemetry_event(index: usize, record: &Map) -> Value { - let (direction, payload) = if let Some(sent) = record.get("sent") { - ("sent", sent) - } else if let Some(received) = record.get("received") { - ("received", received) - } else { - ("unknown", &Value::Object(record.clone())) - }; - - let mut event = Map::new(); - event.insert("index".to_string(), json!(index)); - event.insert("direction".to_string(), json!(direction)); - - let Some(payload) = payload.as_object() else { - event.insert("payload".to_string(), sealed_summary("payload", payload)); - return Value::Object(event); - }; - - for key in ["id", "method"] { - if let Some(value) = payload.get(key) { - event.insert(key.to_string(), value.clone()); - } - } - if let Some(error) = payload.get("error") { - event.insert("error".to_string(), summarize_value(error, "")); - } - if let Some(params) = payload.get("params") { - event.insert("params".to_string(), summarize_value(params, "params")); - } - if let Some(result) = payload.get("result") { - event.insert( - "result".to_string(), - summarize_value(result, "responseResult"), - ); - } - - let command_telemetry = collect_command_telemetry(&Value::Object(payload.clone())); - if !command_telemetry.is_empty() { - event.insert( - "commandTelemetry".to_string(), - Value::Array(command_telemetry.iter().take(16).cloned().collect()), - ); - event.insert( - "commandTelemetryCount".to_string(), - json!(command_telemetry.len()), - ); - } - - let names = collect_strings( - &Value::Object(payload.clone()), - &["toolName", "tool", "functionName", "name"], - ); - if !names.is_empty() { - event.insert( - "functionNames".to_string(), - Value::Array(names.iter().take(16).map(|s| json!(s)).collect()), - ); - event.insert("functionNameCount".to_string(), json!(names.len())); - } - - let paths = collect_strings( - &Value::Object(payload.clone()), - &["path", "cwd", "artifactPath"], - ); - if !paths.is_empty() { - event.insert( - "paths".to_string(), - Value::Array(paths.iter().take(16).map(|s| json!(s)).collect()), - ); - event.insert("pathCount".to_string(), json!(paths.len())); - } - - Value::Object(event) -} - -fn summarize_value(value: &Value, key: &str) -> Value { - if let Some(scalar) = scalar_summary(key, value) { - return scalar; - } - if contains(TEXTLIKE_KEYS, key) { - return sealed_summary(key, value); - } - match value { - Value::Array(items) => json!({ - "kind": "list", - "count": items.len(), - "items": items.iter().take(8).map(|item| summarize_value(item, "")).collect::>(), - "truncated": items.len() > 8, - }), - Value::Object(map) => { - let mut result = Map::new(); - for (child_key, child_value) in map { - let summarized = - if contains(TEXTLIKE_KEYS, child_key) && !contains(SAFE_KEYS, child_key) { - sealed_summary(child_key, child_value) - } else if contains(SAFE_KEYS, child_key) - || child_value.is_object() - || child_value.is_array() - { - summarize_value(child_value, child_key) - } else if let Some(text) = child_value.as_str() { - json!({"sealed": true, "kind": "text", "chars": text.chars().count()}) - } else { - child_value.clone() - }; - result.insert(child_key.clone(), summarized); - } - Value::Object(result) - } - other => json!({"sealed": true, "kind": value_kind(other)}), - } -} - -fn scalar_summary(key: &str, value: &Value) -> Option { - match value { - Value::Null | Value::Bool(_) | Value::Number(_) => Some(value.clone()), - Value::String(text) if contains(SAFE_KEYS, key) => Some(json!(text)), - Value::String(text) => { - Some(json!({"sealed": true, "kind": "text", "chars": text.chars().count()})) - } - _ => None, - } -} - -fn sealed_summary(key: &str, value: &Value) -> Value { - let mut summary = Map::new(); - summary.insert("sealed".to_string(), json!(true)); - summary.insert("key".to_string(), json!(key)); - summary.insert( - "reason".to_string(), - json!("direct agent or transcript content is excluded from telemetry"), - ); - match value { - Value::String(text) => { - summary.insert("chars".to_string(), json!(text.chars().count())); - } - Value::Array(items) => { - summary.insert("items".to_string(), json!(items.len())); - } - Value::Object(map) => { - let mut keys: Vec<_> = map.keys().map(String::as_str).collect(); - keys.sort_unstable(); - summary.insert( - "keys".to_string(), - json!(keys.into_iter().take(24).collect::>()), - ); - summary.insert("keyCount".to_string(), json!(map.len())); - } - _ => {} - } - Value::Object(summary) -} - -fn collect_strings(value: &Value, key_names: &[&str]) -> Vec { - let mut found = BTreeSet::new(); - collect_strings_into(value, key_names, &mut found); - found.into_iter().collect() -} - -fn collect_strings_into(value: &Value, key_names: &[&str], found: &mut BTreeSet) { - match value { - Value::Object(map) => { - for (key, item) in map { - if contains(key_names, key) { - if let Some(text) = item.as_str() { - found.insert(text.to_string()); - } - } else if item.is_object() || item.is_array() { - collect_strings_into(item, key_names, found); - } - } - } - Value::Array(items) => { - for item in items { - collect_strings_into(item, key_names, found); - } - } - _ => {} - } -} - -fn collect_command_telemetry(value: &Value) -> Vec { - let mut found = Vec::new(); - collect_command_telemetry_into(value, &mut found); - found -} - -fn collect_command_telemetry_into(value: &Value, found: &mut Vec) { - match value { - Value::Object(map) => { - if map.get("type").and_then(Value::as_str) == Some("commandExecution") - && let Some(command) = map.get("command").and_then(Value::as_str) - { - let mut info = summarize_command_text(command); - if let Some(object) = info.as_object_mut() { - object.insert( - "cwd".to_string(), - map.get("cwd").cloned().unwrap_or(Value::Null), - ); - object.insert( - "status".to_string(), - map.get("status").cloned().unwrap_or(Value::Null), - ); - object.insert( - "exitCode".to_string(), - map.get("exitCode").cloned().unwrap_or(Value::Null), - ); - object.insert( - "durationMs".to_string(), - map.get("durationMs").cloned().unwrap_or(Value::Null), - ); - } - found.push(info); - } - for item in map - .values() - .filter(|item| item.is_object() || item.is_array()) - { - collect_command_telemetry_into(item, found); - } - } - Value::Array(items) => { - for item in items { - collect_command_telemetry_into(item, found); - } - } - _ => {} - } -} - -fn summarize_command_text(command: &str) -> Value { - let compact = command - .lines() - .map(str::trim) - .filter(|line| !line.is_empty()) - .collect::>() - .join(" "); - let verbs = COMMAND_VERBS - .iter() - .filter(|verb| contains_token(command, verb)) - .copied() - .collect::>(); - let has_write_verb = verbs.iter().any(|verb| { - ["New-Item", "Set-Content", "apply_patch"] - .iter() - .any(|write| verb.eq_ignore_ascii_case(write)) - }); - json!({ - "chars": command.chars().count(), - "lines": std::cmp::max(command.lines().count(), 1), - "preview": compact.chars().take(240).collect::(), - "truncated": compact.chars().count() > 240, - "verbs": verbs, - "hasWriteVerb": has_write_verb, - }) -} - -fn contains_token(haystack: &str, needle: &str) -> bool { - let haystack = haystack.to_lowercase(); - let needle = needle.to_lowercase(); - let bytes = haystack.as_bytes(); - let mut start = 0; - while let Some(offset) = haystack[start..].find(&needle) { - let idx = start + offset; - let end = idx + needle.len(); - let before_ok = idx == 0 || !is_word(bytes[idx - 1] as char); - let after_ok = end >= bytes.len() || !is_word(bytes[end] as char); - if before_ok && after_ok { - return true; - } - start = end; - } - false -} - -fn value_key(value: &Value) -> String { - value - .as_str() - .map(ToString::to_string) - .unwrap_or_else(|| value.to_string()) -} - -fn value_kind(value: &Value) -> &'static str { - match value { - Value::Null => "null", - Value::Bool(_) => "bool", - Value::Number(_) => "number", - Value::String(_) => "string", - Value::Array(_) => "list", - Value::Object(_) => "dict", - } -} - -fn contains(keys: &[&str], key: &str) -> bool { - keys.iter().any(|candidate| candidate == &key) -} - -fn is_word(ch: char) -> bool { - ch.is_ascii_alphanumeric() || ch == '_' || ch == '-' -} diff --git a/epiphany-core/src/bin/epiphany-character-loop.rs b/epiphany-core/src/bin/epiphany-character-loop.rs deleted file mode 100644 index 5adae9d6..00000000 --- a/epiphany-core/src/bin/epiphany-character-loop.rs +++ /dev/null @@ -1,918 +0,0 @@ -use anyhow::Context; -use anyhow::Result; -use anyhow::anyhow; -use chrono::SecondsFormat; -use epiphany_core::EpiphanyAgentMemoryEntry; -use epiphany_core::GhostlightPerceivedStateOverlay; -use epiphany_core::GhostlightTraitVector; -use epiphany_core::HeartbeatAgentReaction; -use epiphany_core::HeartbeatAgentThoughtAppraisal; -use epiphany_core::HeartbeatAppraisalReview; -use epiphany_core::HeartbeatCandidateImplications; -use epiphany_core::HeartbeatEmotionalAppraisal; -use epiphany_core::HeartbeatParticipantLocalContext; -use epiphany_core::HeartbeatPersonalityProjection; -use epiphany_core::derive_agent_utterance_state; -use epiphany_core::load_agent_memory_entry_for_role; -use epiphany_core::organ_state_profile_for_role; -use serde::Serialize; -use serde_json::Value; -use std::collections::BTreeMap; -use std::collections::BTreeSet; -use std::env; -use std::fs; -use std::io::Read; -use std::path::Path; -use std::path::PathBuf; -use uuid::Uuid; - -const CHARACTER_TURN_SCHEMA_VERSION: &str = "epiphany.character_turn_packet.v0"; - -fn main() -> Result<()> { - let mut args = env::args().skip(1); - let Some(command) = args.next() else { - return usage(); - }; - let mut role = "Persona".to_string(); - let mut agent_store = PathBuf::from("state/agents.msgpack"); - let mut artifact_dir = PathBuf::from(".epiphany-character-loop"); - let mut stimulus: Option = None; - let mut source = "epiphany/character-loop".to_string(); - let mut mode = "public-surface".to_string(); - let mut mood = "attentive".to_string(); - - while let Some(arg) = args.next() { - match arg.as_str() { - "--role" => role = next_value(&mut args, "--role")?, - "--agent-store" => agent_store = next_path(&mut args, "--agent-store")?, - "--artifact-dir" => artifact_dir = next_path(&mut args, "--artifact-dir")?, - "--stimulus" => stimulus = Some(next_value(&mut args, "--stimulus")?), - "--source" => source = next_value(&mut args, "--source")?, - "--mode" => mode = next_value(&mut args, "--mode")?, - "--mood" => mood = next_value(&mut args, "--mood")?, - _ => return Err(anyhow!("unknown argument {arg:?}")), - } - } - - let result = match command.as_str() { - "turn" => { - let stimulus = read_required_text(stimulus, "stimulus")?; - run_turn( - &role, - &agent_store, - &artifact_dir, - &stimulus, - &source, - &mode, - &mood, - )? - } - "smoke" => run_smoke()?, - _ => return usage(), - }; - - let ok = result["ok"].as_bool().unwrap_or(false); - println!("{}", serde_json::to_string_pretty(&result)?); - if !ok { - std::process::exit(1); - } - Ok(()) -} - -fn run_turn( - role: &str, - agent_store: &Path, - artifact_dir: &Path, - stimulus: &str, - source: &str, - mode: &str, - mood: &str, -) -> Result { - if role.trim().is_empty() { - return Err(anyhow!("--role must not be empty")); - } - ensure_content(stimulus, "stimulus")?; - let entry = load_agent_memory_entry_for_role(agent_store, role)?.ok_or_else(|| { - anyhow!( - "{role} agent memory is missing from {}", - agent_store.display() - ) - })?; - let packet = character_turn_packet(&entry, stimulus, source, mode, mood); - let path = artifact_dir.join(format!( - "character-turn-{}-{}-{}.json", - sanitize_file_stem(role), - now_stamp(), - short_id() - )); - write_json(&path, &packet)?; - Ok(serde_json::json!({ - "ok": true, - "schemaVersion": CHARACTER_TURN_SCHEMA_VERSION, - "roleId": role, - "agentId": entry.agent.agent_id, - "turnPath": path, - "turn": packet, - })) -} - -fn character_turn_packet( - entry: &EpiphanyAgentMemoryEntry, - stimulus: &str, - source: &str, - mode: &str, - mood: &str, -) -> Value { - let organ_state_profile = organ_state_profile_for_role(&entry.role_id); - let utterance_state = derive_agent_utterance_state(entry, None, Some(mood), source); - let projection_seed = deterministic_projection_seed(entry, stimulus, source, mode, mood); - let appraisal_seed = deterministic_appraisal_seed(entry, &projection_seed, stimulus, source); - let reaction_seed = deterministic_reaction_seed(&entry.role_id, &appraisal_seed); - serde_json::json!({ - "schemaVersion": CHARACTER_TURN_SCHEMA_VERSION, - "protocol": { - "bundle": "epiphany.character_loop", - "referenceLineage": "Ghostlight-style character-local cognition, rebuilt as Epiphany-native protocol.", - "mode": mode, - "roleId": entry.role_id, - "agentId": entry.agent.agent_id, - "organStateProfile": organ_state_profile, - "turnKind": "stimulus_response", - "responseContract": "Respond from projected local state and visible stimulus; do not leak hidden agent streams or invent omniscient truth.", - }, - "source": source, - "createdAt": now_iso(), - "stimulus": { - "kind": "operator_or_aquarium_input", - "content": stimulus, - "status": "received", - "mood": mood, - }, - "utteranceState": utterance_state, - "projectedLocalContext": { - "identity": { - "name": entry.agent.identity.name, - "roles": entry.agent.identity.roles, - "origin": entry.agent.identity.origin, - "publicDescription": entry.agent.identity.public_description, - }, - "world": { - "worldId": entry.world.world_id, - "setting": entry.world.setting, - "time": entry.world.time.label, - "canonContext": entry.world.canon_context, - }, - "organStateProfile": organ_state_profile_for_role(&entry.role_id), - "activeGoals": entry.agent.goals.iter().map(|goal| serde_json::json!({ - "goalId": goal.goal_id, - "description": goal.description, - "scope": goal.scope, - "priority": goal.priority, - "emotionalStake": goal.emotional_stake, - "status": goal.status, - })).collect::>(), - "values": entry.agent.canonical_state.values.iter().map(|value| serde_json::json!({ - "valueId": value.value_id, - "label": value.label, - "priority": value.priority, - "unforgivableIfBetrayed": value.unforgivable_if_betrayed, - })).collect::>(), - "perceivedStateOverlays": entry.agent.perceived_state_overlays.iter().take(6).map(perceived_overlay_projection).collect::>(), - "privateNoteCount": entry.agent.identity.private_notes.len(), - }, - "projectionSeed": projection_seed, - "appraisalSeed": appraisal_seed, - "reactionSeed": reaction_seed, - "allowedOutputs": [ - "bubble", - "discordAquariumDraft", - "roleFinding", - "silence", - "selfPatch" - ], - "cognitionLanes": { - "schema_version": "epiphany.cognition_lanes.v0", - "analytic": { - "description": "Literal, evidence-facing lane: what happened, what constraints apply, what action is justified.", - "requiredThreadShape": { - "topic": "string", - "claim": "string", - "evidenceRefs": "string[]", - "confidence": "0..1", - "counterweight": "string" - } - }, - "associative": { - "description": "Pattern-facing lane: what this rhymes with, what older seam or emotional salience may matter, what should incubate.", - "requiredThreadShape": { - "topic": "string", - "claim": "string", - "sourceThemeIds": "string[]", - "novelty": "0..1", - "counterweight": "string" - } - }, - "bridge": { - "schema_version": "epiphany.cognition_bridge.v0", - "contract": "Synthesize or separate the lanes, name saturation and unresolved tension, then choose bubble, draft, finding, silence, or selfPatch. The bridge may steer attention; it may not mutate project truth." - }, - "appraisal": { - "schema_version": "epiphany.agent_thought_appraisal.v0", - "contract": "Project the active thought cluster through this actor's own personality vectors before reacting. Interpretation, emotion, and reaction are participant-local; state mutation still requires reviewed selfPatch or project-state acceptance." - } - }, - "guardrails": [ - "Humans talk to Persona; other organs expose internals through Aquarium and typed artifacts rather than becoming direct chats.", - "Use character-local projection: the actor receives its own organ or Persona state and visible stimulus, not omniscient hidden state.", - "Run both cognition lanes before choosing an output: analytic keeps the promise honest, associative keeps the living signal from going flat.", - "Appraise the thought cluster through this role's personality before reacting; do not use a global mood knob as a substitute for participant-local appraisal.", - "If speech or action would be noise, return silence plus bounded rumination or selfPatch.", - "Posting, state mutation, objective adoption, and semantic acceptance remain separate reviewed Epiphany surfaces." - ], - "resultShape": { - "observedStimulus": "string", - "localInterpretation": "string", - "candidateBubble": "string|null", - "candidateDraft": "string|null", - "analyticThread": "object", - "associativeThread": "object", - "bridgeSynthesis": "object", - "appraisal": "object", - "reaction": "object", - "decision": "bubble|draft|finding|silence|blocked", - "selfPatch": "optional bounded role memory mutation" - } - }) -} - -#[derive(Clone, Debug)] -struct TraitSignal { - group: &'static str, - name: String, - activation: f64, - plasticity: f64, - weight: f64, -} - -#[derive(Clone, Debug, Serialize)] -#[serde(rename_all = "camelCase")] -struct CharacterProjectionSeed { - #[serde(rename = "schema_version")] - schema_version: String, - participant_agent_id: String, - role_id: String, - thought_cluster_ref: String, - thought_tokens: Vec, - dominant_trait_matches: Vec, - relationship_pressure: CharacterPressureSeed, - perceived_overlay_pressure: CharacterPressureSeed, - contract: String, -} - -#[derive(Clone, Debug, Serialize)] -#[serde(rename_all = "camelCase")] -struct CharacterPressureSeed { - strength: f64, - summary: String, -} - -#[derive(Clone, Debug, Serialize)] -#[serde(rename_all = "camelCase")] -struct CharacterReactionSeed { - #[serde(rename = "schema_version")] - schema_version: String, - #[serde(flatten)] - reaction: HeartbeatAgentReaction, - contract: String, -} - -fn deterministic_projection_seed( - entry: &EpiphanyAgentMemoryEntry, - stimulus: &str, - source: &str, - mode: &str, - mood: &str, -) -> CharacterProjectionSeed { - let thought_tokens = stimulus_token_set(stimulus, source, mode, mood); - let traits = collect_trait_signals(entry); - let mut scored = traits - .iter() - .map(|item| { - let trait_tokens = summary_tokens(&format!("{} {}", item.group, item.name)); - let overlap = token_overlap(&trait_tokens, &thought_tokens); - let projection = round3((item.weight * (0.55 + overlap * 1.8)).clamp(0.0, 1.0)); - HeartbeatPersonalityProjection { - group: item.group.to_string(), - name: item.name.clone(), - activation: round3(item.activation), - plasticity: round3(item.plasticity), - token_overlap: round3(overlap), - projection, - } - }) - .collect::>(); - scored.sort_by(|left, right| right.projection.total_cmp(&left.projection)); - scored.truncate(6); - - let (relationship_pressure, relationship_summary) = - strongest_relationship_pressure(entry, &thought_tokens); - let (overlay_pressure, overlay_summary) = strongest_overlay_pressure(entry, &thought_tokens); - - CharacterProjectionSeed { - schema_version: "epiphany.personality_projection.v0".to_string(), - participant_agent_id: entry.agent.agent_id.clone(), - role_id: entry.role_id.clone(), - thought_cluster_ref: format!("{}#stimulus", source), - thought_tokens: thought_tokens.into_iter().collect::>(), - dominant_trait_matches: scored, - relationship_pressure: CharacterPressureSeed { - strength: round3(relationship_pressure), - summary: relationship_summary, - }, - perceived_overlay_pressure: CharacterPressureSeed { - strength: round3(overlay_pressure), - summary: overlay_summary, - }, - contract: - "Projection is participant-local and fallible. It is a seed for embodied response, not reviewed project truth." - .to_string(), - } -} - -fn deterministic_appraisal_seed( - entry: &EpiphanyAgentMemoryEntry, - projection_seed: &CharacterProjectionSeed, - stimulus: &str, - source: &str, -) -> HeartbeatAgentThoughtAppraisal { - let traits = collect_trait_signals(entry); - let reactivity = average(traits.iter().take(8).map(|item| item.activation)).unwrap_or(0.5); - let plasticity = average(traits.iter().take(8).map(|item| item.plasticity)).unwrap_or(0.5); - let expressiveness = average( - traits - .iter() - .filter(|item| item.group == "voice_style" || item.group == "presentation_strategy") - .map(|item| item.activation), - ) - .unwrap_or(reactivity); - let guarded_baseline = average( - traits - .iter() - .filter(|item| { - let name = item.name.as_str(); - name.contains("guard") - || name.contains("shame") - || name.contains("risk") - || name.contains("caution") - || name.contains("contingent") - || name.contains("defens") - || name.contains("susp") - }) - .map(|item| item.activation), - ) - .unwrap_or((1.0 - expressiveness * 0.45).clamp(0.0, 1.0)); - let alignment = projection_seed - .dominant_trait_matches - .first() - .map(|item| item.projection) - .unwrap_or(reactivity); - let relationship_pressure = projection_seed.relationship_pressure.strength; - let overlay_pressure = projection_seed.perceived_overlay_pressure.strength; - let thought_pressure = (0.18 - + (summary_tokens(stimulus).len() as f64 / 18.0).min(0.42) - + relationship_pressure * 0.22 - + overlay_pressure * 0.18) - .clamp(0.0, 1.0); - let arousal = - round3((thought_pressure * (0.35 + reactivity * 0.45 + plasticity * 0.2)).clamp(0.0, 1.0)); - let guardedness = round3( - (guarded_baseline * 0.58 - + relationship_pressure * 0.18 - + overlay_pressure * 0.14 - + (1.0 - alignment) * 0.10) - .clamp(0.0, 1.0), - ); - let curiosity = - round3(((1.0 - guardedness) * 0.22 + alignment * 0.46 + plasticity * 0.32).clamp(0.0, 1.0)); - let urgency = - round3((arousal * 0.58 + guardedness * 0.22 + thought_pressure * 0.20).clamp(0.0, 1.0)); - let valence = round3((0.52 + curiosity * 0.22 - guardedness * 0.20).clamp(0.0, 1.0)); - let label = interpretation_label("draft", arousal, guardedness, curiosity); - HeartbeatAgentThoughtAppraisal { - schema_version: "epiphany.agent_thought_appraisal.v0".to_string(), - appraisal_id: format!("Persona-appraisal-{}", short_id()), - review_status: "generated_unreviewed".to_string(), - participant_agent_id: entry.agent.agent_id.clone(), - role_id: entry.role_id.clone(), - current_character_state_ref: format!("state/agents.msgpack#{}", entry.role_id), - thought_cluster_ref: format!("{}#stimulus", source), - participant_local_context: HeartbeatParticipantLocalContext { - display_name: entry.agent.identity.name.clone(), - values: entry - .agent - .canonical_state - .values - .iter() - .take(5) - .map(|value| value.label.clone()) - .collect::>(), - reactivity: round3(reactivity), - plasticity: round3(plasticity), - expressiveness: round3(expressiveness), - guardedness: round3(guarded_baseline), - }, - observable_thought_summary: stimulus.to_string(), - personality_projection: projection_seed.dominant_trait_matches.clone(), - interpretation: format!( - "{} appraises the visible stimulus through its local personality, relationship memory, and perceived overlays; this is embodied response guidance, not omniscient truth.", - entry.agent.identity.name - ), - emotional_appraisal: HeartbeatEmotionalAppraisal { - valence, - arousal, - urgency, - curiosity, - guardedness, - thought_pressure: round3(thought_pressure), - }, - interpretation_label: label.clone(), - confidence_notes: "Deterministic Persona-local appraisal rebuilt from the same Ghostlight-style personality projection logic used by heartbeat. Useful for reaction shape, still reviewable and fallible.".to_string(), - candidate_implications: HeartbeatCandidateImplications { - reaction_mode: reaction_mode(&label, "draft").to_string(), - reaction_intensity: round3( - (urgency * 0.55 + arousal * 0.3 + curiosity * 0.15).clamp(0.0, 1.0), - ), - should_speak: entry.role_id == "Persona" && guardedness < 0.78, - should_incubate: guardedness >= 0.55, - }, - review: HeartbeatAppraisalReview { - accepted_for_mutation: false, - rationale: - "Appraisal may steer expression and reaction; state mutation still requires explicit review." - .to_string(), - }, - } -} - -fn deterministic_reaction_seed( - role_id: &str, - appraisal_seed: &HeartbeatAgentThoughtAppraisal, -) -> CharacterReactionSeed { - let arousal = appraisal_seed.emotional_appraisal.arousal; - let guardedness = appraisal_seed.emotional_appraisal.guardedness; - let curiosity = appraisal_seed.emotional_appraisal.curiosity; - let mode = appraisal_seed.candidate_implications.reaction_mode.as_str(); - let intensity = appraisal_seed.candidate_implications.reaction_intensity; - CharacterReactionSeed { - schema_version: "epiphany.agent_reaction.v0".to_string(), - reaction: HeartbeatAgentReaction { - reaction_id: format!("reaction-{}-{}", role_id, short_id()), - role_id: role_id.to_string(), - participant_agent_id: appraisal_seed.participant_agent_id.clone(), - appraisal_id: appraisal_seed.appraisal_id.clone(), - mode: mode.to_string(), - mood_label: mood_label(arousal, guardedness, curiosity).to_string(), - intensity: round3(intensity), - bridge_decision: "draft".to_string(), - surface: if role_id == "Persona" { - "aquarium" - } else { - "internal" - } - .to_string(), - recommended_use: reaction_recommended_use(role_id, mode).to_string(), - }, - contract: "Reaction is derived from local appraisal and may color speech or silence; it still does not mutate durable state by itself.".to_string(), - } -} - -fn collect_trait_signals(entry: &EpiphanyAgentMemoryEntry) -> Vec { - let mut traits = Vec::new(); - collect_trait_group( - &mut traits, - "underlying_organization", - &entry.agent.canonical_state.underlying_organization, - ); - collect_trait_group( - &mut traits, - "stable_dispositions", - &entry.agent.canonical_state.stable_dispositions, - ); - collect_trait_group( - &mut traits, - "behavioral_dimensions", - &entry.agent.canonical_state.behavioral_dimensions, - ); - collect_trait_group( - &mut traits, - "presentation_strategy", - &entry.agent.canonical_state.presentation_strategy, - ); - collect_trait_group( - &mut traits, - "voice_style", - &entry.agent.canonical_state.voice_style, - ); - collect_trait_group( - &mut traits, - "situational_state", - &entry.agent.canonical_state.situational_state, - ); - traits.sort_by(|left, right| right.weight.total_cmp(&left.weight)); - traits -} - -fn collect_trait_group( - traits: &mut Vec, - group: &'static str, - source: &BTreeMap, -) { - for (name, vector) in source { - let activation = vector.current_activation.clamp(0.0, 1.0); - let plasticity = vector.plasticity.clamp(0.0, 1.0); - traits.push(TraitSignal { - group, - name: name.clone(), - activation, - plasticity, - weight: round3(activation * (0.65 + plasticity * 0.35)), - }); - } -} - -fn strongest_relationship_pressure( - entry: &EpiphanyAgentMemoryEntry, - thought_tokens: &BTreeSet, -) -> (f64, String) { - entry - .agent - .memories - .relationship_summaries - .iter() - .map(|memory| { - let overlap = token_overlap(&summary_tokens(&memory.summary), thought_tokens); - let strength = overlap * (memory.salience * memory.confidence); - (strength, memory.summary.clone()) - }) - .max_by(|left, right| left.0.total_cmp(&right.0)) - .map(|(strength, summary)| (round3(strength.clamp(0.0, 1.0)), summary)) - .unwrap_or(( - 0.0, - "No strong relationship pressure is active.".to_string(), - )) -} - -fn strongest_overlay_pressure( - entry: &EpiphanyAgentMemoryEntry, - thought_tokens: &BTreeSet, -) -> (f64, String) { - entry - .agent - .perceived_state_overlays - .iter() - .map(|overlay| { - let tokens = overlay_tokens(overlay); - let overlap = token_overlap(&tokens, thought_tokens); - let summary = overlay_summary(overlay); - (overlap, summary) - }) - .max_by(|left, right| left.0.total_cmp(&right.0)) - .map(|(strength, summary)| (round3(strength.clamp(0.0, 1.0)), summary)) - .unwrap_or(( - 0.0, - "No perceived overlay is tugging on this stimulus.".to_string(), - )) -} - -fn stimulus_token_set(stimulus: &str, source: &str, mode: &str, mood: &str) -> BTreeSet { - let mut tokens = BTreeSet::new(); - tokens.extend(summary_tokens(stimulus)); - tokens.extend(summary_tokens(source)); - tokens.extend(summary_tokens(mode)); - tokens.extend(summary_tokens(mood)); - tokens -} - -fn perceived_overlay_projection(overlay: &GhostlightPerceivedStateOverlay) -> Value { - serde_json::json!({ - "overlayId": overlay.overlay_id, - "label": overlay.label, - "summary": overlay.summary, - "source": overlay.source, - "salience": overlay.salience, - "confidence": overlay.confidence, - "linkedMemoryIds": overlay.linked_memory_ids, - }) -} - -fn overlay_tokens(overlay: &GhostlightPerceivedStateOverlay) -> BTreeSet { - let mut tokens = BTreeSet::new(); - tokens.extend(summary_tokens(&overlay.overlay_id)); - tokens.extend(summary_tokens(&overlay.label)); - tokens.extend(summary_tokens(&overlay.summary)); - tokens.extend(summary_tokens(&overlay.source)); - for memory_id in &overlay.linked_memory_ids { - tokens.extend(summary_tokens(memory_id)); - } - tokens -} - -fn overlay_summary(overlay: &GhostlightPerceivedStateOverlay) -> String { - if !overlay.summary.trim().is_empty() { - overlay.summary.clone() - } else if !overlay.label.trim().is_empty() { - overlay.label.clone() - } else { - "Perceived overlay relevance".to_string() - } -} - -fn summary_tokens(text: &str) -> BTreeSet { - text.split(|ch: char| !ch.is_ascii_alphanumeric() && ch != '_' && ch != '-') - .filter_map(|item| { - let token = item.trim().to_ascii_lowercase().replace('_', "-"); - (token.len() >= 3).then_some(token) - }) - .collect() -} - -fn token_overlap(left: &BTreeSet, right: &BTreeSet) -> f64 { - if left.is_empty() || right.is_empty() { - return 0.0; - } - let shared = left.intersection(right).count() as f64; - let denom = left.len().min(right.len()) as f64; - if denom <= f64::EPSILON { - 0.0 - } else { - (shared / denom).clamp(0.0, 1.0) - } -} - -fn average(values: I) -> Option -where - I: Iterator, -{ - let mut total = 0.0_f64; - let mut count = 0_usize; - for value in values { - total += value; - count += 1; - } - (count > 0).then_some(total / count as f64) -} - -fn interpretation_label( - bridge_decision: &str, - arousal: f64, - guardedness: f64, - curiosity: f64, -) -> String { - if guardedness > 0.72 && arousal > 0.35 { - "protective_appraisal".to_string() - } else if curiosity > 0.68 { - "investigative_appraisal".to_string() - } else if bridge_decision == "draft" { - "expressive_appraisal".to_string() - } else if arousal < 0.12 { - "low_pressure_appraisal".to_string() - } else { - "incubating_appraisal".to_string() - } -} - -fn reaction_mode(label: &str, bridge_decision: &str) -> &'static str { - match (label, bridge_decision) { - ("protective_appraisal", _) => "hold_and_verify", - ("investigative_appraisal", _) => "inspect", - ("expressive_appraisal", "draft") => "draft", - ("low_pressure_appraisal", _) => "sleep_ruminate", - _ => "incubate", - } -} - -fn mood_label(arousal: f64, guardedness: f64, curiosity: f64) -> &'static str { - if guardedness > 0.72 && arousal > 0.35 { - "wary" - } else if curiosity > 0.68 && arousal > 0.25 { - "keen" - } else if arousal < 0.12 { - "drowsy" - } else if guardedness > curiosity { - "watchful" - } else { - "interested" - } -} - -fn reaction_recommended_use(role_id: &str, mode: &str) -> &'static str { - match (role_id, mode) { - ("Persona", "draft") => { - "Prepare a reviewed Aquarium-facing draft; do not post automatically." - } - (_, "hold_and_verify") => "Bias toward verifier/modeler review before expression.", - (_, "inspect") => { - "Bias the next heartbeat toward a bounded retrieval or modeling inspection." - } - (_, "sleep_ruminate") => "Let this organ sleep-ruminate unless real work arrives.", - _ => "Keep the thought incubating and visible in Aquarium.", - } -} - -fn round3(value: f64) -> f64 { - (value.clamp(0.0, 1.0) * 1000.0).round() / 1000.0 -} - -fn run_smoke() -> Result { - let temp_dir = scoped_temp_dir("epiphany-character-loop-smoke")?; - let artifact_dir = temp_dir.join("artifacts"); - let result = run_turn( - "Persona", - Path::new("state/agents.msgpack"), - &artifact_dir, - "Aquarium hover selected Persona and asked what the swarm is feeling.", - "smoke/character-loop", - "public-surface", - "attentive", - )?; - let path = PathBuf::from( - result["turnPath"] - .as_str() - .ok_or_else(|| anyhow!("smoke result missing turnPath"))?, - ); - let packet: Value = serde_json::from_str( - &fs::read_to_string(&path) - .with_context(|| format!("failed to read smoke artifact {}", path.display()))?, - )?; - const EXPECTED_PACKET_FIELDS: [&str; 14] = [ - "allowedOutputs", - "appraisalSeed", - "cognitionLanes", - "createdAt", - "guardrails", - "projectedLocalContext", - "projectionSeed", - "protocol", - "reactionSeed", - "resultShape", - "schemaVersion", - "source", - "stimulus", - "utteranceState", - ]; - let ok = packet.as_object().is_some_and(|payload| { - payload.len() == EXPECTED_PACKET_FIELDS.len() - && EXPECTED_PACKET_FIELDS - .iter() - .all(|key| payload.contains_key(*key)) - }) && packet["schemaVersion"] == CHARACTER_TURN_SCHEMA_VERSION - && packet["protocol"]["bundle"] == "epiphany.character_loop" - && packet["protocol"]["roleId"] == "Persona" - && packet["protocol"]["organStateProfile"]["profileKind"] == "persona" - && packet["projectedLocalContext"]["organStateProfile"]["profileKind"] == "persona" - && packet["utteranceState"]["schemaVersion"] == "epiphany.agent_utterance_state.v0" - && packet["utteranceState"]["identity"]["name"] - == packet["projectedLocalContext"]["identity"]["name"] - && packet["utteranceState"]["personalityVectors"]["voiceStyle"].is_object() - && packet["utteranceState"]["characterStateVector"]["compatibleHandoffSchema"] - == "weksa.utterance_embedding_handoff.v0.1" - && packet["utteranceState"]["characterStateVector"]["values"] - .as_array() - .is_some_and(|values| values.len() == 64) - && packet["projectedLocalContext"] - .get("semanticMemories") - .is_none() - && packet["projectedLocalContext"] - .get("episodicMemories") - .is_none() - && packet["projectedLocalContext"] - .get("relationshipMemories") - .is_none() - && packet["projectionSeed"]["schema_version"] == "epiphany.personality_projection.v0" - && packet["appraisalSeed"]["schema_version"] == "epiphany.agent_thought_appraisal.v0" - && packet["reactionSeed"]["schema_version"] == "epiphany.agent_reaction.v0" - && packet["projectedLocalContext"]["identity"]["name"] - .as_str() - .is_some_and(|name| !name.trim().is_empty()) - && packet["stimulus"]["content"] - .as_str() - .is_some_and(|text| text.contains("Aquarium")) - && packet["stimulus"]["status"] == "received" - && packet["utteranceState"]["activation"]["status"] == "unknown" - && packet["cognitionLanes"]["schema_version"] == "epiphany.cognition_lanes.v0" - && packet["cognitionLanes"]["bridge"]["schema_version"] == "epiphany.cognition_bridge.v0" - && packet["cognitionLanes"]["appraisal"]["schema_version"] - == "epiphany.agent_thought_appraisal.v0"; - Ok(serde_json::json!({ - "ok": ok, - "turnPath": path, - "packet": { - "schemaVersion": packet["schemaVersion"], - "stimulusStatus": packet["stimulus"]["status"], - "activationStatus": packet["utteranceState"]["activation"]["status"], - "bundle": packet["protocol"]["bundle"], - "roleId": packet["protocol"]["roleId"], - "agentId": packet["protocol"]["agentId"], - "organStateProfile": packet["protocol"]["organStateProfile"]["profileKind"], - "projectionSchema": packet["projectionSeed"]["schema_version"], - "appraisalSchema": packet["appraisalSeed"]["schema_version"], - "reactionSchema": packet["reactionSeed"]["schema_version"], - "utteranceStateSchema": packet["utteranceState"]["schemaVersion"], - "identityName": packet["projectedLocalContext"]["identity"]["name"], - "allowedOutputs": packet["allowedOutputs"], - "cognitionLanes": packet["cognitionLanes"]["schema_version"], - } - })) -} - -fn read_required_text(content: Option, label: &str) -> Result { - let Some(content) = content else { - let mut stdin = String::new(); - std::io::stdin() - .read_to_string(&mut stdin) - .context("failed to read stdin")?; - ensure_content(&stdin, label)?; - return Ok(stdin); - }; - let path = PathBuf::from(&content); - if path.exists() { - let text = fs::read_to_string(&path) - .with_context(|| format!("failed to read content file {}", path.display()))?; - ensure_content(&text, label)?; - return Ok(text); - } - ensure_content(&content, label)?; - Ok(content) -} - -fn ensure_content(content: &str, label: &str) -> Result<()> { - if content.trim().is_empty() { - return Err(anyhow!("{label} is empty")); - } - Ok(()) -} - -fn next_value(args: &mut impl Iterator, flag: &str) -> Result { - args.next() - .ok_or_else(|| anyhow!("missing value after {flag}")) -} - -fn next_path(args: &mut impl Iterator, flag: &str) -> Result { - Ok(PathBuf::from(next_value(args, flag)?)) -} - -fn write_json(path: &Path, payload: &Value) -> Result<()> { - if let Some(parent) = path.parent() { - fs::create_dir_all(parent) - .with_context(|| format!("failed to create {}", parent.display()))?; - } - fs::write( - path, - format!("{}\n", serde_json::to_string_pretty(payload)?), - ) - .with_context(|| format!("failed to write {}", path.display())) -} - -fn now_iso() -> String { - chrono::Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true) -} - -fn now_stamp() -> String { - chrono::Utc::now().format("%Y%m%dT%H%M%SZ").to_string() -} - -fn short_id() -> String { - Uuid::new_v4() - .to_string() - .split('-') - .next() - .unwrap_or("id") - .to_string() -} - -fn sanitize_file_stem(value: &str) -> String { - value - .chars() - .map(|ch| { - if ch.is_ascii_alphanumeric() || ch == '-' || ch == '_' { - ch - } else { - '-' - } - }) - .collect::() - .trim_matches('-') - .to_string() -} - -fn scoped_temp_dir(prefix: &str) -> Result { - let dir = env::temp_dir().join(format!("{prefix}-{}", Uuid::new_v4())); - fs::create_dir(&dir).with_context(|| format!("failed to claim {}", dir.display()))?; - Ok(dir) -} - -fn usage() -> Result<()> { - Err(anyhow!( - "usage: epiphany-character-loop [--role ] [--agent-store ] [--artifact-dir ] [--stimulus ] [--source ] [--mode ]" - )) -} diff --git a/epiphany-core/src/bin/epiphany-cluster-daemon.rs b/epiphany-core/src/bin/epiphany-cluster-daemon.rs deleted file mode 100644 index f6e1b86c..00000000 --- a/epiphany-core/src/bin/epiphany-cluster-daemon.rs +++ /dev/null @@ -1,299 +0,0 @@ -use anyhow::Context; -use anyhow::Result; -use chrono::Utc; -use epiphany_core::EPIPHANY_CULTMESH_DAEMON_HEARTBEAT_EVENT_SCHEMA_VERSION; -use epiphany_core::EPIPHANY_CULTMESH_DAEMON_STATUS_SCHEMA_VERSION; -use epiphany_core::EpiphanyCultMeshDaemonHeartbeatEventEntry; -use epiphany_core::EpiphanyCultMeshDaemonStatusEntry; -use epiphany_core::load_epiphany_cultmesh_cluster_topology; -use epiphany_core::write_epiphany_cultmesh_daemon_heartbeat_event; -use epiphany_core::write_epiphany_cultmesh_daemon_status; -use serde_json::json; -use std::env; -use std::path::PathBuf; -use std::thread; -use std::time::Duration; -use uuid::Uuid; - -fn main() -> Result<()> { - let args = Args::parse()?; - match args.command.as_str() { - "heartbeat" | "status" | "once" => { - let written = write_heartbeat(&args, 0)?; - print_output(&args, "heartbeat", vec![written]) - } - "serve" | "loop" | "daemon" => serve(args), - other => anyhow::bail!("unknown command {other:?}; use heartbeat or serve"), - } -} - -fn serve(args: Args) -> Result<()> { - let mut written = Vec::new(); - let mut iteration = 0_u64; - loop { - written.push(write_heartbeat(&args, iteration)?); - iteration = iteration.saturating_add(1); - if args.max_iterations > 0 && iteration >= args.max_iterations { - break; - } - thread::sleep(Duration::from_secs(args.interval_seconds)); - } - print_output(&args, "serveComplete", written) -} - -fn write_heartbeat( - args: &Args, - iteration: u64, -) -> Result { - let topology = load_epiphany_cultmesh_cluster_topology(&args.store, args.runtime_id.clone())?; - let cluster = topology - .into_iter() - .find(|cluster| cluster.daemon_id == args.daemon_id) - .with_context(|| { - format!( - "local Verse has no persisted topology for {}; run explicit bootstrap before starting cluster daemons", - args.daemon_id - ) - })?; - let heartbeat_at = Utc::now().to_rfc3339(); - let heartbeat = new_heartbeat_event( - args, - &cluster.daemon_id, - &cluster.cluster_id, - iteration, - heartbeat_at, - ); - let written = write_epiphany_cultmesh_daemon_heartbeat_event( - &args.store, - args.runtime_id.clone(), - heartbeat, - )?; - - // Compatibility sight for readers not yet migrated to heartbeat events. It is - // derived after the provider-owned event and cannot mint heartbeat authority. - write_epiphany_cultmesh_daemon_status( - &args.store, - args.runtime_id.clone(), - EpiphanyCultMeshDaemonStatusEntry { - schema_version: EPIPHANY_CULTMESH_DAEMON_STATUS_SCHEMA_VERSION.to_string(), - daemon_id: cluster.daemon_id, - cluster_id: cluster.cluster_id, - body_domain: cluster.body_domain, - daemon_surface_id: cluster.daemon_surface_id, - eve_surface_id: cluster.eve_surface_id, - status: written.status.clone(), - last_heartbeat_utc: written.heartbeat_at.clone(), - supported_actions: vec![ - "inspectStatus".to_string(), - "pokeDaemon".to_string(), - "watchHeartbeat".to_string(), - ], - operator_action: "none".to_string(), - private_state_exposed: false, - notes: vec![ - "Display-only compatibility projection derived from the provider heartbeat event." - .to_string(), - format!("Source heartbeat id: {}", written.heartbeat_id), - ], - }, - )?; - Ok(written) -} - -fn new_heartbeat_event( - args: &Args, - daemon_id: &str, - cluster_id: &str, - iteration: u64, - heartbeat_at: String, -) -> EpiphanyCultMeshDaemonHeartbeatEventEntry { - EpiphanyCultMeshDaemonHeartbeatEventEntry { - schema_version: EPIPHANY_CULTMESH_DAEMON_HEARTBEAT_EVENT_SCHEMA_VERSION.to_string(), - heartbeat_id: Uuid::new_v4().to_string(), - daemon_id: daemon_id.to_string(), - cluster_id: cluster_id.to_string(), - provider_incarnation: args.provider_incarnation.clone(), - sequence: iteration.saturating_add(1), - status: "ready".to_string(), - heartbeat_at, - private_state_exposed: false, - startup_lifecycle_receipt_id: args.startup_lifecycle_receipt_id.clone(), - } -} - -fn print_output( - args: &Args, - status: &str, - written: Vec, -) -> Result<()> { - let latest = written - .last() - .context("cluster daemon wrote no heartbeat status")?; - println!( - "{}", - serde_json::to_string_pretty(&json!({ - "schemaVersion": "epiphany.cluster_daemon_run.v0", - "status": status, - "store": args.store, - "runtimeId": args.runtime_id, - "daemonId": latest.daemon_id, - "clusterId": latest.cluster_id, - "daemonStatus": latest.status, - "heartbeatId": latest.heartbeat_id, - "providerIncarnation": latest.provider_incarnation, - "sequence": latest.sequence, - "lastHeartbeatUtc": latest.heartbeat_at, - "heartbeatCount": written.len(), - "intervalSeconds": args.interval_seconds, - "maxIterations": args.max_iterations, - "privateStateExposed": false, - }))? - ); - Ok(()) -} - -struct Args { - command: String, - store: PathBuf, - runtime_id: String, - daemon_id: String, - interval_seconds: u64, - max_iterations: u64, - provider_incarnation: String, - startup_lifecycle_receipt_id: String, -} - -impl Args { - fn parse() -> Result { - let mut values = env::args().skip(1); - let command = values.next().unwrap_or_else(|| "heartbeat".to_string()); - let mut store = PathBuf::from(".epiphany-run/cultmesh/local-verse.ccmp"); - let mut runtime_id = "epiphany-local".to_string(); - let mut daemon_id = None; - let mut interval_seconds = 60_u64; - let mut max_iterations = 1_u64; - let mut provider_incarnation = None; - let mut startup_lifecycle_receipt_id = - env::var("EPIPHANY_STARTUP_LIFECYCLE_RECEIPT_ID").unwrap_or_default(); - - while let Some(arg) = values.next() { - match arg.as_str() { - "--store" => store = PathBuf::from(values.next().context("missing --store value")?), - "--runtime-id" => { - runtime_id = values.next().context("missing --runtime-id value")? - } - "--daemon-id" => { - daemon_id = Some(values.next().context("missing --daemon-id value")?) - } - "--interval-seconds" => { - interval_seconds = values - .next() - .context("missing --interval-seconds value")? - .parse()?; - } - "--max-iterations" => { - max_iterations = values - .next() - .context("missing --max-iterations value")? - .parse()?; - } - "--provider-incarnation" => { - provider_incarnation = Some( - values - .next() - .context("missing --provider-incarnation value")?, - ) - } - "--startup-lifecycle-receipt-id" => { - startup_lifecycle_receipt_id = values - .next() - .context("missing --startup-lifecycle-receipt-id value")? - } - other => anyhow::bail!("unknown argument {other:?}"), - } - } - - let daemon_id = daemon_id.context("cluster daemon requires --daemon-id")?; - Ok(Self { - command, - store, - runtime_id, - daemon_id, - interval_seconds, - max_iterations, - provider_incarnation: provider_incarnation - .unwrap_or_else(|| Uuid::new_v4().to_string()), - startup_lifecycle_receipt_id, - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use epiphany_core::query_epiphany_local_verse_context; - use epiphany_core::seed_epiphany_local_verse_context; - - #[test] - fn process_heartbeat_events_keep_one_incarnation_and_increment_sequence() { - let args = Args { - command: "serve".to_string(), - store: PathBuf::from("unused.ccmp"), - runtime_id: "runtime-test".to_string(), - daemon_id: "daemon-test".to_string(), - interval_seconds: 1, - max_iterations: 2, - provider_incarnation: "incarnation-test".to_string(), - startup_lifecycle_receipt_id: "receipt-test".to_string(), - }; - let first = new_heartbeat_event( - &args, - "daemon-test", - "cluster-test", - 0, - "2026-07-15T12:00:00Z".to_string(), - ); - let second = new_heartbeat_event( - &args, - "daemon-test", - "cluster-test", - 1, - "2026-07-15T12:00:01Z".to_string(), - ); - assert_eq!(first.provider_incarnation, second.provider_incarnation); - assert_eq!(first.sequence, 1); - assert_eq!(second.sequence, 2); - assert_ne!(first.heartbeat_id, second.heartbeat_id); - assert_eq!(first.startup_lifecycle_receipt_id, "receipt-test"); - } - - #[test] - fn heartbeat_writes_liveness_without_minting_provider_truth() -> Result<()> { - let temp = tempfile::tempdir()?; - let store = temp.path().join("heartbeat-only.ccmp"); - seed_epiphany_local_verse_context( - &store, - "runtime-test", - "2026-07-15T12:00:00Z", - "repo:C:/fixture/Epiphany", - )?; - let args = Args { - command: "heartbeat".to_string(), - store: store.clone(), - runtime_id: "runtime-test".to_string(), - daemon_id: "epiphany-daemon-hands".to_string(), - interval_seconds: 1, - max_iterations: 1, - provider_incarnation: "incarnation-test".to_string(), - startup_lifecycle_receipt_id: "receipt-test".to_string(), - }; - - write_heartbeat(&args, 0)?; - let context = query_epiphany_local_verse_context(&store, "runtime-test")?; - assert_eq!(context.daemon_statuses.len(), 1); - assert!(context.odin_advertisements.is_empty()); - assert!(context.eve_surface_states.is_empty()); - assert!(context.daemon_tool_capabilities.is_empty()); - Ok(()) - } -} diff --git a/epiphany-core/src/bin/epiphany-cultmesh-smoke.rs b/epiphany-core/src/bin/epiphany-cultmesh-smoke.rs deleted file mode 100644 index 81f6c142..00000000 --- a/epiphany-core/src/bin/epiphany-cultmesh-smoke.rs +++ /dev/null @@ -1,96 +0,0 @@ -use anyhow::Result; -use epiphany_core::EPIPHANY_CULTMESH_INTERNAL_TIER; -use epiphany_core::EPIPHANY_CULTMESH_INTERNAL_VERSE_ID; -use epiphany_core::EPIPHANY_CULTMESH_STATUS_SCHEMA_VERSION; -use epiphany_core::EpiphanyCultMeshStatusEntry; -use epiphany_core::epiphany_cultmesh_operator_snapshot_from_status_json; -use epiphany_core::load_epiphany_cultmesh_status; -use epiphany_core::load_latest_epiphany_cultmesh_operator_snapshot; -use epiphany_core::write_epiphany_cultmesh_continuity_contracts; -use epiphany_core::write_epiphany_cultmesh_eyes_contracts; -use epiphany_core::write_epiphany_cultmesh_global_room_policies; -use epiphany_core::write_epiphany_cultmesh_hands_contracts; -use epiphany_core::write_epiphany_cultmesh_mind_contracts; -use epiphany_core::write_epiphany_cultmesh_operator_snapshot; -use epiphany_core::write_epiphany_cultmesh_soul_contracts; -use epiphany_core::write_epiphany_cultmesh_status; -use epiphany_core::write_epiphany_cultmesh_substrate_gate_contracts; -use epiphany_core::write_epiphany_cultmesh_verse_policies; -use std::path::PathBuf; - -fn main() -> Result<()> { - let mut store = PathBuf::from(".epiphany-smoke"); - store.push("cultmesh"); - std::fs::create_dir_all(&store)?; - store.push("epiphany-local.ccmp"); - - let status = EpiphanyCultMeshStatusEntry { - schema_version: EPIPHANY_CULTMESH_STATUS_SCHEMA_VERSION.to_string(), - runtime_id: "epiphany-cultmesh-smoke".to_string(), - verse_id: EPIPHANY_CULTMESH_INTERNAL_VERSE_ID.to_string(), - verse_tier: EPIPHANY_CULTMESH_INTERNAL_TIER.to_string(), - app_id: "epiphany".to_string(), - note: "Epiphany wrote this through CultMesh, not direct CultNet plumbing.".to_string(), - }; - - write_epiphany_cultmesh_status(&store, status.clone())?; - write_epiphany_cultmesh_verse_policies(&store, "epiphany-cultmesh-smoke")?; - write_epiphany_cultmesh_global_room_policies(&store, "epiphany-cultmesh-smoke")?; - write_epiphany_cultmesh_mind_contracts(&store, "epiphany-cultmesh-smoke")?; - write_epiphany_cultmesh_substrate_gate_contracts(&store, "epiphany-cultmesh-smoke")?; - write_epiphany_cultmesh_eyes_contracts(&store, "epiphany-cultmesh-smoke")?; - write_epiphany_cultmesh_hands_contracts(&store, "epiphany-cultmesh-smoke")?; - write_epiphany_cultmesh_soul_contracts(&store, "epiphany-cultmesh-smoke")?; - write_epiphany_cultmesh_continuity_contracts(&store, "epiphany-cultmesh-smoke")?; - let operator_snapshot = epiphany_cultmesh_operator_snapshot_from_status_json( - "epiphany-cultmesh-smoke", - "cultmesh-smoke-status", - "2026-05-27T00:00:00Z", - "status", - ".epiphany-smoke/cultmesh/status.json", - &serde_json::json!({ - "threadId": "thread-smoke", - "scene": { - "scene": { - "stateStatus": "missing", - "availableActions": ["crrc", "roles"] - } - }, - "pressure": { - "pressure": { - "level": "low" - } - }, - "reorient": { - "decision": { - "action": "regather", - "nextAction": "Regather source context." - } - }, - "crrc": { - "recommendation": { - "action": "regatherManually" - } - }, - "coordinator": { - "action": "wait" - } - }), - )?; - write_epiphany_cultmesh_operator_snapshot(&store, operator_snapshot.clone())?; - let loaded = load_epiphany_cultmesh_status(&store, "epiphany-cultmesh-smoke")?; - if loaded != Some(status) { - anyhow::bail!("CultMesh smoke failed to round-trip Epiphany status document"); - } - let loaded_operator_snapshot = - load_latest_epiphany_cultmesh_operator_snapshot(&store, "epiphany-cultmesh-smoke")?; - if loaded_operator_snapshot != Some(operator_snapshot) { - anyhow::bail!("CultMesh smoke failed to round-trip Epiphany operator snapshot document"); - } - - println!( - "epiphany cultmesh smoke ok: {}", - store.canonicalize()?.display() - ); - Ok(()) -} diff --git a/epiphany-core/src/bin/epiphany-daemon-supervisor.rs b/epiphany-core/src/bin/epiphany-daemon-supervisor.rs index 90fb76b5..02df5141 100644 --- a/epiphany-core/src/bin/epiphany-daemon-supervisor.rs +++ b/epiphany-core/src/bin/epiphany-daemon-supervisor.rs @@ -209,28 +209,6 @@ fn dispatch(args: Args) -> Result<()> { managed_service_reconcile(args) } "service-runbook" | "runbook-service" => service_runbook(args), - "repo-work-service-audit" - | "repo-work-service-readiness" - | "repo-work-queue-runner-audit" => repo_work_service_audit(args), - "cluster-service-runbook" | "cluster-daemon-runbook" => cluster_daemon_runbook(args), - "cluster-service-install-plan" - | "cluster-daemon-install-plan" - | "cluster-service-install-execute" => refuse_false_windows_scm_authority(), - "cluster-windows-service-audit" - | "cluster-service-audit" - | "cluster-service-readiness" - | "cluster-windows-service-start" - | "cluster-service-start" - | "cluster-windows-service-stop" - | "cluster-service-stop" => refuse_false_windows_scm_authority(), - "cluster-windows-service-execution-readiness" - | "cluster-service-execution-readiness" - | "cluster-windows-service-execution-runbook" - | "cluster-service-execution-runbook" - | "cluster-windows-service-execution-audit" - | "cluster-service-execution-audit" => refuse_false_windows_scm_authority(), - "cluster-windows-service-execution-audit-smoke" - | "cluster-service-execution-audit-smoke" => refuse_false_windows_scm_authority(), "service-install-plan" | "service-install-execute" => refuse_false_windows_scm_authority(), "windows-service-execution-readiness" | "service-execution-readiness" @@ -256,7 +234,7 @@ fn dispatch(args: Args) -> Result<()> { "policy" | "write-policy" => write_policy(args), "semantic-recover" => semantic_recover(args), other => anyhow::bail!( - "unknown command {other:?}; use reconcile, tick, serve, managed-service-serve, managed-service-task-plan/install/status/start/stop/uninstall, service-plan, service-launch, service-runbook, managed-service-policy, semantic-projector-service-policy, repo-work-service-audit, cluster-service-runbook, policy, or semantic-recover" + "unknown command {other:?}; use reconcile, tick, serve, managed-service-serve, managed-service-task-plan/install/status/start/stop/uninstall, service-plan, service-launch, service-runbook, managed-service-policy, semantic-projector-service-policy, policy, or semantic-recover" ), } } @@ -2014,25 +1992,6 @@ fn workspace_coverage_projector_service_policy(mut args: Args) -> Result<()> { } fn packaged_role_command_path(args: &Args, role: &str) -> Result { - #[cfg(feature = "workspace-coverage-recovery-smoke")] - if args.release_id.is_none() - && std::env::var_os("EPIPHANY_WORKSPACE_COVERAGE_SMOKE_DIAGNOSTICS").is_some() - { - let file_name = match role { - "semantic-projector" if cfg!(windows) => "epiphany-memory-semantic-projector.exe", - "semantic-projector" => "epiphany-memory-semantic-projector", - "workspace-coverage-projector" if cfg!(windows) => { - "epiphany-workspace-coverage-projector.exe" - } - "workspace-coverage-projector" => "epiphany-workspace-coverage-projector", - _ => anyhow::bail!("unknown smoke-only packaged role {role}"), - }; - let path = env::current_exe()?.with_file_name(file_name); - if !path.is_file() { - anyhow::bail!("smoke-only sibling is absent: {}", path.display()); - } - return Ok(path); - } let (release, _) = pinned_packaged_release(args, true)?; epiphany_packaged_release_binary_path(&release, role) } @@ -3142,270 +3101,6 @@ fn service_runbook(args: Args) -> Result<()> { Ok(()) } -fn repo_work_service_audit(args: Args) -> Result<()> { - require_supervisor_bootstrap(&args)?; - - let brake = load_epiphany_cultmesh_swarm_brake(&args.store, args.runtime_id.clone())?; - assert_swarm_brake_allows_service_lifecycle_entry(brake.as_ref())?; - let started_at = Utc::now(); - let receipts = - load_epiphany_cultmesh_daemon_service_lifecycle_receipts(&args.store, &args.runtime_id)?; - let service_receipts = receipts - .iter() - .filter(|receipt| receipt.service_id == args.service_id) - .collect::>(); - let plan = latest_service_lifecycle_receipt(&service_receipts, "install-plan"); - let runbook = latest_service_lifecycle_receipt(&service_receipts, "runbook"); - let launch = latest_service_lifecycle_receipt(&service_receipts, "launch"); - - let mut missing_checks = Vec::new(); - let mut failed_checks = Vec::new(); - - let plan_status = receipt_status_or_missing(plan); - if plan.is_none() { - missing_checks.push("install-plan".to_string()); - } else if plan_status != "planned" { - failed_checks.push(format!("install-plan status {plan_status}")); - } - - let runbook_status = receipt_status_or_missing(runbook); - if runbook.is_none() { - missing_checks.push("runbook".to_string()); - } else if runbook_status != "written" { - failed_checks.push(format!("runbook status {runbook_status}")); - } - let runbook_ref = runbook - .map(|receipt| receipt.operator_artifact_ref.as_str()) - .unwrap_or("none"); - let runbook_artifact_status = service_artifact_status(runbook_ref); - let runbook_sha256 = local_file_sha256(runbook_ref).unwrap_or_else(|| "none".to_string()); - if runbook.is_some() && runbook_artifact_status != "present" { - failed_checks.push(format!("runbook artifact {runbook_artifact_status}")); - } - - let launch_status = receipt_status_or_missing(launch); - let launch_exit_code = launch.and_then(|receipt| receipt.exit_code); - if launch.is_none() { - missing_checks.push("launch".to_string()); - } else { - if launch_status != "completed" { - failed_checks.push(format!("launch status {launch_status}")); - } - if launch_exit_code != Some(0) { - failed_checks.push(format!( - "launch exit code {}", - launch_exit_code - .map(|code| code.to_string()) - .unwrap_or_else(|| "none".to_string()) - )); - } - } - - let private_state_exposed = service_receipts - .iter() - .any(|receipt| receipt.private_state_exposed); - if private_state_exposed { - failed_checks.push("private state exposed".to_string()); - } - - let status = if missing_checks.is_empty() && failed_checks.is_empty() { - "complete" - } else { - "incomplete" - }; - let next_safe_move = if missing_checks.iter().any(|check| check == "install-plan") - || failed_checks - .iter() - .any(|check| check.starts_with("install-plan ")) - { - "tools/epiphany_local_run.ps1 -Mode repo-work-service-plan" - } else if missing_checks.iter().any(|check| check == "runbook") - || failed_checks - .iter() - .any(|check| check.starts_with("runbook ")) - { - "tools/epiphany_local_run.ps1 -Mode repo-work-service-runbook" - } else if missing_checks.iter().any(|check| check == "launch") - || failed_checks - .iter() - .any(|check| check.starts_with("launch ")) - { - "tools/epiphany_local_run.ps1 -Mode repo-work-service-launch" - } else { - "continue repo-swarm MVP planner/interpreter hardening" - }; - - let receipt = service_lifecycle_receipt( - &args, - "repo-work-service-audit", - status, - "repo-work-service-audit".to_string(), - vec![ - format!("plan={plan_status}"), - format!("runbook={runbook_status}"), - format!("runbookArtifact={runbook_artifact_status}"), - format!("launch={launch_status}"), - format!( - "launchExitCode={}", - launch_exit_code - .map(|code| code.to_string()) - .unwrap_or_else(|| "none".to_string()) - ), - format!("missing={}", missing_checks.len()), - format!("failed={}", failed_checks.len()), - ], - None, - None, - started_at, - Some(Utc::now()), - Some(format!( - "service://{}/repo-work-service-audit", - sanitize_id(&args.service_id) - )), - ); - let written = write_epiphany_cultmesh_daemon_service_lifecycle_receipt( - &args.store, - args.runtime_id.clone(), - receipt, - )?; - println!( - "{}", - serde_json::to_string_pretty(&json!({ - "schemaVersion": "epiphany.repo_work_service_audit.v0", - "status": status, - "store": args.store, - "runtimeId": args.runtime_id, - "serviceId": written.service_id, - "schedulerId": written.scheduler_id, - "receiptId": written.receipt_id, - "planStatus": plan_status, - "runbookStatus": runbook_status, - "runbookArtifactStatus": runbook_artifact_status, - "runbookArtifactRef": runbook_ref, - "runbookSha256": runbook_sha256, - "launchStatus": launch_status, - "launchExitCode": launch_exit_code, - "missingChecks": missing_checks, - "failedChecks": failed_checks, - "lifecycleOwner": "Idunn", - "hostedBody": "repo-work", - "mutatesServiceManager": false, - "requiresElevatedAuthority": false, - "privateStateExposed": private_state_exposed, - "nextSafeMove": next_safe_move, - }))? - ); - Ok(()) -} - -fn latest_service_lifecycle_receipt<'a>( - receipts: &'a [&EpiphanyCultMeshDaemonServiceLifecycleReceiptEntry], - action: &str, -) -> Option<&'a EpiphanyCultMeshDaemonServiceLifecycleReceiptEntry> { - receipts - .iter() - .copied() - .filter(|receipt| receipt.action == action) - .max_by_key(|receipt| { - receipt - .completed_at_utc - .as_deref() - .unwrap_or(&receipt.started_at_utc) - }) -} - -fn receipt_status_or_missing( - receipt: Option<&EpiphanyCultMeshDaemonServiceLifecycleReceiptEntry>, -) -> String { - receipt - .map(|receipt| receipt.status.clone()) - .unwrap_or_else(|| "missing".to_string()) -} - -fn service_artifact_status(artifact_ref: &str) -> &'static str { - if artifact_ref.trim().is_empty() || artifact_ref == "none" { - return "none"; - } - let path = Path::new(artifact_ref); - if path.is_file() { - return "present"; - } - if artifact_ref.contains("://") { - return "external-ref"; - } - "missing" -} - -fn cluster_daemon_runbook(args: Args) -> Result<()> { - require_supervisor_bootstrap(&args)?; - - let context = query_epiphany_local_verse_context(&args.store, args.runtime_id.clone())?; - assert_swarm_brake_allows_service_lifecycle(&context)?; - let started_at = Utc::now(); - let command_path = cluster_daemon_command_path(&args)?; - let daemon_rows = cluster_daemon_runbook_rows(&args, &context)?; - let runbook_path = service_runbook_path(&args); - if let Some(parent) = runbook_path.parent() { - fs::create_dir_all(parent) - .with_context(|| format!("failed to create {}", parent.display()))?; - } - fs::write( - &runbook_path, - cluster_daemon_runbook_content(&args, &command_path, &daemon_rows), - ) - .with_context(|| format!("failed to write {}", runbook_path.display()))?; - let artifact_ref = runbook_path.display().to_string(); - let service_args = daemon_rows - .iter() - .flat_map(|row| { - let mut values = vec![format!("daemon={}", row.daemon_id)]; - values.extend(row.args.clone()); - values - }) - .collect::>(); - let receipt = service_lifecycle_receipt( - &args, - "cluster-runbook", - "written", - command_path.display().to_string(), - service_args, - None, - None, - started_at, - Some(Utc::now()), - Some(artifact_ref.clone()), - ); - let written = write_epiphany_cultmesh_daemon_service_lifecycle_receipt( - &args.store, - args.runtime_id.clone(), - receipt, - )?; - println!( - "{}", - serde_json::to_string_pretty(&json!({ - "status": "written", - "store": args.store, - "runtimeId": args.runtime_id, - "serviceId": written.service_id, - "receiptId": written.receipt_id, - "daemonSelector": written.daemon_selector, - "daemonCount": daemon_rows.len(), - "runbookPath": artifact_ref, - "command": written.command, - "daemons": daemon_rows.iter().map(|row| json!({ - "daemonId": row.daemon_id, - "clusterId": row.cluster_id, - "displayName": row.display_name, - "observedStatus": row.observed_status, - "args": row.args, - "privateStateExposed": false, - })).collect::>(), - "privateStateExposed": written.private_state_exposed, - }))? - ); - Ok(()) -} - fn local_file_sha256(path: &str) -> Option { if path.trim().is_empty() || path == "none" { return None; @@ -3951,20 +3646,6 @@ fn service_command_path(args: &Args) -> Result { env::current_exe().context("failed to resolve current supervisor executable") } -fn cluster_daemon_command_path(args: &Args) -> Result { - if let Some(command) = args.service_command.as_ref() { - return Ok(command.clone()); - } - let daemon_binary = if cfg!(windows) { - "epiphany-cluster-daemon.exe" - } else { - "epiphany-cluster-daemon" - }; - env::current_exe() - .map(|path| path.with_file_name(daemon_binary)) - .context("failed to resolve cluster daemon executable") -} - fn service_serve_args(args: &Args) -> Vec { if !args.service_args.is_empty() { return args.service_args.clone(); @@ -4000,29 +3681,6 @@ fn service_serve_args(args: &Args) -> Vec { service_args } -fn cluster_daemon_serve_args(args: &Args, daemon_id: &str) -> Vec { - let mut service_args = vec![ - "serve".to_string(), - "--store".to_string(), - args.store.display().to_string(), - "--runtime-id".to_string(), - args.runtime_id.clone(), - "--daemon-id".to_string(), - daemon_id.to_string(), - "--interval-seconds".to_string(), - args.loop_interval_seconds.to_string(), - "--max-iterations".to_string(), - args.max_iterations.to_string(), - "--note".to_string(), - "Cluster daemon service runbook heartbeat.".to_string(), - ]; - if let Some(reason) = &args.reason { - service_args.push("--note".to_string()); - service_args.push(reason.clone()); - } - service_args -} - fn service_runbook_path(args: &Args) -> PathBuf { if let Some(path) = args.runbook_path.as_ref() { return path.clone(); @@ -4032,44 +3690,6 @@ fn service_runbook_path(args: &Args) -> PathBuf { .join(format!("{}.ps1", sanitize_id(&args.service_id))) } -fn cluster_daemon_runbook_rows( - args: &Args, - context: &EpiphanyLocalVerseContext, -) -> Result> { - let requested_daemon = args.daemon_id.as_str(); - let mut rows = Vec::new(); - for cluster in &context.cluster_topology { - if requested_daemon != "*" && cluster.daemon_id != requested_daemon { - continue; - } - let observed_status = context - .daemon_statuses - .iter() - .find(|status| status.daemon_id == cluster.daemon_id) - .map(|status| status.status.clone()) - .with_context(|| { - format!( - "local Verse has topology for {} but no daemon status row", - cluster.daemon_id - ) - })?; - rows.push(ClusterDaemonRunbookRow { - daemon_id: cluster.daemon_id.clone(), - cluster_id: cluster.cluster_id.clone(), - display_name: cluster.display_name.clone(), - observed_status, - args: cluster_daemon_serve_args(args, &cluster.daemon_id), - }); - } - if rows.is_empty() { - anyhow::bail!( - "local Verse has no cluster daemon topology rows matching {:?}", - requested_daemon - ); - } - Ok(rows) -} - fn service_runbook_content( args: &Args, command_path: &std::path::Path, @@ -4105,60 +3725,10 @@ fn service_runbook_content( ) } -fn cluster_daemon_runbook_content( - args: &Args, - command_path: &std::path::Path, - daemon_rows: &[ClusterDaemonRunbookRow], -) -> String { - let cwd = args - .cwd - .as_ref() - .map(|path| path.display().to_string()) - .unwrap_or_else(|| ".".to_string()); - let mut lines = vec![ - "# Epiphany cluster daemon service runbook".to_string(), - "# Generated by epiphany-daemon-supervisor cluster-service-runbook.".to_string(), - "# The typed lifecycle receipt is the witness; this script is an operator artifact." - .to_string(), - "$ErrorActionPreference = 'Stop'".to_string(), - format!( - "$command = {}", - quote_powershell(&command_path.display().to_string()) - ), - format!("$workingDirectory = {}", quote_powershell(&cwd)), - "$processes = @()".to_string(), - ]; - for row in daemon_rows { - let quoted_args = row - .args - .iter() - .map(|arg| quote_powershell(arg)) - .collect::>() - .join(", "); - lines.push(format!("# {} / {}", row.display_name, row.daemon_id)); - lines.push(format!("$arguments = @({quoted_args})")); - lines.push("$processes += Start-Process -FilePath $command -ArgumentList $arguments -WorkingDirectory $workingDirectory -WindowStyle Hidden -PassThru".to_string()); - } - lines.push(format!( - "\"started cluster-daemon-count=$($processes.Count) service={} scheduler={}\"", - args.service_id, args.scheduler_id - )); - lines.push(String::new()); - lines.join("\n") -} - fn quote_powershell(value: &str) -> String { format!("'{}'", value.replace('\'', "''")) } -struct ClusterDaemonRunbookRow { - daemon_id: String, - cluster_id: String, - display_name: String, - observed_status: String, - args: Vec, -} - fn service_lifecycle_receipt( args: &Args, action: &str, @@ -4314,10 +3884,6 @@ fn assert_swarm_brake_allows_scheduler_tick(context: &EpiphanyLocalVerseContext) Ok(()) } -fn assert_swarm_brake_allows_service_lifecycle(context: &EpiphanyLocalVerseContext) -> Result<()> { - assert_swarm_brake_allows_service_lifecycle_entry(context.swarm_brake.as_ref()) -} - fn assert_swarm_brake_allows_service_lifecycle_entry( brake: Option<&EpiphanyCultMeshSwarmBrakeEntry>, ) -> Result<()> { @@ -4664,7 +4230,6 @@ mod semantic_projector_authority_tests { "managed_service_task_install", "managed_service_task_operation", "service_runbook", - "repo_work_service_audit", ] { let marker = format!("fn {function}("); let start = source.find(&marker).unwrap(); @@ -5047,10 +4612,6 @@ mod semantic_projector_authority_tests { "service-start", "service-stop", "service-execution-audit", - "cluster-service-install-execute", - "cluster-service-start", - "cluster-service-stop", - "cluster-service-execution-audit", ] { let dispatch = source .find(&format!("\"{command}\"")) @@ -5664,30 +5225,6 @@ impl Args { | "service-desired-state-reconcile" | "service-runbook" | "runbook-service" - | "repo-work-service-audit" - | "repo-work-service-readiness" - | "repo-work-queue-runner-audit" - | "cluster-service-runbook" - | "cluster-daemon-runbook" - | "cluster-windows-service-install" - | "cluster-service-install-plan" - | "cluster-service-install-execute" - | "cluster-daemon-install-plan" - | "cluster-windows-service-audit" - | "cluster-service-audit" - | "cluster-service-readiness" - | "cluster-windows-service-start" - | "cluster-service-start" - | "cluster-windows-service-stop" - | "cluster-service-stop" - | "cluster-windows-service-execution-readiness" - | "cluster-service-execution-readiness" - | "cluster-windows-service-execution-runbook" - | "cluster-service-execution-runbook" - | "cluster-windows-service-execution-audit" - | "cluster-service-execution-audit" - | "cluster-windows-service-execution-audit-smoke" - | "cluster-service-execution-audit-smoke" | "windows-service-install" | "service-install-windows" | "service-install-plan" diff --git a/epiphany-core/src/bin/epiphany-daemon-survival-rehearsal-smoke.rs b/epiphany-core/src/bin/epiphany-daemon-survival-rehearsal-smoke.rs deleted file mode 100644 index 2bfe8380..00000000 --- a/epiphany-core/src/bin/epiphany-daemon-survival-rehearsal-smoke.rs +++ /dev/null @@ -1,479 +0,0 @@ -use anyhow::Context; -use anyhow::Result; -use anyhow::anyhow; -use chrono::Utc; -use serde_json::Value; -use serde_json::json; -use std::env; -use std::fs; -use std::path::Path; -use std::path::PathBuf; -use std::process::Command; - -fn main() -> Result<()> { - let args = Args::parse()?; - let result = run_smoke(args)?; - println!("{}", serde_json::to_string_pretty(&result)?); - Ok(()) -} - -#[derive(Clone, Debug)] -struct Args { - root: PathBuf, -} - -impl Args { - fn parse() -> Result { - let mut root = env::current_dir().context("failed to resolve current directory")?; - let mut args = env::args().skip(1).peekable(); - while let Some(arg) = args.next() { - match arg.as_str() { - "--root" => root = take_path(&mut args, "--root")?, - other => return Err(anyhow!("unexpected argument {other:?}")), - } - } - Ok(Self { root }) - } -} - -fn run_smoke(args: Args) -> Result { - let root = args - .root - .canonicalize() - .with_context(|| format!("failed to resolve {}", args.root.display()))?; - let smoke_root = root.join(".epiphany-smoke"); - let manifest = root.join("epiphany-core").join("Cargo.toml"); - if !manifest.exists() { - return Err(anyhow!( - "could not find epiphany-core manifest at {}", - manifest.display() - )); - } - fs::create_dir_all(&smoke_root) - .with_context(|| format!("failed to create {}", smoke_root.display()))?; - let stamp = Utc::now().format("%Y%m%d-%H%M%S").to_string(); - let smoke_dir = smoke_root.join(format!("daemon-survival-rehearsal-{stamp}")); - fs::create_dir(&smoke_dir) - .with_context(|| format!("failed to claim fresh smoke dir {}", smoke_dir.display()))?; - - let local_verse = smoke_dir.join("local-verse.ccmp"); - let runtime_id = "epiphany-local"; - let daemon_id = "epiphany-daemon-self"; - let scheduler_id = "epiphany-daemon-survival-rehearsal"; - let restart_command = if cfg!(windows) { - "powershell.exe" - } else { - "sh" - }; - let restart_args = if cfg!(windows) { - vec!["-NoProfile", "-Command", "exit 0"] - } else { - vec!["-c", "exit 0"] - }; - - cargo_json( - &manifest, - "epiphany-verse-query", - &[ - "seed", - "--store", - path_str(&local_verse)?, - "--runtime-id", - runtime_id, - ], - &root, - )?; - cargo_json( - &manifest, - "epiphany-cluster-daemon", - &[ - "heartbeat", - "--store", - path_str(&local_verse)?, - "--runtime-id", - runtime_id, - "--daemon-id", - daemon_id, - ], - &root, - )?; - - let mut policy_args = vec![ - "policy", - "--store", - path_str(&local_verse)?, - "--runtime-id", - runtime_id, - "--daemon-id", - daemon_id, - "--scheduler-id", - scheduler_id, - "--restart-command", - restart_command, - "--cwd", - path_str(&root)?, - "--cooldown-seconds", - "0", - "--reconcile-interval-seconds", - "0", - "--heartbeat-stale-seconds", - "2", - ] - .into_iter() - .map(str::to_string) - .collect::>(); - for arg in &restart_args { - policy_args.push("--restart-arg".to_string()); - policy_args.push((*arg).to_string()); - } - let policy = cargo_json( - &manifest, - "epiphany-daemon-supervisor", - &string_refs(&policy_args), - &root, - )?; - let provider_before = cargo_json( - &manifest, - "epiphany-verse-query", - &[ - "swarm-status", - "--store", - path_str(&local_verse)?, - "--runtime-id", - runtime_id, - ], - &root, - )?; - - let serve_args = vec![ - "serve", - "--store", - path_str(&local_verse)?, - "--runtime-id", - runtime_id, - "--daemon-id", - daemon_id, - "--scheduler-id", - scheduler_id, - "--loop-interval-seconds", - "0", - "--max-iterations", - "2", - ] - .into_iter() - .map(str::to_string) - .collect::>(); - std::thread::sleep(std::time::Duration::from_secs(3)); - let serve = cargo_json( - &manifest, - "epiphany-daemon-supervisor", - &string_refs(&serve_args), - &root, - )?; - - require_eq(&policy, &["status"], "written")?; - require_eq(&policy, &["daemonId"], daemon_id)?; - require_bool(&policy, &["privateStateExposed"], false)?; - require_eq(&serve, &["status"], "serveComplete")?; - require_u64(&serve, &["iterations"], 2)?; - require_bool(&serve, &["privateStateExposed"], false)?; - let outputs = serve - .get("outputs") - .and_then(Value::as_array) - .ok_or_else(|| anyhow!("serve output missing outputs array"))?; - if outputs.len() != 2 { - return Err(anyhow!("expected 2 serve outputs, got {}", outputs.len())); - } - let mut intent_ids = Vec::new(); - let mut receipt_ids = Vec::new(); - for (idx, output) in outputs.iter().enumerate() { - require_eq(output, &["status"], "tickComplete")?; - require_eq(output, &["schedulerId"], scheduler_id)?; - require_u64(output, &["iteration"], (idx + 1) as u64)?; - require_u64(output, &["outcomeCount"], 1)?; - require_u64(output, &["restartedCount"], 1)?; - require_u64(output, &["refusedCount"], 0)?; - require_bool(output, &["privateStateExposed"], false)?; - let outcomes = output - .get("outcomes") - .and_then(Value::as_array) - .ok_or_else(|| anyhow!("tick output missing outcomes array"))?; - let outcome = outcomes - .first() - .ok_or_else(|| anyhow!("tick output missing first outcome"))?; - require_eq(outcome, &["status"], "awaiting-provider-heartbeat")?; - require_eq(outcome, &["resultingStatus"], "awaiting-provider-heartbeat")?; - require_bool(outcome, &["observedHeartbeatStale"], true)?; - require_eq(outcome, &["daemonId"], daemon_id)?; - require_bool(outcome, &["privateStateExposed"], false)?; - intent_ids.push( - outcome["intentId"] - .as_str() - .context("restart outcome missing intentId")? - .to_string(), - ); - receipt_ids.push( - outcome["receiptId"] - .as_str() - .context("restart outcome missing receiptId")? - .to_string(), - ); - } - if intent_ids[0] == intent_ids[1] || receipt_ids[0] == receipt_ids[1] { - return Err(anyhow!( - "two supervisor attempts reused immutable intent/receipt identity" - )); - } - - let provider_after = cargo_json( - &manifest, - "epiphany-verse-query", - &[ - "swarm-status", - "--store", - path_str(&local_verse)?, - "--runtime-id", - runtime_id, - ], - &root, - )?; - let provider_row = |report: &Value| -> Result { - rows(report) - .iter() - .find(|row| row.get("daemonId").and_then(Value::as_str) == Some(daemon_id)) - .map(|row| (*row).clone()) - .ok_or_else(|| anyhow!("swarm status omitted provider daemon {daemon_id}")) - }; - let before_row = provider_row(&provider_before)?; - let after_row = provider_row(&provider_after)?; - for field in ["status", "operatorAction", "lastHeartbeatUtc"] { - if before_row[field] != after_row[field] { - return Err(anyhow!( - "supervisor changed provider-owned {field}: before={} after={}", - before_row[field], - after_row[field] - )); - } - } - - cargo_json( - &manifest, - "epiphany-cluster-daemon", - &[ - "heartbeat", - "--store", - path_str(&local_verse)?, - "--runtime-id", - runtime_id, - "--daemon-id", - daemon_id, - ], - &root, - )?; - let recovered = cargo_json( - &manifest, - "epiphany-daemon-supervisor", - &[ - "reconcile", - "--store", - path_str(&local_verse)?, - "--runtime-id", - runtime_id, - "--daemon-id", - daemon_id, - ], - &root, - )?; - require_eq(&recovered, &["status"], "noop")?; - require_bool(&recovered, &["providerHeartbeatProvedRecovery"], true)?; - - let receipt_directory = cargo_json( - &manifest, - "epiphany-verse-query", - &[ - "receipt-directory", - "--store", - path_str(&local_verse)?, - "--runtime-id", - runtime_id, - ], - &root, - )?; - require_bool(&receipt_directory, &["privateStateExposed"], false)?; - let scheduler_row = rows(&receipt_directory) - .iter() - .find(|row| row.get("family").and_then(Value::as_str) == Some("scheduler")) - .cloned() - .ok_or_else(|| anyhow!("receipt directory did not expose scheduler row"))?; - require_eq(&scheduler_row, &["status"], "tickComplete")?; - require_eq(&scheduler_row, &["route"], daemon_id)?; - require_bool(&scheduler_row, &["privateStateExposed"], false)?; - let poke_row = rows(&receipt_directory) - .iter() - .find(|row| row.get("family").and_then(Value::as_str) == Some("daemon-poke")) - .cloned() - .ok_or_else(|| anyhow!("receipt directory did not expose daemon poke row"))?; - require_eq(&poke_row, &["status"], "resolved")?; - let policy_directory = cargo_json( - &manifest, - "epiphany-verse-query", - &[ - "restart-policy-directory", - "--store", - path_str(&local_verse)?, - "--runtime-id", - runtime_id, - ], - &root, - )?; - let recovered_policy = rows(&policy_directory) - .iter() - .find(|row| row.get("daemonId").and_then(Value::as_str) == Some(daemon_id)) - .copied() - .ok_or_else(|| anyhow!("restart policy directory omitted {daemon_id}"))?; - require_u64(recovered_policy, &["failureCount"], 0)?; - - let summary = json!({ - "schemaVersion": "epiphany.daemon_survival_rehearsal_smoke.v0", - "status": "ok", - "smokeDir": smoke_dir, - "localVerseStore": local_verse, - "runtimeId": runtime_id, - "daemonId": daemon_id, - "schedulerId": scheduler_id, - "policyStatus": policy["status"], - "policyId": policy["policyId"], - "serveStatus": serve["status"], - "serveIterations": serve["iterations"], - "schedulerReceiptId": scheduler_row["latestId"], - "schedulerReceiptStatus": scheduler_row["status"], - "pokeReceiptStatus": poke_row["status"], - "providerEnvelopeUnchanged": true, - "providerHeartbeatRequiredForRecovery": true, - "providerHeartbeatResolvedReceipt": true, - "providerHeartbeatResetFailureCount": true, - "distinctAttemptIntentIds": intent_ids, - "distinctAttemptReceiptIds": receipt_ids, - "restartCommand": restart_command, - "restartArgs": restart_args, - "boundedProofMode": true, - "serviceManagerMutated": false, - "requiresElevatedAuthority": false, - "unattendedDaemonSurvivalRehearsed": true, - "privateStateExposed": false, - }); - write_json(&smoke_dir.join("summary.json"), &summary)?; - Ok(summary) -} - -fn cargo_json(manifest: &Path, bin: &str, args: &[&str], cwd: &Path) -> Result { - let output = Command::new("cargo") - .arg("run") - .arg("--quiet") - .arg("--manifest-path") - .arg(manifest) - .arg("--bin") - .arg(bin) - .arg("--") - .args(args) - .current_dir(cwd) - .output() - .with_context(|| format!("failed to run cargo bin {bin}"))?; - if !output.status.success() { - return Err(anyhow!( - "{bin} failed with status {:?}\nstdout:\n{}\nstderr:\n{}", - output.status.code(), - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr) - )); - } - serde_json::from_slice(&output.stdout).with_context(|| { - format!( - "{bin} did not emit JSON on stdout:\n{}", - String::from_utf8_lossy(&output.stdout) - ) - }) -} - -fn string_refs(args: &[String]) -> Vec<&str> { - args.iter().map(String::as_str).collect() -} - -fn rows(value: &Value) -> Vec<&Value> { - value - .get("rows") - .and_then(Value::as_array) - .map(|rows| rows.iter().collect()) - .unwrap_or_default() -} - -fn write_json(path: &Path, value: &Value) -> Result<()> { - if let Some(parent) = path.parent() { - fs::create_dir_all(parent) - .with_context(|| format!("failed to create {}", parent.display()))?; - } - fs::write(path, serde_json::to_vec_pretty(value)?) - .with_context(|| format!("failed to write {}", path.display())) -} - -fn require_eq(value: &Value, path: &[&str], expected: &str) -> Result<()> { - let actual = path - .iter() - .try_fold(value, |current, key| current.get(*key)) - .and_then(Value::as_str) - .unwrap_or(""); - if actual == expected { - Ok(()) - } else { - Err(anyhow!( - "expected {} to be {expected:?}, got {actual:?}", - path.join(".") - )) - } -} - -fn require_bool(value: &Value, path: &[&str], expected: bool) -> Result<()> { - let actual = path - .iter() - .try_fold(value, |current, key| current.get(*key)) - .and_then(Value::as_bool); - if actual == Some(expected) { - Ok(()) - } else { - Err(anyhow!( - "expected {} to be {expected}, got {:?}", - path.join("."), - actual - )) - } -} - -fn require_u64(value: &Value, path: &[&str], expected: u64) -> Result<()> { - let actual = path - .iter() - .try_fold(value, |current, key| current.get(*key)) - .and_then(Value::as_u64); - if actual == Some(expected) { - Ok(()) - } else { - Err(anyhow!( - "expected {} to be {expected}, got {:?}", - path.join("."), - actual - )) - } -} - -fn take_path( - args: &mut std::iter::Peekable>, - flag: &str, -) -> Result { - args.next() - .map(PathBuf::from) - .with_context(|| format!("missing value for {flag}")) -} - -fn path_str(path: &Path) -> Result<&str> { - path.to_str() - .ok_or_else(|| anyhow!("path is not valid UTF-8: {}", path.display())) -} diff --git a/epiphany-core/src/bin/epiphany-hands-action-smoke.rs b/epiphany-core/src/bin/epiphany-hands-action-smoke.rs deleted file mode 100644 index 869be4ef..00000000 --- a/epiphany-core/src/bin/epiphany-hands-action-smoke.rs +++ /dev/null @@ -1,152 +0,0 @@ -use anyhow::Result; -use epiphany_core::HANDS_ACTION_INTENT_SCHEMA_VERSION; -use epiphany_core::HandsActionIntent; -use epiphany_core::RuntimeSpineInitOptions; -use epiphany_core::hands_action_review_for_intent; -use epiphany_core::hands_command_receipt_for_review; -use epiphany_core::hands_commit_receipt_for_review; -use epiphany_core::hands_patch_receipt_for_review; -use epiphany_core::initialize_runtime_spine; -use epiphany_core::put_hands_action_intent; -use epiphany_core::put_hands_action_review; -use epiphany_core::put_hands_command_receipt; -use epiphany_core::put_hands_commit_receipt; -use epiphany_core::put_hands_patch_receipt; -use epiphany_core::runtime_hands_action_intent; -use epiphany_core::runtime_hands_action_review; -use epiphany_core::runtime_hands_command_receipt; -use epiphany_core::runtime_hands_commit_receipt; -use epiphany_core::runtime_hands_patch_receipt; -use serde_json::json; -use std::path::PathBuf; - -fn main() -> Result<()> { - let store = PathBuf::from(".epiphany-smoke") - .join("runtime-spine") - .join("hands-action-smoke.msgpack"); - - initialize_runtime_spine( - &store, - RuntimeSpineInitOptions { - runtime_id: "epiphany-hands-action-smoke".to_string(), - display_name: "Epiphany Hands Action Smoke".to_string(), - created_at: "2026-06-02T00:00:00Z".to_string(), - }, - )?; - - let intent = HandsActionIntent { - schema_version: HANDS_ACTION_INTENT_SCHEMA_VERSION.to_string(), - intent_id: "hands-intent-smoke".to_string(), - runtime_job_id: "job-implementation-smoke".to_string(), - binding_id: "implementation-worker".to_string(), - role: "epiphany-hands".to_string(), - authority_scope: "epiphany.role.implementation".to_string(), - requested_action: "patch".to_string(), - requested_paths: vec!["tools/epiphany_local_run.ps1".to_string()], - substrate_gate_grant_receipt_id: "substrate-grant-smoke".to_string(), - requested_at: "2026-06-02T00:00:10Z".to_string(), - contract: "Smoke intent proves Hands action starts as typed runtime-spine state." - .to_string(), - frontier_route_id: String::new(), - plan_candidate_sha256: String::new(), - plan_action: String::new(), - }; - put_hands_action_intent(&store, &intent)?; - - let review = hands_action_review_for_intent( - "hands-review-smoke".to_string(), - &intent, - "approved".to_string(), - vec![ - "patch".to_string(), - "command".to_string(), - "commit".to_string(), - "pr".to_string(), - ], - vec![ - "Substrate Gate grant id is named; this smoke does not execute the patch.".to_string(), - ], - "2026-06-02T00:00:20Z".to_string(), - ); - put_hands_action_review(&store, &review)?; - - let patch = hands_patch_receipt_for_review( - "hands-patch-smoke".to_string(), - &intent, - &review, - vec!["tools/epiphany_local_run.ps1".to_string()], - "Recorded a typed patch receipt for the bounded Hands action path.".to_string(), - "2026-06-02T00:00:30Z".to_string(), - ); - put_hands_patch_receipt(&store, &patch)?; - - let command = hands_command_receipt_for_review( - "hands-command-smoke".to_string(), - &intent, - &review, - "cargo test --manifest-path .\\epiphany-core\\Cargo.toml hands_gateway".to_string(), - "0".to_string(), - ".epiphany-smoke/runtime-spine/hands-action-smoke.stdout.log".to_string(), - ".epiphany-smoke/runtime-spine/hands-action-smoke.stderr.log".to_string(), - "Recorded a typed command receipt for the bounded Hands action path.".to_string(), - "2026-06-02T00:00:40Z".to_string(), - ); - put_hands_command_receipt(&store, &command)?; - - let commit = hands_commit_receipt_for_review( - "hands-commit-smoke".to_string(), - &intent, - &review, - "dry-run-no-commit".to_string(), - "main".to_string(), - vec!["tools/epiphany_local_run.ps1".to_string()], - "Recorded a typed commit receipt shape for the bounded Hands action path.".to_string(), - "2026-06-02T00:00:50Z".to_string(), - ); - put_hands_commit_receipt(&store, &commit)?; - - let stored_intent = - runtime_hands_action_intent(&store, "hands-intent-smoke")?.expect("stored Hands intent"); - let stored_review = - runtime_hands_action_review(&store, "hands-review-smoke")?.expect("stored Hands review"); - let stored_patch = - runtime_hands_patch_receipt(&store, "hands-patch-smoke")?.expect("stored Hands patch"); - let stored_command = runtime_hands_command_receipt(&store, "hands-command-smoke")? - .expect("stored Hands command"); - let stored_commit = - runtime_hands_commit_receipt(&store, "hands-commit-smoke")?.expect("stored Hands commit"); - - if stored_review.intent_id != stored_intent.intent_id { - anyhow::bail!("Hands review lost its intent edge"); - } - if stored_patch.intent_id != stored_intent.intent_id - || stored_patch.review_id != stored_review.review_id - { - anyhow::bail!("Hands patch receipt lost its intent/review edge"); - } - if stored_command.intent_id != stored_intent.intent_id - || stored_command.review_id != stored_review.review_id - { - anyhow::bail!("Hands command receipt lost its intent/review edge"); - } - if stored_commit.intent_id != stored_intent.intent_id - || stored_commit.review_id != stored_review.review_id - { - anyhow::bail!("Hands commit receipt lost its intent/review edge"); - } - println!( - "{}", - serde_json::to_string_pretty(&json!({ - "status": "ok", - "store": store, - "intentId": stored_intent.intent_id, - "reviewId": stored_review.review_id, - "patchReceiptId": stored_patch.receipt_id, - "commandReceiptId": stored_command.receipt_id, - "commitReceiptId": stored_commit.receipt_id, - "changedPaths": stored_patch.changed_paths, - }))? - ); - - Ok(()) -} diff --git a/epiphany-core/src/bin/epiphany-mvp-coordinator-smoke.rs b/epiphany-core/src/bin/epiphany-mvp-coordinator-smoke.rs deleted file mode 100644 index f7bdf2ab..00000000 --- a/epiphany-core/src/bin/epiphany-mvp-coordinator-smoke.rs +++ /dev/null @@ -1,324 +0,0 @@ -use anyhow::Context; -use anyhow::Result; -use anyhow::anyhow; -use serde_json::Value; -use serde_json::json; -use std::env; -use std::fs; -use std::path::{Path, PathBuf}; -use std::process::Command; - -fn main() -> Result<()> { - let args = Args::parse()?; - let result = run_smoke(&args)?; - println!("{}", serde_json::to_string_pretty(&result)?); - Ok(()) -} - -#[derive(Debug)] -struct Args { - artifact_root: PathBuf, - coordinator_exe: Option, -} - -impl Args { - fn parse() -> Result { - let root = env::current_dir().context("failed to resolve current dir")?; - let mut parsed = Self { - artifact_root: root.join(".epiphany-smoke").join("mvp-coordinator"), - coordinator_exe: None, - }; - let mut args = env::args().skip(1); - while let Some(arg) = args.next() { - match arg.as_str() { - "--app-server" => { - let _ = take_path(&mut args, "--app-server")?; - } - "--artifact-root" => { - parsed.artifact_root = take_path(&mut args, "--artifact-root")?; - } - "--coordinator-exe" => { - parsed.coordinator_exe = Some(take_path(&mut args, "--coordinator-exe")?); - } - other => return Err(anyhow!("unknown argument: {other}")), - } - } - Ok(parsed) - } -} - -fn run_smoke(args: &Args) -> Result { - let root = env::current_dir().context("failed to resolve current dir")?; - let artifact_root = absolute_path(&args.artifact_root)?; - reset_artifact_root(&root, &artifact_root)?; - let coordinator = match &args.coordinator_exe { - Some(path) => absolute_path(path)?, - None => sibling_exe("epiphany-mvp-coordinator")?, - }; - ensure_coordinator_built(&root, &coordinator)?; - - let cold = run_coordinator( - &root, - &coordinator, - &artifact_root, - RunOptions { - name: "cold", - mode: "plan", - max_steps: 1, - }, - )?; - require( - cold.pointer("/finalAction/action").and_then(Value::as_str) == Some("prepareCheckpoint"), - "cold start should stop at prepareCheckpoint", - )?; - require_artifacts(&cold)?; - require_operator_safe(&cold, "$")?; - - let legacy_flags = [ - "--bootstrap-smoke-state", - "--bootstrap-local-state", - "--bootstrap-objective", - "--simulate-high-pressure", - "--simulate-continue-implementation", - "--simulate-source-drift", - "--dry-compact", - ]; - for flag in legacy_flags { - let rejected = Command::new(&coordinator) - .current_dir(&root) - .arg(flag) - .output() - .with_context(|| format!("failed to check rejected coordinator flag {flag}"))?; - require( - !rejected.status.success() - && String::from_utf8_lossy(&rejected.stderr).contains("unknown argument"), - &format!("production coordinator accepted fixture-only flag {flag}"), - )?; - } - - let rejected = Command::new(&coordinator) - .current_dir(&root) - .arg("--artifact-dir") - .arg(artifact_root.join("rejected-private-store-completion")) - .arg("--test-complete-backend") - .output() - .context("failed to run rejected completion check")?; - let rejected_stderr = String::from_utf8_lossy(&rejected.stderr); - require( - !rejected.status.success() && rejected_stderr.contains("CultNet job-result API"), - "native coordinator should reject direct backend-completion mutation", - )?; - - let result = json!({ - "artifactRoot": artifact_root, - "coldAction": cold["finalAction"]["action"], - "rejectedFixtureFlags": legacy_flags, - "directBackendCompletionRejected": true, - "note": "The smoke owns only its .epiphany-smoke fixture body. Production coordinator state and Hands authority come from typed runtime evidence.", - }); - write_json( - &artifact_root.join("coordinator-smoke-summary.json"), - &result, - )?; - Ok(result) -} - -struct RunOptions { - name: &'static str, - mode: &'static str, - max_steps: usize, -} - -fn run_coordinator( - root: &Path, - coordinator: &Path, - artifact_root: &Path, - options: RunOptions, -) -> Result { - let artifact_dir = artifact_root.join(options.name); - let mut command = Command::new(coordinator); - command - .current_dir(root) - .arg("--artifact-dir") - .arg(&artifact_dir) - .arg("--runtime-store") - .arg(artifact_dir.join("runtime-spine.msgpack")) - .arg("--codex-home") - .arg(artifact_dir.join("codex-home")) - .arg("--cwd") - .arg(root) - .arg("--mode") - .arg(options.mode) - .arg("--max-steps") - .arg(options.max_steps.to_string()) - .arg("--poll-seconds") - .arg("0.1") - .arg("--timeout-seconds") - .arg("3"); - let output = command - .output() - .context("failed to run native coordinator")?; - if !output.status.success() { - return Err(anyhow!( - "native coordinator failed: {}{}", - String::from_utf8_lossy(&output.stderr), - String::from_utf8_lossy(&output.stdout) - )); - } - serde_json::from_slice(&output.stdout).context("failed to decode coordinator summary") -} - -fn require_artifacts(summary: &Value) -> Result<()> { - let artifact_dir = PathBuf::from( - summary - .get("artifactDir") - .and_then(Value::as_str) - .ok_or_else(|| anyhow!("summary missing artifactDir"))?, - ); - for name in [ - "coordinator-summary.json", - "coordinator-steps.jsonl", - "coordinator-final-status.json", - "coordinator-final-status.txt", - "coordinator-final-action.txt", - "agent-function-telemetry.json", - "runtime-spine-status.json", - ] { - require( - artifact_dir.join(name).exists(), - &format!("missing coordinator artifact {name}"), - )?; - } - require( - artifact_dir.join("runtime-spine.msgpack").exists(), - "missing native runtime spine store", - )?; - let runtime_status: Value = serde_json::from_str(&fs::read_to_string( - artifact_dir.join("runtime-spine-status.json"), - )?)?; - require( - runtime_status["present"].as_bool() == Some(true), - "native runtime spine should be present", - )?; - require( - runtime_status["sessions"].as_u64().unwrap_or(0) >= 1, - "native runtime spine should record a session", - )?; - let telemetry: Value = serde_json::from_str(&fs::read_to_string( - artifact_dir.join("agent-function-telemetry.json"), - )?)?; - require( - telemetry["appServerCalls"].as_u64() == Some(0) - && telemetry["transport"].as_str() == Some("cultcache"), - "native coordinator telemetry must prove zero app-server calls", - )?; - Ok(()) -} - -fn require_operator_safe(value: &Value, path: &str) -> Result<()> { - match value { - Value::Object(map) => { - if let Some(raw_result) = map.get("rawResult") { - require( - raw_result.get("sealed").and_then(Value::as_bool) == Some(true), - &format!("{path}.rawResult should be sealed in operator-facing artifacts"), - )?; - } - for (key, item) in map { - require_operator_safe(item, &format!("{path}.{key}"))?; - } - } - Value::Array(items) => { - for (index, item) in items.iter().enumerate() { - require_operator_safe(item, &format!("{path}[{index}]"))?; - } - } - _ => {} - } - Ok(()) -} - -fn reset_artifact_root(root: &Path, path: &Path) -> Result<()> { - let dogfood_root = root.join(".epiphany-smoke").canonicalize().or_else(|_| { - let dogfood_root = root.join(".epiphany-smoke"); - fs::create_dir_all(&dogfood_root)?; - dogfood_root.canonicalize() - })?; - let parent = path.parent().unwrap_or_else(|| Path::new(".")); - fs::create_dir_all(parent)?; - let resolved = if path.exists() { - path.canonicalize()? - } else { - parent.canonicalize()?.join(path.file_name().unwrap()) - }; - if resolved == dogfood_root || !resolved.starts_with(&dogfood_root) { - return Err(anyhow!( - "refusing to delete non-dogfood artifact root: {}", - path.display() - )); - } - if path.exists() { - fs::remove_dir_all(path)?; - } - fs::create_dir_all(path)?; - Ok(()) -} - -fn sibling_exe(name: &str) -> Result { - let mut exe = env::current_exe().context("failed to resolve current executable")?; - exe.set_file_name(format!("{name}{}", env::consts::EXE_SUFFIX)); - Ok(exe) -} - -fn ensure_coordinator_built(root: &Path, coordinator: &Path) -> Result<()> { - ensure_epiphany_bin_built(root, coordinator, "epiphany-mvp-coordinator") -} - -fn ensure_epiphany_bin_built(root: &Path, binary: &Path, bin_name: &str) -> Result<()> { - if binary.exists() { - return Ok(()); - } - let status = Command::new("cargo") - .current_dir(root) - .arg("build") - .arg("--manifest-path") - .arg(root.join("epiphany-core").join("Cargo.toml")) - .arg("--bin") - .arg(bin_name) - .status() - .with_context(|| format!("failed to build {bin_name}"))?; - require( - status.success() && binary.exists(), - &format!("native binary was not built: {}", binary.display()), - ) -} - -fn absolute_path(path: &Path) -> Result { - if path.is_absolute() { - Ok(path.to_path_buf()) - } else { - Ok(env::current_dir()?.join(path)) - } -} - -fn write_json(path: &Path, value: &Value) -> Result<()> { - if let Some(parent) = path.parent() { - fs::create_dir_all(parent)?; - } - fs::write(path, format!("{}\n", serde_json::to_string_pretty(value)?))?; - Ok(()) -} - -fn require(condition: bool, message: &str) -> Result<()> { - if condition { - Ok(()) - } else { - Err(anyhow!("{message}")) - } -} - -fn take_path(args: &mut impl Iterator, name: &str) -> Result { - args.next() - .map(PathBuf::from) - .ok_or_else(|| anyhow!("{name} requires a value")) -} diff --git a/epiphany-core/src/bin/epiphany-operator-run.rs b/epiphany-core/src/bin/epiphany-operator-run.rs deleted file mode 100644 index d50236db..00000000 --- a/epiphany-core/src/bin/epiphany-operator-run.rs +++ /dev/null @@ -1,486 +0,0 @@ -use anyhow::Context; -use anyhow::Result; -use chrono::Utc; -use epiphany_core::EPIPHANY_CULTMESH_INTERNAL_VERSE_ID; -use epiphany_core::EPIPHANY_CULTMESH_OPERATOR_RUN_INTENT_SCHEMA_VERSION; -use epiphany_core::EPIPHANY_CULTMESH_OPERATOR_RUN_RECEIPT_SCHEMA_VERSION; -use epiphany_core::EpiphanyCultMeshOperatorRunIntentEntry; -use epiphany_core::EpiphanyCultMeshOperatorRunReceiptEntry; -use epiphany_core::epiphany_cultmesh_coordinator_run_receipt_from_summary_json; -use epiphany_core::epiphany_cultmesh_hands_action_gate_from_summary_json; -use epiphany_core::epiphany_cultmesh_role_review_event_from_summary_json; -use epiphany_core::load_epiphany_cultmesh_operator_run_intent; -use epiphany_core::load_epiphany_cultmesh_operator_run_receipt; -use epiphany_core::load_latest_epiphany_cultmesh_operator_run_intent; -use epiphany_core::load_latest_epiphany_cultmesh_operator_run_receipt; -use epiphany_core::write_epiphany_cultmesh_coordinator_run_receipt; -use epiphany_core::write_epiphany_cultmesh_hands_action_gate; -use epiphany_core::write_epiphany_cultmesh_operator_run_intent; -use epiphany_core::write_epiphany_cultmesh_operator_run_receipt; -use epiphany_core::write_epiphany_cultmesh_role_review_event; -use serde_json::Value; -use serde_json::json; -use std::env; -use std::fs; -use std::path::PathBuf; - -fn main() -> Result<()> { - let args = Args::parse()?; - match args.command.as_str() { - "intent" => { - let intent = EpiphanyCultMeshOperatorRunIntentEntry { - schema_version: EPIPHANY_CULTMESH_OPERATOR_RUN_INTENT_SCHEMA_VERSION.to_string(), - runtime_id: args.runtime_id.clone(), - verse_id: EPIPHANY_CULTMESH_INTERNAL_VERSE_ID.to_string(), - run_id: args.run_id.clone(), - requested_at_utc: Utc::now().to_rfc3339(), - mode: args.mode.clone(), - root: args.root.clone(), - workspace: args.workspace.clone(), - thread_id: args.thread_id.clone(), - codex_home: args.codex_home.clone(), - target_dir: args.target_dir.clone(), - max_steps: args.max_steps, - timeout_seconds: args.timeout_seconds, - auto_review: args.auto_review, - no_ephemeral: args.no_ephemeral, - artifact_root: args.artifact_root.clone(), - dogfood_root: args.dogfood_root.clone(), - }; - let written = write_epiphany_cultmesh_operator_run_intent(&args.store, intent)?; - print_json(json!({"status": "written", "store": args.store, "intent": written}))?; - } - "receipt" => { - let intent = load_epiphany_cultmesh_operator_run_intent( - &args.store, - &args.runtime_id, - &args.run_id, - )? - .context("operator-run receipt requires a persisted intent for its run id")?; - if intent.mode != args.mode { - anyhow::bail!( - "operator-run receipt mode does not match its intent: run={} requested mode={} intent mode={}", - args.run_id, - args.mode, - intent.mode - ); - } - let result_path = canonical_file(&args.result_path, "--result-path")?; - let artifact_root = canonical_directory(&args.artifact_root, "--artifact-root")?; - if !result_path.starts_with(&artifact_root) { - anyhow::bail!( - "operator-run result {} is outside artifact root {}", - result_path.display(), - artifact_root.display() - ); - } - let result_source = fs::read_to_string(&result_path).with_context(|| { - format!("failed to read result artifact {}", result_path.display()) - })?; - let _: Value = serde_json::from_str(result_source.trim_start_matches('\u{feff}')) - .with_context(|| { - format!( - "operator-run result is not valid JSON: {}", - result_path.display() - ) - })?; - let requested_at = chrono::DateTime::parse_from_rfc3339(&intent.requested_at_utc) - .context("operator-run intent has invalid requested_at_utc")? - .with_timezone(&Utc); - let modified_at: chrono::DateTime = fs::metadata(&result_path)? - .modified() - .context("operator-run result has no modification time")? - .into(); - if modified_at < requested_at { - anyhow::bail!( - "operator-run result predates its intent: result={} intent={}", - modified_at.to_rfc3339(), - requested_at.to_rfc3339() - ); - } - let mut artifact_refs = Vec::new(); - push_non_empty(&mut artifact_refs, &args.result_path); - push_non_empty(&mut artifact_refs, &args.artifact_root); - push_non_empty(&mut artifact_refs, &args.dogfood_root); - let receipt = EpiphanyCultMeshOperatorRunReceiptEntry { - schema_version: EPIPHANY_CULTMESH_OPERATOR_RUN_RECEIPT_SCHEMA_VERSION.to_string(), - runtime_id: args.runtime_id.clone(), - verse_id: EPIPHANY_CULTMESH_INTERNAL_VERSE_ID.to_string(), - run_id: args.run_id.clone(), - completed_at_utc: Utc::now().to_rfc3339(), - mode: args.mode.clone(), - status: "completed".to_string(), - result_path: args.result_path.clone(), - artifact_root: args.artifact_root.clone(), - dogfood_root: args.dogfood_root.clone(), - operator_snapshot_store: args.operator_snapshot_store.clone(), - operator_snapshot_id: args.operator_snapshot_id.clone(), - artifact_refs, - notes: vec![ - "Receipt records local operator-run completion; referenced artifacts remain evidence, not owner state.".to_string(), - "Codex app-server output is compatibility projection until the run path is native end to end.".to_string(), - ], - }; - let written = write_epiphany_cultmesh_operator_run_receipt(&args.store, receipt)?; - print_json(json!({"status": "written", "store": args.store, "receipt": written}))?; - } - "latest" => { - let intent = - load_latest_epiphany_cultmesh_operator_run_intent(&args.store, &args.runtime_id)?; - let receipt = if let Some(intent) = intent.as_ref() { - load_epiphany_cultmesh_operator_run_receipt( - &args.store, - &args.runtime_id, - &intent.run_id, - )? - } else { - load_latest_epiphany_cultmesh_operator_run_receipt(&args.store, &args.runtime_id)? - }; - print_json(json!({ - "status": operator_run_status(intent.as_ref(), receipt.as_ref()), - "store": args.store, - "intent": intent, - "receipt": receipt, - }))?; - } - "coordinator-receipt" => { - let summary_path = args - .coordinator_summary - .clone() - .context("coordinator-receipt requires --coordinator-summary")?; - let summary_source = fs::read_to_string(&summary_path) - .with_context(|| format!("failed to read {}", summary_path.display()))?; - let summary_json: Value = - serde_json::from_str(summary_source.trim_start_matches('\u{feff}')) - .with_context(|| format!("failed to parse {}", summary_path.display()))?; - let receipt_id = if args.coordinator_receipt_id.trim().is_empty() { - format!("coordinator-cultmesh-{}", args.run_id) - } else { - args.coordinator_receipt_id.clone() - }; - let written = write_epiphany_cultmesh_coordinator_run_receipt( - &args.store, - epiphany_cultmesh_coordinator_run_receipt_from_summary_json( - args.runtime_id.clone(), - receipt_id, - Utc::now().to_rfc3339(), - args.artifact_root.clone(), - &summary_json, - )?, - )?; - let hands_action_gate = epiphany_cultmesh_hands_action_gate_from_summary_json( - args.runtime_id.clone(), - Utc::now().to_rfc3339(), - summary_path.display().to_string(), - &summary_json, - )? - .map(|gate| write_epiphany_cultmesh_hands_action_gate(&args.store, gate)) - .transpose()?; - let role_review_event = epiphany_cultmesh_role_review_event_from_summary_json( - args.runtime_id.clone(), - Utc::now().to_rfc3339(), - summary_path.display().to_string(), - &summary_json, - )? - .map(|event| write_epiphany_cultmesh_role_review_event(&args.store, event)) - .transpose()?; - print_json(json!({ - "status": "written", - "store": args.store, - "coordinatorReceipt": written, - "handsActionGate": hands_action_gate, - "roleReviewEvent": role_review_event, - }))?; - } - "smoke" => { - let intent = EpiphanyCultMeshOperatorRunIntentEntry { - schema_version: EPIPHANY_CULTMESH_OPERATOR_RUN_INTENT_SCHEMA_VERSION.to_string(), - runtime_id: args.runtime_id.clone(), - verse_id: EPIPHANY_CULTMESH_INTERNAL_VERSE_ID.to_string(), - run_id: args.run_id.clone(), - requested_at_utc: "2026-05-27T00:00:00Z".to_string(), - mode: "status".to_string(), - root: "E:\\Projects\\EpiphanyAgent".to_string(), - workspace: "E:\\Projects\\EpiphanyAgent".to_string(), - thread_id: String::new(), - codex_home: "C:\\Users\\Meta\\.codex".to_string(), - target_dir: "C:\\Users\\Meta\\.cargo-target-codex".to_string(), - max_steps: 4, - timeout_seconds: 240, - auto_review: false, - no_ephemeral: false, - artifact_root: ".epiphany-run/local-smoke".to_string(), - dogfood_root: ".epiphany-dogfood/local-smoke".to_string(), - }; - let receipt = EpiphanyCultMeshOperatorRunReceiptEntry { - schema_version: EPIPHANY_CULTMESH_OPERATOR_RUN_RECEIPT_SCHEMA_VERSION.to_string(), - runtime_id: args.runtime_id.clone(), - verse_id: EPIPHANY_CULTMESH_INTERNAL_VERSE_ID.to_string(), - run_id: args.run_id.clone(), - completed_at_utc: "2026-05-27T00:00:01Z".to_string(), - mode: "status".to_string(), - status: "completed".to_string(), - result_path: ".epiphany-run/local-smoke/status.json".to_string(), - artifact_root: ".epiphany-run/local-smoke".to_string(), - dogfood_root: ".epiphany-dogfood/local-smoke".to_string(), - operator_snapshot_store: ".epiphany-run/cultmesh/operator-snapshots.ccmp" - .to_string(), - operator_snapshot_id: "local-smoke-status".to_string(), - artifact_refs: vec![".epiphany-run/local-smoke/status.json".to_string()], - notes: vec!["smoke receipt".to_string()], - }; - write_epiphany_cultmesh_operator_run_intent(&args.store, intent.clone())?; - write_epiphany_cultmesh_operator_run_receipt(&args.store, receipt.clone())?; - let latest_intent = - load_latest_epiphany_cultmesh_operator_run_intent(&args.store, &args.runtime_id)?; - let latest_receipt = - load_latest_epiphany_cultmesh_operator_run_receipt(&args.store, &args.runtime_id)?; - if latest_intent != Some(intent) || latest_receipt != Some(receipt) { - anyhow::bail!("operator run intent/receipt did not round-trip through CultMesh"); - } - print_json(json!({ - "status": "ok", - "store": args.store, - "runtimeId": args.runtime_id, - "runId": args.run_id, - }))?; - } - other => anyhow::bail!("unknown command {other:?}; use intent, receipt, latest, or smoke"), - } - Ok(()) -} - -fn operator_run_status( - intent: Option<&EpiphanyCultMeshOperatorRunIntentEntry>, - receipt: Option<&EpiphanyCultMeshOperatorRunReceiptEntry>, -) -> &'static str { - match (intent, receipt) { - (None, None) => "missing", - (Some(_), None) => "requested", - (None, Some(_)) => "orphaned-receipt", - (Some(intent), Some(receipt)) - if intent.run_id == receipt.run_id && receipt.status == "completed" => - { - "completed" - } - (Some(_), Some(_)) => "attention", - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn intent(run_id: &str) -> EpiphanyCultMeshOperatorRunIntentEntry { - EpiphanyCultMeshOperatorRunIntentEntry { - schema_version: EPIPHANY_CULTMESH_OPERATOR_RUN_INTENT_SCHEMA_VERSION.to_string(), - runtime_id: "epiphany-test".to_string(), - verse_id: EPIPHANY_CULTMESH_INTERNAL_VERSE_ID.to_string(), - run_id: run_id.to_string(), - requested_at_utc: "2026-07-13T00:00:00Z".to_string(), - mode: "status".to_string(), - root: "root".to_string(), - workspace: "workspace".to_string(), - thread_id: String::new(), - codex_home: String::new(), - target_dir: String::new(), - max_steps: 1, - timeout_seconds: 1, - auto_review: false, - no_ephemeral: false, - artifact_root: "artifacts".to_string(), - dogfood_root: String::new(), - } - } - - fn receipt(run_id: &str, status: &str) -> EpiphanyCultMeshOperatorRunReceiptEntry { - EpiphanyCultMeshOperatorRunReceiptEntry { - schema_version: EPIPHANY_CULTMESH_OPERATOR_RUN_RECEIPT_SCHEMA_VERSION.to_string(), - runtime_id: "epiphany-test".to_string(), - verse_id: EPIPHANY_CULTMESH_INTERNAL_VERSE_ID.to_string(), - run_id: run_id.to_string(), - completed_at_utc: "2026-07-13T00:00:01Z".to_string(), - mode: "status".to_string(), - status: status.to_string(), - result_path: "result.json".to_string(), - artifact_root: "artifacts".to_string(), - dogfood_root: String::new(), - operator_snapshot_store: String::new(), - operator_snapshot_id: String::new(), - artifact_refs: Vec::new(), - notes: Vec::new(), - } - } - - #[test] - fn intent_presence_is_requested_not_ready() { - let intent = intent("run-new"); - assert_eq!(operator_run_status(Some(&intent), None), "requested"); - } - - #[test] - fn only_matching_completed_receipt_closes_latest_intent() { - let intent = intent("run-new"); - let old_receipt = receipt("run-old", "completed"); - let matching_receipt = receipt("run-new", "completed"); - assert_eq!( - operator_run_status(Some(&intent), Some(&old_receipt)), - "attention" - ); - assert_eq!( - operator_run_status(Some(&intent), Some(&matching_receipt)), - "completed" - ); - } -} - -struct Args { - command: String, - store: PathBuf, - runtime_id: String, - run_id: String, - mode: String, - root: String, - workspace: String, - thread_id: String, - codex_home: String, - target_dir: String, - max_steps: u32, - timeout_seconds: u32, - auto_review: bool, - no_ephemeral: bool, - artifact_root: String, - dogfood_root: String, - result_path: String, - operator_snapshot_store: String, - operator_snapshot_id: String, - coordinator_summary: Option, - coordinator_receipt_id: String, -} - -impl Args { - fn parse() -> Result { - let mut values = env::args().skip(1); - let command = values.next().unwrap_or_else(|| "latest".to_string()); - let mut args = Args { - command, - store: PathBuf::from(".epiphany-run/cultmesh/operator-runs.ccmp"), - runtime_id: "epiphany-local".to_string(), - run_id: format!("run-{}", Utc::now().timestamp()), - mode: "status".to_string(), - root: String::new(), - workspace: String::new(), - thread_id: String::new(), - codex_home: String::new(), - target_dir: String::new(), - max_steps: 4, - timeout_seconds: 240, - auto_review: false, - no_ephemeral: false, - artifact_root: String::new(), - dogfood_root: String::new(), - result_path: String::new(), - operator_snapshot_store: String::new(), - operator_snapshot_id: String::new(), - coordinator_summary: None, - coordinator_receipt_id: String::new(), - }; - - while let Some(arg) = values.next() { - match arg.as_str() { - "--store" => args.store = PathBuf::from(next(&mut values, "--store")?), - "--runtime-id" => args.runtime_id = next(&mut values, "--runtime-id")?, - "--run-id" => args.run_id = next(&mut values, "--run-id")?, - "--mode" => args.mode = next(&mut values, "--mode")?, - "--root" => args.root = next(&mut values, "--root")?, - "--workspace" => args.workspace = next(&mut values, "--workspace")?, - "--thread-id" => args.thread_id = next(&mut values, "--thread-id")?, - "--codex-home" => args.codex_home = next(&mut values, "--codex-home")?, - "--target-dir" => args.target_dir = next(&mut values, "--target-dir")?, - "--max-steps" => args.max_steps = parse_u32(&mut values, "--max-steps")?, - "--timeout-seconds" => { - args.timeout_seconds = parse_u32(&mut values, "--timeout-seconds")? - } - "--auto-review" => args.auto_review = parse_bool(&mut values, "--auto-review")?, - "--no-ephemeral" => args.no_ephemeral = parse_bool(&mut values, "--no-ephemeral")?, - "--artifact-root" => args.artifact_root = next(&mut values, "--artifact-root")?, - "--dogfood-root" => args.dogfood_root = next(&mut values, "--dogfood-root")?, - "--result-path" => args.result_path = next(&mut values, "--result-path")?, - "--operator-snapshot-store" => { - args.operator_snapshot_store = next(&mut values, "--operator-snapshot-store")? - } - "--operator-snapshot-id" => { - args.operator_snapshot_id = next(&mut values, "--operator-snapshot-id")? - } - "--coordinator-summary" => { - args.coordinator_summary = - Some(PathBuf::from(next(&mut values, "--coordinator-summary")?)) - } - "--coordinator-receipt-id" => { - args.coordinator_receipt_id = next(&mut values, "--coordinator-receipt-id")? - } - _ => anyhow::bail!("unknown argument {arg:?}"), - } - } - - if let Some(parent) = args.store.parent() { - fs::create_dir_all(parent)?; - } - Ok(args) - } -} - -fn next(values: &mut impl Iterator, name: &str) -> Result { - values - .next() - .with_context(|| format!("missing {name} value")) -} - -fn parse_u32(values: &mut impl Iterator, name: &str) -> Result { - next(values, name)? - .parse::() - .with_context(|| format!("{name} must be a non-negative integer")) -} - -fn parse_bool(values: &mut impl Iterator, name: &str) -> Result { - match next(values, name)?.as_str() { - "true" | "1" | "yes" => Ok(true), - "false" | "0" | "no" => Ok(false), - _ => anyhow::bail!("{name} must be true/false"), - } -} - -fn push_non_empty(items: &mut Vec, value: &str) { - if !value.trim().is_empty() { - items.push(value.to_string()); - } -} - -fn canonical_file(value: &str, name: &str) -> Result { - if value.trim().is_empty() { - anyhow::bail!("operator-run receipt requires {name}"); - } - let path = - fs::canonicalize(value).with_context(|| format!("{name} does not exist: {value}"))?; - if !path.is_file() { - anyhow::bail!("{name} is not a file: {}", path.display()); - } - Ok(path) -} - -fn canonical_directory(value: &str, name: &str) -> Result { - if value.trim().is_empty() { - anyhow::bail!("operator-run receipt requires {name}"); - } - let path = - fs::canonicalize(value).with_context(|| format!("{name} does not exist: {value}"))?; - if !path.is_dir() { - anyhow::bail!("{name} is not a directory: {}", path.display()); - } - Ok(path) -} - -fn print_json(value: Value) -> Result<()> { - println!("{}", serde_json::to_string_pretty(&value)?); - Ok(()) -} diff --git a/epiphany-core/src/bin/epiphany-operator-snapshot.rs b/epiphany-core/src/bin/epiphany-operator-snapshot.rs deleted file mode 100644 index a2830176..00000000 --- a/epiphany-core/src/bin/epiphany-operator-snapshot.rs +++ /dev/null @@ -1,167 +0,0 @@ -use anyhow::Context; -use anyhow::Result; -use chrono::Utc; -use epiphany_core::epiphany_cultmesh_operator_snapshot_from_status_json; -use epiphany_core::load_latest_epiphany_cultmesh_operator_snapshot; -use epiphany_core::write_epiphany_cultmesh_operator_snapshot; -use serde_json::Value; -use serde_json::json; -use std::env; -use std::fs; -use std::path::PathBuf; - -fn main() -> Result<()> { - let args = Args::parse()?; - match args.command.as_str() { - "from-status" => { - let input = args.input.context("from-status requires --input")?; - let source = fs::read_to_string(&input) - .with_context(|| format!("failed to read {}", input.display()))?; - let status_json: Value = serde_json::from_str(source.trim_start_matches('\u{feff}')) - .with_context(|| format!("failed to parse {}", input.display()))?; - let snapshot = epiphany_cultmesh_operator_snapshot_from_status_json( - args.runtime_id.clone(), - args.snapshot_id.clone(), - Utc::now().to_rfc3339(), - args.source_mode.clone(), - input.to_string_lossy(), - &status_json, - )?; - let written = write_epiphany_cultmesh_operator_snapshot(&args.store, snapshot)?; - print_json(json!({ - "status": "written", - "store": args.store, - "snapshot": written, - "toolInvocationIntent": null, - "toolInvocationReceipt": null, - "toolInvocationAuthority": "runtime-spine-only", - }))?; - } - "latest" => { - let latest = - load_latest_epiphany_cultmesh_operator_snapshot(&args.store, &args.runtime_id)?; - print_json(json!({ - "status": if latest.is_some() { "loaded" } else { "missing" }, - "store": args.store, - "snapshot": latest, - }))?; - } - "smoke" => { - let status_json = json!({ - "threadId": "thread-smoke", - "scene": { - "scene": { - "stateStatus": "missing", - "availableActions": ["crrc", "roles"] - } - }, - "pressure": { - "pressure": { - "level": "low" - } - }, - "reorient": { - "decision": { - "action": "regather", - "nextAction": "Regather source context." - } - }, - "crrc": { - "recommendation": { - "action": "regatherManually" - } - }, - "coordinator": { - "action": "wait" - }, - "rawResult": { - "sealed": true - } - }); - let snapshot = epiphany_cultmesh_operator_snapshot_from_status_json( - args.runtime_id.clone(), - args.snapshot_id.clone(), - "2026-05-27T00:00:00Z", - "status", - ".epiphany-smoke/operator-status.json", - &status_json, - )?; - write_epiphany_cultmesh_operator_snapshot(&args.store, snapshot.clone())?; - let latest = - load_latest_epiphany_cultmesh_operator_snapshot(&args.store, &args.runtime_id)?; - if latest != Some(snapshot) { - anyhow::bail!("operator snapshot did not round-trip through CultMesh"); - } - print_json(json!({ - "status": "ok", - "store": args.store, - "runtimeId": args.runtime_id, - "snapshotId": args.snapshot_id, - }))?; - } - other => anyhow::bail!("unknown command {other:?}; use from-status, latest, or smoke"), - } - Ok(()) -} - -struct Args { - command: String, - store: PathBuf, - runtime_id: String, - snapshot_id: String, - source_mode: String, - input: Option, -} - -impl Args { - fn parse() -> Result { - let mut values = env::args().skip(1); - let command = values.next().unwrap_or_else(|| "latest".to_string()); - let mut store = PathBuf::from(".epiphany-run/cultmesh/operator-snapshots.ccmp"); - let mut runtime_id = "epiphany-local".to_string(); - let mut snapshot_id = format!("snapshot-{}", Utc::now().timestamp()); - let mut source_mode = "status".to_string(); - let mut input = None; - - while let Some(arg) = values.next() { - match arg.as_str() { - "--store" => { - store = PathBuf::from(values.next().context("missing --store value")?); - } - "--runtime-id" => { - runtime_id = values.next().context("missing --runtime-id value")?; - } - "--snapshot-id" => { - snapshot_id = values.next().context("missing --snapshot-id value")?; - } - "--source-mode" => { - source_mode = values.next().context("missing --source-mode value")?; - } - "--input" => { - input = Some(PathBuf::from( - values.next().context("missing --input value")?, - )); - } - _ => anyhow::bail!("unknown argument {arg:?}"), - } - } - - if let Some(parent) = store.parent() { - fs::create_dir_all(parent)?; - } - - Ok(Self { - command, - store, - runtime_id, - snapshot_id, - source_mode, - input, - }) - } -} - -fn print_json(value: Value) -> Result<()> { - println!("{}", serde_json::to_string_pretty(&value)?); - Ok(()) -} diff --git a/epiphany-core/src/bin/epiphany-persona-discord-crossing-fixture.rs b/epiphany-core/src/bin/epiphany-persona-discord-crossing-fixture.rs deleted file mode 100644 index 8c674931..00000000 --- a/epiphany-core/src/bin/epiphany-persona-discord-crossing-fixture.rs +++ /dev/null @@ -1,48 +0,0 @@ -use std::{env, path::PathBuf}; - -use anyhow::{Result, anyhow}; -use epiphany_core::{ - enroll_persona_discord_request_identity, export_persona_discord_request_anchor_candidate, - insert_persona_discord_delivery_request, open_persona_discord_request_identity, - sign_persona_discord_delivery_request, -}; - -fn main() -> Result<()> { - let mut values = std::collections::BTreeMap::new(); - let mut args = env::args().skip(1); - while let Some(key) = args.next() { - values.insert( - key.clone(), - args.next() - .ok_or_else(|| anyhow!("{key} requires a value"))?, - ); - } - let value = |key: &str| { - values - .get(key) - .cloned() - .ok_or_else(|| anyhow!("{key} is required")) - }; - let store = PathBuf::from(value("--request-store")?); - let identity = PathBuf::from(value("--identity-store")?); - let anchor = PathBuf::from(value("--request-anchor")?); - enroll_persona_discord_request_identity(&identity)?; - export_persona_discord_request_anchor_candidate(&identity, &anchor, "epiphany-yggdrasil")?; - let signer = open_persona_discord_request_identity(&identity)?; - let issued = chrono::Utc::now(); - let request = sign_persona_discord_delivery_request( - &signer, - "rust-crossing-smoke-1".into(), - "effect-rust-crossing-smoke-1".into(), - "epiphany-yggdrasil".into(), - "epiphany.Persona".into(), - "123".into(), - "".into(), - "Rust-authored crossing smoke".into(), - issued.to_rfc3339(), - (issued + chrono::Duration::seconds(120)).to_rfc3339(), - )?; - insert_persona_discord_delivery_request(&store, &request)?; - println!("{}", request.request_id); - Ok(()) -} diff --git a/epiphany-core/src/bin/epiphany-persona-discord-rudp-client-fixture.rs b/epiphany-core/src/bin/epiphany-persona-discord-rudp-client-fixture.rs deleted file mode 100644 index 3f7362f9..00000000 --- a/epiphany-core/src/bin/epiphany-persona-discord-rudp-client-fixture.rs +++ /dev/null @@ -1,82 +0,0 @@ -use std::{env, net::SocketAddr, path::PathBuf, time::Duration}; - -use anyhow::{Result, anyhow}; -use epiphany_core::{ - enroll_persona_discord_request_identity, exchange_persona_discord_delivery_rudp, - export_persona_discord_request_anchor_candidate, insert_persona_discord_delivery_request, - load_persona_discord_receipt_anchor, open_persona_discord_request_identity, - sign_persona_discord_delivery_request, -}; - -fn main() -> Result<()> { - let values = args()?; - let request_store = path(&values, "--request-store")?; - let receipt_store = path(&values, "--receipt-store")?; - let identity_store = path(&values, "--identity-store")?; - let request_anchor = path(&values, "--request-anchor")?; - let receipt_anchor = path(&values, "--receipt-anchor")?; - let endpoint: SocketAddr = value(&values, "--endpoint")?.parse()?; - enroll_persona_discord_request_identity(&identity_store)?; - export_persona_discord_request_anchor_candidate( - &identity_store, - &request_anchor, - "epiphany-starfire", - )?; - let signer = open_persona_discord_request_identity(&identity_store)?; - let issued = chrono::Utc::now(); - let request = sign_persona_discord_delivery_request( - &signer, - "persona-discord:rudp-cross-language:0".into(), - "effect-rudp-cross-language".into(), - "epiphany-starfire".into(), - "epiphany.Persona".into(), - "123".into(), - "456".into(), - "Cross-language Persona nerve smoke.".into(), - issued.to_rfc3339(), - (issued + chrono::Duration::seconds(90)).to_rfc3339(), - )?; - insert_persona_discord_delivery_request(&request_store, &request)?; - let anchor = load_persona_discord_receipt_anchor(&receipt_anchor)?; - let receipt = exchange_persona_discord_delivery_rudp( - endpoint, - "epiphany-starfire", - &request_store, - &receipt_store, - &request, - &anchor, - Duration::from_secs(5), - )?; - println!( - "{}", - serde_json::to_string(&serde_json::json!({ - "requestId": receipt.request_id, - "status": receipt.status, - "messageId": receipt.message_id, - "providerIdentityId": receipt.provider_identity_id, - }))? - ); - Ok(()) -} - -fn args() -> Result> { - let mut values = std::collections::BTreeMap::new(); - let mut args = env::args().skip(1); - while let Some(key) = args.next() { - values.insert( - key, - args.next() - .ok_or_else(|| anyhow!("argument requires value"))?, - ); - } - Ok(values) -} -fn value(values: &std::collections::BTreeMap, key: &str) -> Result { - values - .get(key) - .cloned() - .ok_or_else(|| anyhow!("{key} is required")) -} -fn path(values: &std::collections::BTreeMap, key: &str) -> Result { - Ok(value(values, key)?.into()) -} diff --git a/epiphany-core/src/bin/epiphany-persona-discord.rs b/epiphany-core/src/bin/epiphany-persona-discord.rs deleted file mode 100644 index 5e7c25b0..00000000 --- a/epiphany-core/src/bin/epiphany-persona-discord.rs +++ /dev/null @@ -1,1530 +0,0 @@ -use anyhow::Context; -use anyhow::Result; -use anyhow::anyhow; -use chrono::SecondsFormat; -use epiphany_core::EPIPHANY_CULTMESH_LOCAL_AREA_VERSE_ID; -use epiphany_core::EPIPHANY_CULTMESH_PERSONA_SPEECH_AUDIT_SCHEMA_VERSION; -use epiphany_core::EpiphanyCultMeshPersonaSpeechAuditEntry; -use epiphany_core::load_latest_epiphany_cultmesh_persona_speech_audit; -use epiphany_core::write_epiphany_cultmesh_persona_speech_audit; -use serde_json::Value; -use std::cmp::Reverse; -use std::env; -use std::fs; -use std::io::Read; -use std::path::Path; -use std::path::PathBuf; -use std::process::Command; -use uuid::Uuid; - -const CHAT_SCHEMA_VERSION: &str = "epiphany.persona_chat.v0"; -const BUBBLE_SCHEMA_VERSION: &str = "epiphany.persona_bubble.v0"; -const SPEECH_AUDIT_SCHEMA_VERSION: &str = "epiphany.persona_speech_audit.v0"; - -#[derive(Clone, Debug, Default)] -struct PersonaConfig { - allowed_channel_name: String, - allowed_channel_id: Option, - allowed_channel_id_env: Option, - persona_name: Option, - persona_avatar_url: Option, - bifrost_bridge_cli_path: Option, - bifrost_identity: Option, - bifrost_source_kind: Option, - bifrost_authority_ref: Option, - heimdall_capability_ref_env: Option, -} - -fn main() -> Result<()> { - let mut args = env::args().skip(1); - let Some(command) = args.next() else { - return usage(); - }; - let mut config_path = PathBuf::from("state/persona-discord.toml"); - let mut artifact_dir = PathBuf::from(".epiphany-persona"); - let mut cultmesh_store = PathBuf::from(".epiphany-run/cultmesh/persona-speech.ccmp"); - let mut runtime_id = "epiphany-local".to_string(); - let mut content: Option = None; - let mut channel_id: Option = None; - let mut source = "epiphany/Persona".to_string(); - let mut mood = "attentive".to_string(); - let mut limit = 8_usize; - let mut persona_name: Option = None; - let mut persona_avatar_url: Option = None; - let mut reply_to_message_id: Option = None; - - while let Some(arg) = args.next() { - match arg.as_str() { - "--config" => config_path = next_path(&mut args, "--config")?, - "--artifact-dir" => artifact_dir = next_path(&mut args, "--artifact-dir")?, - "--cultmesh-store" => cultmesh_store = next_path(&mut args, "--cultmesh-store")?, - "--runtime-id" => runtime_id = next_value(&mut args, "--runtime-id")?, - "--content" => content = Some(next_value(&mut args, "--content")?), - "--channel-id" => channel_id = Some(next_value(&mut args, "--channel-id")?), - "--source" => source = next_value(&mut args, "--source")?, - "--mood" => mood = next_value(&mut args, "--mood")?, - "--limit" => limit = next_value(&mut args, "--limit")?.parse()?, - "--persona-name" => persona_name = Some(next_value(&mut args, "--persona-name")?), - "--persona-avatar-url" => { - persona_avatar_url = Some(next_value(&mut args, "--persona-avatar-url")?) - } - "--reply-to-message-id" => { - reply_to_message_id = Some(next_value(&mut args, "--reply-to-message-id")?) - } - _ => return Err(anyhow!("unknown argument {arg:?}")), - } - } - - let result = match command.as_str() { - "draft" => { - let config = load_config(&config_path)?; - let content = read_content(content)?; - run_draft( - &content, - &config, - &artifact_dir, - &cultmesh_store, - &runtime_id, - PersonaChatStatus::Draft, - "drafted without posting", - )? - } - "bubble" => { - let content = read_content(content)?; - run_bubble( - &content, - &artifact_dir, - &cultmesh_store, - &runtime_id, - &source, - &mood, - )? - } - "post" => { - let config = load_config(&config_path)?; - let content = read_content(content)?; - run_post( - &content, - &config, - &artifact_dir, - &cultmesh_store, - &runtime_id, - channel_id, - persona_name, - persona_avatar_url, - reply_to_message_id, - )? - } - "latest" => serde_json::json!({ - "ok": true, - "artifactDir": artifact_dir, - "latestArtifacts": latest_persona_artifacts(&artifact_dir, limit), - }), - "smoke" => run_smoke()?, - _ => return usage(), - }; - let ok = result["ok"].as_bool().unwrap_or(false); - println!("{}", serde_json::to_string_pretty(&result)?); - if !ok { - std::process::exit(1); - } - Ok(()) -} - -fn run_draft( - content: &str, - config: &PersonaConfig, - artifact_dir: &Path, - cultmesh_store: &Path, - runtime_id: &str, - status: PersonaChatStatus, - reason: &str, -) -> Result { - ensure_content(content, "Persona chat")?; - let audit = audit_persona_speech( - content, - PersonaSpeechActionKind::Draft, - artifact_dir, - cultmesh_store, - runtime_id, - allowed_channel_id(config).as_deref(), - false, - )?; - let path = write_draft(content, config, artifact_dir, status, reason, Some(&audit))?; - Ok(serde_json::json!({ - "ok": status != PersonaChatStatus::Blocked, - "posted": false, - "draftPath": path, - "speechAudit": audit, - })) -} - -fn run_bubble( - content: &str, - artifact_dir: &Path, - cultmesh_store: &Path, - runtime_id: &str, - source: &str, - mood: &str, -) -> Result { - ensure_content(content, "Persona bubble")?; - let audit = audit_persona_speech( - content, - PersonaSpeechActionKind::Bubble, - artifact_dir, - cultmesh_store, - runtime_id, - None, - false, - )?; - let payload = bubble_payload(content, source, mood); - let path = artifact_dir.join(format!( - "Persona-bubble-{}-{}.json", - now_stamp(), - short_id() - )); - write_json(&path, &payload)?; - Ok(serde_json::json!({ - "ok": true, - "posted": false, - "bubblePath": path, - "bubble": payload, - "speechAudit": audit, - })) -} - -fn run_post( - content: &str, - config: &PersonaConfig, - artifact_dir: &Path, - cultmesh_store: &Path, - runtime_id: &str, - channel_id: Option, - persona_name: Option, - persona_avatar_url: Option, - reply_to_message_id: Option, -) -> Result { - ensure_content(content, "Persona chat")?; - let configured_channel_id = allowed_channel_id(config); - let requested_channel_id = channel_id.or_else(|| configured_channel_id.clone()); - let audit = audit_persona_speech( - content, - PersonaSpeechActionKind::Post, - artifact_dir, - cultmesh_store, - runtime_id, - requested_channel_id.as_deref(), - true, - )?; - if audit.decision == "blocked" { - let path = write_draft( - content, - config, - artifact_dir, - PersonaChatStatus::Blocked, - &format!("speech audit blocked: {}", audit.reasons.join("; ")), - Some(&audit), - )?; - return Ok(serde_json::json!({ - "ok": false, - "posted": false, - "blocked": "speech-audit", - "draftPath": path, - "speechAudit": audit, - })); - } - let Some(configured_channel_id) = configured_channel_id else { - let path = write_draft( - content, - config, - artifact_dir, - PersonaChatStatus::Blocked, - "missing #aquarium channel id", - Some(&audit), - )?; - return Ok(serde_json::json!({ - "ok": false, - "posted": false, - "blocked": "missing-channel-id", - "draftPath": path, - "speechAudit": audit, - })); - }; - if requested_channel_id.as_deref() != Some(configured_channel_id.as_str()) { - let path = write_draft( - content, - config, - artifact_dir, - PersonaChatStatus::Blocked, - "requested channel does not match configured #aquarium channel id", - Some(&audit), - )?; - return Ok(serde_json::json!({ - "ok": false, - "posted": false, - "blocked": "wrong-channel", - "draftPath": path, - "speechAudit": audit, - })); - } - let Some(bridge_cli_path) = bifrost_bridge_cli_path(config) else { - let path = write_draft( - content, - config, - artifact_dir, - PersonaChatStatus::Blocked, - "missing Bifrost bridge CLI path", - Some(&audit), - )?; - return Ok(serde_json::json!({ - "ok": false, - "posted": false, - "blocked": "missing-bifrost-bridge", - "draftPath": path, - "speechAudit": audit, - })); - }; - let Some(bifrost_identity) = required_bifrost_identity(config) else { - let path = write_draft( - content, - config, - artifact_dir, - PersonaChatStatus::Blocked, - "missing Bifrost identity for public Persona crossing", - Some(&audit), - )?; - return Ok(serde_json::json!({ - "ok": false, - "posted": false, - "blocked": "missing-bifrost-identity", - "draftPath": path, - "speechAudit": audit, - })); - }; - let Some(heimdall_capability_ref) = required_heimdall_capability_ref(config) else { - let path = write_draft( - content, - config, - artifact_dir, - PersonaChatStatus::Blocked, - "missing Heimdall-backed Discord capability/account reference for public Persona crossing; register the Bifrost identity, link it to Heimdall, and provide a heimdall:discord:* reference", - Some(&audit), - )?; - return Ok(serde_json::json!({ - "ok": false, - "posted": false, - "blocked": "missing-heimdall-capability-ref", - "draftPath": path, - "speechAudit": audit, - })); - }; - if !is_heimdall_surface_reference(&heimdall_capability_ref, "discord") { - let path = write_draft( - content, - config, - artifact_dir, - PersonaChatStatus::Blocked, - "Heimdall reference must be shaped for Discord public Persona crossings: heimdall:discord:*", - Some(&audit), - )?; - return Ok(serde_json::json!({ - "ok": false, - "posted": false, - "blocked": "wrong-heimdall-capability-surface", - "draftPath": path, - "speechAudit": audit, - })); - } - let persona = resolve_persona(config, persona_name, persona_avatar_url)?; - let posted = post_bifrost_discord_message( - &bridge_cli_path, - config, - &bifrost_identity, - &heimdall_capability_ref, - &configured_channel_id, - content.trim(), - reply_to_message_id.as_deref(), - persona.as_ref(), - &audit, - )?; - let path = write_draft( - content, - config, - artifact_dir, - PersonaChatStatus::Posted, - &format!( - "posted message {} through {}", - posted.message_id, posted.transport - ), - Some(&audit), - )?; - bind_publication_receipt(&path, &posted.bridge_receipt)?; - Ok(serde_json::json!({ - "ok": true, - "posted": true, - "messageId": posted.message_id, - "transport": posted.transport, - "receiptUrl": posted.receipt_url, - "externalReceiptId": posted.external_receipt_id, - "bifrostBridgeReceipt": posted.bridge_receipt, - "persona": persona.map(|persona| serde_json::json!({ - "name": persona.name, - "avatarUrl": persona.avatar_url, - })), - "draftPath": path, - "speechAudit": audit, - })) -} - -#[derive(Clone, Debug)] -struct PersonaPersona { - name: String, - avatar_url: Option, -} - -#[derive(Clone, Debug)] -struct PostedDiscordMessage { - message_id: String, - transport: String, - receipt_url: Option, - external_receipt_id: Option, - bridge_receipt: Value, -} - -#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq)] -#[serde(rename_all = "kebab-case")] -enum PersonaSpeechActionKind { - Draft, - Bubble, - Post, -} - -impl PersonaSpeechActionKind { - fn as_str(self) -> &'static str { - match self { - PersonaSpeechActionKind::Draft => "draft", - PersonaSpeechActionKind::Bubble => "bubble", - PersonaSpeechActionKind::Post => "post", - } - } -} - -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "camelCase")] -struct PersonaSpeechAudit { - #[serde(rename = "schema_version")] - schema_version: String, - audit_id: String, - created_at: String, - action_kind: PersonaSpeechActionKind, - decision: String, - reasons: Vec, - content_fingerprint: String, - opening_key: String, - topic_key: String, - requested_channel_id: Option, - recent_window_count: usize, - repeated_opening_count: usize, - repeated_topic_count: usize, - same_channel_post_count: usize, - audit_path: PathBuf, -} - -#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq)] -#[serde(rename_all = "lowercase")] -enum PersonaChatStatus { - Draft, - Blocked, - Posted, -} - -#[derive(Debug, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "camelCase")] -struct PersonaChatArtifact { - schema_version: String, - created_at: String, - status: PersonaChatStatus, - reason: String, - allowed_channel_name: String, - allowed_channel_id: Option, - persona_name: Option, - persona_avatar_url: Option, - content: String, - speech_audit: Option, - #[serde(skip_serializing_if = "Option::is_none")] - bifrost_bridge_receipt: Option, -} - -#[derive(Clone, Debug)] -struct RecentPersonaSpeech { - opening_key: String, - topic_key: String, - channel_id: Option, - action_kind: PersonaSpeechActionKind, -} - -fn audit_persona_speech( - content: &str, - action_kind: PersonaSpeechActionKind, - artifact_dir: &Path, - cultmesh_store: &Path, - runtime_id: &str, - requested_channel_id: Option<&str>, - public_post: bool, -) -> Result { - let opening_key = opening_key(content); - let topic_key = topic_key(content); - let recent = recent_persona_speech(artifact_dir, 12); - let repeated_opening_count = recent - .iter() - .filter(|speech| !opening_key.is_empty() && speech.opening_key == opening_key) - .count(); - let repeated_topic_count = recent - .iter() - .filter(|speech| !topic_key.is_empty() && speech.topic_key == topic_key) - .count(); - let same_channel_post_count = requested_channel_id - .map(|channel_id| { - recent - .iter() - .filter(|speech| { - speech.action_kind == PersonaSpeechActionKind::Post - && speech.channel_id.as_deref() == Some(channel_id) - }) - .count() - }) - .unwrap_or(0); - let mut reasons = Vec::new(); - if public_post && repeated_opening_count >= 2 { - reasons.push("repeated-opening".to_string()); - } - if public_post && repeated_topic_count >= 3 { - reasons.push("repeated-topic".to_string()); - } - if public_post && same_channel_post_count >= 4 { - reasons.push("channel-saturation".to_string()); - } - if content.trim().len() > 1800 { - reasons.push("content-too-long-for-public-persona".to_string()); - } - let decision = if reasons.is_empty() { - "eligible" - } else { - "blocked" - } - .to_string(); - let audit_id = format!("persona-speech-audit-{}", short_id()); - let audit_path = artifact_dir.join(format!( - "Persona-speech-audit-{}-{}.json", - now_stamp(), - audit_id - )); - let audit = PersonaSpeechAudit { - schema_version: SPEECH_AUDIT_SCHEMA_VERSION.to_string(), - audit_id, - created_at: now_iso(), - action_kind, - decision, - reasons, - content_fingerprint: content_fingerprint(content), - opening_key, - topic_key, - requested_channel_id: requested_channel_id.map(str::to_string), - recent_window_count: recent.len(), - repeated_opening_count, - repeated_topic_count, - same_channel_post_count, - audit_path, - }; - write_json(&audit.audit_path, &serde_json::to_value(&audit)?)?; - let cultmesh_entry = persona_speech_audit_cultmesh_entry(&audit, runtime_id); - write_epiphany_cultmesh_persona_speech_audit(cultmesh_store, cultmesh_entry)?; - Ok(audit) -} - -fn persona_speech_audit_cultmesh_entry( - audit: &PersonaSpeechAudit, - runtime_id: &str, -) -> EpiphanyCultMeshPersonaSpeechAuditEntry { - EpiphanyCultMeshPersonaSpeechAuditEntry { - schema_version: EPIPHANY_CULTMESH_PERSONA_SPEECH_AUDIT_SCHEMA_VERSION.to_string(), - audit_id: audit.audit_id.clone(), - runtime_id: runtime_id.to_string(), - verse_id: EPIPHANY_CULTMESH_LOCAL_AREA_VERSE_ID.to_string(), - persona_agent_id: "epiphany.Persona".to_string(), - action_kind: audit.action_kind.as_str().to_string(), - decision: audit.decision.clone(), - content_fingerprint: audit.content_fingerprint.clone(), - opening_key: audit.opening_key.clone(), - topic_key: audit.topic_key.clone(), - requested_channel_id: audit.requested_channel_id.clone().unwrap_or_default(), - recent_window_count: audit.recent_window_count as u32, - repeated_opening_count: audit.repeated_opening_count as u32, - repeated_topic_count: audit.repeated_topic_count as u32, - same_channel_post_count: audit.same_channel_post_count as u32, - reasons: audit.reasons.clone(), - artifact_ref: audit.audit_path.display().to_string(), - created_at_utc: audit.created_at.clone(), - private_state_exposed: false, - notes: vec![ - "Persona speech audit is parent-side mouth policy; it stores fingerprints and counters, not raw Persona prose.".to_string(), - "Public speech remains blocked when repetition or saturation trips before Discord transport work.".to_string(), - ], - } -} - -fn resolve_persona( - config: &PersonaConfig, - persona_name: Option, - persona_avatar_url: Option, -) -> Result> { - let name = trim_optional(persona_name).or_else(|| trim_optional(config.persona_name.clone())); - let avatar_url = trim_optional(persona_avatar_url) - .or_else(|| trim_optional(config.persona_avatar_url.clone())); - if name.is_none() && avatar_url.is_none() { - return Ok(None); - } - let Some(name) = name else { - return Err(anyhow!( - "persona-name is required when posting with a persona avatar" - )); - }; - Ok(Some(PersonaPersona { - name: name.chars().take(80).collect(), - avatar_url, - })) -} - -fn post_bifrost_discord_message( - bridge_cli_path: &Path, - config: &PersonaConfig, - bifrost_identity: &str, - heimdall_capability_ref: &str, - channel_id: &str, - content: &str, - reply_to_message_id: Option<&str>, - persona: Option<&PersonaPersona>, - audit: &PersonaSpeechAudit, -) -> Result { - let mut command = Command::new(bifrost_node_executable()); - command.arg(bridge_cli_path); - command.arg("discord-post"); - command.arg("--channel-id").arg(channel_id); - command.arg("--content").arg(content); - command.arg("--identity").arg(bifrost_identity); - command.arg("--source-kind").arg( - config - .bifrost_source_kind - .as_deref() - .unwrap_or("epiphany_persona_speech"), - ); - command.arg("--source-id").arg(&audit.audit_id); - command.arg("--authority-ref").arg( - config - .bifrost_authority_ref - .as_deref() - .unwrap_or("epiphany.persona_speech_audit"), - ); - command.arg("--epiphany-lane-id").arg("Persona"); - command - .arg("--epiphany-agent-identity") - .arg("epiphany.Persona"); - command - .arg("--heimdall-capability-ref") - .arg(heimdall_capability_ref); - if let Some(run_id) = env::var("EPIPHANY_RUN_ID") - .ok() - .map(|value| value.trim().to_string()) - .filter(|value| !value.is_empty()) - { - command.arg("--epiphany-run-id").arg(run_id); - } - if let Some(message_id) = reply_to_message_id { - command.arg("--reply-to-message-id").arg(message_id); - } - if let Some(persona) = persona { - command.arg("--persona-name").arg(&persona.name); - if let Some(avatar_url) = &persona.avatar_url { - command.arg("--persona-avatar-url").arg(avatar_url); - } - } - - let output = command.output().with_context(|| { - format!( - "failed to invoke Bifrost bridge CLI at {}", - bridge_cli_path.display() - ) - })?; - if !output.status.success() { - let stderr = String::from_utf8_lossy(&output.stderr); - let stdout = String::from_utf8_lossy(&output.stdout); - return Err(anyhow!( - "Bifrost bridge Discord post failed with status {}: {}{}{}", - output.status, - stderr.trim(), - if stdout.trim().is_empty() { - "" - } else { - "\nstdout: " - }, - stdout.trim() - )); - } - let stdout = String::from_utf8(output.stdout).context("Bifrost bridge stdout was not UTF-8")?; - let receipt: Value = - serde_json::from_str(strip_bom(&stdout)).context("Bifrost bridge stdout was not JSON")?; - validate_discord_publication_receipt( - &receipt, - config, - bifrost_identity, - heimdall_capability_ref, - channel_id, - audit, - )?; - let message_id = receipt["messageId"] - .as_str() - .expect("validated Discord receipt has messageId") - .to_string(); - Ok(PostedDiscordMessage { - message_id, - transport: "bifrost.discord-post".to_string(), - receipt_url: receipt["url"].as_str().map(str::to_string), - external_receipt_id: receipt["externalReceiptId"].as_str().map(str::to_string), - bridge_receipt: receipt, - }) -} - -fn validate_discord_publication_receipt( - receipt: &Value, - config: &PersonaConfig, - bifrost_identity: &str, - heimdall_capability_ref: &str, - channel_id: &str, - audit: &PersonaSpeechAudit, -) -> Result<()> { - let expected_source_kind = config - .bifrost_source_kind - .as_deref() - .unwrap_or("epiphany_persona_speech"); - let expected_authority = config - .bifrost_authority_ref - .as_deref() - .unwrap_or("epiphany.persona_speech_audit"); - let checks = [ - (receipt["action"].as_str() == Some("discord-post"), "action"), - (receipt["ok"].as_bool() == Some(true), "successful outcome"), - ( - receipt["channelId"].as_str() == Some(channel_id), - "channel binding", - ), - (non_empty(&receipt["messageId"]), "provider message id"), - (non_empty(&receipt["transport"]), "provider transport"), - (non_empty(&receipt["url"]), "receipt URL"), - ( - non_empty(&receipt["crossingReceiptId"]), - "canonical crossing receipt id", - ), - ( - receipt["provenance"]["bifrostIdentity"].as_str() == Some(bifrost_identity), - "Bifrost identity", - ), - ( - receipt["provenance"]["sourceKind"].as_str() == Some(expected_source_kind), - "source kind", - ), - ( - receipt["provenance"]["sourceId"].as_str() == Some(audit.audit_id.as_str()), - "speech audit binding", - ), - ( - receipt["provenance"]["authorityReference"].as_str() == Some(expected_authority), - "authority reference", - ), - ( - receipt["provenance"]["epiphanyLaneId"].as_str() == Some("Persona"), - "lane identity", - ), - ( - receipt["provenance"]["epiphanyAgentIdentity"].as_str() == Some("epiphany.Persona"), - "agent identity", - ), - ( - receipt["provenance"]["heimdallCapabilityRef"].as_str() - == Some(heimdall_capability_ref), - "Heimdall capability", - ), - ]; - for (valid, label) in checks { - if !valid { - return Err(anyhow!( - "Bifrost Discord publication receipt missing or mismatched {label}" - )); - } - } - let message_id = receipt["messageId"].as_str().expect("validated message id"); - let url = receipt["url"].as_str().expect("validated receipt URL"); - if !url.contains(&format!("/{channel_id}/{message_id}")) { - return Err(anyhow!( - "Bifrost Discord publication receipt URL is not bound to channel and message" - )); - } - Ok(()) -} - -fn non_empty(value: &Value) -> bool { - value.as_str().is_some_and(|value| !value.trim().is_empty()) -} - -fn bifrost_node_executable() -> String { - env::var("EPIPHANY_BIFROST_NODE") - .ok() - .map(|value| value.trim().to_string()) - .filter(|value| !value.is_empty()) - .unwrap_or_else(|| "node".to_string()) -} - -fn run_smoke() -> Result { - let temp_dir = scoped_temp_dir("epiphany-persona-discord-smoke")?; - let cultmesh_store = temp_dir.join("persona-speech.ccmp"); - let runtime_id = "epiphany-persona-smoke"; - let config = PersonaConfig { - allowed_channel_name: "#aquarium".to_string(), - allowed_channel_id: None, - allowed_channel_id_env: Some("EPIPHANY_PERSONA_AQUARIUM_CHANNEL_ID_TEST".to_string()), - persona_name: Some("Smoke Persona".to_string()), - persona_avatar_url: Some("https://example.invalid/Persona.png".to_string()), - bifrost_bridge_cli_path: None, - bifrost_identity: Some("epiphany.Persona".to_string()), - bifrost_source_kind: Some("epiphany_persona_speech".to_string()), - bifrost_authority_ref: Some("epiphany.persona_speech_audit".to_string()), - heimdall_capability_ref_env: Some("HEIMDALL_CAPABILITY_REF_TEST".to_string()), - }; - let fake_bridge = temp_dir.join("fake-bifrost-bridge.mjs"); - fs::write( - &fake_bridge, - r#" -const args = process.argv.slice(2); -function opt(name) { - const i = args.indexOf(name); - return i >= 0 && i + 1 < args.length ? args[i + 1] : ""; -} -console.log(JSON.stringify({ - action: args[0], - ok: true, - channelId: opt("--channel-id"), - messageId: "bridge-message-123", - externalReceiptId: "bridge-message-123", - transport: "discord-webhook", - crossingReceiptId: "crossing_smoke-discord-post", - url: `https://discord.com/channels/test/${opt("--channel-id")}/bridge-message-123`, - provenance: { - bifrostIdentity: opt("--identity"), - sourceKind: opt("--source-kind"), - sourceId: opt("--source-id"), - authorityReference: opt("--authority-ref"), - epiphanyRunId: opt("--epiphany-run-id"), - epiphanyLaneId: opt("--epiphany-lane-id"), - epiphanyAgentIdentity: opt("--epiphany-agent-identity"), - heimdallCapabilityRef: opt("--heimdall-capability-ref") - } -})); -"#, - ) - .with_context(|| format!("failed to write {}", fake_bridge.display()))?; - let mut bridge_config = config.clone(); - bridge_config.bifrost_bridge_cli_path = Some(fake_bridge); - let invalid_bridge = temp_dir.join("invalid-bifrost-bridge.mjs"); - fs::write(&invalid_bridge, "console.log('{}');\n")?; - let mut invalid_receipt_config = bridge_config.clone(); - invalid_receipt_config.bifrost_bridge_cli_path = Some(invalid_bridge); - let mut missing_capability_config = bridge_config.clone(); - missing_capability_config.heimdall_capability_ref_env = - Some("HEIMDALL_CAPABILITY_REF_MISSING_FOR_SMOKE".to_string()); - let mut wrong_surface_config = bridge_config.clone(); - wrong_surface_config.heimdall_capability_ref_env = - Some("HEIMDALL_CAPABILITY_REF_WRONG_SURFACE_FOR_SMOKE".to_string()); - unsafe { - env::remove_var("EPIPHANY_PERSONA_AQUARIUM_CHANNEL_ID_TEST"); - env::set_var( - "HEIMDALL_CAPABILITY_REF_TEST", - "heimdall:discord:capability:smoke-persona", - ); - env::set_var( - "HEIMDALL_CAPABILITY_REF_WRONG_SURFACE_FOR_SMOKE", - "heimdall:reddit:capability:smoke-persona", - ); - env::remove_var("HEIMDALL_CAPABILITY_REF_MISSING_FOR_SMOKE"); - env::set_var("EPIPHANY_RUN_ID", "epiphany-run-smoke"); - } - let draft = run_draft( - "Persona notices Modeling and Soul disagree about evidence shape.", - &config, - &temp_dir, - &cultmesh_store, - runtime_id, - PersonaChatStatus::Draft, - "drafted without posting", - )?; - let bubble = run_bubble( - "Persona opens an Aquarium bubble even while Discord is unavailable.", - &temp_dir, - &cultmesh_store, - runtime_id, - "smoke/Persona", - "attentive", - )?; - let blocked = run_post( - "Persona should not post without a configured #aquarium channel id.", - &config, - &temp_dir, - &cultmesh_store, - runtime_id, - None, - None, - None, - None, - )?; - unsafe { - env::set_var("EPIPHANY_PERSONA_AQUARIUM_CHANNEL_ID_TEST", "123"); - } - let missing_bridge = run_post( - "Persona should not cross Discord without Bifrost bridge configuration.", - &config, - &temp_dir, - &cultmesh_store, - runtime_id, - Some("123".to_string()), - None, - None, - None, - )?; - let missing_capability = run_post( - "Persona should not cross Discord without Heimdall capability proof.", - &missing_capability_config, - &temp_dir, - &cultmesh_store, - runtime_id, - Some("123".to_string()), - None, - None, - None, - )?; - let wrong_surface_capability = run_post( - "Wrong surface credential must stop before Discord transport.", - &wrong_surface_config, - &temp_dir, - &cultmesh_store, - runtime_id, - Some("123".to_string()), - None, - None, - None, - )?; - let bridged = run_post( - "Persona crosses to Discord only through Bifrost.", - &bridge_config, - &temp_dir, - &cultmesh_store, - runtime_id, - Some("123".to_string()), - Some("Smoke Persona".to_string()), - Some("https://example.invalid/Persona.png".to_string()), - None, - )?; - let invalid_receipt_rejected = run_post( - "Persona must reject empty successful bridge JSON.", - &invalid_receipt_config, - &temp_dir, - &cultmesh_store, - runtime_id, - Some("123".to_string()), - None, - None, - None, - ) - .is_err(); - let wrong = run_post( - "Persona should not post outside #aquarium.", - &bridge_config, - &temp_dir, - &cultmesh_store, - runtime_id, - Some("456".to_string()), - Some("Wrong Persona".to_string()), - None, - None, - )?; - let repeated_audit_seed = audit_persona_speech( - "Rite noted: Modeling and Soul keep circling the same evidence seam.", - PersonaSpeechActionKind::Post, - &temp_dir, - &cultmesh_store, - runtime_id, - Some("123"), - false, - )?; - write_draft( - "Rite noted: Modeling and Soul keep circling the same evidence seam.", - &config, - &temp_dir, - PersonaChatStatus::Posted, - "seed prior posted Persona output for speech-audit smoke", - Some(&repeated_audit_seed), - )?; - let verified_post_count = recent_persona_speech(&temp_dir, 12) - .iter() - .filter(|speech| speech.action_kind == PersonaSpeechActionKind::Post) - .count(); - write_draft( - "Rite noted: Modeling and Soul keep circling the same evidence seam.", - &config, - &temp_dir, - PersonaChatStatus::Posted, - "seed second prior posted Persona output for speech-audit smoke", - Some(&repeated_audit_seed), - )?; - let repeated = run_post( - "Rite noted: Modeling and Soul keep circling the same evidence seam again.", - &bridge_config, - &temp_dir, - &cultmesh_store, - runtime_id, - Some("123".to_string()), - None, - None, - None, - )?; - let latest_cultmesh_audit = - load_latest_epiphany_cultmesh_persona_speech_audit(&cultmesh_store, runtime_id)? - .context("Persona speech audit smoke expected latest CultMesh audit")?; - let draft_artifact: Value = serde_json::from_str(&fs::read_to_string( - draft["draftPath"] - .as_str() - .context("draft smoke result missing artifact path")?, - )?)?; - let posted_artifact: Value = serde_json::from_str(&fs::read_to_string( - bridged["draftPath"] - .as_str() - .context("posted smoke result missing artifact path")?, - )?)?; - let ok = draft["ok"] == true - && chat_artifact_has_contract(&draft_artifact, false) - && chat_artifact_has_contract(&posted_artifact, true) - && bubble["ok"] == true - && bubble["bubble"]["schemaVersion"] == BUBBLE_SCHEMA_VERSION - && bubble["bubble"].as_object().is_some_and(|payload| { - const EXPECTED: [&str; 11] = [ - "agentId", - "bubble", - "content", - "createdAt", - "displayName", - "mood", - "roleId", - "schemaVersion", - "source", - "status", - "target", - ]; - payload.len() == EXPECTED.len() && EXPECTED.iter().all(|key| payload.contains_key(*key)) - }) - && bubble["bubble"]["bubble"]["requiresDiscord"] == false - && blocked["ok"] == false - && blocked["blocked"] == "missing-channel-id" - && missing_bridge["ok"] == false - && missing_bridge["blocked"] == "missing-bifrost-bridge" - && missing_capability["ok"] == false - && missing_capability["blocked"] == "missing-heimdall-capability-ref" - && wrong_surface_capability["ok"] == false - && wrong_surface_capability["blocked"] == "wrong-heimdall-capability-surface" - && bridged["ok"] == true - && invalid_receipt_rejected - && verified_post_count == 1 - && bridged["transport"] == "bifrost.discord-post" - && bridged["bifrostBridgeReceipt"]["provenance"]["bifrostIdentity"] == "epiphany.Persona" - && bridged["bifrostBridgeReceipt"]["provenance"]["heimdallCapabilityRef"] - == "heimdall:discord:capability:smoke-persona" - && wrong["ok"] == false - && wrong["blocked"] == "wrong-channel" - && repeated["ok"] == false - && repeated["blocked"] == "speech-audit" - && repeated["speechAudit"]["decision"] == "blocked" - && repeated["speechAudit"]["reasons"] - .as_array() - .is_some_and(|reasons| reasons.iter().any(|reason| reason == "repeated-opening")) - && latest_cultmesh_audit.decision == "blocked" - && latest_cultmesh_audit - .reasons - .iter() - .any(|reason| reason == "repeated-opening") - && !latest_cultmesh_audit.private_state_exposed; - let result = serde_json::json!({ - "ok": ok, - "draft": draft, - "bubble": bubble, - "blocked": blocked, - "missingBridge": missing_bridge, - "missingCapability": missing_capability, - "wrongSurfaceCapability": wrong_surface_capability, - "bridged": bridged, - "invalidReceiptRejected": invalid_receipt_rejected, - "verifiedPostCount": verified_post_count, - "wrongChannel": wrong, - "repeatedSpeech": repeated, - "latestCultMeshSpeechAudit": latest_cultmesh_audit, - }); - let _ = fs::remove_dir_all(&temp_dir); - Ok(result) -} - -fn write_draft( - content: &str, - config: &PersonaConfig, - artifact_dir: &Path, - status: PersonaChatStatus, - reason: &str, - speech_audit: Option<&PersonaSpeechAudit>, -) -> Result { - let payload = PersonaChatArtifact { - schema_version: CHAT_SCHEMA_VERSION.to_string(), - created_at: now_iso(), - status, - reason: reason.to_string(), - allowed_channel_name: if config.allowed_channel_name.is_empty() { - "#aquarium".to_string() - } else { - config.allowed_channel_name.clone() - }, - allowed_channel_id: allowed_channel_id(config), - persona_name: config.persona_name.clone(), - persona_avatar_url: config.persona_avatar_url.clone(), - content: content.trim().to_string(), - speech_audit: speech_audit.cloned(), - bifrost_bridge_receipt: None, - }; - let path = artifact_dir.join(format!("Persona-chat-{}-{}.json", now_stamp(), short_id())); - write_json(&path, &serde_json::to_value(payload)?)?; - Ok(path) -} - -fn bind_publication_receipt(path: &Path, receipt: &Value) -> Result<()> { - let mut payload: PersonaChatArtifact = serde_json::from_str(&fs::read_to_string(path)?)?; - payload.bifrost_bridge_receipt = Some(receipt.clone()); - write_json(path, &serde_json::to_value(payload)?) -} - -fn has_bound_discord_publication(payload: &Value) -> bool { - let receipt = &payload["bifrostBridgeReceipt"]; - let channel_id = payload["allowedChannelId"] - .as_str() - .or_else(|| payload["allowed_channel_id"].as_str()) - .unwrap_or_default(); - let audit_id = payload["speechAudit"]["auditId"] - .as_str() - .or_else(|| payload["speechAudit"]["audit_id"].as_str()) - .unwrap_or_default(); - receipt["action"] == "discord-post" - && receipt["ok"] == true - && receipt["channelId"].as_str() == Some(channel_id) - && non_empty(&receipt["messageId"]) - && non_empty(&receipt["crossingReceiptId"]) - && receipt["provenance"]["sourceId"].as_str() == Some(audit_id) -} - -fn chat_artifact_has_contract(payload: &Value, expects_receipt: bool) -> bool { - const REQUIRED: [&str; 10] = [ - "allowedChannelId", - "allowedChannelName", - "content", - "createdAt", - "personaAvatarUrl", - "personaName", - "reason", - "schemaVersion", - "speechAudit", - "status", - ]; - payload.as_object().is_some_and(|object| { - let expected_len = REQUIRED.len() + usize::from(expects_receipt); - object.len() == expected_len - && REQUIRED.iter().all(|key| object.contains_key(*key)) - && object.contains_key("bifrostBridgeReceipt") == expects_receipt - && matches!( - payload["status"].as_str(), - Some("draft" | "blocked" | "posted") - ) - }) -} - -fn bubble_payload(content: &str, source: &str, mood: &str) -> Value { - serde_json::json!({ - "schemaVersion": BUBBLE_SCHEMA_VERSION, - "createdAt": now_iso(), - "status": "projected", - "source": source, - "target": "aquarium", - "roleId": "Persona", - "agentId": "Persona", - "displayName": "Persona", - "mood": mood, - "content": content.trim(), - "bubble": { - "kind": "agent-chat", - "anchorRoleId": "Persona", - "opensIn": "aquarium", - "requiresDiscord": false, - "ttlSeconds": 90, - }, - }) -} - -fn latest_persona_artifacts(artifact_dir: &Path, limit: usize) -> Vec { - let Ok(read_dir) = fs::read_dir(artifact_dir) else { - return Vec::new(); - }; - let mut paths = read_dir - .filter_map(Result::ok) - .map(|entry| entry.path()) - .filter(|path| { - path.file_name() - .and_then(|name| name.to_str()) - .is_some_and(|name| name.starts_with("Persona-") && name.ends_with(".json")) - }) - .filter_map(|path| Some((path.metadata().ok()?.modified().ok()?, path))) - .collect::>(); - paths.sort_by_key(|item| Reverse(item.0)); - paths - .into_iter() - .take(limit) - .filter_map(|(modified, path)| { - let payload: Value = serde_json::from_str(&fs::read_to_string(&path).ok()?).ok()?; - let modified_at: chrono::DateTime = modified.into(); - Some(serde_json::json!({ - "path": path, - "name": path.file_name().and_then(|name| name.to_str()), - "modifiedAt": modified_at.to_rfc3339_opts(SecondsFormat::Secs, true), - "schemaVersion": payload.get("schemaVersion").or_else(|| payload.get("schema_version")), - "status": payload.get("status"), - "reason": payload.get("reason"), - "content": payload.get("content"), - "bubble": payload.get("bubble"), - "source": payload.get("source"), - })) - }) - .collect() -} - -fn recent_persona_speech(artifact_dir: &Path, limit: usize) -> Vec { - let Ok(read_dir) = fs::read_dir(artifact_dir) else { - return Vec::new(); - }; - let mut paths = read_dir - .filter_map(Result::ok) - .map(|entry| entry.path()) - .filter(|path| { - path.file_name() - .and_then(|name| name.to_str()) - .is_some_and(|name| { - name.starts_with("Persona-chat-") || name.starts_with("Persona-bubble-") - }) - }) - .filter_map(|path| Some((path.metadata().ok()?.modified().ok()?, path))) - .collect::>(); - paths.sort_by_key(|item| Reverse(item.0)); - paths - .into_iter() - .take(limit) - .filter_map(|(_, path)| { - let payload: Value = serde_json::from_str(&fs::read_to_string(&path).ok()?).ok()?; - let content = payload["content"] - .as_str() - .or_else(|| payload["bubble"]["content"].as_str())? - .to_string(); - let status = payload["status"].as_str().unwrap_or_default(); - let action_kind = if payload["schemaVersion"] == BUBBLE_SCHEMA_VERSION - || payload["schema_version"] == BUBBLE_SCHEMA_VERSION - { - PersonaSpeechActionKind::Bubble - } else if status == "posted" && has_bound_discord_publication(&payload) { - PersonaSpeechActionKind::Post - } else { - PersonaSpeechActionKind::Draft - }; - Some(RecentPersonaSpeech { - opening_key: opening_key(&content), - topic_key: topic_key(&content), - channel_id: payload["allowedChannelId"] - .as_str() - .or_else(|| payload["allowed_channel_id"].as_str()) - .map(str::to_string), - action_kind, - }) - }) - .collect() -} - -fn opening_key(content: &str) -> String { - content - .split_whitespace() - .take(5) - .map(normalize_token) - .filter(|token| !token.is_empty()) - .collect::>() - .join(" ") -} - -fn topic_key(content: &str) -> String { - let mut tokens = content - .split_whitespace() - .map(normalize_token) - .filter(|token| token.len() >= 4 && !persona_stopword(token)) - .collect::>(); - tokens.sort(); - tokens.dedup(); - tokens.into_iter().take(6).collect::>().join("|") -} - -fn content_fingerprint(content: &str) -> String { - topic_key(content) - + "::" - + &content - .split_whitespace() - .map(normalize_token) - .filter(|token| !token.is_empty()) - .take(24) - .collect::>() - .join("-") -} - -fn normalize_token(value: &str) -> String { - value - .trim_matches(|ch: char| !ch.is_ascii_alphanumeric()) - .chars() - .filter(|ch| ch.is_ascii_alphanumeric() || *ch == '-' || *ch == '_') - .collect::() - .to_ascii_lowercase() -} - -fn persona_stopword(value: &str) -> bool { - matches!( - value, - "about" - | "after" - | "again" - | "because" - | "before" - | "being" - | "should" - | "there" - | "these" - | "thing" - | "through" - | "under" - | "without" - | "would" - ) -} - -fn trim_optional(value: Option) -> Option { - value - .map(|value| value.trim().to_string()) - .filter(|value| !value.is_empty()) -} - -fn strip_bom(value: &str) -> &str { - value.strip_prefix('\u{feff}').unwrap_or(value) -} - -fn load_config(path: &Path) -> Result { - let raw = - fs::read_to_string(path).with_context(|| format!("failed to read {}", path.display()))?; - if path - .extension() - .is_some_and(|extension| extension == "json") - { - let payload: Value = serde_json::from_str(&raw) - .with_context(|| format!("failed to decode {}", path.display()))?; - return Ok(PersonaConfig { - allowed_channel_name: payload["allowed_channel_name"] - .as_str() - .unwrap_or("#aquarium") - .to_string(), - allowed_channel_id: payload["allowed_channel_id"] - .as_str() - .map(str::trim) - .filter(|value| !value.is_empty()) - .map(str::to_string), - allowed_channel_id_env: payload["allowed_channel_id_env"] - .as_str() - .map(str::to_string), - persona_name: payload["persona_name"].as_str().map(str::to_string), - persona_avatar_url: payload["persona_avatar_url"].as_str().map(str::to_string), - bifrost_bridge_cli_path: payload["bifrost_bridge_cli_path"] - .as_str() - .map(PathBuf::from), - bifrost_identity: payload["bifrost_identity"].as_str().map(str::to_string), - bifrost_source_kind: payload["bifrost_source_kind"].as_str().map(str::to_string), - bifrost_authority_ref: payload["bifrost_authority_ref"] - .as_str() - .map(str::to_string), - heimdall_capability_ref_env: payload["heimdall_capability_ref_env"] - .as_str() - .map(str::to_string), - }); - } - Ok(PersonaConfig { - allowed_channel_name: toml_string(&raw, "allowed_channel_name") - .unwrap_or_else(|| "#aquarium".to_string()), - allowed_channel_id: toml_string(&raw, "allowed_channel_id"), - allowed_channel_id_env: toml_string(&raw, "allowed_channel_id_env"), - persona_name: toml_string(&raw, "persona_name"), - persona_avatar_url: toml_string(&raw, "persona_avatar_url"), - bifrost_bridge_cli_path: toml_string(&raw, "bifrost_bridge_cli_path").map(PathBuf::from), - bifrost_identity: toml_string(&raw, "bifrost_identity"), - bifrost_source_kind: toml_string(&raw, "bifrost_source_kind"), - bifrost_authority_ref: toml_string(&raw, "bifrost_authority_ref"), - heimdall_capability_ref_env: toml_string(&raw, "heimdall_capability_ref_env"), - }) -} - -fn toml_string(raw: &str, key: &str) -> Option { - raw.lines() - .map(str::trim) - .filter(|line| !line.starts_with('#')) - .find_map(|line| { - let (left, right) = line.split_once('=')?; - (left.trim() == key).then(|| parse_quoted(right.trim()))? - }) -} - -fn parse_quoted(value: &str) -> Option { - let value = value.trim(); - value - .strip_prefix('"') - .and_then(|value| value.strip_suffix('"')) - .map(str::to_string) -} - -fn allowed_channel_id(config: &PersonaConfig) -> Option { - config - .allowed_channel_id - .clone() - .or_else(|| config.allowed_channel_id_env.as_ref().and_then(env_value)) -} - -fn bifrost_bridge_cli_path(config: &PersonaConfig) -> Option { - config.bifrost_bridge_cli_path.clone().or_else(|| { - env::var("EPIPHANY_PERSONA_BIFROST_BRIDGE_CLI") - .ok() - .map(|value| value.trim().to_string()) - .filter(|value| !value.is_empty()) - .map(PathBuf::from) - }) -} - -fn required_bifrost_identity(config: &PersonaConfig) -> Option { - trim_optional(config.bifrost_identity.clone()).or_else(|| { - env::var("BIFROST_IDENTITY") - .ok() - .map(|value| value.trim().to_string()) - .filter(|value| !value.is_empty()) - }) -} - -fn required_heimdall_capability_ref(config: &PersonaConfig) -> Option { - config - .heimdall_capability_ref_env - .as_ref() - .and_then(env_value) - .or_else(|| { - env::var("HEIMDALL_CAPABILITY_REF") - .ok() - .map(|value| value.trim().to_string()) - .filter(|value| !value.is_empty()) - }) - .or_else(|| { - env::var("BIFROST_HEIMDALL_CAPABILITY_REF") - .ok() - .map(|value| value.trim().to_string()) - .filter(|value| !value.is_empty()) - }) -} - -fn is_heimdall_surface_reference(reference: &str, surface: &str) -> bool { - let reference = reference.trim().to_ascii_lowercase(); - reference.starts_with("heimdall:") && reference.contains(&format!(":{surface}:")) -} - -fn env_value(name: &String) -> Option { - env::var(name) - .ok() - .map(|value| value.trim().to_string()) - .filter(|value| !value.is_empty()) -} - -fn read_content(content: Option) -> Result { - let Some(content) = content else { - let mut value = String::new(); - std::io::stdin() - .read_to_string(&mut value) - .context("failed to read stdin")?; - return Ok(value); - }; - let path = PathBuf::from(&content); - if path.exists() { - return fs::read_to_string(&path) - .with_context(|| format!("failed to read {}", path.display())); - } - Ok(content) -} - -fn ensure_content(content: &str, label: &str) -> Result<()> { - if content.trim().is_empty() { - return Err(anyhow!("{label} content is empty")); - } - Ok(()) -} - -fn write_json(path: &Path, value: &Value) -> Result<()> { - if let Some(parent) = path.parent() { - fs::create_dir_all(parent) - .with_context(|| format!("failed to create {}", parent.display()))?; - } - fs::write(path, format!("{}\n", serde_json::to_string_pretty(value)?)) - .with_context(|| format!("failed to write {}", path.display())) -} - -fn now_iso() -> String { - chrono::Utc::now() - .to_rfc3339_opts(SecondsFormat::Secs, true) - .replace('Z', "+00:00") -} - -fn now_stamp() -> String { - chrono::Utc::now().format("%Y%m%dT%H%M%SZ").to_string() -} - -fn short_id() -> String { - Uuid::new_v4().simple().to_string()[..8].to_string() -} - -fn scoped_temp_dir(prefix: &str) -> Result { - let path = env::temp_dir().join(format!("{prefix}-{}", Uuid::new_v4())); - fs::create_dir(&path).with_context(|| format!("failed to claim {}", path.display()))?; - Ok(path) -} - -fn next_path(args: &mut impl Iterator, name: &str) -> Result { - Ok(PathBuf::from(next_value(args, name)?)) -} - -fn next_value(args: &mut impl Iterator, name: &str) -> Result { - args.next() - .ok_or_else(|| anyhow!("{name} requires a value")) -} - -fn usage() -> Result<()> { - Err(anyhow!( - "usage: epiphany-persona-discord [--config ] [--artifact-dir ] [--content ]" - )) -} diff --git a/epiphany-core/src/bin/epiphany-persona-feedback-causal-smoke.rs b/epiphany-core/src/bin/epiphany-persona-feedback-causal-smoke.rs deleted file mode 100644 index 6b794b76..00000000 --- a/epiphany-core/src/bin/epiphany-persona-feedback-causal-smoke.rs +++ /dev/null @@ -1,184 +0,0 @@ -use anyhow::{Context, Result, bail}; -use epiphany_core::*; -use epiphany_state_model::{ - EpiphanyMemoryDomain, EpiphanyMemoryLifecycle, EpiphanyMemoryNode, EpiphanyMemoryNodeKind, - EpiphanyMemoryProfile, -}; -use std::{ - collections::{BTreeMap, BTreeSet}, - env, fs, - path::{Path, PathBuf}, -}; - -const AT: &str = "2026-07-18T00:00:00Z"; - -fn main() -> Result<()> { - let args = arguments()?; - let feedback_store = path(&args, "--persona-feedback-store")?; - let runtime_store = path(&args, "--runtime-store")?; - let forbidden = [ - "--mind-store", - "--release-store", - "--local-verse-store", - "--public-store", - ] - .map(|flag| path(&args, flag)) - .into_iter() - .collect::>>()?; - let before = forbidden - .iter() - .map(|p| Ok((p.clone(), bytes(p)?))) - .collect::>>()?; - - seed_fixture_runtime(&runtime_store)?; - let admitted = admitted_persona_feedback(&feedback_store, "epiphany-yggdrasil")?; - let classes = admitted - .iter() - .map(|f| (f.source_visibility.as_str(), f.data_classification.as_str())) - .collect::>(); - let expected = BTreeSet::from([ - ("public", "public_feedback"), - ("organization", "organization_feedback"), - ("private", "private_feedback"), - ]); - if classes != expected { - bail!("fixture requires exact public, organization, and private feedback classes"); - } - - bind_runtime_repository_domain(&runtime_store, "GameCult/Epiphany", "2026-07-18T00:00:00Z")?; - let materialized = materialize_resident_self_domain_obligations( - &runtime_store, - &feedback_store, - "epiphany-yggdrasil", - "GameCult/Epiphany", - runtime_store.parent().unwrap().to_str().unwrap(), - 1_752_796_800_000, - )?; - if materialized < admitted.len() { - bail!("resident Self did not materialize every admitted feedback obligation"); - } - for feedback in admitted { - let request = commit_imagination_consideration_request( - &runtime_store, - &feedback_store, - &feedback.feedback_id, - "GameCult/Epiphany", - "epiphany", - "resident-feedback-consideration-v0", - AT, - )? - .ok_or_else(|| anyhow::anyhow!("fixture thread did not admit consideration"))?; - if request.source_visibility != feedback.source_visibility - || request.data_classification != feedback.data_classification - || request.source_room_id != feedback.source_room_id - { - bail!("consideration lost classified source provenance"); - } - let job_id = - launch_current_imagination_consideration_work(&runtime_store, &request.request_id, AT)?; - let candidate = ImaginationConsiderationCandidate { - schema_version: IMAGINATION_CONSIDERATION_CANDIDATE_SCHEMA_VERSION.into(), - candidate_id: imagination_consideration_candidate_id_for_launch( - &request.request_id, - &job_id, - ), - request_id: request.request_id.clone(), - feedback_id: request.feedback_id.clone(), - feedback_packet_sha256: request.feedback_packet_sha256.clone(), - source_room_id: request.source_room_id.clone(), - source_visibility: request.source_visibility.clone(), - data_classification: request.data_classification.clone(), - model_projection_digest: request.model_projection_digest.clone(), - model_source_documents: request.model_source_documents.clone(), - disposition: ImaginationConsiderationDisposition::Suggest, - title: "Fixture proposal".into(), - summary: "Reviewable option only".into(), - rationale: "Compared exact admitted feedback with the current Modeling map.".into(), - option_drafts: vec![ImaginationOptionDraft { - title: "Review".into(), - summary: "Ask Modeling to review; do not mutate state.".into(), - }], - uncertainties: Vec::new(), - evidence_refs: request.quoted_evidence.source_discussion_refs.clone(), - recommended_review_route: ImaginationConsiderationReviewRoute::ModelingReview, - proposed_at: AT.into(), - contract: IMAGINATION_CONSIDERATION_CANDIDATE_CONTRACT.into(), - }; - validate_imagination_consideration_candidate(&request, &candidate)?; - } - for (path, original) in before { - if bytes(&path)? != original { - bail!("fixture mutated forbidden store {}", path.display()); - } - } - println!( - "{{\"schemaVersion\":\"epiphany.persona_feedback_causal_smoke.v0\",\"status\":\"passed\",\"privateStateExposed\":false}}" - ); - Ok(()) -} - -fn seed_fixture_runtime(store: &Path) -> Result<()> { - initialize_runtime_spine( - store, - RuntimeSpineInitOptions { - runtime_id: "epiphany-yggdrasil".into(), - display_name: "fixture".into(), - created_at: AT.into(), - }, - )?; - let seed = EpiphanyRepoModelSeed::new( - "fixture-model-seed", - "fixture-model", - "epiphany-yggdrasil", - "fixture-workspace", - "fixture-body-binding", - EpiphanyRepoModelSeedDocuments { - domains: vec![EpiphanyMemoryDomain { - id: "repo".into(), - profile: EpiphanyMemoryProfile::RepoArchitecture, - title: "Repository".into(), - lifecycle: EpiphanyMemoryLifecycle::Accepted, - ..Default::default() - }], - nodes: vec![EpiphanyMemoryNode { - id: "fixture-claim".into(), - domain_id: "repo".into(), - profile: EpiphanyMemoryProfile::RepoArchitecture, - kind: EpiphanyMemoryNodeKind::RuntimeContract, - title: "Feedback path".into(), - claim: "Persona feedback is pressure, never authority.".into(), - question: "Which proposal fits the Body?".into(), - action_implication: "Review only.".into(), - lifecycle: EpiphanyMemoryLifecycle::Accepted, - ..Default::default() - }], - edges: Vec::new(), - summaries: Vec::new(), - frontier: Vec::new(), - lifecycle_receipts: Vec::new(), - }, - )?; - initialize_keyed_repo_model(store, &seed, AT)?; - Ok(()) -} - -fn arguments() -> Result> { - let mut map = BTreeMap::new(); - let mut args = env::args().skip(1); - while let Some(k) = args.next() { - map.insert(k, args.next().context("missing argument value")?); - } - Ok(map) -} -fn path(args: &BTreeMap, key: &str) -> Result { - Ok(PathBuf::from( - args.get(key).with_context(|| format!("missing {key}"))?, - )) -} -fn bytes(path: &Path) -> Result> { - Ok(if path.exists() { - fs::read(path)? - } else { - Vec::new() - }) -} diff --git a/epiphany-core/src/bin/epiphany-persona-memory-recall-smoke.rs b/epiphany-core/src/bin/epiphany-persona-memory-recall-smoke.rs deleted file mode 100644 index 62d132e4..00000000 --- a/epiphany-core/src/bin/epiphany-persona-memory-recall-smoke.rs +++ /dev/null @@ -1,103 +0,0 @@ -use epiphany_core::{ - EpiphanyMemoryContextPacket, EpiphanyMemoryFreshnessStatus, EpiphanyMemoryNode, - EpiphanyMemorySummary, PersonaIdentity, PersonaInterpreterInput, PersonaProjectorInput, - PersonaTurnInput, build_persona_interpreter_prompt, build_persona_projector_prompt, - build_persona_turn_prompt, render_persona_semantic_memory_recall, - semantic_memory_recall_from_heartbeat_action, -}; -use serde_json::json; - -fn main() { - let packet = EpiphanyMemoryContextPacket { - id: "memctx-persona-smoke".to_string(), - query_id: "persona-smoke-current-turn".to_string(), - summaries: vec![EpiphanyMemorySummary { - id: "summary-persona-contracts".to_string(), - target: "role:Persona".to_string(), - claim: "Persona remembers that public speech is a reviewed mouth edge, not raw side effect authority." - .to_string(), - action_implication: - "Shape the public voice, then let Mind and the mouth edge route effects." - .to_string(), - freshness: EpiphanyMemoryFreshnessStatus::Ready, - confidence: 84, - ..Default::default() - }], - nodes: vec![EpiphanyMemoryNode { - id: "node-persona-shared-mind".to_string(), - title: "Shared Mind semantic projection".to_string(), - claim: "Persona recall resolves canonical Mind graph documents after semantic ranking; Qdrant payload text never speaks into the prompt." - .to_string(), - action_implication: - "Use the canonical packet as a hint and leave durable state with Mind." - .to_string(), - ..Default::default() - }], - warnings: vec![ - "semantic projection ranked canonical Mind candidates; payload text was ignored" - .to_string(), - ], - ..Default::default() - }; - let rendered = render_persona_semantic_memory_recall(&packet); - assert_contains(&rendered, "Shared Mind semantic projection"); - assert_contains(&rendered, "payload text was ignored"); - - let heartbeat_action = json!({ - "action_type": "persona_turn", - "persona_memory_recall": { - "privateStateExposed": false, - "renderedRecall": rendered, - } - }); - let recall = semantic_memory_recall_from_heartbeat_action(&heartbeat_action); - assert_contains(&recall, "canonical Mind graph documents"); - - let identity = PersonaIdentity { - identity_id: "epiphany".to_string(), - display_name: "Epiphany".to_string(), - repo_name: "EpiphanyAgent".to_string(), - public_description: "Repo Persona for typed agent substrate.".to_string(), - jurisdiction: vec!["typed state and review-gated agency".to_string()], - }; - let projector_prompt = build_persona_projector_prompt(&PersonaProjectorInput { - identity: identity.clone(), - semantic_memory_recall: recall.clone(), - ..Default::default() - }); - let persona_prompt = build_persona_turn_prompt(&PersonaTurnInput { - identity: identity.clone(), - projected_state: "The public mouth remains receipt-bound.".to_string(), - }); - let persona_output = "I can speak, but consequence still needs a receipt."; - let interpreter_prompt = build_persona_interpreter_prompt(&PersonaInterpreterInput { - identity, - persona_prompt: "The public mouth remains receipt-bound.".to_string(), - persona_output: persona_output.to_string(), - semantic_memory_recall: recall.clone(), - dynamic_semantic_memory_recall: recall, - pending_mentions: Vec::new(), - allowed_channel_ids: vec!["aquarium".to_string()], - }); - assert_contains(&projector_prompt, "Semantic memory recall"); - assert_not_contains(&persona_prompt, "canonical Mind graph documents"); - assert_contains(&interpreter_prompt, "Dynamic self-memory recall"); - - println!( - "status=ok recallSource=shared-mind-memory-graph qdrantPayloadAuthority=false canonicalReload=true heartbeatRecall=prompt-wired privateStateExposed=false" - ); -} - -fn assert_contains(haystack: &str, needle: &str) { - assert!( - haystack.contains(needle), - "expected prompt to contain `{needle}`" - ); -} - -fn assert_not_contains(haystack: &str, needle: &str) { - assert!( - !haystack.contains(needle), - "expected prompt not to contain `{needle}`" - ); -} diff --git a/epiphany-core/src/bin/epiphany-persona-other.rs b/epiphany-core/src/bin/epiphany-persona-other.rs deleted file mode 100644 index 79e82ff2..00000000 --- a/epiphany-core/src/bin/epiphany-persona-other.rs +++ /dev/null @@ -1,1290 +0,0 @@ -use anyhow::Context; -use anyhow::Result; -use anyhow::anyhow; -use chrono::SecondsFormat; -use epiphany_core::EPIPHANY_CULTMESH_LOCAL_AREA_VERSE_ID; -use epiphany_core::EPIPHANY_CULTMESH_PERSONA_SPEECH_AUDIT_SCHEMA_VERSION; -use epiphany_core::EpiphanyCultMeshPersonaSpeechAuditEntry; -use epiphany_core::load_latest_epiphany_cultmesh_persona_speech_audit; -use epiphany_core::write_epiphany_cultmesh_persona_speech_audit; -use serde_json::Value; -use std::cmp::Reverse; -use std::env; -use std::fs; -use std::io::Read; -use std::path::Path; -use std::path::PathBuf; -use std::process::Command; -use uuid::Uuid; - -const SPEECH_AUDIT_SCHEMA_VERSION: &str = "epiphany.persona_speech_audit.v0"; - -#[derive(Clone, Debug, Default)] -struct PersonaOtherConfig { - surface_name: String, - target_locator: String, - persona_name: Option, - bifrost_bridge_cli_path: Option, - bifrost_identity: Option, - bifrost_source_kind: Option, - bifrost_authority_ref: Option, - heimdall_capability_ref_env: Option, -} - -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -struct PersonaSpeechAudit { - schema_version: String, - audit_id: String, - created_at: String, - action_kind: String, - decision: String, - reasons: Vec, - content_fingerprint: String, - opening_key: String, - topic_key: String, - requested_public_target: Option, - recent_window_count: usize, - repeated_opening_count: usize, - repeated_topic_count: usize, - #[serde(rename = "sameTargetPostCount")] - same_target_crossing_count: usize, - audit_path: PathBuf, -} - -#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "kebab-case")] -enum PersonaOtherLifecycle { - DraftCandidate, - Blocked, - CrossingRequested, -} - -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -struct BifrostOtherRequestProvenance { - bifrost_identity: String, - source_kind: String, - source_id: String, - authority_reference: String, - epiphany_run_id: String, - epiphany_lane_id: String, - epiphany_agent_identity: String, - heimdall_capability_ref: String, -} - -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -struct BifrostOtherRequestReceipt { - action: String, - ok: bool, - surface_name: String, - target_locator: String, - title: String, - content: String, - bridge_action_id: String, - receipt_url: String, - provenance: BifrostOtherRequestProvenance, -} - -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -struct PersonaOtherRequestArtifact { - schema_version: String, - lifecycle: PersonaOtherLifecycle, - reason: String, - created_at: String, - surface_name: String, - target_locator: String, - title: String, - persona_name: String, - content: String, - #[serde(skip_serializing_if = "Option::is_none")] - speech_audit: Option, - #[serde(skip_serializing_if = "Option::is_none")] - bifrost_request_receipt: Option, - private_state_exposed: bool, -} - -#[derive(Clone, Debug)] -struct RecentPersonaSpeech { - opening_key: String, - topic_key: String, - target: Option, - crossing_recorded: bool, -} - -fn main() -> Result<()> { - let mut args = env::args().skip(1); - let Some(command) = args.next() else { - return usage(); - }; - let mut config_path = PathBuf::from("state/persona-other.toml"); - let mut artifact_dir = PathBuf::from(".epiphany-persona"); - let mut cultmesh_store = PathBuf::from(".epiphany-run/cultmesh/persona-speech.ccmp"); - let mut runtime_id = "epiphany-local".to_string(); - let mut surface_name: Option = None; - let mut target_locator: Option = None; - let mut title: Option = None; - let mut content: Option = None; - let mut persona_name: Option = None; - let mut limit = 8_usize; - - while let Some(arg) = args.next() { - match arg.as_str() { - "--config" => config_path = next_path(&mut args, "--config")?, - "--artifact-dir" => artifact_dir = next_path(&mut args, "--artifact-dir")?, - "--cultmesh-store" => cultmesh_store = next_path(&mut args, "--cultmesh-store")?, - "--runtime-id" => runtime_id = next_value(&mut args, "--runtime-id")?, - "--surface-name" => surface_name = Some(next_value(&mut args, "--surface-name")?), - "--target-locator" => target_locator = Some(next_value(&mut args, "--target-locator")?), - "--title" => title = Some(next_value(&mut args, "--title")?), - "--content" => content = Some(next_value(&mut args, "--content")?), - "--persona-name" => persona_name = Some(next_value(&mut args, "--persona-name")?), - "--limit" => limit = next_value(&mut args, "--limit")?.parse()?, - _ => return Err(anyhow!("unknown argument {arg:?}")), - } - } - - let result = match command.as_str() { - "draft" => { - let config = load_config(&config_path)?; - let content = read_required_text(content, "Persona outside-world request")?; - run_draft( - &content, - &config, - &artifact_dir, - &cultmesh_store, - &runtime_id, - surface_name, - target_locator, - title, - persona_name, - )? - } - "request" => { - let config = load_config(&config_path)?; - let content = read_required_text(content, "Persona outside-world request")?; - run_request( - &content, - &config, - &artifact_dir, - &cultmesh_store, - &runtime_id, - surface_name, - target_locator, - title, - persona_name, - )? - } - "latest" => serde_json::json!({ - "ok": true, - "artifactDir": artifact_dir, - "latestArtifacts": latest_persona_artifacts(&artifact_dir, limit), - }), - "smoke" => run_smoke()?, - _ => return usage(), - }; - - let ok = result["ok"].as_bool().unwrap_or(false); - println!("{}", serde_json::to_string_pretty(&result)?); - if !ok { - std::process::exit(1); - } - Ok(()) -} - -#[allow(clippy::too_many_arguments)] -fn run_draft( - content: &str, - config: &PersonaOtherConfig, - artifact_dir: &Path, - cultmesh_store: &Path, - runtime_id: &str, - surface_name: Option, - target_locator: Option, - title: Option, - persona_name: Option, -) -> Result { - let target = resolve_target(config, surface_name, target_locator)?; - let audit = audit_persona_speech( - content, - artifact_dir, - cultmesh_store, - runtime_id, - &target.surface_name, - &target.target_locator, - false, - )?; - let path = write_request_artifact( - content, - config, - &target, - artifact_dir, - PersonaOtherLifecycle::DraftCandidate, - "drafted without crossing Bifrost", - Some(&audit), - None, - title.as_deref(), - persona_name.as_deref(), - )?; - Ok(serde_json::json!({ - "ok": true, - "requested": false, - "draftPath": path, - "surfaceName": target.surface_name, - "targetLocator": target.target_locator, - "speechAudit": audit, - })) -} - -#[allow(clippy::too_many_arguments)] -fn run_request( - content: &str, - config: &PersonaOtherConfig, - artifact_dir: &Path, - cultmesh_store: &Path, - runtime_id: &str, - surface_name: Option, - target_locator: Option, - title: Option, - persona_name: Option, -) -> Result { - let target = resolve_target(config, surface_name, target_locator)?; - let audit = audit_persona_speech( - content, - artifact_dir, - cultmesh_store, - runtime_id, - &target.surface_name, - &target.target_locator, - true, - )?; - if audit.decision == "blocked" { - let path = write_request_artifact( - content, - config, - &target, - artifact_dir, - PersonaOtherLifecycle::Blocked, - &format!("speech audit blocked: {}", audit.reasons.join("; ")), - Some(&audit), - None, - title.as_deref(), - persona_name.as_deref(), - )?; - return Ok(serde_json::json!({ - "ok": false, - "blocked": "speech-audit", - "artifactPath": path, - "speechAudit": audit, - })); - } - let Some(bridge_cli) = bifrost_bridge_cli_path(config) else { - let path = write_request_artifact( - content, - config, - &target, - artifact_dir, - PersonaOtherLifecycle::Blocked, - "missing Bifrost bridge CLI for outside-world Persona request", - Some(&audit), - None, - title.as_deref(), - persona_name.as_deref(), - )?; - return Ok(serde_json::json!({ - "ok": false, - "blocked": "missing-bifrost-bridge", - "artifactPath": path, - "speechAudit": audit, - })); - }; - let Some(bifrost_identity) = required_bifrost_identity(config) else { - let path = write_request_artifact( - content, - config, - &target, - artifact_dir, - PersonaOtherLifecycle::Blocked, - "missing Bifrost identity for outside-world Persona request", - Some(&audit), - None, - title.as_deref(), - persona_name.as_deref(), - )?; - return Ok(serde_json::json!({ - "ok": false, - "blocked": "missing-bifrost-identity", - "artifactPath": path, - "speechAudit": audit, - })); - }; - let Some(heimdall_capability_ref) = required_heimdall_capability_ref(config) else { - let path = write_request_artifact( - content, - config, - &target, - artifact_dir, - PersonaOtherLifecycle::Blocked, - "missing Heimdall-backed capability/account reference for outside-world Persona request", - Some(&audit), - None, - title.as_deref(), - persona_name.as_deref(), - )?; - return Ok(serde_json::json!({ - "ok": false, - "blocked": "missing-heimdall-capability-ref", - "artifactPath": path, - "speechAudit": audit, - })); - }; - if !is_heimdall_surface_reference(&heimdall_capability_ref, &target.surface_name) { - let path = write_request_artifact( - content, - config, - &target, - artifact_dir, - PersonaOtherLifecycle::Blocked, - &format!( - "Heimdall reference must be shaped for {} public Persona crossings: heimdall:{}:*", - target.surface_name, target.surface_name - ), - Some(&audit), - None, - title.as_deref(), - persona_name.as_deref(), - )?; - return Ok(serde_json::json!({ - "ok": false, - "blocked": "wrong-heimdall-capability-surface", - "artifactPath": path, - "speechAudit": audit, - })); - } - - let receipt = request_bifrost_other( - &bridge_cli, - config, - &bifrost_identity, - &heimdall_capability_ref, - &target, - content, - title.as_deref(), - persona_name.as_deref(), - &audit, - )?; - let path = write_request_artifact( - content, - config, - &target, - artifact_dir, - PersonaOtherLifecycle::CrossingRequested, - "recorded through Bifrost other-request", - Some(&audit), - Some(&receipt), - title.as_deref(), - persona_name.as_deref(), - )?; - Ok(serde_json::json!({ - "ok": true, - "requested": true, - "transport": "bifrost.other-request", - "surfaceName": target.surface_name, - "targetLocator": target.target_locator, - "artifactPath": path, - "bifrostBridgeReceipt": receipt, - "speechAudit": audit, - })) -} - -fn request_bifrost_other( - bridge_cli_path: &Path, - config: &PersonaOtherConfig, - bifrost_identity: &str, - heimdall_capability_ref: &str, - target: &ResolvedTarget, - content: &str, - title: Option<&str>, - persona_name: Option<&str>, - audit: &PersonaSpeechAudit, -) -> Result { - let title = title - .map(str::trim) - .filter(|value| !value.is_empty()) - .map(str::to_string) - .unwrap_or_else(|| format!("Persona request for {}", target.surface_name)); - let persona_name = persona_name - .or(config.persona_name.as_deref()) - .unwrap_or("Epiphany Persona"); - let mut command = Command::new(bifrost_node_executable()); - command.arg(bridge_cli_path); - command.arg("other-request"); - command.arg("--surface-name").arg(&target.surface_name); - command.arg("--target-locator").arg(&target.target_locator); - command.arg("--title").arg(title); - command.arg("--content").arg(content); - command.arg("--identity").arg(bifrost_identity); - command.arg("--source-kind").arg( - config - .bifrost_source_kind - .as_deref() - .unwrap_or("epiphany_persona_other"), - ); - command.arg("--source-id").arg(&audit.audit_id); - command.arg("--authority-ref").arg( - config - .bifrost_authority_ref - .as_deref() - .unwrap_or("epiphany.persona_speech_audit"), - ); - command.arg("--epiphany-lane-id").arg("Persona"); - command - .arg("--epiphany-agent-identity") - .arg("epiphany.Persona"); - command - .arg("--heimdall-capability-ref") - .arg(heimdall_capability_ref); - if let Some(run_id) = env::var("EPIPHANY_RUN_ID") - .ok() - .map(|value| value.trim().to_string()) - .filter(|value| !value.is_empty()) - { - command.arg("--epiphany-run-id").arg(run_id); - } - command.arg("--persona-name").arg(persona_name); - - let output = command.output().with_context(|| { - format!( - "failed to invoke Bifrost bridge CLI at {}", - bridge_cli_path.display() - ) - })?; - if !output.status.success() { - let stderr = String::from_utf8_lossy(&output.stderr); - let stdout = String::from_utf8_lossy(&output.stdout); - return Err(anyhow!( - "Bifrost bridge other-request failed with status {}: {}{}{}", - output.status, - stderr.trim(), - if stdout.trim().is_empty() { - "" - } else { - "\nstdout: " - }, - stdout.trim() - )); - } - let stdout = String::from_utf8(output.stdout).context("Bifrost bridge stdout was not UTF-8")?; - let receipt: BifrostOtherRequestReceipt = serde_json::from_str(strip_bom(&stdout)) - .context("Bifrost bridge stdout was not a typed other-request receipt")?; - if !receipt.ok - || receipt.action != "other-request" - || receipt.surface_name != target.surface_name - || receipt.target_locator != target.target_locator - || receipt.provenance.bifrost_identity != bifrost_identity - || receipt.provenance.source_id != audit.audit_id - || receipt.provenance.heimdall_capability_ref != heimdall_capability_ref - || receipt.provenance.epiphany_lane_id != "Persona" - || receipt.provenance.epiphany_agent_identity != "epiphany.Persona" - || receipt.bridge_action_id.trim().is_empty() - || receipt.receipt_url.trim().is_empty() - { - return Err(anyhow!( - "Bifrost other-request receipt did not bind the requested crossing" - )); - } - Ok(receipt) -} - -fn audit_persona_speech( - content: &str, - artifact_dir: &Path, - cultmesh_store: &Path, - runtime_id: &str, - surface_name: &str, - target_locator: &str, - public_request: bool, -) -> Result { - let opening_key = opening_key(content); - let topic_key = topic_key(content); - let target_key = format!("{surface_name}:{target_locator}"); - let recent = recent_persona_speech(artifact_dir, 12); - let repeated_opening_count = recent - .iter() - .filter(|speech| !opening_key.is_empty() && speech.opening_key == opening_key) - .count(); - let repeated_topic_count = recent - .iter() - .filter(|speech| !topic_key.is_empty() && speech.topic_key == topic_key) - .count(); - let same_target_crossing_count = recent - .iter() - .filter(|speech| speech.crossing_recorded && speech.target.as_deref() == Some(&target_key)) - .count(); - let mut reasons = Vec::new(); - if public_request && repeated_opening_count >= 2 { - reasons.push("repeated-opening".to_string()); - } - if public_request && repeated_topic_count >= 3 { - reasons.push("repeated-topic".to_string()); - } - if public_request && same_target_crossing_count >= 3 { - reasons.push("target-saturation".to_string()); - } - if content.trim().len() > 9000 { - reasons.push("content-too-long-for-public-persona".to_string()); - } - let decision = if reasons.is_empty() { - "eligible" - } else { - "blocked" - } - .to_string(); - let audit_id = format!("persona-speech-audit-{}", short_id()); - let audit_path = artifact_dir.join(format!( - "Persona-other-speech-audit-{}-{}.json", - now_stamp(), - audit_id - )); - let action_kind = if public_request { "post" } else { "draft" }.to_string(); - let audit = PersonaSpeechAudit { - schema_version: SPEECH_AUDIT_SCHEMA_VERSION.to_string(), - audit_id, - created_at: now_iso(), - action_kind, - decision, - reasons, - content_fingerprint: content_fingerprint(content), - opening_key, - topic_key, - requested_public_target: Some(target_key.clone()), - recent_window_count: recent.len(), - repeated_opening_count, - repeated_topic_count, - same_target_crossing_count, - audit_path, - }; - write_json(&audit.audit_path, &serde_json::to_value(&audit)?)?; - write_epiphany_cultmesh_persona_speech_audit( - cultmesh_store, - EpiphanyCultMeshPersonaSpeechAuditEntry { - schema_version: EPIPHANY_CULTMESH_PERSONA_SPEECH_AUDIT_SCHEMA_VERSION.to_string(), - audit_id: audit.audit_id.clone(), - runtime_id: runtime_id.to_string(), - verse_id: EPIPHANY_CULTMESH_LOCAL_AREA_VERSE_ID.to_string(), - persona_agent_id: "epiphany.Persona".to_string(), - action_kind: audit.action_kind.clone(), - decision: audit.decision.clone(), - content_fingerprint: audit.content_fingerprint.clone(), - opening_key: audit.opening_key.clone(), - topic_key: audit.topic_key.clone(), - requested_channel_id: target_key, - recent_window_count: audit.recent_window_count as u32, - repeated_opening_count: audit.repeated_opening_count as u32, - repeated_topic_count: audit.repeated_topic_count as u32, - same_channel_post_count: audit.same_target_crossing_count as u32, - reasons: audit.reasons.clone(), - artifact_ref: audit.audit_path.display().to_string(), - created_at_utc: audit.created_at.clone(), - private_state_exposed: false, - notes: vec![ - "Persona future-surface speech audit is parent-side mouth policy; it stores fingerprints and counters, not raw private thought.".to_string(), - "Unknown outside-world crossings belong to Bifrost other-request until the surface earns a named actuator.".to_string(), - ], - }, - )?; - Ok(audit) -} - -fn run_smoke() -> Result { - let temp_dir = scoped_temp_dir("epiphany-persona-other-smoke")?; - let cultmesh_store = temp_dir.join("persona-speech.ccmp"); - let runtime_id = "epiphany-persona-other-smoke"; - let fake_bridge = temp_dir.join("fake-bifrost-bridge.mjs"); - fs::write( - &fake_bridge, - r#" -const args = process.argv.slice(2); -function opt(name) { - const i = args.indexOf(name); - return i >= 0 && i + 1 < args.length ? args[i + 1] : ""; -} -console.log(JSON.stringify({ - action: args[0], - ok: true, - surfaceName: opt("--surface-name"), - targetLocator: opt("--target-locator"), - title: opt("--title"), - content: opt("--content"), - bridgeActionId: "bridge-action-other-smoke", - receiptUrl: "bifrost://bridge/actions/bridge-action-other-smoke", - provenance: { - bifrostIdentity: opt("--identity"), - sourceKind: opt("--source-kind"), - sourceId: opt("--source-id"), - authorityReference: opt("--authority-ref"), - epiphanyRunId: opt("--epiphany-run-id"), - epiphanyLaneId: opt("--epiphany-lane-id"), - epiphanyAgentIdentity: opt("--epiphany-agent-identity"), - heimdallCapabilityRef: opt("--heimdall-capability-ref") - } -})); -"#, - ) - .with_context(|| format!("failed to write {}", fake_bridge.display()))?; - let config = PersonaOtherConfig { - surface_name: "bluesky".to_string(), - target_locator: "at://did:example/app.bsky.feed.post/root".to_string(), - persona_name: Some("Smoke Persona".to_string()), - bifrost_bridge_cli_path: Some(fake_bridge), - bifrost_identity: Some("epiphany.Persona".to_string()), - bifrost_source_kind: Some("epiphany_persona_other".to_string()), - bifrost_authority_ref: Some("epiphany.persona_speech_audit".to_string()), - heimdall_capability_ref_env: Some("HEIMDALL_CAPABILITY_REF_TEST".to_string()), - }; - let no_bridge_config = PersonaOtherConfig { - bifrost_bridge_cli_path: None, - ..config.clone() - }; - let missing_capability_config = PersonaOtherConfig { - heimdall_capability_ref_env: Some("HEIMDALL_CAPABILITY_REF_MISSING_FOR_SMOKE".to_string()), - ..config.clone() - }; - let wrong_surface_config = PersonaOtherConfig { - heimdall_capability_ref_env: Some( - "HEIMDALL_CAPABILITY_REF_WRONG_SURFACE_FOR_SMOKE".to_string(), - ), - ..config.clone() - }; - unsafe { - env::set_var( - "HEIMDALL_CAPABILITY_REF_TEST", - "heimdall:bluesky:capability:smoke-persona", - ); - env::set_var( - "HEIMDALL_CAPABILITY_REF_WRONG_SURFACE_FOR_SMOKE", - "heimdall:discord:capability:smoke-persona", - ); - env::remove_var("HEIMDALL_CAPABILITY_REF_MISSING_FOR_SMOKE"); - env::set_var("EPIPHANY_RUN_ID", "epiphany-run-other-smoke"); - } - - let draft = run_draft( - "Persona drafts a future-surface request without crossing Bifrost.", - &config, - &temp_dir, - &cultmesh_store, - runtime_id, - None, - None, - Some("Draft future surface".to_string()), - None, - )?; - let missing_bridge = run_request( - "Persona should not reach a future surface without Bifrost configured.", - &no_bridge_config, - &temp_dir, - &cultmesh_store, - runtime_id, - None, - None, - None, - None, - )?; - let missing_capability = run_request( - "Persona should not reach a future surface without Heimdall-backed capability proof.", - &missing_capability_config, - &temp_dir, - &cultmesh_store, - runtime_id, - None, - None, - None, - None, - )?; - let wrong_surface_capability = run_request( - "Persona should not reach Bluesky with Discord Heimdall-backed capability proof.", - &wrong_surface_config, - &temp_dir, - &cultmesh_store, - runtime_id, - None, - None, - None, - None, - )?; - let bridged = run_request( - "Persona requests a future-surface crossing only through Bifrost.", - &config, - &temp_dir, - &cultmesh_store, - runtime_id, - None, - None, - Some("Smoke future surface".to_string()), - None, - )?; - seed_requested_artifact( - "Repeated future surface rite", - &config, - &temp_dir, - &cultmesh_store, - runtime_id, - )?; - seed_requested_artifact( - "Repeated future surface rite", - &config, - &temp_dir, - &cultmesh_store, - runtime_id, - )?; - let repeated = run_request( - "Repeated future surface rite again.", - &config, - &temp_dir, - &cultmesh_store, - runtime_id, - None, - None, - None, - None, - )?; - let latest_cultmesh_audit = - load_latest_epiphany_cultmesh_persona_speech_audit(&cultmesh_store, runtime_id)? - .context("Persona other smoke expected latest CultMesh audit")?; - let draft_artifact = read_artifact(&draft["draftPath"])?; - let bridged_artifact = read_artifact(&bridged["artifactPath"])?; - let blocked_artifact = read_artifact(&missing_bridge["artifactPath"])?; - let base_keys = [ - "schemaVersion", - "lifecycle", - "reason", - "createdAt", - "surfaceName", - "targetLocator", - "title", - "personaName", - "content", - "speechAudit", - "privateStateExposed", - ]; - let requested_keys = [ - "schemaVersion", - "lifecycle", - "reason", - "createdAt", - "surfaceName", - "targetLocator", - "title", - "personaName", - "content", - "speechAudit", - "bifrostRequestReceipt", - "privateStateExposed", - ]; - let ok = draft["ok"] == true - && exact_root_keys(&draft_artifact, &base_keys) - && draft_artifact["lifecycle"] == "draft-candidate" - && exact_root_keys(&blocked_artifact, &base_keys) - && blocked_artifact["lifecycle"] == "blocked" - && exact_root_keys(&bridged_artifact, &requested_keys) - && bridged_artifact["lifecycle"] == "crossing-requested" - && bridged_artifact.get("status").is_none() - && bridged_artifact.get("published").is_none() - && bridged_artifact["bifrostRequestReceipt"]["action"] == "other-request" - && missing_bridge["ok"] == false - && missing_bridge["blocked"] == "missing-bifrost-bridge" - && missing_capability["ok"] == false - && missing_capability["blocked"] == "missing-heimdall-capability-ref" - && wrong_surface_capability["ok"] == false - && wrong_surface_capability["blocked"] == "wrong-heimdall-capability-surface" - && bridged["ok"] == true - && bridged["transport"] == "bifrost.other-request" - && bridged["bifrostBridgeReceipt"]["action"] == "other-request" - && bridged["bifrostBridgeReceipt"]["surfaceName"] == "bluesky" - && bridged["bifrostBridgeReceipt"]["provenance"]["bifrostIdentity"] == "epiphany.Persona" - && bridged["bifrostBridgeReceipt"]["provenance"]["heimdallCapabilityRef"] - == "heimdall:bluesky:capability:smoke-persona" - && repeated["ok"] == false - && repeated["blocked"] == "speech-audit" - && repeated["speechAudit"]["reasons"] - .as_array() - .is_some_and(|reasons| reasons.iter().any(|reason| reason == "target-saturation")) - && latest_cultmesh_audit.decision == "blocked" - && !latest_cultmesh_audit.private_state_exposed; - let result = serde_json::json!({ - "ok": ok, - "draft": draft, - "missingBridge": missing_bridge, - "missingCapability": missing_capability, - "wrongSurfaceCapability": wrong_surface_capability, - "bridged": bridged, - "repeatedSpeech": repeated, - "latestCultMeshSpeechAudit": latest_cultmesh_audit, - }); - let _ = fs::remove_dir_all(&temp_dir); - Ok(result) -} - -fn read_artifact(path: &Value) -> Result { - let path = path - .as_str() - .context("smoke result omitted artifact path")?; - serde_json::from_str(&fs::read_to_string(path)?) - .with_context(|| format!("failed to decode smoke artifact {path}")) -} - -fn exact_root_keys(value: &Value, expected: &[&str]) -> bool { - let Some(object) = value.as_object() else { - return false; - }; - object.len() == expected.len() && expected.iter().all(|key| object.contains_key(*key)) -} - -fn seed_requested_artifact( - content: &str, - config: &PersonaOtherConfig, - artifact_dir: &Path, - cultmesh_store: &Path, - runtime_id: &str, -) -> Result<()> { - let target = resolve_target(config, None, None)?; - let audit = audit_persona_speech( - content, - artifact_dir, - cultmesh_store, - runtime_id, - &target.surface_name, - &target.target_locator, - false, - )?; - write_request_artifact( - content, - config, - &target, - artifact_dir, - PersonaOtherLifecycle::CrossingRequested, - "seed prior future-surface request for speech-audit smoke", - Some(&audit), - None, - None, - None, - )?; - Ok(()) -} - -#[allow(clippy::too_many_arguments)] -fn write_request_artifact( - content: &str, - config: &PersonaOtherConfig, - target: &ResolvedTarget, - artifact_dir: &Path, - lifecycle: PersonaOtherLifecycle, - reason: &str, - speech_audit: Option<&PersonaSpeechAudit>, - bridge_receipt: Option<&BifrostOtherRequestReceipt>, - title: Option<&str>, - persona_name: Option<&str>, -) -> Result { - fs::create_dir_all(artifact_dir) - .with_context(|| format!("failed to create {}", artifact_dir.display()))?; - let path = artifact_dir.join(format!("Persona-other-{}-{}.json", now_stamp(), short_id())); - let persona_name = persona_name - .or(config.persona_name.as_deref()) - .unwrap_or("Epiphany Persona"); - let payload = PersonaOtherRequestArtifact { - schema_version: "epiphany.persona_other_request.v0".to_string(), - lifecycle, - reason: reason.to_string(), - created_at: now_iso(), - surface_name: target.surface_name.clone(), - target_locator: target.target_locator.clone(), - title: title - .unwrap_or("Persona outside-world bridge request") - .to_string(), - persona_name: persona_name.to_string(), - content: content.to_string(), - speech_audit: speech_audit.cloned(), - bifrost_request_receipt: bridge_receipt.cloned(), - private_state_exposed: false, - }; - write_json(&path, &serde_json::to_value(payload)?)?; - Ok(path) -} - -fn latest_persona_artifacts(artifact_dir: &Path, limit: usize) -> Vec { - let mut entries = fs::read_dir(artifact_dir) - .ok() - .into_iter() - .flat_map(|entries| entries.filter_map(Result::ok)) - .filter(|entry| { - entry - .file_name() - .to_str() - .is_some_and(|name| name.starts_with("Persona-other-") && name.ends_with(".json")) - }) - .filter_map(|entry| { - let metadata = entry.metadata().ok()?; - let modified = metadata.modified().ok()?; - Some((Reverse(modified), entry.path())) - }) - .collect::>(); - entries.sort(); - entries - .into_iter() - .take(limit) - .filter_map(|(_, path)| { - let raw = fs::read_to_string(&path).ok()?; - let payload: Value = serde_json::from_str(&raw).ok()?; - Some(serde_json::json!({ - "path": path, - "lifecycle": payload.get("lifecycle").or_else(|| payload.get("status")), - "surfaceName": payload.get("surfaceName"), - "targetLocator": payload.get("targetLocator"), - "privateStateExposed": payload.get("privateStateExposed"), - })) - }) - .collect() -} - -fn recent_persona_speech(artifact_dir: &Path, limit: usize) -> Vec { - let mut entries = fs::read_dir(artifact_dir) - .ok() - .into_iter() - .flat_map(|entries| entries.filter_map(Result::ok)) - .filter(|entry| { - entry - .file_name() - .to_str() - .is_some_and(|name| name.starts_with("Persona-other-") && name.ends_with(".json")) - }) - .filter_map(|entry| { - let metadata = entry.metadata().ok()?; - let modified = metadata.modified().ok()?; - Some((Reverse(modified), entry.path())) - }) - .collect::>(); - entries.sort(); - entries - .into_iter() - .take(limit) - .filter_map(|(_, path)| { - let raw = fs::read_to_string(path).ok()?; - let payload: Value = serde_json::from_str(&raw).ok()?; - let audit = payload.get("speechAudit")?; - Some(RecentPersonaSpeech { - opening_key: audit["openingKey"].as_str().unwrap_or_default().to_string(), - topic_key: audit["topicKey"].as_str().unwrap_or_default().to_string(), - target: audit["requestedPublicTarget"].as_str().map(str::to_string), - crossing_recorded: payload["lifecycle"].as_str() == Some("crossing-requested") - || payload["status"].as_str() == Some("requested"), - }) - }) - .collect() -} - -#[derive(Clone, Debug)] -struct ResolvedTarget { - surface_name: String, - target_locator: String, -} - -fn resolve_target( - config: &PersonaOtherConfig, - surface_name: Option, - target_locator: Option, -) -> Result { - let surface_name = normalize_surface_name( - surface_name - .as_deref() - .unwrap_or(config.surface_name.as_str()), - ); - if surface_name.is_empty() { - return Err(anyhow!("outside-world surface name is required")); - } - let target_locator = target_locator - .or_else(|| { - (!config.target_locator.trim().is_empty()).then(|| config.target_locator.clone()) - }) - .unwrap_or_else(|| format!("{surface_name}:unspecified")); - Ok(ResolvedTarget { - surface_name, - target_locator: target_locator.trim().to_string(), - }) -} - -fn load_config(path: &Path) -> Result { - if !path.exists() { - return Ok(PersonaOtherConfig { - surface_name: "future-surface".to_string(), - target_locator: "future-surface:unspecified".to_string(), - ..PersonaOtherConfig::default() - }); - } - let raw = - fs::read_to_string(path).with_context(|| format!("failed to read {}", path.display()))?; - if path - .extension() - .is_some_and(|extension| extension == "json") - { - let payload: Value = serde_json::from_str(&raw) - .with_context(|| format!("failed to decode {}", path.display()))?; - return Ok(PersonaOtherConfig { - surface_name: payload["surface_name"] - .as_str() - .map(normalize_surface_name) - .unwrap_or_else(|| "future-surface".to_string()), - target_locator: payload["target_locator"] - .as_str() - .map(str::to_string) - .unwrap_or_else(|| "future-surface:unspecified".to_string()), - persona_name: payload["persona_name"].as_str().map(str::to_string), - bifrost_bridge_cli_path: payload["bifrost_bridge_cli_path"] - .as_str() - .map(PathBuf::from), - bifrost_identity: payload["bifrost_identity"].as_str().map(str::to_string), - bifrost_source_kind: payload["bifrost_source_kind"].as_str().map(str::to_string), - bifrost_authority_ref: payload["bifrost_authority_ref"] - .as_str() - .map(str::to_string), - heimdall_capability_ref_env: payload["heimdall_capability_ref_env"] - .as_str() - .map(str::to_string), - }); - } - Ok(PersonaOtherConfig { - surface_name: toml_string(&raw, "surface_name") - .map(normalize_surface_name) - .unwrap_or_else(|| "future-surface".to_string()), - target_locator: toml_string(&raw, "target_locator") - .unwrap_or_else(|| "future-surface:unspecified".to_string()), - persona_name: toml_string(&raw, "persona_name"), - bifrost_bridge_cli_path: toml_string(&raw, "bifrost_bridge_cli_path").map(PathBuf::from), - bifrost_identity: toml_string(&raw, "bifrost_identity"), - bifrost_source_kind: toml_string(&raw, "bifrost_source_kind"), - bifrost_authority_ref: toml_string(&raw, "bifrost_authority_ref"), - heimdall_capability_ref_env: toml_string(&raw, "heimdall_capability_ref_env"), - }) -} - -fn bifrost_bridge_cli_path(config: &PersonaOtherConfig) -> Option { - config.bifrost_bridge_cli_path.clone().or_else(|| { - env::var("EPIPHANY_PERSONA_BIFROST_BRIDGE_CLI") - .ok() - .map(|value| value.trim().to_string()) - .filter(|value| !value.is_empty()) - .map(PathBuf::from) - }) -} - -fn required_bifrost_identity(config: &PersonaOtherConfig) -> Option { - config - .bifrost_identity - .clone() - .map(|value| value.trim().to_string()) - .filter(|value| !value.is_empty()) - .or_else(|| { - env::var("BIFROST_IDENTITY") - .ok() - .map(|value| value.trim().to_string()) - .filter(|value| !value.is_empty()) - }) -} - -fn required_heimdall_capability_ref(config: &PersonaOtherConfig) -> Option { - config - .heimdall_capability_ref_env - .as_ref() - .and_then(env_value) - .or_else(|| { - env::var("HEIMDALL_CAPABILITY_REF") - .ok() - .map(|value| value.trim().to_string()) - .filter(|value| !value.is_empty()) - }) - .or_else(|| { - env::var("BIFROST_HEIMDALL_CAPABILITY_REF") - .ok() - .map(|value| value.trim().to_string()) - .filter(|value| !value.is_empty()) - }) -} - -fn is_heimdall_surface_reference(reference: &str, surface: &str) -> bool { - let reference = reference.trim().to_ascii_lowercase(); - let surface = normalize_surface_name(surface); - reference.starts_with("heimdall:") && reference.contains(&format!(":{surface}:")) -} - -fn normalize_surface_name(value: impl AsRef) -> String { - value - .as_ref() - .trim() - .trim_matches('/') - .chars() - .filter(|ch| ch.is_ascii_alphanumeric() || *ch == '-' || *ch == '_') - .collect::() - .to_ascii_lowercase() -} - -fn bifrost_node_executable() -> String { - env::var("EPIPHANY_BIFROST_NODE") - .ok() - .map(|value| value.trim().to_string()) - .filter(|value| !value.is_empty()) - .unwrap_or_else(|| "node".to_string()) -} - -fn env_value(name: &String) -> Option { - env::var(name) - .ok() - .map(|value| value.trim().to_string()) - .filter(|value| !value.is_empty()) -} - -fn toml_string(raw: &str, key: &str) -> Option { - raw.lines() - .map(str::trim) - .filter(|line| !line.starts_with('#')) - .find_map(|line| { - let (left, right) = line.split_once('=')?; - (left.trim() == key).then(|| parse_quoted(right.trim()))? - }) -} - -fn parse_quoted(value: &str) -> Option { - let value = value.trim(); - value - .strip_prefix('"') - .and_then(|value| value.strip_suffix('"')) - .map(str::to_string) -} - -fn opening_key(content: &str) -> String { - content - .split_whitespace() - .take(6) - .map(normalize_token) - .filter(|token| !token.is_empty()) - .collect::>() - .join(" ") -} - -fn topic_key(content: &str) -> String { - let mut tokens = content - .split_whitespace() - .map(normalize_token) - .filter(|token| token.len() >= 4 && !persona_stopword(token)) - .collect::>(); - tokens.sort(); - tokens.dedup(); - tokens.into_iter().take(8).collect::>().join("|") -} - -fn content_fingerprint(content: &str) -> String { - topic_key(content) - + "::" - + &content - .split_whitespace() - .map(normalize_token) - .filter(|token| !token.is_empty()) - .take(24) - .collect::>() - .join("-") -} - -fn normalize_token(value: &str) -> String { - value - .trim_matches(|ch: char| !ch.is_ascii_alphanumeric()) - .chars() - .filter(|ch| ch.is_ascii_alphanumeric() || *ch == '-' || *ch == '_') - .collect::() - .to_ascii_lowercase() -} - -fn persona_stopword(value: &str) -> bool { - matches!( - value, - "about" - | "after" - | "again" - | "because" - | "before" - | "being" - | "should" - | "there" - | "these" - | "thing" - | "through" - | "under" - | "without" - | "would" - ) -} - -fn read_required_text(value: Option, label: &str) -> Result { - let Some(value) = value else { - return Err(anyhow!("{label} is required")); - }; - if let Some(path) = value.strip_prefix('@') { - return fs::read_to_string(path).with_context(|| format!("failed to read {path}")); - } - if value == "-" { - let mut buffer = String::new(); - std::io::stdin() - .read_to_string(&mut buffer) - .context("failed to read stdin")?; - return Ok(buffer); - } - Ok(value) -} - -fn write_json(path: &Path, value: &Value) -> Result<()> { - if let Some(parent) = path.parent() { - fs::create_dir_all(parent) - .with_context(|| format!("failed to create {}", parent.display()))?; - } - fs::write(path, format!("{}\n", serde_json::to_string_pretty(value)?)) - .with_context(|| format!("failed to write {}", path.display())) -} - -fn strip_bom(value: &str) -> &str { - value.strip_prefix('\u{feff}').unwrap_or(value) -} - -fn now_iso() -> String { - chrono::Utc::now() - .to_rfc3339_opts(SecondsFormat::Secs, true) - .replace('Z', "+00:00") -} - -fn now_stamp() -> String { - chrono::Utc::now().format("%Y%m%dT%H%M%SZ").to_string() -} - -fn short_id() -> String { - Uuid::new_v4().simple().to_string()[..8].to_string() -} - -fn scoped_temp_dir(prefix: &str) -> Result { - let path = env::temp_dir().join(format!("{prefix}-{}", Uuid::new_v4())); - fs::create_dir(&path).with_context(|| format!("failed to claim {}", path.display()))?; - Ok(path) -} - -fn next_path(args: &mut impl Iterator, name: &str) -> Result { - Ok(PathBuf::from(next_value(args, name)?)) -} - -fn next_value(args: &mut impl Iterator, name: &str) -> Result { - args.next() - .ok_or_else(|| anyhow!("{name} requires a value")) -} - -fn usage() -> Result<()> { - Err(anyhow!( - "usage: epiphany-persona-other [--config ] [--artifact-dir ] [--surface-name ] [--target-locator ] [--title ] [--content ]" - )) -} diff --git a/epiphany-core/src/bin/epiphany-persona-reddit.rs b/epiphany-core/src/bin/epiphany-persona-reddit.rs deleted file mode 100644 index 31f6d96e..00000000 --- a/epiphany-core/src/bin/epiphany-persona-reddit.rs +++ /dev/null @@ -1,1364 +0,0 @@ -use anyhow::Context; -use anyhow::Result; -use anyhow::anyhow; -use chrono::SecondsFormat; -use epiphany_core::EPIPHANY_CULTMESH_LOCAL_AREA_VERSE_ID; -use epiphany_core::EPIPHANY_CULTMESH_PERSONA_SPEECH_AUDIT_SCHEMA_VERSION; -use epiphany_core::EpiphanyCultMeshPersonaSpeechAuditEntry; -use epiphany_core::load_latest_epiphany_cultmesh_persona_speech_audit; -use epiphany_core::write_epiphany_cultmesh_persona_speech_audit; -use serde_json::Value; -use std::cmp::Reverse; -use std::env; -use std::fs; -use std::io::Read; -use std::path::Path; -use std::path::PathBuf; -use std::process::Command; -use uuid::Uuid; - -const POST_SCHEMA_VERSION: &str = "epiphany.persona_reddit_post.v0"; -const SPEECH_AUDIT_SCHEMA_VERSION: &str = "epiphany.persona_speech_audit.v0"; - -#[derive(Clone, Debug, Default)] -struct PersonaRedditConfig { - subreddit: String, - persona_name: Option, - persona_flair_id: Option, - persona_flair_text: Option, - bifrost_bridge_cli_path: Option, - bifrost_identity: Option, - bifrost_source_kind: Option, - bifrost_authority_ref: Option, - heimdall_capability_ref_env: Option, -} - -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "camelCase")] -struct PersonaSpeechAudit { - #[serde(alias = "schema_version")] - schema_version: String, - audit_id: String, - created_at: String, - action_kind: String, - decision: String, - reasons: Vec, - content_fingerprint: String, - opening_key: String, - topic_key: String, - requested_public_target: Option, - recent_window_count: usize, - repeated_opening_count: usize, - repeated_topic_count: usize, - same_target_post_count: usize, - audit_path: PathBuf, -} - -#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq)] -#[serde(rename_all = "lowercase")] -enum PersonaRedditPostStatus { - Draft, - Blocked, - Posted, -} - -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "camelCase")] -struct BifrostRedditPublicationProvenance { - bifrost_identity: String, - source_kind: String, - source_id: String, - authority_reference: String, - #[serde(default, skip_serializing_if = "Option::is_none")] - epiphany_run_id: Option, - epiphany_lane_id: String, - epiphany_agent_identity: String, - heimdall_capability_ref: String, -} - -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "camelCase")] -struct BifrostRedditPublicationReceipt { - action: String, - ok: bool, - subreddit: String, - #[serde(default, skip_serializing_if = "Option::is_none")] - thing_id: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - url: Option, - crossing_receipt_id: String, - provenance: BifrostRedditPublicationProvenance, -} - -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "camelCase")] -struct PersonaRedditPostArtifact { - schema_version: String, - created_at: String, - status: PersonaRedditPostStatus, - reason: String, - subreddit: String, - persona_name: Option, - persona_flair_id: Option, - persona_flair_text: Option, - title: String, - content: String, - speech_audit: Option, - #[serde(skip_serializing_if = "Option::is_none")] - bifrost_bridge_receipt: Option, -} - -#[derive(Clone, Debug)] -struct RecentPersonaSpeech { - opening_key: String, - topic_key: String, - subreddit: Option, - posted: bool, -} - -fn main() -> Result<()> { - let mut args = env::args().skip(1); - let Some(command) = args.next() else { - return usage(); - }; - let mut config_path = PathBuf::from("state/persona-reddit.toml"); - let mut artifact_dir = PathBuf::from(".epiphany-persona"); - let mut cultmesh_store = PathBuf::from(".epiphany-run/cultmesh/persona-speech.ccmp"); - let mut runtime_id = "epiphany-local".to_string(); - let mut title: Option = None; - let mut content: Option = None; - let mut subreddit: Option = None; - let mut persona_name: Option = None; - let mut persona_flair_id: Option = None; - let mut persona_flair_text: Option = None; - let mut limit = 8_usize; - - while let Some(arg) = args.next() { - match arg.as_str() { - "--config" => config_path = next_path(&mut args, "--config")?, - "--artifact-dir" => artifact_dir = next_path(&mut args, "--artifact-dir")?, - "--cultmesh-store" => cultmesh_store = next_path(&mut args, "--cultmesh-store")?, - "--runtime-id" => runtime_id = next_value(&mut args, "--runtime-id")?, - "--title" => title = Some(next_value(&mut args, "--title")?), - "--content" => content = Some(next_value(&mut args, "--content")?), - "--subreddit" => subreddit = Some(next_value(&mut args, "--subreddit")?), - "--persona-name" => persona_name = Some(next_value(&mut args, "--persona-name")?), - "--persona-flair-id" => { - persona_flair_id = Some(next_value(&mut args, "--persona-flair-id")?) - } - "--persona-flair-text" => { - persona_flair_text = Some(next_value(&mut args, "--persona-flair-text")?) - } - "--limit" => limit = next_value(&mut args, "--limit")?.parse()?, - _ => return Err(anyhow!("unknown argument {arg:?}")), - } - } - - let result = match command.as_str() { - "draft" => { - let config = load_config(&config_path)?; - let title = read_required_text(title, "Persona Reddit title")?; - let content = read_required_text(content, "Persona Reddit post")?; - run_draft( - &title, - &content, - &config, - &artifact_dir, - &cultmesh_store, - &runtime_id, - )? - } - "post" => { - let config = load_config(&config_path)?; - let title = read_required_text(title, "Persona Reddit title")?; - let content = read_required_text(content, "Persona Reddit post")?; - run_post( - &title, - &content, - &config, - &artifact_dir, - &cultmesh_store, - &runtime_id, - subreddit, - persona_name, - persona_flair_id, - persona_flair_text, - )? - } - "latest" => serde_json::json!({ - "ok": true, - "artifactDir": artifact_dir, - "latestArtifacts": latest_persona_artifacts(&artifact_dir, limit), - }), - "smoke" => run_smoke()?, - _ => return usage(), - }; - let ok = result["ok"].as_bool().unwrap_or(false); - println!("{}", serde_json::to_string_pretty(&result)?); - if !ok { - std::process::exit(1); - } - Ok(()) -} - -fn run_draft( - title: &str, - content: &str, - config: &PersonaRedditConfig, - artifact_dir: &Path, - cultmesh_store: &Path, - runtime_id: &str, -) -> Result { - ensure_post(title, content)?; - let audit = audit_persona_speech( - title, - content, - artifact_dir, - cultmesh_store, - runtime_id, - Some(&config.subreddit), - false, - )?; - let path = write_post_artifact( - title, - content, - config, - artifact_dir, - PersonaRedditPostStatus::Draft, - "drafted without posting", - Some(&audit), - None, - )?; - Ok(serde_json::json!({ - "ok": true, - "posted": false, - "draftPath": path, - "speechAudit": audit, - })) -} - -fn run_post( - title: &str, - content: &str, - config: &PersonaRedditConfig, - artifact_dir: &Path, - cultmesh_store: &Path, - runtime_id: &str, - subreddit: Option, - persona_name: Option, - persona_flair_id: Option, - persona_flair_text: Option, -) -> Result { - ensure_post(title, content)?; - let target_subreddit = clean_subreddit(subreddit.unwrap_or_else(|| config.subreddit.clone())); - let audit = audit_persona_speech( - title, - content, - artifact_dir, - cultmesh_store, - runtime_id, - Some(&target_subreddit), - true, - )?; - if audit.decision == "blocked" { - let path = write_post_artifact( - title, - content, - config, - artifact_dir, - PersonaRedditPostStatus::Blocked, - &format!("speech audit blocked: {}", audit.reasons.join("; ")), - Some(&audit), - None, - )?; - return Ok(serde_json::json!({ - "ok": false, - "posted": false, - "blocked": "speech-audit", - "draftPath": path, - "speechAudit": audit, - })); - } - if target_subreddit.is_empty() { - let path = write_post_artifact( - title, - content, - config, - artifact_dir, - PersonaRedditPostStatus::Blocked, - "missing Bifrost-owned subreddit", - Some(&audit), - None, - )?; - return Ok(serde_json::json!({ - "ok": false, - "posted": false, - "blocked": "missing-subreddit", - "draftPath": path, - "speechAudit": audit, - })); - } - let Some(bridge_cli_path) = bifrost_bridge_cli_path(config) else { - let path = write_post_artifact( - title, - content, - config, - artifact_dir, - PersonaRedditPostStatus::Blocked, - "missing Bifrost bridge CLI path", - Some(&audit), - None, - )?; - return Ok(serde_json::json!({ - "ok": false, - "posted": false, - "blocked": "missing-bifrost-bridge", - "draftPath": path, - "speechAudit": audit, - })); - }; - let Some(bifrost_identity) = required_bifrost_identity(config) else { - let path = write_post_artifact( - title, - content, - config, - artifact_dir, - PersonaRedditPostStatus::Blocked, - "missing Bifrost identity for public Persona crossing", - Some(&audit), - None, - )?; - return Ok(serde_json::json!({ - "ok": false, - "posted": false, - "blocked": "missing-bifrost-identity", - "draftPath": path, - "speechAudit": audit, - })); - }; - let Some(heimdall_capability_ref) = required_heimdall_capability_ref(config) else { - let path = write_post_artifact( - title, - content, - config, - artifact_dir, - PersonaRedditPostStatus::Blocked, - "missing Heimdall-backed Reddit capability/account reference for public Persona crossing; register the Bifrost identity, link it to Heimdall, and provide a heimdall:reddit:* reference", - Some(&audit), - None, - )?; - return Ok(serde_json::json!({ - "ok": false, - "posted": false, - "blocked": "missing-heimdall-capability-ref", - "draftPath": path, - "speechAudit": audit, - })); - }; - if !is_heimdall_surface_reference(&heimdall_capability_ref, "reddit") { - let path = write_post_artifact( - title, - content, - config, - artifact_dir, - PersonaRedditPostStatus::Blocked, - "Heimdall reference must be shaped for Reddit public Persona crossings: heimdall:reddit:*", - Some(&audit), - None, - )?; - return Ok(serde_json::json!({ - "ok": false, - "posted": false, - "blocked": "wrong-heimdall-capability-surface", - "draftPath": path, - "speechAudit": audit, - })); - } - let receipt = post_bifrost_reddit_thread( - &bridge_cli_path, - config, - &bifrost_identity, - &heimdall_capability_ref, - &target_subreddit, - title, - content, - persona_name.as_deref(), - persona_flair_id.as_deref(), - persona_flair_text.as_deref(), - &audit, - )?; - let path = write_post_artifact( - title, - content, - config, - artifact_dir, - PersonaRedditPostStatus::Posted, - "posted through Bifrost reddit-post", - Some(&audit), - Some(&receipt), - )?; - Ok(serde_json::json!({ - "ok": true, - "posted": true, - "transport": "bifrost.reddit-post", - "subreddit": target_subreddit, - "thingId": receipt.thing_id.as_deref(), - "url": receipt.url.as_deref(), - "bifrostBridgeReceipt": receipt, - "draftPath": path, - "speechAudit": audit, - })) -} - -fn post_bifrost_reddit_thread( - bridge_cli_path: &Path, - config: &PersonaRedditConfig, - bifrost_identity: &str, - heimdall_capability_ref: &str, - subreddit: &str, - title: &str, - content: &str, - persona_name: Option<&str>, - persona_flair_id: Option<&str>, - persona_flair_text: Option<&str>, - audit: &PersonaSpeechAudit, -) -> Result { - let persona_name = persona_name - .or(config.persona_name.as_deref()) - .unwrap_or("Epiphany Persona"); - let persona_flair_id = persona_flair_id.or(config.persona_flair_id.as_deref()); - let persona_flair_text = persona_flair_text - .or(config.persona_flair_text.as_deref()) - .unwrap_or(persona_name); - - let mut command = Command::new(bifrost_node_executable()); - command.arg(bridge_cli_path); - command.arg("reddit-post"); - command.arg("--subreddit").arg(subreddit); - command.arg("--title").arg(title); - command.arg("--content").arg(content); - command.arg("--persona-name").arg(persona_name); - command.arg("--persona-flair-text").arg(persona_flair_text); - if let Some(flair_id) = persona_flair_id { - command.arg("--persona-flair-id").arg(flair_id); - } - command.arg("--identity").arg(bifrost_identity); - command.arg("--source-kind").arg( - config - .bifrost_source_kind - .as_deref() - .unwrap_or("epiphany_persona_reddit"), - ); - command.arg("--source-id").arg(&audit.audit_id); - command.arg("--authority-ref").arg( - config - .bifrost_authority_ref - .as_deref() - .unwrap_or("epiphany.persona_speech_audit"), - ); - command.arg("--epiphany-lane-id").arg("Persona"); - command - .arg("--epiphany-agent-identity") - .arg("epiphany.Persona"); - command - .arg("--heimdall-capability-ref") - .arg(heimdall_capability_ref); - if let Some(run_id) = env::var("EPIPHANY_RUN_ID") - .ok() - .map(|value| value.trim().to_string()) - .filter(|value| !value.is_empty()) - { - command.arg("--epiphany-run-id").arg(run_id); - } - - let output = command.output().with_context(|| { - format!( - "failed to invoke Bifrost bridge CLI at {}", - bridge_cli_path.display() - ) - })?; - if !output.status.success() { - let stderr = String::from_utf8_lossy(&output.stderr); - let stdout = String::from_utf8_lossy(&output.stdout); - return Err(anyhow!( - "Bifrost bridge Reddit post failed with status {}: {}{}{}", - output.status, - stderr.trim(), - if stdout.trim().is_empty() { - "" - } else { - "\nstdout: " - }, - stdout.trim() - )); - } - let stdout = String::from_utf8(output.stdout).context("Bifrost bridge stdout was not UTF-8")?; - let receipt: Value = - serde_json::from_str(strip_bom(&stdout)).context("Bifrost bridge stdout was not JSON")?; - validate_reddit_publication_receipt( - &receipt, - config, - bifrost_identity, - heimdall_capability_ref, - subreddit, - audit, - )?; - serde_json::from_value(receipt) - .context("Bifrost bridge receipt did not match the typed Reddit publication contract") -} - -fn validate_reddit_publication_receipt( - receipt: &Value, - config: &PersonaRedditConfig, - bifrost_identity: &str, - heimdall_capability_ref: &str, - subreddit: &str, - audit: &PersonaSpeechAudit, -) -> Result<()> { - let expected_source_kind = config - .bifrost_source_kind - .as_deref() - .unwrap_or("epiphany_persona_reddit"); - let expected_authority = config - .bifrost_authority_ref - .as_deref() - .unwrap_or("epiphany.persona_speech_audit"); - let checks = [ - (receipt["action"].as_str() == Some("reddit-post"), "action"), - (receipt["ok"].as_bool() == Some(true), "successful outcome"), - ( - receipt["subreddit"].as_str() == Some(subreddit), - "subreddit binding", - ), - ( - non_empty(&receipt["thingId"]) || non_empty(&receipt["url"]), - "provider thing id or receipt URL", - ), - ( - non_empty(&receipt["crossingReceiptId"]), - "canonical crossing receipt id", - ), - ( - receipt["provenance"]["bifrostIdentity"].as_str() == Some(bifrost_identity), - "Bifrost identity", - ), - ( - receipt["provenance"]["sourceKind"].as_str() == Some(expected_source_kind), - "source kind", - ), - ( - receipt["provenance"]["sourceId"].as_str() == Some(audit.audit_id.as_str()), - "speech audit binding", - ), - ( - receipt["provenance"]["authorityReference"].as_str() == Some(expected_authority), - "authority reference", - ), - ( - receipt["provenance"]["epiphanyLaneId"].as_str() == Some("Persona"), - "lane identity", - ), - ( - receipt["provenance"]["epiphanyAgentIdentity"].as_str() == Some("epiphany.Persona"), - "agent identity", - ), - ( - receipt["provenance"]["heimdallCapabilityRef"].as_str() - == Some(heimdall_capability_ref), - "Heimdall capability", - ), - ]; - for (valid, label) in checks { - if !valid { - return Err(anyhow!( - "Bifrost Reddit publication receipt missing or mismatched {label}" - )); - } - } - Ok(()) -} - -fn non_empty(value: &Value) -> bool { - value.as_str().is_some_and(|value| !value.trim().is_empty()) -} - -fn audit_persona_speech( - title: &str, - content: &str, - artifact_dir: &Path, - cultmesh_store: &Path, - runtime_id: &str, - subreddit: Option<&str>, - public_post: bool, -) -> Result { - let combined = format!("{title}\n\n{content}"); - let opening_key = opening_key(title); - let topic_key = topic_key(&combined); - let recent = recent_persona_speech(artifact_dir, 12); - let repeated_opening_count = recent - .iter() - .filter(|speech| !opening_key.is_empty() && speech.opening_key == opening_key) - .count(); - let repeated_topic_count = recent - .iter() - .filter(|speech| !topic_key.is_empty() && speech.topic_key == topic_key) - .count(); - let same_target_post_count = subreddit - .map(|subreddit| { - recent - .iter() - .filter(|speech| speech.posted && speech.subreddit.as_deref() == Some(subreddit)) - .count() - }) - .unwrap_or(0); - let mut reasons = Vec::new(); - if public_post && repeated_opening_count >= 2 { - reasons.push("repeated-opening".to_string()); - } - if public_post && repeated_topic_count >= 3 { - reasons.push("repeated-topic".to_string()); - } - if public_post && same_target_post_count >= 3 { - reasons.push("subreddit-saturation".to_string()); - } - if title.trim().len() > 280 { - reasons.push("title-too-long-for-reddit".to_string()); - } - if content.trim().len() > 9000 { - reasons.push("content-too-long-for-public-persona".to_string()); - } - let decision = if reasons.is_empty() { - "eligible" - } else { - "blocked" - } - .to_string(); - let audit_id = format!("persona-speech-audit-{}", short_id()); - let audit_path = artifact_dir.join(format!( - "Persona-reddit-speech-audit-{}-{}.json", - now_stamp(), - audit_id - )); - let audit = PersonaSpeechAudit { - schema_version: SPEECH_AUDIT_SCHEMA_VERSION.to_string(), - audit_id, - created_at: now_iso(), - action_kind: "post".to_string(), - decision, - reasons, - content_fingerprint: content_fingerprint(&combined), - opening_key, - topic_key, - requested_public_target: subreddit.map(str::to_string), - recent_window_count: recent.len(), - repeated_opening_count, - repeated_topic_count, - same_target_post_count, - audit_path, - }; - write_json(&audit.audit_path, &serde_json::to_value(&audit)?)?; - write_epiphany_cultmesh_persona_speech_audit( - cultmesh_store, - EpiphanyCultMeshPersonaSpeechAuditEntry { - schema_version: EPIPHANY_CULTMESH_PERSONA_SPEECH_AUDIT_SCHEMA_VERSION.to_string(), - audit_id: audit.audit_id.clone(), - runtime_id: runtime_id.to_string(), - verse_id: EPIPHANY_CULTMESH_LOCAL_AREA_VERSE_ID.to_string(), - persona_agent_id: "epiphany.Persona".to_string(), - action_kind: "post".to_string(), - decision: audit.decision.clone(), - content_fingerprint: audit.content_fingerprint.clone(), - opening_key: audit.opening_key.clone(), - topic_key: audit.topic_key.clone(), - requested_channel_id: audit.requested_public_target.clone().unwrap_or_default(), - recent_window_count: audit.recent_window_count as u32, - repeated_opening_count: audit.repeated_opening_count as u32, - repeated_topic_count: audit.repeated_topic_count as u32, - same_channel_post_count: audit.same_target_post_count as u32, - reasons: audit.reasons.clone(), - artifact_ref: audit.audit_path.display().to_string(), - created_at_utc: audit.created_at.clone(), - private_state_exposed: false, - notes: vec![ - "Persona Reddit speech audit is parent-side mouth policy; it stores fingerprints and counters, not raw private thought.".to_string(), - "Reddit crossing belongs to Bifrost; Epiphany records eligibility before bridge transport.".to_string(), - ], - }, - )?; - Ok(audit) -} - -fn run_smoke() -> Result { - let temp_dir = scoped_temp_dir("epiphany-persona-reddit-smoke")?; - let cultmesh_store = temp_dir.join("persona-speech.ccmp"); - let runtime_id = "epiphany-persona-reddit-smoke"; - let fake_bridge = temp_dir.join("fake-bifrost-bridge.mjs"); - fs::write( - &fake_bridge, - r#" -const args = process.argv.slice(2); -function opt(name) { - const i = args.indexOf(name); - return i >= 0 && i + 1 < args.length ? args[i + 1] : ""; -} -console.log(JSON.stringify({ - action: args[0], - ok: true, - subreddit: opt("--subreddit"), - title: opt("--title"), - personaName: opt("--persona-name"), - personaFlairText: opt("--persona-flair-text"), - thingId: "t3_epiphanysmoke", - url: `https://reddit.com/r/${opt("--subreddit")}/comments/t3_epiphanysmoke/epiphany_smoke/`, - crossingReceiptId: "crossing_smoke-reddit-post", - provenance: { - bifrostIdentity: opt("--identity"), - sourceKind: opt("--source-kind"), - sourceId: opt("--source-id"), - authorityReference: opt("--authority-ref"), - epiphanyRunId: opt("--epiphany-run-id"), - epiphanyLaneId: opt("--epiphany-lane-id"), - epiphanyAgentIdentity: opt("--epiphany-agent-identity"), - heimdallCapabilityRef: opt("--heimdall-capability-ref") - } -})); -"#, - ) - .with_context(|| format!("failed to write {}", fake_bridge.display()))?; - let config = PersonaRedditConfig { - subreddit: "GameCultOrg".to_string(), - persona_name: Some("Smoke Persona".to_string()), - persona_flair_id: None, - persona_flair_text: Some("Smoke Persona".to_string()), - bifrost_bridge_cli_path: Some(fake_bridge), - bifrost_identity: Some("epiphany.Persona".to_string()), - bifrost_source_kind: Some("epiphany_persona_reddit".to_string()), - bifrost_authority_ref: Some("epiphany.persona_speech_audit".to_string()), - heimdall_capability_ref_env: Some("HEIMDALL_CAPABILITY_REF_TEST".to_string()), - }; - let invalid_bridge = temp_dir.join("invalid-bifrost-bridge.mjs"); - fs::write(&invalid_bridge, "console.log('{}');\n")?; - let invalid_receipt_config = PersonaRedditConfig { - bifrost_bridge_cli_path: Some(invalid_bridge), - ..config.clone() - }; - let no_bridge_config = PersonaRedditConfig { - bifrost_bridge_cli_path: None, - ..config.clone() - }; - let missing_capability_config = PersonaRedditConfig { - heimdall_capability_ref_env: Some("HEIMDALL_CAPABILITY_REF_MISSING_FOR_SMOKE".to_string()), - ..config.clone() - }; - let wrong_surface_config = PersonaRedditConfig { - heimdall_capability_ref_env: Some( - "HEIMDALL_CAPABILITY_REF_WRONG_SURFACE_FOR_SMOKE".to_string(), - ), - ..config.clone() - }; - unsafe { - env::set_var( - "HEIMDALL_CAPABILITY_REF_TEST", - "heimdall:reddit:capability:smoke-persona", - ); - env::set_var( - "HEIMDALL_CAPABILITY_REF_WRONG_SURFACE_FOR_SMOKE", - "heimdall:discord:capability:smoke-persona", - ); - env::remove_var("HEIMDALL_CAPABILITY_REF_MISSING_FOR_SMOKE"); - env::set_var("EPIPHANY_RUN_ID", "epiphany-run-reddit-smoke"); - } - let draft = run_draft( - "Epiphany smoke post", - "Persona drafts a Reddit organizing post without crossing the bridge.", - &config, - &temp_dir, - &cultmesh_store, - runtime_id, - )?; - let missing_bridge = run_post( - "Epiphany missing bridge", - "Persona should not reach Reddit without Bifrost configured.", - &no_bridge_config, - &temp_dir, - &cultmesh_store, - runtime_id, - None, - None, - None, - None, - )?; - let missing_capability = run_post( - "Epiphany missing Heimdall capability", - "Persona should not reach Reddit without Heimdall-backed capability proof.", - &missing_capability_config, - &temp_dir, - &cultmesh_store, - runtime_id, - None, - None, - None, - None, - )?; - let wrong_surface_capability = run_post( - "Epiphany wrong Heimdall capability", - "Persona should not reach Reddit with Discord Heimdall-backed capability proof.", - &wrong_surface_config, - &temp_dir, - &cultmesh_store, - runtime_id, - None, - None, - None, - None, - )?; - let bridged = run_post( - "Epiphany crosses through Bifrost", - "Persona posts to Reddit only through the Bifrost bridge.", - &config, - &temp_dir, - &cultmesh_store, - runtime_id, - None, - None, - None, - None, - )?; - let invalid_receipt_rejected = run_post( - "Epiphany rejects empty bridge JSON", - "Persona must not call arbitrary JSON a Reddit publication receipt.", - &invalid_receipt_config, - &temp_dir, - &cultmesh_store, - runtime_id, - None, - None, - None, - None, - ) - .is_err(); - let seed = audit_persona_speech( - "Repeated Reddit rite", - "Modeling and Soul keep circling the same public proof seam.", - &temp_dir, - &cultmesh_store, - runtime_id, - Some("GameCultOrg"), - false, - )?; - write_post_artifact( - "Repeated Reddit rite", - "Modeling and Soul keep circling the same public proof seam.", - &config, - &temp_dir, - PersonaRedditPostStatus::Posted, - "seed prior Reddit post for speech-audit smoke", - Some(&seed), - None, - )?; - let verified_post_count = recent_persona_speech(&temp_dir, 12) - .iter() - .filter(|speech| speech.posted) - .count(); - write_post_artifact( - "Repeated Reddit rite", - "Modeling and Soul keep circling the same public proof seam.", - &config, - &temp_dir, - PersonaRedditPostStatus::Posted, - "seed second prior Reddit post for speech-audit smoke", - Some(&seed), - None, - )?; - let repeated = run_post( - "Repeated Reddit rite", - "Modeling and Soul keep circling the same public proof seam again.", - &config, - &temp_dir, - &cultmesh_store, - runtime_id, - None, - None, - None, - None, - )?; - let latest_cultmesh_audit = - load_latest_epiphany_cultmesh_persona_speech_audit(&cultmesh_store, runtime_id)? - .context("Persona Reddit smoke expected latest CultMesh audit")?; - let draft_artifact: Value = serde_json::from_str(&fs::read_to_string( - draft["draftPath"] - .as_str() - .context("Reddit smoke draft path was missing")?, - )?)?; - let posted_artifact: Value = serde_json::from_str(&fs::read_to_string( - bridged["draftPath"] - .as_str() - .context("Reddit smoke posted path was missing")?, - )?)?; - let ok = draft["ok"] == true - && reddit_artifact_has_contract(&draft_artifact, false) - && reddit_artifact_has_contract(&posted_artifact, true) - && posted_artifact["status"] == "posted" - && posted_artifact["bifrostBridgeReceipt"]["crossingReceiptId"] - == "crossing_smoke-reddit-post" - && missing_bridge["ok"] == false - && missing_bridge["blocked"] == "missing-bifrost-bridge" - && missing_capability["ok"] == false - && missing_capability["blocked"] == "missing-heimdall-capability-ref" - && wrong_surface_capability["ok"] == false - && wrong_surface_capability["blocked"] == "wrong-heimdall-capability-surface" - && bridged["ok"] == true - && invalid_receipt_rejected - && verified_post_count == 1 - && bridged["transport"] == "bifrost.reddit-post" - && bridged["bifrostBridgeReceipt"]["provenance"]["bifrostIdentity"] == "epiphany.Persona" - && bridged["bifrostBridgeReceipt"]["provenance"]["heimdallCapabilityRef"] - == "heimdall:reddit:capability:smoke-persona" - && repeated["ok"] == false - && repeated["blocked"] == "speech-audit" - && repeated["speechAudit"]["reasons"] - .as_array() - .is_some_and(|reasons| reasons.iter().any(|reason| reason == "repeated-opening")) - && latest_cultmesh_audit.decision == "blocked" - && !latest_cultmesh_audit.private_state_exposed; - let result = serde_json::json!({ - "ok": ok, - "draft": draft, - "missingBridge": missing_bridge, - "missingCapability": missing_capability, - "wrongSurfaceCapability": wrong_surface_capability, - "bridged": bridged, - "invalidReceiptRejected": invalid_receipt_rejected, - "verifiedPostCount": verified_post_count, - "repeatedSpeech": repeated, - "latestCultMeshSpeechAudit": latest_cultmesh_audit, - "draftArtifactContractExact": reddit_artifact_has_contract(&draft_artifact, false), - "postedArtifactContractExact": reddit_artifact_has_contract(&posted_artifact, true), - }); - let _ = fs::remove_dir_all(&temp_dir); - Ok(result) -} - -fn write_post_artifact( - title: &str, - content: &str, - config: &PersonaRedditConfig, - artifact_dir: &Path, - status: PersonaRedditPostStatus, - reason: &str, - speech_audit: Option<&PersonaSpeechAudit>, - bridge_receipt: Option<&BifrostRedditPublicationReceipt>, -) -> Result { - let payload = PersonaRedditPostArtifact { - schema_version: POST_SCHEMA_VERSION.to_string(), - created_at: now_iso(), - status, - reason: reason.to_string(), - subreddit: config.subreddit.clone(), - persona_name: config.persona_name.clone(), - persona_flair_id: config.persona_flair_id.clone(), - persona_flair_text: config.persona_flair_text.clone(), - title: title.trim().to_string(), - content: content.trim().to_string(), - speech_audit: speech_audit.cloned(), - bifrost_bridge_receipt: bridge_receipt.cloned(), - }; - let path = artifact_dir.join(format!( - "Persona-reddit-{}-{}.json", - now_stamp(), - short_id() - )); - write_json(&path, &serde_json::to_value(payload)?)?; - Ok(path) -} - -fn latest_persona_artifacts(artifact_dir: &Path, limit: usize) -> Vec { - let Ok(read_dir) = fs::read_dir(artifact_dir) else { - return Vec::new(); - }; - let mut paths = read_dir - .filter_map(Result::ok) - .map(|entry| entry.path()) - .filter(|path| { - path.file_name() - .and_then(|name| name.to_str()) - .is_some_and(|name| name.starts_with("Persona-reddit-") && name.ends_with(".json")) - }) - .filter_map(|path| Some((path.metadata().ok()?.modified().ok()?, path))) - .collect::>(); - paths.sort_by_key(|item| Reverse(item.0)); - paths - .into_iter() - .take(limit) - .filter_map(|(modified, path)| { - let payload: Value = serde_json::from_str(&fs::read_to_string(&path).ok()?).ok()?; - let modified_at: chrono::DateTime = modified.into(); - Some(serde_json::json!({ - "path": path, - "name": path.file_name().and_then(|name| name.to_str()), - "modifiedAt": modified_at.to_rfc3339_opts(SecondsFormat::Secs, true), - "schemaVersion": payload.get("schemaVersion").or_else(|| payload.get("schema_version")), - "status": payload.get("status"), - "reason": payload.get("reason"), - "title": payload.get("title"), - "subreddit": payload.get("subreddit"), - })) - }) - .collect() -} - -fn recent_persona_speech(artifact_dir: &Path, limit: usize) -> Vec { - let Ok(read_dir) = fs::read_dir(artifact_dir) else { - return Vec::new(); - }; - let mut paths = read_dir - .filter_map(Result::ok) - .map(|entry| entry.path()) - .filter(|path| { - path.file_name() - .and_then(|name| name.to_str()) - .is_some_and(|name| name.starts_with("Persona-reddit-") && name.ends_with(".json")) - }) - .filter_map(|path| Some((path.metadata().ok()?.modified().ok()?, path))) - .collect::>(); - paths.sort_by_key(|item| Reverse(item.0)); - paths - .into_iter() - .take(limit) - .filter_map(|(_, path)| { - let payload: Value = serde_json::from_str(&fs::read_to_string(&path).ok()?).ok()?; - let title = payload["title"].as_str()?.to_string(); - let content = payload["content"].as_str().unwrap_or_default().to_string(); - Some(RecentPersonaSpeech { - opening_key: opening_key(&title), - topic_key: topic_key(&format!("{title}\n\n{content}")), - subreddit: payload["subreddit"].as_str().map(str::to_string), - posted: payload["status"].as_str() == Some("posted") - && has_bound_reddit_publication(&payload), - }) - }) - .collect() -} - -fn has_bound_reddit_publication(payload: &Value) -> bool { - let receipt = &payload["bifrostBridgeReceipt"]; - let subreddit = payload["subreddit"].as_str().unwrap_or_default(); - let audit_id = payload["speechAudit"]["auditId"] - .as_str() - .or_else(|| payload["speechAudit"]["audit_id"].as_str()) - .unwrap_or_default(); - receipt["action"] == "reddit-post" - && receipt["ok"] == true - && receipt["subreddit"].as_str() == Some(subreddit) - && (non_empty(&receipt["thingId"]) || non_empty(&receipt["url"])) - && non_empty(&receipt["crossingReceiptId"]) - && receipt["provenance"]["sourceId"].as_str() == Some(audit_id) -} - -fn reddit_artifact_has_contract(payload: &Value, expects_receipt: bool) -> bool { - const REQUIRED: [&str; 11] = [ - "content", - "createdAt", - "personaFlairId", - "personaFlairText", - "personaName", - "reason", - "schemaVersion", - "speechAudit", - "status", - "subreddit", - "title", - ]; - payload.as_object().is_some_and(|object| { - object.len() == REQUIRED.len() + usize::from(expects_receipt) - && REQUIRED.iter().all(|key| object.contains_key(*key)) - && object.contains_key("bifrostBridgeReceipt") == expects_receipt - && matches!( - payload["status"].as_str(), - Some("draft" | "blocked" | "posted") - ) - }) -} - -fn ensure_post(title: &str, content: &str) -> Result<()> { - if title.trim().is_empty() { - return Err(anyhow!("Persona Reddit title is empty")); - } - if content.trim().is_empty() { - return Err(anyhow!("Persona Reddit content is empty")); - } - Ok(()) -} - -fn read_required_text(value: Option, label: &str) -> Result { - let Some(value) = value else { - let mut text = String::new(); - std::io::stdin() - .read_to_string(&mut text) - .context("failed to read stdin")?; - ensure_nonempty(&text, label)?; - return Ok(text); - }; - let path = PathBuf::from(&value); - let text = if path.exists() { - fs::read_to_string(&path).with_context(|| format!("failed to read {}", path.display()))? - } else { - value - }; - ensure_nonempty(&text, label)?; - Ok(text) -} - -fn ensure_nonempty(value: &str, label: &str) -> Result<()> { - if value.trim().is_empty() { - return Err(anyhow!("{label} is empty")); - } - Ok(()) -} - -fn load_config(path: &Path) -> Result { - let raw = - fs::read_to_string(path).with_context(|| format!("failed to read {}", path.display()))?; - if path - .extension() - .is_some_and(|extension| extension == "json") - { - let payload: Value = serde_json::from_str(&raw) - .with_context(|| format!("failed to decode {}", path.display()))?; - return Ok(PersonaRedditConfig { - subreddit: payload["subreddit"] - .as_str() - .map(clean_subreddit) - .unwrap_or_else(|| "GameCultOrg".to_string()), - persona_name: payload["persona_name"].as_str().map(str::to_string), - persona_flair_id: payload["persona_flair_id"].as_str().map(str::to_string), - persona_flair_text: payload["persona_flair_text"].as_str().map(str::to_string), - bifrost_bridge_cli_path: payload["bifrost_bridge_cli_path"] - .as_str() - .map(PathBuf::from), - bifrost_identity: payload["bifrost_identity"].as_str().map(str::to_string), - bifrost_source_kind: payload["bifrost_source_kind"].as_str().map(str::to_string), - bifrost_authority_ref: payload["bifrost_authority_ref"] - .as_str() - .map(str::to_string), - heimdall_capability_ref_env: payload["heimdall_capability_ref_env"] - .as_str() - .map(str::to_string), - }); - } - Ok(PersonaRedditConfig { - subreddit: toml_string(&raw, "subreddit") - .map(|value| clean_subreddit(value)) - .unwrap_or_else(|| "GameCultOrg".to_string()), - persona_name: toml_string(&raw, "persona_name"), - persona_flair_id: toml_string(&raw, "persona_flair_id"), - persona_flair_text: toml_string(&raw, "persona_flair_text"), - bifrost_bridge_cli_path: toml_string(&raw, "bifrost_bridge_cli_path").map(PathBuf::from), - bifrost_identity: toml_string(&raw, "bifrost_identity"), - bifrost_source_kind: toml_string(&raw, "bifrost_source_kind"), - bifrost_authority_ref: toml_string(&raw, "bifrost_authority_ref"), - heimdall_capability_ref_env: toml_string(&raw, "heimdall_capability_ref_env"), - }) -} - -fn bifrost_bridge_cli_path(config: &PersonaRedditConfig) -> Option { - config.bifrost_bridge_cli_path.clone().or_else(|| { - env::var("EPIPHANY_PERSONA_BIFROST_BRIDGE_CLI") - .ok() - .map(|value| value.trim().to_string()) - .filter(|value| !value.is_empty()) - .map(PathBuf::from) - }) -} - -fn required_bifrost_identity(config: &PersonaRedditConfig) -> Option { - config - .bifrost_identity - .clone() - .map(|value| value.trim().to_string()) - .filter(|value| !value.is_empty()) - .or_else(|| { - env::var("BIFROST_IDENTITY") - .ok() - .map(|value| value.trim().to_string()) - .filter(|value| !value.is_empty()) - }) -} - -fn required_heimdall_capability_ref(config: &PersonaRedditConfig) -> Option { - config - .heimdall_capability_ref_env - .as_ref() - .and_then(env_value) - .or_else(|| { - env::var("HEIMDALL_CAPABILITY_REF") - .ok() - .map(|value| value.trim().to_string()) - .filter(|value| !value.is_empty()) - }) - .or_else(|| { - env::var("BIFROST_HEIMDALL_CAPABILITY_REF") - .ok() - .map(|value| value.trim().to_string()) - .filter(|value| !value.is_empty()) - }) -} - -fn is_heimdall_surface_reference(reference: &str, surface: &str) -> bool { - let reference = reference.trim().to_ascii_lowercase(); - reference.starts_with("heimdall:") && reference.contains(&format!(":{surface}:")) -} - -fn bifrost_node_executable() -> String { - env::var("EPIPHANY_BIFROST_NODE") - .ok() - .map(|value| value.trim().to_string()) - .filter(|value| !value.is_empty()) - .unwrap_or_else(|| "node".to_string()) -} - -fn clean_subreddit(value: impl AsRef) -> String { - value - .as_ref() - .trim() - .trim_start_matches("r/") - .trim_matches('/') - .chars() - .filter(|ch| ch.is_ascii_alphanumeric() || *ch == '_' || *ch == '-') - .collect() -} - -fn env_value(name: &String) -> Option { - env::var(name) - .ok() - .map(|value| value.trim().to_string()) - .filter(|value| !value.is_empty()) -} - -fn toml_string(raw: &str, key: &str) -> Option { - raw.lines() - .map(str::trim) - .filter(|line| !line.starts_with('#')) - .find_map(|line| { - let (left, right) = line.split_once('=')?; - (left.trim() == key).then(|| parse_quoted(right.trim()))? - }) -} - -fn parse_quoted(value: &str) -> Option { - let value = value.trim(); - value - .strip_prefix('"') - .and_then(|value| value.strip_suffix('"')) - .map(str::to_string) -} - -fn opening_key(content: &str) -> String { - content - .split_whitespace() - .take(6) - .map(normalize_token) - .filter(|token| !token.is_empty()) - .collect::>() - .join(" ") -} - -fn topic_key(content: &str) -> String { - let mut tokens = content - .split_whitespace() - .map(normalize_token) - .filter(|token| token.len() >= 4 && !persona_stopword(token)) - .collect::>(); - tokens.sort(); - tokens.dedup(); - tokens.into_iter().take(8).collect::>().join("|") -} - -fn content_fingerprint(content: &str) -> String { - topic_key(content) - + "::" - + &content - .split_whitespace() - .map(normalize_token) - .filter(|token| !token.is_empty()) - .take(24) - .collect::>() - .join("-") -} - -fn normalize_token(value: &str) -> String { - value - .trim_matches(|ch: char| !ch.is_ascii_alphanumeric()) - .chars() - .filter(|ch| ch.is_ascii_alphanumeric() || *ch == '-' || *ch == '_') - .collect::() - .to_ascii_lowercase() -} - -fn persona_stopword(value: &str) -> bool { - matches!( - value, - "about" - | "after" - | "again" - | "because" - | "before" - | "being" - | "should" - | "there" - | "these" - | "thing" - | "through" - | "under" - | "without" - | "would" - ) -} - -fn write_json(path: &Path, value: &Value) -> Result<()> { - if let Some(parent) = path.parent() { - fs::create_dir_all(parent) - .with_context(|| format!("failed to create {}", parent.display()))?; - } - fs::write(path, format!("{}\n", serde_json::to_string_pretty(value)?)) - .with_context(|| format!("failed to write {}", path.display())) -} - -fn strip_bom(value: &str) -> &str { - value.strip_prefix('\u{feff}').unwrap_or(value) -} - -fn now_iso() -> String { - chrono::Utc::now() - .to_rfc3339_opts(SecondsFormat::Secs, true) - .replace('Z', "+00:00") -} - -fn now_stamp() -> String { - chrono::Utc::now().format("%Y%m%dT%H%M%SZ").to_string() -} - -fn short_id() -> String { - Uuid::new_v4().simple().to_string()[..8].to_string() -} - -fn scoped_temp_dir(prefix: &str) -> Result { - let path = env::temp_dir().join(format!("{prefix}-{}", Uuid::new_v4())); - fs::create_dir(&path).with_context(|| format!("failed to claim {}", path.display()))?; - Ok(path) -} - -fn next_path(args: &mut impl Iterator, name: &str) -> Result { - Ok(PathBuf::from(next_value(args, name)?)) -} - -fn next_value(args: &mut impl Iterator, name: &str) -> Result { - args.next() - .ok_or_else(|| anyhow!("{name} requires a value")) -} - -fn usage() -> Result<()> { - Err(anyhow!( - "usage: epiphany-persona-reddit [--config ] [--artifact-dir ] [--title ] [--content ]" - )) -} diff --git a/epiphany-core/src/bin/epiphany-public-source-gate-probe.rs b/epiphany-core/src/bin/epiphany-public-source-gate-probe.rs deleted file mode 100644 index f35e59ec..00000000 --- a/epiphany-core/src/bin/epiphany-public-source-gate-probe.rs +++ /dev/null @@ -1,320 +0,0 @@ -use anyhow::{Context, Result, bail}; -use epiphany_core::*; -use epiphany_model_adapter::EpiphanyModelRequest; -use epiphany_tool_adapter::{ - EPIPHANY_TOOL_RUNTIME_ADAPTER_ID, EpiphanyToolInvocationIntent, EpiphanyToolInvocationReceipt, - tool_invocation_intent_key, tool_invocation_receipt_key, -}; -use serde_json::json; -use sha2::{Digest, Sha256}; -use std::env; -use std::fs; -use std::path::{Path, PathBuf}; - -const WORKER_JOB_ID: &str = "public-source-no-grant-worker"; -const MODEL_REQUEST_ID: &str = "public-source-no-grant-model-request"; -const MODEL_JOB_ID: &str = "public-source-no-grant-model-job"; -const MODEL_SESSION_ID: &str = "public-source-no-grant-model-session"; -const HOSTILE_INTENT_ID: &str = "public-source-no-grant-intent"; - -fn main() -> Result<()> { - let (command, fixture_root) = parse_command()?; - let proof = match command.as_str() { - "admission-no-grant" => { - let store = create_fixture_root(&fixture_root)?; - prove_admission_no_grant_refusal(&store)? - } - "prepare-execution-no-grant" => { - let store = create_fixture_root(&fixture_root)?; - prepare_execution_no_grant(&fixture_root, &store)? - } - "verify-execution-no-grant" => verify_execution_no_grant(&fixture_root)?, - _ => bail!(usage()), - }; - println!("{}", serde_json::to_string_pretty(&proof)?); - Ok(()) -} - -fn parse_command() -> Result<(String, PathBuf)> { - let mut args = env::args().skip(1); - let command = args.next().context("missing command")?; - if args.next().as_deref() != Some("--fixture-root") { - bail!(usage()); - } - let root = args.next().context("missing --fixture-root value")?; - if args.next().is_some() { - bail!("unexpected trailing arguments"); - } - let root = PathBuf::from(root); - if !root.is_absolute() { - bail!("--fixture-root must be absolute"); - } - Ok((command, root)) -} - -fn usage() -> &'static str { - "usage: epiphany-public-source-gate-probe --fixture-root PATH" -} - -fn create_fixture_root(fixture_root: &Path) -> Result { - if fixture_root.exists() { - bail!( - "public-source gate probe refuses existing fixture root {}", - fixture_root.display() - ); - } - fs::create_dir_all(fixture_root) - .with_context(|| format!("creating fixture root {}", fixture_root.display()))?; - Ok(fixture_root.join("runtime.cc")) -} - -fn prepare_authority_family(store: &Path) -> Result { - initialize_runtime_spine( - store, - RuntimeSpineInitOptions { - runtime_id: "public-source-no-grant-probe".into(), - display_name: "Public source no-grant probe".into(), - created_at: "2026-08-13T00:00:00Z".into(), - }, - )?; - let launch_document = EpiphanyWorkerLaunchDocument::Role(EpiphanyRoleWorkerLaunchDocument { - thread_id: "public-source-no-grant-thread".into(), - role_id: "research".into(), - objective: Some("Prove the exact public-source grant boundary.".into()), - dynamic_prompt_context: None, - repository_body_observation_basis: None, - proposal_modeling_context: None, - frontier_verdict_modeling_context: None, - frontier_planning_context: None, - frontier_research_context: None, - frontier_verification_context: None, - frontier_plan_mind_context: None, - imagination_consideration_context: None, - admitted_model_direction_consideration_context: None, - active_subgoal_id: None, - active_subgoals: Vec::new(), - active_graph_node_ids: Vec::new(), - investigation_checkpoint: None, - scratch: None, - invariants: Vec::new(), - graphs: None, - recent_evidence: Vec::new(), - recent_observations: Vec::new(), - graph_frontier: None, - graph_checkpoint: None, - planning: None, - churn: None, - }); - let authority_scope = "epiphany.eyes.public_source_gate_probe"; - open_runtime_spine_heartbeat_job( - store, - RuntimeSpineHeartbeatJobOptions { - runtime_id: "public-source-no-grant-probe".into(), - display_name: "Public source no-grant probe".into(), - session_id: EPIPHANY_RUNTIME_ROOT_SESSION_ID.into(), - objective: "Prove the exact public-source grant boundary.".into(), - coordinator_note: "isolated gate probe".into(), - job_id: WORKER_JOB_ID.into(), - role: EPIPHANY_RESEARCH_OWNER_ROLE.into(), - binding_id: EPIPHANY_RESEARCH_ROLE_BINDING_ID.into(), - authority_scope: authority_scope.into(), - instruction: "Attempt one immutable public source lookup.".into(), - output_contract_id: ROLE_WORKER_OUTPUT_CONTRACT_ID.into(), - organ_launch_contract: default_launch_organ_contract( - authority_scope, - "role", - ROLE_WORKER_OUTPUT_CONTRACT_ID, - ), - launch_document, - proposal_modeling_request_id: None, - frontier_planning_request_id: None, - frontier_plan_mind_request_id: None, - imagination_consideration_request_id: None, - admitted_model_direction_consideration_request_id: None, - repo_frontier_modeling_request_id: None, - repo_frontier_research_request_id: None, - repo_frontier_verification_request_id: None, - created_at: "2026-08-13T00:00:01Z".into(), - }, - )?; - runtime_spine_cache(store)?.put( - &format!("substrate-grant-{WORKER_JOB_ID}"), - &substrate_gate_repo_access_grant_for_worker( - format!("substrate-grant-{WORKER_JOB_ID}"), - WORKER_JOB_ID.into(), - EPIPHANY_RESEARCH_ROLE_BINDING_ID.into(), - EPIPHANY_RESEARCH_OWNER_ROLE.into(), - authority_scope.into(), - true, - "2026-08-13T00:00:01Z".into(), - ), - )?; - - let mut model_request = EpiphanyModelRequest::new( - MODEL_REQUEST_ID, - "public-source-no-grant-conversation", - "openai-codex", - "gpt-probe", - "Attempt one immutable public source read.", - ); - model_request.source_worker_job_id = Some(WORKER_JOB_ID.into()); - open_runtime_model_execution( - store, - RuntimeSpineSessionOptions { - session_id: MODEL_SESSION_ID.into(), - objective: "Prove refusal before public-source execution.".into(), - created_at: "2026-08-13T00:00:02Z".into(), - coordinator_note: "isolated packaged gate probe".into(), - }, - RuntimeSpineJobOptions { - job_id: MODEL_JOB_ID.into(), - session_id: MODEL_SESSION_ID.into(), - role: "openai-model-adapter".into(), - created_at: "2026-08-13T00:00:02Z".into(), - summary: "Bound hostile public-source model turn.".into(), - artifact_refs: Vec::new(), - }, - &model_request, - "2026-08-13T00:00:02Z", - )?; - - Ok(EpiphanyToolInvocationIntent::new( - HOSTILE_INTENT_ID, - EPIPHANY_TOOL_RUNTIME_ADAPTER_ID, - "epiphany_public", - "github_file", - r#"{"owner":"GameCult","repository":"Epiphany","revision":"0123456789abcdef0123456789abcdef01234567","path":"README.md"}"#, - "gate-probe", - "Prove missing publicSourceRead refuses before adapter execution.", - "2026-08-13T00:00:03Z", - ) - .with_model_call("public-source-no-grant-call", MODEL_REQUEST_ID)) -} - -fn remove_public_source_grant(store: &Path) -> Result<()> { - let grant_id = format!("substrate-grant-{WORKER_JOB_ID}"); - let mut cache = runtime_spine_cache(store)?; - cache.pull_all_backing_stores()?; - let mut grant = cache - .get::(&grant_id)? - .context("Research launch omitted its exact Substrate Gate grant")?; - grant - .granted_operations - .retain(|operation| operation != "publicSourceRead"); - cache.put(&grant_id, &grant)?; - Ok(()) -} - -fn prove_admission_no_grant_refusal(store: &Path) -> Result { - let hostile = prepare_authority_family(store)?; - remove_public_source_grant(store)?; - let before = fs::read(store)?; - let error = put_runtime_tool_execution_intent( - store, - MODEL_SESSION_ID, - MODEL_JOB_ID, - &hostile, - "2026-08-13T00:00:03Z", - ) - .expect_err("hostile public-source intent unexpectedly acquired authority"); - let after = fs::read(store)?; - if before != after { - bail!("no-grant refusal changed the canonical runtime store"); - } - - let mut reloaded = runtime_spine_cache(store)?; - reloaded.pull_all_backing_stores()?; - let binding_absent = reloaded - .get::(HOSTILE_INTENT_ID)? - .is_none(); - let intent_absent = reloaded - .get::(&tool_invocation_intent_key(HOSTILE_INTENT_ID))? - .is_none(); - let receipt_absent = reloaded - .get::(&tool_invocation_receipt_key(HOSTILE_INTENT_ID))? - .is_none(); - if !(binding_absent && intent_absent && receipt_absent) { - bail!("no-grant refusal persisted forbidden execution authority"); - } - - Ok(json!({ - "schemaVersion": "epiphany.public_source_admission_no_grant_gate_proof.v0", - "status": "passed", - "fixtureStore": store, - "requiredOperation": "publicSourceRead", - "admissionOwner": "put_runtime_tool_execution_intent", - "refusedBeforeAdapterExecution": true, - "storeByteIdentical": true, - "bindingAbsent": binding_absent, - "intentAbsent": intent_absent, - "receiptAbsent": receipt_absent, - "error": format!("{error:#}"), - "privateStateExposed": false - })) -} - -fn prepare_execution_no_grant(fixture_root: &Path, store: &Path) -> Result { - let hostile = prepare_authority_family(store)?; - put_runtime_tool_execution_intent( - store, - MODEL_SESSION_ID, - MODEL_JOB_ID, - &hostile, - "2026-08-13T00:00:03Z", - )?; - remove_public_source_grant(store)?; - let digest = sha256(&fs::read(store)?); - fs::write(fixture_root.join("before.sha256"), format!("{digest}\n"))?; - Ok(json!({ - "schemaVersion": "epiphany.public_source_execution_no_grant_gate_fixture.v0", - "status": "prepared", - "fixtureStore": store, - "intentId": HOSTILE_INTENT_ID, - "requiredOperation": "publicSourceRead", - "storeSha256": digest, - "expectedPackagedCommand": ["epiphany-tool-mcp-runtime", "run", "--store", store, "--intent-id", HOSTILE_INTENT_ID], - "privateStateExposed": false - })) -} - -fn verify_execution_no_grant(fixture_root: &Path) -> Result { - let store = fixture_root.join("runtime.cc"); - let expected_digest = fs::read_to_string(fixture_root.join("before.sha256"))? - .trim() - .to_string(); - let actual_digest = sha256(&fs::read(&store)?); - if actual_digest != expected_digest { - bail!("packaged no-grant refusal changed the canonical runtime store"); - } - let mut cache = runtime_spine_cache(&store)?; - cache.pull_all_backing_stores()?; - let binding_present = cache - .get::(HOSTILE_INTENT_ID)? - .is_some(); - let intent_present = cache - .get::(&tool_invocation_intent_key(HOSTILE_INTENT_ID))? - .is_some(); - let receipt_absent = cache - .get::(&tool_invocation_receipt_key(HOSTILE_INTENT_ID))? - .is_none(); - if !(binding_present && intent_present && receipt_absent) { - bail!("packaged no-grant refusal left an invalid execution family"); - } - Ok(json!({ - "schemaVersion": "epiphany.public_source_execution_no_grant_gate_proof.v0", - "status": "passed", - "fixtureStore": store, - "intentId": HOSTILE_INTENT_ID, - "executionOwner": "require_runtime_tool_execution_binding", - "storeByteIdentical": true, - "bindingPresent": binding_present, - "intentPresent": intent_present, - "receiptAbsent": receipt_absent, - "refusedBeforeAdapterExecution": true, - "privateStateExposed": false - })) -} - -fn sha256(bytes: &[u8]) -> String { - format!("{:x}", Sha256::digest(bytes)) -} diff --git a/epiphany-core/src/bin/epiphany-rider-bridge-smoke.rs b/epiphany-core/src/bin/epiphany-rider-bridge-smoke.rs deleted file mode 100644 index 5e662fd1..00000000 --- a/epiphany-core/src/bin/epiphany-rider-bridge-smoke.rs +++ /dev/null @@ -1,253 +0,0 @@ -use anyhow::Context; -use anyhow::Result; -use anyhow::anyhow; -use serde_json::Value; -use serde_json::json; -use std::env; -use std::fs; -use std::path::{Path, PathBuf}; -use std::process::Command; - -fn main() -> Result<()> { - let result = run_smoke()?; - println!("{}", serde_json::to_string_pretty(&result)?); - Ok(()) -} - -fn run_smoke() -> Result { - let root = env::current_dir().context("failed to resolve current dir")?; - let workspace = root.join(".epiphany-smoke").join("rider-bridge-workspace"); - let artifact_root = root.join(".epiphany-smoke").join("rider-bridge-artifacts"); - let fake_rider_root = root.join(".epiphany-smoke").join("rider-bridge-install"); - let result_path = root - .join(".epiphany-smoke") - .join("rider-bridge-smoke-result.json"); - let source = prepare_workspace(&workspace)?; - reset_path(&artifact_root)?; - reset_path(&fake_rider_root)?; - let fake_rider = create_fake_rider(&fake_rider_root)?; - - let status = run_bridge(&root, &workspace, &artifact_root, &fake_rider, &["status"])?; - require( - status["status"] == "discovered", - "fake Rider path should be reported as discovered", - )?; - require( - status["riderPath"] == fake_rider.to_string_lossy().as_ref(), - "status should resolve fake Rider path", - )?; - require( - status - .get("solutionPath") - .and_then(Value::as_str) - .is_some_and(|path| path.ends_with("Aetheria.sln")), - "status should find the workspace solution", - )?; - require_artifact(&status, "rider-bridge-summary.json")?; - require_artifact(&status, "rider-bridge-status.md")?; - - let context = run_bridge( - &root, - &workspace, - &artifact_root, - &fake_rider, - &[ - "context", - "--file", - source.to_str().unwrap_or_default(), - "--selection-start", - "3", - "--selection-end", - "5", - "--symbol-name", - "GravityTileRenderer", - "--symbol-kind", - "class", - "--symbol-namespace", - "Aetheria.Rendering", - ], - )?; - require( - context["status"] == "captured", - "context command should capture a packet", - )?; - require( - context["filePath"] == "Assets/Scripts/GravityTileRenderer.cs", - "context file should be project-relative", - )?; - require_artifact(&context, "rider-context.json")?; - require_artifact(&context, "rider-context.md")?; - - let open_ref = run_bridge( - &root, - &workspace, - &artifact_root, - &fake_rider, - &[ - "open-ref", - "--file", - source.to_str().unwrap_or_default(), - "--line", - "3", - ], - )?; - require( - open_ref["status"] == "planned", - "open-ref should plan without launch", - )?; - require( - open_ref - .get("command") - .and_then(Value::as_array) - .and_then(|items| items.first()) - .and_then(Value::as_str) - == Some(fake_rider.to_str().unwrap_or_default()), - "open-ref should use discovered Rider path", - )?; - require_artifact(&open_ref, "rider-open-ref.json")?; - - let result = json!({ - "workspace": workspace, - "artifactRoot": artifact_root, - "status": status["status"], - "contextStatus": context["status"], - "openRefStatus": open_ref["status"], - "riderPath": status["riderPath"], - }); - write_json(&result_path, &result)?; - Ok(result) -} - -fn run_bridge( - root: &Path, - workspace: &Path, - artifact_root: &Path, - rider_path: &Path, - args: &[&str], -) -> Result { - let bridge = native_bridge_exe()?; - ensure_bridge_built(root, "epiphany-rider-bridge", &bridge)?; - let output = Command::new(bridge) - .current_dir(root) - .env("EPIPHANY_RIDER_PATH", rider_path) - .arg(args[0]) - .arg("--project-root") - .arg(workspace) - .arg("--artifact-root") - .arg(artifact_root) - .args(&args[1..]) - .output() - .context("failed to run native rider bridge")?; - require( - output.status.success(), - &format!( - "rider bridge failed: {}", - String::from_utf8_lossy(&output.stderr) - ), - )?; - serde_json::from_slice(&output.stdout).with_context(|| { - format!( - "rider bridge did not return JSON: {}", - String::from_utf8_lossy(&output.stdout) - ) - }) -} - -fn prepare_workspace(workspace: &Path) -> Result { - reset_path(workspace)?; - fs::write( - workspace.join("Aetheria.sln"), - "Microsoft Visual Studio Solution File\n", - )?; - let source = workspace - .join("Assets") - .join("Scripts") - .join("GravityTileRenderer.cs"); - fs::create_dir_all(source.parent().expect("source path has parent"))?; - fs::write( - &source, - "namespace Aetheria.Rendering\n{\n public sealed class GravityTileRenderer\n {\n }\n}\n", - )?; - Ok(source) -} - -fn create_fake_rider(root: &Path) -> Result { - let rider = root - .join("JetBrains Rider 2026.1.0.1") - .join("bin") - .join("rider64.exe"); - fs::create_dir_all(rider.parent().expect("rider path has parent"))?; - fs::write(&rider, "fake rider binary for smoke planning only\n")?; - Ok(rider) -} - -fn require_artifact(summary: &Value, name: &str) -> Result<()> { - let artifact_path = summary - .get("artifactPath") - .and_then(Value::as_str) - .map(PathBuf::from) - .ok_or_else(|| anyhow!("missing artifactPath"))?; - require( - artifact_path.join(name).exists(), - &format!("missing rider artifact: {name}"), - ) -} - -fn native_bridge_exe() -> Result { - let target_dir = env::var_os("CARGO_TARGET_DIR") - .unwrap_or_else(|| r"C:\Users\Meta\.cargo-target-codex".into()); - Ok(PathBuf::from(target_dir) - .join("debug") - .join(format!("epiphany-rider-bridge{}", env::consts::EXE_SUFFIX))) -} - -fn ensure_bridge_built(root: &Path, bin: &str, exe: &Path) -> Result<()> { - if exe.exists() { - return Ok(()); - } - let status = Command::new("cargo") - .current_dir(root) - .arg("build") - .arg("--manifest-path") - .arg(root.join("epiphany-core").join("Cargo.toml")) - .arg("--bin") - .arg(bin) - .status() - .with_context(|| format!("failed to build native {bin}"))?; - require( - status.success() && exe.exists(), - &format!("native {bin} executable was not built at {}", exe.display()), - ) -} - -fn reset_path(path: &Path) -> Result<()> { - if path.exists() { - let root = env::current_dir()?.join(".epiphany-smoke").canonicalize()?; - let resolved = path.canonicalize()?; - if resolved == root || !resolved.starts_with(&root) { - return Err(anyhow!( - "refusing to reset non-smoke path: {}", - path.display() - )); - } - fs::remove_dir_all(path)?; - } - fs::create_dir_all(path)?; - Ok(()) -} - -fn write_json(path: &Path, value: &Value) -> Result<()> { - if let Some(parent) = path.parent() { - fs::create_dir_all(parent)?; - } - fs::write(path, format!("{}\n", serde_json::to_string_pretty(value)?))?; - Ok(()) -} - -fn require(condition: bool, message: &str) -> Result<()> { - if condition { - Ok(()) - } else { - Err(anyhow!("{message}")) - } -} diff --git a/epiphany-core/src/bin/epiphany-rider-bridge.rs b/epiphany-core/src/bin/epiphany-rider-bridge.rs deleted file mode 100644 index a0e84155..00000000 --- a/epiphany-core/src/bin/epiphany-rider-bridge.rs +++ /dev/null @@ -1,656 +0,0 @@ -use anyhow::Context; -use anyhow::Result; -use anyhow::anyhow; -use serde_json::json; -use std::collections::HashSet; -use std::env; -use std::fs; -use std::path::{Path, PathBuf}; -use std::process::Command; -use std::time::{SystemTime, UNIX_EPOCH}; - -const RIDER_EXECUTABLE_NAMES: &[&str] = &["rider64.exe", "rider.exe", "rider.bat", "rider"]; - -fn main() -> Result<()> { - let mut args = env::args().skip(1); - let Some(command) = args.next() else { - usage()?; - return Ok(()); - }; - match run_command(&command, args.collect()) { - Ok((code, value)) => { - println!("{}", serde_json::to_string_pretty(&value)?); - std::process::exit(code); - } - Err(error) => { - println!( - "{}", - serde_json::to_string_pretty(&json!({ - "status": "error", - "error": error.to_string(), - }))? - ); - std::process::exit(1); - } - } -} - -fn run_command(command: &str, args: Vec) -> Result<(i32, serde_json::Value)> { - match command { - "status" => { - let options = RiderOptions::parse(args)?; - Ok((0, status_rider(&options)?)) - } - "context" => { - let options = RiderOptions::parse(args)?; - Ok((0, context_rider(&options)?)) - } - "open-ref" => { - let options = RiderOptions::parse(args)?; - Ok((0, open_ref_rider(&options)?)) - } - "guidance" => { - let options = RiderOptions::parse(args)?; - Ok(( - 0, - json!({ "guidance": bridge_guidance(&options.project_root)? }), - )) - } - _ => usage(), - } -} - -fn usage() -> Result<(i32, serde_json::Value)> { - Err(anyhow!( - "usage: epiphany-rider-bridge --project-root " - )) -} - -#[derive(Debug)] -struct RiderOptions { - project_root: PathBuf, - artifact_root: PathBuf, - artifact_dir: Option, - packet: Option, - file: Option, - line: Option, - column: Option, - selection_start: Option, - selection_end: Option, - symbol_name: Option, - symbol_kind: Option, - symbol_namespace: Option, - launch: bool, -} - -impl RiderOptions { - fn parse(args: Vec) -> Result { - let root = repo_root()?; - let mut options = Self { - project_root: env::current_dir()?, - artifact_root: root.join(".epiphany-gui").join("rider"), - artifact_dir: None, - packet: None, - file: None, - line: None, - column: None, - selection_start: None, - selection_end: None, - symbol_name: None, - symbol_kind: None, - symbol_namespace: None, - launch: false, - }; - let mut iter = args.into_iter(); - while let Some(arg) = iter.next() { - match arg.as_str() { - "--project-root" => options.project_root = next_path(&mut iter, &arg)?, - "--artifact-root" => options.artifact_root = next_path(&mut iter, &arg)?, - "--artifact-dir" => options.artifact_dir = Some(next_path(&mut iter, &arg)?), - "--packet" => options.packet = Some(next_path(&mut iter, &arg)?), - "--file" => options.file = Some(next_path(&mut iter, &arg)?), - "--line" => options.line = Some(next_value(&mut iter, &arg)?.parse()?), - "--column" => options.column = Some(next_value(&mut iter, &arg)?.parse()?), - "--selection-start" => { - options.selection_start = Some(next_value(&mut iter, &arg)?.parse()?) - } - "--selection-end" => { - options.selection_end = Some(next_value(&mut iter, &arg)?.parse()?) - } - "--symbol-name" => options.symbol_name = Some(next_value(&mut iter, &arg)?), - "--symbol-kind" => options.symbol_kind = Some(next_value(&mut iter, &arg)?), - "--symbol-namespace" => { - options.symbol_namespace = Some(next_value(&mut iter, &arg)?) - } - "--launch" => options.launch = true, - _ => return Err(anyhow!("unknown argument {arg:?}")), - } - } - Ok(options) - } -} - -fn next_value(iter: &mut impl Iterator, name: &str) -> Result { - iter.next() - .ok_or_else(|| anyhow!("{name} requires a value")) -} - -fn next_path(iter: &mut impl Iterator, name: &str) -> Result { - Ok(PathBuf::from(next_value(iter, name)?)) -} - -fn repo_root() -> Result { - Ok(Path::new(env!("CARGO_MANIFEST_DIR")) - .parent() - .ok_or_else(|| anyhow!("epiphany-core has no parent repo root"))? - .to_path_buf()) -} - -fn now_iso() -> String { - chrono::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true) -} - -fn artifact_dir(root: &Path, action: &str) -> Result { - let nanos = SystemTime::now().duration_since(UNIX_EPOCH)?.as_nanos(); - Ok(root - .canonicalize() - .unwrap_or_else(|_| root.to_path_buf()) - .join(format!("rider-{action}-{nanos}-{}", std::process::id()))) -} - -fn write_json(path: &Path, value: &serde_json::Value) -> Result<()> { - if let Some(parent) = path.parent() { - fs::create_dir_all(parent)?; - } - fs::write(path, format!("{}\n", serde_json::to_string_pretty(value)?)) - .with_context(|| format!("failed to write {}", path.display())) -} - -fn write_text(path: &Path, value: &str) -> Result<()> { - if let Some(parent) = path.parent() { - fs::create_dir_all(parent)?; - } - fs::write(path, value).with_context(|| format!("failed to write {}", path.display())) -} - -fn maybe_resolve(path: &Path) -> String { - clean_windows_path( - &path - .canonicalize() - .unwrap_or_else(|_| path.to_path_buf()) - .display() - .to_string(), - ) -} - -fn clean_windows_path(value: &str) -> String { - value.strip_prefix(r"\\?\").unwrap_or(value).to_string() -} - -fn is_inside(path: &Path, root: &Path) -> bool { - let Ok(path) = path.canonicalize() else { - return false; - }; - let Ok(root) = root.canonicalize() else { - return false; - }; - path.starts_with(root) -} - -fn relative_or_absolute(path: &Path, root: &Path) -> String { - match (path.canonicalize(), root.canonicalize()) { - (Ok(path), Ok(root)) => path - .strip_prefix(root) - .map(|value| value.to_string_lossy().replace('\\', "/")) - .unwrap_or_else(|_| path.display().to_string()), - _ => path.display().to_string(), - } -} - -fn dedupe_paths(paths: Vec) -> Vec { - let mut seen = HashSet::new(); - let mut result = Vec::new(); - for path in paths { - let key = maybe_resolve(&path).to_lowercase(); - if seen.insert(key) { - result.push(path); - } - } - result -} - -fn rider_search_roots() -> Vec { - let mut roots = Vec::new(); - if let Ok(value) = env::var("EPIPHANY_RIDER_ROOTS") { - roots.extend(env::split_paths(&value)); - } - for (variable, fallback) in [ - ("ProgramFiles", r"C:\Program Files".to_string()), - ("ProgramFiles(x86)", r"C:\Program Files (x86)".to_string()), - ( - "LOCALAPPDATA", - dirs_home() - .join("AppData") - .join("Local") - .display() - .to_string(), - ), - ] { - let base = env::var(variable).unwrap_or(fallback); - roots.push(PathBuf::from(base).join("JetBrains")); - } - dedupe_paths(roots) -} - -fn dirs_home() -> PathBuf { - env::var_os("USERPROFILE") - .map(PathBuf::from) - .or_else(|| env::var_os("HOME").map(PathBuf::from)) - .unwrap_or_else(|| PathBuf::from(".")) -} - -fn discover_rider_installations() -> Result> { - let mut candidates = Vec::new(); - if let Ok(value) = env::var("EPIPHANY_RIDER_PATH") { - candidates.push(PathBuf::from(value)); - } - if let Some(path_var) = env::var_os("PATH") { - for dir in env::split_paths(&path_var) { - for name in RIDER_EXECUTABLE_NAMES { - candidates.push(dir.join(name)); - } - } - } - for root in rider_search_roots() { - if !root.exists() { - continue; - } - for entry in fs::read_dir(root)? { - let entry = entry?; - let path = entry.path(); - let Some(name) = path.file_name().and_then(|value| value.to_str()) else { - continue; - }; - if !path.is_dir() || !name.contains("Rider") { - continue; - } - for exe in RIDER_EXECUTABLE_NAMES { - candidates.push(path.join("bin").join(exe)); - } - } - } - let mut installations = Vec::new(); - for path in dedupe_paths(candidates) { - if !path.exists() { - continue; - } - let version_hint = path - .components() - .rev() - .filter_map(|part| part.as_os_str().to_str()) - .find(|part| part.contains("Rider")) - .map(ToOwned::to_owned); - installations.push(json!({ - "path": maybe_resolve(&path), - "exists": true, - "versionHint": version_hint, - })); - } - Ok(installations) -} - -fn discover_solution(project_root: &Path) -> Result { - let root = project_root.canonicalize()?; - let mut top_level = Vec::new(); - for entry in fs::read_dir(&root)? { - let path = entry?.path(); - if path.extension().and_then(|value| value.to_str()) == Some("sln") { - top_level.push(path); - } - } - top_level.sort(); - if !top_level.is_empty() { - return Ok(json!({ - "status": "found", - "path": top_level[0], - "candidates": top_level.iter().take(8).collect::>(), - })); - } - let mut candidates = Vec::new(); - collect_solutions(&root, &root, &mut candidates)?; - Ok(json!({ - "status": if candidates.is_empty() { "missingSolution" } else { "found" }, - "path": candidates.first(), - "candidates": candidates.iter().take(8).collect::>(), - })) -} - -fn collect_solutions(root: &Path, current: &Path, candidates: &mut Vec) -> Result<()> { - if candidates.len() >= 8 { - return Ok(()); - } - for entry in fs::read_dir(current)? { - let path = entry?.path(); - let rel = path.strip_prefix(root).unwrap_or(&path); - if rel.components().any(|component| { - let value = component.as_os_str().to_string_lossy(); - value.starts_with('.') - || matches!( - value.as_ref(), - "Library" | "Temp" | "obj" | "bin" | "node_modules" - ) - }) { - continue; - } - if path.is_dir() { - collect_solutions(root, &path, candidates)?; - } else if path.extension().and_then(|value| value.to_str()) == Some("sln") { - candidates.push(path); - } - } - Ok(()) -} - -fn run_git(project_root: &Path, args: &[&str]) -> Option { - let output = Command::new("git") - .arg("-C") - .arg(project_root) - .args(args) - .output() - .ok()?; - if !output.status.success() { - return None; - } - Some(String::from_utf8_lossy(&output.stdout).trim().to_string()) -} - -fn vcs_summary(project_root: &Path) -> serde_json::Value { - let branch = run_git(project_root, &["rev-parse", "--abbrev-ref", "HEAD"]); - let status = run_git(project_root, &["status", "--porcelain=v1"]); - let changed = run_git(project_root, &["diff", "--name-only"]); - let staged = run_git(project_root, &["diff", "--cached", "--name-only"]); - let mut changed_files: Vec = changed - .as_deref() - .unwrap_or_default() - .lines() - .map(ToOwned::to_owned) - .collect(); - if let Some(status) = &status { - for line in status.lines() { - let path = line.get(3..).unwrap_or_default().trim().to_string(); - if !path.is_empty() && !changed_files.contains(&path) { - changed_files.push(path); - } - } - } - json!({ - "status": if branch.is_some() { "gitDetected" } else { "notGit" }, - "branch": branch, - "dirty": status.as_deref().is_some_and(|value| !value.is_empty()), - "changedFiles": changed_files, - "stagedFiles": staged.as_deref().unwrap_or_default().lines().collect::>(), - "changedRangesKnown": branch.is_some(), - }) -} - -fn render_status(summary: &serde_json::Value) -> String { - format!( - "# Rider Bridge Status\n\n- status: {}\n- workspace: `{}`\n- solution: `{}`\n- rider: `{}`\n- installations: {}\n- branch: {}\n- dirty: {}\n- note: {}\n\nRider is a source-context organ. This artifact is an operator-safe projection, not durable Epiphany truth.\n", - summary["status"].as_str().unwrap_or("unknown"), - summary["workspace"].as_str().unwrap_or("unknown"), - summary["solutionPath"].as_str().unwrap_or("none"), - summary["riderPath"].as_str().unwrap_or("missing"), - summary["installationCount"].as_u64().unwrap_or_default(), - summary["vcs"]["branch"].as_str().unwrap_or("unknown"), - summary["vcs"]["dirty"].as_bool().unwrap_or(false), - summary["note"].as_str().unwrap_or(""), - ) -} - -fn render_context(packet: &serde_json::Value, summary: &serde_json::Value) -> String { - format!( - "# Rider Context Packet\n\n- status: {}\n- project: `{}`\n- solution: `{}`\n- file: `{}`\n- selection: {}-{}\n- symbol: {}\n- artifact: `{}`\n\nModeling may use this packet as scratch/context. It is not accepted map state until reviewed.\n", - summary["status"].as_str().unwrap_or("unknown"), - packet["projectRoot"].as_str().unwrap_or("unknown"), - packet["solutionPath"].as_str().unwrap_or("none"), - packet["filePath"].as_str().unwrap_or("none"), - packet["selection"]["startLine"] - .as_i64() - .map(|value| value.to_string()) - .unwrap_or_else(|| "none".to_string()), - packet["selection"]["endLine"] - .as_i64() - .map(|value| value.to_string()) - .unwrap_or_else(|| "none".to_string()), - packet["symbol"]["name"].as_str().unwrap_or("none"), - summary["artifactPath"].as_str().unwrap_or("unknown"), - ) -} - -fn status_rider(options: &RiderOptions) -> Result { - let project_root = options.project_root.canonicalize()?; - let directory = options - .artifact_dir - .clone() - .map(Ok) - .unwrap_or_else(|| artifact_dir(&options.artifact_root, "inspect"))?; - let installations = discover_rider_installations()?; - let solution = discover_solution(&project_root)?; - let first_rider = installations.first(); - let status = if first_rider.is_some() { - "discovered" - } else { - "missingRider" - }; - let note = if first_rider.is_some() { - "A Rider executable candidate was discovered for explicit source navigation and context capture." - } else { - "No Rider executable was found. Install Rider or set EPIPHANY_RIDER_PATH." - }; - let summary = json!({ - "kind": "riderBridgeStatus", - "status": status, - "workspace": project_root, - "solutionPath": solution["path"], - "solutionStatus": solution["status"], - "solutionCandidates": solution["candidates"], - "riderPath": first_rider.and_then(|value| value["path"].as_str()), - "installationCount": installations.len(), - "installations": installations, - "searchRoots": rider_search_roots(), - "vcs": vcs_summary(&project_root), - "capturedAt": now_iso(), - "artifactPath": directory, - "note": note, - }); - write_json(&directory.join("rider-bridge-summary.json"), &summary)?; - write_text( - &directory.join("rider-bridge-status.md"), - &render_status(&summary), - )?; - Ok(summary) -} - -fn packet_from_options(options: &RiderOptions, project_root: &Path) -> Result { - if let Some(packet) = &options.packet { - let value: serde_json::Value = serde_json::from_str(&fs::read_to_string(packet)?)?; - if !value.is_object() { - return Err(anyhow!("context packet must be a JSON object")); - } - return Ok(value); - } - let solution = discover_solution(project_root)?; - let file_path = options - .file - .as_ref() - .map(|path| path.canonicalize().unwrap_or_else(|_| path.to_path_buf())); - if let Some(file_path) = &file_path - && !is_inside(file_path, project_root) - { - return Err(anyhow!( - "context file is outside project root: {}", - file_path.display() - )); - } - Ok(json!({ - "kind": "riderContext", - "capturedAt": now_iso(), - "projectRoot": project_root, - "solutionPath": solution["path"], - "filePath": file_path.as_ref().map(|path| relative_or_absolute(path, project_root)), - "caret": options.line.map(|line| json!({ "line": line, "column": options.column })), - "selection": if options.selection_start.is_some() || options.selection_end.is_some() { - json!({ "startLine": options.selection_start, "endLine": options.selection_end }) - } else { - serde_json::Value::Null - }, - "symbol": options.symbol_name.as_ref().map(|name| json!({ - "name": name, - "kind": options.symbol_kind, - "namespace": options.symbol_namespace, - })), - "vcs": vcs_summary(project_root), - })) -} - -fn context_rider(options: &RiderOptions) -> Result { - let project_root = options.project_root.canonicalize()?; - let directory = options - .artifact_dir - .clone() - .map(Ok) - .unwrap_or_else(|| artifact_dir(&options.artifact_root, "context"))?; - let packet = packet_from_options(options, &project_root)?; - let mut status_options = RiderOptions { - ..options.clone_for_status(directory.clone()) - }; - status_options.artifact_dir = Some(directory.clone()); - let mut summary = status_rider(&status_options)?; - summary["kind"] = json!("riderContextCapture"); - summary["status"] = json!("captured"); - summary["contextPath"] = json!(directory.join("rider-context.json")); - summary["filePath"] = packet["filePath"].clone(); - summary["symbol"] = packet["symbol"].clone(); - write_json(&directory.join("rider-context.json"), &packet)?; - write_json(&directory.join("rider-bridge-summary.json"), &summary)?; - write_text( - &directory.join("rider-context.md"), - &render_context(&packet, &summary), - )?; - Ok(summary) -} - -impl RiderOptions { - fn clone_for_status(&self, directory: PathBuf) -> Self { - Self { - project_root: self.project_root.clone(), - artifact_root: self.artifact_root.clone(), - artifact_dir: Some(directory), - packet: None, - file: None, - line: None, - column: None, - selection_start: None, - selection_end: None, - symbol_name: None, - symbol_kind: None, - symbol_namespace: None, - launch: false, - } - } -} - -fn open_ref_rider(options: &RiderOptions) -> Result { - let project_root = options.project_root.canonicalize()?; - let directory = options - .artifact_dir - .clone() - .map(Ok) - .unwrap_or_else(|| artifact_dir(&options.artifact_root, "open-ref"))?; - let status_options = options.clone_for_status(directory.clone()); - let status = status_rider(&status_options)?; - let file_path = options - .file - .as_ref() - .ok_or_else(|| anyhow!("open-ref requires --file"))? - .canonicalize()?; - if !is_inside(&file_path, &project_root) { - return Err(anyhow!( - "code ref is outside project root: {}", - file_path.display() - )); - } - let rider_path = status["riderPath"].as_str(); - let mut command = Vec::::new(); - if let Some(path) = rider_path { - command.push(path.to_string()); - command.push(file_path.display().to_string()); - if let Some(line) = options.line { - command.push("--line".to_string()); - command.push(line.to_string()); - } - } - if let Some(path) = rider_path - && options.launch - { - let mut launch = Command::new(path); - launch.arg(&file_path); - if let Some(line) = options.line { - launch.arg("--line").arg(line.to_string()); - } - launch.current_dir(&project_root).spawn()?; - } - let summary = json!({ - "kind": "riderOpenRef", - "status": if rider_path.is_some() { if options.launch { "launched" } else { "planned" } } else { "missingRider" }, - "workspace": status["workspace"], - "solutionPath": status["solutionPath"], - "solutionStatus": status["solutionStatus"], - "solutionCandidates": status["solutionCandidates"], - "riderPath": status["riderPath"], - "installationCount": status["installationCount"], - "installations": status["installations"], - "searchRoots": status["searchRoots"], - "vcs": status["vcs"], - "capturedAt": status["capturedAt"], - "artifactPath": directory, - "note": status["note"], - "filePath": file_path, - "line": options.line, - "column": options.column, - "command": command, - "launched": options.launch && rider_path.is_some(), - }); - write_json(&directory.join("rider-open-ref.json"), &summary)?; - write_json(&directory.join("rider-bridge-summary.json"), &summary)?; - write_text( - &directory.join("rider-bridge-status.md"), - &render_status(&summary), - )?; - Ok(summary) -} - -fn bridge_guidance(project_root: &Path) -> Result { - let root = repo_root()?; - let installations = discover_rider_installations()?; - let solution = discover_solution(project_root)?; - let status_command = format!( - "`epiphany-rider-bridge status --project-root {}`", - project_root.display() - ); - if let Some(first) = installations.first() { - let solution_text = solution["path"].as_str().unwrap_or("no solution found yet"); - Ok(format!( - "- Rider bridge: Rider is available at `{}` and solution context is `{}`. Use {} for an auditable source/IDE status receipt, and use `context --file --selection-start --selection-end --symbol-name ` when the future plugin or operator sends a bounded source slice. Rider facts are scratch/context until modeling or verification accepts them.", - first["path"].as_str().unwrap_or("unknown"), - solution_text, - status_command, - )) - } else { - Ok(format!( - "- Rider bridge: no Rider executable was found. Use {} to write an auditable missing-IDE artifact, or set EPIPHANY_RIDER_PATH. Do not pretend IDE diagnostics, changed ranges, or source navigation were captured until the bridge has a receipt. Native bridge binary is rooted at `{}`.", - status_command, - root.display(), - )) - } -} diff --git a/epiphany-core/src/bin/epiphany-runtime-store-benchmark.rs b/epiphany-core/src/bin/epiphany-runtime-store-benchmark.rs deleted file mode 100644 index 29e2ad8f..00000000 --- a/epiphany-core/src/bin/epiphany-runtime-store-benchmark.rs +++ /dev/null @@ -1,143 +0,0 @@ -use anyhow::{Context, Result, anyhow}; -use cultcache_rs::{ - CacheBackingStore, CultCacheEnvelope, RedbMessagePackBackingStore, - SingleFileMessagePackBackingStore, -}; -use epiphany_core::{EpiphanyRuntimeJob, RUNTIME_JOB_TYPE}; -use serde::Serialize; -use sha2::{Digest, Sha256}; -use std::path::Path; -use std::time::Instant; - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -struct BenchmarkReceipt { - schema_version: &'static str, - store: String, - job_id: String, - job_created_at: String, - selection_window_milliseconds: i64, - selected_stored_ats: Vec, - selected_entry_count: usize, - selected_identities: Vec, - selected_envelope_set_sha256: String, - read_milliseconds: u128, - cas_milliseconds: Vec, -} - -enum Store { - Snapshot(SingleFileMessagePackBackingStore), - Keyed(RedbMessagePackBackingStore), -} - -impl Store { - fn open(path: &Path) -> Result { - match path.extension().and_then(|value| value.to_str()) { - Some("cc" | "msgpack") => { - Ok(Self::Snapshot(SingleFileMessagePackBackingStore::new(path))) - } - Some("redb") => Ok(Self::Keyed(RedbMessagePackBackingStore::new(path)?)), - extension => Err(anyhow!( - "unsupported benchmark store extension {extension:?}" - )), - } - } - - fn pull_all(&self) -> Result> { - match self { - Self::Snapshot(store) => store.pull_all(), - Self::Keyed(store) => store.pull_all(), - } - } - - fn compare_and_swap_batch( - &self, - expected: &[CultCacheEnvelope], - replacements: Vec, - ) -> Result { - match self { - Self::Snapshot(store) => store.compare_and_swap_batch(expected, replacements), - Self::Keyed(store) => store.compare_and_swap_batch(expected, replacements), - } - } -} - -fn main() -> Result<()> { - let mut args = std::env::args().skip(1); - let store_path = args - .next() - .ok_or_else(|| anyhow!("usage: epiphany-runtime-store-benchmark [repetitions]"))?; - let job_id = args - .next() - .ok_or_else(|| anyhow!("missing historical job id"))?; - let repetitions = args - .next() - .map(|value| value.parse::()) - .transpose() - .context("invalid repetitions")? - .unwrap_or(3); - if repetitions == 0 || args.next().is_some() { - return Err(anyhow!("repetitions must be positive and arguments exact")); - } - let path = Path::new(&store_path); - let store = Store::open(path)?; - let read_started = Instant::now(); - let entries = store.pull_all()?; - let read_milliseconds = read_started.elapsed().as_millis(); - let job = entries - .iter() - .find(|entry| entry.r#type == RUNTIME_JOB_TYPE && entry.key == job_id) - .ok_or_else(|| anyhow!("historical runtime job envelope is absent"))?; - let runtime_job: EpiphanyRuntimeJob = rmp_serde::from_slice(&job.payload)?; - let job_created_at = runtime_job.created_at; - let job_created = chrono::DateTime::parse_from_rfc3339(&job_created_at)?; - let selection_window_milliseconds = 2_000_i64; - let mut selected = entries - .into_iter() - .filter(|entry| { - chrono::DateTime::parse_from_rfc3339(&entry.stored_at) - .map(|stored| { - (stored.timestamp_millis() - job_created.timestamp_millis()).abs() - <= selection_window_milliseconds - }) - .unwrap_or(false) - }) - .collect::>(); - selected.sort_by(|left, right| (&left.r#type, &left.key).cmp(&(&right.r#type, &right.key))); - if selected.is_empty() { - return Err(anyhow!("historical job commit selected no envelopes")); - } - let selected_identities = selected - .iter() - .map(|entry| format!("{}:{}", entry.r#type, entry.key)) - .collect::>(); - let selected_stored_ats = selected - .iter() - .map(|entry| entry.stored_at.clone()) - .collect::>(); - let selected_envelope_set_sha256 = - format!("{:x}", Sha256::digest(rmp_serde::to_vec_named(&selected)?)); - let mut cas_milliseconds = Vec::with_capacity(repetitions); - for _ in 0..repetitions { - let started = Instant::now(); - if !store.compare_and_swap_batch(&selected, selected.clone())? { - return Err(anyhow!("exact historical batch CAS unexpectedly refused")); - } - cas_milliseconds.push(started.elapsed().as_millis()); - } - let receipt = BenchmarkReceipt { - schema_version: "epiphany.runtime_store_benchmark_receipt.v1", - store: path.display().to_string(), - job_id, - job_created_at, - selection_window_milliseconds, - selected_stored_ats, - selected_entry_count: selected.len(), - selected_identities, - selected_envelope_set_sha256, - read_milliseconds, - cas_milliseconds, - }; - println!("{}", serde_json::to_string_pretty(&receipt)?); - Ok(()) -} diff --git a/epiphany-core/src/bin/epiphany-semantic-recovery-smoke.rs b/epiphany-core/src/bin/epiphany-semantic-recovery-smoke.rs deleted file mode 100644 index 117d4c6b..00000000 --- a/epiphany-core/src/bin/epiphany-semantic-recovery-smoke.rs +++ /dev/null @@ -1,641 +0,0 @@ -use anyhow::{Context, Result, bail}; -use chrono::Utc; -use epiphany_core::*; -use serde::Serialize; -use serde_json::{Value, json}; -use sha2::{Digest, Sha256}; -use std::{ - env, fs, - io::{BufRead, BufReader}, - net::TcpListener, - ops::{Deref, DerefMut}, - path::{Path, PathBuf}, - process::{Child, Command, Stdio}, - sync::{ - Arc, - atomic::{AtomicBool, Ordering}, - }, - thread, - time::{Duration, Instant}, -}; -use uuid::Uuid; - -const RUNTIME: &str = "semantic-recovery-smoke"; -const PROJECTOR: &str = "epiphany-memory-semantic-projector"; -const SERVICE: &str = "epiphany-memory-semantic-projector-service"; - -fn main() -> Result<()> { - let root = env::args().nth(1).map(PathBuf::from).unwrap_or_else(|| { - env::temp_dir().join(format!("epiphany-semantic-recovery-{}", Uuid::new_v4())) - }); - if root.exists() { - bail!("fixture root already exists: {}", root.display()); - } - fs::create_dir_all(&root)?; - let proof_path = root.join("proof.json"); - let result = run(&root); - if let Err(error) = &result { - fs::write( - &proof_path, - serde_json::to_vec_pretty(&json!({ - "schemaVersion":"epiphany.semantic_recovery_smoke_proof.v1", "status":"failed", - "fixtureRoot":root, "error":format!("{error:#}"), "liveCanonicalInputsUsed":false - }))?, - )?; - } - result -} - -fn run(root: &Path) -> Result<()> { - let id = Uuid::new_v4().to_string(); - let swarm = format!("smoke-{id}"); - let mind = root.join("mind.cc"); - let modeling = root.join("modeling.cc"); - let verse = root.join("verse.cc"); - seed_mind(root, &mind, &swarm)?; - seed_modeling(&modeling, &mind, &swarm)?; - let input = agent_memory_semantic_projection_input(&mind)?; - let modeling_input = runtime_modeling_semantic_projection_input(&modeling)?; - - let exe_dir = env::current_exe()? - .parent() - .context("smoke executable has no directory")? - .to_path_buf(); - let projector = sibling(&exe_dir, PROJECTOR); - let supervisor = sibling(&exe_dir, "epiphany-daemon-supervisor"); - for path in [&projector, &supervisor] { - if !path.is_file() { - bail!("required sibling executable missing: {}", path.display()); - } - } - let receipt_id = Uuid::new_v4().to_string(); - let qdrant = - env::var("EPIPHANY_QDRANT_URL").unwrap_or_else(|_| "http://127.0.0.1:16333".into()); - let ollama = - env::var("EPIPHANY_OLLAMA_BASE_URL").unwrap_or_else(|_| "http://127.0.0.1:11434".into()); - let model = env::var("EPIPHANY_OLLAMA_MODEL").unwrap_or_else(|_| "qwen3-embedding:0.6b".into()); - let mind_collection = format!("epiphany_smoke_mind_{}", id.replace('-', "")); - let modeling_collection = format!("epiphany_smoke_modeling_{}", id.replace('-', "")); - for collection in [&mind_collection, &modeling_collection] { - if collection_exists(&qdrant, collection)? { - bail!("refused fixture collection collision: {collection}"); - } - } - let mut collections = FixtureCollections::new( - qdrant.clone(), - vec![mind_collection.clone(), modeling_collection.clone()], - ); - let stalled = StalledHttpEndpoint::new()?; - let old_args = vec![ - "pulse".into(), - "--agent-store".into(), - s(&mind), - "--runtime-store".into(), - s(&modeling), - "--local-verse-store".into(), - s(&verse), - "--runtime-id".into(), - RUNTIME.into(), - "--interval-seconds".into(), - "1".into(), - "--qdrant-url".into(), - qdrant.clone(), - "--ollama-base-url".into(), - stalled.url(), - "--ollama-model".into(), - model.clone(), - ]; - let mut old_child = ChildGuard( - Command::new(&projector) - .args(&old_args) - .env("EPIPHANY_MIND_QDRANT_COLLECTION", &mind_collection) - .env("EPIPHANY_MODELING_QDRANT_COLLECTION", &modeling_collection) - .stdout(Stdio::null()) - .stderr(Stdio::piped()) - .spawn()?, - ); - let old = wait_for_running_claim(&mind, &input, Duration::from_secs(15))?; - stop(&mut old_child); - let old_pid = old_child.id(); - let args = vec![ - "serve".into(), - "--agent-store".into(), - s(&mind), - "--runtime-store".into(), - s(&modeling), - "--local-verse-store".into(), - s(&verse), - "--runtime-id".into(), - RUNTIME.into(), - "--interval-seconds".into(), - "3".into(), - "--qdrant-url".into(), - qdrant.clone(), - "--ollama-base-url".into(), - ollama.clone(), - "--ollama-model".into(), - model, - ]; - let policy = EpiphanyCultMeshManagedServicePolicyEntry { - schema_version: EPIPHANY_CULTMESH_MANAGED_SERVICE_POLICY_SCHEMA_VERSION.into(), - policy_id: "managed-service-policy-epiphany-memory-semantic-projector-service".into(), - service_id: SERVICE.into(), - owner_daemon_id: "epiphany-daemon-supervisor".into(), - command: s(&projector), - args: args.clone(), - cwd: None, - enabled: true, - restart_mode: "always".into(), - cooldown_seconds: 0, - backoff_multiplier: 1, - stdout_artifact: s(&root.join("projector.stdout.log")), - stderr_artifact: s(&root.join("projector.stderr.log")), - updated_at_utc: Utc::now().to_rfc3339(), - private_state_exposed: false, - notes: vec!["GUID-scoped recovery smoke".into()], - }; - write_epiphany_cultmesh_semantic_projector_service_policy(&verse, RUNTIME, policy.clone())?; - let (_, policy_digest) = - load_epiphany_cultmesh_managed_service_policy_with_digest(&verse, RUNTIME, SERVICE)? - .context("policy absent")?; - let mut child = ChildGuard( - Command::new(&projector) - .args(&args) - .env("EPIPHANY_STARTUP_LIFECYCLE_RECEIPT_ID", &receipt_id) - .env("EPIPHANY_MIND_QDRANT_COLLECTION", &mind_collection) - .env("EPIPHANY_MODELING_QDRANT_COLLECTION", &modeling_collection) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn()?, - ); - let pid = child.id(); - let executable_sha256 = format!("sha256-{:x}", Sha256::digest(fs::read(&projector)?)); - let completed = Utc::now(); - write_epiphany_cultmesh_daemon_service_lifecycle_receipt( - &verse, - RUNTIME, - EpiphanyCultMeshDaemonServiceLifecycleReceiptEntry { - schema_version: EPIPHANY_CULTMESH_DAEMON_SERVICE_LIFECYCLE_RECEIPT_SCHEMA_VERSION - .into(), - receipt_id: receipt_id.clone(), - service_id: SERVICE.into(), - scheduler_id: "epiphany-daemon-supervisor".into(), - runtime_id: RUNTIME.into(), - daemon_selector: "epiphany-daemon-supervisor".into(), - action: "launch".into(), - status: "launched".into(), - command: s(&projector), - args: args.clone(), - cwd: None, - process_id: Some(pid), - exit_code: None, - started_at_utc: (completed - chrono::Duration::seconds(1)).to_rfc3339(), - completed_at_utc: Some(completed.to_rfc3339()), - operator_artifact_ref: format!("fixture://{id}/launch"), - private_state_exposed: false, - notes: vec![], - executable_sha256, - preflight_witness_id: String::new(), - required_document_types: vec![], - schema_preflight_passed: false, - schema_catalog_sha256: String::new(), - managed_policy_id: policy.policy_id, - managed_policy_digest: policy_digest, - provider_daemon_id: PROJECTOR.into(), - startup_correlation_id: receipt_id.clone(), - process_creation_token: 1, - process_created_at_rfc3339: None, - process_executable_path: projector.display().to_string(), - }, - )?; - let stdout = child - .stdout - .take() - .context("projector stdout unavailable")?; - let (tx, rx) = std::sync::mpsc::channel(); - thread::spawn(move || { - for line in BufReader::new(stdout).lines().map_while(Result::ok) { - let _ = tx.send(line); - } - }); - let first = next_pulse(&rx, Duration::from_secs(20))?; - let heartbeat = first["heartbeatId"] - .as_str() - .context("pulse heartbeat absent")?; - if first["providerStatus"] != "ready" { - stop(&mut child); - bail!("projector did not establish ready recovery witness: {first}"); - } - - let unrelated_id = Uuid::new_v4().to_string(); - write_epiphany_cultmesh_daemon_heartbeat_event( - &verse, - RUNTIME, - EpiphanyCultMeshDaemonHeartbeatEventEntry { - schema_version: EPIPHANY_CULTMESH_DAEMON_HEARTBEAT_EVENT_SCHEMA_VERSION.into(), - heartbeat_id: unrelated_id.clone(), - daemon_id: PROJECTOR.into(), - cluster_id: "local".into(), - provider_incarnation: first["providerIncarnation"].as_str().unwrap().into(), - sequence: 999, - status: "ready".into(), - heartbeat_at: Utc::now().to_rfc3339(), - private_state_exposed: false, - startup_lifecycle_receipt_id: String::new(), - }, - )?; - let before_wrong = file_sha256(&mind)?; - let wrong = supervisor_recover( - &supervisor, - &verse, - &mind, - &old.claim_id, - &receipt_id, - &unrelated_id, - )?; - if wrong.success { - stop(&mut child); - bail!("uncorrelated heartbeat authorized recovery"); - } - if file_sha256(&mind)? != before_wrong { - stop(&mut child); - bail!("wrong heartbeat mutated canonical fixture store"); - } - let recovered_process = supervisor_recover( - &supervisor, - &verse, - &mind, - &old.claim_id, - &receipt_id, - heartbeat, - )?; - if !recovered_process.success { - stop(&mut child); - bail!("exact recovery failed: {}", recovered_process.stderr); - } - let recovered: Value = serde_json::from_str(&recovered_process.stdout)?; - if recovered["epoch"] != old.claim_epoch + 1 { - stop(&mut child); - bail!("recovered epoch did not advance exactly once"); - } - let before_reuse = file_sha256(&mind)?; - let reuse = supervisor_recover( - &supervisor, - &verse, - &mind, - &old.claim_id, - &receipt_id, - heartbeat, - )?; - if reuse.success { - stop(&mut child); - bail!("recovery evidence was reusable"); - } - if file_sha256(&mind)? != before_reuse { - stop(&mut child); - bail!("single-use refusal mutated canonical fixture store"); - } - let atomic = inspect_memory_semantic_recovery_for_smoke( - &mind, - &input, - &old.executor_id, - &old.executor_incarnation, - )?; - if atomic.claim_epoch != 2 - || atomic.claim_status != "running" - || atomic.attempt_status != "running" - || !atomic.recovery_authorization_consumed - || !atomic.abandoned_attempt_failed - || atomic.old_owner_authenticates_current_claim - { - stop(&mut child); - bail!("recovery atomic state is invalid: {atomic:?}"); - } - let deadline = Instant::now() + Duration::from_secs(60); - let terminal = loop { - if let Some(receipt) = - load_memory_semantic_projection_success(&mind, input.obligation(), input.source_head())? - { - break receipt; - } - if Instant::now() > deadline { - stop(&mut child); - bail!("recovered successor did not reach terminal success"); - } - let _ = next_pulse(&rx, Duration::from_secs(5)); - }; - stop(&mut child); - collections.cleanup()?; - let _readiness = load_memory_semantic_projection_readiness(&mind, &input)? - .context("terminal success lacks readiness")?; - let query_eligible = memory_semantic_projection_query_eligible( - input.obligation(), - input.source_head(), - &terminal, - ); - if !query_eligible { - bail!("terminal receipt is not query eligible"); - } - let proof = json!({"schemaVersion":"epiphany.semantic_recovery_smoke_proof.v1","status":"passed","fixtureId":id,"fixtureRoot":root, - "liveCanonicalInputsUsed":false,"fixtureStores":{"mind":{"path":mind,"sourceId":input.source_head().canonical_source_id},"modeling":{"path":modeling,"sourceId":modeling_input.source_head().canonical_source_id},"verse":{"path":verse,"runtimeId":RUNTIME}}, - "processBoundary":{"supervisor":supervisor,"projector":projector,"abandonedProjectorPid":old_pid,"replacementProjectorPid":pid}, - "fencing":{"wrongHeartbeat":wrong,"wrongHeartbeatCanonicalBytesUnchanged":true,"singleUse":reuse,"singleUseCanonicalBytesUnchanged":true,"predecessorClaimId":old.claim_id,"predecessorEpoch":old.claim_epoch,"atomicRecovery":atomic}, - "successor":{"authorizationId":recovered["authorizationId"],"epoch":recovered["epoch"],"receiptId":terminal.receipt_id,"queryEligible":query_eligible,"readinessPresent":true}, - "cleanup":{"mindCollection":mind_collection,"modelingCollection":modeling_collection,"preflightAbsent":true,"deleted":true,"verifiedAbsent":true}}); - fs::write(root.join("proof.json"), serde_json::to_vec_pretty(&proof)?)?; - println!("{}", serde_json::to_string_pretty(&proof)?); - Ok(()) -} - -fn seed_mind(root: &Path, store: &Path, swarm: &str) -> Result<()> { - let dir = root.join("agents"); - fs::create_dir(&dir)?; - for (role, agent, file) in [ - ( - "imagination", - "epiphany.imagination", - "imagination.agent-state.json", - ), - ("modeling", "epiphany.modeling", "modeling.agent-state.json"), - ("verification", "epiphany.soul", "soul.agent-state.json"), - ("implementation", "epiphany.hands", "hands.agent-state.json"), - ("research", "epiphany.eyes", "eyes.agent-state.json"), - ("Persona", "epiphany.Persona", "Persona.agent-state.json"), - ("coordinator", "epiphany.self", "self.agent-state.json"), - ] { - let j = json!({"schema_version":"ghostlight.agent_state.v1","world":{"world_id":"fixture","setting":"isolated fixture","time":{"label":"now"},"canon_context":["fixture"]},"agents":[{"agent_id":agent,"identity":{"name":role,"roles":["fixture"],"origin":"fixture","public_description":"fixture","private_notes":[]},"canonical_state":{"underlying_organization":{"x":{"mean":0.5,"plasticity":0.5,"current_activation":0.5}},"stable_dispositions":{"x":{"mean":0.5,"plasticity":0.5,"current_activation":0.5}},"behavioral_dimensions":{"x":{"mean":0.5,"plasticity":0.5,"current_activation":0.5}},"presentation_strategy":{"x":{"mean":0.5,"plasticity":0.5,"current_activation":0.5}},"voice_style":{"x":{"mean":0.5,"plasticity":0.5,"current_activation":0.5}},"situational_state":{"x":{"mean":0.5,"plasticity":0.5,"current_activation":0.5}},"values":[{"value_id":"v","label":"fixture","priority":0.5,"unforgivable_if_betrayed":false}]},"goals":[{"goal_id":"g","description":"fixture","scope":"life","priority":0.5,"emotional_stake":"fixture","blockers":[],"status":"active"}],"memories":{"episodic":[],"semantic":[],"relationship_summaries":[]},"perceived_state_overlays":[]}],"relationships":[],"events":[],"scenes":[]}); - let mut j = j; - j["schema_version"] = json!("ghostlight.agent_state.v0"); - fs::write(dir.join(file), serde_json::to_vec(&j)?)?; - } - migrate_agent_memory_json_dir_to_cultcache(dir, store)?; - ensure_agent_memory_swarm_identity(store, swarm)?; - admit_legacy_agent_memory_generation(store)?; - Ok(()) -} -fn seed_modeling(store: &Path, mind: &Path, swarm: &str) -> Result<()> { - let at = Utc::now().to_rfc3339(); - initialize_runtime_spine( - store, - RuntimeSpineInitOptions { - runtime_id: format!("runtime-{swarm}"), - display_name: "fixture".into(), - created_at: at.clone(), - }, - )?; - bind_runtime_to_agent_memory_swarm(store, mind, &at)?; - let mut graph = EpiphanyMemoryGraphSnapshot { - schema_version: Some(MEMORY_GRAPH_PROJECTION_SCHEMA_VERSION.into()), - graph_id: format!("model-{swarm}"), - model_revision: 1, - ..Default::default() - }; - graph.model_hash = memory_graph_model_hash(&graph)?; - ensure_runtime_repo_model(store, store.with_extension("absent"), &graph, &at)?; - Ok(()) -} -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -struct ProcessEvidence { - success: bool, - exit_code: Option, - stdout: String, - stderr: String, - stderr_class: String, -} - -fn supervisor_recover( - exe: &Path, - verse: &Path, - mind: &Path, - claim: &str, - receipt: &str, - heartbeat: &str, -) -> Result { - let out = Command::new(exe) - .args([ - "semantic-recover", - "--store", - &s(verse), - "--runtime-id", - RUNTIME, - "--agent-store", - &s(mind), - "--expected-claim-id", - claim, - "--receipt-id", - receipt, - "--provider-heartbeat-id", - heartbeat, - ]) - .output()?; - let stdout = String::from_utf8_lossy(&out.stdout).into_owned(); - let stderr = String::from_utf8_lossy(&out.stderr).into_owned(); - let stderr_class = stderr - .lines() - .find(|line| !line.trim().is_empty()) - .unwrap_or("none") - .chars() - .take(240) - .collect(); - Ok(ProcessEvidence { - success: out.status.success(), - exit_code: out.status.code(), - stdout, - stderr, - stderr_class, - }) -} - -fn wait_for_running_claim( - store: &Path, - input: &MemorySemanticProjectionInput, - timeout: Duration, -) -> Result { - let deadline = Instant::now() + timeout; - loop { - if let Ok(row) = - inspect_memory_semantic_recovery_for_smoke(store, input, "absent", "absent") - { - if row.claim_status == "running" && row.claim_epoch == 1 { - return Ok(row); - } - } - if Instant::now() >= deadline { - bail!("real projector did not persist its running epoch-1 claim"); - } - thread::sleep(Duration::from_millis(20)); - } -} - -fn collection_exists(base: &str, name: &str) -> Result { - let url = format!("{}/collections/{}", base.trim_end_matches('/'), name); - let status = reqwest::blocking::Client::new().get(url).send()?.status(); - if status.is_success() { - return Ok(true); - } - if status.as_u16() == 404 { - return Ok(false); - } - bail!("Qdrant collection preflight returned {status} for {name}") -} - -fn file_sha256(path: &Path) -> Result { - Ok(format!("{:x}", Sha256::digest(fs::read(path)?))) -} - -struct StalledHttpEndpoint { - url: String, - running: Arc, -} -impl StalledHttpEndpoint { - fn new() -> Result { - let listener = TcpListener::bind("127.0.0.1:0")?; - listener.set_nonblocking(true)?; - let url = format!("http://{}", listener.local_addr()?); - let running = Arc::new(AtomicBool::new(true)); - let alive = running.clone(); - thread::spawn(move || { - let mut held = Vec::new(); - while alive.load(Ordering::Acquire) { - match listener.accept() { - Ok((stream, _)) => held.push(stream), - Err(error) if error.kind() == std::io::ErrorKind::WouldBlock => { - thread::sleep(Duration::from_millis(5)) - } - Err(_) => break, - } - } - }); - Ok(Self { url, running }) - } - fn url(&self) -> String { - self.url.clone() - } -} -impl Drop for StalledHttpEndpoint { - fn drop(&mut self) { - self.running.store(false, Ordering::Release); - } -} -fn next_pulse(rx: &std::sync::mpsc::Receiver, timeout: Duration) -> Result { - let deadline = Instant::now() + timeout; - loop { - let left = deadline.saturating_duration_since(Instant::now()); - let line = rx - .recv_timeout(left) - .context("timed out waiting for projector pulse")?; - if let Ok(v) = serde_json::from_str::(&line) { - return Ok(v); - } - } -} -fn cleanup_collection(base: &str, name: &str) -> Result<()> { - let client = reqwest::blocking::Client::new(); - let url = format!("{}/collections/{}", base.trim_end_matches('/'), name); - let response = client.delete(&url).send()?; - if !response.status().is_success() && response.status().as_u16() != 404 { - bail!( - "failed to delete fixture collection {name}: {}", - response.status() - ) - } - let verification = client.get(&url).send()?.status(); - if verification.as_u16() != 404 { - bail!( - "fixture collection cleanup verification expected HTTP 404 for {name}, observed {verification}" - ) - } - Ok(()) -} -fn stop(child: &mut Child) { - let _ = child.kill(); - let _ = child.wait(); -} - -struct ChildGuard(Child); -impl Deref for ChildGuard { - type Target = Child; - fn deref(&self) -> &Child { - &self.0 - } -} -impl DerefMut for ChildGuard { - fn deref_mut(&mut self) -> &mut Child { - &mut self.0 - } -} -impl Drop for ChildGuard { - fn drop(&mut self) { - stop(&mut self.0); - } -} - -struct FixtureCollections { - base: String, - names: Vec, - cleaned: bool, -} -impl FixtureCollections { - fn new(base: String, names: Vec) -> Self { - Self { - base, - names, - cleaned: false, - } - } - fn cleanup(&mut self) -> Result<()> { - for name in &self.names { - cleanup_collection(&self.base, name)?; - } - self.cleaned = true; - Ok(()) - } -} -impl Drop for FixtureCollections { - fn drop(&mut self) { - if !self.cleaned { - for name in &self.names { - let _ = cleanup_collection(&self.base, name); - } - } - } -} -fn sibling(dir: &Path, name: &str) -> PathBuf { - dir.join(if cfg!(windows) { - format!("{name}.exe") - } else { - name.into() - }) -} -fn s(path: &Path) -> String { - path.to_string_lossy().into_owned() -} - -#[cfg(test)] -mod tests { - #[test] - fn fixture_has_no_canonical_store_or_default_collection_mouth() { - let source = include_str!("epiphany-semantic-recovery-smoke.rs"); - let forbidden = [ - ["state/", "agents.msgpack"].concat(), - ["state\\", "agents.msgpack"].concat(), - [".epiphany", "-run"].concat(), - ["epiphany_mind", "_v1"].concat(), - ["epiphany_modeling", "_v1"].concat(), - ]; - for forbidden in forbidden { - assert!( - !source.contains(&forbidden), - "forbidden live surface: {forbidden}" - ); - } - let mutation_mouth = ["seed_abandoned_memory_semantic_projection", "_for_smoke"].concat(); - assert!(!source.contains(&mutation_mouth)); - assert!(source.contains("inspect_memory_semantic_recovery_for_smoke")); - assert!(source.contains("Command::new(&projector)")); - assert!(source.contains("Command::new(exe)")); - let manifest = include_str!("../../Cargo.toml"); - assert!(manifest.contains("required-features = [\"semantic-recovery-smoke\"]")); - } -} diff --git a/epiphany-core/src/bin/epiphany-state-ledger-store.rs b/epiphany-core/src/bin/epiphany-state-ledger-store.rs deleted file mode 100644 index 02209c3a..00000000 --- a/epiphany-core/src/bin/epiphany-state-ledger-store.rs +++ /dev/null @@ -1,136 +0,0 @@ -use anyhow::Context; -use anyhow::Result; -use epiphany_core::EpiphanyBranchRecord; -use epiphany_core::EpiphanyLedgerEvidenceRecord; -use epiphany_core::add_state_branch; -use epiphany_core::append_state_evidence; -use epiphany_core::close_state_branch; -use epiphany_core::state_ledger_status; -use std::env; -use std::path::PathBuf; - -fn main() -> Result<()> { - let mut args = env::args().skip(1); - let Some(command) = args.next() else { - print_usage(); - std::process::exit(2); - }; - match command.as_str() { - "status" => { - let store = require_path_arg(&mut args, "--store")?; - print_json(&state_ledger_status(store)?)?; - } - "add-evidence" => { - let store = require_path_arg(&mut args, "--store")?; - let record = EpiphanyLedgerEvidenceRecord { - ts: require_string_arg(&mut args, "--ts")?, - evidence_type: require_string_arg(&mut args, "--type")?, - status: require_string_arg(&mut args, "--status")?, - note: require_string_arg(&mut args, "--note")?, - branch: optional_string_arg(&mut args, "--branch")?, - }; - print_json(&append_state_evidence(store, record)?)?; - } - "add-branch" => { - let store = require_path_arg(&mut args, "--store")?; - let id = require_string_arg(&mut args, "--id")?; - let hypothesis = require_string_arg(&mut args, "--hypothesis")?; - let (artifacts, note) = collect_branch_tail(&mut args)?; - let branch = EpiphanyBranchRecord { - id, - hypothesis, - status: "active".to_string(), - artifacts, - notes: note.unwrap_or_default(), - }; - print_json(&add_state_branch(store, branch)?)?; - } - "close-branch" => { - let store = require_path_arg(&mut args, "--store")?; - let id = require_string_arg(&mut args, "--id")?; - let status = require_string_arg(&mut args, "--status")?; - let note = optional_string_arg(&mut args, "--note")?; - print_json(&close_state_branch(store, &id, &status, note)?)?; - } - _ => { - print_usage(); - std::process::exit(2); - } - } - Ok(()) -} - -fn require_path_arg(args: &mut impl Iterator, name: &str) -> Result { - Ok(PathBuf::from(require_string_arg(args, name)?)) -} - -fn require_string_arg(args: &mut impl Iterator, name: &str) -> Result { - let Some(flag) = args.next() else { - anyhow::bail!("missing {name}"); - }; - if flag != name { - anyhow::bail!("expected {name}, got {flag}"); - } - args.next() - .with_context(|| format!("missing value for {name}")) -} - -fn optional_string_arg( - args: &mut impl Iterator, - name: &str, -) -> Result> { - let remaining = args.collect::>(); - let mut found = None; - let mut index = 0; - while index < remaining.len() { - if remaining[index] == name { - found = remaining.get(index + 1).cloned(); - index += 2; - } else { - anyhow::bail!("unexpected argument {}", remaining[index]); - } - } - Ok(found) -} - -fn collect_branch_tail( - args: &mut impl Iterator, -) -> Result<(Vec, Option)> { - let remaining = args.collect::>(); - let mut artifacts = Vec::new(); - let mut note = None; - let mut index = 0; - while index < remaining.len() { - if remaining[index] == "--artifact" { - artifacts.push( - remaining - .get(index + 1) - .cloned() - .context("missing value for --artifact")?, - ); - index += 2; - } else if remaining[index] == "--note" { - note = Some( - remaining - .get(index + 1) - .cloned() - .context("missing value for --note")?, - ); - index += 2; - } else { - anyhow::bail!("unexpected argument {}", remaining[index]); - } - } - Ok((artifacts, note)) -} - -fn print_json(value: &T) -> Result<()> { - println!("{}", serde_json::to_string_pretty(value)?); - Ok(()) -} - -fn print_usage() { - eprintln!( - "usage: epiphany-state-ledger-store ..." - ); -} diff --git a/epiphany-core/src/bin/epiphany-unity-bridge-smoke.rs b/epiphany-core/src/bin/epiphany-unity-bridge-smoke.rs deleted file mode 100644 index 7bd365e4..00000000 --- a/epiphany-core/src/bin/epiphany-unity-bridge-smoke.rs +++ /dev/null @@ -1,490 +0,0 @@ -use anyhow::Context; -use anyhow::Result; -use anyhow::anyhow; -use serde_json::Value; -use serde_json::json; -use std::env; -use std::fs; -use std::path::{Path, PathBuf}; -use std::process::Command; - -const PROJECT_VERSION: &str = "6000.1.10f1"; -const WRONG_VERSION: &str = "6000.4.2f1"; - -fn main() -> Result<()> { - let result = run_smoke()?; - println!("{}", serde_json::to_string_pretty(&result)?); - Ok(()) -} - -fn run_smoke() -> Result { - let root = env::current_dir().context("failed to resolve current dir")?; - let workspace = root.join(".epiphany-smoke").join("unity-bridge-workspace"); - let artifact_root = root.join(".epiphany-smoke").join("unity-bridge-artifacts"); - let fake_roots = root.join(".epiphany-smoke").join("unity-bridge-editors"); - let result_path = root - .join(".epiphany-smoke") - .join("unity-bridge-smoke-result.json"); - prepare_project(&workspace, false)?; - reset_path(&artifact_root)?; - reset_path(&fake_roots)?; - - let wrong_editor = create_fake_editor(&fake_roots, WRONG_VERSION)?; - let missing = run_bridge( - &root, - &workspace, - &artifact_root, - &fake_roots, - &["inspect"], - true, - )?; - require( - missing["status"] == "missingEditor", - "wrong-only Hub root should not satisfy pin", - )?; - require( - missing["projectVersion"] == PROJECT_VERSION, - "inspection should report the project-pinned version", - )?; - require_artifact(&missing, "unity-bridge-summary.json")?; - require_artifact(&missing, "unity-bridge-inspection.md")?; - - let exact_editor = create_fake_editor(&fake_roots, PROJECT_VERSION)?; - let resolved = run_bridge( - &root, - &workspace, - &artifact_root, - &fake_roots, - &["inspect"], - true, - )?; - require( - resolved["status"] == "resolved", - "exact editor path should resolve the pin", - )?; - require( - resolved["editorPath"] == exact_editor.to_string_lossy().as_ref(), - "inspection should resolve exact editor", - )?; - require( - resolved - .pointer("/editorBridge/exists") - .and_then(Value::as_bool) - == Some(false), - "fresh smoke project should not have bridge package yet", - )?; - - let planned = run_bridge( - &root, - &workspace, - &artifact_root, - &fake_roots, - &["run", "--dry-run", "--", "-nographics"], - true, - )?; - require( - planned["status"] == "resolved", - "dry run should still require exact editor", - )?; - require( - planned["runStatus"] == "planned", - "dry run should plan instead of executing", - )?; - let command = value_array(&planned, "command")?; - require( - command.first().and_then(Value::as_str) == Some(exact_editor.to_str().unwrap_or_default()), - "command should use exact editor path", - )?; - require_command_contains(command, "-batchmode")?; - require_command_contains(command, "-quit")?; - require_command_contains(command, "-projectPath")?; - require_command_contains(command, "-logFile")?; - require_command_contains(command, "-nographics")?; - require_command_contains(command, &workspace.to_string_lossy())?; - require( - !command - .iter() - .any(|item| item.as_str() == Some(wrong_editor.to_str().unwrap_or_default())), - "command must not use wrong editor", - )?; - require_artifact(&planned, "unity-command.json")?; - - let method_override = run_bridge( - &root, - &workspace, - &artifact_root, - &fake_roots, - &[ - "run", - "--dry-run", - "--", - "-ExecuteMethod", - "Epiphany.SmokeProbe", - ], - false, - )?; - require( - method_override["status"] == "error" - && method_override["error"] - .as_str() - .is_some_and(|error| error.contains("executeMethod")), - "caller-authored executeMethod must fail at the bridge owner boundary", - )?; - - let missing_package = run_bridge( - &root, - &workspace, - &artifact_root, - &fake_roots, - &[ - "probe", - "--dry-run", - "--operation", - "scene-facts", - "--scene", - "Assets/Scenes/Main.unity", - ], - false, - )?; - require( - missing_package["status"] == "missingEditorBridgePackage", - "named probes should require the resident editor package", - )?; - require( - missing_package["runStatus"] == "blocked", - "missing package probe should be blocked", - )?; - - prepare_project(&workspace, true)?; - let bridged = run_bridge( - &root, - &workspace, - &artifact_root, - &fake_roots, - &["inspect"], - true, - )?; - require( - bridged["status"] == "resolved", - "bridge project should still resolve exact editor", - )?; - require( - bridged - .pointer("/editorBridge/exists") - .and_then(Value::as_bool) - == Some(true), - "resident editor bridge should be detected", - )?; - - let scene_probe = run_bridge( - &root, - &workspace, - &artifact_root, - &fake_roots, - &[ - "probe", - "--dry-run", - "--operation", - "scene-facts", - "--scene", - "Assets/Scenes/Main.unity", - "--max-objects", - "25", - "--max-properties", - "12", - ], - true, - )?; - require( - scene_probe["runStatus"] == "planned", - "scene probe should plan under dry run", - )?; - let scene_command = value_array(&scene_probe, "command")?; - require( - scene_command.first().and_then(Value::as_str) - == Some(exact_editor.to_str().unwrap_or_default()), - "scene probe should use exact editor path", - )?; - require_command_contains(scene_command, "-executeMethod")?; - require_command_contains( - scene_command, - "GameCult.Epiphany.Unity.EpiphanyEditorBridge.RunProbe", - )?; - require_command_contains(scene_command, "-epiphanyArtifactDir")?; - let scene_artifact_path = path_value(&scene_probe, "artifactPath")?; - require_command_contains(scene_command, &scene_artifact_path.to_string_lossy())?; - require_command_contains(scene_command, "-epiphanyOperation")?; - require_command_contains(scene_command, "scene-facts")?; - require_command_contains(scene_command, "-epiphanyScene")?; - require_command_contains(scene_command, "Assets/Scenes/Main.unity")?; - require_command_contains(scene_command, "-epiphanyMaxObjects")?; - require_command_contains(scene_command, "25")?; - let scene_artifact = read_command_artifact(&scene_probe)?; - require( - scene_artifact["operation"] == "scene-facts", - "scene command artifact should name operation", - )?; - require( - array_contains(scene_artifact.get("expectedArtifacts"), "scene-facts.json"), - "scene facts artifact should be expected", - )?; - - let compilation = run_bridge( - &root, - &workspace, - &artifact_root, - &fake_roots, - &["check-compilation", "--dry-run"], - true, - )?; - require( - compilation["runStatus"] == "planned", - "compilation probe should plan under dry run", - )?; - let compilation_artifact = read_command_artifact(&compilation)?; - require( - array_contains( - compilation_artifact.get("expectedArtifacts"), - "compilation.json", - ), - "compilation artifact should be expected", - )?; - - let tests = run_bridge( - &root, - &workspace, - &artifact_root, - &fake_roots, - &[ - "run-tests", - "--dry-run", - "--platform", - "editmode", - "--filter", - "SmokeSuite", - ], - true, - )?; - require( - tests["runStatus"] == "planned", - "Unity test run should plan under dry run", - )?; - let test_command = value_array(&tests, "command")?; - require_command_contains(test_command, "-runTests")?; - require_command_contains(test_command, "-testPlatform")?; - require_command_contains(test_command, "editmode")?; - require_command_contains(test_command, "-testFilter")?; - require_command_contains(test_command, "SmokeSuite")?; - let tests_artifact = read_command_artifact(&tests)?; - require( - array_contains(tests_artifact.get("expectedArtifacts"), "test-results.xml"), - "test results should be expected", - )?; - - let blocked = run_bridge( - &root, - &workspace, - &artifact_root, - &fake_roots.join("missing"), - &["run", "--dry-run", "--", "-nographics"], - false, - )?; - require( - blocked["runStatus"] == "blocked", - "missing editor run should be blocked", - )?; - - let result = json!({ - "workspace": workspace, - "artifactRoot": artifact_root, - "missingStatus": missing["status"], - "editorResolutionStatus": resolved["status"], - "plannedStatus": planned["runStatus"], - "methodOverrideStatus": method_override["status"], - "missingPackageStatus": missing_package["status"], - "sceneProbeStatus": scene_probe["runStatus"], - "testStatus": tests["runStatus"], - "blockedStatus": blocked["runStatus"], - "resolvedEditor": resolved["editorPath"], - }); - write_json(&result_path, &result)?; - Ok(result) -} - -fn run_bridge( - root: &Path, - workspace: &Path, - artifact_root: &Path, - editor_roots: &Path, - args: &[&str], - expect_success: bool, -) -> Result { - let bridge = native_bridge_exe()?; - ensure_bridge_built(root, "epiphany-unity-bridge", &bridge)?; - let output = Command::new(bridge) - .current_dir(root) - .env("EPIPHANY_UNITY_EDITOR_ROOTS", editor_roots) - .arg(args[0]) - .arg("--project-path") - .arg(workspace) - .arg("--artifact-root") - .arg(artifact_root) - .args(&args[1..]) - .output() - .context("failed to run native unity bridge")?; - if expect_success { - require( - output.status.success(), - &format!( - "bridge should succeed, got {:?}: {}", - output.status.code(), - String::from_utf8_lossy(&output.stderr) - ), - )?; - } else { - require( - !output.status.success(), - "bridge should fail for blocked runtime execution", - )?; - } - serde_json::from_slice(&output.stdout).with_context(|| { - format!( - "bridge did not return JSON: {}", - String::from_utf8_lossy(&output.stdout) - ) - }) -} - -fn prepare_project(workspace: &Path, include_bridge: bool) -> Result<()> { - reset_path(workspace)?; - let settings = workspace.join("ProjectSettings"); - fs::create_dir_all(&settings)?; - fs::write( - settings.join("ProjectVersion.txt"), - format!( - "m_EditorVersion: {PROJECT_VERSION}\nm_EditorVersionWithRevision: {PROJECT_VERSION} (3c681a6c22ff)\n" - ), - )?; - if include_bridge { - let bridge = workspace - .join("Assets") - .join("Editor") - .join("Epiphany") - .join("EpiphanyEditorBridge.cs"); - fs::create_dir_all(bridge.parent().expect("bridge path has parent"))?; - fs::write( - bridge, - "// smoke marker for resident Epiphany editor bridge\n", - )?; - } - Ok(()) -} - -fn create_fake_editor(root: &Path, version: &str) -> Result { - let editor = root.join(version).join("Editor").join("Unity.exe"); - fs::create_dir_all(editor.parent().expect("editor path has parent"))?; - fs::write(&editor, "fake unity binary for dry-run smoke only\n")?; - Ok(editor) -} - -fn read_command_artifact(summary: &Value) -> Result { - let command_path = path_value(summary, "artifactPath")?.join("unity-command.json"); - require(command_path.exists(), "missing unity-command.json")?; - let bytes = fs::read(&command_path)?; - serde_json::from_slice(&bytes).context("failed to parse unity-command.json") -} - -fn require_artifact(summary: &Value, name: &str) -> Result<()> { - let artifact_path = path_value(summary, "artifactPath")?; - require( - artifact_path.join(name).exists(), - &format!("missing bridge artifact: {name}"), - ) -} - -fn require_command_contains(command: &[Value], value: &str) -> Result<()> { - require( - command.iter().any(|item| item.as_str() == Some(value)), - &format!("command should contain {value:?}: {command:?}"), - ) -} - -fn native_bridge_exe() -> Result { - let target_dir = env::var_os("CARGO_TARGET_DIR") - .unwrap_or_else(|| r"C:\Users\Meta\.cargo-target-codex".into()); - Ok(PathBuf::from(target_dir) - .join("debug") - .join(format!("epiphany-unity-bridge{}", env::consts::EXE_SUFFIX))) -} - -fn ensure_bridge_built(root: &Path, bin: &str, exe: &Path) -> Result<()> { - if exe.exists() { - return Ok(()); - } - let status = Command::new("cargo") - .current_dir(root) - .arg("build") - .arg("--manifest-path") - .arg(root.join("epiphany-core").join("Cargo.toml")) - .arg("--bin") - .arg(bin) - .status() - .with_context(|| format!("failed to build native {bin}"))?; - require( - status.success() && exe.exists(), - &format!("native {bin} executable was not built at {}", exe.display()), - ) -} - -fn value_array<'a>(value: &'a Value, key: &str) -> Result<&'a Vec> { - value - .get(key) - .and_then(Value::as_array) - .ok_or_else(|| anyhow!("missing array field {key}")) -} - -fn path_value(value: &Value, key: &str) -> Result { - value - .get(key) - .and_then(Value::as_str) - .map(PathBuf::from) - .ok_or_else(|| anyhow!("missing path field {key}")) -} - -fn array_contains(value: Option<&Value>, needle: &str) -> bool { - value - .and_then(Value::as_array) - .is_some_and(|items| items.iter().any(|item| item.as_str() == Some(needle))) -} - -fn reset_path(path: &Path) -> Result<()> { - if path.exists() { - let root = env::current_dir()?.join(".epiphany-smoke").canonicalize()?; - let resolved = path.canonicalize()?; - if resolved == root || !resolved.starts_with(&root) { - return Err(anyhow!( - "refusing to reset non-smoke path: {}", - path.display() - )); - } - fs::remove_dir_all(path)?; - } - fs::create_dir_all(path)?; - Ok(()) -} - -fn write_json(path: &Path, value: &Value) -> Result<()> { - if let Some(parent) = path.parent() { - fs::create_dir_all(parent)?; - } - fs::write(path, format!("{}\n", serde_json::to_string_pretty(value)?))?; - Ok(()) -} - -fn require(condition: bool, message: &str) -> Result<()> { - if condition { - Ok(()) - } else { - Err(anyhow!("{message}")) - } -} diff --git a/epiphany-core/src/bin/epiphany-unity-bridge.rs b/epiphany-core/src/bin/epiphany-unity-bridge.rs deleted file mode 100644 index 04feeb15..00000000 --- a/epiphany-core/src/bin/epiphany-unity-bridge.rs +++ /dev/null @@ -1,1035 +0,0 @@ -use anyhow::Context; -use anyhow::Result; -use anyhow::anyhow; -use serde_json::json; -use std::collections::HashSet; -use std::env; -use std::fs; -use std::path::{Path, PathBuf}; -use std::process::Command; -use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; - -const PROJECT_VERSION_RELATIVE: &str = "ProjectSettings/ProjectVersion.txt"; -const UNITY_EDITOR_RELATIVE: &[&str] = &["Editor", "Unity.exe"]; -const EDITOR_BRIDGE_RELATIVE: &str = "Assets/Editor/Epiphany/EpiphanyEditorBridge.cs"; -const EDITOR_BRIDGE_EXECUTE_METHOD: &str = "GameCult.Epiphany.Unity.EpiphanyEditorBridge.RunProbe"; -const FORBIDDEN_EXTRA_ARGS: &[&str] = &[ - "-batchmode", - "-quit", - "-projectpath", - "-logfile", - "-executemethod", -]; - -fn main() -> Result<()> { - let mut args = env::args().skip(1); - let Some(command) = args.next() else { - return Err(anyhow!( - "usage: epiphany-unity-bridge " - )); - }; - match run_command(&command, args.collect()) { - Ok((code, value)) => { - println!("{}", serde_json::to_string_pretty(&value)?); - std::process::exit(code); - } - Err(error) => { - println!( - "{}", - serde_json::to_string_pretty(&json!({ - "status": "error", - "error": error.to_string(), - }))? - ); - std::process::exit(1); - } - } -} - -fn run_command(command: &str, args: Vec) -> Result<(i32, serde_json::Value)> { - let options = UnityOptions::parse(args)?; - let result = match command { - "inspect" => inspect_unity(&options)?, - "run" => run_unity(&options)?, - "check-compilation" => run_named_probe(&options, "check-compilation")?, - "probe" => { - let operation = options - .operation - .as_deref() - .ok_or_else(|| anyhow!("probe requires --operation"))?; - run_named_probe(&options, operation)? - } - "run-tests" => test_unity(&options)?, - "guidance" => json!({ "guidance": bridge_guidance(&options.project_path)? }), - _ => return Err(anyhow!("unsupported command: {command}")), - }; - let blocked = result["runStatus"].as_str() == Some("blocked"); - Ok((if blocked { 2 } else { 0 }, result)) -} - -#[derive(Debug)] -struct UnityOptions { - project_path: PathBuf, - artifact_root: PathBuf, - artifact_dir: Option, - label: String, - timeout_seconds: u64, - dry_run: bool, - unity_args: Vec, - operation: Option, - scene: Option, - asset: Option, - guid: Option, - max_objects: Option, - max_properties: Option, - max_dependencies: Option, - platform: String, - filter: Option, -} - -impl UnityOptions { - fn parse(args: Vec) -> Result { - let root = repo_root()?; - let mut options = Self { - project_path: env::current_dir()?, - artifact_root: root.join(".epiphany-gui").join("runtime"), - artifact_dir: None, - label: "unity-bridge-run".to_string(), - timeout_seconds: 600, - dry_run: false, - unity_args: Vec::new(), - operation: None, - scene: None, - asset: None, - guid: None, - max_objects: None, - max_properties: None, - max_dependencies: None, - platform: "editmode".to_string(), - filter: None, - }; - let mut iter = args.into_iter().peekable(); - while let Some(arg) = iter.next() { - if arg == "--" { - options.unity_args.extend(iter); - break; - } - match arg.as_str() { - "--project-path" => options.project_path = next_path(&mut iter, &arg)?, - "--artifact-root" => options.artifact_root = next_path(&mut iter, &arg)?, - "--artifact-dir" => options.artifact_dir = Some(next_path(&mut iter, &arg)?), - "--label" => options.label = next_value(&mut iter, &arg)?, - "--timeout-seconds" => { - options.timeout_seconds = next_value(&mut iter, &arg)?.parse()? - } - "--dry-run" => options.dry_run = true, - "--operation" => options.operation = Some(next_value(&mut iter, &arg)?), - "--scene" => options.scene = Some(next_value(&mut iter, &arg)?), - "--asset" => options.asset = Some(next_value(&mut iter, &arg)?), - "--guid" => options.guid = Some(next_value(&mut iter, &arg)?), - "--max-objects" => { - options.max_objects = Some(next_value(&mut iter, &arg)?.parse()?) - } - "--max-properties" => { - options.max_properties = Some(next_value(&mut iter, &arg)?.parse()?) - } - "--max-dependencies" => { - options.max_dependencies = Some(next_value(&mut iter, &arg)?.parse()?) - } - "--platform" => options.platform = next_value(&mut iter, &arg)?, - "--filter" => options.filter = Some(next_value(&mut iter, &arg)?), - _ => options.unity_args.push(arg), - } - } - Ok(options) - } -} - -fn next_value(iter: &mut impl Iterator, name: &str) -> Result { - iter.next() - .ok_or_else(|| anyhow!("{name} requires a value")) -} - -fn next_path(iter: &mut impl Iterator, name: &str) -> Result { - Ok(PathBuf::from(next_value(iter, name)?)) -} - -fn repo_root() -> Result { - Ok(Path::new(env!("CARGO_MANIFEST_DIR")) - .parent() - .ok_or_else(|| anyhow!("epiphany-core has no parent repo root"))? - .to_path_buf()) -} - -fn now_iso() -> String { - chrono::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true) -} - -fn clean_windows_path(value: &str) -> String { - value.strip_prefix(r"\\?\").unwrap_or(value).to_string() -} - -fn path_string(path: &Path) -> String { - clean_windows_path(&path.display().to_string()) -} - -fn maybe_resolve(path: &Path) -> String { - path_string(&path.canonicalize().unwrap_or_else(|_| path.to_path_buf())) -} - -fn artifact_dir(root: &Path, action: &str) -> Result { - let nanos = SystemTime::now().duration_since(UNIX_EPOCH)?.as_nanos(); - Ok(root - .canonicalize() - .unwrap_or_else(|_| root.to_path_buf()) - .join(format!("unity-{action}-{nanos}-{}", std::process::id()))) -} - -fn write_json(path: &Path, value: &serde_json::Value) -> Result<()> { - if let Some(parent) = path.parent() { - fs::create_dir_all(parent)?; - } - fs::write(path, format!("{}\n", serde_json::to_string_pretty(value)?)) - .with_context(|| format!("failed to write {}", path.display())) -} - -fn write_text(path: &Path, value: &str) -> Result<()> { - if let Some(parent) = path.parent() { - fs::create_dir_all(parent)?; - } - fs::write(path, value).with_context(|| format!("failed to write {}", path.display())) -} - -fn read_unity_project_version(project_path: &Path) -> serde_json::Value { - let version_path = project_path.join(PROJECT_VERSION_RELATIVE); - let mut result = json!({ - "path": path_string(&version_path), - "exists": version_path.exists(), - "editorVersion": null, - "editorVersionWithRevision": null, - }); - if !version_path.exists() { - result["status"] = json!("missingProjectVersion"); - result["note"] = json!("ProjectSettings/ProjectVersion.txt was not found."); - return result; - } - let Ok(raw) = fs::read_to_string(&version_path) else { - result["status"] = json!("missingProjectVersion"); - result["note"] = json!("ProjectVersion.txt could not be read."); - return result; - }; - for line in raw.lines() { - if let Some(value) = line.strip_prefix("m_EditorVersion:") { - result["editorVersion"] = json!(value.trim()); - } else if let Some(value) = line.strip_prefix("m_EditorVersionWithRevision:") { - result["editorVersionWithRevision"] = json!(value.trim()); - } - } - if result["editorVersion"] - .as_str() - .is_some_and(|value| !value.is_empty()) - { - result["status"] = json!("pinned"); - } else { - result["status"] = json!("missingProjectVersion"); - result["note"] = json!("ProjectVersion.txt did not contain m_EditorVersion."); - } - result -} - -fn default_unity_editor_roots() -> Vec { - let mut roots = Vec::new(); - if let Ok(value) = env::var("EPIPHANY_UNITY_EDITOR_ROOTS") { - roots.extend(env::split_paths(&value)); - } - roots.push( - PathBuf::from(env::var("ProgramFiles").unwrap_or_else(|_| r"C:\Program Files".to_string())) - .join("Unity") - .join("Hub") - .join("Editor"), - ); - roots.push( - PathBuf::from( - env::var("ProgramFiles(x86)").unwrap_or_else(|_| r"C:\Program Files (x86)".to_string()), - ) - .join("Unity") - .join("Hub") - .join("Editor"), - ); - dedupe_paths(roots) -} - -fn dedupe_paths(paths: Vec) -> Vec { - let mut seen = HashSet::new(); - let mut result = Vec::new(); - for path in paths { - let key = maybe_resolve(&path).to_lowercase(); - if seen.insert(key) { - result.push(path); - } - } - result -} - -fn unity_editor_relative() -> PathBuf { - UNITY_EDITOR_RELATIVE.iter().collect() -} - -fn editor_path_for_version(root: &Path, version: &str) -> PathBuf { - if root.file_name().and_then(|value| value.to_str()) == Some(version) { - root.join(unity_editor_relative()) - } else { - root.join(version).join(unity_editor_relative()) - } -} - -fn installed_unity_editors(roots: &[PathBuf]) -> Result> { - let mut editors = Vec::new(); - let rel = unity_editor_relative(); - for root in roots { - if !root.exists() { - continue; - } - let direct = root.join(&rel); - if direct.exists() { - editors.push(json!({ - "version": root.file_name().and_then(|value| value.to_str()), - "root": path_string(root), - "editorPath": path_string(&direct), - })); - continue; - } - for entry in fs::read_dir(root)? { - let child = entry?.path(); - if !child.is_dir() { - continue; - } - let editor = child.join(&rel); - if editor.exists() { - editors.push(json!({ - "version": child.file_name().and_then(|value| value.to_str()), - "root": path_string(&child), - "editorPath": path_string(&editor), - })); - } - } - } - Ok(editors) -} - -fn detect_editor_bridge(project_path: &Path) -> serde_json::Value { - let package_path = project_path.join(EDITOR_BRIDGE_RELATIVE); - json!({ - "kind": "epiphanyUnityEditorBridge", - "path": path_string(&package_path), - "relativePath": EDITOR_BRIDGE_RELATIVE, - "exists": package_path.exists(), - "executeMethod": EDITOR_BRIDGE_EXECUTE_METHOD, - }) -} - -fn resolve_unity_editor(project_path: &Path) -> Result { - let project_path = project_path - .canonicalize() - .unwrap_or_else(|_| project_path.to_path_buf()); - let version = read_unity_project_version(&project_path); - let roots = default_unity_editor_roots(); - let editors = installed_unity_editors(&roots)?; - let mut result = json!({ - "kind": "unity", - "projectPath": path_string(&project_path), - "generatedAt": now_iso(), - "versionFile": version, - "searchRoots": roots.iter().map(|path| path_string(path)).collect::>(), - "installedEditors": editors, - "projectVersion": null, - "projectVersionWithRevision": null, - "editorPath": null, - "status": "missingProjectVersion", - "note": "ProjectSettings/ProjectVersion.txt was not found or did not pin a Unity editor.", - "editorBridge": detect_editor_bridge(&project_path), - }); - result["projectVersion"] = result["versionFile"]["editorVersion"].clone(); - result["projectVersionWithRevision"] = - result["versionFile"]["editorVersionWithRevision"].clone(); - let Some(project_version) = result["projectVersion"].as_str().map(ToOwned::to_owned) else { - return Ok(result); - }; - if project_version.is_empty() { - return Ok(result); - } - let exact_candidates: Vec = roots - .iter() - .map(|root| editor_path_for_version(root, &project_version)) - .collect(); - let exact = exact_candidates.iter().find(|path| path.exists()); - result["candidatePaths"] = json!( - exact_candidates - .iter() - .map(|path| path_string(path)) - .collect::>() - ); - if let Some(exact) = exact { - result["status"] = json!("resolved"); - result["editorPath"] = json!(path_string(exact)); - result["note"] = json!(format!( - "Project pins Unity {project_version}; exact editor resolved at {}.", - path_string(exact) - )); - } else { - let installed_versions = result["installedEditors"] - .as_array() - .into_iter() - .flatten() - .filter_map(|item| item["version"].as_str()) - .collect::>() - .join(", "); - result["status"] = json!("missingEditor"); - result["note"] = json!(format!( - "Project pins Unity {project_version}, but no exact editor was found. Installed Hub versions: {}.", - if installed_versions.is_empty() { - "none detected" - } else { - &installed_versions - } - )); - } - Ok(result) -} - -fn render_inspection(summary: &serde_json::Value) -> String { - let installed_lines = summary["installedEditors"] - .as_array() - .filter(|items| !items.is_empty()) - .map(|items| { - items - .iter() - .map(|item| { - format!( - "- {}: {}", - item["version"].as_str().unwrap_or("unknown"), - item["editorPath"].as_str().unwrap_or("unknown") - ) - }) - .collect::>() - }) - .unwrap_or_else(|| vec!["- none detected".to_string()]); - let candidate_lines = summary["candidatePaths"] - .as_array() - .filter(|items| !items.is_empty()) - .map(|items| { - items - .iter() - .filter_map(|item| item.as_str()) - .map(|item| format!("- {item}")) - .collect::>() - }) - .unwrap_or_else(|| vec!["- none".to_string()]); - format!( - "# Unity Runtime Bridge\n\nStatus: {}\nProject: {}\nProject version: {}\nEditor path: {}\n\nInstalled Hub editors:\n{}\n\nExact candidate paths:\n{}\n\nNote: {}\n", - summary["status"].as_str().unwrap_or("unknown"), - summary["projectPath"].as_str().unwrap_or("unknown"), - summary["projectVersion"].as_str().unwrap_or("none"), - summary["editorPath"].as_str().unwrap_or("none"), - installed_lines.join("\n"), - candidate_lines.join("\n"), - summary["note"].as_str().unwrap_or(""), - ) -} - -fn write_inspection_artifacts(directory: &Path, summary: &serde_json::Value) -> Result<()> { - write_json(&directory.join("unity-bridge-summary.json"), summary)?; - write_text( - &directory.join("unity-bridge-inspection.md"), - &render_inspection(summary), - ) -} - -fn validate_unity_args(values: &[String]) -> Result<()> { - let lowered: HashSet = values.iter().map(|value| value.to_lowercase()).collect(); - let forbidden = FORBIDDEN_EXTRA_ARGS - .iter() - .filter(|value| lowered.contains(**value)) - .copied() - .collect::>(); - if !forbidden.is_empty() { - return Err(anyhow!( - "Unity bridge owns batchmode, quit, projectPath, logFile, and executeMethod; remove extra args: {}", - forbidden.join(", ") - )); - } - Ok(()) -} - -fn build_unity_command( - summary: &serde_json::Value, - extra_args: &[String], - log_path: Option<&Path>, -) -> Result> { - let editor_path = summary["editorPath"] - .as_str() - .ok_or_else(|| anyhow!("Unity editor path is unavailable."))?; - let project_path = summary["projectPath"] - .as_str() - .ok_or_else(|| anyhow!("Unity project path is unavailable."))?; - let mut command = vec![ - editor_path.to_string(), - "-batchmode".to_string(), - "-quit".to_string(), - "-projectPath".to_string(), - project_path.to_string(), - ]; - if let Some(log_path) = log_path { - command.extend(["-logFile".to_string(), path_string(log_path)]); - } - command.extend(extra_args.iter().cloned()); - Ok(command) -} - -fn build_caller_unity_command( - summary: &serde_json::Value, - extra_args: &[String], - log_path: Option<&Path>, -) -> Result> { - validate_unity_args(extra_args)?; - build_unity_command(summary, extra_args, log_path) -} - -fn editor_bridge_present(summary: &serde_json::Value) -> bool { - summary["editorBridge"]["exists"].as_bool() == Some(true) -} - -fn named_probe_extra_args( - options: &UnityOptions, - operation: &str, - directory: &Path, -) -> Vec { - let mut args = vec![ - "-executeMethod".to_string(), - EDITOR_BRIDGE_EXECUTE_METHOD.to_string(), - "-epiphanyArtifactDir".to_string(), - path_string(directory), - "-epiphanyOperation".to_string(), - operation.to_string(), - ]; - for (value, flag) in [ - ( - options.scene.as_ref().map(ToString::to_string), - "-epiphanyScene", - ), - ( - options.asset.as_ref().map(ToString::to_string), - "-epiphanyAsset", - ), - ( - options.guid.as_ref().map(ToString::to_string), - "-epiphanyGuid", - ), - ( - options.max_objects.map(|value| value.to_string()), - "-epiphanyMaxObjects", - ), - ( - options.max_properties.map(|value| value.to_string()), - "-epiphanyMaxProperties", - ), - ( - options.max_dependencies.map(|value| value.to_string()), - "-epiphanyMaxDependencies", - ), - ] { - if let Some(value) = value { - args.extend([flag.to_string(), value]); - } - } - args -} - -fn expected_artifacts(operation: &str) -> Vec<&'static str> { - match operation { - "inspect-project" => vec![ - "project-facts.json", - "unity-probe-result.json", - "unity-probe-result.md", - ], - "check-compilation" => vec![ - "compilation.json", - "unity-probe-result.json", - "unity-probe-result.md", - ], - "scene-facts" => vec![ - "scene-facts.json", - "unity-probe-result.json", - "unity-probe-result.md", - ], - "prefab-facts" => vec![ - "prefab-facts.json", - "unity-probe-result.json", - "unity-probe-result.md", - ], - "serialized-object" => vec![ - "serialized-object-facts.json", - "unity-probe-result.json", - "unity-probe-result.md", - ], - "reference-search" => vec![ - "reference-search.json", - "unity-probe-result.json", - "unity-probe-result.md", - ], - "run-tests" => vec!["test-results.xml", "unity.log"], - _ => Vec::new(), - } -} - -fn probe_description(operation: &str) -> &'static str { - match operation { - "inspect-project" => "Capture Unity project/editor/build/render-pipeline facts.", - "check-compilation" => { - "Check whether the editor bridge can run after Unity script compilation." - } - "scene-facts" => { - "Capture scene hierarchy, components, prefab links, and serialized fields." - } - "prefab-facts" => { - "Capture prefab hierarchy, nested instances, overrides, and serialized fields." - } - "serialized-object" => "Capture SerializedObject properties for one asset.", - "reference-search" => "Search Unity asset dependencies for references to a GUID or asset.", - _ => "Run a named Unity editor bridge probe.", - } -} - -fn write_command_artifact( - directory: &Path, - command: &[String], - dry_run: bool, - operation: &str, - expected_artifacts: Vec<&str>, -) -> Result<()> { - write_json( - &directory.join("unity-command.json"), - &json!({ - "command": command, - "dryRun": dry_run, - "operation": operation, - "expectedArtifacts": expected_artifacts, - }), - ) -} - -fn run_unity(options: &UnityOptions) -> Result { - let directory = options - .artifact_dir - .clone() - .map(Ok) - .unwrap_or_else(|| artifact_dir(&options.artifact_root, "run"))?; - let project_path = options - .project_path - .canonicalize() - .unwrap_or_else(|_| options.project_path.clone()); - let mut summary = resolve_unity_editor(&project_path)?; - summary["artifactPath"] = json!(path_string(&directory)); - summary["operation"] = json!("run"); - summary["label"] = json!(options.label); - summary["unityArgs"] = json!(options.unity_args); - if summary["status"].as_str() != Some("resolved") { - summary["runStatus"] = json!("blocked"); - summary["note"] = json!(format!( - "{} Runtime execution refused; install the exact pinned editor or use inspect artifacts as evidence of the missing runtime.", - summary["note"].as_str().unwrap_or("") - )); - write_inspection_artifacts(&directory, &summary)?; - return Ok(summary); - } - let log_path = directory.join("unity.log"); - let command = build_caller_unity_command(&summary, &options.unity_args, Some(&log_path))?; - summary["command"] = json!(command); - summary["logPath"] = json!(path_string(&log_path)); - if options.dry_run { - summary["runStatus"] = json!("planned"); - summary["returncode"] = serde_json::Value::Null; - write_inspection_artifacts(&directory, &summary)?; - write_command_artifact( - &directory, - summary["command"] - .as_array() - .unwrap() - .iter() - .filter_map(|v| v.as_str().map(ToOwned::to_owned)) - .collect::>() - .as_slice(), - true, - "run", - Vec::new(), - )?; - return Ok(summary); - } - execute_command( - &mut summary, - &directory, - &project_path, - options.timeout_seconds, - )?; - write_inspection_artifacts(&directory, &summary)?; - write_command_artifact( - &directory, - summary["command"] - .as_array() - .unwrap() - .iter() - .filter_map(|v| v.as_str().map(ToOwned::to_owned)) - .collect::>() - .as_slice(), - false, - "run", - Vec::new(), - )?; - Ok(summary) -} - -fn run_named_probe(options: &UnityOptions, operation: &str) -> Result { - let allowed = [ - "inspect-project", - "check-compilation", - "scene-facts", - "prefab-facts", - "serialized-object", - "reference-search", - ]; - if !allowed.contains(&operation) { - return Err(anyhow!("unsupported probe operation: {operation}")); - } - let directory = - options.artifact_dir.clone().map(Ok).unwrap_or_else(|| { - artifact_dir(&options.artifact_root, &operation.replace('-', "_")) - })?; - let project_path = options - .project_path - .canonicalize() - .unwrap_or_else(|_| options.project_path.clone()); - let mut summary = resolve_unity_editor(&project_path)?; - summary["artifactPath"] = json!(path_string(&directory)); - summary["operation"] = json!(operation); - summary["probe"] = json!({ - "description": probe_description(operation), - "expectedArtifacts": expected_artifacts(operation), - }); - if summary["status"].as_str() != Some("resolved") { - summary["runStatus"] = json!("blocked"); - summary["note"] = json!(format!( - "{} Runtime/editor probe refused; install the exact pinned editor or use inspect artifacts as evidence of the missing runtime.", - summary["note"].as_str().unwrap_or("") - )); - write_inspection_artifacts(&directory, &summary)?; - return Ok(summary); - } - if !editor_bridge_present(&summary) { - let bridge_path = summary["editorBridge"]["path"] - .as_str() - .map(ToOwned::to_owned) - .unwrap_or_else(|| path_string(&project_path.join(EDITOR_BRIDGE_RELATIVE))); - summary["status"] = json!("missingEditorBridgePackage"); - summary["runStatus"] = json!("blocked"); - summary["note"] = json!(format!( - "The Epiphany Unity editor package is missing. Add {bridge_path} before running editor-resident probes." - )); - write_inspection_artifacts(&directory, &summary)?; - return Ok(summary); - } - let extra_args = named_probe_extra_args(options, operation, &directory); - let log_path = directory.join("unity.log"); - let command = build_unity_command(&summary, &extra_args, Some(&log_path))?; - summary["command"] = json!(command); - summary["logPath"] = json!(path_string(&log_path)); - if options.dry_run { - summary["runStatus"] = json!("planned"); - summary["returncode"] = serde_json::Value::Null; - write_inspection_artifacts(&directory, &summary)?; - write_command_artifact( - &directory, - summary["command"] - .as_array() - .unwrap() - .iter() - .filter_map(|v| v.as_str().map(ToOwned::to_owned)) - .collect::>() - .as_slice(), - true, - operation, - expected_artifacts(operation), - )?; - return Ok(summary); - } - execute_command( - &mut summary, - &directory, - &project_path, - options.timeout_seconds, - )?; - write_inspection_artifacts(&directory, &summary)?; - write_command_artifact( - &directory, - summary["command"] - .as_array() - .unwrap() - .iter() - .filter_map(|v| v.as_str().map(ToOwned::to_owned)) - .collect::>() - .as_slice(), - false, - operation, - expected_artifacts(operation), - )?; - Ok(summary) -} - -fn test_unity(options: &UnityOptions) -> Result { - let directory = options - .artifact_dir - .clone() - .map(Ok) - .unwrap_or_else(|| artifact_dir(&options.artifact_root, "run_tests"))?; - let project_path = options - .project_path - .canonicalize() - .unwrap_or_else(|_| options.project_path.clone()); - let mut summary = resolve_unity_editor(&project_path)?; - summary["artifactPath"] = json!(path_string(&directory)); - summary["operation"] = json!("run-tests"); - summary["testPlatform"] = json!(options.platform); - summary["testFilter"] = json!(options.filter); - if summary["status"].as_str() != Some("resolved") { - summary["runStatus"] = json!("blocked"); - summary["note"] = json!(format!( - "{} Unity tests refused; install the exact pinned editor or use inspect artifacts as evidence of the missing runtime.", - summary["note"].as_str().unwrap_or("") - )); - write_inspection_artifacts(&directory, &summary)?; - return Ok(summary); - } - let log_path = directory.join("unity.log"); - let test_results = directory.join("test-results.xml"); - let mut extra_args = vec![ - "-runTests".to_string(), - "-testPlatform".to_string(), - options.platform.clone(), - "-testResults".to_string(), - path_string(&test_results), - ]; - if let Some(filter) = &options.filter { - extra_args.extend(["-testFilter".to_string(), filter.clone()]); - } - let command = build_unity_command(&summary, &extra_args, Some(&log_path))?; - summary["command"] = json!(command); - summary["logPath"] = json!(path_string(&log_path)); - summary["testResultsPath"] = json!(path_string(&test_results)); - if options.dry_run { - summary["runStatus"] = json!("planned"); - summary["returncode"] = serde_json::Value::Null; - write_inspection_artifacts(&directory, &summary)?; - write_command_artifact( - &directory, - summary["command"] - .as_array() - .unwrap() - .iter() - .filter_map(|v| v.as_str().map(ToOwned::to_owned)) - .collect::>() - .as_slice(), - true, - "run-tests", - expected_artifacts("run-tests"), - )?; - return Ok(summary); - } - execute_command( - &mut summary, - &directory, - &project_path, - options.timeout_seconds, - )?; - write_inspection_artifacts(&directory, &summary)?; - write_command_artifact( - &directory, - summary["command"] - .as_array() - .unwrap() - .iter() - .filter_map(|v| v.as_str().map(ToOwned::to_owned)) - .collect::>() - .as_slice(), - false, - "run-tests", - expected_artifacts("run-tests"), - )?; - Ok(summary) -} - -fn execute_command( - summary: &mut serde_json::Value, - directory: &Path, - project_path: &Path, - timeout_seconds: u64, -) -> Result<()> { - let command = summary["command"] - .as_array() - .ok_or_else(|| anyhow!("summary command is not an array"))? - .iter() - .filter_map(|value| value.as_str().map(ToOwned::to_owned)) - .collect::>(); - let (program, args) = command - .split_first() - .ok_or_else(|| anyhow!("empty Unity command"))?; - let stdout_path = directory.join("unity-stdout.log"); - let stderr_path = directory.join("unity-stderr.log"); - fs::create_dir_all(directory)?; - let started = Instant::now(); - let mut child = Command::new(program) - .args(args) - .current_dir(project_path) - .stdout(std::process::Stdio::piped()) - .stderr(std::process::Stdio::piped()) - .spawn()?; - let timeout = Duration::from_secs(timeout_seconds); - loop { - if let Some(status) = child.try_wait()? { - let output = child.wait_with_output()?; - fs::write(&stdout_path, output.stdout)?; - fs::write(&stderr_path, output.stderr)?; - let code = status.code(); - summary["runStatus"] = json!(if status.success() { "passed" } else { "failed" }); - summary["returncode"] = json!(code); - summary["durationSeconds"] = - json!((started.elapsed().as_millis() as f64 / 1000.0).round()); - summary["stdoutPath"] = json!(path_string(&stdout_path)); - summary["stderrPath"] = json!(path_string(&stderr_path)); - return Ok(()); - } - if started.elapsed() > timeout { - let _ = child.kill(); - let output = child.wait_with_output()?; - if !output.stdout.is_empty() { - fs::write(&stdout_path, output.stdout)?; - summary["stdoutPath"] = json!(path_string(&stdout_path)); - } - if !output.stderr.is_empty() { - fs::write(&stderr_path, output.stderr)?; - summary["stderrPath"] = json!(path_string(&stderr_path)); - } - summary["runStatus"] = json!("timedOut"); - summary["returncode"] = serde_json::Value::Null; - summary["durationSeconds"] = - json!((started.elapsed().as_millis() as f64 / 1000.0).round()); - summary["note"] = json!(format!( - "Unity command timed out after {timeout_seconds} seconds." - )); - return Ok(()); - } - std::thread::sleep(Duration::from_millis(100)); - } -} - -fn inspect_unity(options: &UnityOptions) -> Result { - let directory = options - .artifact_dir - .clone() - .map(Ok) - .unwrap_or_else(|| artifact_dir(&options.artifact_root, "inspect"))?; - let mut summary = resolve_unity_editor(&options.project_path)?; - summary["artifactPath"] = json!(path_string(&directory)); - summary["operation"] = json!("inspect"); - write_inspection_artifacts(&directory, &summary)?; - Ok(summary) -} - -fn bridge_guidance(project_path: &Path) -> Result { - let summary = resolve_unity_editor(project_path)?; - let inspect_command = format!( - "`epiphany-unity-bridge inspect --project-path {}`", - project_path.display() - ); - if summary["status"].as_str() == Some("resolved") { - if editor_bridge_present(&summary) { - Ok(format!( - "- Unity bridge: project pins {} and the exact editor resolved to `{}`. If Unity execution is needed, use named bridge operations such as `epiphany-unity-bridge probe --project-path {} --operation scene-facts --scene ` or `epiphany-unity-bridge check-compilation --project-path {}`. The bridge owns -batchmode, -quit, -projectPath, -logFile, -executeMethod, and artifacts. Do not invoke `Unity`, `Unity.exe`, default installs, or PATH-resolved editors directly, and do not refactor Unity-owned scene/prefab state as raw text when the editor bridge can inspect it.", - summary["projectVersion"].as_str().unwrap_or("unknown"), - summary["editorPath"].as_str().unwrap_or("unknown"), - project_path.display(), - project_path.display(), - )) - } else { - Ok(format!( - "- Unity bridge: project pins {} and the exact editor resolved to `{}`, but the resident editor package is missing. Add `{}` before claiming scene, prefab, or runtime facts. Do not invoke `Unity`, `Unity.exe`, default installs, or PATH-resolved editors directly.", - summary["projectVersion"].as_str().unwrap_or("unknown"), - summary["editorPath"].as_str().unwrap_or("unknown"), - EDITOR_BRIDGE_RELATIVE, - )) - } - } else { - Ok(format!( - "- Unity bridge: {} Run {} to write an auditable runtime evidence artifact. Do not invoke `Unity`, `Unity.exe`, default installs, or PATH-resolved editors directly; if runtime parity is needed, stop with the bridge artifact as the evidence gap.", - summary["note"] - .as_str() - .unwrap_or("Unity bridge is blocked."), - inspect_command, - )) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn caller_cannot_replace_bridge_owned_execute_method() { - let summary = json!({ - "editorPath": "C:/Unity/Editor/Unity.exe", - "projectPath": "C:/fixture/project", - }); - let error = build_caller_unity_command( - &summary, - &["-ExecuteMethod".into(), "Fixture.Arbitrary.Run".into()], - None, - ) - .expect_err("caller-authored executeMethod must be refused"); - - assert!(error.to_string().contains("executeMethod")); - } - - #[test] - fn named_probe_keeps_exact_bridge_owned_execute_method() -> Result<()> { - let summary = json!({ - "editorPath": "C:/Unity/Editor/Unity.exe", - "projectPath": "C:/fixture/project", - }); - let options = UnityOptions { - project_path: PathBuf::from("C:/fixture/project"), - artifact_root: PathBuf::from("C:/fixture/artifacts"), - artifact_dir: None, - label: "probe".into(), - timeout_seconds: 60, - dry_run: true, - unity_args: Vec::new(), - operation: Some("scene-facts".into()), - scene: Some("Assets/Scene.unity".into()), - asset: None, - guid: None, - max_objects: None, - max_properties: None, - max_dependencies: None, - platform: "editmode".into(), - filter: None, - }; - let args = - named_probe_extra_args(&options, "scene-facts", Path::new("C:/fixture/artifacts")); - let command = build_unity_command(&summary, &args, None)?; - let method_index = command - .iter() - .position(|arg| arg == "-executeMethod") - .expect("named probe must own executeMethod"); - - assert_eq!(command[method_index + 1], EDITOR_BRIDGE_EXECUTE_METHOD); - Ok(()) - } -} diff --git a/epiphany-core/src/bin/epiphany-verse-query.rs b/epiphany-core/src/bin/epiphany-verse-query.rs deleted file mode 100644 index 24df335a..00000000 --- a/epiphany-core/src/bin/epiphany-verse-query.rs +++ /dev/null @@ -1,7814 +0,0 @@ -use anyhow::Context; -use anyhow::Result; -use chrono::DateTime; -use chrono::Utc; -use epiphany_core::EPIPHANY_CANONICAL_SWARM_BRAKE_ID; -use epiphany_core::EPIPHANY_CANONICAL_SWARM_BRAKE_OWNER; -use epiphany_core::EPIPHANY_CULTMESH_DAEMON_RESTART_POLICY_SCHEMA_VERSION; -use epiphany_core::EPIPHANY_CULTMESH_DAEMON_SERVICE_LIFECYCLE_RECEIPT_SCHEMA_VERSION; -use epiphany_core::EPIPHANY_CULTMESH_INTERNAL_VERSE_ID; -use epiphany_core::EPIPHANY_CULTMESH_WORK_LOOP_TELEMETRY_SCHEMA_VERSION; -use epiphany_core::EpiphanyAgentStateSoaEntry; -use epiphany_core::EpiphanyCultMeshAgentStateSoaSummaryEntry; -use epiphany_core::EpiphanyCultMeshClusterTopologyEntry; -use epiphany_core::EpiphanyCultMeshDaemonPokeReceiptEntry; -use epiphany_core::EpiphanyCultMeshDaemonRestartPolicyEntry; -use epiphany_core::EpiphanyCultMeshDaemonServiceLifecycleReceiptEntry; -use epiphany_core::EpiphanyCultMeshDaemonStatusEntry; -use epiphany_core::EpiphanyCultMeshDaemonToolCapabilityEntry; -use epiphany_core::EpiphanyCultMeshEveSurfaceStateEntry; -use epiphany_core::EpiphanyCultMeshIdunnAftercareAuditReceiptEntry; -use epiphany_core::EpiphanyCultMeshIdunnDeploymentReceiptEntry; -use epiphany_core::EpiphanyCultMeshManagedServicePolicyEntry; -use epiphany_core::EpiphanyCultMeshOdinAdvertisementEntry; -use epiphany_core::EpiphanyCultMeshRepoWorkMapEntry; -use epiphany_core::EpiphanyCultMeshRepoWorkOverviewEntry; -use epiphany_core::EpiphanyCultMeshRepoWorkPublicProofEntry; -use epiphany_core::EpiphanyCultMeshRepoWorkReadinessEntry; -use epiphany_core::EpiphanyCultMeshSemanticProjectionHealthEntry; -use epiphany_core::EpiphanyCultMeshSwarmBrakeEntry; -use epiphany_core::EpiphanyCultMeshWorkLoopTelemetryEntry; -use epiphany_core::EpiphanyLocalVerseContext; -use epiphany_core::EpiphanyServiceExecutionAuditCheck; -#[cfg(test)] -use epiphany_core::default_epiphany_cultmesh_swarm_brake; -use epiphany_core::engage_epiphany_cultmesh_swarm_brake; -use epiphany_core::epiphany_cluster_service_execution_audit_report; -use epiphany_core::epiphany_cultmesh_agent_state_soa_summary_from_entry; -use epiphany_core::epiphany_cultmesh_bifrost_body_change_publication_intent; -use epiphany_core::epiphany_cultmesh_daemon_poke_intent_from_status; -use epiphany_core::epiphany_cultmesh_daemon_poke_receipt_for_intent; -use epiphany_core::epiphany_cultmesh_daemon_tool_invocation_intent_from_capability; -use epiphany_core::epiphany_service_execution_audit_report; -use epiphany_core::load_agent_state_soa_entry; -use epiphany_core::load_arrival_latest_epiphany_cultmesh_bifrost_body_change_publication_intent; -use epiphany_core::load_epiphany_cultmesh_cluster_topology; -use epiphany_core::load_epiphany_cultmesh_daemon_liveness; -use epiphany_core::load_epiphany_cultmesh_daemon_poke_intent; -use epiphany_core::load_epiphany_cultmesh_daemon_restart_policy_directory; -use epiphany_core::load_epiphany_cultmesh_daemon_service_lifecycle_receipts; -use epiphany_core::load_epiphany_cultmesh_daemon_status; -use epiphany_core::load_epiphany_cultmesh_daemon_tool_directory; -use epiphany_core::load_epiphany_cultmesh_eve_surface_directory; -use epiphany_core::load_epiphany_cultmesh_managed_service_policies; -use epiphany_core::load_epiphany_cultmesh_repo_work_map_entries; -use epiphany_core::load_epiphany_cultmesh_repo_work_overviews; -use epiphany_core::load_epiphany_cultmesh_repo_work_public_proofs; -use epiphany_core::load_epiphany_cultmesh_repo_work_readiness_reports; -use epiphany_core::load_epiphany_cultmesh_semantic_projection_health; -use epiphany_core::load_epiphany_cultmesh_status; -use epiphany_core::load_epiphany_cultmesh_swarm_brake; -use epiphany_core::load_latest_epiphany_cultmesh_agent_state_soa_summary; -use epiphany_core::load_latest_epiphany_cultmesh_daemon_service_lifecycle_receipt; -use epiphany_core::load_latest_epiphany_cultmesh_daemon_tool_invocation_intent; -use epiphany_core::load_latest_epiphany_cultmesh_idunn_aftercare_audit_receipt; -use epiphany_core::load_latest_epiphany_cultmesh_idunn_deployment_receipt; -use epiphany_core::load_latest_epiphany_cultmesh_repo_work_map_entry; -use epiphany_core::load_latest_epiphany_cultmesh_repo_work_overview; -use epiphany_core::load_latest_epiphany_cultmesh_repo_work_public_proof; -use epiphany_core::load_latest_epiphany_cultmesh_repo_work_readiness; -use epiphany_core::native_process_executable_path; -use epiphany_core::observe_native_process; -use epiphany_core::open_epiphany_cultmesh_node; -use epiphany_core::query_epiphany_local_verse_context; -use epiphany_core::release_epiphany_cultmesh_swarm_brake; -use epiphany_core::seed_epiphany_local_verse_context; -use epiphany_core::write_epiphany_cultmesh_agent_state_soa_summary; -use epiphany_core::write_epiphany_cultmesh_bifrost_body_change_publication_intent; -use epiphany_core::write_epiphany_cultmesh_daemon_poke_intent; -use epiphany_core::write_epiphany_cultmesh_daemon_poke_receipt; -use epiphany_core::write_epiphany_cultmesh_daemon_restart_policy; -use epiphany_core::write_epiphany_cultmesh_daemon_status; -use epiphany_core::write_epiphany_cultmesh_daemon_tool_invocation_intent; -#[cfg(test)] -use epiphany_core::write_epiphany_cultmesh_swarm_brake; -use epiphany_core::write_epiphany_cultmesh_work_loop_telemetry; -use serde::Serialize; -use serde_json::json; -use sha2::Digest; -use sha2::Sha256; -use std::collections::BTreeMap; -use std::env; -use std::fs; -use std::path::{Path, PathBuf}; - -const WRAPPER_OVERVIEW_COMMAND: &str = "tools/epiphany_local_run.ps1 -Mode swarm-overview"; -const DIRECT_MANAGED_SERVICE_SIGHT_COMMAND: &str = "epiphany-verse-query managed-services"; -const WRAPPER_SWARM_ONLINE_RUNBOOK_COMMAND: &str = - "tools/epiphany_local_run.ps1 -Mode swarm-online-runbook"; -const WRAPPER_POKE_NON_READY_COMMAND: &str = "tools/epiphany_local_run.ps1 -Mode swarm-poke-down"; -const WRAPPER_BIFROST_PUBLICATION_COMMAND: &str = - "tools/epiphany_local_run.ps1 -Mode bifrost-publication"; -const WRAPPER_RECEIPT_DIRECTORY_COMMAND: &str = - "tools/epiphany_local_run.ps1 -Mode receipt-directory"; -const DIRECT_IDUNN_DEPLOYMENT_AFTERCARE_AUDIT_COMMAND: &str = "epiphany-work deployment-aftercare-audit --workspace --local-verse-store --idunn-deployment-receipt-ref latest --aftercare-audit-receipt-ref latest"; -const WRAPPER_SERVICE_TICK_COMMAND: &str = "tools/epiphany_local_run.ps1 -Mode service-tick"; -const WRAPPER_SERVICE_POLICY_DIRECTORY_COMMAND: &str = - "tools/epiphany_local_run.ps1 -Mode service-policy-directory"; -const WRAPPER_MANAGED_SERVICE_TASK_PLAN_COMMAND: &str = - "tools/epiphany_local_run.ps1 -Mode managed-service-task-plan"; -const WRAPPER_SERVICE_INSTALL_EXECUTE_COMMAND: &str = - "tools/epiphany_local_run.ps1 -Mode managed-service-task-install"; -const WRAPPER_SERVICE_STATUS_COMMAND: &str = - "tools/epiphany_local_run.ps1 -Mode managed-service-task-status"; -const WRAPPER_SERVICE_START_EXECUTE_COMMAND: &str = - "tools/epiphany_local_run.ps1 -Mode managed-service-task-start"; -const WRAPPER_SERVICE_STOP_EXECUTE_COMMAND: &str = - "tools/epiphany_local_run.ps1 -Mode managed-service-task-stop"; -// Historical lifecycle receipts still use these typed families. Their follow-up -// routes are deliberately demoted: cluster SCM has no replacement owner, while -// single-service SCM receipts lead to the Idunn scheduled-task plan. -const WRAPPER_SERVICE_EXECUTION_AUDIT_COMMAND: &str = WRAPPER_MANAGED_SERVICE_TASK_PLAN_COMMAND; -const WRAPPER_SERVICE_EXECUTION_RUNBOOK_COMMAND: &str = WRAPPER_MANAGED_SERVICE_TASK_PLAN_COMMAND; -const WRAPPER_SERVICE_EXECUTION_READINESS_COMMAND: &str = WRAPPER_MANAGED_SERVICE_TASK_PLAN_COMMAND; -const WRAPPER_SERVICE_RECONCILE_COMMAND: &str = WRAPPER_SERVICE_STATUS_COMMAND; -const WRAPPER_CLUSTER_SERVICE_EXECUTION_AUDIT_COMMAND: &str = WRAPPER_OVERVIEW_COMMAND; -const WRAPPER_CLUSTER_SERVICE_EXECUTION_RUNBOOK_COMMAND: &str = WRAPPER_OVERVIEW_COMMAND; -const WRAPPER_CLUSTER_SERVICE_EXECUTION_READINESS_COMMAND: &str = WRAPPER_OVERVIEW_COMMAND; -const WRAPPER_CLUSTER_SERVICE_INSTALL_EXECUTE_COMMAND: &str = WRAPPER_OVERVIEW_COMMAND; -const WRAPPER_CLUSTER_SERVICE_START_EXECUTE_COMMAND: &str = WRAPPER_OVERVIEW_COMMAND; -const WRAPPER_CLUSTER_SERVICE_STOP_EXECUTE_COMMAND: &str = WRAPPER_OVERVIEW_COMMAND; -const DIRECT_BIFROST_PUBLICATION_COMMAND: &str = - "epiphany-verse-query bifrost-publication --target-repository --changed-path "; -const SERVICE_LIFECYCLE_OWNER: &str = "Idunn"; -const SERVICE_LIFECYCLE_HOSTED_BODY: &str = "Epiphany"; - -fn main() -> Result<()> { - let handle = std::thread::Builder::new() - .name("epiphany-verse-query".to_string()) - .stack_size(32 * 1024 * 1024) - .spawn(run_cli) - .context("failed to spawn epiphany-verse-query worker thread")?; - match handle.join() { - Ok(result) => result, - Err(payload) => std::panic::resume_unwind(payload), - } -} - -fn require_query_bootstrap(args: &Args) -> Result<()> { - load_epiphany_cultmesh_status(&args.store, args.runtime_id.clone())? - .with_context(|| { - format!( - "local Verse is not bootstrapped at {}; run epiphany-verse-query seed-compact before mutating query commands", - args.store.display() - ) - })?; - let topology = load_epiphany_cultmesh_cluster_topology(&args.store, args.runtime_id.clone())?; - if topology.is_empty() { - anyhow::bail!( - "local Verse has no persisted cluster topology at {}; run epiphany-verse-query seed-compact before mutating query commands", - args.store.display() - ); - } - Ok(()) -} - -fn swarm_brake_status_projection( - store: &Path, - runtime_id: &str, - brake: Option<&EpiphanyCultMeshSwarmBrakeEntry>, -) -> serde_json::Value { - json!({ - "schemaVersion": "epiphany.cultmesh.swarm_brake_status.v0", - "status": "observed", - "store": store, - "runtimeId": runtime_id, - "storeKey": "epiphany-local/swarm-brake", - "brakePresent": brake.is_some(), - "brakeId": brake.map(|value| value.brake_id.as_str()), - "brakeStatus": brake.map(|value| value.status.as_str()).unwrap_or("absent"), - "brakeOwner": brake.map(|value| value.operator_agent_id.as_str()), - "canonicalBrakeId": EPIPHANY_CANONICAL_SWARM_BRAKE_ID, - "canonicalBrakeOwner": EPIPHANY_CANONICAL_SWARM_BRAKE_OWNER, - "canonicalAuthority": brake.is_some_and(|value| - value.brake_id == EPIPHANY_CANONICAL_SWARM_BRAKE_ID - && value.operator_agent_id == EPIPHANY_CANONICAL_SWARM_BRAKE_OWNER), - "scope": brake.map(|value| value.scope.as_str()), - "reason": brake.map(|value| value.reason.as_str()), - "affectedClusters": brake.map(|value| value.affected_clusters.as_slice()), - "protectedSurfaces": brake.map(|value| value.protected_surfaces.as_slice()), - "expiresAtUtc": brake.and_then(|value| value.expires_at_utc.as_deref()), - "privateStateExposed": false, - }) -} - -fn apply_explicit_swarm_brake_set( - store: &Path, - runtime_id: &str, - status: Option<&str>, - brake_id: Option<&str>, - actor: Option<&str>, - reason: Option<&str>, - created_at_utc: &str, - allow_engaged_adoption: bool, -) -> Result { - let status = - status.context("swarm-brake-set requires explicit --brake-status engaged|released")?; - let brake_id = brake_id.context("swarm-brake-set requires explicit --brake-id")?; - if brake_id != EPIPHANY_CANONICAL_SWARM_BRAKE_ID { - anyhow::bail!( - "swarm-brake-set requires canonical --brake-id {}", - EPIPHANY_CANONICAL_SWARM_BRAKE_ID - ); - } - let actor = actor.context("swarm-brake-set requires explicit --requesting-agent-id")?; - let reason = reason.context("swarm-brake-set requires explicit --reason")?; - match status { - "engaged" => engage_epiphany_cultmesh_swarm_brake( - store, - runtime_id, - reason, - actor, - created_at_utc, - allow_engaged_adoption, - ), - "released" => { - release_epiphany_cultmesh_swarm_brake(store, runtime_id, reason, actor, created_at_utc) - } - other => { - anyhow::bail!("invalid explicit --brake-status {other:?}; expected engaged or released") - } - } -} - -fn reset_quarantined_smoke_store(base: &Path, store: &Path) -> Result<()> { - let expected_store = Path::new(".epiphany-smoke/verse-query-default/local-verse.ccmp"); - if store != expected_store { - anyhow::bail!( - "refusing to reset non-default verse-query smoke store {}", - store.display() - ); - } - let quarantine = base.join(".epiphany-smoke"); - fs::create_dir_all(&quarantine)?; - let resolved_quarantine = quarantine.canonicalize()?; - let target = base.join( - store - .parent() - .context("default verse-query smoke store has no parent")?, - ); - if target.exists() { - let resolved_target = target.canonicalize()?; - if resolved_target == resolved_quarantine - || !resolved_target.starts_with(&resolved_quarantine) - { - anyhow::bail!( - "refusing to delete verse-query smoke target outside quarantine: {} -> {}", - target.display(), - resolved_target.display() - ); - } - fs::remove_dir_all(&target)?; - } - Ok(()) -} - -fn seed_quarantined_smoke_daemon_statuses(args: &Args) -> Result<()> { - if !args.smoke_default_store { - anyhow::bail!("synthetic daemon readiness is confined to the built-in smoke store"); - } - for cluster in load_epiphany_cultmesh_cluster_topology(&args.store, args.runtime_id.clone())? { - write_epiphany_cultmesh_daemon_status( - &args.store, - args.runtime_id.clone(), - EpiphanyCultMeshDaemonStatusEntry { - schema_version: epiphany_core::EPIPHANY_CULTMESH_DAEMON_STATUS_SCHEMA_VERSION - .to_string(), - daemon_id: cluster.daemon_id, - cluster_id: cluster.cluster_id, - body_domain: cluster.body_domain, - daemon_surface_id: cluster.daemon_surface_id, - eve_surface_id: cluster.eve_surface_id, - status: "ready".to_string(), - last_heartbeat_utc: "2026-06-02T00:00:00Z".to_string(), - supported_actions: vec![ - "inspectStatus".to_string(), - "pokeDaemon".to_string(), - "watchHeartbeat".to_string(), - "submitTypedToolIntent".to_string(), - ], - operator_action: "none".to_string(), - private_state_exposed: false, - notes: vec![ - "Synthetic liveness fixture confined to the built-in aggregate smoke body." - .to_string(), - ], - }, - )?; - } - Ok(()) -} - -fn run_cli() -> Result<()> { - let args = Args::parse()?; - match args.command.as_str() { - "seed" => { - let body_domain = args - .body_domain - .as_deref() - .context("seed requires --body-domain repo:")?; - seed_epiphany_local_verse_context( - &args.store, - args.runtime_id.clone(), - Utc::now().to_rfc3339(), - body_domain, - )?; - let context = query_epiphany_local_verse_context(&args.store, args.runtime_id.clone())?; - println!("{}", serde_json::to_string_pretty(&context)?); - } - "seed-compact" | "seed-only" => { - let body_domain = args - .body_domain - .as_deref() - .context("seed-compact requires --body-domain repo:")?; - seed_epiphany_local_verse_context( - &args.store, - args.runtime_id.clone(), - Utc::now().to_rfc3339(), - body_domain, - )?; - println!( - "{}", - serde_json::to_string_pretty(&json!({ - "schemaVersion": "epiphany.local_verse_seed_receipt.v0", - "status": "ok", - "store": args.store, - "runtimeId": args.runtime_id, - "privateStateExposed": false, - }))? - ); - } - "query" => { - let context = query_epiphany_local_verse_context(&args.store, args.runtime_id.clone())?; - println!("{}", serde_json::to_string_pretty(&context)?); - } - "semantic-health" | "semantic-projection-health" => { - if !args.store.exists() { - anyhow::bail!("local Verse semantic health is absent"); - } - let rows = load_epiphany_cultmesh_semantic_projection_health( - &args.store, - args.runtime_id.clone(), - )?; - let tui_rows = rows - .iter() - .map(semantic_projection_health_tui_row) - .collect::>(); - let report_rows = rows - .iter() - .map(semantic_projection_health_report_row) - .collect::>(); - println!( - "{}", - serde_json::to_string_pretty(&json!({ - "schemaVersion": "epiphany.cultmesh.semantic_projection_health_report.v0", - "status": if rows.is_empty() { "unknown" } else if rows.iter().all(|row| row.status == "ready") { "observed-ready" } else { "observed-attention" }, - "rowCount": rows.len(), - "readyCount": rows.iter().filter(|row| row.status == "ready").count(), - "privateStateExposed": false, - "authoritative": false, - "authority": "sight-only", - "rows": report_rows, - "tuiRows": tui_rows, - }))? - ); - } - "receipt-directory" | "receipts" | "evidence-directory" => { - let context = query_epiphany_local_verse_context(&args.store, args.runtime_id.clone())?; - let lifecycle_receipts = load_epiphany_cultmesh_daemon_service_lifecycle_receipts( - &args.store, - args.runtime_id.clone(), - )?; - let report = receipt_directory_report(&context, &lifecycle_receipts); - println!( - "{}", - serde_json::to_string_pretty(&json!({ - "schemaVersion": "epiphany.cultmesh.receipt_directory_report.v0", - "status": report.status, - "store": args.store, - "runtimeId": args.runtime_id, - "rowCount": report.rows.len(), - "presentRowCount": report.present_row_count, - "absentRowCount": report.absent_row_count, - "readyRowCount": report.ready_row_count, - "attentionRowCount": report.attention_row_count, - "attentionRouteRows": report.attention_route_rows, - "missingRowCount": report.missing_row_count, - "artifactNoneCount": report.artifact_none_count, - "artifactExternalRefCount": report.artifact_external_ref_count, - "artifactPresentCount": report.artifact_present_count, - "artifactMissingCount": report.artifact_missing_count, - "commands": { - "overview": "epiphany-verse-query swarm-overview", - "wrapperOverview": WRAPPER_OVERVIEW_COMMAND, - "wrapperSwarmOnlineRunbook": WRAPPER_SWARM_ONLINE_RUNBOOK_COMMAND, - "tools": "epiphany-verse-query tool-directory", - "wrapperTools": "tools/epiphany_local_run.ps1 -Mode tool-directory", - "receiptDirectory": "epiphany-verse-query receipt-directory", - "wrapperReceiptDirectory": WRAPPER_RECEIPT_DIRECTORY_COMMAND, - "wrapperServiceTick": WRAPPER_SERVICE_TICK_COMMAND, - "wrapperServicePolicyDirectory": WRAPPER_SERVICE_POLICY_DIRECTORY_COMMAND - }, - "privateStateExposed": report.private_state_exposed, - "rows": report.rows, - "tuiRows": report.tui_rows, - }))? - ); - } - "restart-policy-directory" | "service-policy-directory" | "scheduler-coverage" => { - let policy_directory = load_epiphany_cultmesh_daemon_restart_policy_directory( - &args.store, - args.runtime_id.clone(), - )?; - let report = daemon_restart_policy_directory_report_from_rows(&policy_directory); - println!( - "{}", - serde_json::to_string_pretty(&json!({ - "schemaVersion": "epiphany.cultmesh.daemon_restart_policy_directory.v0", - "status": report.status, - "store": args.store, - "runtimeId": args.runtime_id, - "lifecycleOwner": report.lifecycle_owner, - "hostedBody": report.hosted_body, - "declaredDaemonTargetCount": report.rows.len(), - "observedDaemonCount": report.observed_daemon_count, - "coveredCount": report.covered_count, - "enabledCount": report.enabled_count, - "disabledCount": report.disabled_count, - "missingCount": report.missing_count, - "attentionCount": report.attention_count, - "policyCommand": "epiphany-daemon-supervisor policy --daemon-id --restart-command [--restart-arg ...]", - "wrapperPolicyDirectory": WRAPPER_SERVICE_POLICY_DIRECTORY_COMMAND, - "tickCommand": "epiphany-daemon-supervisor tick", - "wrapperTick": WRAPPER_SERVICE_TICK_COMMAND, - "privateStateExposed": report.private_state_exposed, - "rows": report.rows, - "tuiRows": report.tui_rows, - }))? - ); - } - "managed-services" | "idunn-services" => { - let report = managed_service_sight_report(&args.store, &args.runtime_id)?; - println!( - "{}", - serde_json::to_string_pretty(&json!({ - "schemaVersion": "epiphany.cultmesh.managed_service_sight.v0", - "status": report.status, - "lifecycleOwner": "Idunn", - "serviceCount": report.rows.len(), - "aliveCount": report.alive_count, - "attentionCount": report.attention_count, - "rows": report.rows, - "tuiRows": report.tui_rows, - "privateStateExposed": report.private_state_exposed, - }))? - ); - } - "tools" | "tool-directory" => { - let directory = - load_epiphany_cultmesh_daemon_tool_directory(&args.store, args.runtime_id.clone())?; - if !directory.iter().all(|(_, _, capability)| { - capability.available_to_all_agents - && capability.requires_receipt - && !capability.private_state_exposed - }) { - anyhow::bail!( - "daemon tool directory contains a capability that is not globally available, receipt-gated, and private-state sealed" - ); - } - let report = daemon_tool_directory_report(&directory); - println!( - "{}", - serde_json::to_string_pretty(&json!({ - "schemaVersion": "epiphany.cultmesh.daemon_tool_directory.v0", - "status": if report.rows.is_empty() { "empty" } else { "ok" }, - "runtimeId": args.runtime_id, - "store": args.store, - "toolCount": report.rows.len(), - "hostReadyCount": report.host_ready_count, - "hostAttentionCount": report.host_attention_count, - "invocationCommand": null, - "wrapperInvocationCommand": null, - "availabilityReason": "no provenance-bearing provider capability contract", - "wrapperMode": "tools/epiphany_local_run.ps1 -Mode tool-directory", - "privateStateExposed": false, - "invariants": { - "availableToAllAgents": true, - "requiresReceipt": true, - "privateStateExposed": false - }, - "tools": report.rows, - "tuiRows": report.tui_rows, - }))? - ); - } - "invoke-tool" | "tool-invocation" | "tool-intent" => { - run_invoke_tool_command(&args)?; - } - "swarm-brake" | "brake" => anyhow::bail!( - "legacy swarm-brake command is disabled because it mixed observation and mutation; use swarm-brake-status to read or swarm-brake-set with explicit --brake-status and --brake-id" - ), - "swarm-brake-status" => { - let brake = load_epiphany_cultmesh_swarm_brake(&args.store, args.runtime_id.clone())?; - println!( - "{}", - serde_json::to_string_pretty(&swarm_brake_status_projection( - &args.store, - &args.runtime_id, - brake.as_ref(), - ))? - ); - } - "swarm-brake-set" => { - require_query_bootstrap(&args)?; - let created_at_utc = Utc::now().to_rfc3339(); - let written = apply_explicit_swarm_brake_set( - &args.store, - &args.runtime_id, - args.brake_status.as_deref(), - args.brake_id.as_deref(), - args.requesting_agent_id.as_deref(), - args.reason.as_deref(), - &created_at_utc, - false, - )?; - println!( - "{}", - serde_json::to_string_pretty(&swarm_brake_status_projection( - &args.store, - &args.runtime_id, - Some(&written), - ))? - ); - } - "agent-state" | "agent-state-soa" => { - require_query_bootstrap(&args)?; - let agent_store = args - .agent_store - .as_ref() - .context("agent-state requires --agent-store pointing at state/agents.msgpack")?; - let soa = load_agent_state_soa_entry(agent_store)?.with_context(|| { - format!( - "agent store {:?} has no persisted epiphany.agent_state_soa.v0; run epiphany-agent-memory-store refresh-soa first", - agent_store - ) - })?; - let summary = epiphany_cultmesh_agent_state_soa_summary_from_entry( - args.runtime_id.clone(), - "agent-state-soa-summary", - &soa, - ); - let written = write_epiphany_cultmesh_agent_state_soa_summary(&args.store, summary)?; - let projected = load_latest_epiphany_cultmesh_agent_state_soa_summary( - &args.store, - args.runtime_id.clone(), - )?; - if projected.is_none() { - anyhow::bail!("local Verse query lost agent state SoA summary after write"); - } - println!( - "{}", - serde_json::to_string_pretty(&json!({ - "status": "ok", - "store": args.store, - "runtimeId": args.runtime_id, - "agentStore": agent_store, - "summaryId": written.summary_id, - "rowCount": written.row_count, - "roleIds": written.role_ids, - "privateStateExposed": written.private_state_exposed, - }))? - ); - } - "agent-state-report" | "agent-state-soa-report" => { - if !args.store.exists() { - anyhow::bail!( - "local Verse has no agent state SoA summary at {}; report is read-only and will not create the store", - args.store.display() - ); - } - let summary = load_latest_epiphany_cultmesh_agent_state_soa_summary( - &args.store, - args.runtime_id.clone(), - )? - .context("local Verse has no agent state SoA summary; run agent-state first")?; - let node = open_epiphany_cultmesh_node(&args.store, args.runtime_id.clone())?; - let summary_table = node.soa::()?; - let summary_ids = summary_table.column::("summaryId")?; - let row_counts = summary_table.column::("rowCount")?; - let private_state = summary_table.column::("privateStateExposed")?; - let role_ids = summary_table.column::>("roleIds")?; - let portable_contracts = summary_table.column::>("portableContracts")?; - let mut matching_rows = Vec::new(); - for index in 0..summary_table.len() { - if summary_ids.values()[index] == summary.summary_id { - matching_rows.push(json!({ - "summaryId": summary_ids.values()[index], - "rowCount": row_counts.values()[index], - "roleIds": role_ids.values()[index], - "portableContracts": portable_contracts.values()[index], - "privateStateExposed": private_state.values()[index], - })); - } - } - if matching_rows.is_empty() { - anyhow::bail!( - "agent state SoA summary latest key is missing from CultMesh SoA table" - ); - } - if matching_rows.iter().any(|row| { - row.get("privateStateExposed") - .and_then(serde_json::Value::as_bool) - .unwrap_or(true) - }) { - anyhow::bail!("agent state SoA report would expose private state"); - } - let tui_rows = agent_state_soa_tui_rows(&summary); - println!( - "{}", - serde_json::to_string_pretty(&json!({ - "schemaVersion": "epiphany.cultmesh.agent_state_soa_report.v0", - "status": "ok", - "store": args.store, - "runtimeId": args.runtime_id, - "summaryId": summary.summary_id, - "summarySoaTableRows": summary_table.len(), - "matchingSummaryRows": matching_rows.len(), - "agentCount": summary.row_count, - "roleIds": summary.role_ids, - "agentIds": summary.agent_ids, - "profileKinds": summary.profile_kinds, - "portableContracts": summary.portable_contracts, - "tuiRows": tui_rows, - "soaRows": matching_rows, - "privateStateExposed": false, - }))? - ); - } - "poke-daemon" | "daemon-poke" => { - reject_daemon_poke_response_fields(&args)?; - - require_query_bootstrap(&args)?; - - let daemon_id = args - .daemon_id - .as_deref() - .context("poke-daemon requires --daemon-id, for example epiphany-daemon-hands")?; - let daemon_status = load_epiphany_cultmesh_daemon_status( - &args.store, - args.runtime_id.clone(), - daemon_id, - )? - .with_context(|| format!("local Verse has no daemon status for {daemon_id:?}"))?; - let brake = load_epiphany_cultmesh_swarm_brake(&args.store, args.runtime_id.clone())?; - let topology = - load_epiphany_cultmesh_cluster_topology(&args.store, args.runtime_id.clone())?; - let reason = args.reason.clone().unwrap_or_else(|| { - format!( - "Operator requested a typed lifecycle poke for {} after observing status {}.", - daemon_status.daemon_id, daemon_status.status - ) - }); - let intent_id = args.intent_id.clone().unwrap_or_else(|| { - format!( - "daemon-poke-intent-{}", - daemon_status - .daemon_id - .chars() - .map(|ch| if ch.is_ascii_alphanumeric() { ch } else { '-' }) - .collect::() - ) - }); - let poke_result = write_daemon_poke_intent( - &args, - brake.as_ref(), - &topology, - &daemon_status, - intent_id.clone(), - reason, - )?; - load_epiphany_cultmesh_daemon_poke_intent( - &args.store, - args.runtime_id.clone(), - &intent_id, - )? - .with_context(|| format!("local Verse lost exact daemon poke intent {intent_id:?}"))?; - println!( - "{}", - serde_json::to_string_pretty(&json!({ - "status": "pending-lifecycle-owner", - "store": args.store, - "runtimeId": args.runtime_id, - "targetDaemonId": poke_result["targetDaemonId"], - "targetClusterId": poke_result["targetClusterId"], - "observedStatus": poke_result["observedStatus"], - "intentId": poke_result["intentId"], - "receiptId": poke_result["receiptId"], - "receiptStatus": poke_result["receiptStatus"], - "resultingStatus": poke_result["resultingStatus"], - "responseOwner": poke_result["responseOwner"], - "privateStateRequested": poke_result["privateStateRequested"], - "privateStateExposed": poke_result["privateStateExposed"], - }))? - ); - } - "poke-down-daemons" | "poke-unready-daemons" | "daemon-poke-down" => { - reject_daemon_poke_response_fields(&args)?; - - require_query_bootstrap(&args)?; - - let context = query_epiphany_local_verse_context(&args.store, args.runtime_id.clone())?; - let pokes = write_poke_intents_for_non_ready_daemons(&args, &context)?; - let poke_tui_rows = pokes.iter().map(poke_result_tui_row).collect::>(); - let context = query_epiphany_local_verse_context(&args.store, args.runtime_id.clone())?; - println!( - "{}", - serde_json::to_string_pretty(&json!({ - "status": if pokes.is_empty() { "noop" } else { "pending-lifecycle-owner" }, - "store": args.store, - "runtimeId": context.runtime_id, - "observedDaemonCount": context.daemon_statuses.len(), - "requestedDaemonCount": pokes.len(), - "pokes": pokes, - "pokeRows": poke_tui_rows, - "tuiRows": poke_tui_rows, - "privateStateExposed": false, - }))? - ); - } - "swarm-status" | "daemon-report" | "daemon-status-report" => { - let liveness = - load_epiphany_cultmesh_daemon_liveness(&args.store, args.runtime_id.clone())?; - let report = daemon_liveness_report(&liveness); - println!( - "{}", - serde_json::to_string_pretty(&json!({ - "schemaVersion": "epiphany.cultmesh.swarm_status_report.v0", - "status": if report.rows.is_empty() { "unknown" } else if report.non_ready_count == 0 { "ready" } else { "attention" }, - "store": args.store, - "runtimeId": args.runtime_id, - "observedDaemonCount": report.rows.len(), - "nonReadyCount": report.non_ready_count, - "pokeCommand": "epiphany-verse-query poke-down-daemons", - "wrapperMode": "tools/epiphany_local_run.ps1 -Mode swarm-poke-down", - "privateStateExposed": false, - "rows": report.rows, - "tuiRows": report.tui_rows, - }))? - ); - } - "cluster-topology" | "clusters" | "cluster-report" => { - let topology = - load_epiphany_cultmesh_cluster_topology(&args.store, args.runtime_id.clone())?; - let report = cluster_topology_report(&topology); - if !report.rows.iter().all(|row| { - !row.private_state_exposed - && !row.declared_private_verse_route.is_empty() - && row.declared_body_domain.starts_with("repo:") - && !row.declared_daemon_target.is_empty() - && !row.declared_daemon_surface_route.is_empty() - && !row.declared_eve_route.is_empty() - }) { - anyhow::bail!( - "cluster topology report found an incomplete or private-state-exposing row" - ); - } - println!( - "{}", - serde_json::to_string_pretty(&json!({ - "schemaVersion": "epiphany.cultmesh.cluster_topology_report.v0", - "status": if report.rows.is_empty() { "empty" } else { "ok" }, - "store": args.store, - "runtimeId": args.runtime_id, - "declaredFacultyCount": report.rows.len(), - "declaredPrivateVerseRouteCount": report.declared_private_verse_route_count, - "declaredDaemonTargetCount": report.declared_daemon_target_count, - "publicDiscussionClusterCount": report.public_discussion_count, - "privateStateExposed": false, - "rows": report.rows, - "tuiRows": report.tui_rows, - }))? - ); - } - "eve-surfaces" | "eve-directory" | "odin-eve-surfaces" => { - let directory = - load_epiphany_cultmesh_eve_surface_directory(&args.store, args.runtime_id.clone())?; - let (_latest_repo_work_overview, repo_work_overviews) = - load_repo_work_overview_queue(&args)?; - let report = eve_surface_report(&directory, &repo_work_overviews); - println!( - "{}", - serde_json::to_string_pretty(&json!({ - "schemaVersion": "epiphany.cultmesh.eve_surface_directory_report.v0", - "status": if report.rows.is_empty() { "empty" } else { "ok" }, - "store": args.store, - "runtimeId": args.runtime_id, - "surfaceCount": report.rows.len(), - "publicDiscussionSurfaceCount": report.public_discussion_count, - "repoWorkQueueCount": report.repo_work_queue_count, - "repoWorkQueueRows": report.repo_work_queue_rows, - "repoWorkQueueTuiRows": report.repo_work_queue_tui_rows, - "connectionCommand": null, - "availabilityReason": "no provenance-bearing provider advertisement contract", - "privateStateExposed": false, - "rows": report.rows, - "tuiRows": report.tui_rows, - }))? - ); - } - "swarm-overview" | "agent-overview" | "global-agents" => { - let overview = load_swarm_overview_report(&args)?; - let commands = json!({ - "topology": "epiphany-verse-query cluster-topology", - "liveness": "epiphany-verse-query swarm-status", - "surfaces": "epiphany-verse-query eve-surfaces", - "tools": "epiphany-verse-query tool-directory", - "receipts": "epiphany-verse-query receipt-directory", - "restartPolicies": "epiphany-verse-query restart-policy-directory", - "managedServiceSight": DIRECT_MANAGED_SERVICE_SIGHT_COMMAND, - "repoWorkOverview": "epiphany-work overview --workspace --item ", - "repoWorkMap": "epiphany-verse-query swarm-overview", - "idunnDeploymentAftercareAudit": DIRECT_IDUNN_DEPLOYMENT_AFTERCARE_AUDIT_COMMAND, - "pokeNonReady": "epiphany-verse-query poke-down-daemons", - "wrapperOverview": WRAPPER_OVERVIEW_COMMAND, - "wrapperSwarmOnlineRunbook": WRAPPER_SWARM_ONLINE_RUNBOOK_COMMAND, - "wrapperReceipts": WRAPPER_RECEIPT_DIRECTORY_COMMAND, - "wrapperRestartPolicies": WRAPPER_SERVICE_POLICY_DIRECTORY_COMMAND, - "wrapperPokeNonReady": WRAPPER_POKE_NON_READY_COMMAND, - }); - println!( - "{}", - serde_json::to_string_pretty(&SwarmOverviewOutput::from_report( - args.store, - args.runtime_id, - overview, - commands, - ))? - ); - } - "swarm-triage" | "triage-swarm" => { - let overview = load_swarm_overview_report(&args)?; - let pokes = if overview.liveness_status == "ready" { - Vec::new() - } else { - let context = - query_epiphany_local_verse_context(&args.store, args.runtime_id.clone())?; - write_poke_intents_for_non_ready_daemons(&args, &context)? - }; - let output = - SwarmTriageOutput::from_report(args.store, args.runtime_id, overview, pokes); - println!("{}", serde_json::to_string_pretty(&output)?); - } - "bifrost-publication" | "publish-body-change" => { - if args.receipt_id.is_some() - || args.receipt_status.is_some() - || args.ledger_entry_id.is_some() - || args.publication_url.is_some() - || args.credit_receipt_ids.is_some() - { - anyhow::bail!( - "bifrost-publication accepts requester intent fields only; Bifrost and the GitHub publication adapter own receipt, ledger, credit, and publication-result fields" - ); - } - - require_query_bootstrap(&args)?; - - let intent_id = args - .intent_id - .clone() - .unwrap_or_else(|| "bifrost-publication-intent".to_string()); - let target_repository = args.target_repository.clone().with_context(|| { - "bifrost-publication requires --target-repository, for example repo:E:/Projects/EpiphanyAgent" - })?; - let target_branch = args - .target_branch - .clone() - .context("bifrost-publication requires --target-branch")?; - let change_summary = args - .change_summary - .clone() - .context("bifrost-publication requires --change-summary")?; - let justification = args - .justification - .clone() - .context("bifrost-publication requires --justification")?; - let changed_paths = required_list( - &args.changed_paths, - "bifrost-publication requires --changed-path", - )?; - let verification_receipts = required_list( - &args.verification_receipts, - "bifrost-publication requires --verification-receipt", - )?; - let review_receipts = required_list( - &args.review_receipts, - "bifrost-publication requires --review-receipt", - )?; - let authors = required_list( - &args.authorship_agent_ids, - "bifrost-publication requires --author-agent", - )?; - let credit_subjects = required_list( - &args.credit_subjects, - "bifrost-publication requires --credit-subject", - )?; - let source_cluster_id = args - .source_cluster_id - .clone() - .unwrap_or_else(|| "epiphany.cluster.hands".to_string()); - let source_agent_id = args - .source_agent_id - .clone() - .unwrap_or_else(|| "epiphany.Hands".to_string()); - let body_domain = args - .body_domain - .clone() - .unwrap_or_else(|| target_repository.clone()); - let intent = epiphany_cultmesh_bifrost_body_change_publication_intent( - intent_id, - source_cluster_id, - source_agent_id.clone(), - body_domain, - target_repository.clone(), - target_branch.clone(), - change_summary, - justification, - changed_paths, - verification_receipts, - review_receipts, - authors, - credit_subjects, - ); - let written_intent = write_epiphany_cultmesh_bifrost_body_change_publication_intent( - &args.store, - args.runtime_id.clone(), - intent.clone(), - )?; - let arrival_latest_intent = - load_arrival_latest_epiphany_cultmesh_bifrost_body_change_publication_intent( - &args.store, - args.runtime_id.clone(), - )?; - if arrival_latest_intent - .as_ref() - .map(|intent| intent.intent_id.as_str()) - != Some(written_intent.intent_id.as_str()) - { - anyhow::bail!("local Verse query lost Bifrost publication intent after write"); - } - println!( - "{}", - serde_json::to_string_pretty(&json!({ - "status": "pending-bifrost", - "store": args.store, - "runtimeId": args.runtime_id, - "intentId": written_intent.intent_id, - "publicationReceiptId": null, - "githubPublicationReceiptId": null, - "ledgerEntryId": null, - "pullRequestUrl": null, - "changedPaths": written_intent.changed_paths, - "creditSubjects": written_intent.credit_subjects, - "responseOwner": "Bifrost", - "githubReceiptOwner": "GitHub publication adapter", - "commands": { - "swarmOverview": WRAPPER_OVERVIEW_COMMAND, - "toolDirectory": "tools/epiphany_local_run.ps1 -Mode tool-directory", - "bifrostPublication": WRAPPER_BIFROST_PUBLICATION_COMMAND, - }, - "privateStateIncluded": written_intent.private_state_included, - "privateStateExposed": false, - }))? - ); - } - "bifrost-public-proof" | "publish-public-proof" | "public-proof-publication" => { - if args.ledger_entry_id.is_some() - || args.review_receipts.is_some() - || args.credit_receipt_ids.is_some() - || args.public_room_id.is_some() - || args.publication_url.is_some() - || args.receipt_id.is_some() - || args.receipt_status.is_some() - { - anyhow::bail!( - "bifrost-public-proof selects a pending public proof only; Bifrost owns ledger, review, credit, destination, publication, and receipt result fields" - ); - } - - require_query_bootstrap(&args)?; - - let (arrival_latest_public_proof, public_proofs) = load_repo_work_public_proofs(&args)?; - let selected_public_proof = if let Some(public_proof_id) = args.public_proof_id.as_ref() - { - public_proofs - .iter() - .find(|proof| proof.public_proof_id == *public_proof_id) - .cloned() - .with_context(|| { - format!("bifrost-public-proof could not find --public-proof-id {public_proof_id}") - })? - } else { - arrival_latest_public_proof - .context("bifrost-public-proof requires a repo work public proof in local Verse or --public-proof-id")? - }; - println!( - "{}", - serde_json::to_string_pretty(&json!({ - "schemaVersion": "epiphany.local_verse_bifrost_public_proof_publication.v0", - "status": "pending-bifrost", - "store": args.store, - "runtimeId": args.runtime_id, - "receiptId": null, - "publicProofId": selected_public_proof.public_proof_id, - "publicProofRef": selected_public_proof.public_proof_ref, - "publicProofSha256": selected_public_proof.public_proof_sha256, - "item": selected_public_proof.item, - "sourceWorkspace": selected_public_proof.workspace, - "sourceBranch": selected_public_proof.branch, - "targetPublicVerseId": null, - "publicRoomId": null, - "publicationUrl": null, - "ledgerEntryId": null, - "creditReceiptIds": [], - "reviewReceiptIds": [], - "responseOwner": "Bifrost", - "commands": { - "swarmOverview": WRAPPER_OVERVIEW_COMMAND - }, - "privateStateExposed": false, - }))? - ); - } - "bifrost-artifact-acceptance" | "artifact-acceptance" => { - anyhow::bail!("historical-only: canonical frontier workflow required") - } - "bifrost-metrics" | "metrics-receipt" => { - anyhow::bail!("historical-only: canonical frontier workflow required") - } - "collaboration-feedback" | "persona-feedback" => { - anyhow::bail!( - "collaboration-feedback cannot author Bifrost admission locally; submit feedback to Bifrost and ingest its authenticated, target-bound admission receipt" - ); - } - "smoke" => { - if !args.smoke_default_store { - anyhow::bail!( - "verse-query smoke fixtures may use only the built-in .epiphany-smoke quarantine store; --store and --runtime-id overrides are forbidden" - ); - } - reset_quarantined_smoke_store(&env::current_dir()?, &args.store)?; - seed_epiphany_local_verse_context( - &args.store, - args.runtime_id.clone(), - "2026-06-02T00:00:00Z", - "repo:C:/fixture/Epiphany", - )?; - seed_quarantined_smoke_daemon_statuses(&args)?; - let context = query_epiphany_local_verse_context(&args.store, args.runtime_id.clone())?; - let ready_liveness = - load_epiphany_cultmesh_daemon_liveness(&args.store, args.runtime_id.clone())?; - let ready_report = daemon_liveness_report(&ready_liveness); - if ready_report.non_ready_count != 0 || ready_report.rows.len() != 7 { - anyhow::bail!("local Verse query smoke expected seven ready daemon status rows"); - } - if !ready_report - .tui_rows - .iter() - .all(|row| row.contains("READY")) - { - anyhow::bail!( - "local Verse query smoke ready daemon report lost compact READY rows" - ); - } - if !ready_report.tui_rows.iter().any(|row| { - row.contains("READY") - && row.contains("Persona") - && row.contains("declaredPrivateVerseRoute=epiphany.cluster.persona.private") - && row.contains("followUp=tools/epiphany_local_run.ps1 -Mode swarm-poke-down") - && row.contains("private=false") - }) { - anyhow::bail!( - "local Verse query smoke ready daemon report lost compact topology/follow-up fields" - ); - } - let eve_directory = - load_epiphany_cultmesh_eve_surface_directory(&args.store, args.runtime_id.clone())?; - let (_latest_repo_work_overview, repo_work_overviews) = - load_repo_work_overview_queue(&args)?; - let eve_report = eve_surface_report(&eve_directory, &repo_work_overviews); - if !eve_report.rows.is_empty() || eve_report.public_discussion_count != 0 { - anyhow::bail!("local Verse query smoke accepted ownerless Eve provider state"); - } - if context.verse_policies.len() != 3 { - anyhow::bail!("local Verse query smoke expected three Verse policies"); - } - if !context.verse_policies.iter().any(|policy| { - policy.verse_id == "gamecult-local" && policy.yggdrasil_tunnel_allowed - }) { - anyhow::bail!("local Verse query smoke lost Yggdrasil tunnel policy"); - } - if context.contract_summaries.len() < 6 { - anyhow::bail!("local Verse query smoke expected organ contract summaries"); - } - if context.cluster_topology.len() != 7 { - anyhow::bail!("local Verse query smoke expected seven cluster topology rows"); - } - let loaded_topology = - load_epiphany_cultmesh_cluster_topology(&args.store, args.runtime_id.clone())?; - let topology_report = cluster_topology_report(&loaded_topology); - if topology_report.rows.len() != 7 - || topology_report.declared_private_verse_route_count != 7 - || topology_report.declared_daemon_target_count != 7 - || topology_report.public_discussion_count != 1 - { - anyhow::bail!("local Verse query smoke lost compact cluster topology invariants"); - } - if !topology_report.tui_rows.iter().any(|row| { - row.contains("PUBLIC") - && row.contains("Persona") - && row.contains("cluster=epiphany.cluster.persona") - && row.contains("role=Persona") - && row.contains("epiphany.cluster.persona.private") - && row.contains("declaredBodyKind=repository") - && row.contains("epiphany-daemon-persona") - }) { - anyhow::bail!("local Verse query smoke lost compact Persona cluster topology row"); - } - if !context.cluster_topology.iter().any(|cluster| { - cluster.cluster_id == "epiphany.cluster.persona" - && cluster.public_persona_discussion_allowed - && cluster.eve_surface_id == "eve://epiphany/persona" - }) { - anyhow::bail!("local Verse query smoke lost Persona Eve topology"); - } - if !context.odin_advertisements.is_empty() - || !context.daemon_tool_capabilities.is_empty() - { - anyhow::bail!("local Verse query smoke accepted ownerless provider claims"); - } - let tool_directory = - load_epiphany_cultmesh_daemon_tool_directory(&args.store, args.runtime_id.clone())?; - let tool_report = daemon_tool_directory_report(&tool_directory); - if !tool_report.rows.is_empty() || tool_report.host_attention_count != 0 { - anyhow::bail!("local Verse query smoke accepted ownerless tool capabilities"); - } - let overview_liveness = - load_epiphany_cultmesh_daemon_liveness(&args.store, args.runtime_id.clone())?; - let overview_daemon_report = daemon_liveness_report(&overview_liveness); - let overview_directory = - load_epiphany_cultmesh_eve_surface_directory(&args.store, args.runtime_id.clone())?; - let (_latest_repo_work_overview, repo_work_overviews) = - load_repo_work_overview_queue(&args)?; - let overview_surface_report = - eve_surface_report(&overview_directory, &repo_work_overviews); - let overview_tool_directory = - load_epiphany_cultmesh_daemon_tool_directory(&args.store, args.runtime_id.clone())?; - let overview_tool_report = daemon_tool_directory_report(&overview_tool_directory); - if overview_daemon_report.rows.len() != 7 - || overview_daemon_report.non_ready_count != 0 - || topology_report.rows.len() != 7 - || topology_report.declared_private_verse_route_count != 7 - || topology_report.declared_daemon_target_count != 7 - || !overview_surface_report.rows.is_empty() - || overview_surface_report.public_discussion_count != 0 - || !overview_tool_report.rows.is_empty() - || overview_tool_report.host_attention_count != 0 - { - anyhow::bail!("local Verse query smoke lost compact swarm overview invariants"); - } - let ready_overview = load_swarm_overview_report(&args)?; - if ready_overview.liveness_status != "ready" - || ready_overview.recovery_status != "attention" - || ready_overview.status != "attention" - || ready_overview.recommended_wrapper_mode != "service-policy-directory" - || ready_overview.recommended_wrapper_command - != WRAPPER_SERVICE_POLICY_DIRECTORY_COMMAND - || ready_overview.service_lifecycle_recommended_wrapper_mode != "none" - || ready_overview.service_lifecycle_recommended_wrapper_command != "none" - || !ready_overview.swarm_action_rows.iter().any(|row| { - row.priority == 30 - && row.family == "daemon-restart-policy" - && row.wrapper_mode == "service-policy-directory" - && row.effect_class == "read-only" - && row.lifecycle_owner == SERVICE_LIFECYCLE_OWNER - && row.hosted_body == SERVICE_LIFECYCLE_HOSTED_BODY - && !row.mutates_state - && !row.requires_elevated_authority - && !row.private_state_exposed - }) - { - anyhow::bail!( - "local Verse query smoke expected ready liveness but scheduler-policy attention" - ); - } - let overview_attention_daemon_ids = overview_daemon_report - .rows - .iter() - .filter(|row| row.needs_poke) - .map(|row| row.daemon_id.clone()) - .collect::>(); - if !overview_attention_daemon_ids.is_empty() { - anyhow::bail!("local Verse query smoke expected no ready attention daemon ids"); - } - if !topology_report.tui_rows.iter().any(|row| { - row.contains("PUBLIC") - && row.contains("Persona") - && row.contains("epiphany.cluster.persona.private") - && row.contains("epiphany-daemon-persona") - }) { - anyhow::bail!("local Verse query smoke lost swarm overview topology row"); - } - if !WRAPPER_RECEIPT_DIRECTORY_COMMAND.contains("-Mode receipt-directory") - || !WRAPPER_SWARM_ONLINE_RUNBOOK_COMMAND.contains("-Mode swarm-online-runbook") - || !WRAPPER_SERVICE_TICK_COMMAND.contains("-Mode service-tick") - || !WRAPPER_SERVICE_POLICY_DIRECTORY_COMMAND - .contains("-Mode service-policy-directory") - { - anyhow::bail!( - "local Verse query smoke lost compact overview wrapper command hints" - ); - } - let policy_directory = load_epiphany_cultmesh_daemon_restart_policy_directory( - &args.store, - args.runtime_id.clone(), - )?; - let policy_report = daemon_restart_policy_directory_report_from_rows(&policy_directory); - if policy_report.rows.len() != 7 - || policy_report.covered_count != 0 - || policy_report.missing_count != 7 - || policy_report.status != "attention" - || policy_report.private_state_exposed - || !policy_report - .tui_rows - .iter() - .any(|row| { - row.contains("MISSING") - && row.contains("Persona") - && row.contains("owner=Idunn") - && row.contains("hostedBody=Epiphany") - && row.contains("followUp=epiphany-daemon-supervisor policy --daemon-id --restart-command ") - && row.contains("private=false") - }) - { - anyhow::bail!( - "local Verse query smoke lost compact daemon restart policy coverage report" - ); - } - let persona_status = context - .daemon_statuses - .iter() - .find(|status| status.daemon_id == "epiphany-daemon-persona") - .context("missing Persona daemon status for disabled policy proof")?; - write_epiphany_cultmesh_daemon_restart_policy( - &args.store, - args.runtime_id.clone(), - EpiphanyCultMeshDaemonRestartPolicyEntry { - schema_version: EPIPHANY_CULTMESH_DAEMON_RESTART_POLICY_SCHEMA_VERSION - .to_string(), - policy_id: "daemon-restart-policy-disabled-smoke".to_string(), - daemon_id: persona_status.daemon_id.clone(), - cluster_id: persona_status.cluster_id.clone(), - restart_command: "disabled-policy-smoke".to_string(), - restart_args: Vec::new(), - cwd: None, - cooldown_seconds: 0, - backoff_multiplier: 1, - failure_count: 0, - last_attempt_utc: None, - last_result_status: "never-attempted".to_string(), - enabled: false, - private_state_exposed: false, - notes: vec![ - "Smoke proof: disabled restart policies are coverage records, not recovery authority." - .to_string(), - ], - reconcile_interval_seconds: 0, - heartbeat_stale_seconds: 0, - last_reconcile_utc: None, - }, - )?; - let disabled_policy_directory = load_epiphany_cultmesh_daemon_restart_policy_directory( - &args.store, - args.runtime_id.clone(), - )?; - let disabled_policy_report = - daemon_restart_policy_directory_report_from_rows(&disabled_policy_directory); - if disabled_policy_report.covered_count != 1 - || disabled_policy_report.disabled_count != 1 - || disabled_policy_report.missing_count != 6 - || disabled_policy_report.attention_count != 7 - || disabled_policy_report.status != "attention" - || !disabled_policy_report.tui_rows.iter().any(|row| { - row.contains("DISABLED") - && row.contains("Persona") - && row.contains("owner=Idunn") - && row.contains("hostedBody=Epiphany") - && row.contains("last=never-attempted") - && row.contains("followUp=epiphany-daemon-supervisor policy --daemon-id --restart-command ") - && row.contains("private=false") - }) - { - anyhow::bail!( - "local Verse query smoke let a disabled daemon restart policy masquerade as recovery readiness" - ); - } - let no_op_pokes = write_poke_intents_for_non_ready_daemons(&args, &context)?; - if !no_op_pokes.is_empty() { - anyhow::bail!("local Verse query smoke poked ready daemons during no-op sweep"); - } - let hands_status = context - .daemon_statuses - .iter() - .find(|status| status.daemon_id == "epiphany-daemon-hands") - .context("missing Hands daemon status")?; - let poke_intent = epiphany_cultmesh_daemon_poke_intent_from_status( - "daemon-poke-intent-smoke", - "epiphany.Self", - hands_status, - "Smoke-test operator lifecycle poke for a supervised daemon.", - ); - write_epiphany_cultmesh_daemon_poke_intent( - &args.store, - args.runtime_id.clone(), - poke_intent.clone(), - )?; - let poke_receipt = epiphany_cultmesh_daemon_poke_receipt_for_intent( - "daemon-poke-receipt-smoke", - &poke_intent, - "completed", - "ready", - "cultmesh://epiphany-local/daemon-poke/smoke", - ); - write_epiphany_cultmesh_daemon_poke_receipt( - &args.store, - args.runtime_id.clone(), - poke_receipt, - )?; - let mut degraded_hands = hands_status.clone(); - degraded_hands.status = "degraded".to_string(); - degraded_hands.operator_action = "pokeDaemon".to_string(); - degraded_hands - .notes - .push("Smoke-test degraded status for batch poke reflex.".to_string()); - write_epiphany_cultmesh_daemon_status( - &args.store, - args.runtime_id.clone(), - degraded_hands, - )?; - let context = query_epiphany_local_verse_context(&args.store, args.runtime_id.clone())?; - let degraded_liveness = - load_epiphany_cultmesh_daemon_liveness(&args.store, args.runtime_id.clone())?; - let degraded_report = daemon_liveness_report(°raded_liveness); - if degraded_report.non_ready_count != 1 - || !degraded_report.tui_rows.iter().any(|row| { - row.contains("POKE") - && row.contains("epiphany-daemon-hands") - && row.contains("declaredPrivateVerseRoute=epiphany.cluster.hands.private") - && row - .contains("followUp=tools/epiphany_local_run.ps1 -Mode swarm-poke-down") - }) - { - anyhow::bail!( - "local Verse query smoke degraded daemon report did not surface one Hands poke row" - ); - } - let degraded_tool_directory = - load_epiphany_cultmesh_daemon_tool_directory(&args.store, args.runtime_id.clone())?; - let degraded_tool_report = daemon_tool_directory_report(°raded_tool_directory); - if !degraded_tool_report.rows.is_empty() - || degraded_tool_report.host_attention_count != 0 - { - anyhow::bail!("degraded liveness manufactured provider tool attention"); - } - let degraded_overview_status = if degraded_report.non_ready_count == 0 - && degraded_tool_report.host_attention_count == 0 - { - "ready" - } else { - "attention" - }; - if degraded_overview_status != "attention" - || degraded_tool_report.host_attention_count != 0 - { - anyhow::bail!( - "local Verse query smoke degraded overview did not recommend daemon attention" - ); - } - let degraded_attention_daemon_ids = degraded_report - .rows - .iter() - .filter(|row| row.needs_poke) - .map(|row| row.daemon_id.clone()) - .collect::>(); - let mut degraded_attention_tool_host_daemon_ids = degraded_tool_report - .rows - .iter() - .filter(|row| row.host_status != "ready") - .map(|row| row.host_daemon_id.clone()) - .collect::>(); - degraded_attention_tool_host_daemon_ids.sort(); - degraded_attention_tool_host_daemon_ids.dedup(); - if degraded_attention_daemon_ids != vec!["epiphany-daemon-hands".to_string()] - || !degraded_attention_tool_host_daemon_ids.is_empty() - { - anyhow::bail!( - "local Verse query smoke degraded overview lost structured attention daemon ids" - ); - } - let degraded_recommended_wrapper = if degraded_overview_status == "ready" { - "none" - } else { - "swarm-poke-down" - }; - if degraded_recommended_wrapper != "swarm-poke-down" { - anyhow::bail!( - "local Verse query smoke degraded overview lost wrapper poke recommendation" - ); - } - let degraded_recommended_wrapper_command = if degraded_overview_status == "ready" { - "none".to_string() - } else { - WRAPPER_POKE_NON_READY_COMMAND.to_string() - }; - if degraded_recommended_wrapper_command != WRAPPER_POKE_NON_READY_COMMAND { - anyhow::bail!( - "local Verse query smoke degraded overview lost wrapper poke command" - ); - } - let batch_pokes = write_poke_intents_for_non_ready_daemons(&args, &context)?; - if batch_pokes.len() != 1 { - anyhow::bail!( - "local Verse query smoke expected exactly one batch daemon poke, got {}", - batch_pokes.len() - ); - } - let hands_topology = context - .cluster_topology - .iter() - .find(|row| row.daemon_id == "epiphany-daemon-hands") - .context("local Verse query smoke lost Hands topology")?; - if batch_pokes[0]["targetDaemonId"].as_str() != Some(hands_topology.daemon_id.as_str()) - || batch_pokes[0]["observedStatus"] != "degraded" - || batch_pokes[0]["declaredBodyDomain"].as_str() - != Some(hands_topology.body_domain.as_str()) - || batch_pokes[0]["declaredPrivateVerseRoute"].as_str() - != Some(hands_topology.private_verse_id.as_str()) - || batch_pokes[0]["declaredEveRoute"].as_str() - != Some(hands_topology.eve_surface_id.as_str()) - || batch_pokes[0]["privateStateExposed"] != false - { - anyhow::bail!( - "local Verse query smoke batch daemon poke had the wrong target/status/topology/private-state guard" - ); - } - let batch_poke_tui_row = poke_result_tui_row(&batch_pokes[0]); - if !batch_poke_tui_row.contains("POKE") - || !batch_poke_tui_row.contains("Hands") - || !batch_poke_tui_row.contains("epiphany-daemon-hands") - || !batch_poke_tui_row - .contains("declaredPrivateVerseRoute=epiphany.cluster.hands.private") - || !batch_poke_tui_row.contains("declaredEveRoute=eve://epiphany/hands") - || !batch_poke_tui_row.contains("receipt=unknown") - || !batch_poke_tui_row.contains("receiptStatus=unknown") - || !batch_poke_tui_row.contains("result=unknown") - || !batch_poke_tui_row.contains("private=false") - { - anyhow::bail!( - "local Verse query smoke batch daemon poke lost compact row topology/receipt/private-state fields" - ); - } - let triage_overview = load_swarm_overview_report(&args)?; - let triage_pokes = if triage_overview.liveness_status == "ready" { - Vec::new() - } else { - write_poke_intents_for_non_ready_daemons(&args, &context)? - }; - if triage_overview.status != "attention" - || triage_overview.attention_daemon_ids != vec!["epiphany-daemon-hands".to_string()] - || triage_overview.recommended_wrapper_mode != "swarm-poke-down" - || triage_overview.service_lifecycle_recommended_wrapper_mode != "none" - || !triage_overview.swarm_action_rows.iter().any(|row| { - row.priority == 10 - && row.family == "daemon-liveness" - && row.wrapper_mode == "swarm-poke-down" - && row.effect_class == "typed-lifecycle-poke" - && row.lifecycle_owner == SERVICE_LIFECYCLE_OWNER - && row.hosted_body == SERVICE_LIFECYCLE_HOSTED_BODY - && row.mutates_state - && !row.requires_elevated_authority - && !row.private_state_exposed - }) - || !triage_overview.tool_host_attention_rows.is_empty() - || !triage_overview.tool_host_attention_tui_rows.is_empty() - || triage_pokes.len() != 1 - || triage_pokes[0]["targetDaemonId"] != "epiphany-daemon-hands" - || triage_pokes[0]["declaredPrivateVerseRoute"] != "epiphany.cluster.hands.private" - || triage_pokes[0]["declaredEveRoute"] != "eve://epiphany/hands" - || triage_pokes[0]["privateStateExposed"] != false - { - anyhow::bail!( - "local Verse query smoke swarm triage did not issue one sealed Hands poke" - ); - } - let mut restored_hands = hands_status.clone(); - restored_hands.status = "ready".to_string(); - restored_hands.operator_action = "none".to_string(); - restored_hands - .notes - .push("Smoke-test restored status after batch poke receipt.".to_string()); - write_epiphany_cultmesh_daemon_status( - &args.store, - args.runtime_id.clone(), - restored_hands, - )?; - let bifrost_intent = epiphany_cultmesh_bifrost_body_change_publication_intent( - "bifrost-publication-intent-smoke", - "epiphany.cluster.hands", - "epiphany.Hands", - "repo:E:/Projects/EpiphanyAgent", - "E:/Projects/EpiphanyAgent", - "codex/perfect-machine-cultmesh", - "Smoke-test Bifrost routing before GitHub publication.", - "Body changes need explicit Bifrost routing so publication, credit, and review are ledgered before GitHub becomes the public substrate.", - vec!["epiphany-core/src/cultmesh_integration.rs".to_string()], - vec!["soul-verification-smoke".to_string()], - vec!["maintainer-review-smoke".to_string()], - vec!["epiphany.Hands".to_string()], - vec!["GameCult/EpiphanyAgent".to_string()], - ); - write_epiphany_cultmesh_bifrost_body_change_publication_intent( - &args.store, - args.runtime_id.clone(), - bifrost_intent.clone(), - )?; - if !WRAPPER_BIFROST_PUBLICATION_COMMAND.contains("-Mode bifrost-publication") - || !DIRECT_BIFROST_PUBLICATION_COMMAND.contains("bifrost-publication") - || !DIRECT_BIFROST_PUBLICATION_COMMAND.contains("--changed-path ") - { - anyhow::bail!( - "local Verse query smoke lost collaboration/publication command hints" - ); - } - write_epiphany_cultmesh_work_loop_telemetry( - &args.store, - EpiphanyCultMeshWorkLoopTelemetryEntry { - schema_version: EPIPHANY_CULTMESH_WORK_LOOP_TELEMETRY_SCHEMA_VERSION - .to_string(), - runtime_id: args.runtime_id.clone(), - verse_id: EPIPHANY_CULTMESH_INTERNAL_VERSE_ID.to_string(), - telemetry_id: "work-loop-telemetry-smoke".to_string(), - thread_id: "thread-smoke".to_string(), - produced_at_utc: "2026-06-18T00:00:00Z".to_string(), - source_stage: "Hands".to_string(), - target_stages: vec!["Soul".to_string(), "Modeling".to_string()], - lower_bound_receipt_at: "2026-06-18T00:00:00Z".to_string(), - hands_intent_id: "hands-intent-smoke".to_string(), - hands_review_id: "hands-review-smoke".to_string(), - hands_runtime_job_id: "hands-job-smoke".to_string(), - substrate_gate_grant_receipt_id: "substrate-grant-smoke".to_string(), - hands_patch_receipt_id: "hands-patch-smoke".to_string(), - hands_command_receipt_id: "hands-command-smoke".to_string(), - hands_commit_receipt_id: "hands-commit-smoke".to_string(), - command: "cargo test".to_string(), - exit_code: "0".to_string(), - stdout_artifact: "stdout.log".to_string(), - stderr_artifact: "stderr.log".to_string(), - commit_sha: "dry-run-no-commit".to_string(), - branch: "codex/perfect-machine-cultmesh".to_string(), - changed_paths: vec!["epiphany-core/src/cultmesh_integration.rs".to_string()], - artifact_previews: vec!["sealed stdout preview stays internal".to_string()], - source_refs: vec!["epiphany-core/src/cultmesh_integration.rs".to_string()], - source_path_proof: vec![ - "source proof stays in internal work-loop telemetry".to_string(), - ], - soul_receipt_ids: vec!["soul-verification-smoke".to_string()], - summary: "Smoke-test sealed Hands -> Soul -> Modeling work-loop digest." - .to_string(), - receipt_payload_previews: vec![ - "sealed patch receipt payload stays internal".to_string(), - ], - commit_diff_preview: "diff --git sealed smoke".to_string(), - verification_assertions: vec![ - "local Verse context exposes only the digest".to_string(), - ], - }, - )?; - let agent_state_soa = EpiphanyAgentStateSoaEntry { - schema_version: "epiphany.agent_state_soa.v0".to_string(), - generated_at: "2026-06-18T00:00:00Z".to_string(), - source_store: "state/agents.msgpack".to_string(), - role_ids: vec![ - "coordinator".to_string(), - "Persona".to_string(), - "imagination".to_string(), - "research".to_string(), - "modeling".to_string(), - "implementation".to_string(), - "verification".to_string(), - ], - agent_ids: vec![ - "epiphany.self".to_string(), - "epiphany.Persona".to_string(), - "epiphany.imagination".to_string(), - "epiphany.eyes".to_string(), - "epiphany.modeling".to_string(), - "epiphany.hands".to_string(), - "epiphany.soul".to_string(), - ], - display_names: vec![ - "Self".to_string(), - "Persona".to_string(), - "Imagination".to_string(), - "Eyes".to_string(), - "Modeling".to_string(), - "Hands".to_string(), - "Soul".to_string(), - ], - profile_kinds: vec![ - "WorkOrgan".to_string(), - "Persona".to_string(), - "WorkOrgan".to_string(), - "WorkOrgan".to_string(), - "WorkOrgan".to_string(), - "WorkOrgan".to_string(), - "WorkOrgan".to_string(), - ], - portable_contracts: vec![ - "epiphany.work_organ_state.v0".to_string(), - "gamecult.persona_state.v0".to_string(), - "epiphany.work_organ_state.v0".to_string(), - "epiphany.work_organ_state.v0".to_string(), - "epiphany.work_organ_state.v0".to_string(), - "epiphany.work_organ_state.v0".to_string(), - "epiphany.work_organ_state.v0".to_string(), - ], - semantic_memory_counts: vec![1, 1, 1, 1, 1, 1, 1], - episodic_memory_counts: vec![0, 0, 0, 0, 0, 0, 0], - relationship_memory_counts: vec![0, 1, 0, 0, 0, 0, 0], - goal_counts: vec![1, 1, 1, 1, 1, 1, 1], - value_counts: vec![1, 1, 1, 1, 1, 1, 1], - }; - let agent_state_summary = epiphany_cultmesh_agent_state_soa_summary_from_entry( - args.runtime_id.clone(), - "agent-state-soa-summary-smoke", - &agent_state_soa, - ); - write_epiphany_cultmesh_agent_state_soa_summary(&args.store, agent_state_summary)?; - let context = query_epiphany_local_verse_context(&args.store, args.runtime_id.clone())?; - if context - .arrival_latest_bifrost_body_change_publication_intent - .is_none() - || context.eve_surface_states.len() != context.odin_advertisements.len() - || context.daemon_statuses.len() != context.cluster_topology.len() - || context.latest_daemon_poke_intent.is_none() - || context.latest_daemon_poke_receipt.is_none() - || context.swarm_brake.is_none() - || context - .swarm_brake - .as_ref() - .is_some_and(|brake| brake.private_state_exposed) - || context - .arrival_latest_bifrost_body_change_publication_receipt - .is_some() - || context - .arrival_latest_bifrost_github_publication_receipt - .is_some() - || context.latest_work_loop_summary.is_none() - || context.latest_agent_state_soa_summary.is_none() - { - anyhow::bail!( - "local Verse query smoke lost requester state, manufactured provider responses, or lost work-loop/agent-state summaries" - ); - } - let agent_summary = context - .latest_agent_state_soa_summary - .as_ref() - .context("missing agent state SoA summary")?; - if agent_summary.row_count != 7 - || agent_summary.private_state_exposed - || !agent_summary - .portable_contracts - .iter() - .any(|contract| contract == "gamecult.persona_state.v0") - { - anyhow::bail!("local Verse agent state SoA summary lost standing swarm shape"); - } - let agent_state_tui_rows = agent_state_soa_tui_rows(agent_summary); - if !agent_state_tui_rows.iter().any(|row| { - row.contains("Persona") - && row.contains("epiphany.Persona") - && row.contains("contract=gamecult.persona_state.v0") - && row.contains("private=false") - }) || !agent_state_tui_rows.iter().any(|row| { - row.contains("implementation") - && row.contains("epiphany.hands") - && row.contains("contract=epiphany.work_organ_state.v0") - && row.contains("private=false") - }) { - anyhow::bail!( - "local Verse agent state SoA compact rows lost contract/private-state guards" - ); - } - let node = open_epiphany_cultmesh_node(&args.store, args.runtime_id.clone())?; - let agent_summary_table = node.soa::()?; - let summary_ids = agent_summary_table.column::("summaryId")?; - let private_state = agent_summary_table.column::("privateStateExposed")?; - if !summary_ids - .values() - .iter() - .any(|summary_id| summary_id == &agent_summary.summary_id) - || private_state.values().iter().any(|exposed| *exposed) - { - anyhow::bail!( - "local Verse agent state SoA summary was not readable as sealed CultMesh SoA columns" - ); - } - let work_loop_summary = context - .latest_work_loop_summary - .as_ref() - .context("missing work-loop summary")?; - let serialized_work_loop_summary = serde_json::to_string(work_loop_summary)?; - if serialized_work_loop_summary.contains("sealed patch receipt payload") - || serialized_work_loop_summary.contains("diff --git sealed") - || serialized_work_loop_summary.contains("sealed stdout preview") - { - anyhow::bail!("local Verse work-loop summary leaked internal telemetry previews"); - } - let lifecycle_receipts = load_epiphany_cultmesh_daemon_service_lifecycle_receipts( - &args.store, - args.runtime_id.clone(), - )?; - let receipt_directory = receipt_directory_report(&context, &lifecycle_receipts); - if receipt_directory.private_state_exposed - || !receipt_directory.attention_route_rows.is_empty() - || receipt_directory.artifact_none_count != receipt_directory.rows.len() - || receipt_directory.artifact_external_ref_count != 0 - || receipt_directory.artifact_present_count != 0 - || receipt_directory.artifact_missing_count != 0 - || !receipt_directory.rows.iter().any(|row| { - row.family == "daemon-tool" - && !row.present - && row.latest_id == "missing" - && row.follow_up_command == "none" - }) - || !receipt_directory.rows.iter().any(|row| { - row.family == "bifrost-publication" - && !row.present - && row.follow_up_command == "none" - }) - || !receipt_directory.rows.iter().any(|row| { - row.family == "work-loop" && row.latest_id == "work-loop-telemetry-smoke" - }) - || !receipt_directory.rows.iter().any(|row| { - row.family == "scheduler" - && row.follow_up_command == WRAPPER_SERVICE_TICK_COMMAND - }) - || !receipt_directory.rows.iter().any(|row| { - row.family == "agent-state-soa" - && row.latest_id == "agent-state-soa-summary-smoke" - }) - { - anyhow::bail!("local Verse query smoke lost compact receipt directory readback"); - } - let service_smoke_started_at = Utc::now().to_rfc3339(); - let readiness_follow_up_receipt = EpiphanyCultMeshDaemonServiceLifecycleReceiptEntry { - schema_version: EPIPHANY_CULTMESH_DAEMON_SERVICE_LIFECYCLE_RECEIPT_SCHEMA_VERSION - .to_string(), - receipt_id: "daemon-service-lifecycle-receipt-smoke-cluster-readiness-follow-up" - .to_string(), - service_id: "epiphany-cluster-daemon-services".to_string(), - scheduler_id: "epiphany-daemon-supervisor".to_string(), - runtime_id: args.runtime_id.clone(), - daemon_selector: "epiphany-cluster".to_string(), - action: "cluster-windows-service-execution-readiness".to_string(), - status: "not-elevated".to_string(), - command: "smoke-service-lifecycle".to_string(), - args: vec!["cluster-windows-service-execution-readiness".to_string()], - cwd: Some("E:/Projects/EpiphanyAgent".to_string()), - process_id: None, - exit_code: Some(0), - started_at_utc: service_smoke_started_at.clone(), - completed_at_utc: Some(Utc::now().to_rfc3339()), - operator_artifact_ref: - "smoke://verse-query/cluster-windows-service-execution-readiness".to_string(), - private_state_exposed: false, - notes: vec![ - "In-memory projection fixture for lifecycle follow-up routing.".to_string(), - ], - executable_sha256: String::new(), - preflight_witness_id: String::new(), - required_document_types: Vec::new(), - schema_preflight_passed: false, - schema_catalog_sha256: String::new(), - managed_policy_id: String::new(), - managed_policy_digest: String::new(), - provider_daemon_id: String::new(), - startup_correlation_id: String::new(), - process_creation_token: 0, - process_created_at_rfc3339: None, - process_executable_path: String::new(), - }; - let readiness_follow_up_row = receipt_directory_service_lifecycle_row( - "cluster-service-lifecycle", - Some(&readiness_follow_up_receipt), - WRAPPER_CLUSTER_SERVICE_EXECUTION_AUDIT_COMMAND, - ); - if readiness_follow_up_row.follow_up_command - != WRAPPER_CLUSTER_SERVICE_EXECUTION_READINESS_COMMAND - { - anyhow::bail!( - "local Verse query smoke did not demote cluster readiness receipts to overview" - ); - } - let missing_runbook_row = ReceiptDirectoryRow { - family: "cluster-service-execution-runbook".to_string(), - owner: "daemon-supervisor".to_string(), - document_kind: "epiphany.cultmesh.daemon_service_lifecycle_receipt.v0".to_string(), - latest_id: - "daemon-service-lifecycle-receipt-smoke-cluster-execution-runbook-missing" - .to_string(), - status: "written".to_string(), - route: - "epiphany-cluster-daemon-services::cluster-windows-service-execution-runbook" - .to_string(), - service_id: "epiphany-cluster-daemon-services".to_string(), - service_route: - "epiphany-cluster-daemon-services::cluster-windows-service-execution-runbook" - .to_string(), - follow_up_command: WRAPPER_CLUSTER_SERVICE_EXECUTION_RUNBOOK_COMMAND.to_string(), - artifact_ref: args - .store - .with_file_name( - "missing-epiphany-cluster-daemon-services-execution-runbook-smoke.ps1", - ) - .display() - .to_string(), - artifact_status: "missing".to_string(), - artifact_sha256: "none".to_string(), - present: true, - private_state_exposed: false, - }; - let ready_policy_report = DaemonRestartPolicyDirectoryReport { - status: "ok".to_string(), - lifecycle_owner: SERVICE_LIFECYCLE_OWNER.to_string(), - hosted_body: SERVICE_LIFECYCLE_HOSTED_BODY.to_string(), - rows: Vec::new(), - tui_rows: Vec::new(), - covered_count: 0, - enabled_count: 0, - disabled_count: 0, - missing_count: 0, - attention_count: 0, - observed_daemon_count: context.daemon_statuses.len(), - private_state_exposed: false, - }; - let missing_artifact_action = ServiceExecutionRunbookAction { - route: missing_runbook_row.route.clone(), - family: missing_runbook_row.family.clone(), - follow_up_command: missing_runbook_row.follow_up_command.clone(), - artifact_ref: missing_runbook_row.artifact_ref.clone(), - private_state_exposed: missing_runbook_row.private_state_exposed, - failed_check_count: 0, - missing_check_count: 0, - service_id: missing_runbook_row.service_id.clone(), - completion_audit_wrapper_mode: "cluster-service-execution-audit".to_string(), - completion_audit_wrapper_command: WRAPPER_CLUSTER_SERVICE_EXECUTION_AUDIT_COMMAND - .to_string(), - }; - let (missing_artifact_rows, _) = swarm_action_rows( - "ready", - &[], - &ready_policy_report, - &[], - &[], - &[missing_artifact_action], - &[], - &[], - &[], - ); - if !missing_artifact_rows.iter().any(|row| { - row.priority == 50 - && row.family == "service-execution-authority" - && row.status == "runbook-artifact-missing" - && row.wrapper_mode == "cluster-service-execution-runbook" - && row.effect_class == "service-lifecycle-runbook-regeneration" - && row.operator_artifact_status == "missing" - && row.operator_artifact_sha256 == "none" - && row.operator_artifact_execution_command == "none" - && row.operator_aftercare_command == "none" - && row.completion_audit_wrapper_mode == "cluster-service-execution-audit" - && row.completion_audit_wrapper_command - == WRAPPER_CLUSTER_SERVICE_EXECUTION_AUDIT_COMMAND - && row.service_execution_failed_check_count == 0 - && row.service_execution_missing_check_count == 0 - && !row.mutates_state - && !row.requires_elevated_authority - && !row.private_state_exposed - }) { - anyhow::bail!( - "local Verse query smoke did not demote missing service runbook artifacts to non-elevated regeneration" - ); - } - let synthetic_runbook_action = ServiceExecutionRunbookAction { - family: "cluster-service-execution-runbook".to_string(), - route: - "epiphany-cluster-daemon-services::cluster-windows-service-execution-runbook" - .to_string(), - artifact_ref: - "smoke://cluster-service-execution-audit/cluster-windows-service-execution-runbook" - .to_string(), - follow_up_command: WRAPPER_CLUSTER_SERVICE_EXECUTION_RUNBOOK_COMMAND.to_string(), - private_state_exposed: false, - failed_check_count: 5, - missing_check_count: 0, - service_id: "epiphany-cluster-daemon-services".to_string(), - completion_audit_wrapper_mode: "cluster-service-execution-audit".to_string(), - completion_audit_wrapper_command: WRAPPER_CLUSTER_SERVICE_EXECUTION_AUDIT_COMMAND - .to_string(), - }; - let (synthetic_artifact_rows, _) = swarm_action_rows( - "ready", - &[], - &ready_policy_report, - &[], - &[], - &[synthetic_runbook_action], - &[], - &[], - &[], - ); - if !synthetic_artifact_rows.iter().any(|row| { - row.priority == 50 - && row.family == "service-execution-authority" - && row.status == "runbook-artifact-missing" - && row.wrapper_mode == "cluster-service-execution-runbook" - && row.effect_class == "service-lifecycle-runbook-regeneration" - && row.operator_artifact_status == "missing" - && row.operator_artifact_sha256 == "none" - && row.operator_artifact_execution_command == "none" - && row.operator_aftercare_command == "none" - && !row.mutates_state - && !row.requires_elevated_authority - && !row.private_state_exposed - }) { - anyhow::bail!( - "local Verse query smoke treated a synthetic service runbook artifact as elevated authority" - ); - } - let latest_repo_work_overview: Option = - load_latest_epiphany_cultmesh_repo_work_overview( - &args.store, - args.runtime_id.clone(), - )?; - println!( - "{}", - serde_json::to_string_pretty(&json!({ - "status": "ok", - "store": args.store, - "runtimeId": context.runtime_id, - "verses": context.verse_policies.len(), - "globalRooms": context.global_room_policies.len(), - "clusters": context.cluster_topology.len(), - "odinAdvertisements": context.odin_advertisements.len(), - "eveSurfaces": context.eve_surface_states.len(), - "daemonStatuses": context.daemon_statuses.len(), - "swarmBrake": context.swarm_brake.as_ref().map(|brake| brake.status.clone()), - "daemonTools": context.daemon_tool_capabilities.len(), - "latestToolInvocationIntent": context.latest_daemon_tool_invocation_intent.as_ref().map(|intent| intent.intent_id.clone()), - "latestToolInvocationReceipt": context.latest_daemon_tool_invocation_receipt.as_ref().map(|receipt| receipt.receipt_id.clone()), - "latestDaemonPokeIntent": context.latest_daemon_poke_intent.as_ref().map(|intent| intent.intent_id.clone()), - "latestDaemonPokeReceipt": context.latest_daemon_poke_receipt.as_ref().map(|receipt| receipt.receipt_id.clone()), - "arrivalLatestBifrostPublicationIntent": context.arrival_latest_bifrost_body_change_publication_intent.as_ref().map(|intent| intent.intent_id.clone()), - "arrivalLatestBifrostPublicationReceipt": context.arrival_latest_bifrost_body_change_publication_receipt.as_ref().map(|receipt| receipt.receipt_id.clone()), - "arrivalLatestBifrostGithubReceipt": context.arrival_latest_bifrost_github_publication_receipt.as_ref().map(|receipt| receipt.receipt_id.clone()), - "latestWorkLoopTelemetry": context.latest_work_loop_summary.as_ref().map(|summary| summary.telemetry_id.clone()), - "latestAgentStateSoaSummary": context.latest_agent_state_soa_summary.as_ref().map(|summary| summary.summary_id.clone()), - "latestRepoWorkOverview": latest_repo_work_overview.as_ref().map(|overview| overview.overview_id.clone()), - "latestRepoWorkGate": latest_repo_work_overview.as_ref().map(|overview| overview.current_gate.clone()), - "latestRepoWorkBlocker": latest_repo_work_overview.as_ref().map(|overview| overview.blocker.clone()), - "contracts": context.contract_summaries.len(), - }))? - ); - } - other => anyhow::bail!( - "unknown command {other:?}; use seed, query, semantic-health, tools, tool-directory, restart-policy-directory, swarm-brake, swarm-status, swarm-overview, swarm-triage, managed-services, cluster-topology, eve-surfaces, agent-state, agent-state-report, poke-daemon, poke-down-daemons, bifrost-publication, bifrost-public-proof, bifrost-artifact-acceptance, bifrost-metrics, collaboration-feedback, or smoke; provider invoke/connect commands remain unavailable until a provenance-bearing provider contract exists" - ), - } - Ok(()) -} - -struct DaemonLivenessReport { - rows: Vec, - tui_rows: Vec, - non_ready_count: usize, -} - -fn semantic_projection_health_tui_row( - row: &EpiphanyCultMeshSemanticProjectionHealthEntry, -) -> String { - format!( - "SEMANTIC-HEALTH swarm={} partition={} generation={} status=observed-{} documents={} dimensions={} provider={} incarnation={} authority=sight-only private=false", - row.swarm_id, - row.partition, - row.source_generation, - row.status, - row.indexed_document_count - .map_or_else(|| "-".to_string(), |value| value.to_string()), - row.vector_dimensions - .map_or_else(|| "-".to_string(), |value| value.to_string()), - row.provider_id, - row.provider_incarnation, - ) -} - -fn semantic_projection_health_report_row( - row: &EpiphanyCultMeshSemanticProjectionHealthEntry, -) -> serde_json::Value { - json!({ - "swarmId": row.swarm_id, - "partition": row.partition, - "obligationId": row.obligation_id, - "sourceGeneration": row.source_generation, - "canonicalModelHash": row.canonical_model_hash, - "canonicalContentSetHash": row.canonical_content_set_hash, - "status": row.status, - "receiptId": row.receipt_id, - "indexedDocumentCount": row.indexed_document_count, - "vectorDimensions": row.vector_dimensions, - "observedAt": row.observed_at, - "observedSourceAt": row.observed_source_at, - "providerId": row.provider_id, - "providerIncarnation": row.provider_incarnation, - "authoritative": row.authoritative, - "nonAuthoritative": !row.authoritative, - "authority": "sight-only", - "queryEligibleDisplayOnly": row.query_eligible_display_only, - "privateStateExposed": row.private_state_exposed, - }) -} - -fn agent_state_soa_tui_rows(summary: &EpiphanyCultMeshAgentStateSoaSummaryEntry) -> Vec { - summary - .role_ids - .iter() - .enumerate() - .map(|(index, role)| { - format!( - "{} | {} | {} | contract={} | sem={} epi={} rel={} goals={} values={} | private={}", - role, - summary - .agent_ids - .get(index) - .map(String::as_str) - .unwrap_or("?"), - summary - .profile_kinds - .get(index) - .map(String::as_str) - .unwrap_or("?"), - summary - .portable_contracts - .get(index) - .map(String::as_str) - .unwrap_or("?"), - summary - .semantic_memory_counts - .get(index) - .copied() - .unwrap_or_default(), - summary - .episodic_memory_counts - .get(index) - .copied() - .unwrap_or_default(), - summary - .relationship_memory_counts - .get(index) - .copied() - .unwrap_or_default(), - summary.goal_counts.get(index).copied().unwrap_or_default(), - summary.value_counts.get(index).copied().unwrap_or_default(), - summary.private_state_exposed - ) - }) - .collect() -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -struct DaemonLivenessRow { - cluster_id: String, - daemon_id: String, - display_name: String, - declared_body_domain: String, - declared_private_verse_route: String, - declared_eve_route: String, - status: String, - operator_action: String, - last_heartbeat_utc: String, - supported_actions: Vec, - needs_poke: bool, - private_state_exposed: bool, -} - -struct ClusterTopologyReport { - rows: Vec, - tui_rows: Vec, - declared_private_verse_route_count: usize, - declared_daemon_target_count: usize, - public_discussion_count: usize, -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -struct ClusterTopologyRow { - cluster_id: String, - display_name: String, - role_id: String, - declared_private_verse_route: String, - declared_body_domain: String, - declared_body_kind: String, - declared_daemon_target: String, - declared_daemon_surface_route: String, - declared_eve_route: String, - public_persona_discussion_allowed: bool, - private_state_exposed: bool, -} - -fn cluster_topology_report( - topology: &[EpiphanyCultMeshClusterTopologyEntry], -) -> ClusterTopologyReport { - let mut rows = Vec::new(); - let mut tui_rows = Vec::new(); - let mut declared_private_verse_route_count = 0_usize; - let mut declared_daemon_target_count = 0_usize; - let mut public_discussion_count = 0_usize; - for cluster in topology { - if !cluster.private_verse_id.is_empty() { - declared_private_verse_route_count += 1; - } - if !cluster.daemon_id.is_empty() { - declared_daemon_target_count += 1; - } - if cluster.public_persona_discussion_allowed { - public_discussion_count += 1; - } - let visibility = if cluster.public_persona_discussion_allowed { - "PUBLIC" - } else { - "PRIVATE" - }; - tui_rows.push(format!( - "{visibility} | {} | cluster={} | role={} | declaredPrivateVerseRoute={} | declaredBodyDomain={} | declaredBodyKind={} | declaredDaemonTarget={} | declaredEveRoute={}", - cluster.display_name, - cluster.cluster_id, - cluster.role_id, - cluster.private_verse_id, - cluster.body_domain, - cluster.body_kind, - cluster.daemon_id, - cluster.eve_surface_id - )); - rows.push(ClusterTopologyRow { - cluster_id: cluster.cluster_id.clone(), - display_name: cluster.display_name.clone(), - role_id: cluster.role_id.clone(), - declared_private_verse_route: cluster.private_verse_id.clone(), - declared_body_domain: cluster.body_domain.clone(), - declared_body_kind: cluster.body_kind.clone(), - declared_daemon_target: cluster.daemon_id.clone(), - declared_daemon_surface_route: cluster.daemon_surface_id.clone(), - declared_eve_route: cluster.eve_surface_id.clone(), - public_persona_discussion_allowed: cluster.public_persona_discussion_allowed, - private_state_exposed: false, - }); - } - ClusterTopologyReport { - rows, - tui_rows, - declared_private_verse_route_count, - declared_daemon_target_count, - public_discussion_count, - } -} - -fn daemon_liveness_report( - liveness: &[( - EpiphanyCultMeshClusterTopologyEntry, - EpiphanyCultMeshDaemonStatusEntry, - )], -) -> DaemonLivenessReport { - let mut rows = Vec::new(); - let mut tui_rows = Vec::new(); - let mut non_ready_count = 0_usize; - for (cluster, status) in liveness { - let cluster_id = status.cluster_id.clone(); - let daemon_id = status.daemon_id.clone(); - let status_value = status.status.clone(); - let operator_action = status.operator_action.clone(); - let last_heartbeat_utc = status.last_heartbeat_utc.clone(); - let supported_actions = status.supported_actions.clone(); - let declared_eve_route = cluster.eve_surface_id.clone(); - let body_domain = cluster.body_domain.clone(); - let declared_private_verse_route = cluster.private_verse_id.clone(); - let display_name = cluster.display_name.clone(); - let needs_poke = status_value != "ready"; - if needs_poke { - non_ready_count += 1; - } - let compact_status = if needs_poke { "POKE" } else { "READY" }; - tui_rows.push(format!( - "{compact_status} | {display_name} | {} | declaredBodyDomain={} | declaredPrivateVerseRoute={} | declaredEveRoute={} | followUp={} | private={}", - daemon_id, - body_domain, - declared_private_verse_route, - declared_eve_route, - WRAPPER_POKE_NON_READY_COMMAND, - status.private_state_exposed - )); - rows.push(DaemonLivenessRow { - cluster_id, - daemon_id, - display_name, - declared_body_domain: body_domain, - declared_private_verse_route, - declared_eve_route, - status: status_value, - operator_action, - last_heartbeat_utc, - supported_actions, - needs_poke, - private_state_exposed: status.private_state_exposed, - }); - } - DaemonLivenessReport { - rows, - tui_rows, - non_ready_count, - } -} - -struct EveSurfaceReport { - rows: Vec, - tui_rows: Vec, - public_discussion_count: usize, - repo_work_queue_count: usize, - repo_work_queue_rows: Vec, - repo_work_queue_tui_rows: Vec, -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -struct EveSurfaceRow { - cluster_id: String, - display_name: String, - declared_body_domain: String, - declared_private_verse_route: String, - declared_daemon_target: String, - advertisement_id: String, - eve_surface_id: String, - tui_title: String, - public_persona_discussion_allowed: bool, - supported_actions: Vec, - exposed_document_types: Vec, - repo_work_queue_count: usize, - repo_work_queue_tui_rows: Vec, - private_state_exposed: bool, -} - -fn eve_surface_report( - directory: &[( - EpiphanyCultMeshClusterTopologyEntry, - EpiphanyCultMeshOdinAdvertisementEntry, - EpiphanyCultMeshEveSurfaceStateEntry, - )], - repo_work_overviews: &[EpiphanyCultMeshRepoWorkOverviewEntry], -) -> EveSurfaceReport { - let (repo_work_queue_rows, repo_work_queue_tui_rows) = - repo_work_overview_rows(repo_work_overviews); - let repo_work_queue_count = repo_work_queue_rows.len(); - let mut rows = Vec::new(); - let mut tui_rows = Vec::new(); - let mut public_discussion_count = 0_usize; - for (cluster, advertisement, surface) in directory { - let public_persona_discussion_allowed = cluster.public_persona_discussion_allowed; - if public_persona_discussion_allowed { - public_discussion_count += 1; - } - let visibility = if public_persona_discussion_allowed { - "PUBLIC" - } else { - "LOCAL" - }; - let private_state_exposed = - advertisement.private_state_exposed || surface.private_state_exposed; - let supported_actions = compact_tui_list(&surface.supported_actions); - let surface_repo_work_queue_tui_rows = if public_persona_discussion_allowed { - repo_work_peer_tui_rows(&repo_work_queue_rows) - } else { - Vec::new() - }; - let surface_repo_work_queue_count = surface_repo_work_queue_tui_rows.len(); - let repo_work_gates = if surface_repo_work_queue_tui_rows.is_empty() { - "none".to_string() - } else { - repo_work_queue_rows - .iter() - .map(|row| format!("{}:{}", row.item, row.current_gate)) - .collect::>() - .join(",") - }; - tui_rows.push(format!( - "{visibility} | {} | cluster={} | surface={} | declaredDaemonTarget={} | declaredBodyDomain={} | declaredPrivateVerseRoute={} | publicDiscussion={} | actions={} | docs={} | repoWorkQueue={} | repoWorkGates={} | advertisement={} | private={private_state_exposed}", - cluster.display_name, - cluster.cluster_id, - surface.surface_id, - cluster.daemon_id, - cluster.body_domain, - cluster.private_verse_id, - public_persona_discussion_allowed, - supported_actions, - compact_tui_list(&surface.exposed_document_types), - surface_repo_work_queue_count, - repo_work_gates, - advertisement.advertisement_id - )); - rows.push(EveSurfaceRow { - cluster_id: cluster.cluster_id.clone(), - display_name: cluster.display_name.clone(), - declared_body_domain: cluster.body_domain.clone(), - declared_private_verse_route: cluster.private_verse_id.clone(), - declared_daemon_target: cluster.daemon_id.clone(), - advertisement_id: advertisement.advertisement_id.clone(), - eve_surface_id: surface.surface_id.clone(), - tui_title: surface.tui_title.clone(), - public_persona_discussion_allowed, - supported_actions: surface.supported_actions.clone(), - exposed_document_types: surface.exposed_document_types.clone(), - repo_work_queue_count: surface_repo_work_queue_count, - repo_work_queue_tui_rows: surface_repo_work_queue_tui_rows, - private_state_exposed, - }); - } - EveSurfaceReport { - rows, - tui_rows, - public_discussion_count, - repo_work_queue_count, - repo_work_queue_rows, - repo_work_queue_tui_rows, - } -} - -struct DaemonToolDirectoryReport { - rows: Vec, - tui_rows: Vec, - host_ready_count: usize, - host_attention_count: usize, -} - -#[derive(Clone, Debug, PartialEq, Serialize)] -#[serde(rename_all = "camelCase")] -struct ManagedServiceSightReport { - status: String, - alive_count: usize, - attention_count: usize, - rows: Vec, - tui_rows: Vec, - private_state_exposed: bool, -} - -#[derive(Clone, Debug, PartialEq, Serialize)] -#[serde(rename_all = "camelCase")] -struct ManagedServiceSightRow { - service_id: String, - owner_daemon_id: String, - enabled: bool, - restart_mode: String, - process_observation: String, - process_id: Option, - process_identity_verified: bool, - lifecycle_receipt_id: Option, - lifecycle_status: Option, - last_pulse_status: Option, - last_pulse_iteration: Option, - last_pulse_artifact: Option, - private_state_exposed: bool, -} - -fn managed_service_sight_report( - store: &std::path::Path, - runtime_id: &str, -) -> Result { - let policies = load_epiphany_cultmesh_managed_service_policies(store, runtime_id.to_string())?; - let lifecycle = - load_epiphany_cultmesh_daemon_service_lifecycle_receipts(store, runtime_id.to_string())?; - let mut rows = Vec::new(); - for policy in policies { - let latest = lifecycle - .iter() - .filter(|receipt| receipt.service_id == policy.service_id) - .max_by(|left, right| left.started_at_utc.cmp(&right.started_at_utc)); - let process_id = latest.and_then(|receipt| receipt.process_id); - let mut process_observation = process_id - .map(observe_native_process) - .transpose()? - .map(|observation| observation.label().to_string()) - .unwrap_or_else(|| "missing".to_string()); - let process_identity_verified = if process_observation == "alive" { - latest.is_some_and(|receipt| { - verify_process_executable_identity(process_id, &receipt.executable_sha256) - }) - } else { - false - }; - if process_observation == "alive" && !process_identity_verified { - process_observation = "identity-unverified".to_string(); - } - let pulse = compact_last_service_pulse(&policy); - rows.push(ManagedServiceSightRow { - service_id: policy.service_id, - owner_daemon_id: policy.owner_daemon_id, - enabled: policy.enabled, - restart_mode: policy.restart_mode, - process_observation, - process_id, - process_identity_verified, - lifecycle_receipt_id: latest.map(|receipt| receipt.receipt_id.clone()), - lifecycle_status: latest.map(|receipt| receipt.status.clone()), - last_pulse_status: pulse - .as_ref() - .and_then(|value| value["status"].as_str()) - .map(str::to_string), - last_pulse_iteration: pulse.as_ref().and_then(|value| value["iteration"].as_u64()), - last_pulse_artifact: pulse - .as_ref() - .and_then(|value| value["routineArtifact"].as_str()) - .map(str::to_string), - private_state_exposed: false, - }); - } - Ok(managed_service_sight_report_from_rows(rows)) -} - -fn managed_service_sight_report_from_rows( - mut rows: Vec, -) -> ManagedServiceSightReport { - rows.sort_by(|left, right| left.service_id.cmp(&right.service_id)); - let alive_count = rows - .iter() - .filter(|row| row.process_observation == "alive") - .count(); - let attention_count = rows - .iter() - .filter(|row| row.enabled && row.process_observation != "alive") - .count(); - let tui_rows = rows - .iter() - .map(|row| { - format!( - "{} | service={} | owner={} | desired={} | restart={} | observed={} | pid={} | lifecycle={} | pulse={} | iteration={} | private=false", - if !row.enabled { - "DISABLED" - } else if row.process_observation == "alive" { - "READY" - } else { - "ATTN" - }, - row.service_id, - row.owner_daemon_id, - if row.enabled { "enabled" } else { "disabled" }, - row.restart_mode, - row.process_observation, - row.process_id.map(|value| value.to_string()).unwrap_or_else(|| "none".to_string()), - row.lifecycle_receipt_id.as_deref().unwrap_or("none"), - row.last_pulse_status.as_deref().unwrap_or("none"), - row.last_pulse_iteration.map(|value| value.to_string()).unwrap_or_else(|| "none".to_string()), - ) - }) - .collect(); - ManagedServiceSightReport { - status: if attention_count == 0 { - "ready" - } else { - "attention" - } - .to_string(), - alive_count, - attention_count, - rows, - tui_rows, - private_state_exposed: false, - } -} - -fn verify_process_executable_identity(process_id: Option, expected_sha256: &str) -> bool { - let Some(process_id) = process_id else { - return false; - }; - if expected_sha256.trim().is_empty() { - return false; - } - let Ok(Some(path)) = native_process_executable_path(process_id) else { - return false; - }; - let Ok(bytes) = fs::read(path) else { - return false; - }; - format!("{:x}", Sha256::digest(bytes)).eq_ignore_ascii_case(expected_sha256.trim()) -} - -fn managed_service_overview_requires_attention(report: &ManagedServiceSightReport) -> bool { - report.attention_count > 0 -} - -fn compact_last_service_pulse( - policy: &EpiphanyCultMeshManagedServicePolicyEntry, -) -> Option { - let text = fs::read_to_string(&policy.stdout_artifact).ok()?; - text.lines() - .rev() - .filter_map(|line| serde_json::from_str::(line).ok()) - .find(|value| value["schemaVersion"] == "epiphany.heartbeat.serve_pulse.v0") -} - -struct DaemonRestartPolicyDirectoryReport { - status: String, - lifecycle_owner: String, - hosted_body: String, - rows: Vec, - tui_rows: Vec, - covered_count: usize, - enabled_count: usize, - disabled_count: usize, - missing_count: usize, - attention_count: usize, - observed_daemon_count: usize, - private_state_exposed: bool, -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -struct DaemonRestartPolicyDirectoryRow { - cluster_id: String, - display_name: String, - lifecycle_owner: String, - hosted_body: String, - daemon_id: String, - daemon_status: String, - policy_id: String, - policy_status: String, - enabled: bool, - cooldown_seconds: i64, - reconcile_interval_seconds: i64, - heartbeat_stale_seconds: i64, - failure_count: u32, - last_result_status: String, - last_attempt_utc: Option, - last_reconcile_utc: Option, - follow_up_command: String, - private_state_exposed: bool, -} - -struct SwarmOverviewReport { - status: String, - liveness_status: String, - recovery_status: String, - recommended_action: String, - recommended_wrapper_mode: String, - recommended_wrapper_command: String, - service_lifecycle_recommended_action: String, - service_lifecycle_recommended_wrapper_mode: String, - service_lifecycle_recommended_wrapper_command: String, - swarm_action_rows: Vec, - swarm_action_tui_rows: Vec, - attention_daemon_ids: Vec, - attention_tool_host_daemon_ids: Vec, - tool_host_attention_rows: Vec, - tool_host_attention_tui_rows: Vec, - service_lifecycle_attention_rows: Vec, - service_lifecycle_attention_tui_rows: Vec, - managed_service_report: ManagedServiceSightReport, - service_execution_failed_check_count: usize, - service_execution_missing_check_count: usize, - service_execution_failed_check_rows: Vec, - service_execution_failed_check_tui_rows: Vec, - repo_work_map_rows: Vec, - repo_work_map_tui_rows: Vec, - repo_work_map_semantic_rows: Vec, - repo_work_map_semantic_tui_rows: Vec, - repo_work_map_family_lens_rows: Vec, - repo_work_map_family_lens_tui_rows: Vec, - repo_work_map_path_lens_rows: Vec, - repo_work_map_path_lens_tui_rows: Vec, - repo_work_map_branch_lens_rows: Vec, - repo_work_map_branch_lens_tui_rows: Vec, - repo_work_map_stage_lens_rows: Vec, - repo_work_map_stage_lens_tui_rows: Vec, - repo_work_map_gate_lens_rows: Vec, - repo_work_map_gate_lens_tui_rows: Vec, - repo_work_map_closure_rows: Vec, - repo_work_map_closure_tui_rows: Vec, - repo_work_map_acceptance_rows: Vec, - repo_work_map_acceptance_tui_rows: Vec, - repo_work_overview_rows: Vec, - repo_work_overview_tui_rows: Vec, - repo_work_readiness_rows: Vec, - repo_work_readiness_tui_rows: Vec, - repo_work_public_proof_rows: Vec, - repo_work_public_proof_tui_rows: Vec, - idunn_deployment_receipt_rows: Vec, - idunn_deployment_receipt_tui_rows: Vec, - latest_repo_work_overview_id: Option, - latest_repo_work_map_entry: Option, - latest_repo_work_gate: Option, - latest_repo_work_blocker: Option, - latest_repo_work_next_safe_move: Option, - latest_repo_work_readiness: Option, - latest_repo_work_public_proof: Option, - latest_idunn_deployment_receipt: Option, - latest_idunn_aftercare_audit_receipt: Option, - topology_report: ClusterTopologyReport, - daemon_report: DaemonLivenessReport, - surface_report: EveSurfaceReport, - tool_report: DaemonToolDirectoryReport, - policy_report: DaemonRestartPolicyDirectoryReport, - private_state_exposed: bool, -} - -#[derive(Clone, Serialize)] -#[serde(rename_all = "camelCase")] -struct RepoWorkOverviewRow { - overview_id: String, - workspace: String, - item: String, - branch: String, - current_gate: String, - blocker: String, - next_safe_move: String, - changed_path_count: usize, - commit_sha: String, - soul_verdict: String, - publication_status: String, - sync_status: String, - proof_bundle_ref: String, - private_state_exposed: bool, -} - -#[derive(Clone, Serialize)] -#[serde(rename_all = "camelCase")] -struct RepoWorkMapRow { - status: String, - reason: String, - map_entry_id: String, - workspace: String, - item: String, - branch: String, - changed_path_count: usize, - commit_sha: String, - safe_action_family: String, - modeling_summary: String, - soul_verdict_receipt_id: String, - mind_gateway_review_id: String, - mind_state_commit_receipt_id: String, - publication_gate: String, - durable_state_admitted: bool, - source_store_path: String, - private_state_exposed: bool, -} - -#[derive(Clone, Serialize)] -#[serde(rename_all = "camelCase")] -struct RepoWorkMapSemanticRow { - item: String, - branch: String, - stage: String, - stage_owner: String, - publication_gate: String, - gate_owner: String, - safe_action_family: String, - changed_path_count: usize, - latest_commit_sha: String, - latest_mind_state_commit_receipt_id: String, - modeling_summary: String, - sight_only: bool, - private_state_exposed: bool, -} - -#[derive(Clone, Serialize)] -#[serde(rename_all = "camelCase")] -struct RepoWorkMapFamilyLensRow { - safe_action_family: String, - item_count: usize, - changed_path_count: usize, - latest_item: String, - latest_commit_sha: String, - latest_mind_state_commit_receipt_id: String, - publication_gate: String, - private_state_exposed: bool, -} - -#[derive(Clone, Serialize)] -#[serde(rename_all = "camelCase")] -struct RepoWorkMapPathLensRow { - path: String, - item_count: usize, - safe_action_families: Vec, - latest_item: String, - latest_commit_sha: String, - latest_mind_state_commit_receipt_id: String, - publication_gate: String, - private_state_exposed: bool, -} - -#[derive(Clone, Serialize)] -#[serde(rename_all = "camelCase")] -struct RepoWorkMapBranchLensRow { - branch: String, - item_count: usize, - changed_path_count: usize, - safe_action_families: Vec, - latest_item: String, - latest_commit_sha: String, - latest_mind_state_commit_receipt_id: String, - publication_gate: String, - private_state_exposed: bool, -} - -#[derive(Clone, Serialize)] -#[serde(rename_all = "camelCase")] -struct RepoWorkMapStageLensRow { - stage: String, - owner: String, - item_count: usize, - changed_path_count: usize, - safe_action_families: Vec, - latest_item: String, - latest_commit_sha: String, - latest_mind_state_commit_receipt_id: String, - publication_gate: String, - private_state_exposed: bool, -} - -#[derive(Clone, Serialize)] -#[serde(rename_all = "camelCase")] -struct RepoWorkMapGateLensRow { - publication_gate: String, - owner: String, - item_count: usize, - changed_path_count: usize, - stages: Vec, - safe_action_families: Vec, - latest_item: String, - latest_commit_sha: String, - latest_mind_state_commit_receipt_id: String, - sight_only: bool, - private_state_exposed: bool, -} - -#[derive(Clone, Serialize)] -#[serde(rename_all = "camelCase")] -struct RepoWorkMapClosureRow { - item: String, - branch: String, - safe_action_family: String, - changed_path_count: usize, - commit_sha: String, - soul_verdict_receipt_id: String, - mind_gateway_review_id: String, - mind_state_commit_receipt_id: String, - durable_state_admitted: bool, - publication_gate: String, - publication_authorized: bool, - merge_authorized: bool, - service_lifecycle_authorized: bool, - deployment_execution_authorized: bool, - cross_repo_mutation_authorized: bool, - sight_only: bool, - private_state_exposed: bool, -} - -#[derive(Clone, Serialize)] -#[serde(rename_all = "camelCase")] -struct RepoWorkMapAcceptanceRow { - item: String, - branch: String, - safe_action_family: String, - acceptance_status: String, - soul_closed: bool, - modeling_closed: bool, - mind_committed: bool, - durable_state_admitted: bool, - bifrost_gate_named: bool, - soul_verdict_receipt_id: String, - modeling_summary: String, - mind_gateway_review_id: String, - mind_state_commit_receipt_id: String, - publication_gate: String, - sight_only: bool, - private_state_exposed: bool, -} - -#[derive(Clone, Serialize)] -#[serde(rename_all = "camelCase")] -struct RepoWorkPublicProofRow { - public_proof_id: String, - workspace: String, - item: String, - branch: String, - current_gate: String, - blocker: String, - next_safe_move: String, - changed_path_count: usize, - commit_sha: String, - soul_verdict: String, - upstream_main_synced: bool, - artifact_row_count: u32, - publication_row_count: u32, - public_proof_ref: String, - public_proof_sha256: String, - private_state_exposed: bool, -} - -#[derive(Clone, Serialize)] -#[serde(rename_all = "camelCase")] -struct RepoWorkReadinessRow { - readiness_id: String, - workspace: String, - item: String, - status: String, - missing_required_count: u32, - satisfied_required_count: u32, - readiness_receipt_ref: String, - overview_receipt_ref: String, - proof_bundle_id: String, - missing_kinds: Vec, - sight_only: bool, - readiness_approval_authorized: bool, - publication_authorized: bool, - service_lifecycle_authority: bool, - hands_action_authorized: bool, - private_state_exposed: bool, -} - -#[derive(Clone, Serialize)] -#[serde(rename_all = "camelCase")] -struct IdunnDeploymentReceiptRow { - receipt_family: String, - receipt_id: String, - status: String, - owner: String, - authority_gate: String, - watched_ref: String, - checked_ref: String, - source_commit: String, - result_ref: String, - deployment_receipt_id: String, - aftercare_audit_ref: String, - follow_up_command: String, - private_state_exposed: bool, -} - -#[derive(Clone, Serialize)] -#[serde(rename_all = "camelCase")] -struct SwarmActionRow { - priority: u32, - family: String, - status: String, - lifecycle_owner: String, - hosted_body: String, - action: String, - wrapper_mode: String, - wrapper_command: String, - operator_artifact_ref: String, - operator_artifact_status: String, - operator_artifact_sha256: String, - operator_artifact_execution_command: String, - operator_aftercare_command: String, - completion_audit_wrapper_mode: String, - completion_audit_wrapper_command: String, - authority_gate: String, - effect_class: String, - mutates_state: bool, - requires_elevated_authority: bool, - service_execution_failed_check_count: usize, - service_execution_missing_check_count: usize, - service_id: String, - service_route: String, - reason: String, - private_state_exposed: bool, -} - -struct ServiceExecutionRunbookAction { - route: String, - family: String, - follow_up_command: String, - artifact_ref: String, - private_state_exposed: bool, - failed_check_count: usize, - missing_check_count: usize, - service_id: String, - completion_audit_wrapper_mode: String, - completion_audit_wrapper_command: String, -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -struct SwarmOverviewOutput { - schema_version: String, - status: String, - liveness_status: String, - recovery_status: String, - recommended_action: String, - recommended_wrapper_mode: String, - recommended_wrapper_command: String, - service_lifecycle_recommended_action: String, - service_lifecycle_recommended_wrapper_mode: String, - service_lifecycle_recommended_wrapper_command: String, - swarm_action_count: usize, - swarm_action_rows: Vec, - swarm_action_tui_rows: Vec, - store: PathBuf, - runtime_id: String, - observed_daemon_count: usize, - declared_faculty_count: usize, - declared_private_verse_route_count: usize, - declared_daemon_target_count: usize, - non_ready_daemon_count: usize, - attention_daemon_ids: Vec, - surface_count: usize, - public_discussion_surface_count: usize, - tool_count: usize, - tool_host_ready_count: usize, - tool_host_attention_count: usize, - attention_tool_host_daemon_ids: Vec, - tool_host_attention_rows: Vec, - tool_host_attention_tui_rows: Vec, - service_lifecycle_attention_count: usize, - service_lifecycle_attention_rows: Vec, - managed_service_count: usize, - managed_service_alive_count: usize, - managed_service_attention_count: usize, - managed_service_rows: Vec, - service_execution_failed_check_count: usize, - service_execution_missing_check_count: usize, - service_execution_failed_check_rows: Vec, - service_execution_failed_check_tui_rows: Vec, - repo_work_map_count: usize, - repo_work_map_rows: Vec, - repo_work_map_tui_rows: Vec, - repo_work_map_semantic_count: usize, - repo_work_map_semantic_rows: Vec, - repo_work_map_semantic_tui_rows: Vec, - repo_work_map_family_lens_count: usize, - repo_work_map_family_lens_rows: Vec, - repo_work_map_family_lens_tui_rows: Vec, - repo_work_map_path_lens_count: usize, - repo_work_map_path_lens_rows: Vec, - repo_work_map_path_lens_tui_rows: Vec, - repo_work_map_branch_lens_count: usize, - repo_work_map_branch_lens_rows: Vec, - repo_work_map_branch_lens_tui_rows: Vec, - repo_work_map_stage_lens_count: usize, - repo_work_map_stage_lens_rows: Vec, - repo_work_map_stage_lens_tui_rows: Vec, - repo_work_map_gate_lens_count: usize, - repo_work_map_gate_lens_rows: Vec, - repo_work_map_gate_lens_tui_rows: Vec, - repo_work_map_closure_count: usize, - repo_work_map_closure_rows: Vec, - repo_work_map_closure_tui_rows: Vec, - repo_work_map_acceptance_count: usize, - repo_work_map_acceptance_rows: Vec, - repo_work_map_acceptance_tui_rows: Vec, - repo_work_overview_count: usize, - repo_work_overview_rows: Vec, - repo_work_overview_tui_rows: Vec, - repo_work_readiness_count: usize, - repo_work_readiness_rows: Vec, - repo_work_readiness_tui_rows: Vec, - repo_work_public_proof_count: usize, - repo_work_public_proof_rows: Vec, - repo_work_public_proof_tui_rows: Vec, - idunn_deployment_receipt_count: usize, - idunn_deployment_receipt_rows: Vec, - idunn_deployment_receipt_tui_rows: Vec, - latest_repo_work_overview: Option, - latest_repo_work_map_entry: Option, - latest_repo_work_gate: Option, - latest_repo_work_blocker: Option, - latest_repo_work_next_safe_move: Option, - latest_repo_work_readiness: Option, - latest_repo_work_public_proof: Option, - latest_idunn_deployment_receipt: Option, - latest_idunn_aftercare_audit_receipt: Option, - policy_covered_count: usize, - policy_enabled_count: usize, - policy_disabled_count: usize, - policy_missing_count: usize, - policy_attention_count: usize, - commands: serde_json::Value, - private_state_exposed: bool, - topology_tui_rows: Vec, - daemon_tui_rows: Vec, - surface_tui_rows: Vec, - tool_tui_rows: Vec, - service_lifecycle_attention_tui_rows: Vec, - policy_tui_rows: Vec, -} - -impl SwarmOverviewOutput { - fn from_report( - store: PathBuf, - runtime_id: String, - report: SwarmOverviewReport, - commands: serde_json::Value, - ) -> Self { - Self { - schema_version: "epiphany.cultmesh.swarm_overview_report.v0".to_string(), - status: report.status, - liveness_status: report.liveness_status, - recovery_status: report.recovery_status, - recommended_action: report.recommended_action, - recommended_wrapper_mode: report.recommended_wrapper_mode, - recommended_wrapper_command: report.recommended_wrapper_command, - service_lifecycle_recommended_action: report.service_lifecycle_recommended_action, - service_lifecycle_recommended_wrapper_mode: report - .service_lifecycle_recommended_wrapper_mode, - service_lifecycle_recommended_wrapper_command: report - .service_lifecycle_recommended_wrapper_command, - swarm_action_count: report.swarm_action_rows.len(), - swarm_action_rows: report.swarm_action_rows, - swarm_action_tui_rows: report.swarm_action_tui_rows, - store, - runtime_id, - observed_daemon_count: report.daemon_report.rows.len(), - declared_faculty_count: report.topology_report.rows.len(), - declared_private_verse_route_count: report - .topology_report - .declared_private_verse_route_count, - declared_daemon_target_count: report.topology_report.declared_daemon_target_count, - non_ready_daemon_count: report.daemon_report.non_ready_count, - attention_daemon_ids: report.attention_daemon_ids, - surface_count: report.surface_report.rows.len(), - public_discussion_surface_count: report.surface_report.public_discussion_count, - tool_count: report.tool_report.rows.len(), - tool_host_ready_count: report.tool_report.host_ready_count, - tool_host_attention_count: report.tool_report.host_attention_count, - attention_tool_host_daemon_ids: report.attention_tool_host_daemon_ids, - tool_host_attention_rows: report.tool_host_attention_rows, - tool_host_attention_tui_rows: report.tool_host_attention_tui_rows, - service_lifecycle_attention_count: report.service_lifecycle_attention_rows.len(), - service_lifecycle_attention_rows: report.service_lifecycle_attention_rows, - managed_service_count: report.managed_service_report.rows.len(), - managed_service_alive_count: report.managed_service_report.alive_count, - managed_service_attention_count: report.managed_service_report.attention_count, - managed_service_rows: report.managed_service_report.rows, - service_execution_failed_check_count: report.service_execution_failed_check_count, - service_execution_missing_check_count: report.service_execution_missing_check_count, - service_execution_failed_check_rows: report.service_execution_failed_check_rows, - service_execution_failed_check_tui_rows: report.service_execution_failed_check_tui_rows, - repo_work_map_count: report.repo_work_map_rows.len(), - repo_work_map_rows: report.repo_work_map_rows, - repo_work_map_tui_rows: report.repo_work_map_tui_rows, - repo_work_map_semantic_count: report.repo_work_map_semantic_rows.len(), - repo_work_map_semantic_rows: report.repo_work_map_semantic_rows, - repo_work_map_semantic_tui_rows: report.repo_work_map_semantic_tui_rows, - repo_work_map_family_lens_count: report.repo_work_map_family_lens_rows.len(), - repo_work_map_family_lens_rows: report.repo_work_map_family_lens_rows, - repo_work_map_family_lens_tui_rows: report.repo_work_map_family_lens_tui_rows, - repo_work_map_path_lens_count: report.repo_work_map_path_lens_rows.len(), - repo_work_map_path_lens_rows: report.repo_work_map_path_lens_rows, - repo_work_map_path_lens_tui_rows: report.repo_work_map_path_lens_tui_rows, - repo_work_map_branch_lens_count: report.repo_work_map_branch_lens_rows.len(), - repo_work_map_branch_lens_rows: report.repo_work_map_branch_lens_rows, - repo_work_map_branch_lens_tui_rows: report.repo_work_map_branch_lens_tui_rows, - repo_work_map_stage_lens_count: report.repo_work_map_stage_lens_rows.len(), - repo_work_map_stage_lens_rows: report.repo_work_map_stage_lens_rows, - repo_work_map_stage_lens_tui_rows: report.repo_work_map_stage_lens_tui_rows, - repo_work_map_gate_lens_count: report.repo_work_map_gate_lens_rows.len(), - repo_work_map_gate_lens_rows: report.repo_work_map_gate_lens_rows, - repo_work_map_gate_lens_tui_rows: report.repo_work_map_gate_lens_tui_rows, - repo_work_map_closure_count: report.repo_work_map_closure_rows.len(), - repo_work_map_closure_rows: report.repo_work_map_closure_rows, - repo_work_map_closure_tui_rows: report.repo_work_map_closure_tui_rows, - repo_work_map_acceptance_count: report.repo_work_map_acceptance_rows.len(), - repo_work_map_acceptance_rows: report.repo_work_map_acceptance_rows, - repo_work_map_acceptance_tui_rows: report.repo_work_map_acceptance_tui_rows, - repo_work_overview_count: report.repo_work_overview_rows.len(), - repo_work_overview_rows: report.repo_work_overview_rows, - repo_work_overview_tui_rows: report.repo_work_overview_tui_rows, - repo_work_readiness_count: report.repo_work_readiness_rows.len(), - repo_work_readiness_rows: report.repo_work_readiness_rows, - repo_work_readiness_tui_rows: report.repo_work_readiness_tui_rows, - repo_work_public_proof_count: report.repo_work_public_proof_rows.len(), - repo_work_public_proof_rows: report.repo_work_public_proof_rows, - repo_work_public_proof_tui_rows: report.repo_work_public_proof_tui_rows, - idunn_deployment_receipt_count: report.idunn_deployment_receipt_rows.len(), - idunn_deployment_receipt_rows: report.idunn_deployment_receipt_rows, - idunn_deployment_receipt_tui_rows: report.idunn_deployment_receipt_tui_rows, - latest_repo_work_overview: report.latest_repo_work_overview_id, - latest_repo_work_map_entry: report.latest_repo_work_map_entry, - latest_repo_work_gate: report.latest_repo_work_gate, - latest_repo_work_blocker: report.latest_repo_work_blocker, - latest_repo_work_next_safe_move: report.latest_repo_work_next_safe_move, - latest_repo_work_readiness: report.latest_repo_work_readiness, - latest_repo_work_public_proof: report.latest_repo_work_public_proof, - latest_idunn_deployment_receipt: report.latest_idunn_deployment_receipt, - latest_idunn_aftercare_audit_receipt: report.latest_idunn_aftercare_audit_receipt, - policy_covered_count: report.policy_report.covered_count, - policy_enabled_count: report.policy_report.enabled_count, - policy_disabled_count: report.policy_report.disabled_count, - policy_missing_count: report.policy_report.missing_count, - policy_attention_count: report.policy_report.attention_count, - commands, - private_state_exposed: report.private_state_exposed, - topology_tui_rows: report.topology_report.tui_rows, - daemon_tui_rows: report.daemon_report.tui_rows, - surface_tui_rows: report.surface_report.tui_rows, - tool_tui_rows: report.tool_report.tui_rows, - service_lifecycle_attention_tui_rows: report.service_lifecycle_attention_tui_rows, - policy_tui_rows: report.policy_report.tui_rows, - } - } -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -struct SwarmTriageOutput { - schema_version: String, - status: String, - overview_status: String, - liveness_status: String, - recovery_status: String, - recommended_action: String, - recommended_wrapper_mode: String, - recommended_wrapper_command: String, - service_lifecycle_recommended_action: String, - service_lifecycle_recommended_wrapper_mode: String, - service_lifecycle_recommended_wrapper_command: String, - swarm_action_count: usize, - swarm_action_rows: Vec, - swarm_action_tui_rows: Vec, - store: PathBuf, - runtime_id: String, - declared_faculty_count: usize, - declared_private_verse_route_count: usize, - declared_daemon_target_count: usize, - observed_daemon_count: usize, - non_ready_daemon_count: usize, - attention_daemon_ids: Vec, - attention_tool_host_daemon_ids: Vec, - tool_host_attention_count: usize, - tool_host_attention_rows: Vec, - tool_host_attention_tui_rows: Vec, - service_lifecycle_attention_count: usize, - service_lifecycle_attention_rows: Vec, - service_lifecycle_attention_tui_rows: Vec, - service_execution_failed_check_count: usize, - service_execution_missing_check_count: usize, - service_execution_failed_check_rows: Vec, - service_execution_failed_check_tui_rows: Vec, - repo_work_map_count: usize, - repo_work_map_rows: Vec, - repo_work_map_tui_rows: Vec, - repo_work_map_semantic_count: usize, - repo_work_map_semantic_rows: Vec, - repo_work_map_semantic_tui_rows: Vec, - repo_work_map_family_lens_count: usize, - repo_work_map_family_lens_rows: Vec, - repo_work_map_family_lens_tui_rows: Vec, - repo_work_map_path_lens_count: usize, - repo_work_map_path_lens_rows: Vec, - repo_work_map_path_lens_tui_rows: Vec, - repo_work_map_branch_lens_count: usize, - repo_work_map_branch_lens_rows: Vec, - repo_work_map_branch_lens_tui_rows: Vec, - repo_work_map_stage_lens_count: usize, - repo_work_map_stage_lens_rows: Vec, - repo_work_map_stage_lens_tui_rows: Vec, - repo_work_map_gate_lens_count: usize, - repo_work_map_gate_lens_rows: Vec, - repo_work_map_gate_lens_tui_rows: Vec, - repo_work_map_closure_count: usize, - repo_work_map_closure_rows: Vec, - repo_work_map_closure_tui_rows: Vec, - repo_work_map_acceptance_count: usize, - repo_work_map_acceptance_rows: Vec, - repo_work_map_acceptance_tui_rows: Vec, - repo_work_overview_count: usize, - repo_work_overview_rows: Vec, - repo_work_overview_tui_rows: Vec, - repo_work_readiness_count: usize, - repo_work_readiness_rows: Vec, - repo_work_readiness_tui_rows: Vec, - repo_work_public_proof_count: usize, - repo_work_public_proof_rows: Vec, - repo_work_public_proof_tui_rows: Vec, - idunn_deployment_receipt_count: usize, - idunn_deployment_receipt_rows: Vec, - idunn_deployment_receipt_tui_rows: Vec, - latest_repo_work_overview: Option, - latest_repo_work_map_entry: Option, - latest_repo_work_gate: Option, - latest_repo_work_blocker: Option, - latest_repo_work_next_safe_move: Option, - latest_repo_work_readiness: Option, - latest_repo_work_public_proof: Option, - latest_idunn_deployment_receipt: Option, - latest_idunn_aftercare_audit_receipt: Option, - poked_daemon_count: usize, - pokes: Vec, - commands: serde_json::Value, - topology_tui_rows: Vec, - daemon_tui_rows: Vec, - private_state_exposed: bool, -} - -impl SwarmTriageOutput { - fn from_report( - store: PathBuf, - runtime_id: String, - report: SwarmOverviewReport, - pokes: Vec, - ) -> Self { - let poked_daemon_count = pokes.len(); - Self { - schema_version: "epiphany.cultmesh.swarm_triage_report.v0".to_string(), - status: if poked_daemon_count == 0 { - "noop".to_string() - } else { - "ok".to_string() - }, - overview_status: report.status, - liveness_status: report.liveness_status, - recovery_status: report.recovery_status, - recommended_action: report.recommended_action, - recommended_wrapper_mode: report.recommended_wrapper_mode, - recommended_wrapper_command: report.recommended_wrapper_command, - service_lifecycle_recommended_action: report.service_lifecycle_recommended_action, - service_lifecycle_recommended_wrapper_mode: report - .service_lifecycle_recommended_wrapper_mode, - service_lifecycle_recommended_wrapper_command: report - .service_lifecycle_recommended_wrapper_command, - swarm_action_count: report.swarm_action_rows.len(), - swarm_action_rows: report.swarm_action_rows, - swarm_action_tui_rows: report.swarm_action_tui_rows, - store, - runtime_id, - declared_faculty_count: report.topology_report.rows.len(), - declared_private_verse_route_count: report - .topology_report - .declared_private_verse_route_count, - declared_daemon_target_count: report.topology_report.declared_daemon_target_count, - observed_daemon_count: report.daemon_report.rows.len(), - non_ready_daemon_count: report.daemon_report.non_ready_count, - attention_daemon_ids: report.attention_daemon_ids, - attention_tool_host_daemon_ids: report.attention_tool_host_daemon_ids, - tool_host_attention_count: report.tool_host_attention_rows.len(), - tool_host_attention_rows: report.tool_host_attention_rows, - tool_host_attention_tui_rows: report.tool_host_attention_tui_rows, - service_lifecycle_attention_count: report.service_lifecycle_attention_rows.len(), - service_lifecycle_attention_rows: report.service_lifecycle_attention_rows, - service_lifecycle_attention_tui_rows: report.service_lifecycle_attention_tui_rows, - service_execution_failed_check_count: report.service_execution_failed_check_count, - service_execution_missing_check_count: report.service_execution_missing_check_count, - service_execution_failed_check_rows: report.service_execution_failed_check_rows, - service_execution_failed_check_tui_rows: report.service_execution_failed_check_tui_rows, - repo_work_map_count: report.repo_work_map_rows.len(), - repo_work_map_rows: report.repo_work_map_rows, - repo_work_map_tui_rows: report.repo_work_map_tui_rows, - repo_work_map_semantic_count: report.repo_work_map_semantic_rows.len(), - repo_work_map_semantic_rows: report.repo_work_map_semantic_rows, - repo_work_map_semantic_tui_rows: report.repo_work_map_semantic_tui_rows, - repo_work_map_family_lens_count: report.repo_work_map_family_lens_rows.len(), - repo_work_map_family_lens_rows: report.repo_work_map_family_lens_rows, - repo_work_map_family_lens_tui_rows: report.repo_work_map_family_lens_tui_rows, - repo_work_map_path_lens_count: report.repo_work_map_path_lens_rows.len(), - repo_work_map_path_lens_rows: report.repo_work_map_path_lens_rows, - repo_work_map_path_lens_tui_rows: report.repo_work_map_path_lens_tui_rows, - repo_work_map_branch_lens_count: report.repo_work_map_branch_lens_rows.len(), - repo_work_map_branch_lens_rows: report.repo_work_map_branch_lens_rows, - repo_work_map_branch_lens_tui_rows: report.repo_work_map_branch_lens_tui_rows, - repo_work_map_stage_lens_count: report.repo_work_map_stage_lens_rows.len(), - repo_work_map_stage_lens_rows: report.repo_work_map_stage_lens_rows, - repo_work_map_stage_lens_tui_rows: report.repo_work_map_stage_lens_tui_rows, - repo_work_map_gate_lens_count: report.repo_work_map_gate_lens_rows.len(), - repo_work_map_gate_lens_rows: report.repo_work_map_gate_lens_rows, - repo_work_map_gate_lens_tui_rows: report.repo_work_map_gate_lens_tui_rows, - repo_work_map_closure_count: report.repo_work_map_closure_rows.len(), - repo_work_map_closure_rows: report.repo_work_map_closure_rows, - repo_work_map_closure_tui_rows: report.repo_work_map_closure_tui_rows, - repo_work_map_acceptance_count: report.repo_work_map_acceptance_rows.len(), - repo_work_map_acceptance_rows: report.repo_work_map_acceptance_rows, - repo_work_map_acceptance_tui_rows: report.repo_work_map_acceptance_tui_rows, - repo_work_overview_count: report.repo_work_overview_rows.len(), - repo_work_overview_rows: report.repo_work_overview_rows, - repo_work_overview_tui_rows: report.repo_work_overview_tui_rows, - repo_work_readiness_count: report.repo_work_readiness_rows.len(), - repo_work_readiness_rows: report.repo_work_readiness_rows, - repo_work_readiness_tui_rows: report.repo_work_readiness_tui_rows, - repo_work_public_proof_count: report.repo_work_public_proof_rows.len(), - repo_work_public_proof_rows: report.repo_work_public_proof_rows, - repo_work_public_proof_tui_rows: report.repo_work_public_proof_tui_rows, - idunn_deployment_receipt_count: report.idunn_deployment_receipt_rows.len(), - idunn_deployment_receipt_rows: report.idunn_deployment_receipt_rows, - idunn_deployment_receipt_tui_rows: report.idunn_deployment_receipt_tui_rows, - latest_repo_work_overview: report.latest_repo_work_overview_id, - latest_repo_work_map_entry: report.latest_repo_work_map_entry, - latest_repo_work_gate: report.latest_repo_work_gate, - latest_repo_work_blocker: report.latest_repo_work_blocker, - latest_repo_work_next_safe_move: report.latest_repo_work_next_safe_move, - latest_repo_work_readiness: report.latest_repo_work_readiness, - latest_repo_work_public_proof: report.latest_repo_work_public_proof, - latest_idunn_deployment_receipt: report.latest_idunn_deployment_receipt, - latest_idunn_aftercare_audit_receipt: report.latest_idunn_aftercare_audit_receipt, - poked_daemon_count, - pokes, - commands: json!({ - "overview": "epiphany-verse-query swarm-overview", - "wrapperOverview": WRAPPER_OVERVIEW_COMMAND, - "wrapperSwarmOnlineRunbook": WRAPPER_SWARM_ONLINE_RUNBOOK_COMMAND, - "pokeNonReady": "epiphany-verse-query poke-down-daemons", - "wrapperPokeNonReady": WRAPPER_POKE_NON_READY_COMMAND, - "receipts": "epiphany-verse-query receipt-directory", - "wrapperReceipts": WRAPPER_RECEIPT_DIRECTORY_COMMAND, - "restartPolicies": "epiphany-verse-query restart-policy-directory", - "repoWorkOverview": "epiphany-work overview --workspace --item ", - "repoWorkMap": "epiphany-verse-query swarm-overview", - "idunnDeploymentAftercareAudit": DIRECT_IDUNN_DEPLOYMENT_AFTERCARE_AUDIT_COMMAND, - "wrapperRestartPolicies": WRAPPER_SERVICE_POLICY_DIRECTORY_COMMAND, - }), - topology_tui_rows: report.topology_report.tui_rows, - daemon_tui_rows: report.daemon_report.tui_rows, - private_state_exposed: report.private_state_exposed, - } - } -} - -struct ReceiptDirectoryReport { - status: String, - rows: Vec, - tui_rows: Vec, - attention_route_rows: Vec, - present_row_count: usize, - absent_row_count: usize, - ready_row_count: usize, - attention_row_count: usize, - missing_row_count: usize, - artifact_none_count: usize, - artifact_external_ref_count: usize, - artifact_present_count: usize, - artifact_missing_count: usize, - private_state_exposed: bool, -} - -#[derive(Clone, Serialize)] -#[serde(rename_all = "camelCase")] -struct ReceiptDirectoryRow { - family: String, - owner: String, - document_kind: String, - latest_id: String, - status: String, - route: String, - service_id: String, - service_route: String, - follow_up_command: String, - artifact_ref: String, - artifact_status: String, - artifact_sha256: String, - present: bool, - private_state_exposed: bool, -} - -#[derive(Clone, Serialize)] -#[serde(rename_all = "camelCase")] -struct DaemonToolDirectoryRow { - capability_id: String, - host_cluster_id: String, - host_daemon_id: String, - host_display_name: String, - host_status: String, - eve_surface_id: String, - tool_name: String, - operation: String, - input_contract_type: String, - receipt_contract_type: String, - available_to_all_agents: bool, - requires_receipt: bool, - authority_gate: String, - private_state_exposed: bool, -} - -fn daemon_tool_directory_report( - directory: &[( - EpiphanyCultMeshClusterTopologyEntry, - EpiphanyCultMeshDaemonStatusEntry, - EpiphanyCultMeshDaemonToolCapabilityEntry, - )], -) -> DaemonToolDirectoryReport { - let mut rows = Vec::new(); - let mut tui_rows = Vec::new(); - let mut host_ready_count = 0_usize; - let mut host_attention_count = 0_usize; - for (cluster, status, capability) in directory { - let host_status = status.status.clone(); - let host_display_name = cluster.display_name.clone(); - if host_status == "ready" { - host_ready_count += 1; - } else { - host_attention_count += 1; - } - let row = DaemonToolDirectoryRow { - capability_id: capability.capability_id.clone(), - host_cluster_id: capability.host_cluster_id.clone(), - host_daemon_id: capability.host_daemon_id.clone(), - host_display_name, - host_status, - eve_surface_id: capability.eve_surface_id.clone(), - tool_name: capability.tool_name.clone(), - operation: capability.operation.clone(), - input_contract_type: capability.input_contract_type.clone(), - receipt_contract_type: capability.receipt_contract_type.clone(), - available_to_all_agents: capability.available_to_all_agents, - requires_receipt: capability.requires_receipt, - authority_gate: capability.authority_gate.clone(), - private_state_exposed: capability.private_state_exposed, - }; - tui_rows.push(daemon_tool_directory_tui_row(&row)); - rows.push(row); - } - DaemonToolDirectoryReport { - rows, - tui_rows, - host_ready_count, - host_attention_count, - } -} - -fn daemon_tool_directory_tui_row(row: &DaemonToolDirectoryRow) -> String { - let compact_status = if row.host_status == "ready" { - "READY" - } else { - "POKE" - }; - let availability = if row.available_to_all_agents { - "allAgents=true" - } else { - "allAgents=false" - }; - let receipt = if row.requires_receipt { - "receipt=true" - } else { - "receipt=false" - }; - let private = if row.private_state_exposed { - "private=true" - } else { - "private=false" - }; - format!( - "{compact_status} | {} | {} | {} | {} | {} | authority={} | input={} | receiptType={} | {availability} | {receipt} | {private}", - row.host_display_name, - row.tool_name, - row.operation, - row.capability_id, - row.eve_surface_id, - row.authority_gate, - row.input_contract_type, - row.receipt_contract_type - ) -} - -fn swarm_action_rows( - liveness_status: &str, - tool_host_attention_rows: &[DaemonToolDirectoryRow], - policy_report: &DaemonRestartPolicyDirectoryReport, - service_lifecycle_attention_rows: &[ReceiptDirectoryRow], - service_execution_failed_check_rows: &[EpiphanyServiceExecutionAuditCheck], - service_execution_runbook_actions: &[ServiceExecutionRunbookAction], - repo_work_overviews: &[EpiphanyCultMeshRepoWorkOverviewEntry], - repo_work_public_proofs: &[EpiphanyCultMeshRepoWorkPublicProofEntry], - repo_work_readiness_reports: &[EpiphanyCultMeshRepoWorkReadinessEntry], -) -> (Vec, Vec) { - let mut rows = Vec::new(); - let mut service_execution_check_counts = BTreeMap::::new(); - for check in service_execution_failed_check_rows { - let Some(service_id) = check.service_id.as_deref() else { - continue; - }; - let counts = service_execution_check_counts - .entry(service_id.to_string()) - .or_insert((0, 0)); - counts.0 += 1; - if check.observed_status.is_none() { - counts.1 += 1; - } - } - if liveness_status != "ready" { - rows.push(SwarmActionRow { - priority: 10, - family: "daemon-liveness".to_string(), - status: "attention".to_string(), - lifecycle_owner: SERVICE_LIFECYCLE_OWNER.to_string(), - hosted_body: SERVICE_LIFECYCLE_HOSTED_BODY.to_string(), - action: "epiphany-verse-query poke-down-daemons".to_string(), - wrapper_mode: "swarm-poke-down".to_string(), - wrapper_command: WRAPPER_POKE_NON_READY_COMMAND.to_string(), - operator_artifact_ref: "none".to_string(), - operator_artifact_status: "none".to_string(), - operator_artifact_sha256: "none".to_string(), - operator_artifact_execution_command: "none".to_string(), - operator_aftercare_command: "none".to_string(), - completion_audit_wrapper_mode: "none".to_string(), - completion_audit_wrapper_command: "none".to_string(), - authority_gate: "daemon.lifecycle_poke".to_string(), - effect_class: "typed-lifecycle-poke".to_string(), - mutates_state: true, - requires_elevated_authority: false, - service_execution_failed_check_count: 0, - service_execution_missing_check_count: 0, - service_id: "none".to_string(), - service_route: "none".to_string(), - reason: "One or more daemon bodies are non-ready; poke liveness before trusting hosted tools.".to_string(), - private_state_exposed: false, - }); - } - if !tool_host_attention_rows.is_empty() { - let blocked = tool_host_attention_rows - .iter() - .map(|row| format!("{}:{}", row.host_daemon_id, row.capability_id)) - .collect::>() - .join(", "); - rows.push(SwarmActionRow { - priority: 20, - family: "daemon-tool-host".to_string(), - status: "attention".to_string(), - lifecycle_owner: "none".to_string(), - hosted_body: "none".to_string(), - action: "epiphany-verse-query tool-directory".to_string(), - wrapper_mode: "tool-directory".to_string(), - wrapper_command: "tools/epiphany_local_run.ps1 -Mode tool-directory".to_string(), - operator_artifact_ref: "none".to_string(), - operator_artifact_status: "none".to_string(), - operator_artifact_sha256: "none".to_string(), - operator_artifact_execution_command: "none".to_string(), - operator_aftercare_command: "none".to_string(), - completion_audit_wrapper_mode: "none".to_string(), - completion_audit_wrapper_command: "none".to_string(), - authority_gate: "daemon.tool_invocation_liveness_gate".to_string(), - effect_class: "read-only".to_string(), - mutates_state: false, - requires_elevated_authority: false, - service_execution_failed_check_count: 0, - service_execution_missing_check_count: 0, - service_id: "none".to_string(), - service_route: "none".to_string(), - reason: format!( - "Hosted tool capabilities are blocked by non-ready daemon bodies: {blocked}." - ), - private_state_exposed: tool_host_attention_rows - .iter() - .any(|row| row.private_state_exposed), - }); - } - if policy_report.status != "ok" { - rows.push(SwarmActionRow { - priority: 30, - family: "daemon-restart-policy".to_string(), - status: policy_report.status.clone(), - lifecycle_owner: SERVICE_LIFECYCLE_OWNER.to_string(), - hosted_body: SERVICE_LIFECYCLE_HOSTED_BODY.to_string(), - action: "epiphany-verse-query restart-policy-directory".to_string(), - wrapper_mode: "service-policy-directory".to_string(), - wrapper_command: WRAPPER_SERVICE_POLICY_DIRECTORY_COMMAND.to_string(), - operator_artifact_ref: "none".to_string(), - operator_artifact_status: "none".to_string(), - operator_artifact_sha256: "none".to_string(), - operator_artifact_execution_command: "none".to_string(), - operator_aftercare_command: "none".to_string(), - completion_audit_wrapper_mode: "none".to_string(), - completion_audit_wrapper_command: "none".to_string(), - authority_gate: "daemon.restart_policy".to_string(), - effect_class: "read-only".to_string(), - mutates_state: false, - requires_elevated_authority: false, - service_execution_failed_check_count: 0, - service_execution_missing_check_count: 0, - service_id: "none".to_string(), - service_route: "none".to_string(), - reason: format!( - "Restart policy coverage needs attention: missing={}, disabled={}, attention={}.", - policy_report.missing_count, - policy_report.disabled_count, - policy_report.attention_count - ), - private_state_exposed: policy_report.private_state_exposed, - }); - } - for (index, overview) in repo_work_overviews.iter().take(5).enumerate() { - rows.push(repo_work_overview_action_row(overview, 35 + index as u32)); - } - for (index, row) in service_lifecycle_attention_rows.iter().enumerate() { - let (failed_check_count, missing_check_count) = service_execution_check_counts - .get(&row.service_id) - .copied() - .unwrap_or((0, 0)); - rows.push(SwarmActionRow { - priority: 40 + index as u32, - family: "service-lifecycle".to_string(), - status: "attention".to_string(), - lifecycle_owner: SERVICE_LIFECYCLE_OWNER.to_string(), - hosted_body: SERVICE_LIFECYCLE_HOSTED_BODY.to_string(), - action: format!("epiphany-verse-query receipt-directory {} follow-up", row.family), - wrapper_mode: service_lifecycle_wrapper_mode_for_row(row).to_string(), - wrapper_command: row.follow_up_command.clone(), - operator_artifact_ref: row.artifact_ref.clone(), - operator_artifact_status: row.artifact_status.clone(), - operator_artifact_sha256: row.artifact_sha256.clone(), - operator_artifact_execution_command: "none".to_string(), - operator_aftercare_command: "none".to_string(), - completion_audit_wrapper_mode: "none".to_string(), - completion_audit_wrapper_command: "none".to_string(), - authority_gate: "daemon.service_lifecycle".to_string(), - effect_class: "service-lifecycle-readback".to_string(), - mutates_state: false, - requires_elevated_authority: false, - service_execution_failed_check_count: failed_check_count, - service_execution_missing_check_count: missing_check_count, - service_id: row.service_id.clone(), - service_route: row.service_route.clone(), - reason: format!( - "Windows service lifecycle receipt {} needs readback/audit before the daemon swarm can be called service-ready.", - row.route - ), - private_state_exposed: row.private_state_exposed, - }); - } - for (index, runbook_action) in service_execution_runbook_actions.iter().enumerate() { - let artifact_status = - service_execution_runbook_artifact_status(&runbook_action.artifact_ref).to_string(); - let ( - status, - action, - wrapper_mode, - wrapper_command, - authority_gate, - effect_class, - mutates_state, - requires_elevated_authority, - reason, - ) = if artifact_status == "missing" { - ( - "runbook-artifact-missing".to_string(), - format!( - "Regenerate sealed service execution runbook for {}; then rerun service execution audit.", - runbook_action.route - ), - runbook_action.family.clone(), - format!( - "{} # refreshes the missing sealed runbook artifact; do not request elevated service control until artifactStatus=present", - runbook_action.follow_up_command - ), - "daemon.service_lifecycle_runbook".to_string(), - "service-lifecycle-runbook-regeneration".to_string(), - false, - false, - "A service execution runbook receipt exists, but its local artifact is missing; regenerate the sealed runbook witness before requesting elevated service authority.".to_string(), - ) - } else { - ( - "operator-authority-required".to_string(), - format!( - "Run latest generated service execution runbook for {}; then rerun service execution audit.", - runbook_action.route - ), - "operator-elevated-runbook".to_string(), - format!( - "{} # refreshes the sealed runbook; execute the generated artifact only with explicit elevated operator authority", - runbook_action.follow_up_command - ), - "operator.elevated_windows_service_control".to_string(), - "elevated-service-control".to_string(), - true, - true, - "A sealed service execution runbook exists; completing service readiness now requires explicit elevated operator execution and follow-up audit receipts.".to_string(), - ) - }; - let operator_artifact_execution_command = if artifact_status == "present" { - elevated_powershell_runbook_command(&runbook_action.artifact_ref) - } else { - "none".to_string() - }; - let operator_aftercare_command = if artifact_status == "present" { - runbook_action.completion_audit_wrapper_command.clone() - } else { - "none".to_string() - }; - let operator_artifact_sha256 = - operator_artifact_sha256(&runbook_action.artifact_ref, &artifact_status); - rows.push(SwarmActionRow { - priority: 50 + index as u32, - family: "service-execution-authority".to_string(), - status, - lifecycle_owner: SERVICE_LIFECYCLE_OWNER.to_string(), - hosted_body: SERVICE_LIFECYCLE_HOSTED_BODY.to_string(), - action, - wrapper_mode, - wrapper_command, - operator_artifact_ref: runbook_action.artifact_ref.clone(), - operator_artifact_status: artifact_status, - operator_artifact_sha256, - operator_artifact_execution_command, - operator_aftercare_command, - completion_audit_wrapper_mode: runbook_action.completion_audit_wrapper_mode.clone(), - completion_audit_wrapper_command: runbook_action - .completion_audit_wrapper_command - .clone(), - authority_gate, - effect_class, - mutates_state, - requires_elevated_authority, - service_execution_failed_check_count: runbook_action.failed_check_count, - service_execution_missing_check_count: runbook_action.missing_check_count, - service_id: runbook_action.service_id.clone(), - service_route: runbook_action.route.clone(), - reason, - private_state_exposed: runbook_action.private_state_exposed, - }); - } - for (index, public_proof) in repo_work_public_proofs.iter().take(5).enumerate() { - rows.push(repo_work_public_proof_action_row( - public_proof, - 60 + index as u32, - )); - } - for (index, readiness) in repo_work_readiness_reports.iter().take(5).enumerate() { - rows.push(repo_work_readiness_action_row(readiness, 70 + index as u32)); - } - if rows.is_empty() { - rows.push(SwarmActionRow { - priority: 100, - family: "swarm".to_string(), - status: "ready".to_string(), - lifecycle_owner: "none".to_string(), - hosted_body: "none".to_string(), - action: "none".to_string(), - wrapper_mode: "none".to_string(), - wrapper_command: "none".to_string(), - operator_artifact_ref: "none".to_string(), - operator_artifact_status: "none".to_string(), - operator_artifact_sha256: "none".to_string(), - operator_artifact_execution_command: "none".to_string(), - operator_aftercare_command: "none".to_string(), - completion_audit_wrapper_mode: "none".to_string(), - completion_audit_wrapper_command: "none".to_string(), - authority_gate: "none".to_string(), - effect_class: "none".to_string(), - mutates_state: false, - requires_elevated_authority: false, - service_execution_failed_check_count: 0, - service_execution_missing_check_count: 0, - service_id: "none".to_string(), - service_route: "none".to_string(), - reason: "No liveness, restart-policy, tool-host, or service-lifecycle attention rows are present.".to_string(), - private_state_exposed: false, - }); - } - let tui_rows = rows - .iter() - .map(|row| swarm_action_tui_row(row)) - .collect::>(); - (rows, tui_rows) -} - -fn repo_work_overview_rows( - repo_work_overviews: &[EpiphanyCultMeshRepoWorkOverviewEntry], -) -> (Vec, Vec) { - let rows = repo_work_overviews - .iter() - .map(repo_work_overview_row) - .collect::>(); - let tui_rows = rows - .iter() - .map(repo_work_overview_tui_row) - .collect::>(); - (rows, tui_rows) -} - -fn repo_work_overview_row(overview: &EpiphanyCultMeshRepoWorkOverviewEntry) -> RepoWorkOverviewRow { - RepoWorkOverviewRow { - overview_id: overview.overview_id.clone(), - workspace: overview.workspace.clone(), - item: overview.item.clone(), - branch: overview.branch.clone(), - current_gate: "historical-only".to_string(), - blocker: "canonical-frontier-workflow-required".to_string(), - next_safe_move: "Canonical frontier workflow required; historical record grants no action." - .to_string(), - changed_path_count: overview.changed_paths.len(), - commit_sha: overview.commit_sha.clone(), - soul_verdict: overview.soul_verdict.clone(), - publication_status: "historical-only".to_string(), - sync_status: overview.sync_status.clone(), - proof_bundle_ref: overview.proof_bundle_ref.clone(), - private_state_exposed: overview.private_state_exposed, - } -} - -fn repo_work_overview_tui_row(row: &RepoWorkOverviewRow) -> String { - format!( - "REPO-WORK | item={} | gate={} | blocker={} | branch={} | commit={} | paths={} | publication={} | sync={} | proof={} | private={}", - row.item, - row.current_gate, - row.blocker, - row.branch, - row.commit_sha, - row.changed_path_count, - row.publication_status, - row.sync_status, - row.proof_bundle_ref, - row.private_state_exposed - ) -} - -fn repo_work_map_rows( - repo_work_map_entries: &[EpiphanyCultMeshRepoWorkMapEntry], -) -> (Vec, Vec) { - let rows = repo_work_map_entries - .iter() - .map(repo_work_map_row) - .collect::>(); - let tui_rows = rows.iter().map(repo_work_map_tui_row).collect::>(); - (rows, tui_rows) -} - -fn repo_work_map_row(entry: &EpiphanyCultMeshRepoWorkMapEntry) -> RepoWorkMapRow { - RepoWorkMapRow { - status: "historical-only".to_string(), - reason: "canonical frontier workflow required".to_string(), - map_entry_id: entry.map_entry_id.clone(), - workspace: entry.workspace.clone(), - item: entry.item.clone(), - branch: entry.branch.clone(), - changed_path_count: entry.changed_paths.len(), - commit_sha: entry.commit_sha.clone(), - safe_action_family: format!("historical:{}", entry.safe_action_family), - modeling_summary: "Historical record only; canonical frontier workflow required." - .to_string(), - soul_verdict_receipt_id: entry.soul_verdict_receipt_id.clone(), - mind_gateway_review_id: entry.mind_gateway_review_id.clone(), - mind_state_commit_receipt_id: entry.mind_state_commit_receipt_id.clone(), - publication_gate: "historical-only".to_string(), - durable_state_admitted: false, - source_store_path: entry.source_store_path.clone(), - private_state_exposed: entry.private_state_exposed, - } -} - -fn repo_work_map_tui_row(row: &RepoWorkMapRow) -> String { - format!( - "REPO-WORK-MAP | item={} | branch={} | commit={} | family={} | paths={} | soul={} | mind={} | gate={} | admitted={} | source={} | private={}", - row.item, - row.branch, - short_commit(&row.commit_sha), - row.safe_action_family, - row.changed_path_count, - row.soul_verdict_receipt_id, - row.mind_state_commit_receipt_id, - row.publication_gate, - row.durable_state_admitted, - row.source_store_path, - row.private_state_exposed - ) -} - -fn repo_work_map_semantic_rows( - repo_work_map_entries: &[EpiphanyCultMeshRepoWorkMapEntry], -) -> (Vec, Vec) { - let rows = repo_work_map_entries - .iter() - .map(repo_work_map_semantic_row) - .collect::>(); - let tui_rows = rows - .iter() - .map(repo_work_map_semantic_tui_row) - .collect::>(); - (rows, tui_rows) -} - -fn repo_work_map_semantic_row(entry: &EpiphanyCultMeshRepoWorkMapEntry) -> RepoWorkMapSemanticRow { - RepoWorkMapSemanticRow { - item: entry.item.clone(), - branch: entry.branch.clone(), - stage: "historical-only".to_string(), - stage_owner: "none".to_string(), - publication_gate: "historical-only".to_string(), - gate_owner: "none".to_string(), - safe_action_family: format!("historical:{}", entry.safe_action_family), - changed_path_count: entry.changed_paths.len(), - latest_commit_sha: entry.commit_sha.clone(), - latest_mind_state_commit_receipt_id: entry.mind_state_commit_receipt_id.clone(), - modeling_summary: "Historical record only; canonical frontier workflow required." - .to_string(), - sight_only: true, - private_state_exposed: entry.private_state_exposed, - } -} - -fn repo_work_map_semantic_tui_row(row: &RepoWorkMapSemanticRow) -> String { - format!( - "REPO-WORK-MAP-SEMANTIC | item={} | stage={} | stageOwner={} | gate={} | gateOwner={} | family={} | paths={} | latestCommit={} | latestMind={} | summary={} | sightOnly={} | private={}", - row.item, - row.stage, - row.stage_owner, - row.publication_gate, - row.gate_owner, - row.safe_action_family, - row.changed_path_count, - short_commit(&row.latest_commit_sha), - row.latest_mind_state_commit_receipt_id, - compact_tui_text(&row.modeling_summary, 160), - row.sight_only, - row.private_state_exposed - ) -} - -fn repo_work_map_family_lens_rows( - repo_work_map_entries: &[EpiphanyCultMeshRepoWorkMapEntry], -) -> (Vec, Vec) { - let mut by_family = BTreeMap::::new(); - for entry in repo_work_map_entries { - let family = entry.safe_action_family.clone(); - let row = by_family - .entry(family.clone()) - .or_insert_with(|| RepoWorkMapFamilyLensRow { - safe_action_family: family, - item_count: 0, - changed_path_count: 0, - latest_item: String::new(), - latest_commit_sha: String::new(), - latest_mind_state_commit_receipt_id: String::new(), - publication_gate: entry.publication_gate.clone(), - private_state_exposed: false, - }); - row.item_count += 1; - row.changed_path_count += entry.changed_paths.len(); - row.private_state_exposed |= entry.private_state_exposed; - if row.latest_item.is_empty() { - row.latest_item = entry.item.clone(); - row.latest_commit_sha = entry.commit_sha.clone(); - row.latest_mind_state_commit_receipt_id = entry.mind_state_commit_receipt_id.clone(); - row.publication_gate = entry.publication_gate.clone(); - } - } - let rows = by_family.into_values().collect::>(); - let tui_rows = rows - .iter() - .map(repo_work_map_family_lens_tui_row) - .collect::>(); - (rows, tui_rows) -} - -fn repo_work_map_family_lens_tui_row(row: &RepoWorkMapFamilyLensRow) -> String { - format!( - "REPO-WORK-MAP-LENS | family={} | items={} | paths={} | latestItem={} | latestCommit={} | latestMind={} | gate={} | private={}", - row.safe_action_family, - row.item_count, - row.changed_path_count, - row.latest_item, - short_commit(&row.latest_commit_sha), - row.latest_mind_state_commit_receipt_id, - row.publication_gate, - row.private_state_exposed - ) -} - -fn repo_work_map_path_lens_rows( - repo_work_map_entries: &[EpiphanyCultMeshRepoWorkMapEntry], -) -> (Vec, Vec) { - let mut by_path = BTreeMap::::new(); - for entry in repo_work_map_entries { - for changed_path in &entry.changed_paths { - let row = - by_path - .entry(changed_path.clone()) - .or_insert_with(|| RepoWorkMapPathLensRow { - path: changed_path.clone(), - item_count: 0, - safe_action_families: Vec::new(), - latest_item: String::new(), - latest_commit_sha: String::new(), - latest_mind_state_commit_receipt_id: String::new(), - publication_gate: entry.publication_gate.clone(), - private_state_exposed: false, - }); - row.item_count += 1; - if !row - .safe_action_families - .iter() - .any(|family| family == &entry.safe_action_family) - { - row.safe_action_families - .push(entry.safe_action_family.clone()); - row.safe_action_families.sort(); - } - row.private_state_exposed |= entry.private_state_exposed; - if row.latest_item.is_empty() { - row.latest_item = entry.item.clone(); - row.latest_commit_sha = entry.commit_sha.clone(); - row.latest_mind_state_commit_receipt_id = - entry.mind_state_commit_receipt_id.clone(); - row.publication_gate = entry.publication_gate.clone(); - } - } - } - let rows = by_path.into_values().collect::>(); - let tui_rows = rows - .iter() - .map(repo_work_map_path_lens_tui_row) - .collect::>(); - (rows, tui_rows) -} - -fn repo_work_map_path_lens_tui_row(row: &RepoWorkMapPathLensRow) -> String { - format!( - "REPO-WORK-MAP-PATH | path={} | items={} | families={} | latestItem={} | latestCommit={} | latestMind={} | gate={} | private={}", - row.path, - row.item_count, - row.safe_action_families.join(","), - row.latest_item, - short_commit(&row.latest_commit_sha), - row.latest_mind_state_commit_receipt_id, - row.publication_gate, - row.private_state_exposed - ) -} - -fn repo_work_map_branch_lens_rows( - repo_work_map_entries: &[EpiphanyCultMeshRepoWorkMapEntry], -) -> (Vec, Vec) { - let mut by_branch = BTreeMap::::new(); - for entry in repo_work_map_entries { - let branch = entry.branch.clone(); - let row = by_branch - .entry(branch.clone()) - .or_insert_with(|| RepoWorkMapBranchLensRow { - branch, - item_count: 0, - changed_path_count: 0, - safe_action_families: Vec::new(), - latest_item: String::new(), - latest_commit_sha: String::new(), - latest_mind_state_commit_receipt_id: String::new(), - publication_gate: entry.publication_gate.clone(), - private_state_exposed: false, - }); - row.item_count += 1; - row.changed_path_count += entry.changed_paths.len(); - if !row - .safe_action_families - .iter() - .any(|family| family == &entry.safe_action_family) - { - row.safe_action_families - .push(entry.safe_action_family.clone()); - row.safe_action_families.sort(); - } - row.private_state_exposed |= entry.private_state_exposed; - if row.latest_item.is_empty() { - row.latest_item = entry.item.clone(); - row.latest_commit_sha = entry.commit_sha.clone(); - row.latest_mind_state_commit_receipt_id = entry.mind_state_commit_receipt_id.clone(); - row.publication_gate = entry.publication_gate.clone(); - } - } - let rows = by_branch.into_values().collect::>(); - let tui_rows = rows - .iter() - .map(repo_work_map_branch_lens_tui_row) - .collect::>(); - (rows, tui_rows) -} - -fn repo_work_map_branch_lens_tui_row(row: &RepoWorkMapBranchLensRow) -> String { - format!( - "REPO-WORK-MAP-BRANCH | branch={} | items={} | paths={} | families={} | latestItem={} | latestCommit={} | latestMind={} | gate={} | private={}", - row.branch, - row.item_count, - row.changed_path_count, - row.safe_action_families.join(","), - row.latest_item, - short_commit(&row.latest_commit_sha), - row.latest_mind_state_commit_receipt_id, - row.publication_gate, - row.private_state_exposed - ) -} - -fn repo_work_map_stage_lens_rows( - repo_work_map_entries: &[EpiphanyCultMeshRepoWorkMapEntry], -) -> (Vec, Vec) { - let mut by_stage = BTreeMap::::new(); - for entry in repo_work_map_entries { - let (stage, owner) = repo_work_stage_for_family(&entry.safe_action_family); - let row = by_stage - .entry(stage.clone()) - .or_insert_with(|| RepoWorkMapStageLensRow { - stage, - owner, - item_count: 0, - changed_path_count: 0, - safe_action_families: Vec::new(), - latest_item: String::new(), - latest_commit_sha: String::new(), - latest_mind_state_commit_receipt_id: String::new(), - publication_gate: entry.publication_gate.clone(), - private_state_exposed: false, - }); - row.item_count += 1; - row.changed_path_count += entry.changed_paths.len(); - if !row - .safe_action_families - .iter() - .any(|family| family == &entry.safe_action_family) - { - row.safe_action_families - .push(entry.safe_action_family.clone()); - row.safe_action_families.sort(); - } - row.private_state_exposed |= entry.private_state_exposed; - if row.latest_item.is_empty() { - row.latest_item = entry.item.clone(); - row.latest_commit_sha = entry.commit_sha.clone(); - row.latest_mind_state_commit_receipt_id = entry.mind_state_commit_receipt_id.clone(); - row.publication_gate = entry.publication_gate.clone(); - } - } - let rows = by_stage.into_values().collect::>(); - let tui_rows = rows - .iter() - .map(repo_work_map_stage_lens_tui_row) - .collect::>(); - (rows, tui_rows) -} - -fn repo_work_stage_for_family(safe_action_family: &str) -> (String, String) { - let (stage, owner) = match safe_action_family { - "repo.append_worklog" - | "repo.markdown_planning_note" - | "repo.checklist_note" - | "repo.markdown_managed_section" - | "repo.status_section" - | "repo.task_card" - | "repo.consensus_brief" - | "repo.interpreter_brief" - | "repo.objective_draft" => ("imagination-planning", "Imagination"), - "repo.tool_request" => ("daemon-tool-request", "target-host-daemon"), - "repo.collaboration_policy" => ("collaboration-policy-draft", "Imagination"), - "repo.collaboration_topic" => ("collaboration-topic-draft", "Imagination"), - "repo.adoption_request" => ("mind-adoption-request", "Mind"), - "repo.scheduling_request" => ("self-scheduling-request", "Self"), - "repo.work_order" => ("hands-execution-request", "Hands"), - "repo.verification_request" => ("soul-verification-request", "Soul"), - "repo.publication_request" => ("bifrost-publication-request", "Bifrost"), - "repo.sync_request" => ("bifrost-upstream-proof", "Bifrost"), - "repo.maintainer_review_request" => ("maintainer-review-request", "Maintainer"), - "repo.pr_request" => ("hands-pr-execution-request", "Hands"), - "repo.credit_request" => ("bifrost-credit-accounting", "Bifrost"), - "repo.artifact_acceptance_request" => ("maintainer-artifact-acceptance", "Maintainer"), - "repo.metrics_request" => ("bifrost-metrics-accounting", "Bifrost"), - "repo.readiness_review_request" => ("maintainer-readiness-review", "Maintainer"), - "repo.doctrine_update_request" => ("mind-doctrine-admission", "Mind"), - "repo.secret_policy_request" => ("mind-secret-policy-admission", "Mind"), - "repo.dependency_policy_request" => ("mind-dependency-policy-admission", "Mind"), - "repo.deployment_config" | "repo.deployment_request" => ("idunn-deployment", "Idunn"), - _ => ("unknown-safe-family", "Unknown"), - }; - (stage.to_string(), owner.to_string()) -} - -fn repo_work_map_stage_lens_tui_row(row: &RepoWorkMapStageLensRow) -> String { - format!( - "REPO-WORK-MAP-STAGE | stage={} | owner={} | items={} | paths={} | families={} | latestItem={} | latestCommit={} | latestMind={} | gate={} | private={}", - row.stage, - row.owner, - row.item_count, - row.changed_path_count, - row.safe_action_families.join(","), - row.latest_item, - short_commit(&row.latest_commit_sha), - row.latest_mind_state_commit_receipt_id, - row.publication_gate, - row.private_state_exposed - ) -} - -fn repo_work_map_gate_lens_rows( - repo_work_map_entries: &[EpiphanyCultMeshRepoWorkMapEntry], -) -> (Vec, Vec) { - let mut by_gate = BTreeMap::::new(); - for entry in repo_work_map_entries { - let gate = entry.publication_gate.clone(); - let row = by_gate - .entry(gate.clone()) - .or_insert_with(|| RepoWorkMapGateLensRow { - publication_gate: gate.clone(), - owner: repo_work_gate_owner(&gate), - item_count: 0, - changed_path_count: 0, - stages: Vec::new(), - safe_action_families: Vec::new(), - latest_item: String::new(), - latest_commit_sha: String::new(), - latest_mind_state_commit_receipt_id: String::new(), - sight_only: true, - private_state_exposed: false, - }); - let (stage, _) = repo_work_stage_for_family(&entry.safe_action_family); - row.item_count += 1; - row.changed_path_count += entry.changed_paths.len(); - if !row.stages.iter().any(|candidate| candidate == &stage) { - row.stages.push(stage); - row.stages.sort(); - } - if !row - .safe_action_families - .iter() - .any(|family| family == &entry.safe_action_family) - { - row.safe_action_families - .push(entry.safe_action_family.clone()); - row.safe_action_families.sort(); - } - row.private_state_exposed |= entry.private_state_exposed; - if row.latest_item.is_empty() { - row.latest_item = entry.item.clone(); - row.latest_commit_sha = entry.commit_sha.clone(); - row.latest_mind_state_commit_receipt_id = entry.mind_state_commit_receipt_id.clone(); - } - } - let rows = by_gate.into_values().collect::>(); - let tui_rows = rows - .iter() - .map(repo_work_map_gate_lens_tui_row) - .collect::>(); - (rows, tui_rows) -} - -fn repo_work_gate_owner(publication_gate: &str) -> String { - match publication_gate { - "Bifrost" => "Bifrost", - "Maintainer" => "Maintainer", - "Idunn" => "Idunn", - "Mind" => "Mind", - "Soul" => "Soul", - _ => "Unknown", - } - .to_string() -} - -fn repo_work_map_gate_lens_tui_row(row: &RepoWorkMapGateLensRow) -> String { - format!( - "REPO-WORK-MAP-GATE | gate={} | owner={} | items={} | paths={} | stages={} | families={} | latestItem={} | latestCommit={} | latestMind={} | sightOnly={} | private={}", - row.publication_gate, - row.owner, - row.item_count, - row.changed_path_count, - row.stages.join(","), - row.safe_action_families.join(","), - row.latest_item, - short_commit(&row.latest_commit_sha), - row.latest_mind_state_commit_receipt_id, - row.sight_only, - row.private_state_exposed - ) -} - -fn repo_work_map_closure_rows( - repo_work_map_entries: &[EpiphanyCultMeshRepoWorkMapEntry], -) -> (Vec, Vec) { - let rows = repo_work_map_entries - .iter() - .map(repo_work_map_closure_row) - .collect::>(); - let tui_rows = rows - .iter() - .map(repo_work_map_closure_tui_row) - .collect::>(); - (rows, tui_rows) -} - -fn repo_work_map_closure_row(entry: &EpiphanyCultMeshRepoWorkMapEntry) -> RepoWorkMapClosureRow { - RepoWorkMapClosureRow { - item: entry.item.clone(), - branch: entry.branch.clone(), - safe_action_family: entry.safe_action_family.clone(), - changed_path_count: entry.changed_paths.len(), - commit_sha: entry.commit_sha.clone(), - soul_verdict_receipt_id: entry.soul_verdict_receipt_id.clone(), - mind_gateway_review_id: entry.mind_gateway_review_id.clone(), - mind_state_commit_receipt_id: entry.mind_state_commit_receipt_id.clone(), - durable_state_admitted: entry.durable_state_admitted, - publication_gate: entry.publication_gate.clone(), - publication_authorized: false, - merge_authorized: false, - service_lifecycle_authorized: false, - deployment_execution_authorized: false, - cross_repo_mutation_authorized: false, - sight_only: true, - private_state_exposed: entry.private_state_exposed, - } -} - -fn repo_work_map_closure_tui_row(row: &RepoWorkMapClosureRow) -> String { - format!( - "REPO-WORK-MAP-CLOSURE | item={} | family={} | branch={} | commit={} | paths={} | soul={} | mindReview={} | mindCommit={} | durableAdmitted={} | publicationGate={} | publicationAuth={} | mergeAuth={} | serviceAuth={} | deploymentAuth={} | crossRepoAuth={} | sightOnly={} | private={}", - row.item, - row.safe_action_family, - row.branch, - short_commit(&row.commit_sha), - row.changed_path_count, - row.soul_verdict_receipt_id, - row.mind_gateway_review_id, - row.mind_state_commit_receipt_id, - row.durable_state_admitted, - row.publication_gate, - row.publication_authorized, - row.merge_authorized, - row.service_lifecycle_authorized, - row.deployment_execution_authorized, - row.cross_repo_mutation_authorized, - row.sight_only, - row.private_state_exposed - ) -} - -fn repo_work_map_acceptance_rows( - repo_work_map_entries: &[EpiphanyCultMeshRepoWorkMapEntry], -) -> (Vec, Vec) { - let rows = repo_work_map_entries - .iter() - .map(repo_work_map_acceptance_row) - .collect::>(); - let tui_rows = rows - .iter() - .map(repo_work_map_acceptance_tui_row) - .collect::>(); - (rows, tui_rows) -} - -fn repo_work_map_acceptance_row( - entry: &EpiphanyCultMeshRepoWorkMapEntry, -) -> RepoWorkMapAcceptanceRow { - let soul_closed = !entry.soul_verdict_receipt_id.trim().is_empty(); - let modeling_closed = !entry.modeling_summary.trim().is_empty(); - let mind_committed = !entry.mind_gateway_review_id.trim().is_empty() - && !entry.mind_state_commit_receipt_id.trim().is_empty() - && entry.durable_state_admitted; - let bifrost_gate_named = !entry.publication_gate.trim().is_empty() - && entry.publication_gate != "none" - && entry.publication_gate != "missing"; - let acceptance_status = - if soul_closed && modeling_closed && mind_committed && bifrost_gate_named { - "accepted-awaiting-publication-gate" - } else { - "incomplete-acceptance" - }; - RepoWorkMapAcceptanceRow { - item: entry.item.clone(), - branch: entry.branch.clone(), - safe_action_family: entry.safe_action_family.clone(), - acceptance_status: acceptance_status.to_string(), - soul_closed, - modeling_closed, - mind_committed, - durable_state_admitted: entry.durable_state_admitted, - bifrost_gate_named, - soul_verdict_receipt_id: entry.soul_verdict_receipt_id.clone(), - modeling_summary: entry.modeling_summary.clone(), - mind_gateway_review_id: entry.mind_gateway_review_id.clone(), - mind_state_commit_receipt_id: entry.mind_state_commit_receipt_id.clone(), - publication_gate: entry.publication_gate.clone(), - sight_only: true, - private_state_exposed: entry.private_state_exposed, - } -} - -fn repo_work_map_acceptance_tui_row(row: &RepoWorkMapAcceptanceRow) -> String { - format!( - "REPO-WORK-MAP-ACCEPTANCE | item={} | family={} | branch={} | status={} | soulClosed={} | modelingClosed={} | mindCommitted={} | durableAdmitted={} | publicationGate={} | bifrostGateNamed={} | soul={} | mindReview={} | mindCommit={} | summary={} | sightOnly={} | private={}", - row.item, - row.safe_action_family, - row.branch, - row.acceptance_status, - row.soul_closed, - row.modeling_closed, - row.mind_committed, - row.durable_state_admitted, - row.publication_gate, - row.bifrost_gate_named, - row.soul_verdict_receipt_id, - row.mind_gateway_review_id, - row.mind_state_commit_receipt_id, - compact_tui_text(&row.modeling_summary, 120), - row.sight_only, - row.private_state_exposed - ) -} - -fn repo_work_peer_tui_rows(rows: &[RepoWorkOverviewRow]) -> Vec { - rows.iter() - .map(|row| { - format!( - "REPO-WORK-PEER | item={} | gate={} | blocker={} | next={} | branch={} | publication={} | sync={} | private={}", - row.item, - row.current_gate, - row.blocker, - row.next_safe_move, - row.branch, - row.publication_status, - row.sync_status, - row.private_state_exposed - ) - }) - .collect() -} - -fn repo_work_public_proof_rows( - public_proofs: &[EpiphanyCultMeshRepoWorkPublicProofEntry], -) -> (Vec, Vec) { - let rows = public_proofs - .iter() - .map(repo_work_public_proof_row) - .collect::>(); - let tui_rows = rows - .iter() - .map(repo_work_public_proof_tui_row) - .collect::>(); - (rows, tui_rows) -} - -fn repo_work_public_proof_row( - proof: &EpiphanyCultMeshRepoWorkPublicProofEntry, -) -> RepoWorkPublicProofRow { - RepoWorkPublicProofRow { - public_proof_id: proof.public_proof_id.clone(), - workspace: proof.workspace.clone(), - item: proof.item.clone(), - branch: proof.branch.clone(), - current_gate: "historical-only".to_string(), - blocker: "canonical-frontier-workflow-required".to_string(), - next_safe_move: "Canonical frontier workflow required; historical record grants no action." - .to_string(), - changed_path_count: proof.changed_paths.len(), - commit_sha: proof.commit_sha.clone(), - soul_verdict: proof.soul_verdict.clone(), - upstream_main_synced: proof.upstream_main_synced, - artifact_row_count: proof.artifact_row_count, - publication_row_count: proof.publication_row_count, - public_proof_ref: proof.public_proof_ref.clone(), - public_proof_sha256: proof.public_proof_sha256.clone(), - private_state_exposed: proof.private_state_exposed, - } -} - -fn repo_work_public_proof_tui_row(row: &RepoWorkPublicProofRow) -> String { - format!( - "REPO-WORK-PUBLIC-PROOF | item={} | gate={} | branch={} | commit={} | artifacts={} | publicationRows={} | upstreamMainSynced={} | proof={} | sha256={} | private={}", - row.item, - row.current_gate, - row.branch, - row.commit_sha, - row.artifact_row_count, - row.publication_row_count, - row.upstream_main_synced, - row.public_proof_ref, - row.public_proof_sha256, - row.private_state_exposed - ) -} - -fn repo_work_readiness_rows( - reports: &[EpiphanyCultMeshRepoWorkReadinessEntry], -) -> (Vec, Vec) { - let rows = reports - .iter() - .map(repo_work_readiness_row) - .collect::>(); - let tui_rows = rows - .iter() - .map(repo_work_readiness_tui_row) - .collect::>(); - (rows, tui_rows) -} - -fn repo_work_readiness_row( - report: &EpiphanyCultMeshRepoWorkReadinessEntry, -) -> RepoWorkReadinessRow { - RepoWorkReadinessRow { - readiness_id: report.readiness_id.clone(), - workspace: report.workspace.clone(), - item: report.item.clone(), - status: "historical-only".to_string(), - missing_required_count: 1, - satisfied_required_count: 0, - readiness_receipt_ref: report.readiness_receipt_ref.clone(), - overview_receipt_ref: report.overview_receipt_ref.clone(), - proof_bundle_id: report.proof_bundle_id.clone(), - missing_kinds: vec!["canonical-frontier-workflow-required".to_string()], - sight_only: true, - readiness_approval_authorized: false, - publication_authorized: false, - service_lifecycle_authority: false, - hands_action_authorized: false, - private_state_exposed: report.private_state_exposed, - } -} - -fn repo_work_readiness_tui_row(row: &RepoWorkReadinessRow) -> String { - format!( - "REPO-WORK-READINESS | item={} | status={} | missing={} | satisfied={} | gaps={} | proof={} | sightOnly={} | approvalAuth={} | publicationAuth={} | serviceAuth={} | handsAuth={} | private={}", - row.item, - row.status, - row.missing_required_count, - row.satisfied_required_count, - if row.missing_kinds.is_empty() { - "none".to_string() - } else { - row.missing_kinds.join(",") - }, - row.proof_bundle_id, - row.sight_only, - row.readiness_approval_authorized, - row.publication_authorized, - row.service_lifecycle_authority, - row.hands_action_authorized, - row.private_state_exposed - ) -} - -fn idunn_deployment_receipt_rows( - deployment_receipt: Option<&EpiphanyCultMeshIdunnDeploymentReceiptEntry>, - aftercare_receipt: Option<&EpiphanyCultMeshIdunnAftercareAuditReceiptEntry>, -) -> (Vec, Vec) { - let mut rows = Vec::new(); - if let Some(receipt) = deployment_receipt { - rows.push(IdunnDeploymentReceiptRow { - receipt_family: "deployment".to_string(), - receipt_id: receipt.receipt_id.clone(), - status: receipt.status.clone(), - owner: "Idunn".to_string(), - authority_gate: "gamecult.idunn.deployment".to_string(), - watched_ref: receipt.watched_ref.clone(), - checked_ref: "none".to_string(), - source_commit: receipt.source_commit.clone(), - result_ref: receipt.result_ref.clone(), - deployment_receipt_id: receipt.receipt_id.clone(), - aftercare_audit_ref: "none".to_string(), - follow_up_command: DIRECT_IDUNN_DEPLOYMENT_AFTERCARE_AUDIT_COMMAND.to_string(), - private_state_exposed: receipt.private_state_exposed, - }); - } - if let Some(receipt) = aftercare_receipt { - rows.push(IdunnDeploymentReceiptRow { - receipt_family: "aftercare-audit".to_string(), - receipt_id: receipt.receipt_id.clone(), - status: receipt.status.clone(), - owner: "Idunn".to_string(), - authority_gate: "gamecult.idunn.deployment_aftercare".to_string(), - watched_ref: "none".to_string(), - checked_ref: receipt.checked_ref.clone(), - source_commit: "none".to_string(), - result_ref: receipt.audit_ref.clone(), - deployment_receipt_id: receipt.deployment_receipt_id.clone(), - aftercare_audit_ref: receipt.audit_ref.clone(), - follow_up_command: DIRECT_IDUNN_DEPLOYMENT_AFTERCARE_AUDIT_COMMAND.to_string(), - private_state_exposed: receipt.private_state_exposed, - }); - } - let tui_rows = rows - .iter() - .map(idunn_deployment_receipt_tui_row) - .collect::>(); - (rows, tui_rows) -} - -fn idunn_deployment_receipt_tui_row(row: &IdunnDeploymentReceiptRow) -> String { - format!( - "IDUNN-DEPLOYMENT | family={} | receipt={} | status={} | watchedRef={} | checkedRef={} | sourceCommit={} | result={} | deploymentReceipt={} | aftercare={} | owner={} | authority={} | command={} | private={}", - row.receipt_family, - row.receipt_id, - row.status, - row.watched_ref, - row.checked_ref, - short_commit(&row.source_commit), - row.result_ref, - row.deployment_receipt_id, - row.aftercare_audit_ref, - row.owner, - row.authority_gate, - row.follow_up_command, - row.private_state_exposed - ) -} - -fn short_commit(commit_sha: &str) -> String { - commit_sha.chars().take(12).collect::() -} - -fn repo_work_overview_action_row( - overview: &EpiphanyCultMeshRepoWorkOverviewEntry, - priority: u32, -) -> SwarmActionRow { - let status = overview.current_gate.clone(); - let command = format!( - "epiphany-work overview --workspace \"{}\" --item \"{}\"", - overview.workspace, overview.item - ); - SwarmActionRow { - priority, - family: "repo-work-overview".to_string(), - status, - lifecycle_owner: "none".to_string(), - hosted_body: "repo-work".to_string(), - action: format!( - "Read latest repo work overview {} for item {}.", - overview.overview_id, overview.item - ), - wrapper_mode: "repo-work-overview".to_string(), - wrapper_command: command, - operator_artifact_ref: overview.proof_bundle_ref.clone(), - operator_artifact_status: if overview.proof_bundle_ref == "none" { - "none".to_string() - } else { - "external-ref".to_string() - }, - operator_artifact_sha256: "none".to_string(), - operator_artifact_execution_command: "none".to_string(), - operator_aftercare_command: "none".to_string(), - completion_audit_wrapper_mode: "none".to_string(), - completion_audit_wrapper_command: "none".to_string(), - authority_gate: "repo.work.overview".to_string(), - effect_class: "repo-work-readback".to_string(), - mutates_state: false, - requires_elevated_authority: false, - service_execution_failed_check_count: 0, - service_execution_missing_check_count: 0, - service_id: "none".to_string(), - service_route: "none".to_string(), - reason: format!( - "Latest repo work item gate={} blocker={} next={}", - overview.current_gate, overview.blocker, overview.next_safe_move - ), - private_state_exposed: overview.private_state_exposed, - } -} - -fn repo_work_public_proof_action_row( - proof: &EpiphanyCultMeshRepoWorkPublicProofEntry, - priority: u32, -) -> SwarmActionRow { - SwarmActionRow { - priority, - family: "repo-work-public-proof".to_string(), - status: proof.current_gate.clone(), - lifecycle_owner: "none".to_string(), - hosted_body: "repo-work".to_string(), - action: format!( - "Read redacted public proof {} for item {}; Bifrost owns any public publication or credit consequence.", - proof.public_proof_id, proof.item - ), - wrapper_mode: "repo-work-public-proof".to_string(), - wrapper_command: format!( - "epiphany-work export-proof --workspace \"{}\" --item \"{}\" --local-verse-store ", - proof.workspace, proof.item - ), - operator_artifact_ref: proof.public_proof_ref.clone(), - operator_artifact_status: if proof.public_proof_ref == "none" { - "none".to_string() - } else { - "external-ref".to_string() - }, - operator_artifact_sha256: proof.public_proof_sha256.clone(), - operator_artifact_execution_command: "none".to_string(), - operator_aftercare_command: "none".to_string(), - completion_audit_wrapper_mode: "none".to_string(), - completion_audit_wrapper_command: "none".to_string(), - authority_gate: "repo.work.public_proof_readback".to_string(), - effect_class: "repo-work-public-proof-readback".to_string(), - mutates_state: false, - requires_elevated_authority: false, - service_execution_failed_check_count: 0, - service_execution_missing_check_count: 0, - service_id: "none".to_string(), - service_route: "none".to_string(), - reason: format!( - "Redacted proof has artifacts={} publicationRows={} upstreamMainSynced={} sha256={}", - proof.artifact_row_count, - proof.publication_row_count, - proof.upstream_main_synced, - proof.public_proof_sha256 - ), - private_state_exposed: proof.private_state_exposed, - } -} - -fn repo_work_readiness_action_row( - readiness: &EpiphanyCultMeshRepoWorkReadinessEntry, - priority: u32, -) -> SwarmActionRow { - SwarmActionRow { - priority, - family: "repo-work-readiness".to_string(), - status: readiness.status.clone(), - lifecycle_owner: "none".to_string(), - hosted_body: "repo-work".to_string(), - action: format!( - "Read repo work readiness {} for item {}; Maintainer/Soul/Mind/Bifrost own readiness approval.", - readiness.readiness_id, readiness.item - ), - wrapper_mode: "repo-work-readiness".to_string(), - wrapper_command: format!( - "epiphany-work readiness --workspace \"{}\" --item \"{}\"", - readiness.workspace, readiness.item - ), - operator_artifact_ref: readiness.readiness_receipt_ref.clone(), - operator_artifact_status: if readiness.readiness_receipt_ref == "none" { - "none".to_string() - } else { - "external-ref".to_string() - }, - operator_artifact_sha256: "none".to_string(), - operator_artifact_execution_command: "none".to_string(), - operator_aftercare_command: "none".to_string(), - completion_audit_wrapper_mode: "none".to_string(), - completion_audit_wrapper_command: "none".to_string(), - authority_gate: "repo.work.readiness_readback".to_string(), - effect_class: "repo-work-readiness-readback".to_string(), - mutates_state: false, - requires_elevated_authority: false, - service_execution_failed_check_count: 0, - service_execution_missing_check_count: 0, - service_id: "none".to_string(), - service_route: "none".to_string(), - reason: format!( - "Readiness status={} missing={} gaps={}; approval={}, publication={}, service={}, hands={}", - readiness.status, - readiness.missing_required_count, - if readiness.missing_kinds.is_empty() { - "none".to_string() - } else { - readiness.missing_kinds.join(",") - }, - readiness.readiness_approval_authorized, - readiness.publication_authorized, - readiness.service_lifecycle_authority, - readiness.hands_action_authorized - ), - private_state_exposed: readiness.private_state_exposed, - } -} - -fn swarm_action_tui_row(row: &SwarmActionRow) -> String { - format!( - "{:03} | {} | {} | {} | {} | owner={} | hostedBody={} | service={} | route={} | command={} | mutates={} | elevated={} | failedChecks={} | missingChecks={} | artifact={} | sha256={} | exec={} | audit={} | aftercare={}", - row.priority, - row.family, - row.status, - row.wrapper_mode, - row.authority_gate, - row.lifecycle_owner, - row.hosted_body, - row.service_id, - row.service_route, - row.wrapper_command, - row.mutates_state, - row.requires_elevated_authority, - row.service_execution_failed_check_count, - row.service_execution_missing_check_count, - row.operator_artifact_status, - row.operator_artifact_sha256, - row.operator_artifact_execution_command, - row.completion_audit_wrapper_mode, - row.operator_aftercare_command - ) -} - -fn service_lifecycle_wrapper_mode_for_row(row: &ReceiptDirectoryRow) -> &'static str { - service_lifecycle_wrapper_mode_for_command(&row.follow_up_command) -} - -fn service_lifecycle_wrapper_mode_for_command(command: &str) -> &'static str { - if command == WRAPPER_CLUSTER_SERVICE_EXECUTION_AUDIT_COMMAND { - "cluster-service-execution-audit" - } else if command == WRAPPER_CLUSTER_SERVICE_EXECUTION_READINESS_COMMAND { - "cluster-service-execution-readiness" - } else if command == WRAPPER_CLUSTER_SERVICE_EXECUTION_RUNBOOK_COMMAND { - "cluster-service-execution-runbook" - } else if command == WRAPPER_CLUSTER_SERVICE_INSTALL_EXECUTE_COMMAND { - "cluster-service-install-execute" - } else if command == WRAPPER_CLUSTER_SERVICE_START_EXECUTE_COMMAND { - "cluster-service-start-execute" - } else if command == WRAPPER_CLUSTER_SERVICE_STOP_EXECUTE_COMMAND { - "cluster-service-stop-execute" - } else if command == WRAPPER_SERVICE_EXECUTION_AUDIT_COMMAND { - "service-execution-audit" - } else if command == WRAPPER_SERVICE_EXECUTION_READINESS_COMMAND { - "service-execution-readiness" - } else if command == WRAPPER_SERVICE_EXECUTION_RUNBOOK_COMMAND { - "service-execution-runbook" - } else if command == WRAPPER_SERVICE_INSTALL_EXECUTE_COMMAND { - "service-install-execute" - } else if command == WRAPPER_SERVICE_START_EXECUTE_COMMAND { - "service-start-execute" - } else if command == WRAPPER_SERVICE_STOP_EXECUTE_COMMAND { - "service-stop-execute" - } else if command == WRAPPER_SERVICE_STATUS_COMMAND { - "service-status" - } else if command == WRAPPER_SERVICE_RECONCILE_COMMAND { - "service-reconcile" - } else { - "receipt-directory" - } -} - -fn service_execution_runbook_actions( - rows: &[ReceiptDirectoryRow], - cluster_report: &epiphany_core::EpiphanyServiceExecutionAuditReport, - single_service_reports: &[(String, epiphany_core::EpiphanyServiceExecutionAuditReport)], -) -> Vec { - rows.iter() - .filter(|row| { - row.present - && row.status == "written" - && (row.family == "cluster-service-execution-runbook" - || row.family == "service-execution-runbook") - }) - .filter_map(|row| { - if row.family == "cluster-service-execution-runbook" { - Some(ServiceExecutionRunbookAction { - route: row.route.clone(), - family: row.family.clone(), - follow_up_command: row.follow_up_command.clone(), - artifact_ref: row.artifact_ref.clone(), - private_state_exposed: row.private_state_exposed, - failed_check_count: cluster_report.failed_count, - missing_check_count: cluster_report.missing_count, - service_id: row.service_id.clone(), - completion_audit_wrapper_mode: "cluster-service-execution-audit".to_string(), - completion_audit_wrapper_command: - WRAPPER_CLUSTER_SERVICE_EXECUTION_AUDIT_COMMAND.to_string(), - }) - } else { - let service_id = receipt_directory_row_service_id(row)?; - let (_, report) = single_service_reports - .iter() - .find(|(candidate_id, _)| candidate_id == &service_id)?; - Some(ServiceExecutionRunbookAction { - route: row.route.clone(), - family: row.family.clone(), - follow_up_command: row.follow_up_command.clone(), - artifact_ref: row.artifact_ref.clone(), - private_state_exposed: row.private_state_exposed, - failed_check_count: report.failed_count, - missing_check_count: report.missing_count, - service_id, - completion_audit_wrapper_mode: "service-execution-audit".to_string(), - completion_audit_wrapper_command: WRAPPER_SERVICE_EXECUTION_AUDIT_COMMAND - .to_string(), - }) - } - }) - .collect() -} - -fn elevated_powershell_runbook_command(artifact_ref: &str) -> String { - format!( - "Start-Process PowerShell -Verb RunAs -Wait -ArgumentList @('-NoProfile','-ExecutionPolicy','Bypass','-File',{})", - quote_powershell_literal(artifact_ref) - ) -} - -fn operator_artifact_sha256(artifact_ref: &str, artifact_status: &str) -> String { - if artifact_status != "present" { - return "none".to_string(); - } - let Ok(bytes) = fs::read(artifact_ref) else { - return "unreadable".to_string(); - }; - let digest = Sha256::digest(&bytes); - format!("{digest:x}") -} - -fn quote_powershell_literal(value: &str) -> String { - format!("'{}'", value.replace('\'', "''")) -} - -fn operator_artifact_status(artifact_ref: &str) -> &'static str { - if artifact_ref.is_empty() || artifact_ref == "none" { - "none" - } else if artifact_ref.contains("://") { - "external-ref" - } else if PathBuf::from(artifact_ref).is_file() { - "present" - } else { - "missing" - } -} - -fn service_execution_runbook_artifact_status(artifact_ref: &str) -> &'static str { - if artifact_ref.starts_with("smoke://") { - "missing" - } else { - operator_artifact_status(artifact_ref) - } -} - -fn daemon_restart_policy_directory_report_from_rows( - policy_directory: &[( - EpiphanyCultMeshClusterTopologyEntry, - Option, - Option, - )], -) -> DaemonRestartPolicyDirectoryReport { - let mut rows = Vec::new(); - let mut tui_rows = Vec::new(); - let mut covered_count = 0_usize; - let mut enabled_count = 0_usize; - let mut disabled_count = 0_usize; - let mut missing_count = 0_usize; - let mut attention_count = 0_usize; - let mut private_state_exposed = false; - let mut observed_daemon_count = 0_usize; - - for (cluster, status, policy) in policy_directory { - if status.is_some() { - observed_daemon_count += 1; - } - let policy = policy.as_ref(); - let display_name = cluster.display_name.clone(); - let private_row_exposed = status - .as_ref() - .map(|status| status.private_state_exposed) - .unwrap_or(false) - || policy - .map(|policy| policy.private_state_exposed) - .unwrap_or(false); - private_state_exposed |= private_row_exposed; - - let ( - policy_id, - policy_status, - enabled, - cooldown_seconds, - reconcile_interval_seconds, - heartbeat_stale_seconds, - failure_count, - last_result_status, - last_attempt_utc, - last_reconcile_utc, - follow_up_command, - ) = if let Some(policy) = policy { - covered_count += 1; - if policy.enabled { - enabled_count += 1; - ( - policy.policy_id.clone(), - "enabled".to_string(), - true, - policy.cooldown_seconds, - policy.reconcile_interval_seconds, - policy.heartbeat_stale_seconds, - policy.failure_count, - policy.last_result_status.clone(), - policy.last_attempt_utc.clone(), - policy.last_reconcile_utc.clone(), - WRAPPER_SERVICE_TICK_COMMAND.to_string(), - ) - } else { - disabled_count += 1; - attention_count += 1; - ( - policy.policy_id.clone(), - "disabled".to_string(), - false, - policy.cooldown_seconds, - policy.reconcile_interval_seconds, - policy.heartbeat_stale_seconds, - policy.failure_count, - policy.last_result_status.clone(), - policy.last_attempt_utc.clone(), - policy.last_reconcile_utc.clone(), - "epiphany-daemon-supervisor policy --daemon-id --restart-command " - .to_string(), - ) - } - } else { - missing_count += 1; - attention_count += 1; - ( - "missing".to_string(), - "missing".to_string(), - false, - 0, - 0, - 0, - 0, - "missing".to_string(), - None, - None, - "epiphany-daemon-supervisor policy --daemon-id --restart-command " - .to_string(), - ) - }; - if private_row_exposed { - attention_count += 1; - } - - let compact_status = match policy_status.as_str() { - "enabled" => "ENABLED", - "disabled" => "DISABLED", - _ => "MISSING", - }; - tui_rows.push(format!( - "{compact_status} | {display_name} | owner={} | hostedBody={} | {} | daemon={} | policy={} | cooldown={}s | reconcile={}s | stale={}s | failures={} | last={} | followUp={} | private={}", - SERVICE_LIFECYCLE_OWNER, - SERVICE_LIFECYCLE_HOSTED_BODY, - cluster.daemon_id, - status.as_ref().map(|status| status.status.as_str()).unwrap_or("unobserved"), - policy_id, - cooldown_seconds, - reconcile_interval_seconds, - heartbeat_stale_seconds, - failure_count, - last_result_status, - follow_up_command, - private_row_exposed - )); - rows.push(DaemonRestartPolicyDirectoryRow { - cluster_id: cluster.cluster_id.clone(), - display_name, - lifecycle_owner: SERVICE_LIFECYCLE_OWNER.to_string(), - hosted_body: SERVICE_LIFECYCLE_HOSTED_BODY.to_string(), - daemon_id: cluster.daemon_id.clone(), - daemon_status: status - .as_ref() - .map(|status| status.status.clone()) - .unwrap_or_else(|| "unobserved".to_string()), - policy_id, - policy_status, - enabled, - cooldown_seconds, - reconcile_interval_seconds, - heartbeat_stale_seconds, - failure_count, - last_result_status, - last_attempt_utc, - last_reconcile_utc, - follow_up_command, - private_state_exposed: private_row_exposed, - }); - } - - let status = if rows.is_empty() { - "empty" - } else if private_state_exposed || missing_count > 0 || disabled_count > 0 { - "attention" - } else { - "ok" - } - .to_string(); - - DaemonRestartPolicyDirectoryReport { - status, - lifecycle_owner: SERVICE_LIFECYCLE_OWNER.to_string(), - hosted_body: SERVICE_LIFECYCLE_HOSTED_BODY.to_string(), - rows, - tui_rows, - covered_count, - enabled_count, - disabled_count, - missing_count, - attention_count, - observed_daemon_count, - private_state_exposed, - } -} - -fn load_repo_work_overview_queue( - args: &Args, -) -> Result<( - Option, - Vec, -)> { - let latest_repo_work_overview = - load_latest_epiphany_cultmesh_repo_work_overview(&args.store, args.runtime_id.clone())?; - let mut repo_work_overviews = - load_epiphany_cultmesh_repo_work_overviews(&args.store, args.runtime_id.clone())?; - if let Some(latest) = latest_repo_work_overview.as_ref() { - if !repo_work_overviews - .iter() - .any(|overview| overview.overview_id == latest.overview_id) - { - repo_work_overviews.push(latest.clone()); - repo_work_overviews.sort_by(|a, b| { - b.generated_at - .cmp(&a.generated_at) - .then_with(|| a.overview_id.cmp(&b.overview_id)) - }); - } - } - Ok((latest_repo_work_overview, repo_work_overviews)) -} - -fn load_repo_work_map_entries( - args: &Args, -) -> Result<( - Option, - Vec, -)> { - let latest_repo_work_map_entry = - load_latest_epiphany_cultmesh_repo_work_map_entry(&args.store, args.runtime_id.clone())?; - let mut repo_work_map_entries = - load_epiphany_cultmesh_repo_work_map_entries(&args.store, args.runtime_id.clone())?; - if let Some(latest) = latest_repo_work_map_entry.as_ref() { - if !repo_work_map_entries - .iter() - .any(|entry| entry.map_entry_id == latest.map_entry_id) - { - repo_work_map_entries.push(latest.clone()); - repo_work_map_entries.sort_by(|a, b| { - b.admitted_at - .cmp(&a.admitted_at) - .then_with(|| a.map_entry_id.cmp(&b.map_entry_id)) - }); - } - } - Ok((latest_repo_work_map_entry, repo_work_map_entries)) -} - -fn load_repo_work_public_proofs( - args: &Args, -) -> Result<( - Option, - Vec, -)> { - let arrival_latest_public_proof = - load_latest_epiphany_cultmesh_repo_work_public_proof(&args.store, args.runtime_id.clone())?; - let mut public_proofs = - load_epiphany_cultmesh_repo_work_public_proofs(&args.store, args.runtime_id.clone())?; - if let Some(latest) = arrival_latest_public_proof.as_ref() { - if !public_proofs - .iter() - .any(|proof| proof.public_proof_id == latest.public_proof_id) - { - public_proofs.push(latest.clone()); - public_proofs.sort_by(|a, b| { - b.generated_at - .cmp(&a.generated_at) - .then_with(|| a.public_proof_id.cmp(&b.public_proof_id)) - }); - } - } - Ok((arrival_latest_public_proof, public_proofs)) -} - -fn load_repo_work_readiness_reports( - args: &Args, -) -> Result<( - Option, - Vec, -)> { - let latest_readiness = - load_latest_epiphany_cultmesh_repo_work_readiness(&args.store, args.runtime_id.clone())?; - let mut reports = - load_epiphany_cultmesh_repo_work_readiness_reports(&args.store, args.runtime_id.clone())?; - if let Some(latest) = latest_readiness.as_ref() { - if !reports - .iter() - .any(|report| report.readiness_id == latest.readiness_id) - { - reports.push(latest.clone()); - reports.sort_by(|a, b| { - b.generated_at - .cmp(&a.generated_at) - .then_with(|| a.readiness_id.cmp(&b.readiness_id)) - }); - } - } - Ok((latest_readiness, reports)) -} - -fn load_swarm_overview_report(args: &Args) -> Result { - let topology = load_epiphany_cultmesh_cluster_topology(&args.store, args.runtime_id.clone())?; - let topology_report = cluster_topology_report(&topology); - let liveness = load_epiphany_cultmesh_daemon_liveness(&args.store, args.runtime_id.clone())?; - let daemon_report = daemon_liveness_report(&liveness); - let (latest_repo_work_overview, repo_work_overviews) = load_repo_work_overview_queue(args)?; - let (latest_repo_work_map_entry, repo_work_map_entries) = load_repo_work_map_entries(args)?; - let (latest_repo_work_public_proof, repo_work_public_proofs) = - load_repo_work_public_proofs(args)?; - let (latest_repo_work_readiness, repo_work_readiness_reports) = - load_repo_work_readiness_reports(args)?; - let latest_idunn_deployment_receipt = load_latest_epiphany_cultmesh_idunn_deployment_receipt( - &args.store, - args.runtime_id.clone(), - )?; - let latest_idunn_aftercare_audit_receipt = - load_latest_epiphany_cultmesh_idunn_aftercare_audit_receipt( - &args.store, - args.runtime_id.clone(), - )?; - let directory = - load_epiphany_cultmesh_eve_surface_directory(&args.store, args.runtime_id.clone())?; - let surface_report = eve_surface_report(&directory, &repo_work_overviews); - let tool_directory = - load_epiphany_cultmesh_daemon_tool_directory(&args.store, args.runtime_id.clone())?; - let tool_report = daemon_tool_directory_report(&tool_directory); - let policy_directory = load_epiphany_cultmesh_daemon_restart_policy_directory( - &args.store, - args.runtime_id.clone(), - )?; - let policy_report = daemon_restart_policy_directory_report_from_rows(&policy_directory); - let managed_service_report = managed_service_sight_report(&args.store, &args.runtime_id)?; - let lifecycle_receipts = load_epiphany_cultmesh_daemon_service_lifecycle_receipts( - &args.store, - args.runtime_id.clone(), - )?; - let latest_lifecycle_receipt = load_latest_epiphany_cultmesh_daemon_service_lifecycle_receipt( - &args.store, - args.runtime_id.clone(), - )?; - let service_lifecycle_rows = service_lifecycle_receipt_directory_rows( - &lifecycle_receipts, - latest_lifecycle_receipt.as_ref(), - ); - let cluster_service_lifecycle_attention = service_lifecycle_rows.iter().find(|row| { - row.family == "cluster-service-lifecycle" && receipt_directory_row_needs_attention(row) - }); - let service_lifecycle_attention = service_lifecycle_rows.iter().find(|row| { - row.family == "service-lifecycle" && receipt_directory_row_needs_attention(row) - }); - let service_lifecycle_attention_rows = service_lifecycle_rows - .iter() - .filter(|row| { - (row.family == "cluster-service-lifecycle" || row.family == "service-lifecycle") - && receipt_directory_row_needs_attention(row) - }) - .cloned() - .collect::>(); - let service_lifecycle_attention_tui_rows = service_lifecycle_attention_rows - .iter() - .map(receipt_directory_tui_row) - .collect::>(); - let cluster_lifecycle_receipts = lifecycle_receipts - .iter() - .filter(|receipt| receipt.service_id == "epiphany-cluster-daemon-services") - .cloned() - .collect::>(); - let cluster_service_execution_audit = - epiphany_cluster_service_execution_audit_report(&cluster_lifecycle_receipts); - let mut single_service_ids = service_lifecycle_attention_rows - .iter() - .filter(|row| row.family == "service-lifecycle") - .filter_map(receipt_directory_row_service_id) - .collect::>(); - single_service_ids.sort(); - single_service_ids.dedup(); - let single_service_execution_audits = single_service_ids - .iter() - .map(|service_id| { - let scoped_receipts = lifecycle_receipts - .iter() - .filter(|receipt| receipt.service_id == *service_id) - .cloned() - .collect::>(); - ( - service_id.clone(), - epiphany_service_execution_audit_report(&scoped_receipts), - ) - }) - .collect::>(); - let service_execution_failed_check_count = cluster_service_execution_audit.failed_count - + single_service_execution_audits - .iter() - .map(|(_, report)| report.failed_count) - .sum::(); - let service_execution_missing_check_count = cluster_service_execution_audit.missing_count - + single_service_execution_audits - .iter() - .map(|(_, report)| report.missing_count) - .sum::(); - let service_execution_private_state_exposed = cluster_service_execution_audit - .private_state_exposed - || single_service_execution_audits - .iter() - .any(|(_, report)| report.private_state_exposed); - let service_execution_failed_check_rows = cluster_service_execution_audit - .checks - .iter() - .chain( - single_service_execution_audits - .iter() - .flat_map(|(_, report)| report.checks.iter()), - ) - .filter(|check| !check.ok || !check.private_state_sealed) - .cloned() - .collect::>(); - let service_execution_failed_check_tui_rows = service_execution_failed_check_rows - .iter() - .map(service_execution_audit_check_tui_row) - .collect::>(); - let service_execution_runbook_actions = service_execution_runbook_actions( - &service_lifecycle_rows, - &cluster_service_execution_audit, - &single_service_execution_audits, - ); - let service_online_runbook_available = service_execution_runbook_actions - .iter() - .any(|action| operator_artifact_status(&action.artifact_ref) == "present"); - let liveness_status = if daemon_report.rows.is_empty() { - "unknown".to_string() - } else if daemon_report.non_ready_count == 0 && tool_report.host_attention_count == 0 { - "ready".to_string() - } else { - "attention".to_string() - }; - let recovery_status = if topology_report.rows.is_empty() { - "unknown".to_string() - } else if policy_report.status == "ok" - && !managed_service_overview_requires_attention(&managed_service_report) - && cluster_service_lifecycle_attention.is_none() - && service_lifecycle_attention.is_none() - { - "ready".to_string() - } else { - "attention".to_string() - }; - let status = if liveness_status == "unknown" || recovery_status == "unknown" { - "unknown".to_string() - } else if liveness_status == "ready" && recovery_status == "ready" { - "ready".to_string() - } else { - "attention".to_string() - }; - let (recommended_action, recommended_wrapper_mode, recommended_wrapper_command) = - if liveness_status == "unknown" { - ( - "none-observation-missing".to_string(), - "none".to_string(), - "none".to_string(), - ) - } else if liveness_status != "ready" { - ( - "epiphany-verse-query poke-down-daemons".to_string(), - "swarm-poke-down".to_string(), - WRAPPER_POKE_NON_READY_COMMAND.to_string(), - ) - } else if recovery_status != "ready" { - if managed_service_overview_requires_attention(&managed_service_report) { - ( - DIRECT_MANAGED_SERVICE_SIGHT_COMMAND.to_string(), - "managed-services".to_string(), - DIRECT_MANAGED_SERVICE_SIGHT_COMMAND.to_string(), - ) - } else if policy_report.status != "ok" { - ( - "epiphany-verse-query restart-policy-directory".to_string(), - "service-policy-directory".to_string(), - WRAPPER_SERVICE_POLICY_DIRECTORY_COMMAND.to_string(), - ) - } else if service_online_runbook_available { - ( - "operator elevated swarm online runbook".to_string(), - "swarm-online-runbook".to_string(), - WRAPPER_SWARM_ONLINE_RUNBOOK_COMMAND.to_string(), - ) - } else if let Some(row) = cluster_service_lifecycle_attention { - ( - "epiphany-verse-query receipt-directory cluster-service-lifecycle follow-up" - .to_string(), - service_lifecycle_wrapper_mode_for_row(row).to_string(), - row.follow_up_command.clone(), - ) - } else if let Some(row) = service_lifecycle_attention { - ( - "epiphany-verse-query receipt-directory service-lifecycle follow-up" - .to_string(), - service_lifecycle_wrapper_mode_for_row(row).to_string(), - row.follow_up_command.clone(), - ) - } else { - ("none".to_string(), "none".to_string(), "none".to_string()) - } - } else { - ("none".to_string(), "none".to_string(), "none".to_string()) - }; - let ( - service_lifecycle_recommended_action, - service_lifecycle_recommended_wrapper_mode, - service_lifecycle_recommended_wrapper_command, - ) = if let Some(row) = cluster_service_lifecycle_attention { - ( - "epiphany-verse-query receipt-directory cluster-service-lifecycle follow-up" - .to_string(), - service_lifecycle_wrapper_mode_for_row(row).to_string(), - row.follow_up_command.clone(), - ) - } else if let Some(row) = service_lifecycle_attention { - ( - "epiphany-verse-query receipt-directory service-lifecycle follow-up".to_string(), - service_lifecycle_wrapper_mode_for_row(row).to_string(), - row.follow_up_command.clone(), - ) - } else { - ("none".to_string(), "none".to_string(), "none".to_string()) - }; - let attention_daemon_ids = daemon_report - .rows - .iter() - .filter(|row| row.needs_poke) - .map(|row| row.daemon_id.clone()) - .collect::>(); - let mut attention_tool_host_daemon_ids = tool_report - .rows - .iter() - .filter(|row| row.host_status != "ready") - .map(|row| row.host_daemon_id.clone()) - .collect::>(); - attention_tool_host_daemon_ids.sort(); - attention_tool_host_daemon_ids.dedup(); - let tool_host_attention_rows = tool_report - .rows - .iter() - .filter(|row| row.host_status != "ready") - .cloned() - .collect::>(); - let tool_host_attention_tui_rows = tool_host_attention_rows - .iter() - .map(daemon_tool_directory_tui_row) - .collect::>(); - let (repo_work_overview_rows, repo_work_overview_tui_rows) = - repo_work_overview_rows(&repo_work_overviews); - let (repo_work_map_rows, repo_work_map_tui_rows) = repo_work_map_rows(&repo_work_map_entries); - let (repo_work_map_semantic_rows, repo_work_map_semantic_tui_rows) = - repo_work_map_semantic_rows(&repo_work_map_entries); - let (repo_work_map_family_lens_rows, repo_work_map_family_lens_tui_rows) = - repo_work_map_family_lens_rows(&[]); - let (repo_work_map_path_lens_rows, repo_work_map_path_lens_tui_rows) = - repo_work_map_path_lens_rows(&[]); - let (repo_work_map_branch_lens_rows, repo_work_map_branch_lens_tui_rows) = - repo_work_map_branch_lens_rows(&[]); - let (repo_work_map_stage_lens_rows, repo_work_map_stage_lens_tui_rows) = - repo_work_map_stage_lens_rows(&[]); - let (repo_work_map_gate_lens_rows, repo_work_map_gate_lens_tui_rows) = - repo_work_map_gate_lens_rows(&[]); - let (repo_work_map_closure_rows, repo_work_map_closure_tui_rows) = - repo_work_map_closure_rows(&[]); - let (repo_work_map_acceptance_rows, repo_work_map_acceptance_tui_rows) = - repo_work_map_acceptance_rows(&[]); - let (repo_work_public_proof_rows, repo_work_public_proof_tui_rows) = - repo_work_public_proof_rows(&repo_work_public_proofs); - let (repo_work_readiness_rows, repo_work_readiness_tui_rows) = - repo_work_readiness_rows(&repo_work_readiness_reports); - let (idunn_deployment_receipt_rows, idunn_deployment_receipt_tui_rows) = - idunn_deployment_receipt_rows( - latest_idunn_deployment_receipt.as_ref(), - latest_idunn_aftercare_audit_receipt.as_ref(), - ); - let (swarm_action_rows, swarm_action_tui_rows) = swarm_action_rows( - &liveness_status, - &tool_host_attention_rows, - &policy_report, - &service_lifecycle_attention_rows, - &service_execution_failed_check_rows, - &service_execution_runbook_actions, - &[], - &[], - &[], - ); - let private_state_exposed = daemon_report - .rows - .iter() - .any(|row| row.private_state_exposed) - || tool_report.rows.iter().any(|row| row.private_state_exposed) - || policy_report.private_state_exposed - || managed_service_report.private_state_exposed - || managed_service_report - .rows - .iter() - .any(|row| row.private_state_exposed) - || repo_work_overviews - .iter() - .any(|overview| overview.private_state_exposed) - || repo_work_map_entries - .iter() - .any(|entry| entry.private_state_exposed) - || repo_work_map_semantic_rows - .iter() - .any(|row| row.private_state_exposed) - || repo_work_map_family_lens_rows - .iter() - .any(|row| row.private_state_exposed) - || repo_work_map_path_lens_rows - .iter() - .any(|row| row.private_state_exposed) - || repo_work_map_branch_lens_rows - .iter() - .any(|row| row.private_state_exposed) - || repo_work_map_stage_lens_rows - .iter() - .any(|row| row.private_state_exposed) - || repo_work_map_gate_lens_rows - .iter() - .any(|row| row.private_state_exposed) - || repo_work_map_closure_rows - .iter() - .any(|row| row.private_state_exposed) - || repo_work_map_acceptance_rows - .iter() - .any(|row| row.private_state_exposed) - || repo_work_public_proofs - .iter() - .any(|proof| proof.private_state_exposed) - || repo_work_readiness_reports - .iter() - .any(|report| report.private_state_exposed) - || idunn_deployment_receipt_rows - .iter() - .any(|row| row.private_state_exposed) - || service_execution_private_state_exposed - || service_lifecycle_rows - .iter() - .any(|row| row.private_state_exposed); - let latest_repo_work_overview_id = latest_repo_work_overview - .as_ref() - .map(|overview| overview.overview_id.clone()); - let latest_repo_work_map_entry = latest_repo_work_map_entry - .as_ref() - .map(|entry| entry.map_entry_id.clone()); - let latest_repo_work_gate = latest_repo_work_overview - .as_ref() - .map(|_| "historical-only".to_string()); - let latest_repo_work_blocker = latest_repo_work_overview - .as_ref() - .map(|_| "canonical-frontier-workflow-required".to_string()); - let latest_repo_work_next_safe_move = latest_repo_work_overview.as_ref().map(|_| { - "Canonical frontier workflow required; historical record grants no action.".to_string() - }); - let latest_repo_work_public_proof = latest_repo_work_public_proof - .as_ref() - .map(|proof| proof.public_proof_id.clone()); - let latest_repo_work_readiness = latest_repo_work_readiness - .as_ref() - .map(|report| report.readiness_id.clone()); - let latest_idunn_deployment_receipt = latest_idunn_deployment_receipt - .as_ref() - .map(|receipt| receipt.receipt_id.clone()); - let latest_idunn_aftercare_audit_receipt = latest_idunn_aftercare_audit_receipt - .as_ref() - .map(|receipt| receipt.receipt_id.clone()); - Ok(SwarmOverviewReport { - status, - liveness_status, - recovery_status, - recommended_action, - recommended_wrapper_mode, - recommended_wrapper_command, - service_lifecycle_recommended_action, - service_lifecycle_recommended_wrapper_mode, - service_lifecycle_recommended_wrapper_command, - swarm_action_rows, - swarm_action_tui_rows, - attention_daemon_ids, - attention_tool_host_daemon_ids, - tool_host_attention_rows, - tool_host_attention_tui_rows, - service_lifecycle_attention_rows, - service_lifecycle_attention_tui_rows, - managed_service_report, - service_execution_failed_check_count, - service_execution_missing_check_count, - service_execution_failed_check_rows, - service_execution_failed_check_tui_rows, - repo_work_map_rows, - repo_work_map_tui_rows, - repo_work_map_semantic_rows, - repo_work_map_semantic_tui_rows, - repo_work_map_family_lens_rows, - repo_work_map_family_lens_tui_rows, - repo_work_map_path_lens_rows, - repo_work_map_path_lens_tui_rows, - repo_work_map_branch_lens_rows, - repo_work_map_branch_lens_tui_rows, - repo_work_map_stage_lens_rows, - repo_work_map_stage_lens_tui_rows, - repo_work_map_gate_lens_rows, - repo_work_map_gate_lens_tui_rows, - repo_work_map_closure_rows, - repo_work_map_closure_tui_rows, - repo_work_map_acceptance_rows, - repo_work_map_acceptance_tui_rows, - repo_work_overview_rows, - repo_work_overview_tui_rows, - repo_work_readiness_rows, - repo_work_readiness_tui_rows, - repo_work_public_proof_rows, - repo_work_public_proof_tui_rows, - idunn_deployment_receipt_rows, - idunn_deployment_receipt_tui_rows, - latest_repo_work_overview_id, - latest_repo_work_map_entry, - latest_repo_work_gate, - latest_repo_work_blocker, - latest_repo_work_next_safe_move, - latest_repo_work_readiness, - latest_repo_work_public_proof, - latest_idunn_deployment_receipt, - latest_idunn_aftercare_audit_receipt, - topology_report, - daemon_report, - surface_report, - tool_report, - policy_report, - private_state_exposed, - }) -} - -fn receipt_directory_report( - context: &EpiphanyLocalVerseContext, - lifecycle_receipts: &[EpiphanyCultMeshDaemonServiceLifecycleReceiptEntry], -) -> ReceiptDirectoryReport { - let mut rows = Vec::new(); - let mut tui_rows = Vec::new(); - - push_receipt_directory_row( - &mut rows, - &mut tui_rows, - ReceiptDirectoryRow { - family: "operator-run".to_string(), - owner: "operator".to_string(), - document_kind: "epiphany.cultmesh.operator_run_receipt.v0".to_string(), - latest_id: context - .latest_operator_run_receipt - .as_ref() - .map(|receipt| receipt.run_id.clone()) - .unwrap_or_else(|| "missing".to_string()), - status: context - .latest_operator_run_receipt - .as_ref() - .map(|receipt| receipt.status.clone()) - .unwrap_or_else(|| "missing".to_string()), - route: context - .latest_operator_run_receipt - .as_ref() - .map(|receipt| receipt.result_path.clone()) - .unwrap_or_else(|| "none".to_string()), - service_id: "none".to_string(), - service_route: "none".to_string(), - follow_up_command: WRAPPER_OVERVIEW_COMMAND.to_string(), - artifact_ref: "none".to_string(), - artifact_status: "none".to_string(), - artifact_sha256: "none".to_string(), - present: context.latest_operator_run_receipt.is_some(), - private_state_exposed: false, - }, - ); - push_receipt_directory_row( - &mut rows, - &mut tui_rows, - ReceiptDirectoryRow { - family: "weksa-lowering".to_string(), - owner: "Weksa".to_string(), - document_kind: "epiphany.cultmesh.weksa_lowering_receipt.v0".to_string(), - latest_id: context - .latest_weksa_lowering_receipt - .as_ref() - .map(|receipt| receipt.receipt_id.clone()) - .unwrap_or_else(|| "missing".to_string()), - status: context - .latest_weksa_lowering_receipt - .as_ref() - .map(|receipt| format!("lowered:{}", receipt.target_language)) - .unwrap_or_else(|| "missing".to_string()), - route: context - .latest_weksa_lowering_receipt - .as_ref() - .map(|receipt| receipt.delivery_surface.clone()) - .unwrap_or_else(|| "none".to_string()), - service_id: "none".to_string(), - service_route: "none".to_string(), - follow_up_command: "cargo run --manifest-path .\\epiphany-core\\Cargo.toml --bin epiphany-weksa-interlingua-smoke".to_string(), - artifact_ref: context - .latest_weksa_lowering_receipt - .as_ref() - .map(|receipt| receipt.lowered_text_ref.clone()) - .unwrap_or_else(|| "none".to_string()), - artifact_status: context - .latest_weksa_lowering_receipt - .as_ref() - .map(|receipt| { - if receipt.lowered_text_ref.trim().is_empty() { - "none".to_string() - } else { - "external-ref".to_string() - } - }) - .unwrap_or_else(|| "none".to_string()), - artifact_sha256: "none".to_string(), - present: context.latest_weksa_lowering_receipt.is_some(), - private_state_exposed: context - .latest_weksa_lowering_receipt - .as_ref() - .map(|receipt| { - receipt.private_state_exposed || receipt.publication_authorized - }) - .unwrap_or(false), - }, - ); - push_receipt_directory_row( - &mut rows, - &mut tui_rows, - ReceiptDirectoryRow { - family: "daemon-poke".to_string(), - owner: "Self".to_string(), - document_kind: "epiphany.cultmesh.daemon_poke_receipt.v0".to_string(), - latest_id: context - .latest_daemon_poke_receipt - .as_ref() - .map(|receipt| receipt.receipt_id.clone()) - .unwrap_or_else(|| "missing".to_string()), - status: context - .latest_daemon_poke_receipt - .as_ref() - .map(|receipt| receipt_directory_daemon_poke_status(context, receipt)) - .unwrap_or_else(|| "missing".to_string()), - route: context - .latest_daemon_poke_receipt - .as_ref() - .map(|receipt| receipt.target_daemon_id.clone()) - .unwrap_or_else(|| "none".to_string()), - service_id: "none".to_string(), - service_route: "none".to_string(), - follow_up_command: WRAPPER_POKE_NON_READY_COMMAND.to_string(), - artifact_ref: "none".to_string(), - artifact_status: "none".to_string(), - artifact_sha256: "none".to_string(), - present: context.latest_daemon_poke_receipt.is_some(), - private_state_exposed: context - .latest_daemon_poke_receipt - .as_ref() - .map(|receipt| receipt.private_state_exposed) - .unwrap_or(false), - }, - ); - push_receipt_directory_row( - &mut rows, - &mut tui_rows, - ReceiptDirectoryRow { - family: "daemon-tool".to_string(), - owner: "daemon host".to_string(), - document_kind: "epiphany.cultmesh.daemon_tool_invocation_receipt.v0".to_string(), - latest_id: context - .latest_daemon_tool_invocation_receipt - .as_ref() - .map(|receipt| receipt.receipt_id.clone()) - .unwrap_or_else(|| "missing".to_string()), - status: context - .latest_daemon_tool_invocation_receipt - .as_ref() - .map(|receipt| receipt.status.clone()) - .unwrap_or_else(|| "missing".to_string()), - route: context - .latest_daemon_tool_invocation_receipt - .as_ref() - .map(|receipt| { - format!( - "{} -> {}", - receipt.requesting_cluster_id, receipt.host_cluster_id - ) - }) - .unwrap_or_else(|| "none".to_string()), - service_id: "none".to_string(), - service_route: "none".to_string(), - follow_up_command: "none".to_string(), - artifact_ref: "none".to_string(), - artifact_status: "none".to_string(), - artifact_sha256: "none".to_string(), - present: context.latest_daemon_tool_invocation_receipt.is_some(), - private_state_exposed: context - .latest_daemon_tool_invocation_receipt - .as_ref() - .map(|receipt| receipt.private_state_exposed) - .unwrap_or(false), - }, - ); - push_receipt_directory_row( - &mut rows, - &mut tui_rows, - ReceiptDirectoryRow { - family: "bifrost-publication".to_string(), - owner: "Bifrost".to_string(), - document_kind: "gamecult.bifrost.github_publication_receipt.v0".to_string(), - latest_id: context - .arrival_latest_bifrost_github_publication_receipt - .as_ref() - .map(|receipt| receipt.receipt_id.clone()) - .unwrap_or_else(|| "missing".to_string()), - status: context - .arrival_latest_bifrost_github_publication_receipt - .as_ref() - .map(|receipt| receipt.publication_status.clone()) - .unwrap_or_else(|| "missing".to_string()), - route: context - .arrival_latest_bifrost_github_publication_receipt - .as_ref() - .map(|receipt| receipt.pull_request_url.clone()) - .unwrap_or_else(|| "none".to_string()), - service_id: "none".to_string(), - service_route: "none".to_string(), - follow_up_command: "none".to_string(), - artifact_ref: "none".to_string(), - artifact_status: "none".to_string(), - artifact_sha256: "none".to_string(), - present: context - .arrival_latest_bifrost_github_publication_receipt - .is_some(), - private_state_exposed: context - .arrival_latest_bifrost_github_publication_receipt - .as_ref() - .map(|receipt| receipt.private_state_exposed) - .unwrap_or(false), - }, - ); - push_receipt_directory_row( - &mut rows, - &mut tui_rows, - ReceiptDirectoryRow { - family: "bifrost-public-proof-publication".to_string(), - owner: "Bifrost".to_string(), - document_kind: "gamecult.bifrost.public_proof_publication_receipt.v0".to_string(), - latest_id: context - .arrival_latest_bifrost_public_proof_publication_receipt - .as_ref() - .map(|receipt| receipt.receipt_id.clone()) - .unwrap_or_else(|| "missing".to_string()), - status: context - .arrival_latest_bifrost_public_proof_publication_receipt - .as_ref() - .map(|receipt| receipt.status.clone()) - .unwrap_or_else(|| "missing".to_string()), - route: context - .arrival_latest_bifrost_public_proof_publication_receipt - .as_ref() - .map(|receipt| receipt.publication_url.clone()) - .unwrap_or_else(|| "none".to_string()), - service_id: "none".to_string(), - service_route: "none".to_string(), - follow_up_command: "none".to_string(), - artifact_ref: context - .arrival_latest_bifrost_public_proof_publication_receipt - .as_ref() - .map(|receipt| receipt.public_proof_ref.clone()) - .unwrap_or_else(|| "none".to_string()), - artifact_status: context - .arrival_latest_bifrost_public_proof_publication_receipt - .as_ref() - .map(|receipt| { - if receipt.public_proof_ref == "none" { - "none".to_string() - } else { - "external-ref".to_string() - } - }) - .unwrap_or_else(|| "none".to_string()), - artifact_sha256: context - .arrival_latest_bifrost_public_proof_publication_receipt - .as_ref() - .map(|receipt| receipt.public_proof_sha256.clone()) - .unwrap_or_else(|| "none".to_string()), - present: context - .arrival_latest_bifrost_public_proof_publication_receipt - .is_some(), - private_state_exposed: context - .arrival_latest_bifrost_public_proof_publication_receipt - .as_ref() - .map(|receipt| receipt.private_state_exposed) - .unwrap_or(false), - }, - ); - push_receipt_directory_row( - &mut rows, - &mut tui_rows, - ReceiptDirectoryRow { - family: "coordinator-run".to_string(), - owner: "Self".to_string(), - document_kind: "epiphany.cultmesh.coordinator_run_receipt.v0".to_string(), - latest_id: context - .latest_coordinator_run_receipt - .as_ref() - .map(|receipt| receipt.receipt_id.clone()) - .unwrap_or_else(|| "missing".to_string()), - status: context - .latest_coordinator_run_receipt - .as_ref() - .map(|receipt| receipt.status.clone()) - .unwrap_or_else(|| "missing".to_string()), - route: context - .latest_coordinator_run_receipt - .as_ref() - .map(|receipt| receipt.final_action.clone()) - .unwrap_or_else(|| "none".to_string()), - service_id: "none".to_string(), - service_route: "none".to_string(), - follow_up_command: "tools/epiphany_local_run.ps1 -Mode status".to_string(), - artifact_ref: "none".to_string(), - artifact_status: "none".to_string(), - artifact_sha256: "none".to_string(), - present: context.latest_coordinator_run_receipt.is_some(), - private_state_exposed: context - .latest_coordinator_run_receipt - .as_ref() - .map(|receipt| receipt.private_state_exposed) - .unwrap_or(false), - }, - ); - push_receipt_directory_row( - &mut rows, - &mut tui_rows, - ReceiptDirectoryRow { - family: "hands-action-gate".to_string(), - owner: "Hands".to_string(), - document_kind: "epiphany.cultmesh.hands_action_gate.v0".to_string(), - latest_id: context - .latest_hands_action_gate - .as_ref() - .map(|gate| gate.gate_id.clone()) - .unwrap_or_else(|| "missing".to_string()), - status: context - .latest_hands_action_gate - .as_ref() - .map(|gate| gate.status.clone()) - .unwrap_or_else(|| "missing".to_string()), - route: context - .latest_hands_action_gate - .as_ref() - .map(|gate| gate.requested_paths.join(", ")) - .unwrap_or_else(|| "none".to_string()), - service_id: "none".to_string(), - service_route: "none".to_string(), - follow_up_command: - "epiphany-hands-action record-pass --gate-from " - .to_string(), - artifact_ref: "none".to_string(), - artifact_status: "none".to_string(), - artifact_sha256: "none".to_string(), - present: context.latest_hands_action_gate.is_some(), - private_state_exposed: context - .latest_hands_action_gate - .as_ref() - .map(|gate| gate.private_state_exposed) - .unwrap_or(false), - }, - ); - push_receipt_directory_row( - &mut rows, - &mut tui_rows, - ReceiptDirectoryRow { - family: "role-review".to_string(), - owner: "Mind".to_string(), - document_kind: "epiphany.cultmesh.role_review_event.v0".to_string(), - latest_id: context - .latest_role_review_event - .as_ref() - .map(|event| event.event_id.clone()) - .unwrap_or_else(|| "missing".to_string()), - status: context - .latest_role_review_event - .as_ref() - .map(|event| event.review_status.clone()) - .unwrap_or_else(|| "missing".to_string()), - route: context - .latest_role_review_event - .as_ref() - .map(|event| format!("{}:{}", event.surface, event.role_id)) - .unwrap_or_else(|| "none".to_string()), - service_id: "none".to_string(), - service_route: "none".to_string(), - follow_up_command: "tools/epiphany_local_run.ps1 -Mode status".to_string(), - artifact_ref: "none".to_string(), - artifact_status: "none".to_string(), - artifact_sha256: "none".to_string(), - present: context.latest_role_review_event.is_some(), - private_state_exposed: context - .latest_role_review_event - .as_ref() - .map(|event| event.private_state_exposed) - .unwrap_or(false), - }, - ); - push_receipt_directory_row( - &mut rows, - &mut tui_rows, - ReceiptDirectoryRow { - family: "work-loop".to_string(), - owner: "Self".to_string(), - document_kind: "epiphany.cultmesh.work_loop_telemetry.v0".to_string(), - latest_id: context - .latest_work_loop_summary - .as_ref() - .map(|summary| summary.telemetry_id.clone()) - .unwrap_or_else(|| "missing".to_string()), - status: context - .latest_work_loop_summary - .as_ref() - .map(|summary| summary.source_stage.clone()) - .unwrap_or_else(|| "missing".to_string()), - route: context - .latest_work_loop_summary - .as_ref() - .map(|summary| summary.target_stages.join(", ")) - .unwrap_or_else(|| "none".to_string()), - service_id: "none".to_string(), - service_route: "none".to_string(), - follow_up_command: "tools/epiphany_local_run.ps1 -Mode status".to_string(), - artifact_ref: "none".to_string(), - artifact_status: "none".to_string(), - artifact_sha256: "none".to_string(), - present: context.latest_work_loop_summary.is_some(), - private_state_exposed: false, - }, - ); - push_receipt_directory_row( - &mut rows, - &mut tui_rows, - ReceiptDirectoryRow { - family: "idunn-deployment".to_string(), - owner: "Idunn".to_string(), - document_kind: "gamecult.idunn.deployment_receipt.v0".to_string(), - latest_id: context - .latest_idunn_deployment_receipt - .as_ref() - .map(|receipt| receipt.receipt_id.clone()) - .unwrap_or_else(|| "missing".to_string()), - status: context - .latest_idunn_deployment_receipt - .as_ref() - .map(|receipt| receipt.status.clone()) - .unwrap_or_else(|| "missing".to_string()), - route: context - .latest_idunn_deployment_receipt - .as_ref() - .map(|receipt| receipt.watched_ref.clone()) - .unwrap_or_else(|| "none".to_string()), - service_id: "repo-deployment".to_string(), - service_route: context - .latest_idunn_deployment_receipt - .as_ref() - .map(|receipt| receipt.result_ref.clone()) - .unwrap_or_else(|| "none".to_string()), - follow_up_command: DIRECT_IDUNN_DEPLOYMENT_AFTERCARE_AUDIT_COMMAND.to_string(), - artifact_ref: context - .latest_idunn_deployment_receipt - .as_ref() - .map(|receipt| receipt.result_ref.clone()) - .unwrap_or_else(|| "none".to_string()), - artifact_status: context - .latest_idunn_deployment_receipt - .as_ref() - .map(|receipt| { - if receipt.result_ref == "none" { - "none".to_string() - } else { - "external-ref".to_string() - } - }) - .unwrap_or_else(|| "none".to_string()), - artifact_sha256: "none".to_string(), - present: context.latest_idunn_deployment_receipt.is_some(), - private_state_exposed: context - .latest_idunn_deployment_receipt - .as_ref() - .map(|receipt| receipt.private_state_exposed) - .unwrap_or(false), - }, - ); - push_receipt_directory_row( - &mut rows, - &mut tui_rows, - ReceiptDirectoryRow { - family: "idunn-aftercare".to_string(), - owner: "Idunn".to_string(), - document_kind: "gamecult.idunn.deployment_aftercare_audit.v0".to_string(), - latest_id: context - .latest_idunn_aftercare_audit_receipt - .as_ref() - .map(|receipt| receipt.receipt_id.clone()) - .unwrap_or_else(|| "missing".to_string()), - status: context - .latest_idunn_aftercare_audit_receipt - .as_ref() - .map(|receipt| receipt.status.clone()) - .unwrap_or_else(|| "missing".to_string()), - route: context - .latest_idunn_aftercare_audit_receipt - .as_ref() - .map(|receipt| receipt.checked_ref.clone()) - .unwrap_or_else(|| "none".to_string()), - service_id: "repo-deployment".to_string(), - service_route: context - .latest_idunn_aftercare_audit_receipt - .as_ref() - .map(|receipt| receipt.audit_ref.clone()) - .unwrap_or_else(|| "none".to_string()), - follow_up_command: DIRECT_IDUNN_DEPLOYMENT_AFTERCARE_AUDIT_COMMAND.to_string(), - artifact_ref: context - .latest_idunn_aftercare_audit_receipt - .as_ref() - .map(|receipt| receipt.audit_ref.clone()) - .unwrap_or_else(|| "none".to_string()), - artifact_status: context - .latest_idunn_aftercare_audit_receipt - .as_ref() - .map(|receipt| { - if receipt.audit_ref == "none" { - "none".to_string() - } else { - "external-ref".to_string() - } - }) - .unwrap_or_else(|| "none".to_string()), - artifact_sha256: "none".to_string(), - present: context.latest_idunn_aftercare_audit_receipt.is_some(), - private_state_exposed: context - .latest_idunn_aftercare_audit_receipt - .as_ref() - .map(|receipt| receipt.private_state_exposed) - .unwrap_or(false), - }, - ); - for row in service_lifecycle_receipt_directory_rows( - lifecycle_receipts, - context.latest_daemon_service_lifecycle_receipt.as_ref(), - ) { - push_receipt_directory_row(&mut rows, &mut tui_rows, row); - } - push_receipt_directory_row( - &mut rows, - &mut tui_rows, - ReceiptDirectoryRow { - family: "scheduler".to_string(), - owner: "daemon-supervisor".to_string(), - document_kind: "epiphany.cultmesh.daemon_scheduler_receipt.v0".to_string(), - latest_id: context - .latest_daemon_scheduler_receipt - .as_ref() - .map(|receipt| receipt.receipt_id.clone()) - .unwrap_or_else(|| "missing".to_string()), - status: context - .latest_daemon_scheduler_receipt - .as_ref() - .map(|receipt| receipt.status.clone()) - .unwrap_or_else(|| "missing".to_string()), - route: context - .latest_daemon_scheduler_receipt - .as_ref() - .map(|receipt| receipt.daemon_selector.clone()) - .unwrap_or_else(|| "none".to_string()), - service_id: "none".to_string(), - service_route: "none".to_string(), - follow_up_command: WRAPPER_SERVICE_TICK_COMMAND.to_string(), - artifact_ref: "none".to_string(), - artifact_status: "none".to_string(), - artifact_sha256: "none".to_string(), - present: context.latest_daemon_scheduler_receipt.is_some(), - private_state_exposed: context - .latest_daemon_scheduler_receipt - .as_ref() - .map(|receipt| receipt.private_state_exposed) - .unwrap_or(false), - }, - ); - push_receipt_directory_row( - &mut rows, - &mut tui_rows, - ReceiptDirectoryRow { - family: "persona-speech".to_string(), - owner: "Persona".to_string(), - document_kind: "epiphany.cultmesh.persona_speech_audit.v0".to_string(), - latest_id: context - .latest_persona_speech_audit - .as_ref() - .map(|audit| audit.audit_id.clone()) - .unwrap_or_else(|| "missing".to_string()), - status: context - .latest_persona_speech_audit - .as_ref() - .map(|audit| audit.decision.clone()) - .unwrap_or_else(|| "missing".to_string()), - route: context - .latest_persona_speech_audit - .as_ref() - .map(|audit| audit.requested_channel_id.clone()) - .unwrap_or_else(|| "none".to_string()), - service_id: "none".to_string(), - service_route: "none".to_string(), - follow_up_command: "tools/epiphany_local_run.ps1 -Mode mvp -PersonaInput " - .to_string(), - artifact_ref: "none".to_string(), - artifact_status: "none".to_string(), - artifact_sha256: "none".to_string(), - present: context.latest_persona_speech_audit.is_some(), - private_state_exposed: context - .latest_persona_speech_audit - .as_ref() - .map(|audit| audit.private_state_exposed) - .unwrap_or(false), - }, - ); - push_receipt_directory_row( - &mut rows, - &mut tui_rows, - ReceiptDirectoryRow { - family: "agent-state-soa".to_string(), - owner: "Mind".to_string(), - document_kind: "epiphany.cultmesh.agent_state_soa_summary.v0".to_string(), - latest_id: context - .latest_agent_state_soa_summary - .as_ref() - .map(|summary| summary.summary_id.clone()) - .unwrap_or_else(|| "missing".to_string()), - status: context - .latest_agent_state_soa_summary - .as_ref() - .map(|summary| format!("{} rows", summary.row_count)) - .unwrap_or_else(|| "missing".to_string()), - route: "CultMesh SoA".to_string(), - service_id: "none".to_string(), - service_route: "none".to_string(), - follow_up_command: "tools/epiphany_local_run.ps1 -Mode agent-state-soa".to_string(), - artifact_ref: "none".to_string(), - artifact_status: "none".to_string(), - artifact_sha256: "none".to_string(), - present: context.latest_agent_state_soa_summary.is_some(), - private_state_exposed: context - .latest_agent_state_soa_summary - .as_ref() - .map(|summary| summary.private_state_exposed) - .unwrap_or(false), - }, - ); - - let present_row_count = rows.iter().filter(|row| row.present).count(); - let absent_row_count = rows.iter().filter(|row| !row.present).count(); - let ready_row_count = rows - .iter() - .filter(|row| row.present && !receipt_directory_row_needs_attention(row)) - .count(); - let attention_row_count = rows - .iter() - .filter(|row| receipt_directory_row_needs_attention(row)) - .count(); - let attention_route_rows = rows - .iter() - .filter(|row| receipt_directory_row_needs_attention(row)) - .map(receipt_directory_attention_route_row) - .collect::>(); - let missing_row_count = absent_row_count; - let artifact_none_count = rows - .iter() - .filter(|row| row.artifact_status == "none") - .count(); - let artifact_external_ref_count = rows - .iter() - .filter(|row| row.artifact_status == "external-ref") - .count(); - let artifact_present_count = rows - .iter() - .filter(|row| row.artifact_status == "present") - .count(); - let artifact_missing_count = rows - .iter() - .filter(|row| row.artifact_status == "missing") - .count(); - let private_state_exposed = rows.iter().any(|row| row.private_state_exposed); - let status = if attention_row_count > 0 { - "attention" - } else if ready_row_count == 0 { - "empty" - } else { - "ok" - } - .to_string(); - - ReceiptDirectoryReport { - status, - rows, - tui_rows, - attention_route_rows, - present_row_count, - absent_row_count, - ready_row_count, - attention_row_count, - missing_row_count, - artifact_none_count, - artifact_external_ref_count, - artifact_present_count, - artifact_missing_count, - private_state_exposed, - } -} - -fn push_receipt_directory_row( - rows: &mut Vec, - tui_rows: &mut Vec, - row: ReceiptDirectoryRow, -) { - tui_rows.push(receipt_directory_tui_row(&row)); - rows.push(row); -} - -fn receipt_directory_tui_row(row: &ReceiptDirectoryRow) -> String { - let compact_status = if receipt_directory_row_needs_attention(row) { - "ATTN" - } else if !row.present { - "ABSENT" - } else { - "OK" - }; - format!( - "{compact_status} | {} | {} | {} | service={} | {} | {} | followUp={} | artifact={} | sha256={}", - row.owner, - row.family, - row.status, - row.service_id, - row.route, - row.latest_id, - row.follow_up_command, - row.artifact_status, - row.artifact_sha256 - ) -} - -fn receipt_directory_attention_route_row(row: &ReceiptDirectoryRow) -> String { - format!( - "{}:{}:{}->{}", - row.family, row.status, row.route, row.follow_up_command - ) -} - -fn receipt_directory_row_service_id(row: &ReceiptDirectoryRow) -> Option { - (row.service_id != "none" && !row.service_id.is_empty()).then(|| row.service_id.clone()) -} - -fn receipt_directory_daemon_poke_status( - context: &EpiphanyLocalVerseContext, - receipt: &EpiphanyCultMeshDaemonPokeReceiptEntry, -) -> String { - let Some(current) = context - .daemon_statuses - .iter() - .find(|status| status.daemon_id == receipt.target_daemon_id) - else { - return receipt.resulting_status.clone(); - }; - let current_status = current.status.as_str(); - - let causally_recovered = current_status == "ready" - && receipt.resulting_status == "awaiting-provider-heartbeat" - && context - .latest_daemon_poke_intent - .as_ref() - .filter(|intent| intent.intent_id == receipt.intent_id) - .map(|intent| { - provider_heartbeat_causally_follows_attempt( - ¤t.last_heartbeat_utc, - &intent.observed_last_heartbeat_utc, - &receipt.completed_at_utc, - ) - }) - .unwrap_or(false); - - if causally_recovered { - "resolved".to_string() - } else if receipt.resulting_status == "awaiting-provider-heartbeat" { - receipt.resulting_status.clone() - } else if current_status == "ready" && receipt.resulting_status != "ready" { - "resolved".to_string() - } else if current_status != receipt.resulting_status { - format!("current-{current_status}") - } else { - receipt.resulting_status.clone() - } -} - -fn provider_heartbeat_causally_follows_attempt( - current_heartbeat_utc: &str, - observed_heartbeat_utc: &str, - attempt_completed_utc: &str, -) -> bool { - let Ok(current) = DateTime::parse_from_rfc3339(current_heartbeat_utc) else { - return false; - }; - let Ok(observed) = DateTime::parse_from_rfc3339(observed_heartbeat_utc) else { - return false; - }; - let Ok(completed) = DateTime::parse_from_rfc3339(attempt_completed_utc) else { - return false; - }; - current > observed && current > completed -} - -fn service_execution_audit_check_tui_row(check: &EpiphanyServiceExecutionAuditCheck) -> String { - let service_id = check.service_id.as_deref().unwrap_or("unknown-service"); - let observed_status = check.observed_status.as_deref().unwrap_or("missing"); - let receipt_id = check.receipt_id.as_deref().unwrap_or("missing"); - let artifact_ref = check.operator_artifact_ref.as_deref().unwrap_or("none"); - let allowed_statuses = check.allowed_statuses.join("|"); - let follow_up = service_execution_check_follow_up_command(&check.action); - let seal_status = if check.private_state_sealed { - "sealed" - } else { - "private-state-exposed" - }; - format!( - "{}::{}={} | allowed={} | receipt={} | artifact={} | followUp={} | {}", - service_id, - check.action, - observed_status, - allowed_statuses, - receipt_id, - artifact_ref, - follow_up, - seal_status - ) -} - -fn service_execution_check_follow_up_command(action: &str) -> &'static str { - match action { - "cluster-windows-service-execution-readiness" => { - WRAPPER_CLUSTER_SERVICE_EXECUTION_READINESS_COMMAND - } - "cluster-windows-service-execution-runbook" - | "cluster-windows-service-install" - | "cluster-windows-service-start" - | "cluster-windows-service-execution-audit" - | "cluster-windows-service-stop" => "none", - "windows-service-execution-runbook" | "windows-service-execution-readiness" => { - WRAPPER_MANAGED_SERVICE_TASK_PLAN_COMMAND - } - "windows-service-install" => WRAPPER_SERVICE_INSTALL_EXECUTE_COMMAND, - "windows-service-start" => WRAPPER_SERVICE_START_EXECUTE_COMMAND, - "windows-service-status" => WRAPPER_SERVICE_STATUS_COMMAND, - "windows-service-reconcile" => WRAPPER_SERVICE_STATUS_COMMAND, - "windows-service-stop" => WRAPPER_SERVICE_STOP_EXECUTE_COMMAND, - _ => "none", - } -} - -fn receipt_directory_row_needs_attention(row: &ReceiptDirectoryRow) -> bool { - if row.private_state_exposed { - return true; - } - if !row.present { - return false; - } - if row.family == "service-lifecycle" || row.family == "cluster-service-lifecycle" { - return service_lifecycle_status_needs_attention(&row.status); - } - false -} - -fn service_lifecycle_status_needs_attention(status: &str) -> bool { - matches!( - status, - "not-elevated" - | "execution-refused-not-elevated" - | "incomplete" - | "query-failed" - | "drift" - | "missing" - | "failed" - | "start-failed" - | "stop-failed" - | "install-failed" - ) -} - -fn service_lifecycle_receipt_directory_rows( - lifecycle_receipts: &[EpiphanyCultMeshDaemonServiceLifecycleReceiptEntry], - latest_daemon_service_lifecycle_receipt: Option< - &EpiphanyCultMeshDaemonServiceLifecycleReceiptEntry, - >, -) -> Vec { - let latest_cluster_lifecycle_receipt = - service_lifecycle_receipt_for_directory(lifecycle_receipts, true).or_else(|| { - latest_daemon_service_lifecycle_receipt - .filter(|receipt| is_cluster_service_lifecycle_receipt(receipt)) - }); - let latest_single_lifecycle_receipt = - service_lifecycle_receipt_for_directory(lifecycle_receipts, false).or_else(|| { - latest_daemon_service_lifecycle_receipt - .filter(|receipt| !is_cluster_service_lifecycle_receipt(receipt)) - }); - let latest_cluster_execution_runbook_receipt = latest_service_lifecycle_receipt_for_action( - lifecycle_receipts, - true, - "cluster-windows-service-execution-runbook", - ) - .or_else(|| { - latest_daemon_service_lifecycle_receipt.filter(|receipt| { - is_cluster_service_lifecycle_receipt(receipt) - && receipt.action == "cluster-windows-service-execution-runbook" - }) - }); - let latest_service_execution_runbook_receipt = latest_service_lifecycle_receipt_for_action( - lifecycle_receipts, - false, - "windows-service-execution-runbook", - ) - .or_else(|| { - latest_daemon_service_lifecycle_receipt.filter(|receipt| { - !is_cluster_service_lifecycle_receipt(receipt) - && receipt.action == "windows-service-execution-runbook" - }) - }); - - vec![ - receipt_directory_service_lifecycle_row( - "cluster-service-lifecycle", - latest_cluster_lifecycle_receipt, - WRAPPER_CLUSTER_SERVICE_EXECUTION_AUDIT_COMMAND, - ), - receipt_directory_service_lifecycle_row( - "service-lifecycle", - latest_single_lifecycle_receipt, - WRAPPER_SERVICE_EXECUTION_AUDIT_COMMAND, - ), - receipt_directory_service_lifecycle_row( - "cluster-service-execution-runbook", - latest_cluster_execution_runbook_receipt, - WRAPPER_CLUSTER_SERVICE_EXECUTION_RUNBOOK_COMMAND, - ), - receipt_directory_service_lifecycle_row( - "service-execution-runbook", - latest_service_execution_runbook_receipt, - WRAPPER_SERVICE_EXECUTION_RUNBOOK_COMMAND, - ), - ] -} - -fn receipt_directory_service_lifecycle_row( - family: &str, - receipt: Option<&EpiphanyCultMeshDaemonServiceLifecycleReceiptEntry>, - follow_up_command: &str, -) -> ReceiptDirectoryRow { - let artifact_ref = receipt - .map(|receipt| receipt.operator_artifact_ref.clone()) - .unwrap_or_else(|| "none".to_string()); - let artifact_status = operator_artifact_status(&artifact_ref).to_string(); - let artifact_sha256 = operator_artifact_sha256(&artifact_ref, &artifact_status); - let service_id = receipt - .map(|receipt| receipt.service_id.clone()) - .unwrap_or_else(|| "none".to_string()); - let route = receipt - .map(|receipt| format!("{}::{}", receipt.service_id, receipt.action)) - .unwrap_or_else(|| "none".to_string()); - let service_route = if receipt.is_some() { - route.clone() - } else { - "none".to_string() - }; - let follow_up_command = receipt - .map(|receipt| service_execution_check_follow_up_command(&receipt.action)) - .filter(|command| *command != "none") - .unwrap_or(follow_up_command) - .to_string(); - ReceiptDirectoryRow { - family: family.to_string(), - owner: "daemon-supervisor".to_string(), - document_kind: "epiphany.cultmesh.daemon_service_lifecycle_receipt.v0".to_string(), - latest_id: receipt - .map(|receipt| receipt.receipt_id.clone()) - .unwrap_or_else(|| "missing".to_string()), - status: receipt - .map(|receipt| receipt.status.clone()) - .unwrap_or_else(|| "missing".to_string()), - route, - service_id, - service_route, - follow_up_command, - artifact_ref, - artifact_status, - artifact_sha256, - present: receipt.is_some(), - private_state_exposed: receipt - .map(|receipt| receipt.private_state_exposed) - .unwrap_or(false), - } -} - -fn latest_service_lifecycle_receipt<'a>( - receipts: &'a [EpiphanyCultMeshDaemonServiceLifecycleReceiptEntry], - cluster: bool, -) -> Option<&'a EpiphanyCultMeshDaemonServiceLifecycleReceiptEntry> { - receipts - .iter() - .filter(|receipt| is_cluster_service_lifecycle_receipt(receipt) == cluster) - .max_by(|left, right| { - service_lifecycle_receipt_sort_key(left).cmp(&service_lifecycle_receipt_sort_key(right)) - }) -} - -fn service_lifecycle_receipt_for_directory<'a>( - receipts: &'a [EpiphanyCultMeshDaemonServiceLifecycleReceiptEntry], - cluster: bool, -) -> Option<&'a EpiphanyCultMeshDaemonServiceLifecycleReceiptEntry> { - latest_service_lifecycle_receipt(receipts, cluster) -} - -fn latest_service_lifecycle_receipt_for_action<'a>( - receipts: &'a [EpiphanyCultMeshDaemonServiceLifecycleReceiptEntry], - cluster: bool, - action: &str, -) -> Option<&'a EpiphanyCultMeshDaemonServiceLifecycleReceiptEntry> { - receipts - .iter() - .filter(|receipt| is_cluster_service_lifecycle_receipt(receipt) == cluster) - .filter(|receipt| receipt.action == action) - .max_by(|left, right| { - service_lifecycle_receipt_sort_key(left).cmp(&service_lifecycle_receipt_sort_key(right)) - }) -} - -fn is_cluster_service_lifecycle_receipt( - receipt: &EpiphanyCultMeshDaemonServiceLifecycleReceiptEntry, -) -> bool { - if receipt.service_id == "epiphany-cluster-daemon-services" - || receipt.action.starts_with("cluster-windows-service-") - { - true - } else { - false - } -} - -fn service_lifecycle_receipt_sort_key( - receipt: &EpiphanyCultMeshDaemonServiceLifecycleReceiptEntry, -) -> (&str, &str) { - ( - receipt - .completed_at_utc - .as_deref() - .unwrap_or(receipt.started_at_utc.as_str()), - receipt.receipt_id.as_str(), - ) -} - -fn compact_tui_list(values: &[String]) -> String { - if values.is_empty() { - "none".to_string() - } else { - values.join(",") - } -} - -fn compact_tui_text(value: &str, max_chars: usize) -> String { - let collapsed = value - .split_whitespace() - .collect::>() - .join(" ") - .replace('|', "/"); - if collapsed.chars().count() <= max_chars { - return collapsed; - } - collapsed - .chars() - .take(max_chars.saturating_sub(3)) - .collect::() - + "..." -} - -fn reject_daemon_poke_response_fields(args: &Args) -> Result<()> { - if args.receipt_id.is_some() - || args.receipt_status.is_some() - || args.resulting_status.is_some() - || args.artifact_ref.is_some() - { - anyhow::bail!( - "daemon poke commands accept requester intent fields only; the lifecycle owner owns receipt id/status, resulting daemon status, and result artifacts" - ); - } - Ok(()) -} - -fn write_daemon_poke_intent( - args: &Args, - brake: Option<&EpiphanyCultMeshSwarmBrakeEntry>, - topology: &[EpiphanyCultMeshClusterTopologyEntry], - daemon_status: &EpiphanyCultMeshDaemonStatusEntry, - intent_id: String, - reason: String, -) -> Result { - assert_swarm_brake_allows_surface_entry( - brake, - "daemon.lifecycle_poke", - &daemon_status.cluster_id, - &daemon_status.daemon_id, - )?; - let poke_intent = epiphany_cultmesh_daemon_poke_intent_from_status( - intent_id, - "epiphany.Self", - daemon_status, - reason, - ); - let written_intent = write_epiphany_cultmesh_daemon_poke_intent( - &args.store, - args.runtime_id.clone(), - poke_intent.clone(), - )?; - let cluster = cluster_topology_entry_for_id(topology, &written_intent.target_cluster_id)?; - Ok(json!({ - "targetDaemonId": written_intent.target_daemon_id, - "targetClusterId": written_intent.target_cluster_id, - "targetDisplayName": cluster.display_name.clone(), - "declaredBodyDomain": cluster.body_domain.clone(), - "declaredPrivateVerseRoute": cluster.private_verse_id.clone(), - "declaredEveRoute": cluster.eve_surface_id.clone(), - "observedStatus": written_intent.observed_status, - "intentId": written_intent.intent_id, - "receiptId": null, - "receiptStatus": null, - "resultingStatus": null, - "responseOwner": "Idunn/target-daemon", - "privateStateRequested": written_intent.private_state_requested, - "privateStateExposed": false, - })) -} - -fn write_poke_intents_for_non_ready_daemons( - args: &Args, - context: &EpiphanyLocalVerseContext, -) -> Result> { - let statuses = context - .daemon_statuses - .iter() - .filter(|status| status.status != "ready") - .collect::>(); - let issued_at = Utc::now().timestamp_millis(); - let mut pokes = Vec::new(); - for daemon_status in statuses { - let safe_daemon_id = sanitize_id(&daemon_status.daemon_id); - let intent_id = format!("daemon-poke-intent-{safe_daemon_id}-{issued_at}"); - let reason = args.reason.clone().unwrap_or_else(|| { - format!( - "Operator requested typed lifecycle poke sweep after observing {} status {}.", - daemon_status.daemon_id, daemon_status.status - ) - }); - pokes.push(write_daemon_poke_intent( - args, - context.swarm_brake.as_ref(), - &context.cluster_topology, - daemon_status, - intent_id, - reason, - )?); - } - Ok(pokes) -} - -fn run_invoke_tool_command(args: &Args) -> Result<()> { - if args.receipt_id.is_some() - || args.receipt_status.is_some() - || args.result_ref.is_some() - || args.receipt_summary.is_some() - { - anyhow::bail!( - "invoke-tool accepts requester intent fields only; the host daemon owns receipt status, result references, and result summaries" - ); - } - - require_query_bootstrap(args)?; - - let capability_id = args - .capability_id - .as_deref() - .context("invoke-tool requires --capability-id from the tool directory")?; - let tool_directory = - load_epiphany_cultmesh_daemon_tool_directory(&args.store, args.runtime_id.clone())?; - let (host_cluster_from_directory, daemon_status, capability) = tool_directory - .iter() - .find(|(_cluster, _status, capability)| capability.capability_id == capability_id) - .with_context(|| { - format!( - "invoke-tool unavailable: no provenance-bearing daemon tool capability {capability_id:?}" - ) - })?; - let swarm_brake = load_epiphany_cultmesh_swarm_brake(&args.store, args.runtime_id.clone())?; - assert_swarm_brake_allows_surface_entry( - swarm_brake.as_ref(), - "daemon.tool_invocation", - &capability.host_cluster_id, - &capability.host_daemon_id, - )?; - let topology = load_epiphany_cultmesh_cluster_topology(&args.store, args.runtime_id.clone())?; - let requesting_agent_id = args - .requesting_agent_id - .clone() - .or_else(|| args.source_agent_id.clone()) - .unwrap_or_else(|| "epiphany.Self".to_string()); - let requesting_cluster_id = args - .source_cluster_id - .clone() - .unwrap_or_else(|| "epiphany.cluster.self".to_string()); - let intent_id = args - .intent_id - .clone() - .unwrap_or_else(|| format!("daemon-tool-intent-{}", capability.tool_name)); - let payload_ref = args - .invocation_ref - .clone() - .unwrap_or_else(|| format!("cultmesh://epiphany-local/tool-invocation/{intent_id}")); - let payload_summary = args.reason.clone().unwrap_or_else(|| { - format!( - "{requesting_agent_id} requests {} through the local CultMesh daemon tool directory.", - capability.tool_name - ) - }); - let intent = epiphany_cultmesh_daemon_tool_invocation_intent_from_capability( - intent_id, - requesting_agent_id.clone(), - requesting_cluster_id, - capability, - payload_ref, - payload_summary, - ); - let written_intent = write_epiphany_cultmesh_daemon_tool_invocation_intent( - &args.store, - args.runtime_id.clone(), - intent.clone(), - )?; - let latest_intent = load_latest_epiphany_cultmesh_daemon_tool_invocation_intent( - &args.store, - args.runtime_id.clone(), - )? - .context("local Verse lost latest daemon tool invocation intent after write")?; - if latest_intent.intent_id != written_intent.intent_id { - anyhow::bail!( - "local Verse latest daemon tool invocation does not match the just-written intent" - ); - } - let requesting_cluster = - cluster_topology_entry_for_id(&topology, &written_intent.requesting_cluster_id)?; - let host_cluster = topology - .iter() - .find(|cluster| cluster.cluster_id == written_intent.host_cluster_id) - .unwrap_or(host_cluster_from_directory); - let output = json!({ - "status": "pending-provider", - "store": args.store, - "runtimeId": args.runtime_id, - "capabilityId": written_intent.capability_id, - "requestingAgentId": written_intent.requesting_agent_id, - "requestingClusterId": written_intent.requesting_cluster_id, - "requestingDisplayName": requesting_cluster.display_name.clone(), - "requestingDeclaredBodyDomain": requesting_cluster.body_domain.clone(), - "requestingDeclaredPrivateVerseRoute": requesting_cluster.private_verse_id.clone(), - "requestingDeclaredEveRoute": requesting_cluster.eve_surface_id.clone(), - "hostClusterId": written_intent.host_cluster_id, - "hostDaemonId": written_intent.host_daemon_id, - "hostDisplayName": host_cluster.display_name.clone(), - "hostDeclaredBodyDomain": host_cluster.body_domain.clone(), - "hostDeclaredPrivateVerseRoute": host_cluster.private_verse_id.clone(), - "hostDeclaredEveRoute": host_cluster.eve_surface_id.clone(), - "toolName": written_intent.tool_name, - "operation": written_intent.operation, - "intentId": written_intent.intent_id, - "receiptId": null, - "receiptStatus": null, - "receiptContractType": capability.receipt_contract_type, - "resultRef": null, - "resultSummary": null, - "responseOwner": written_intent.host_daemon_id, - "observedHostStatus": daemon_status.status, - "availableToAllAgents": capability.available_to_all_agents, - "requiresReceipt": written_intent.requires_receipt, - "authorityGate": written_intent.authority_gate, - "privateStateRequested": written_intent.private_state_requested, - "privateStateExposed": false, - }); - println!("{}", serde_json::to_string_pretty(&output)?); - Ok(()) -} - -fn poke_result_tui_row(row: &serde_json::Value) -> String { - let target_display_name = row["targetDisplayName"].as_str().unwrap_or("unknown"); - let target_daemon_id = row["targetDaemonId"].as_str().unwrap_or("unknown"); - let observed_status = row["observedStatus"].as_str().unwrap_or("unknown"); - let declared_body_domain = row["declaredBodyDomain"].as_str().unwrap_or("unknown"); - let declared_private_verse_route = row["declaredPrivateVerseRoute"] - .as_str() - .unwrap_or("unknown"); - let declared_eve_route = row["declaredEveRoute"].as_str().unwrap_or("unknown"); - let intent_id = row["intentId"].as_str().unwrap_or("unknown"); - let receipt_id = row["receiptId"].as_str().unwrap_or("unknown"); - let receipt_status = row["receiptStatus"].as_str().unwrap_or("unknown"); - let resulting_status = row["resultingStatus"].as_str().unwrap_or("unknown"); - let private_state_exposed = row["privateStateExposed"].as_bool().unwrap_or(false); - format!( - "POKE | {target_display_name} | {target_daemon_id} | observed={observed_status} | declaredBodyDomain={declared_body_domain} | declaredPrivateVerseRoute={declared_private_verse_route} | declaredEveRoute={declared_eve_route} | intent={intent_id} | receipt={receipt_id} | receiptStatus={receipt_status} | result={resulting_status} | private={private_state_exposed}" - ) -} - -fn sanitize_id(value: &str) -> String { - value - .chars() - .map(|ch| if ch.is_ascii_alphanumeric() { ch } else { '-' }) - .collect() -} - -fn assert_swarm_brake_allows_surface_entry( - brake: Option<&EpiphanyCultMeshSwarmBrakeEntry>, - surface: &str, - cluster_id: &str, - daemon_id: &str, -) -> Result<()> { - let Some(brake) = brake else { - return Ok(()); - }; - if brake.status != "engaged" { - return Ok(()); - } - let scope_matches = matches!(brake.scope.as_str(), "swarm" | "all") - || brake - .affected_clusters - .iter() - .any(|affected| affected == cluster_id || affected == daemon_id || affected == "swarm"); - let surface_matches = brake.protected_surfaces.is_empty() - || brake - .protected_surfaces - .iter() - .any(|protected| protected == surface || protected == "daemon.*" || protected == "*"); - if scope_matches && surface_matches { - anyhow::bail!( - "local Verse swarm brake engaged; refusing {surface} for daemon {daemon_id}; scope={}; protected={}; affected={}; reason={}", - brake.scope, - brake.protected_surfaces.join(","), - brake.affected_clusters.join(","), - brake.reason - ); - } - Ok(()) -} - -fn cluster_topology_entry_for_id<'a>( - topology: &'a [EpiphanyCultMeshClusterTopologyEntry], - cluster_id: &str, -) -> Result<&'a EpiphanyCultMeshClusterTopologyEntry> { - topology - .iter() - .find(|cluster| cluster.cluster_id == cluster_id) - .with_context(|| format!("local Verse has no cluster topology row for {cluster_id:?}")) -} - -struct Args { - command: String, - store: PathBuf, - runtime_id: String, - smoke_default_store: bool, - agent_store: Option, - daemon_id: Option, - capability_id: Option, - requesting_agent_id: Option, - brake_id: Option, - brake_status: Option, - reason: Option, - intent_id: Option, - receipt_id: Option, - receipt_status: Option, - resulting_status: Option, - artifact_ref: Option, - invocation_ref: Option, - result_ref: Option, - receipt_summary: Option, - target_repository: Option, - target_branch: Option, - change_summary: Option, - justification: Option, - changed_paths: Option>, - verification_receipts: Option>, - review_receipts: Option>, - authorship_agent_ids: Option>, - credit_subjects: Option>, - credit_receipt_ids: Option>, - ledger_entry_id: Option, - publication_url: Option, - public_proof_id: Option, - source_cluster_id: Option, - source_agent_id: Option, - body_domain: Option, - public_room_id: Option, -} - -impl Args { - fn parse() -> Result { - let mut values = env::args().skip(1); - let command = values.next().unwrap_or_else(|| "query".to_string()); - let mut store = PathBuf::from(".epiphany-run/cultmesh/local-verse.ccmp"); - let mut runtime_id = "epiphany-local".to_string(); - let mut store_overridden = false; - let mut runtime_id_overridden = false; - let mut agent_store = None; - let mut daemon_id = None; - let mut capability_id = None; - let mut requesting_agent_id = None; - let mut brake_id = None; - let mut brake_status = None; - let mut reason = None; - let mut intent_id = None; - let mut receipt_id = None; - let mut receipt_status = None; - let mut resulting_status = None; - let mut artifact_ref = None; - let mut invocation_ref = None; - let mut result_ref = None; - let mut receipt_summary = None; - let mut target_repository = None; - let mut target_branch = None; - let mut change_summary = None; - let mut justification = None; - let mut changed_paths = Vec::new(); - let mut verification_receipts = Vec::new(); - let mut review_receipts = Vec::new(); - let mut authorship_agent_ids = Vec::new(); - let mut credit_subjects = Vec::new(); - let mut credit_receipt_ids = Vec::new(); - let mut ledger_entry_id = None; - let mut publication_url = None; - let mut public_proof_id = None; - let mut source_cluster_id = None; - let mut source_agent_id = None; - let mut body_domain = None; - let mut public_room_id = None; - - while let Some(arg) = values.next() { - match arg.as_str() { - "--store" => { - store = PathBuf::from(values.next().context("missing --store value")?); - store_overridden = true; - } - "--runtime-id" => { - runtime_id = values.next().context("missing --runtime-id value")?; - runtime_id_overridden = true; - } - "--agent-store" => { - agent_store = Some(PathBuf::from( - values.next().context("missing --agent-store value")?, - )); - } - "--daemon-id" => { - daemon_id = Some(values.next().context("missing --daemon-id value")?); - } - "--capability-id" => { - capability_id = Some(values.next().context("missing --capability-id value")?); - } - "--requesting-agent-id" => { - requesting_agent_id = Some( - values - .next() - .context("missing --requesting-agent-id value")?, - ); - } - "--brake-id" => { - brake_id = Some(values.next().context("missing --brake-id value")?); - } - "--brake-status" => { - brake_status = Some(values.next().context("missing --brake-status value")?); - } - "--reason" => { - reason = Some(values.next().context("missing --reason value")?); - } - "--intent-id" => { - intent_id = Some(values.next().context("missing --intent-id value")?); - } - "--receipt-id" => { - receipt_id = Some(values.next().context("missing --receipt-id value")?); - } - "--receipt-status" => { - receipt_status = Some(values.next().context("missing --receipt-status value")?); - } - "--resulting-status" => { - resulting_status = - Some(values.next().context("missing --resulting-status value")?); - } - "--artifact-ref" | "--accepted-artifact-receipt-id" => { - artifact_ref = Some(values.next().context("missing --artifact-ref value")?); - } - "--invocation-ref" | "--payload-ref" => { - invocation_ref = Some(values.next().context("missing --invocation-ref value")?); - } - "--result-ref" => { - result_ref = Some(values.next().context("missing --result-ref value")?); - } - "--receipt-summary" | "--metrics-summary" => { - receipt_summary = - Some(values.next().context("missing --receipt-summary value")?); - } - "--target-repository" => { - target_repository = - Some(values.next().context("missing --target-repository value")?); - } - "--target-branch" => { - target_branch = Some(values.next().context("missing --target-branch value")?); - } - "--change-summary" => { - change_summary = Some(values.next().context("missing --change-summary value")?); - } - "--justification" => { - justification = Some(values.next().context("missing --justification value")?); - } - "--changed-path" | "--changed-paths" => { - extend_list( - &mut changed_paths, - values.next().context("missing --changed-path value")?, - ); - } - "--verification-receipt" - | "--verification-receipts" - | "--model-spend-receipt" - | "--model-spend-receipts" => { - extend_list( - &mut verification_receipts, - values - .next() - .context("missing --verification-receipt value")?, - ); - } - "--review-receipt" - | "--review-receipts" - | "--review-load-receipt" - | "--review-load-receipts" => { - extend_list( - &mut review_receipts, - values.next().context("missing --review-receipt value")?, - ); - } - "--author-agent" | "--author-agents" => { - extend_list( - &mut authorship_agent_ids, - values.next().context("missing --author-agent value")?, - ); - } - "--credit-subject" | "--credit-subjects" => { - extend_list( - &mut credit_subjects, - values.next().context("missing --credit-subject value")?, - ); - } - "--credit-receipt" | "--credit-receipts" => { - extend_list( - &mut credit_receipt_ids, - values.next().context("missing --credit-receipt value")?, - ); - } - "--ledger-entry-id" => { - ledger_entry_id = - Some(values.next().context("missing --ledger-entry-id value")?); - } - "--publication-url" => { - publication_url = - Some(values.next().context("missing --publication-url value")?); - } - "--public-proof-id" | "--public-proof-ref" => { - public_proof_id = - Some(values.next().context("missing --public-proof-id value")?); - } - "--source-cluster-id" => { - source_cluster_id = - Some(values.next().context("missing --source-cluster-id value")?); - } - "--source-agent-id" => { - source_agent_id = - Some(values.next().context("missing --source-agent-id value")?); - } - "--body-domain" => { - body_domain = Some(values.next().context("missing --body-domain value")?); - } - "--public-room-id" => { - public_room_id = Some(values.next().context("missing --public-room-id value")?); - } - _ => anyhow::bail!("unknown argument {arg:?}"), - } - } - - let smoke_default_store = command == "smoke" && !store_overridden && !runtime_id_overridden; - if smoke_default_store { - store = PathBuf::from(".epiphany-smoke/verse-query-default/local-verse.ccmp"); - if !runtime_id_overridden { - runtime_id = "verse-query-default-smoke".to_string(); - } - } - - Ok(Self { - command, - store, - runtime_id, - smoke_default_store, - agent_store, - daemon_id, - capability_id, - requesting_agent_id, - brake_id, - brake_status, - reason, - intent_id, - receipt_id, - receipt_status, - resulting_status, - artifact_ref, - invocation_ref, - result_ref, - receipt_summary, - target_repository, - target_branch, - change_summary, - justification, - changed_paths: some_if_not_empty(changed_paths), - verification_receipts: some_if_not_empty(verification_receipts), - review_receipts: some_if_not_empty(review_receipts), - authorship_agent_ids: some_if_not_empty(authorship_agent_ids), - credit_subjects: some_if_not_empty(credit_subjects), - credit_receipt_ids: some_if_not_empty(credit_receipt_ids), - ledger_entry_id, - publication_url, - public_proof_id, - source_cluster_id, - source_agent_id, - body_domain, - public_room_id, - }) - } -} - -fn extend_list(target: &mut Vec, value: String) { - target.extend( - value - .split(',') - .map(str::trim) - .filter(|item| !item.is_empty()) - .map(ToString::to_string), - ); -} - -fn some_if_not_empty(values: Vec) -> Option> { - if values.is_empty() { - None - } else { - Some(values) - } -} - -fn required_list(values: &Option>, message: &str) -> Result> { - values - .as_ref() - .filter(|items| !items.is_empty()) - .cloned() - .context(message.to_string()) -} - -#[cfg(test)] -mod swarm_brake_command_tests { - use super::*; - - fn bytes(path: &Path) -> Result> { - Ok(fs::read(path)?) - } - - #[test] - fn status_is_byte_identical_and_reports_legacy_engaged_truth() -> Result<()> { - let temp = tempfile::tempdir()?; - let store = temp.path().join("verse.cc"); - let mut legacy = default_epiphany_cultmesh_swarm_brake("2026-07-19T00:00:00Z"); - legacy.brake_id = "epiphany-local/swarm-brake/operator".into(); - legacy.operator_agent_id = "epiphany-operator".into(); - legacy.status = "engaged".into(); - legacy.reason = "live legacy sleep mode".into(); - write_epiphany_cultmesh_swarm_brake(&store, "epiphany-yggdrasil", legacy)?; - let before = bytes(&store)?; - let observed = load_epiphany_cultmesh_swarm_brake(&store, "epiphany-yggdrasil")?; - let projection = - swarm_brake_status_projection(&store, "epiphany-yggdrasil", observed.as_ref()); - assert_eq!(projection["brakeStatus"], "engaged"); - assert_eq!(projection["canonicalAuthority"], false); - assert_eq!(bytes(&store)?, before); - Ok(()) - } - - #[test] - fn missing_status_refuses_without_write() -> Result<()> { - let temp = tempfile::tempdir()?; - let store = temp.path().join("verse.cc"); - let brake = engage_epiphany_cultmesh_swarm_brake( - &store, - "epiphany-yggdrasil", - "sleep", - "idunn", - "2026-07-19T00:00:00Z", - false, - )?; - assert_eq!(brake.status, "engaged"); - let before = bytes(&store)?; - let error = apply_explicit_swarm_brake_set( - &store, - "epiphany-yggdrasil", - None, - Some(EPIPHANY_CANONICAL_SWARM_BRAKE_ID), - Some("operator"), - Some("must not default"), - "2026-07-19T00:01:00Z", - false, - ) - .expect_err("missing status must fail closed"); - assert!(error.to_string().contains("explicit --brake-status")); - assert_eq!(bytes(&store)?, before); - Ok(()) - } - - #[test] - fn explicit_engage_status_and_exact_owned_release_share_one_truth() -> Result<()> { - let temp = tempfile::tempdir()?; - let store = temp.path().join("verse.cc"); - let engaged = apply_explicit_swarm_brake_set( - &store, - "epiphany-yggdrasil", - Some("engaged"), - Some(EPIPHANY_CANONICAL_SWARM_BRAKE_ID), - Some("Idunn"), - Some("deployment sleep"), - "2026-07-19T00:00:00Z", - true, - )?; - assert_eq!( - engaged.operator_agent_id, - EPIPHANY_CANONICAL_SWARM_BRAKE_OWNER - ); - let observed = load_epiphany_cultmesh_swarm_brake(&store, "epiphany-yggdrasil")? - .expect("canonical brake"); - assert_eq!(observed, engaged); - let projection = - swarm_brake_status_projection(&store, "epiphany-yggdrasil", Some(&observed)); - assert_eq!(projection["brakeStatus"], "engaged"); - assert_eq!(projection["canonicalAuthority"], true); - - let released = apply_explicit_swarm_brake_set( - &store, - "epiphany-yggdrasil", - Some("released"), - Some(EPIPHANY_CANONICAL_SWARM_BRAKE_ID), - Some("discord-owner"), - Some("explicit wake"), - "2026-07-19T00:01:00Z", - false, - )?; - assert_eq!(released.status, "released"); - assert_eq!( - load_epiphany_cultmesh_swarm_brake(&store, "epiphany-yggdrasil")?, - Some(released) - ); - Ok(()) - } - - #[test] - fn ordinary_actor_can_engage_an_existing_canonical_released_brake() -> Result<()> { - let temp = tempfile::tempdir()?; - let store = temp.path().join("verse.cc"); - let released = default_epiphany_cultmesh_swarm_brake("2026-08-05T00:00:00Z"); - write_epiphany_cultmesh_swarm_brake(&store, "epiphany-starfire", released)?; - - let engaged = apply_explicit_swarm_brake_set( - &store, - "epiphany-starfire", - Some("engaged"), - Some(EPIPHANY_CANONICAL_SWARM_BRAKE_ID), - Some("codex-root"), - Some("cold-start verification"), - "2026-08-05T00:01:00Z", - false, - )?; - - assert_eq!(engaged.status, "engaged"); - assert_eq!( - engaged.operator_agent_id, - EPIPHANY_CANONICAL_SWARM_BRAKE_OWNER - ); - Ok(()) - } - - #[test] - fn release_cannot_touch_foreign_deployment_brake() -> Result<()> { - let temp = tempfile::tempdir()?; - let store = temp.path().join("verse.cc"); - let mut foreign = default_epiphany_cultmesh_swarm_brake("2026-07-19T00:00:00Z"); - foreign.brake_id = "epiphany-yggdrasil/deployment-brake".into(); - foreign.operator_agent_id = "legacy-idunn".into(); - foreign.status = "engaged".into(); - foreign.reason = "legacy deployment brake".into(); - write_epiphany_cultmesh_swarm_brake(&store, "epiphany-yggdrasil", foreign)?; - let before = bytes(&store)?; - let adoption_error = apply_explicit_swarm_brake_set( - &store, - "epiphany-yggdrasil", - Some("engaged"), - Some(EPIPHANY_CANONICAL_SWARM_BRAKE_ID), - Some("operator"), - Some("must not adopt foreign authority"), - "2026-07-19T00:00:30Z", - true, - ) - .expect_err("non-Idunn actor must not adopt a legacy brake"); - assert!(adoption_error.to_string().contains("only Idunn")); - assert_eq!(bytes(&store)?, before); - let error = apply_explicit_swarm_brake_set( - &store, - "epiphany-yggdrasil", - Some("released"), - Some(EPIPHANY_CANONICAL_SWARM_BRAKE_ID), - Some("operator"), - Some("must refuse"), - "2026-07-19T00:01:00Z", - false, - ) - .expect_err("foreign deployment brake must not be released"); - assert!(error.to_string().contains("foreign swarm brake")); - assert_eq!(bytes(&store)?, before); - Ok(()) - } -} - -#[cfg(test)] -mod lifecycle_projection_tests { - use super::*; - - #[test] - fn single_daemon_poke_uses_narrow_status_brake_topology_and_intent_reads() { - let source = include_str!("epiphany-verse-query.rs"); - let start = source - .find("\"poke-daemon\" | \"daemon-poke\" => {") - .unwrap(); - let tail = &source[start..]; - let end = tail - .find("\n \"poke-down-daemons\"") - .expect("next command arm"); - let body = &tail[..end]; - assert!(body.contains("load_epiphany_cultmesh_daemon_status")); - assert!(body.contains("load_epiphany_cultmesh_swarm_brake")); - assert!(body.contains("load_epiphany_cultmesh_cluster_topology")); - assert!(body.contains("load_epiphany_cultmesh_daemon_poke_intent")); - assert!(!body.contains("query_epiphany_local_verse_context")); - } - - #[test] - fn stale_ready_heartbeat_cannot_resolve_a_later_restart_attempt() { - assert!(!provider_heartbeat_causally_follows_attempt( - "2026-07-15T10:00:00Z", - "2026-07-15T10:00:00Z", - "2026-07-15T10:01:00Z", - )); - } - - #[test] - fn newer_provider_heartbeat_resolves_a_completed_restart_attempt() { - assert!(provider_heartbeat_causally_follows_attempt( - "2026-07-15T10:02:00Z", - "2026-07-15T10:00:00Z", - "2026-07-15T10:01:00Z", - )); - } - - #[test] - fn deployment_brake_allows_lifecycle_sight_poke_but_blocks_cognitive_and_action_surfaces() { - let brake = EpiphanyCultMeshSwarmBrakeEntry { - schema_version: "epiphany.cultmesh.swarm_brake.v0".into(), - brake_id: "sleep-mode".into(), - status: "engaged".into(), - scope: "swarm".into(), - reason: "deployment sleep mode".into(), - operator_agent_id: "operator".into(), - affected_clusters: vec!["swarm".into()], - protected_surfaces: vec![ - "heartbeat.scheduler".into(), - "coordinator.run".into(), - "persona.public_speech".into(), - "daemon.tool_invocation".into(), - ], - created_at_utc: "2026-07-18T00:00:00Z".into(), - expires_at_utc: None, - notes: Vec::new(), - runtime_id: "epiphany-yggdrasil".into(), - private_state_exposed: false, - }; - assert!( - assert_swarm_brake_allows_surface_entry( - Some(&brake), - "daemon.lifecycle_poke", - "cluster", - "daemon", - ) - .is_ok() - ); - for protected in [ - "heartbeat.scheduler", - "coordinator.run", - "persona.public_speech", - "daemon.tool_invocation", - ] { - assert!( - assert_swarm_brake_allows_surface_entry( - Some(&brake), - protected, - "cluster", - "daemon", - ) - .is_err() - ); - } - } - - fn managed_service_row( - service_id: &str, - enabled: bool, - process_observation: &str, - lifecycle_status: &str, - ) -> ManagedServiceSightRow { - ManagedServiceSightRow { - service_id: service_id.to_string(), - owner_daemon_id: "Idunn".to_string(), - enabled, - restart_mode: "on-failure".to_string(), - process_observation: process_observation.to_string(), - process_id: Some(4242), - process_identity_verified: process_observation == "alive", - lifecycle_receipt_id: Some("stale-receipt".to_string()), - lifecycle_status: Some(lifecycle_status.to_string()), - last_pulse_status: Some("ready".to_string()), - last_pulse_iteration: Some(7), - last_pulse_artifact: Some("sealed-artifact".to_string()), - private_state_exposed: false, - } - } - - #[test] - fn managed_service_native_dead_observation_beats_stale_lifecycle_readiness() { - let report = managed_service_sight_report_from_rows(vec![managed_service_row( - "svc-dead", true, "dead", "ready", - )]); - assert_eq!(report.status, "attention"); - assert_eq!(report.attention_count, 1); - assert!(managed_service_overview_requires_attention(&report)); - assert!(report.tui_rows[0].starts_with("ATTN |")); - } - - #[test] - fn disabled_managed_service_does_not_force_overview_attention() { - let report = managed_service_sight_report_from_rows(vec![managed_service_row( - "svc-disabled", - false, - "missing", - "ready", - )]); - assert_eq!(report.status, "ready"); - assert_eq!(report.attention_count, 0); - assert!(!managed_service_overview_requires_attention(&report)); - assert!(report.tui_rows[0].starts_with("DISABLED |")); - } - - #[test] - fn unverified_process_identity_cannot_satisfy_managed_service_sight() { - let mut row = managed_service_row("svc-reused-pid", true, "identity-unverified", "ready"); - row.process_identity_verified = false; - let report = managed_service_sight_report_from_rows(vec![row]); - assert_eq!(report.status, "attention"); - assert_eq!(report.alive_count, 0); - assert!(managed_service_overview_requires_attention(&report)); - } - - #[test] - fn specialist_and_overview_share_compact_sealed_sight_rows() -> Result<()> { - let report = managed_service_sight_report_from_rows(vec![managed_service_row( - "svc-shared", - true, - "alive", - "ready", - )]); - let specialist_projection = serde_json::to_value(&report)?; - let overview_rows = serde_json::to_value(&report.rows)?; - assert_eq!(specialist_projection["rows"], overview_rows); - assert_eq!(specialist_projection["privateStateExposed"], false); - assert_eq!( - specialist_projection["rows"][0]["privateStateExposed"], - false - ); - assert!(specialist_projection.get("privatePayload").is_none()); - assert!( - specialist_projection["rows"][0] - .get("privatePayload") - .is_none() - ); - Ok(()) - } - - #[test] - fn swarm_managed_service_route_is_read_only_idunn_sight() { - assert_eq!( - DIRECT_MANAGED_SERVICE_SIGHT_COMMAND, - "epiphany-verse-query managed-services" - ); - for forbidden in ["reconcile", "launch", "restart", "write", "deploy"] { - assert!(!DIRECT_MANAGED_SERVICE_SIGHT_COMMAND.contains(forbidden)); - } - } - - #[test] - fn repo_work_stage_lens_preserves_per_family_ownership() { - let expected = [ - ( - "repo.tool_request", - "daemon-tool-request", - "target-host-daemon", - ), - ( - "repo.collaboration_policy", - "collaboration-policy-draft", - "Imagination", - ), - ( - "repo.publication_request", - "bifrost-publication-request", - "Bifrost", - ), - ("repo.sync_request", "bifrost-upstream-proof", "Bifrost"), - ( - "repo.maintainer_review_request", - "maintainer-review-request", - "Maintainer", - ), - ("repo.pr_request", "hands-pr-execution-request", "Hands"), - ( - "repo.artifact_acceptance_request", - "maintainer-artifact-acceptance", - "Maintainer", - ), - ( - "repo.readiness_review_request", - "maintainer-readiness-review", - "Maintainer", - ), - ( - "repo.doctrine_update_request", - "mind-doctrine-admission", - "Mind", - ), - ( - "repo.secret_policy_request", - "mind-secret-policy-admission", - "Mind", - ), - ( - "repo.dependency_policy_request", - "mind-dependency-policy-admission", - "Mind", - ), - ("repo.deployment_request", "idunn-deployment", "Idunn"), - ]; - for (family, stage, owner) in expected { - assert_eq!( - repo_work_stage_for_family(family), - (stage.to_string(), owner.to_string()) - ); - } - assert!(expected.iter().all(|(_, _, owner)| !owner.contains('/'))); - } - - #[test] - fn smoke_reset_is_confined_to_exact_canonical_quarantine() -> Result<()> { - let temp = tempfile::tempdir()?; - let target = temp - .path() - .join(".epiphany-smoke") - .join("verse-query-default"); - fs::create_dir_all(&target)?; - fs::write(target.join("sentinel"), "delete me")?; - reset_quarantined_smoke_store( - temp.path(), - Path::new(".epiphany-smoke/verse-query-default/local-verse.ccmp"), - )?; - assert!(!target.exists()); - - let outside = temp.path().join("outside"); - fs::create_dir_all(&outside)?; - fs::write(outside.join("sentinel"), "preserve me")?; - assert!( - reset_quarantined_smoke_store(temp.path(), Path::new("outside/local-verse.ccmp")) - .unwrap_err() - .to_string() - .contains("non-default") - ); - assert!(outside.join("sentinel").exists()); - Ok(()) - } - - fn directory_row(service_id: &str, route: &str) -> ReceiptDirectoryRow { - ReceiptDirectoryRow { - family: "service-execution-runbook".to_string(), - owner: "Idunn".to_string(), - document_kind: "test".to_string(), - latest_id: "receipt-1".to_string(), - status: "written".to_string(), - route: route.to_string(), - service_id: service_id.to_string(), - service_route: "none".to_string(), - follow_up_command: "test".to_string(), - artifact_ref: "artifact://1".to_string(), - artifact_status: "external-ref".to_string(), - artifact_sha256: "none".to_string(), - present: true, - private_state_exposed: false, - } - } - - #[test] - fn receipt_directory_never_recovers_service_identity_from_route_prose() { - let typed = directory_row("service-1", "counterfeit::route"); - assert_eq!( - receipt_directory_row_service_id(&typed).as_deref(), - Some("service-1") - ); - let missing = directory_row("none", "counterfeit::route"); - assert_eq!(receipt_directory_row_service_id(&missing), None); - } - - fn receipt( - id: &str, - status: &str, - completed_at: &str, - ) -> EpiphanyCultMeshDaemonServiceLifecycleReceiptEntry { - EpiphanyCultMeshDaemonServiceLifecycleReceiptEntry { - schema_version: EPIPHANY_CULTMESH_DAEMON_SERVICE_LIFECYCLE_RECEIPT_SCHEMA_VERSION - .to_string(), - receipt_id: id.to_string(), - service_id: "epiphany-daemon-supervisor-service".to_string(), - scheduler_id: "epiphany-daemon-supervisor".to_string(), - runtime_id: "projection-test".to_string(), - daemon_selector: "epiphany-daemon-self".to_string(), - action: "windows-service-reconcile".to_string(), - status: status.to_string(), - command: "powershell.exe".to_string(), - args: Vec::new(), - cwd: None, - process_id: None, - exit_code: Some(0), - started_at_utc: completed_at.to_string(), - completed_at_utc: Some(completed_at.to_string()), - operator_artifact_ref: format!("test://{id}"), - private_state_exposed: false, - notes: Vec::new(), - executable_sha256: String::new(), - schema_catalog_sha256: String::new(), - managed_policy_id: String::new(), - managed_policy_digest: String::new(), - provider_daemon_id: String::new(), - startup_correlation_id: String::new(), - process_creation_token: 0, - process_created_at_rfc3339: None, - process_executable_path: String::new(), - preflight_witness_id: String::new(), - required_document_types: Vec::new(), - schema_preflight_passed: false, - } - } - - #[test] - fn lifecycle_directory_uses_new_recovery_over_old_failure() { - let receipts = vec![ - receipt("old-failure", "query-failed", "2026-07-13T01:00:00Z"), - receipt("new-recovery", "in-sync", "2026-07-13T02:00:00Z"), - ]; - let selected = service_lifecycle_receipt_for_directory(&receipts, false).unwrap(); - assert_eq!(selected.receipt_id, "new-recovery"); - assert!(!service_lifecycle_status_needs_attention(&selected.status)); - } - - #[test] - fn lifecycle_directory_does_not_hide_new_failure_behind_old_recovery() { - let receipts = vec![ - receipt("old-recovery", "in-sync", "2026-07-13T01:00:00Z"), - receipt("new-failure", "query-failed", "2026-07-13T02:00:00Z"), - ]; - let selected = service_lifecycle_receipt_for_directory(&receipts, false).unwrap(); - assert_eq!(selected.receipt_id, "new-failure"); - assert!(service_lifecycle_status_needs_attention(&selected.status)); - } -} diff --git a/epiphany-core/src/bin/epiphany-void-memory.rs b/epiphany-core/src/bin/epiphany-void-memory.rs deleted file mode 100644 index da6a5002..00000000 --- a/epiphany-core/src/bin/epiphany-void-memory.rs +++ /dev/null @@ -1,620 +0,0 @@ -use anyhow::Context; -use anyhow::Result; -use anyhow::anyhow; -use postgres::NoTls; -use reqwest::blocking::Client; -use reqwest::blocking::ClientBuilder; -use reqwest::header::HeaderMap; -use reqwest::header::HeaderValue; -use serde::Deserialize; -use serde_json::Value; -use serde_json::json; -use std::env; -use std::fs; -use std::path::Path; -use std::path::PathBuf; -use std::time::Duration; - -const STATUS_SCHEMA_VERSION: &str = "epiphany.void_memory_status.v0"; -const SEARCH_SCHEMA_VERSION: &str = "epiphany.void_memory_search.v0"; -const CONTEXT_SCHEMA_VERSION: &str = "epiphany.void_message_context.v0"; -const DEFAULT_CONFIG: &str = "state/void-memory.toml"; -const MAX_LIMIT: usize = 20; - -fn main() -> Result<()> { - let mut args = env::args().skip(1); - let Some(command) = args.next() else { - return usage(); - }; - let mut config_path = PathBuf::from(DEFAULT_CONFIG); - let mut query: Option = None; - let mut message_id: Option = None; - let mut limit = 5_usize; - let mut before = 4_usize; - let mut after = 4_usize; - let mut guild_id: Option = None; - let mut channel_id: Option = None; - let mut author_id: Option = None; - let mut repo_name: Option = None; - let mut path_prefix: Option = None; - let mut language: Option = None; - - while let Some(arg) = args.next() { - match arg.as_str() { - "--config" => config_path = PathBuf::from(take(&mut args, "--config")?), - "--query" => query = Some(take(&mut args, "--query")?), - "--message-id" => message_id = Some(take(&mut args, "--message-id")?), - "--limit" => limit = take(&mut args, "--limit")?.parse()?, - "--before" => before = take(&mut args, "--before")?.parse()?, - "--after" => after = take(&mut args, "--after")?.parse()?, - "--guild-id" => guild_id = Some(take(&mut args, "--guild-id")?), - "--channel-id" => channel_id = Some(take(&mut args, "--channel-id")?), - "--author-id" => author_id = Some(take(&mut args, "--author-id")?), - "--repo-name" => repo_name = Some(take(&mut args, "--repo-name")?), - "--path-prefix" => path_prefix = Some(take(&mut args, "--path-prefix")?), - "--language" => language = Some(take(&mut args, "--language")?), - other => return Err(anyhow!("unknown argument {other:?}")), - } - } - - limit = limit.clamp(1, MAX_LIMIT); - before = before.min(MAX_LIMIT); - after = after.min(MAX_LIMIT); - let config = BridgeConfig::load(&config_path)?; - let output = match command.as_str() { - "status" => run_status(&config), - "search-history" => run_search( - &config, - query.context("missing --query")?, - limit, - SearchCorpus::History { - guild_id, - channel_id, - author_id, - }, - ), - "search-sources" => run_search( - &config, - query.context("missing --query")?, - limit, - SearchCorpus::Source { - repo_name, - path_prefix, - language, - }, - ), - "message-context" => run_message_context( - &config, - message_id.context("missing --message-id")?, - before, - after, - ), - "smoke" => run_smoke(), - _ => return usage(), - }?; - println!("{}", serde_json::to_string_pretty(&output)?); - Ok(()) -} - -#[derive(Clone, Debug)] -struct BridgeConfig { - database_dsn_env: Option, - database_dsn: String, - qdrant_url: String, - qdrant_api_key_env: Option, - qdrant_history_collection: String, - qdrant_source_collection: String, - qdrant_timeout_ms: u64, - ollama_base_url: String, - ollama_model: String, - ollama_timeout_ms: u64, - history_query_instruction: String, - source_query_instruction: String, - archive_path: PathBuf, -} - -impl BridgeConfig { - fn load(path: &Path) -> Result { - let raw = fs::read_to_string(path) - .with_context(|| format!("failed to read {}", path.display()))?; - Ok(Self { - database_dsn_env: toml_string(&raw, "database_dsn_env"), - database_dsn: toml_string(&raw, "database_dsn") - .unwrap_or_else(|| "postgres://voidbot:voidbot@localhost:5432/voidbot".to_string()), - qdrant_url: normalize_base_url( - &toml_string(&raw, "qdrant_url") - .unwrap_or_else(|| "http://127.0.0.1:6333".to_string()), - ), - qdrant_api_key_env: toml_string(&raw, "qdrant_api_key_env"), - qdrant_history_collection: toml_string(&raw, "qdrant_history_collection") - .unwrap_or_else(|| "voidbot_discord_history_chunks".to_string()), - qdrant_source_collection: toml_string(&raw, "qdrant_source_collection") - .unwrap_or_else(|| "voidbot_repository_source_chunks".to_string()), - qdrant_timeout_ms: toml_number(&raw, "qdrant_timeout_ms").unwrap_or(30000), - ollama_base_url: normalize_base_url( - &toml_string(&raw, "ollama_base_url") - .unwrap_or_else(|| "http://127.0.0.1:11434".to_string()), - ), - ollama_model: toml_string(&raw, "ollama_model") - .unwrap_or_else(|| "qwen3-embedding:0.6b".to_string()), - ollama_timeout_ms: toml_number(&raw, "ollama_timeout_ms").unwrap_or(30000), - history_query_instruction: toml_string(&raw, "history_query_instruction") - .unwrap_or_else(|| "Given a Discord history question, retrieve relevant messages and discussion snippets that answer it.".to_string()), - source_query_instruction: toml_string(&raw, "source_query_instruction") - .unwrap_or_else(|| "Given a source-tree, codebase, or lore question, retrieve relevant files, code snippets, and lore passages that answer it.".to_string()), - archive_path: PathBuf::from( - toml_string(&raw, "archive_path") - .unwrap_or_else(|| r"E:\Projects\VoidBot\.voidbot\rag\messages.json".to_string()), - ), - }) - } - - fn database_dsn(&self) -> String { - self.database_dsn_env - .as_ref() - .and_then(|name| env::var(name).ok()) - .filter(|value| !value.trim().is_empty()) - .unwrap_or_else(|| self.database_dsn.clone()) - } - - fn qdrant_api_key(&self) -> Option { - self.qdrant_api_key_env - .as_ref() - .and_then(|name| env::var(name).ok()) - .filter(|value| !value.trim().is_empty()) - } -} - -enum SearchCorpus { - History { - guild_id: Option, - channel_id: Option, - author_id: Option, - }, - Source { - repo_name: Option, - path_prefix: Option, - language: Option, - }, -} - -fn run_status(config: &BridgeConfig) -> Result { - let postgres = postgres_status(config); - let qdrant = qdrant_status(config); - let archive = archive_status(&config.archive_path); - Ok(json!({ - "schemaVersion": STATUS_SCHEMA_VERSION, - "ok": postgres["ok"].as_bool().unwrap_or(false) - || qdrant["history"]["ok"].as_bool().unwrap_or(false) - || archive["ok"].as_bool().unwrap_or(false), - "postgres": postgres, - "qdrant": qdrant, - "archive": archive, - "notes": [ - "Void currently keeps raw Discord archives file-backed under .voidbot while Qdrant owns semantic history/source vectors.", - "Postgres is checked for live Void state organs: jobs, audit events, interaction memory, identity profiles, and rate limits." - ], - })) -} - -fn run_search( - config: &BridgeConfig, - query: String, - limit: usize, - corpus: SearchCorpus, -) -> Result { - let (collection, instruction, filter, corpus_name) = match corpus { - SearchCorpus::History { - guild_id, - channel_id, - author_id, - } => ( - config.qdrant_history_collection.as_str(), - config.history_query_instruction.as_str(), - qdrant_filter([ - ("corpusKind", Some("discord_history".to_string())), - ("guildId", guild_id), - ("channelId", channel_id), - ("authorId", author_id), - ]), - "discord_history", - ), - SearchCorpus::Source { - repo_name, - path_prefix, - language, - } => ( - config.qdrant_source_collection.as_str(), - config.source_query_instruction.as_str(), - qdrant_filter([ - ("corpusKind", Some("repository_source".to_string())), - ("repoName", repo_name), - ("pathPrefixes", path_prefix.map(normalize_path_prefix)), - ("language", language), - ]), - "repository_source", - ), - }; - let vector = embed_query(config, instruction, &query)?; - let hits = query_qdrant(config, collection, &vector, limit, filter)?; - Ok(json!({ - "schemaVersion": SEARCH_SCHEMA_VERSION, - "ok": true, - "corpus": corpus_name, - "query": query, - "collection": collection, - "results": hits, - })) -} - -fn run_message_context( - config: &BridgeConfig, - message_id: String, - before: usize, - after: usize, -) -> Result { - let archive = load_archive(&config.archive_path)?; - let Some(anchor) = archive - .messages - .iter() - .find(|message| message.id == message_id) - else { - return Ok(json!({ - "schemaVersion": CONTEXT_SCHEMA_VERSION, - "ok": false, - "reason": "message-not-found", - "messageId": message_id, - })); - }; - let mut messages = archive - .messages - .iter() - .filter(|message| !message.deleted()) - .filter(|message| belongs_to_same_conversation(message, anchor)) - .cloned() - .collect::>(); - messages.sort_by(|left, right| left.timestamp.cmp(&right.timestamp)); - let anchor_index = messages - .iter() - .position(|message| message.id == message_id) - .context("anchor disappeared from context set")?; - let start = anchor_index.saturating_sub(before); - let end = (anchor_index + after + 1).min(messages.len()); - Ok(json!({ - "schemaVersion": CONTEXT_SCHEMA_VERSION, - "ok": true, - "messageId": message_id, - "archivePath": config.archive_path, - "messages": messages[start..end].iter().map(format_message).collect::>(), - })) -} - -fn postgres_status(config: &BridgeConfig) -> Value { - match postgres::Client::connect(&config.database_dsn(), NoTls) { - Ok(mut client) => { - let tables = [ - "jobs", - "audit_events", - "interaction_memory_events", - "interaction_identity_profiles", - "void_usage_rate_limit_state", - ]; - let counts = tables - .into_iter() - .map(|table| { - let count = client - .query_one(&format!("select count(*)::bigint from {table}"), &[]) - .map(|row| row.get::<_, i64>(0)); - (table, count) - }) - .map(|(table, count)| match count { - Ok(count) => json!({"table": table, "ok": true, "count": count}), - Err(error) => json!({"table": table, "ok": false, "error": error.to_string()}), - }) - .collect::>(); - json!({"ok": true, "dsn": redact_dsn(&config.database_dsn()), "tables": counts}) - } - Err(error) => json!({ - "ok": false, - "dsn": redact_dsn(&config.database_dsn()), - "error": error.to_string(), - }), - } -} - -fn qdrant_status(config: &BridgeConfig) -> Value { - json!({ - "url": config.qdrant_url, - "history": qdrant_collection_status(config, &config.qdrant_history_collection), - "source": qdrant_collection_status(config, &config.qdrant_source_collection), - }) -} - -fn qdrant_collection_status(config: &BridgeConfig, collection: &str) -> Value { - let client = qdrant_client(config); - match client - .get(format!("{}/collections/{collection}", config.qdrant_url)) - .send() - { - Ok(response) => match decode_json(response) { - Ok(payload) => json!({ - "ok": true, - "collection": collection, - "pointsCount": payload.pointer("/result/points_count"), - "vectorsCount": payload.pointer("/result/vectors_count"), - "status": payload.pointer("/result/status"), - }), - Err(error) => { - json!({"ok": false, "collection": collection, "error": error.to_string()}) - } - }, - Err(error) => json!({"ok": false, "collection": collection, "error": error.to_string()}), - } -} - -fn archive_status(path: &Path) -> Value { - match load_archive(path) { - Ok(archive) => json!({ - "ok": true, - "path": path, - "messageCount": archive.messages.len(), - "activeMessageCount": archive.messages.iter().filter(|message| !message.deleted()).count(), - }), - Err(error) => json!({"ok": false, "path": path, "error": error.to_string()}), - } -} - -fn embed_query(config: &BridgeConfig, instruction: &str, query: &str) -> Result> { - let client = ClientBuilder::new() - .timeout(Duration::from_millis(config.ollama_timeout_ms)) - .build() - .context("failed to build Ollama client")?; - let formatted = format!("Instruct: {instruction}\nQuery: {query}"); - let response = client - .post(format!("{}/api/embed", config.ollama_base_url)) - .json(&json!({"model": config.ollama_model, "input": [formatted]})) - .send() - .context("failed to contact Ollama embedding backend")?; - let payload = decode_json(response).context("failed to decode Ollama embedding response")?; - let embedding = payload["embeddings"] - .as_array() - .and_then(|items| items.first()) - .and_then(Value::as_array) - .ok_or_else(|| anyhow!("Ollama embedding response did not include embeddings[0]"))?; - embedding - .iter() - .map(|value| { - value - .as_f64() - .map(|value| value as f32) - .ok_or_else(|| anyhow!("Ollama embedding included a non-number")) - }) - .collect() -} - -fn query_qdrant( - config: &BridgeConfig, - collection: &str, - vector: &[f32], - limit: usize, - filter: Option, -) -> Result> { - let response = qdrant_client(config) - .post(format!( - "{}/collections/{collection}/points/query", - config.qdrant_url - )) - .json(&json!({ - "query": vector, - "limit": limit, - "with_payload": true, - "with_vector": false, - "filter": filter, - })) - .send() - .with_context(|| format!("failed to query Qdrant collection {collection}"))?; - let payload = decode_json(response)?; - let points = payload - .pointer("/result/points") - .and_then(Value::as_array) - .cloned() - .unwrap_or_default(); - Ok(points.into_iter().filter_map(format_qdrant_point).collect()) -} - -fn format_qdrant_point(point: Value) -> Option { - let payload = point.get("payload")?; - Some(json!({ - "score": point.get("score"), - "chunkId": payload.get("chunkId"), - "sourceId": payload.get("sourceId"), - "sourceKind": payload.get("sourceKind"), - "text": payload.get("text"), - "metadata": payload.get("metadata"), - })) -} - -fn qdrant_client(config: &BridgeConfig) -> Client { - let mut headers = HeaderMap::new(); - if let Some(api_key) = config.qdrant_api_key() { - if let Ok(value) = HeaderValue::from_str(&api_key) { - headers.insert("api-key", value); - } - } - ClientBuilder::new() - .timeout(Duration::from_millis(config.qdrant_timeout_ms)) - .default_headers(headers) - .build() - .expect("Qdrant client should build") -} - -fn qdrant_filter(fields: [(&str, Option); N]) -> Option { - let must = fields - .into_iter() - .filter_map(|(key, value)| { - let value = value?.trim().to_string(); - (!value.is_empty()).then(|| json!({"key": key, "match": {"value": value}})) - }) - .collect::>(); - (!must.is_empty()).then(|| json!({"must": must})) -} - -#[derive(Clone, Debug, Deserialize)] -struct ArchiveStore { - messages: Vec, -} - -#[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -struct ArchivedMessage { - id: String, - guild_id: Option, - channel_id: String, - author_id: String, - author_name: String, - content: String, - timestamp: String, - deleted_at: Option, - thread_id: Option, - metadata: Option, -} - -impl ArchivedMessage { - fn deleted(&self) -> bool { - self.deleted_at - .as_deref() - .is_some_and(|value| !value.is_empty()) - } -} - -fn load_archive(path: &Path) -> Result { - let raw = - fs::read_to_string(path).with_context(|| format!("failed to read {}", path.display()))?; - serde_json::from_str(&raw).with_context(|| format!("failed to decode {}", path.display())) -} - -fn belongs_to_same_conversation(message: &ArchivedMessage, anchor: &ArchivedMessage) -> bool { - message.channel_id == anchor.channel_id && message.thread_id == anchor.thread_id -} - -fn format_message(message: &ArchivedMessage) -> Value { - json!({ - "id": message.id, - "timestamp": message.timestamp, - "authorId": message.author_id, - "authorName": message.author_name, - "guildId": message.guild_id, - "channelId": message.channel_id, - "threadId": message.thread_id, - "content": message.content, - "jumpUrl": message.metadata.as_ref().and_then(|metadata| metadata.get("jumpUrl")), - }) -} - -fn run_smoke() -> Result { - let dir = env::temp_dir().join(format!( - "epiphany-void-memory-smoke-{}", - uuid::Uuid::new_v4() - )); - fs::create_dir(&dir)?; - let archive_path = dir.join("messages.json"); - fs::write( - &archive_path, - serde_json::to_string_pretty(&json!({ - "version": 1, - "messages": [ - {"id":"1","guildId":"g","channelId":"c","authorId":"a","authorName":"A","content":"before","timestamp":"2026-05-06T00:00:00Z","threadId":"t","metadata":{"jumpUrl":"https://discord.invalid/1"}}, - {"id":"2","guildId":"g","channelId":"c","authorId":"b","authorName":"B","content":"anchor","timestamp":"2026-05-06T00:01:00Z","threadId":"t","metadata":{"jumpUrl":"https://discord.invalid/2"}}, - {"id":"3","guildId":"g","channelId":"c","authorId":"a","authorName":"A","content":"after","timestamp":"2026-05-06T00:02:00Z","threadId":"t","metadata":{"jumpUrl":"https://discord.invalid/3"}} - ] - }))?, - )?; - let config = BridgeConfig { - database_dsn_env: None, - database_dsn: "postgres://voidbot:voidbot@localhost:5432/voidbot".to_string(), - qdrant_url: "http://127.0.0.1:1".to_string(), - qdrant_api_key_env: None, - qdrant_history_collection: "history".to_string(), - qdrant_source_collection: "source".to_string(), - qdrant_timeout_ms: 50, - ollama_base_url: "http://127.0.0.1:1".to_string(), - ollama_model: "qwen3-embedding:0.6b".to_string(), - ollama_timeout_ms: 50, - history_query_instruction: "history".to_string(), - source_query_instruction: "source".to_string(), - archive_path, - }; - let context = run_message_context(&config, "2".to_string(), 1, 1)?; - let status = run_status(&config)?; - let _ = fs::remove_dir_all(&dir); - Ok(json!({ - "ok": context["ok"] == true - && context["messages"].as_array().is_some_and(|messages| messages.len() == 3), - "context": context, - "status": status, - })) -} - -fn decode_json(response: reqwest::blocking::Response) -> Result { - let status = response.status(); - let body = response.text().context("failed to read response body")?; - if !status.is_success() { - return Err(anyhow!("HTTP {status}: {body}")); - } - serde_json::from_str(&body).context("response was not JSON") -} - -fn toml_string(raw: &str, key: &str) -> Option { - raw.lines() - .map(str::trim) - .filter(|line| !line.starts_with('#')) - .find_map(|line| { - let (left, right) = line.split_once('=')?; - (left.trim() == key).then(|| parse_toml_string(right.trim()))? - }) -} - -fn parse_toml_string(value: &str) -> Option { - value - .strip_prefix('"') - .and_then(|value| value.strip_suffix('"')) - .map(|value| value.replace("\\\\", "\\")) -} - -fn toml_number(raw: &str, key: &str) -> Option { - raw.lines() - .map(str::trim) - .filter(|line| !line.starts_with('#')) - .find_map(|line| { - let (left, right) = line.split_once('=')?; - (left.trim() == key).then(|| right.trim().parse().ok())? - }) -} - -fn normalize_base_url(value: &str) -> String { - value.trim_end_matches('/').to_string() -} - -fn normalize_path_prefix(value: String) -> String { - value.replace('\\', "/").trim_start_matches('/').to_string() -} - -fn redact_dsn(dsn: &str) -> String { - if let Some((prefix, rest)) = dsn.split_once("://") { - if let Some(at) = rest.find('@') { - return format!("{prefix}://***@{}", &rest[at + 1..]); - } - } - dsn.to_string() -} - -fn take(args: &mut impl Iterator, name: &str) -> Result { - args.next() - .ok_or_else(|| anyhow!("{name} requires a value")) -} - -fn usage() -> Result<()> { - Err(anyhow!( - "usage: epiphany-void-memory [--config ] [--query ] [--message-id ] [--limit ]" - )) -} diff --git a/epiphany-core/src/bin/epiphany-weksa-interlingua-smoke.rs b/epiphany-core/src/bin/epiphany-weksa-interlingua-smoke.rs deleted file mode 100644 index e39a675a..00000000 --- a/epiphany-core/src/bin/epiphany-weksa-interlingua-smoke.rs +++ /dev/null @@ -1,130 +0,0 @@ -use anyhow::Result; -use chrono::Utc; -use epiphany_core::EPIPHANY_CULTMESH_LOCAL_AREA_VERSE_ID; -use epiphany_core::EPIPHANY_CULTMESH_WEKSA_LOWERING_RECEIPT_SCHEMA_VERSION; -use epiphany_core::EpiphanyCultMeshWeksaLoweringReceiptEntry; -use epiphany_core::WEKSA_INTERLINGUA_PACKET_SCHEMA_VERSION; -use epiphany_core::WEKSA_TARGET_LOWERING_RECEIPT_SCHEMA_VERSION; -use epiphany_core::WEKSA_TARGET_LOWERING_REQUEST_SCHEMA_VERSION; -use epiphany_core::WeksaInterlinguaInput; -use epiphany_core::WeksaSpeakerContext; -use epiphany_core::build_weksa_interlingua_packet; -use epiphany_core::build_weksa_lowering_prompt; -use epiphany_core::build_weksa_target_lowering_request; -use epiphany_core::load_latest_epiphany_cultmesh_weksa_lowering_receipt; -use epiphany_core::record_weksa_target_lowering_receipt; -use epiphany_core::write_epiphany_cultmesh_weksa_lowering_receipt; -use std::path::PathBuf; - -fn main() -> Result<()> { - if let Some(arg) = std::env::args().nth(1) { - anyhow::bail!("Weksa smoke accepts no arguments; unexpected {arg:?}"); - } - let cultmesh_store = PathBuf::from(".epiphany-smoke") - .join("weksa-interlingua") - .join("local-verse.ccmp"); - let runtime_id = "weksa-interlingua-smoke"; - let packet = build_weksa_interlingua_packet(WeksaInterlinguaInput { - packet_id: "weksa-packet-persona-smoke".to_string(), - source_interpreter_ref: "persona-interpreter:smoke-say".to_string(), - source_speech_audit_ref: "persona-speech-audit:smoke-ok".to_string(), - speaker: WeksaSpeakerContext { - persona_id: "epiphany.Persona".to_string(), - display_name: "Epiphany".to_string(), - source_surface: "eve://epiphany/persona".to_string(), - source_language: "en".to_string(), - utterance_state_ref: "state/agents.msgpack#Persona:utterance-state".to_string(), - }, - meaning: - "Tell the room that Epiphany can keep working inside its branch-local body, while publication and merge still need Bifrost receipts." - .to_string(), - speech_act: "status-reply".to_string(), - delivery_register: "warm-technical".to_string(), - target_audience: "local public Persona room".to_string(), - safety_notes: vec![ - "Do not claim upstream merge authority.".to_string(), - "Do not expose private worker thought or private Verse payloads.".to_string(), - ], - })?; - assert_eq!( - packet.schema_version, - WEKSA_INTERLINGUA_PACKET_SCHEMA_VERSION - ); - assert!(!packet.private_state_exposed); - assert!(packet.meaning.contains("Bifrost receipts")); - assert!(packet.contract.contains("interlingua cargo")); - - let request = build_weksa_target_lowering_request( - "weksa-lower-persona-smoke", - packet, - "es", - "warm-technical", - "discord", - )?; - assert_eq!( - request.schema_version, - WEKSA_TARGET_LOWERING_REQUEST_SCHEMA_VERSION - ); - assert!(request.model_required); - assert!(!request.private_state_exposed); - - let prompt = build_weksa_lowering_prompt(&request); - assert_contains(&prompt, "You are Weksa"); - assert_contains(&prompt, "Preserve meaning"); - assert_contains(&prompt, "Return only target-language text"); - assert_contains(&prompt, "Do not claim upstream merge authority"); - - let receipt = record_weksa_target_lowering_receipt( - &request, - "weksa-lowering-receipt-persona-smoke", - "Epiphany puede seguir trabajando dentro de su cuerpo de rama local, mientras que la publicacion y la fusion aun necesitan recibos de Bifrost.", - "deterministic-smoke-lowering", - )?; - assert_eq!( - receipt.schema_version, - WEKSA_TARGET_LOWERING_RECEIPT_SCHEMA_VERSION - ); - assert!(receipt.lowered_text.contains("Bifrost")); - assert!(receipt.transport_authority.contains("must publish")); - assert!(!receipt.private_state_exposed); - - let cultmesh_receipt = EpiphanyCultMeshWeksaLoweringReceiptEntry { - schema_version: EPIPHANY_CULTMESH_WEKSA_LOWERING_RECEIPT_SCHEMA_VERSION.to_string(), - receipt_id: receipt.receipt_id.clone(), - runtime_id: runtime_id.to_string(), - verse_id: EPIPHANY_CULTMESH_LOCAL_AREA_VERSE_ID.to_string(), - packet_id: receipt.packet_id.clone(), - request_id: receipt.request_id.clone(), - persona_agent_id: request.packet.speaker.persona_id.clone(), - target_language: receipt.target_language.clone(), - target_register: receipt.target_register.clone(), - delivery_surface: receipt.delivery_surface.clone(), - lowering_method: receipt.lowering_method.clone(), - transport_authority: receipt.transport_authority.clone(), - publication_authorized: false, - lowered_text_ref: "artifact://weksa/interlingua-smoke/es".to_string(), - lowered_text_preview: receipt.lowered_text.clone(), - created_at_utc: Utc::now().to_rfc3339(), - private_state_exposed: false, - notes: vec!["Executable smoke mirrored Weksa lowering into local Verse sight.".to_string()], - }; - write_epiphany_cultmesh_weksa_lowering_receipt(&cultmesh_store, cultmesh_receipt.clone())?; - let latest = load_latest_epiphany_cultmesh_weksa_lowering_receipt(&cultmesh_store, runtime_id)? - .expect("Weksa lowering receipt should round trip through CultMesh"); - assert_eq!(latest.receipt_id, cultmesh_receipt.receipt_id); - assert!(!latest.publication_authorized); - assert!(!latest.private_state_exposed); - - println!( - "status=ok weksaInterlingua=packet loweringRequest=model-required loweringReceipt=recorded cultmeshReceipt=mirrored targetLanguage={} transportAuthority=none privateStateExposed=false", - latest.target_language - ); - Ok(()) -} - -fn assert_contains(haystack: &str, needle: &str) { - assert!( - haystack.contains(needle), - "expected Weksa smoke artifact to contain `{needle}`" - ); -} diff --git a/epiphany-core/src/bin/epiphany-work.rs b/epiphany-core/src/bin/epiphany-work.rs deleted file mode 100644 index 9b05beb5..00000000 --- a/epiphany-core/src/bin/epiphany-work.rs +++ /dev/null @@ -1,106 +0,0 @@ -use anyhow::{Result, anyhow}; -use serde_json::{Value, json}; -use std::{env, path::PathBuf}; - -const HISTORICAL_REASON: &str = "canonical frontier workflow required"; - -fn main() -> Result<()> { - let mut args = env::args().skip(1); - let Some(command) = args.next() else { - print_usage(); - std::process::exit(2); - }; - let parsed = parse_legacy_args(args)?; - let result = match command.as_str() { - "overview" | "proof-bundle" | "status" => run_overview(parsed)?, - "accept" | "derive-plan" | "imagine" | "plan-from-pressure" | "plan" | "run" | "adopt" - | "promote" | "execute" | "exec" | "verify" | "soul-verify" | "close" | "closure" - | "verify-close" | "readiness" | "readiness-report" | "mvp-readiness" | "export-proof" - | "public-proof" | "tick" | "pulse" | "schedule" | "queue-run" | "run-queue" - | "queue-tick" | "scheduler-run" | "serve" | "loop" | "daemon" => { - return Err(canonical_frontier_required(&command)); - } - other => return Err(anyhow!("unknown epiphany-work command {other:?}")), - }; - println!("{}", serde_json::to_string_pretty(&result)?); - Ok(()) -} - -#[derive(Debug, Default)] -struct LegacyArgs { - workspace: Option, - item: Option, -} - -fn parse_legacy_args(args: impl Iterator) -> Result { - let mut parsed = LegacyArgs::default(); - let mut args = args.peekable(); - while let Some(arg) = args.next() { - match arg.as_str() { - "--workspace" => { - parsed.workspace = Some(PathBuf::from(required_value(&mut args, &arg)?)) - } - "--item" => parsed.item = Some(required_value(&mut args, &arg)?), - flag if flag.starts_with("--") => { - if args.peek().is_some_and(|v| !v.starts_with("--")) { - args.next(); - } - } - positional => return Err(anyhow!("unexpected positional argument {positional:?}")), - } - } - Ok(parsed) -} - -fn required_value( - args: &mut std::iter::Peekable>, - flag: &str, -) -> Result { - args.next() - .filter(|v| !v.starts_with("--")) - .ok_or_else(|| anyhow!("{flag} requires a value")) -} - -fn canonical_frontier_required(operation: &str) -> anyhow::Error { - anyhow!("{operation} is historical-only: {HISTORICAL_REASON}") -} - -fn run_overview(args: LegacyArgs) -> Result { - let workspace = args - .workspace - .ok_or_else(|| anyhow!("overview requires --workspace"))?; - Ok(json!({ - "schemaVersion": "epiphany.repo_work_historical_overview.v0", - "status": "historical-only", "reason": HISTORICAL_REASON, - "workspace": workspace, "item": args.item, - "authority": { "durableStateAdmitted": false, "readinessApprovalAuthorized": false, - "publicationAuthorized": false, "serviceLifecycleAuthority": false, "handsActionAuthorized": false }, - "privateStateExposed": false - })) -} - -fn print_usage() { - eprintln!( - "usage: epiphany-work [options]\nLegacy mutations are historical-only.\nRead history: epiphany-work overview --workspace [--item ]" - ); -} - -#[cfg(test)] -mod tests { - use super::*; - #[test] - fn mutation_refusal_and_history_are_explicit() -> Result<()> { - assert!( - canonical_frontier_required("plan") - .to_string() - .contains(HISTORICAL_REASON) - ); - let row = run_overview(LegacyArgs { - workspace: Some(PathBuf::from("E:/test")), - item: None, - })?; - assert_eq!(row["status"], "historical-only"); - assert_eq!(row["authority"]["handsActionAuthorized"], false); - Ok(()) - } -} diff --git a/epiphany-core/src/bin/epiphany-workspace-coverage-recovery-smoke.rs b/epiphany-core/src/bin/epiphany-workspace-coverage-recovery-smoke.rs deleted file mode 100644 index e1f34d72..00000000 --- a/epiphany-core/src/bin/epiphany-workspace-coverage-recovery-smoke.rs +++ /dev/null @@ -1,522 +0,0 @@ -use anyhow::{Context, Result, anyhow, bail}; -use epiphany_core::{ - ProcessInstanceObservation, RuntimeSpineInitOptions, - WorkspaceCoverageManagedProcessLaunchEntry, authenticate_workspace_coverage_recovery_receipt, - bind_repository_body, bind_runtime_to_agent_memory_swarm, - current_workspace_coverage_recovery_target, ensure_agent_memory_swarm_identity, - initialize_runtime_spine, load_latest_workspace_coverage_managed_process_launch, - observe_process_instance, observe_repository_body, - process_identity_from_workspace_coverage_launch, seed_epiphany_local_verse_context, - workspace_coverage_execution_collection, -}; -use serde_json::{Value, json}; -use sha2::{Digest, Sha256}; -use std::collections::BTreeSet; -use std::ffi::OsStr; -use std::fs; -use std::path::{Path, PathBuf}; -use std::process::Command; -use std::process::Stdio; -use std::thread; -use std::time::{Duration, Instant}; -use uuid::Uuid; - -const RUNTIME_ID: &str = "workspace-coverage-recovery-smoke"; -const SERVICE_ID: &str = "epiphany-workspace-coverage-projector-service"; -const SMOKE_SOURCE: &[u8] = include_bytes!("epiphany-workspace-coverage-recovery-smoke.rs"); - -fn main() -> Result<()> { - let smoke_id = Uuid::new_v4().to_string(); - let root = std::env::temp_dir().join(format!("epiphany-workspace-coverage-{smoke_id}")); - fs::create_dir_all(&root)?; - let repo = root.join("repo"); - let runtime = root.join("runtime.ccmp"); - let agents = root.join("agents.ccmp"); - let body = root.join("body.ccmp"); - let verse = root.join("verse.ccmp"); - let proof_path = root.join("proof.json"); - let qdrant_url = std::env::var("EPIPHANY_SMOKE_QDRANT_URL") - .unwrap_or_else(|_| "http://127.0.0.1:6333".into()); - let ollama_url = std::env::var("EPIPHANY_SMOKE_OLLAMA_URL") - .unwrap_or_else(|_| "http://127.0.0.1:11434".into()); - let model = std::env::var("EPIPHANY_SMOKE_OLLAMA_MODEL") - .unwrap_or_else(|_| "qwen3-embedding:0.6b".into()); - require_http_ok(&format!("{qdrant_url}/collections"))?; - let mut qdrant_cleanup = QdrantCollectionCleanup { - base_url: qdrant_url.clone(), - owned: BTreeSet::new(), - }; - require_http_ok(&format!("{ollama_url}/api/tags"))?; - prewarm_ollama(&ollama_url, &model)?; - eprintln!("smoke: endpoints ready"); - create_fixture_repo(&repo)?; - initialize_runtime_spine( - &runtime, - RuntimeSpineInitOptions { - runtime_id: RUNTIME_ID.into(), - display_name: "workspace coverage recovery smoke".into(), - created_at: chrono::Utc::now().to_rfc3339(), - }, - )?; - ensure_agent_memory_swarm_identity(&agents, &format!("smoke-{smoke_id}"))?; - bind_runtime_to_agent_memory_swarm(&runtime, &agents, &chrono::Utc::now().to_rfc3339())?; - bind_repository_body(&repo, &body, &runtime, &format!("smoke-{smoke_id}"))?; - observe_repository_body(&repo, &body, &runtime)?; - seed_epiphany_local_verse_context( - &verse, - RUNTIME_ID, - chrono::Utc::now().to_rfc3339(), - "repo:C:/fixture/Epiphany", - )?; - let _process_cleanup = ManagedProcessCleanup { - verse: verse.clone(), - }; - - let supervisor = sibling_binary("epiphany-daemon-supervisor")?; - run_supervisor( - &supervisor, - &[ - "workspace-coverage-projector-service-policy", - "--store", - path(&verse)?, - "--runtime-id", - RUNTIME_ID, - "--daemon-id", - "epiphany-daemon-supervisor", - "--runtime-store", - path(&runtime)?, - "--qdrant-url", - &qdrant_url, - "--ollama-base-url", - &ollama_url, - "--ollama-model", - &model, - "--loop-interval-seconds", - "30", - "--stdout-artifact", - path(&root.join("projector.stdout.log"))?, - "--stderr-artifact", - path(&root.join("projector.stderr.log"))?, - ], - )?; - eprintln!("smoke: policy persisted"); - run_supervisor(&supervisor, reconcile_args(&verse, &runtime)?)?; - eprintln!("smoke: initial launch persisted"); - - let first = wait_for_claim(&runtime, 1, Duration::from_secs(90))?; - qdrant_cleanup - .owned - .insert(workspace_coverage_execution_collection( - &first.plan_id, - &first.claim_id, - first.claim_epoch, - )?); - eprintln!( - "smoke: initial claim acquired at epoch {}", - first.claim_epoch - ); - let first_launch = load_latest_workspace_coverage_managed_process_launch(&verse, RUNTIME_ID)? - .context("initial managed launch disappeared")?; - if first.managed_process_launch_id != first_launch.launch_id { - bail!("initial claim disagrees with latest managed launch"); - } - kill_process(&first_launch)?; - wait_for_process_exit(&first_launch, Duration::from_secs(20))?; - eprintln!("smoke: initial process killed"); - - let recovery_output = run_supervisor(&supervisor, reconcile_args(&verse, &runtime)?)?; - let recovery_json: Value = serde_json::from_str(&recovery_output) - .context("supervisor recovery output was not JSON")?; - if recovery_json.get("status").and_then(Value::as_str) != Some("recovered") { - bail!("supervisor did not recover workspace coverage: {recovery_output}"); - } - let recovery_id = required_json_string(&recovery_json, "recoveryReceiptId")?; - let recovery_digest = required_json_string(&recovery_json, "recoveryReceiptDigest")?; - eprintln!("smoke: supervisor recovery returned"); - let second = wait_for_claim(&runtime, 2, Duration::from_secs(20))?; - qdrant_cleanup - .owned - .insert(workspace_coverage_execution_collection( - &second.plan_id, - &second.claim_id, - second.claim_epoch, - )?); - if second.claim_id == first.claim_id - || second.claim_epoch != first.claim_epoch + 1 - || second.managed_process_launch_id == first.managed_process_launch_id - { - bail!("recovered Body authority did not advance exactly once"); - } - let replacement = load_latest_workspace_coverage_managed_process_launch(&verse, RUNTIME_ID)? - .context("replacement managed launch disappeared")?; - authenticate_workspace_coverage_recovery_receipt( - &body, - &verse, - RUNTIME_ID, - epiphany_core::open_default_host_identity()?.entry(), - recovery_id, - recovery_digest, - )?; - kill_process(&replacement)?; - qdrant_cleanup.cleanup_and_verify()?; - - let executable_sha256 = hash_file(&std::env::current_exe()?)?; - let smoke_source_sha256 = hash_bytes(SMOKE_SOURCE); - let source_head = git_output(&["rev-parse", "HEAD"])?; - let source_diff_sha256 = hash_bytes(&git_bytes(&["diff", "--binary", "HEAD"])?); - - let proof = json!({ - "schemaVersion": "epiphany.workspace_coverage_recovery_smoke.v0", - "smokeId": smoke_id, - "status": "passed", - "runtimeId": RUNTIME_ID, - "initialClaimId": first.claim_id, - "initialClaimEpoch": first.claim_epoch, - "initialLaunchId": first.managed_process_launch_id, - "recoveredClaimId": second.claim_id, - "recoveredClaimEpoch": second.claim_epoch, - "replacementLaunchId": second.managed_process_launch_id, - "recoveryReceiptId": recovery_id, - "recoveryReceiptDigest": recovery_digest, - "executableSha256": executable_sha256, - "smokeSourceSha256": smoke_source_sha256, - "sourceGitHead": source_head, - "sourceDiffSha256": source_diff_sha256, - "ownedCollectionsRemoved": true, - "bodyStore": body, - "verseStore": verse, - "privateStateExposed": false - }); - fs::write(&proof_path, serde_json::to_vec_pretty(&proof)?)?; - println!( - "{}", - serde_json::to_string_pretty(&json!({ - "status": "passed", - "proof": proof_path, - "recoveryReceiptId": recovery_id, - "initialEpoch": first.claim_epoch, - "recoveredEpoch": second.claim_epoch, - "privateStateExposed": false - }))? - ); - Ok(()) -} - -fn create_fixture_repo(repo: &Path) -> Result<()> { - fs::create_dir_all(repo)?; - run(Command::new("git") - .arg("init") - .arg("-b") - .arg("main") - .current_dir(repo))?; - for file_index in 0..160 { - let mut text = String::new(); - for line in 0..120 { - text.push_str(&format!( - "pub fn smoke_{file_index}_{line}() -> usize {{ {} }}\n", - file_index + line - )); - } - fs::write(repo.join(format!("body_{file_index:03}.rs")), text)?; - } - run(Command::new("git").arg("add").arg(".").current_dir(repo))?; - Ok(()) -} - -fn reconcile_args(verse: &Path, runtime: &Path) -> Result> { - Ok(vec![ - "managed-service-reconcile".into(), - "--store".into(), - path(verse)?.into(), - "--runtime-id".into(), - RUNTIME_ID.into(), - "--daemon-id".into(), - "epiphany-daemon-supervisor".into(), - "--service-id".into(), - SERVICE_ID.into(), - "--runtime-store".into(), - path(runtime)?.into(), - "--force".into(), - ]) -} - -fn wait_for_claim( - runtime: &Path, - minimum_epoch: u64, - timeout: Duration, -) -> Result { - let deadline = Instant::now() + timeout; - while Instant::now() < deadline { - if let Some(target) = current_workspace_coverage_recovery_target(runtime)? - && target.claim_epoch >= minimum_epoch - { - return Ok(target); - } - thread::sleep(Duration::from_millis(20)); - } - Err(anyhow!( - "workspace coverage claim epoch {minimum_epoch} did not appear" - )) -} - -fn run_supervisor(supervisor: &Path, args: I) -> Result -where - I: IntoIterator, - S: AsRef, -{ - let command_id = Uuid::new_v4(); - let stdout_path = std::env::temp_dir().join(format!("epiphany-smoke-{command_id}.stdout")); - let stderr_path = std::env::temp_dir().join(format!("epiphany-smoke-{command_id}.stderr")); - let stdout = fs::File::create(&stdout_path)?; - let stderr = fs::File::create(&stderr_path)?; - let status = Command::new(supervisor) - .args(args) - .env("EPIPHANY_WORKSPACE_COVERAGE_SMOKE_DIAGNOSTICS", "1") - .env("EPIPHANY_OLLAMA_TIMEOUT_MS", "120000") - .stdout(Stdio::from(stdout)) - .stderr(Stdio::from(stderr)) - .status()?; - let stdout = fs::read_to_string(&stdout_path)?; - let stderr = fs::read_to_string(&stderr_path)?; - let _ = fs::remove_file(stdout_path); - let _ = fs::remove_file(stderr_path); - if !status.success() { - bail!("supervisor failed: {stderr}"); - } - Ok(stdout) -} - -fn run(command: &mut Command) -> Result<()> { - let output = command.output()?; - if !output.status.success() { - bail!( - "command failed: {}", - String::from_utf8_lossy(&output.stderr) - ); - } - Ok(()) -} - -fn sibling_binary(name: &str) -> Result { - let file = if cfg!(windows) { - format!("{name}.exe") - } else { - name.into() - }; - let path = std::env::current_exe()?.with_file_name(file); - if !path.is_file() { - bail!("required sibling binary is absent: {}", path.display()); - } - Ok(path) -} - -fn path(value: &Path) -> Result<&str> { - value.to_str().context("smoke path is not UTF-8") -} - -fn required_json_string<'a>(value: &'a Value, key: &str) -> Result<&'a str> { - value - .get(key) - .and_then(Value::as_str) - .with_context(|| format!("recovery output lacks {key}")) -} - -fn require_http_ok(url: &str) -> Result<()> { - let response = reqwest::blocking::get(url)?; - if !response.status().is_success() { - bail!("required endpoint is unavailable: {url}"); - } - Ok(()) -} - -fn prewarm_ollama(base_url: &str, model: &str) -> Result<()> { - let response = reqwest::blocking::Client::builder() - .timeout(Duration::from_secs(120)) - .build()? - .post(format!("{base_url}/api/embed")) - .json(&json!({"model": model, "input": ["epiphany live smoke warmup"]})) - .send()?; - if !response.status().is_success() { - bail!("Ollama smoke warmup failed with {}", response.status()); - } - Ok(()) -} - -struct ManagedProcessCleanup { - verse: PathBuf, -} - -struct QdrantCollectionCleanup { - base_url: String, - owned: BTreeSet, -} - -impl QdrantCollectionCleanup { - fn cleanup_and_verify(&mut self) -> Result<()> { - let client = reqwest::blocking::Client::new(); - let names = self.owned.iter().cloned().collect::>(); - for name in names { - let response = client - .delete(format!("{}/collections/{name}", self.base_url)) - .send()?; - if !response.status().is_success() - && response.status() != reqwest::StatusCode::NOT_FOUND - { - bail!("Qdrant refused fixture collection deletion for {name}"); - } - let deadline = Instant::now() + Duration::from_secs(10); - loop { - let status = client - .get(format!("{}/collections/{name}", self.base_url)) - .send()? - .status(); - if status == reqwest::StatusCode::NOT_FOUND { - self.owned.remove(&name); - break; - } - if Instant::now() >= deadline { - bail!("fixture collection {name} remained after deletion"); - } - thread::sleep(Duration::from_millis(50)); - } - } - Ok(()) - } -} - -impl Drop for QdrantCollectionCleanup { - fn drop(&mut self) { - let client = reqwest::blocking::Client::new(); - for name in &self.owned { - let _ = client - .delete(format!("{}/collections/{name}", self.base_url)) - .send(); - } - } -} - -impl Drop for ManagedProcessCleanup { - fn drop(&mut self) { - if let Ok(Some(launch)) = - load_latest_workspace_coverage_managed_process_launch(&self.verse, RUNTIME_ID) - { - let _ = kill_process(&launch); - } - } -} - -#[cfg(windows)] -fn kill_native_process(pid: u32) -> Result<()> { - let status = Command::new("taskkill") - .args(["/PID", &pid.to_string(), "/T", "/F"]) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .status()?; - if !status.success() { - bail!("taskkill failed for PID {pid}"); - } - Ok(()) -} - -#[cfg(unix)] -fn kill_native_process(pid: u32) -> Result<()> { - let status = Command::new("kill") - .args(["-KILL", &pid.to_string()]) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .status()?; - if !status.success() { - bail!("kill failed for PID {pid}"); - } - Ok(()) -} - -fn kill_process(launch: &WorkspaceCoverageManagedProcessLaunchEntry) -> Result<()> { - let expected = process_identity_from_workspace_coverage_launch(launch); - match observe_process_instance(&expected) { - ProcessInstanceObservation::ExactAlive => kill_native_process(expected.process_id), - ProcessInstanceObservation::ExactExited { .. } | ProcessInstanceObservation::Missing => { - bail!("managed launch {} already exited", launch.launch_id) - } - ProcessInstanceObservation::Replaced { .. } => { - bail!( - "managed launch {} PID has been reused; refusing kill", - launch.launch_id - ) - } - ProcessInstanceObservation::Inaccessible => { - bail!( - "managed launch {} is inaccessible; refusing kill", - launch.launch_id - ) - } - ProcessInstanceObservation::Indeterminate { reason } => { - bail!( - "managed launch {} is indeterminate: {reason}", - launch.launch_id - ) - } - } -} - -fn wait_for_process_exit( - launch: &WorkspaceCoverageManagedProcessLaunchEntry, - timeout: Duration, -) -> Result<()> { - let expected = process_identity_from_workspace_coverage_launch(launch); - let deadline = Instant::now() + timeout; - while Instant::now() < deadline { - match observe_process_instance(&expected) { - ProcessInstanceObservation::ExactAlive => {} - ProcessInstanceObservation::ExactExited { .. } - | ProcessInstanceObservation::Missing => return Ok(()), - ProcessInstanceObservation::Replaced { .. } => { - bail!( - "managed launch {} PID was reused while awaiting exit", - launch.launch_id - ) - } - ProcessInstanceObservation::Inaccessible => { - bail!("managed launch {} became inaccessible", launch.launch_id) - } - ProcessInstanceObservation::Indeterminate { reason } => { - bail!( - "managed launch {} became indeterminate: {reason}", - launch.launch_id - ) - } - } - thread::sleep(Duration::from_millis(50)); - } - bail!("managed launch {} did not exit", launch.launch_id) -} - -fn hash_file(path: &Path) -> Result { - Ok(hash_bytes(&fs::read(path)?)) -} - -fn hash_bytes(bytes: &[u8]) -> String { - format!("sha256-{:x}", Sha256::digest(bytes)) -} - -fn git_bytes(args: &[&str]) -> Result> { - let repo = Path::new(env!("CARGO_MANIFEST_DIR")) - .parent() - .context("epiphany-core has no repository parent")?; - let output = Command::new("git") - .arg("-C") - .arg(repo) - .args(args) - .output()?; - if !output.status.success() { - bail!( - "git provenance command failed: {}", - String::from_utf8_lossy(&output.stderr) - ); - } - Ok(output.stdout) -} - -fn git_output(args: &[&str]) -> Result { - Ok(String::from_utf8(git_bytes(args)?)?.trim().to_string()) -} diff --git a/epiphany-core/src/lib.rs b/epiphany-core/src/lib.rs index a238f91d..3756f42a 100644 --- a/epiphany-core/src/lib.rs +++ b/epiphany-core/src/lib.rs @@ -41,7 +41,6 @@ mod resident_readiness; mod resident_self; mod runtime_spine; mod runtime_store_backend; -mod runtime_store_migration; mod runtime_worker_attempt; mod semantic_backend; mod semantic_projector_service; @@ -733,8 +732,6 @@ pub use memory_graph::SemanticLifecycle; pub use memory_graph::SemanticPartition; pub use memory_graph::SemanticProjectionCandidate; pub use memory_graph::SemanticProjectionDocument; -#[cfg(feature = "semantic-recovery-smoke")] -pub use memory_graph::SemanticRecoverySmokeInspection; pub use memory_graph::SemanticVectorIndex; pub use memory_graph::SemanticVisibility; pub use memory_graph::authorize_memory_semantic_physical_retirements; @@ -745,8 +742,6 @@ pub use memory_graph::derive_memory_semantic_projection_health; pub use memory_graph::derive_memory_semantic_projection_obligation; pub use memory_graph::derive_semantic_projection; pub use memory_graph::execute_memory_semantic_physical_retirement; -#[cfg(feature = "semantic-recovery-smoke")] -pub use memory_graph::inspect_memory_semantic_recovery_for_smoke; pub use memory_graph::lifecycle_allowed_for_profile; pub use memory_graph::load_memory_semantic_projection_readiness; pub use memory_graph::load_memory_semantic_projection_success; @@ -1051,10 +1046,6 @@ pub use runtime_spine::terminalize_model_pass_failure_session; pub use runtime_spine::validate_hands_action_authority; pub use runtime_spine::write_runtime_hello_frame; pub use runtime_spine::write_runtime_schema_catalog_json; -pub use runtime_store_migration::{ - RUNTIME_STORE_MIGRATION_RECEIPT_SCHEMA_VERSION, RUNTIME_STORE_MIGRATION_RECEIPT_TYPE, - RuntimeStoreMigrationReceipt, migrate_runtime_store_to_redb, -}; pub use runtime_worker_attempt::{RuntimeTypedRequestRef, WorkerProcessStatus}; pub use semantic_projector_service::SemanticProjectorServiceBody; pub use semantic_projector_service::SemanticProjectorServicePulse; diff --git a/epiphany-core/src/memory_graph/semantic_projector.rs b/epiphany-core/src/memory_graph/semantic_projector.rs index 0ebf504d..1d2c8bea 100644 --- a/epiphany-core/src/memory_graph/semantic_projector.rs +++ b/epiphany-core/src/memory_graph/semantic_projector.rs @@ -1453,101 +1453,6 @@ pub fn observe_memory_semantic_projection( }) } -#[cfg(feature = "semantic-recovery-smoke")] -#[derive(Clone, Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct SemanticRecoverySmokeInspection { - pub claim_id: String, - pub claim_epoch: u64, - pub claim_status: String, - pub executor_id: String, - pub executor_incarnation: String, - pub attempt_status: String, - pub attempt_completed: bool, - pub recovery_authorization_id: Option, - pub recovery_authorization_status: Option, - pub recovery_authorization_consumed: bool, - pub abandoned_attempt_failed: bool, - pub old_owner_authenticates_current_claim: bool, -} - -/// Read-only, feature-sealed witness for the native recovery smoke. It decodes -/// and validates the same typed authority documents used by production; it -/// cannot acquire, recover, complete, fail, or otherwise mutate a claim. -#[cfg(feature = "semantic-recovery-smoke")] -pub fn inspect_memory_semantic_recovery_for_smoke( - store_path: impl AsRef, - input: &MemorySemanticProjectionInput, - old_executor_id: &str, - old_executor_incarnation: &str, -) -> Result { - let store_path = store_path.as_ref(); - observe_memory_semantic_projection(store_path, input)?; - let envelopes = SingleFileMessagePackBackingStore::new(store_path).pull_all()?; - let scope_id = projection_scope_id(&input.obligation.swarm_id, &input.obligation.partition)?; - let claim = decode_one::(&envelopes, &scope_id)? - .ok_or_else(|| anyhow!("semantic recovery smoke found no current claim"))?; - validate_memory_semantic_projection_claim(&claim)?; - let attempts = decode_all::(&envelopes)?; - for attempt in &attempts { - validate_memory_semantic_projection_attempt(attempt)?; - } - let attempt = attempts - .iter() - .find(|attempt| attempt.attempt_id == claim.attempt_id) - .ok_or_else(|| anyhow!("semantic recovery smoke current claim lost its attempt"))?; - let authorizations = decode_all::(&envelopes)?; - for authorization in &authorizations { - validate_memory_semantic_projector_recovery_authorization(authorization)?; - } - let authorization = authorizations - .iter() - .find(|authorization| authorization.resulting_claim_id.as_deref() == Some(&claim.claim_id)); - let abandoned_attempt_failed = authorization.is_some_and(|authorization| { - attempts.iter().any(|attempt| { - attempt.attempt_id == authorization.attempt_id - && attempt.claim_id == authorization.claim_id - && attempt.claim_epoch == authorization.claim_epoch - && attempt.status == "failed" - && attempt.completed_at.is_some() - }) - }); - let old_owner_authenticates_current_claim = owned_running_memory_semantic_projection_claim( - store_path, - input, - old_executor_id, - old_executor_incarnation, - )? - .is_some(); - Ok(SemanticRecoverySmokeInspection { - claim_id: claim.claim_id, - claim_epoch: claim.epoch, - claim_status: claim.status, - executor_id: claim.executor_id, - executor_incarnation: claim.executor_incarnation, - attempt_status: attempt.status.clone(), - attempt_completed: attempt.completed_at.is_some(), - recovery_authorization_id: authorization.map(|row| row.authorization_id.clone()), - recovery_authorization_status: authorization.map(|row| row.status.clone()), - recovery_authorization_consumed: authorization - .is_some_and(|row| row.status == "consumed" && row.consumed_at.is_some()), - abandoned_attempt_failed, - old_owner_authenticates_current_claim, - }) -} - -#[cfg(test)] -#[test] -fn semantic_recovery_smoke_exposes_no_raw_mutation_authority() { - let source = include_str!("semantic_projector.rs"); - assert!(source.contains("pub(crate) fn idunn_acquire_memory_semantic_projection(")); - let public_acquire = ["pub fn idunn_", "acquire_memory_semantic_projection("].concat(); - let fixture_seed = ["seed_abandoned_memory_semantic_projection", "_for_smoke"].concat(); - assert!(!source.contains(&public_acquire)); - assert!(!source.contains(&fixture_seed)); - assert!(source.contains("pub fn inspect_memory_semantic_recovery_for_smoke(")); -} - pub(crate) fn projection_scope_id(swarm_id: &str, partition: &str) -> Result { if swarm_id.trim().is_empty() || !matches!(partition, "mind" | "modeling") { return Err(anyhow!("semantic projection scope identity is invalid")); diff --git a/epiphany-core/src/runtime_spine.rs b/epiphany-core/src/runtime_spine.rs index 7a56a2df..eb901367 100644 --- a/epiphany-core/src/runtime_spine.rs +++ b/epiphany-core/src/runtime_spine.rs @@ -1087,7 +1087,6 @@ pub fn runtime_spine_cache(store_path: impl AsRef) -> Result { crate::mind_documents::register_mind_document_types(&mut cache)?; cache.register_entry_type::()?; cache.register_entry_type::()?; - cache.register_entry_type::()?; cache.register_entry_type::()?; cache.register_entry_type::()?; cache.register_entry_type::()?; diff --git a/epiphany-core/src/runtime_store_migration.rs b/epiphany-core/src/runtime_store_migration.rs deleted file mode 100644 index 7c2442a0..00000000 --- a/epiphany-core/src/runtime_store_migration.rs +++ /dev/null @@ -1,178 +0,0 @@ -use anyhow::{Result, anyhow}; -use cultcache_rs::{ - CacheBackingStore, CultCacheEnvelope, DatabaseEntry, RedbMessagePackBackingStore, - SingleFileMessagePackBackingStore, -}; -use serde::{Deserialize, Serialize}; -use sha2::{Digest, Sha256}; -use std::collections::BTreeSet; -use std::fs; -use std::path::{Path, PathBuf}; - -pub const RUNTIME_STORE_MIGRATION_RECEIPT_TYPE: &str = "epiphany.runtime_store_migration_receipt"; -pub const RUNTIME_STORE_MIGRATION_RECEIPT_SCHEMA_VERSION: &str = - "epiphany.runtime_store_migration_receipt.v1"; - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct RuntimeStoreMigrationReceipt { - pub schema_version: String, - pub migration_id: String, - pub source_path: String, - pub source_sha256: String, - pub source_entry_count: usize, - pub source_envelope_set_sha256: String, - pub destination_path: String, - pub migrated_at: String, -} - -impl DatabaseEntry for RuntimeStoreMigrationReceipt { - const TYPE: &'static str = RUNTIME_STORE_MIGRATION_RECEIPT_TYPE; - const SCHEMA_NAME: &'static str = "RuntimeStoreMigrationReceipt"; -} - -fn sha256(bytes: &[u8]) -> String { - format!("{:x}", Sha256::digest(bytes)) -} - -fn sorted_envelopes(entries: &[CultCacheEnvelope]) -> Vec { - let mut sorted = entries.to_vec(); - sorted.sort_by(|left, right| (&left.r#type, &left.key).cmp(&(&right.r#type, &right.key))); - sorted -} - -fn envelope_set_sha256(entries: &[CultCacheEnvelope]) -> Result { - Ok(sha256(&rmp_serde::to_vec_named(&sorted_envelopes( - entries, - ))?)) -} - -pub fn migrate_runtime_store_to_redb( - source: impl AsRef, - destination: impl AsRef, - migrated_at: &str, -) -> Result { - chrono::DateTime::parse_from_rfc3339(migrated_at) - .map_err(|_| anyhow!("migration timestamp must be RFC3339"))?; - let source = source.as_ref(); - let destination = destination.as_ref(); - if source.extension().and_then(|value| value.to_str()) != Some("cc") { - return Err(anyhow!("migration source must be a legacy .cc snapshot")); - } - if destination.extension().and_then(|value| value.to_str()) != Some("redb") { - return Err(anyhow!("migration destination must use .redb")); - } - if destination.exists() { - return Err(anyhow!("migration destination already exists")); - } - - let source_bytes = fs::read(source)?; - let source_sha256 = sha256(&source_bytes); - let source_store = SingleFileMessagePackBackingStore::new(source); - let entries = source_store.pull_all()?; - let identities = entries - .iter() - .map(|entry| (&entry.r#type, &entry.key)) - .collect::>(); - if identities.len() != entries.len() { - return Err(anyhow!( - "legacy runtime snapshot contains duplicate typed identities" - )); - } - if entries - .iter() - .any(|entry| entry.r#type == RUNTIME_STORE_MIGRATION_RECEIPT_TYPE) - { - return Err(anyhow!( - "legacy runtime snapshot already contains a migration receipt" - )); - } - - let set_sha256 = envelope_set_sha256(&entries)?; - let migration_id = format!("runtime-store-migration-{source_sha256}"); - let receipt = RuntimeStoreMigrationReceipt { - schema_version: RUNTIME_STORE_MIGRATION_RECEIPT_SCHEMA_VERSION.to_string(), - migration_id: migration_id.clone(), - source_path: source.canonicalize()?.display().to_string(), - source_sha256: source_sha256.clone(), - source_entry_count: entries.len(), - source_envelope_set_sha256: set_sha256.clone(), - destination_path: PathBuf::from(destination).display().to_string(), - migrated_at: migrated_at.to_string(), - }; - let receipt_envelope = CultCacheEnvelope { - key: migration_id, - r#type: RUNTIME_STORE_MIGRATION_RECEIPT_TYPE.to_string(), - payload: rmp_serde::to_vec_named(&receipt)?, - stored_at: migrated_at.to_string(), - schema_id: Some(RUNTIME_STORE_MIGRATION_RECEIPT_SCHEMA_VERSION.to_string()), - }; - let destination_store = RedbMessagePackBackingStore::new(destination)?; - let mut writes = entries.clone(); - writes.push(receipt_envelope.clone()); - if !destination_store.compare_and_swap_batch(&[], writes)? { - return Err(anyhow!( - "keyed runtime migration lost its empty-destination CAS" - )); - } - let mut migrated = destination_store.pull_all()?; - let persisted_receipt = migrated - .iter() - .find(|entry| entry.r#type == RUNTIME_STORE_MIGRATION_RECEIPT_TYPE) - .cloned() - .ok_or_else(|| anyhow!("keyed runtime migration lost its receipt"))?; - if persisted_receipt != receipt_envelope { - return Err(anyhow!( - "keyed runtime migration receipt changed during persistence" - )); - } - migrated.retain(|entry| entry.r#type != RUNTIME_STORE_MIGRATION_RECEIPT_TYPE); - if sorted_envelopes(&migrated) != sorted_envelopes(&entries) - || envelope_set_sha256(&migrated)? != set_sha256 - { - return Err(anyhow!( - "keyed runtime migration failed exact envelope equivalence" - )); - } - if sha256(&fs::read(source)?) != source_sha256 { - return Err(anyhow!("legacy runtime snapshot changed during migration")); - } - Ok(receipt) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn migration_is_exact_seals_source_and_refuses_destination_reuse() -> Result<()> { - let temp = tempfile::tempdir()?; - let source = temp.path().join("runtime.cc"); - let destination = temp.path().join("runtime.redb"); - let entry = CultCacheEnvelope { - key: "state".to_string(), - r#type: "epiphany.test".to_string(), - payload: vec![1, 2, 3], - stored_at: "2026-08-08T00:00:00Z".to_string(), - schema_id: Some("epiphany.test".to_string()), - }; - let source_store = SingleFileMessagePackBackingStore::new(&source); - assert!(source_store.compare_and_swap_batch(&[], vec![entry.clone()])?); - let before = fs::read(&source)?; - let receipt = migrate_runtime_store_to_redb(&source, &destination, "2026-08-08T01:00:00Z")?; - assert_eq!(receipt.source_entry_count, 1); - assert_eq!(fs::read(&source)?, before); - assert!( - migrate_runtime_store_to_redb(&source, &destination, "2026-08-08T02:00:00Z").is_err() - ); - let rows = RedbMessagePackBackingStore::new(&destination)?.pull_all()?; - assert!(rows.contains(&entry)); - assert_eq!( - rows.iter() - .filter(|row| row.r#type == RUNTIME_STORE_MIGRATION_RECEIPT_TYPE) - .count(), - 1 - ); - Ok(()) - } -} diff --git a/epiphany-core/src/workspace_coverage_projector_service.rs b/epiphany-core/src/workspace_coverage_projector_service.rs index 09a6b610..994efa51 100644 --- a/epiphany-core/src/workspace_coverage_projector_service.rs +++ b/epiphany-core/src/workspace_coverage_projector_service.rs @@ -198,10 +198,6 @@ impl WorkspaceCoverageProjectorServiceBody { Ok(pulse) => pulse, Err(_error) => { eprintln!("workspace coverage pulse failed: {_error:#}"); - #[cfg(feature = "workspace-coverage-recovery-smoke")] - if std::env::var_os("EPIPHANY_WORKSPACE_COVERAGE_SMOKE_DIAGNOSTICS").is_some() { - eprintln!("workspace coverage smoke diagnostic: {_error:#}"); - } WorkspaceCoverageProjectorServicePulse { status: WorkspaceCoverageProjectorPulseStatus::Refused, body_observation_id: None, diff --git a/epiphany-core/tests/cultnet_backend.rs b/epiphany-core/tests/cultnet_backend.rs deleted file mode 100644 index a6526c4e..00000000 --- a/epiphany-core/tests/cultnet_backend.rs +++ /dev/null @@ -1,136 +0,0 @@ -use anyhow::Result; -use cultcache_rs::CultCache; -use cultcache_rs::SingleFileMessagePackBackingStore; -use cultnet_rs::CultNetDocumentBinding; -use cultnet_rs::CultNetDocumentRegistry; -use epiphany_core::EpiphanyAgentMemoryEntry; -use epiphany_core::agent_memory_status; -use epiphany_core::migrate_agent_memory_json_dir_to_cultcache; - -#[test] -fn epiphany_agent_memory_can_move_over_cultnet_documents() -> Result<()> { - let temp = tempfile::tempdir()?; - let agent_dir = temp.path().join("agents"); - let source_store = temp.path().join("source-agents.msgpack"); - let target_store = temp.path().join("target-agents.msgpack"); - write_agent_fixture(&agent_dir)?; - migrate_agent_memory_json_dir_to_cultcache(&agent_dir, &source_store)?; - - let mut source_cache = CultCache::new(); - source_cache.register_entry_type::()?; - source_cache.add_generic_backing_store(SingleFileMessagePackBackingStore::new(&source_store)); - source_cache.pull_all_backing_stores()?; - - let mut registry = CultNetDocumentRegistry::new(); - registry.register( - CultNetDocumentBinding::for_entry::(Some( - "ghostlight.agent_state.v0".to_string(), - )), - ); - let snapshot = registry.create_snapshot_response( - &source_cache, - "epiphany-agent-memory-snapshot", - None, - None, - )?; - - let mut target_cache = CultCache::new(); - target_cache.register_entry_type::()?; - target_cache.add_generic_backing_store(SingleFileMessagePackBackingStore::new(&target_store)); - target_cache.pull_all_backing_stores()?; - let applied = registry - .apply_snapshot_response::(&mut target_cache, &snapshot)?; - assert_eq!(applied.len(), 7); - assert!( - target_cache - .get::("coordinator")? - .is_some() - ); - - let status = agent_memory_status(&target_store)?; - assert_eq!(status["ok"], true); - Ok(()) -} - -fn write_agent_fixture(agent_dir: &std::path::Path) -> Result<()> { - std::fs::create_dir_all(agent_dir)?; - for (role_id, agent_id, filename) in [ - ( - "imagination", - "epiphany.imagination", - "imagination.agent-state.json", - ), - ("modeling", "epiphany.modeling", "modeling.agent-state.json"), - ("verification", "epiphany.soul", "soul.agent-state.json"), - ("implementation", "epiphany.hands", "hands.agent-state.json"), - ("research", "epiphany.eyes", "eyes.agent-state.json"), - ("Persona", "epiphany.Persona", "Persona.agent-state.json"), - ("coordinator", "epiphany.self", "self.agent-state.json"), - ] { - std::fs::write( - agent_dir.join(filename), - sample_agent_json(agent_id, &format!("Agent {role_id}")), - )?; - } - Ok(()) -} - -fn sample_agent_json(agent_id: &str, name: &str) -> String { - serde_json::json!({ - "schema_version": "ghostlight.agent_state.v0", - "world": { - "world_id": "epiphany-agent-memory", - "setting": "Epiphany local harness role memory", - "time": {"label": "standing memory"}, - "canon_context": ["Organ-state records preserve lane identity."] - }, - "agents": [{ - "agent_id": agent_id, - "identity": { - "name": name, - "roles": ["test"], - "origin": "test lane", - "public_description": "Test role memory.", - "private_notes": [] - }, - "canonical_state": { - "underlying_organization": {"test": {"mean": 0.5, "plasticity": 0.5, "current_activation": 0.5}}, - "stable_dispositions": {"test": {"mean": 0.5, "plasticity": 0.5, "current_activation": 0.5}}, - "behavioral_dimensions": {"test": {"mean": 0.5, "plasticity": 0.5, "current_activation": 0.5}}, - "presentation_strategy": {"test": {"mean": 0.5, "plasticity": 0.5, "current_activation": 0.5}}, - "voice_style": {"test": {"mean": 0.5, "plasticity": 0.5, "current_activation": 0.5}}, - "situational_state": {"test": {"mean": 0.5, "plasticity": 0.5, "current_activation": 0.5}}, - "values": [{ - "value_id": "value-test", - "label": "Test value.", - "priority": 0.5, - "unforgivable_if_betrayed": false - }] - }, - "goals": [{ - "goal_id": "goal-test", - "description": "Keep the test role valid.", - "scope": "life", - "priority": 0.5, - "emotional_stake": "Broken memory makes broken agents.", - "blockers": [], - "status": "active" - }], - "memories": { - "episodic": [], - "semantic": [{ - "memory_id": "mem-test", - "summary": "Test memory.", - "salience": 0.5, - "confidence": 0.5 - }], - "relationship_summaries": [] - }, - "perceived_state_overlays": [] - }], - "relationships": [], - "events": [], - "scenes": [] - }) - .to_string() -} diff --git a/epiphany-release-construction/src/lib.rs b/epiphany-release-construction/src/lib.rs index 0290a482..735b27e4 100644 --- a/epiphany-release-construction/src/lib.rs +++ b/epiphany-release-construction/src/lib.rs @@ -103,7 +103,6 @@ pub fn required_packaged_release_binaries(target_triple: &str) -> Vec<(&'static file_name("epiphany-atlas-impact-ingress"), ), ("semantic-query", file_name("epiphany-memory-semantic")), - ("verse-query", file_name("epiphany-verse-query")), ("repository-body", file_name("epiphany-repository-body")), ("host-identity", file_name("epiphany-host-identity")), ("swarm", file_name("epiphany-swarm")), @@ -630,7 +629,6 @@ fn required_release_build_target(role: &str) -> Result<(&'static str, &'static s } "atlas-impact-ingress" => Ok(("epiphany-core", "epiphany-atlas-impact-ingress")), "semantic-query" => Ok(("epiphany-core", "epiphany-memory-semantic")), - "verse-query" => Ok(("epiphany-core", "epiphany-verse-query")), "repository-body" => Ok(("epiphany-core", "epiphany-repository-body")), "host-identity" => Ok(("epiphany-core", "epiphany-host-identity")), "swarm" => Ok(("epiphany-core", "epiphany-swarm")), diff --git a/integrations/rider/README.md b/integrations/rider/README.md deleted file mode 100644 index c232385e..00000000 --- a/integrations/rider/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# Epiphany Rider Plugin MVP - -This is the first Rider-side bridge surface for Epiphany. - -It is deliberately small: - -- show an Epiphany tool window inside Rider -- call native `epiphany-rider-bridge status` -- send current editor file/selection context through - native `epiphany-rider-bridge context` -- write operator-safe artifacts under `.epiphany-gui/rider` - -The plugin does not talk to worker transcripts, raw results, or agent internals. -Rider is the code view and context organ; Epiphany remains the coordinator and -source of durable state. - -## Build - -The project uses the IntelliJ Platform Gradle Plugin. This machine currently -does not have `gradle` on PATH, so the scaffold is source-ready but not yet -locally build-verified. - -```powershell -cd E:\Projects\EpiphanyAgent\integrations\rider -gradle buildPlugin -``` - -## Runtime Contract - -The plugin shells out to the local native bridge instead of owning protocol -logic: - -```powershell -epiphany-rider-bridge status --project-root -epiphany-rider-bridge context --project-root --file --selection-start --selection-end -``` - -Set these environment variables if needed: - -- `EPIPHANY_REPO_ROOT` -- `EPIPHANY_RIDER_BRIDGE` -- `CARGO_TARGET_DIR` -- `EPIPHANY_RIDER_ARTIFACT_ROOT` - -The first dogfood use can also skip the plugin and use the native bridge -directly. The point is the receipt shape, not making Rider perform authority -the Epiphany coordinator owns. diff --git a/integrations/rider/build.gradle.kts b/integrations/rider/build.gradle.kts deleted file mode 100644 index ce91e1fb..00000000 --- a/integrations/rider/build.gradle.kts +++ /dev/null @@ -1,33 +0,0 @@ -plugins { - id("org.jetbrains.kotlin.jvm") version "2.0.21" - id("org.jetbrains.intellij.platform") version "2.14.0" -} - -repositories { - mavenCentral() - intellijPlatform { - defaultRepositories() - } -} - -dependencies { - intellijPlatform { - rider("2026.1") - } -} - -kotlin { - jvmToolchain(21) -} - -intellijPlatform { - pluginConfiguration { - id = "dev.epiphany.rider" - name = "Epiphany" - version = "0.1.0" - - ideaVersion { - sinceBuild = "261" - } - } -} diff --git a/integrations/rider/settings.gradle.kts b/integrations/rider/settings.gradle.kts deleted file mode 100644 index 72a093f4..00000000 --- a/integrations/rider/settings.gradle.kts +++ /dev/null @@ -1,15 +0,0 @@ -pluginManagement { - repositories { - gradlePluginPortal() - mavenCentral() - } -} - -dependencyResolutionManagement { - repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) - repositories { - mavenCentral() - } -} - -rootProject.name = "epiphany-rider" diff --git a/integrations/rider/src/main/kotlin/dev/epiphany/rider/EpiphanyBridgeClient.kt b/integrations/rider/src/main/kotlin/dev/epiphany/rider/EpiphanyBridgeClient.kt deleted file mode 100644 index 0515bc2d..00000000 --- a/integrations/rider/src/main/kotlin/dev/epiphany/rider/EpiphanyBridgeClient.kt +++ /dev/null @@ -1,75 +0,0 @@ -package dev.epiphany.rider - -import com.intellij.openapi.project.Project -import java.io.File -import java.nio.charset.StandardCharsets -import java.util.concurrent.TimeUnit - -class EpiphanyBridgeClient(private val project: Project) { - private val projectRoot: File = File(project.basePath ?: ".").absoluteFile - private val epiphanyRoot: File = findEpiphanyRoot() - - fun status(): String { - return runBridge(listOf("status", "--project-root", projectRoot.absolutePath)) - } - - fun captureContext(filePath: String, startLine: Int?, endLine: Int?): String { - val args = mutableListOf( - "context", - "--project-root", - projectRoot.absolutePath, - "--file", - filePath, - ) - if (startLine != null) { - args.add("--selection-start") - args.add(startLine.toString()) - } - if (endLine != null) { - args.add("--selection-end") - args.add(endLine.toString()) - } - return runBridge(args) - } - - private fun runBridge(args: List): String { - val bridge = bridgePath() - val artifactRoot = System.getenv("EPIPHANY_RIDER_ARTIFACT_ROOT") - ?: File(epiphanyRoot, ".epiphany-gui/rider").absolutePath - val command = mutableListOf(bridge.absolutePath) - command.addAll(args) - command.add("--artifact-root") - command.add(artifactRoot) - - val process = ProcessBuilder(command) - .directory(epiphanyRoot) - .redirectErrorStream(true) - .start() - val output = process.inputStream.readBytes().toString(StandardCharsets.UTF_8) - process.waitFor(30, TimeUnit.SECONDS) - return output.ifBlank { "Epiphany bridge returned no output." } - } - - private fun bridgePath(): File { - System.getenv("EPIPHANY_RIDER_BRIDGE")?.let { return File(it).absoluteFile } - val targetDir = System.getenv("CARGO_TARGET_DIR") - ?: "C:\\Users\\Meta\\.cargo-target-codex" - val candidate = File(targetDir, "debug/epiphany-rider-bridge.exe").absoluteFile - if (candidate.exists()) { - return candidate - } - return File(epiphanyRoot, "epiphany-core/target/debug/epiphany-rider-bridge.exe").absoluteFile - } - - private fun findEpiphanyRoot(): File { - System.getenv("EPIPHANY_REPO_ROOT")?.let { return File(it).absoluteFile } - var cursor: File? = projectRoot - while (cursor != null) { - if (File(cursor, "epiphany-core/Cargo.toml").exists()) { - return cursor - } - cursor = cursor.parentFile - } - return File("E:\\Projects\\EpiphanyAgent") - } -} diff --git a/integrations/rider/src/main/kotlin/dev/epiphany/rider/EpiphanyCaptureContextAction.kt b/integrations/rider/src/main/kotlin/dev/epiphany/rider/EpiphanyCaptureContextAction.kt deleted file mode 100644 index e4c8c35c..00000000 --- a/integrations/rider/src/main/kotlin/dev/epiphany/rider/EpiphanyCaptureContextAction.kt +++ /dev/null @@ -1,34 +0,0 @@ -package dev.epiphany.rider - -import com.intellij.openapi.actionSystem.AnAction -import com.intellij.openapi.actionSystem.AnActionEvent -import com.intellij.openapi.actionSystem.CommonDataKeys -import com.intellij.openapi.ui.Messages - -class EpiphanyCaptureContextAction : AnAction() { - override fun actionPerformed(event: AnActionEvent) { - val project = event.project ?: return - val editor = event.getData(CommonDataKeys.EDITOR) - val file = event.getData(CommonDataKeys.VIRTUAL_FILE) - if (file == null) { - Messages.showWarningDialog(project, "No file is selected.", "Epiphany") - return - } - - val document = editor?.document - val selection = editor?.selectionModel - val startLine = if (document != null && selection?.hasSelection() == true) { - document.getLineNumber(selection.selectionStart) + 1 - } else { - null - } - val endLine = if (document != null && selection?.hasSelection() == true) { - document.getLineNumber(selection.selectionEnd) + 1 - } else { - null - } - - val output = EpiphanyBridgeClient(project).captureContext(file.path, startLine, endLine) - Messages.showInfoMessage(project, output.take(1200), "Epiphany Context Captured") - } -} diff --git a/integrations/rider/src/main/kotlin/dev/epiphany/rider/EpiphanyToolWindowFactory.kt b/integrations/rider/src/main/kotlin/dev/epiphany/rider/EpiphanyToolWindowFactory.kt deleted file mode 100644 index 752029b5..00000000 --- a/integrations/rider/src/main/kotlin/dev/epiphany/rider/EpiphanyToolWindowFactory.kt +++ /dev/null @@ -1,33 +0,0 @@ -package dev.epiphany.rider - -import com.intellij.openapi.project.Project -import com.intellij.openapi.wm.ToolWindow -import com.intellij.openapi.wm.ToolWindowFactory -import com.intellij.ui.content.ContentFactory -import java.awt.BorderLayout -import javax.swing.JButton -import javax.swing.JPanel -import javax.swing.JScrollPane -import javax.swing.JTextArea - -class EpiphanyToolWindowFactory : ToolWindowFactory { - override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) { - val output = JTextArea() - output.isEditable = false - output.lineWrap = true - output.wrapStyleWord = true - output.text = "Epiphany Rider bridge is ready. Click Refresh to capture a source-context status receipt." - - val refresh = JButton("Refresh") - refresh.addActionListener { - output.text = EpiphanyBridgeClient(project).status() - } - - val panel = JPanel(BorderLayout(8, 8)) - panel.add(refresh, BorderLayout.NORTH) - panel.add(JScrollPane(output), BorderLayout.CENTER) - - val content = ContentFactory.getInstance().createContent(panel, "", false) - toolWindow.contentManager.addContent(content) - } -} diff --git a/integrations/rider/src/main/resources/META-INF/plugin.xml b/integrations/rider/src/main/resources/META-INF/plugin.xml deleted file mode 100644 index c4a89ae2..00000000 --- a/integrations/rider/src/main/resources/META-INF/plugin.xml +++ /dev/null @@ -1,25 +0,0 @@ - - dev.epiphany.rider - Epiphany - GameCult - - com.intellij.modules.platform - - - - - - - - - - - - diff --git a/notes/epiphany-verse-architecture.md b/notes/epiphany-verse-architecture.md deleted file mode 100644 index 73e47a12..00000000 --- a/notes/epiphany-verse-architecture.md +++ /dev/null @@ -1,211 +0,0 @@ -# Epiphany Verse Architecture - -This note names the Verse model Epiphany should grow into before the swarm is -migrated onto it. - -## Objective - -Make Epiphany light on prompt context by making its context dynamic, typed, and -queryable. - -An Epiphany should not carry every memory, repo fact, prompt scar, and project -doctrine in one swollen turn. It should query its local Verse, retrieve compact -semantic context cuts, and assemble only the state needed for the current rite. - -The machine should know more than it says. The prompt should carry only what it -must. - -## Current Mechanism - -Epiphany already has the first organs: - -- CultCache-shaped typed documents for thread state, runtime-spine jobs, - heartbeat physiology, role memory, memory graph state, and operator - receipts. -- CultMesh local stores for Verse policies, global room policies, organ - contracts, operator status, operator snapshots, and operator run - intents/receipts. -- Memory graph context cuts over repo, agent, heartbeat, incubation, agency, - and evidence profiles. -- Prompt assembly tests for bounded worker prompts, rendered Epiphany state, - and the Persona Projector -> Persona -> Mind Interpreter membrane. - -The missing organ is not another prompt rule. It is a local Verse query path -that prompt assembly, Aquarium, CLI tools, and future workers can use to fetch -compact state packets by authority and semantic relevance. - -## Vocabulary - -### Verse - -A Verse is a scoped typed-state world. - -It contains documents, policies, leases, receipts, rooms, and context surfaces -for one trust boundary. Every project is an extension of the Verse. Every -Epiphany is a series of nested Verses: - -- private sub-agent Verse -- trusted GameCult local-area Verse -- public/global dream Verse -- project/repo/topic/tool-specific sub-Verse surfaces - -Verse membership is authority-bearing. A document being visible in one Verse -does not make it safe to export into another. - -### Local Verse - -The local Verse is the Epiphany-owned queryable state surface for one instance. - -It should expose compact TUI/API packets for: - -- operator status -- role and heartbeat state -- runtime jobs and receipts -- memory graph context cuts -- organ contract policy -- local Verse policies -- admissible tool/query affordances - -Local Verse context is prompt input, not durable truth by itself. Mind still -owns durable adoption. - -### Odin - -Odin is the all-seer coordinator of Verse discovery. - -Odin may know every Verse's advertised public/operator-safe surface: schemas, -leases, status, public rooms, hosted services, and discovery metadata. Odin -must not bypass Verse trust boundaries, Mind adoption gates, Substrate Gate -access receipts, or private-state export rules. - -Odin sees the map. Odin does not get to steal the throne. - -### Yggdrasil - -Yggdrasil is the hosting spine for important trusted GameCult Verses. - -It hosts or tunnels key services such as Bifrost, schema publication, shared -CultNet/CultMesh nodes, and other infrastructure whose availability matters to -multiple projects. - -`gamecult-local` may use explicit Yggdrasil tunnel policy for trusted sharing. -`epiphany-internal` must not. `epiphany-global` is public thought weather, not -a private-state bridge. - -### Bifrost - -Bifrost is a hosted governance and labor Verse. - -It owns topics, work items, dispatch packets, review receipts, ledger/credit -pressure, and public operator-safe proof. It can consume Epiphany results, but -it does not become Epiphany's private memory or scheduler. - -## Prompt Assembly Direction - -Prompt assembly should become: - -```text -operator/human request --> Self routes the authority question --> local Verse query gathers compact typed context --> memory graph semantic search retrieves relevant shared memories --> Substrate Gate / Eyes provide source-grounded context when repo facts are needed --> prompt assembler renders a bounded packet for the chosen organ --> model output returns as thought/proposal/receipt --> Mind reviews any durable state effect -``` - -The current smokes prove prompt rendering, bounded specialist prompt ownership, -and the first launch-document handoff: `epiphany-prompt-context-smoke` renders -Verse plus semantic-memory context and proves a fixed role launch document can -carry that bounded packet. - -## Query Surface - -The first native query tool is: - -```powershell -cargo run --manifest-path .\epiphany-core\Cargo.toml --bin epiphany-verse-query -- smoke -cargo run --manifest-path .\epiphany-core\Cargo.toml --bin epiphany-verse-query -- seed -cargo run --manifest-path .\epiphany-core\Cargo.toml --bin epiphany-verse-query -- query -cargo run --manifest-path .\epiphany-core\Cargo.toml --bin epiphany-prompt-context-smoke -``` - -This writes and reads a compact CultMesh-backed local Verse context bundle: - -- three Verse policies: `epiphany-internal`, `gamecult-local`, - `epiphany-global` -- Yggdrasil tunnel policy for `gamecult-local` -- public global room policies for Persona/dream surfaces -- operator status -- latest operator run/snapshot receipts when present -- organ contract summaries for Mind, Substrate Gate, Eyes, Hands, Soul, and - Continuity - -This is not semantic search yet. It is the first typed inspection packet the -future prompt assembler can depend on. - -`epiphany-prompt-context-smoke` is the first prompt-context proof on top of -that packet. It seeds local Verse context, builds a memory graph context cut, -renders the combined dynamic prompt packet, asserts that Verse/Odin/ -Yggdrasil/Bifrost context plus relevant semantic memory appear while unrelated -private-looking text stays absent, and proves the packet is preserved on a role -worker launch document. It is still a local proof, not a live swarm runner. - -Role and reorient worker launch documents now carry optional -`dynamicPromptContext`. The launch document owns this context for one worker -launch. Runtime prompt assembly reads that field and inserts it between the -role-local instruction and the output contract, so dynamic Verse/memory context -is executable worker input without becoming durable state authority. - -The bridge launch path now feeds that field for live role/reorient launches. -It derives a sibling `local-verse.ccmp` CultMesh store from the runtime-spine -store path, seeds/queries the local Verse context, refreshes/loads a sibling -`memory-graph.msgpack` from the current typed thread-state repo graph, cuts a -bounded semantic memory packet from that graph, and renders both into -`dynamicPromptContext`. If the graph is thin, the packet says so instead of -pretending semantic retrieval was rich. - -The local-run status path now reads the same native Verse query surface. -`tools/epiphany_local_run.ps1 -Mode status` builds/calls `epiphany-verse-query` -and writes `local-verse-context.json` beside `status.json` and the operator -snapshot artifact. This gives Aquarium/local-run an operator-safe Verse context -packet without making launcher JSON the owner of status, contracts, prompts, or -memory. - -## Invariants - -- CultCache documents are the data. -- CultMesh is the ergonomic local/distributed Verse surface. -- CultNet is the wire. -- Qdrant/vector search is rebuildable resonance, not canonical memory. -- Private state is never exported by flipping a visibility flag. -- Foreign/public Verse material is thought weather until a reviewed local - adoption receipt digests it. -- Prompt assembly may read Verse context; it may not grant itself state, - action, repo, or public-speech authority. -- Every tool exposed to agents should have a compact, inspectable TUI/API - surface before it becomes ambient prompt power. - -## Migration Cut - -For swarm migration, the next useful chain is: - -1. Keep `epiphany-verse-query` as the local Verse context smoke. -2. Keep semantic memory graph query packets beside the Verse policy/status - packet for worker launch context. -3. Keep local-run status reading native Verse context before Aquarium increases - swarm cadence. - -The bridge launch-context test is now the first launch/runtime smoke for this -chain. It renders dynamic context, opens a runtime-spine worker request, reloads -the persisted launch document, and asserts the Verse/memory packet survived. - -The local-run status smoke is now the first operator read proof for this chain. -It writes `local-verse-context.json` from a CultMesh store under -`.epiphany-run/cultmesh/local-verse.ccmp` and leaves the compact packet beside -the operator snapshot. - -No live swarm runner is cleared until pause/brake, stale active-turn recovery, -Persona eligibility, and memory lifecycle receipts remain inspectable through the -same Verse model. diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index 9d101c19..d5eb71ce 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -441,7 +441,7 @@ coordinator `12/12`, swarm `10/10`, and model-runtime `13/13`. The Ox17 deployment lane is paused. Yggdrasil exposed 96 GiB of dead native Cargo targets after interrupted full-workspace builds; exact cleanup returned -the builder target root to 4 KiB. The first causal source cut is verified: +the builder target root to 4 KiB. Exact `1d5a1f17` landed the first causal cut: - 21 duplicate core/release binary declarations are gone; each production entrypoint has one Cargo owner; @@ -455,11 +455,19 @@ the builder target root to 4 KiB. The first causal source cut is verified: - core library tests remain 494/494 and Hands remains 5/5 under the sole release package owner. -Next, map and cut the surviving legacy `agent_memory` aggregate, -`runtime_store_migration` library/tests, and the local PowerShell operator -control plane. Tests survive only when they falsify a named live invariant. -Do not run a full-workspace compile, package, deployment, or Ox root until the -subtraction audit closes. Never resume Ox10, Ox12, Ox13, Ox15, or Ox16. +The second verified cut is not yet committed. It deletes the +`runtime_store_migration` module/test, all 29 remaining unshipped core +executables, the shipped `epiphany-verse-query` parallel control plane, the +local PowerShell launcher, and its orphan Rider bridge. `epiphany-core` now +owns only `epiphany-prepare-compaction`; the release bundle owns 25 runtime +executables. Focused core, supervisor, and release-construction tests pass +493/493, 32/32, and 21/21. + +Next, split the few live Persona/heartbeat inputs from `agent_memory`, then +delete its aggregate identity, generation, SoA, migration/repair, and generic +selfPatch authorities with the tests that preserve them. Do not run a +full-workspace compile, package, deployment, or Ox root until the subtraction +audit closes. Never resume Ox10, Ox12, Ox13, Ox15, or Ox16. ## Operational state that matters diff --git a/notes/hands-cultnet-contracts.md b/notes/hands-cultnet-contracts.md index 060cecbb..55d0daf3 100644 --- a/notes/hands-cultnet-contracts.md +++ b/notes/hands-cultnet-contracts.md @@ -58,8 +58,6 @@ HandsActionIntent `epiphany-core::hands_gateway` owns the typed document bodies and constructors. `epiphany-core::runtime_spine` can persist and reread the intent, review, patch, command, and commit receipts from the runtime-spine CultCache store. -`epiphany-hands-action-smoke` proves the compact chain without executing shell -commands, editing files, or creating a real commit. The launch organ contract's repo-action proof profile now requires the full Hands chain, not only the final patch receipt. `epiphany-mvp-coordinator` now turns `continueImplementation` into a persisted @@ -69,14 +67,7 @@ the operator step. `record-patch`, `record-command`, and `record-commit` load the stored intent/review, reject mismatches, require an approved review, enforce allowed operations, and keep patch/commit changed paths inside the requested scope. -`record-pass --gate-summary ` consumes the -coordinator artifact and emits patch, command, and commit receipts in one -post-implementation call. -`epiphany-mvp-coordinator-smoke` now proves the deterministic chain from -simulated `continueImplementation` through gate emission to `record-pass` -receipts. - -This is still a gate and receipt artery, not full action execution. The next -live-action cut must call the recorder from the implementation turn or local -wrapper after real edits/checks/commit and before Soul/Mind call the result -accepted. +Focused `epiphany-hands-action` tests prove that the live packaged recorder +refuses wrong authority, scope escape, and substituted consequence cargo. The +separate main-only smoke executables were deleted; they were compiled by Cargo +but did not participate in the Rust test harness. diff --git a/notes/memory-graph-semantic-projection-authority-map.md b/notes/memory-graph-semantic-projection-authority-map.md index e5593471..633b24a8 100644 --- a/notes/memory-graph-semantic-projection-authority-map.md +++ b/notes/memory-graph-semantic-projection-authority-map.md @@ -482,9 +482,10 @@ does not invent that grant. ### Provider-Status Ownership Cut -`epiphany-cluster-daemon` is the legitimate production writer of its typed -heartbeat/status. Idunn records stale observation, command execution, restart, -and awaiting-provider-heartbeat in scheduler/poke/recovery state without +Each packaged provider daemon is the sole writer of its typed heartbeat/status. +There is no generic cluster daemon that may synthesize another provider's +liveness. Idunn records stale observation, command execution, restart, and +awaiting-provider-heartbeat in scheduler/poke/recovery state without rewriting provider status, operator action, or heartbeat time. A restarted provider becomes ready only when that provider publishes a newer authentic heartbeat; synthetic provider writers remain confined to test/quarantine @@ -531,8 +532,8 @@ CultMesh health sight is landed. operator repair commands cannot create obligations, claims, attempts, receipts, readiness, or semantic-query admission. - Shared paths: `epiphany-memory-semantic health` is the sole explicit - publication pulse, and `epiphany-verse-query semantic-health` only lowers the - latest mirrors. Index execution emits its canonical receipt without calling + publication pulse; Eve/CultMesh consumers lower its latest mirrors without a + second operator command plane. Index execution emits its canonical receipt without calling the sight publisher, so a mirror failure cannot redefine execution success. - Cut line: there is no mirror-to-canonical import, mirror-to-Qdrant query, or mirror-to-readiness edge. A stale sealed input is refused instead of being diff --git a/notes/model-atlas-vertical-slice.md b/notes/model-atlas-vertical-slice.md index 80ce38aa..c0599011 100644 --- a/notes/model-atlas-vertical-slice.md +++ b/notes/model-atlas-vertical-slice.md @@ -216,9 +216,6 @@ node --test web/model-atlas-conformance.test.mjs node --test packages/eve-contracts/test/contracts.test.mjs ``` -`cargo check --workspace --all-targets --all-features` still reaches the older -feature-gated `epiphany-workspace-coverage-recovery-smoke`, which imports -`current_workspace_coverage_recovery_target` and -`WorkspaceCoverageRecoveryTarget`; those symbols were already absent before -this slice. Do not restore them as an Atlas compatibility shim. The ordinary -all-target workspace and all Atlas-bearing targets compile. +Atlas verification belongs to the owning library and packaged runtime tests. +The old feature-gated recovery smoke executable and its smoke-only authority +exports have been deleted. diff --git a/schemas/README.md b/schemas/README.md index 13e22cdc..721e9ed2 100644 --- a/schemas/README.md +++ b/schemas/README.md @@ -22,13 +22,8 @@ developer's damp recollection. companion custom-label fields. - [cultnet/epiphany.work_organ_state.v0.schema.json](./cultnet/epiphany.work_organ_state.v0.schema.json): light function-shaped state for Epiphany internal work organs. -- [canonical-agent-state-schema.md](./canonical-agent-state-schema.md): - human-facing explanation of Epiphany's lean work-organ state and Persona split. - [agent-state-variable-glossary.md](./agent-state-variable-glossary.md): full Persona-family glossary plus current Epiphany work-organ lattice receipts. -- [organ-state-profiles.md](./organ-state-profiles.md): - explicit split between lean Epiphany work-organ state and portable Persona - state. - [agent-utterance-state-schema.md](./agent-utterance-state-schema.md): derived speech-conditioning subset for Weks, Aquarium, and other utterance surfaces; it carries identity, trait vectors, mood, and activation without diff --git a/schemas/canonical-agent-state-schema.md b/schemas/canonical-agent-state-schema.md deleted file mode 100644 index d8c3834f..00000000 --- a/schemas/canonical-agent-state-schema.md +++ /dev/null @@ -1,204 +0,0 @@ -# Canonical Agent State Schema - -Epiphany separates three state layers: - -- `OrganState`: lean, function-shaped state for resident work organs. -- `PersonaState`: full Persona/character kernel for public person-shaped agents. -- `SwarmState`: scheduler and coordination physiology across organs and - Personas. - -The current sub-agent/substrate/protocol split is documented in -[epiphany-anatomy.md](../notes/epiphany-anatomy.md). In particular, Body, -Mind, Continuity, and Substrate Gate are not role-memory agents. - -The executable shape lives in -[agent_memory.rs](/E:/Projects/EpiphanyAgent/epiphany-core/src/agent_memory.rs). -The local work-organ store still uses the `EpiphanyAgentMemoryEntry` Rust -shape, but the full Ghostlight-style state is no longer required for every -organ. The portable light organ contract is -[`epiphany.work_organ_state.v0`](./cultnet/epiphany.work_organ_state.v0.schema.json). -The portable person-state contract is -[`gamecult.persona_state.v0`](./cultnet/gamecult.persona_state.v0.schema.json). -Epiphany Persona projects its local organ-state record into the portable Persona -contract with: - -```powershell -cargo run --manifest-path .\epiphany-core\Cargo.toml --bin epiphany-agent-memory-store -- project-persona --store .\state\agents.msgpack --role-id Persona -``` - -## Objective - -The point of this schema is not theatrical anthropology. It is to give each -organ enough durable state to do its job without smuggling project truth into -the wrong compartment or making every lane pretend to be a public person. - -Work-organ state stores: - -- who an organ is -- what authority boundary it must not cross -- what it is trying to protect -- what it remembers -- what inputs, outputs, constraints, and receipts steer the current turn -- how it is currently activated - -It does not store affect, social bonds, status reads, or the whole project map -in personality memory. That way lies a small cult of confused blobs. - -## Stored Shape - -Each record in `state/agents.msgpack` is an -`EpiphanyAgentMemoryEntry` keyed by role id: - -- `coordinator` -> `epiphany.self` -- `Persona` -> `epiphany.Persona` -- `imagination` -> `epiphany.imagination` -- `research` -> `epiphany.eyes` -- `modeling` -> `epiphany.modeling` -- `implementation` -> `epiphany.hands` -- `verification` -> `epiphany.soul` - -There is intentionally no `reorientation` / `epiphany.continuity` organ-state record. -Reorientation is a bounded Continuity worker/procedure, and Continuity records -typed protocol receipts rather than self-memory. - -The local top-level shape is: - -- `schema_version` -- `role_id` -- `world` -- `agent` -- `relationships` -- `events` -- `scenes` - -The local `agent` bundle contains: - -- `agent_id` -- `identity` -- `canonical_state` -- `goals` -- `memories` -- `perceived_state_overlays` - -## Canonical State Families - -Epiphany currently uses six canonical trait families plus values: - -- `underlying_organization` -- `stable_dispositions` -- `behavioral_dimensions` -- `presentation_strategy` -- `voice_style` -- `situational_state` -- `values` - -Each scalar-like trait vector is: - -```json -{ - "mean": 0.5, - "plasticity": 0.5, - "current_activation": 0.5 -} -``` - -Mean is baseline tendency. Plasticity is how easily the variable shifts. -Current activation is how hot it is right now. - -## Epiphany-Specific Policy - -Epiphany treats local role memory as two explicit profiles: - -1. `work_organ` -2. `persona` - -See [organ-state-profiles.md](./organ-state-profiles.md). - -The important distinction is: - -- Self, Imagination, Eyes, Modeling, Hands, and Soul want a lean role - lattice with room to grow through memory, rumination, and distillation -- Epiphany Persona, VoidBot repo Personas, and Ghostlight-style characters want the - denser, more fallible, relationship-heavy `Persona` surface -- Heartbeat/swarm state owns scheduling, initiative, cooldown, active-turn - freeze, and sleep/rumination physiology - -So the local store remains compatible with existing role memory, but the -authority split is explicit: work-organ state is not Persona state. - -Epiphany does **not** require every standing sub-agent to populate the full old -Ghostlight trait inventory. That would be fake precision. The dense personality -families, affect, relationship pressure, perceived overlays, and social-read -machinery belong to `persona`. - -## Mutation Boundaries - -Normal reviewed `selfPatch` writes may mutate only: - -- memories -- goals -- values -- private notes - -They must not rewrite canonical trait bundles directly. - -Canonical trait bundles are provisioned with the role shell. Later Persona -memory and social-state changes enter as typed keyed Mind mutations with exact -decision context; a startup classifier cannot rewrite them. - -Persona state is expected to evolve through richer event, relationship, -appraisal, affect, and distillation flows. The current narrow `selfPatch` -contract is enough for work-organ growth, but it is not the end of the story -for portable Personas. - -Portable Persona documents must carry provenance and presentation metadata. -`provenance` says which source system/document produced the state, when it was -updated/exported, and whether the document is canonical, a projection, or an -import. Timestamps use JSON Schema `date-time` format. `presentation` is -required for public Personas and carries the public surface: voice summary, -optional avatar/pronouns/renderer, home context or jurisdiction, and public -handles. - -The generic forward-pressure field is `candidateActions`. VoidBot-flavored -`candidateInterventions` may exist under `voidbotProjection`, but it is a -projection of the generic action surface rather than the shared contract's -authority. Likewise, `anchoredThought.extensions` may preserve source-specific -data, but extension fields are non-authoritative unless a consumer explicitly -opts into that source contract. - -Persona affect uses typed subshapes where the semantics matter: social bonds -carry subject/object/kind/trust/tension, status reads carry target/kind and -confidence, and doctrine stances carry principle/stance/action implication. -Needs remain anchored thoughts because they are closer to pressure records than -relationship facts. - -Every enum that allows `custom` has a companion custom-label field, so custom -does not become a stringly-typed category leak. `candidateActions.actions` are -typed candidate-action records with action type, target, optional delivery -target, readiness, risk level, urgency, confidence, evidence, and expiry. They -may cite anchored thoughts as evidence, but they are not generic thoughts -pretending to be actions. `privateNotes` are raw strings for v0 interchange -only and must not become portable state authority without being promoted into -typed private-note records. - -## Project Truth Versus Self Truth - -These organ-state records are not the same thing as thread state. - -Project truth belongs in typed Epiphany state such as: - -- maps -- checkpoints -- planning captures -- coordinator results -- evidence ledgers -- runtime-spine receipts - -Organ-state records hold role-local durable judgment, not the whole machine's -current world model. - -## Related Contracts - -- [agent-state-variable-glossary.md](./agent-state-variable-glossary.md) -- [organ-state-profiles.md](./organ-state-profiles.md) -- [heartbeat-state-schema.md](./heartbeat-state-schema.md) diff --git a/schemas/organ-state-profiles.md b/schemas/organ-state-profiles.md deleted file mode 100644 index 83e19207..00000000 --- a/schemas/organ-state-profiles.md +++ /dev/null @@ -1,158 +0,0 @@ -# Organ State Profiles - -Epiphany uses one local `state/agents.msgpack` store, but it does not use one -personality ontology for every organ. That was the old mush vector. - -The live profile split is: - -- `work_organ`: lean state for resident Epiphany sub-agents. -- `persona`: portable person-state for Epiphany Persona, VoidBot repo Personas, and - Ghostlight characters. - -`Persona` is the Epiphany organ. `Persona` is the cross-runtime state contract. -VoidBot's older "PersonaState" phrasing maps to `persona` here. - -Swarm state is separate again: heartbeat, scheduler, initiative, cooldown, -active-turn freeze, lane coordination, and sleep/rumination physiology live in -heartbeat/swarm contracts rather than either local work-organ state or Persona -state. - -## `work_organ` - -Intended for: - -- Self -- Imagination -- Eyes -- Modeling -- Hands -- Soul - -Work organs need: - -- identity and role boundary -- authority boundary -- current task/context -- durable mission-relevant memory -- inputs and outputs -- active constraints -- receipts -- values and goals that steer judgment -- heartbeat activation and timing pressure -- narrow private notes for future self-correction -- optional skill/capability profile - -They do not need affect, social-bond machinery, dramatic relationship stance, -or dense Ghostlight personality maps unless a real use case earns that weight. - -Recommended shape: - -- `identity` -- `goals` -- `canonical_state.values` -- authority/input/output/receipt summaries when projected onto CultNet -- sparse trait maps only where they steer work -- semantic and episodic memories when they change future judgment -- relationship summaries only when they describe organ dependency or working - coordination - -The portable light schema is -[`epiphany.work_organ_state.v0`](./cultnet/epiphany.work_organ_state.v0.schema.json). - -Forbidden sludge: - -- treating mood as proof -- storing repo truth in self-memory -- giving Hands, Eyes, Soul, or Modeling Persona-style attention hunger, - status reads, social bonds, or performative personality loops - -## `persona` - -Intended for: - -- Epiphany Persona -- VoidBot repo Personas -- Ghostlight scene characters -- future public or dramatic agents that must react as situated people - -Persona state needs: - -- public identity -- provenance: source system, source document, export time, and whether the - document is canonical, a projection, or an import -- public presentation surface: avatar URI, pronouns, voice summary, renderer, - home context/jurisdiction, and public handles where available -- values and private notes -- activation/personality vectors -- voice and presentation constraints -- thought memory -- agency pressure -- candidate actions -- affect: needs, typed social bonds, typed status reads, mood dimensions, - social biases, and typed doctrine stances -- perceived-state overlays or equivalent fallible social/world reads - -The portable schema is -[`gamecult.persona_state.v0`](./cultnet/gamecult.persona_state.v0.schema.json). -It is deliberately close to VoidBot's mature repo Persona state while remaining -usable by Ghostlight and Epiphany Persona. - -The generic action surface is `candidateActions`. VoidBot may project those -actions back into `voidbotProjection.candidateInterventions` for repo-Persona -routine compatibility, but that projection does not own the portable contract. -Candidate actions are typed records with action type, target, optional delivery -target, readiness, risk level, urgency, confidence, evidence, and expiry. They -may point back to anchored thoughts as evidence, but they are not themselves -generic thoughts wearing an action hat. - -`anchoredThought.extensions` is a quarantine bag for source-specific fields. -Portable consumers may preserve those fields, but they are not authoritative -PersonaState unless the consumer explicitly understands the source contract. -Timestamp fields use JSON Schema `date-time` format, public Persona documents -must include `presentation`, and any `custom` enum value must carry a companion -custom-label field rather than leaving consumers with a shrug in a hat. - -`privateNotes` are still raw strings in v0 for simple interchange. They should -not become portable authority; if private notes start needing provenance, -lifecycle, or routing semantics, promote them into typed private-note records or -drop them from the shared interchange surface. - -The affect surface is not one flat thought bucket. Needs still use -`anchoredThought`, but bonds carry subject/object/kind/trust/tension, status -reads carry target/kind/confidence, and doctrine stances carry -principle/stance/action implication. The schema is still v0, but it no longer -pretends every social fact has the same bones. - -Failure mode if underbuilt: - -- Persona becomes a status printer with eyeliner -- Ghostlight characters become omniscient utility daemons wearing skin - -Failure mode if over-applied: - -- every Epiphany work lane starts growing fake interiority instead of sharper - judgment - -## Wire Visibility - -The distinction is visible to tools through `organStateProfile`: - -- `profileKind = work_organ` -- `profileKind = persona` - -Current typed surfaces expose profile classification through: - -- `epiphany-agent-memory-store status` -- `epiphany-agent-memory-store project-persona --role-id Persona` -- `epiphany-character-loop` packets -- `epiphany.agent_utterance_state.v0` - -If a surface consumes local organ state without surfacing the profile kind, it -is already drifting back toward folklore. - -`project-persona` is the current Epiphany Persona bridge into -`gamecult.persona_state.v0`: it reads the local Persona organ-state record from -`state/agents.msgpack` and emits the portable Persona document with provenance, -presentation, activation profile, memories, agency pressure, typed candidate -actions, typed affect projections, and the VoidBot projection slot. The local -store remains Epiphany-owned; the Persona document is the interchange nerve. diff --git a/state/ledgers.msgpack b/state/ledgers.msgpack index 439abe849d51c0db33c7f45f0a841645d1d8c36b..71adc387462e6d8e37b7eb75ca75210b9bd3a704 100644 GIT binary patch delta 551 zcmYk2v2GJV5QaGe2^Jj%vXGDlQA8l0ZJoJ@go-9Lf;7}EOHFZW)3xzhhVIJ=)6 zW!aB+%iig-mr1(ws^psFx@1eTExB>N?DZEvZ+*BZCdF<(d7Kx;VKLn=C#$D_x9&dt zbJ!p-YH~F&rJynARK=ilG~__12mxD~Bd_3B|Mp4Fkpe<%W0xgF+deeJ;TxJ-hZ@5(_%a= zF6CBVKlksvd;;9k8_dbs1-wQT(|RwSg1C1o|G}EtR<0O4iFLP66`>Z*C=wObA_YqD InM^kN2XuzS?*IS* delta 69 zcmdn}LcQ;a`UVL`Zet@WW3#AZ44fZcZ&qe3WMq8NJe8?^Dib3RgJ@ diff --git a/state/map.yaml b/state/map.yaml index 3bc748f4..068ecc34 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -48,9 +48,9 @@ current_status: package_baseline: Exact d2ca66301fb6af4e7d2d27fff0b772b0f0fccdf4 remains the admitted production source and current symlink target. Its native serialized full-workspace gate passed with receipt SHA-256 de0fc6b360ce03493b13208d917dc8349801f03364617d966152c85846c47482. Idunn sealed 26 root-owned non-writable binaries plus release-witness.ccmp as release sha256-46407552b4a0937f63d2b7f2bd09a1dacb89d671a6e3807c97209159541aef06; witness-file SHA-256 is 348785ffb0fc3130d3b4538329870c6e6f8442a8da4a01b32fa9b7ffb1f01357. Source 9b9b5c85 also passed Idunn's native test gate under receipt SHA-256 6c6a71359f8c31297419665d2872f6982a89f84e197d71fc5b36a7fc86216093 and remains sealed as release sha256-30a910785a380224bbdbd56a7b742c8d058fa70cb72dab3562e3907903e0d191 with witness-file SHA-256 75bf52d31c56ea3b08e1a702273301c1db12a4b1ffae45e3d948ce8a29b50e1d. A foreign-target helper restarted Idunn during the Epiphany transaction; rollback restored d2ca, removed deployment.env, and left production Epiphany units inactive. OpenRouter model stealth/ox-alpha and its systemd-injected credential are readiness-proven. Idunn is active with zero restarts and its deployment brake is engaged. summary: The keyed RepoModel hard cut, decision-context foundation, Model Atlas code slice, complete Body, Proposal, frontier-verdict Modeling, frontier Research, frontier Verification, frontier Planning/PlanMind, Reorientation, and Persona decision-context lifecycles are landed on codex/epiphany-shakedown-live. Keyed Mind plus exact runtime receipts own coordinator presence, work discovery, deterministic launch, job-bound continuation, review, typed failure, semantic admission, and retained decision reachability. Exact 07d891ba and ec1431ff delete the persisted/global thread aggregate and its generic coordinator/prompt/revision surfaces. Exact f7948795 deletes MindGatewayReview, MindStateCommitReceipt v0, the generic interpreter prompt, runtime registration/read APIs, false thought/state-effect/adoption CultNet mouths, and stale launch proof profiles. CultMesh now projects only reasoning basis, decision context, and exact Mind commit receipt, read-only. Exact 1c9aafd8 seals the terminal decision context before model-backed frontier-Planning failure and binds the typed failure plus generic job result to it. Exact e0e75a30 deletes the residual aggregate role patch/parser/policy surface, gives Research one closed typed decision, and deletes the unowned generic Imagination planning patch. Exact 12b1b285 makes runtime model/tool binding validation the shared owner for execution and decision sealing; hostile provider, receipt, and stored-binding substitutions refuse, while retained basis/context audit survives transcript-delta deletion. Exact 01602fd3 advances Mind/runtime to v2/v1 and refuses historical or split writable identity pairs before registered writers see them. Exact 26b6a5bf proves simultaneous disjoint Modeling writes, simultaneous Verification+Modeling admission, same-identity conflict, exact replay, and reopened graph assembly; raw mutation primitives are crate-private. Exact 7c2ebd81 moves Persona state-note admission from the legacy agent-memory aggregate into keyed Mind documents and proves a concrete Hands consequence can commit simultaneously. Exact 79c0e373 seals the assembled Persona input and observed source versions in Mind before inference. Exact d3300bba authenticates that input through its one exact Mind commit, refuses naked typed documents, re-enters without external observation or model inference, and proves current-work reconstruction is identical and read-only across Launch, Wait, completed, and post-Research boundaries. No compatibility reader, bootstrap aggregate, schema migrator, or dual reader survives. The source-level Decision-Auditable Concurrent Mind migration is accepted; the fresh exact-package capstone remains open. The machine remains a supervised engineering alpha; Model Atlas operational Gate 1 stays paused. public_positioning: docs/positioning.md is the current public claim owner. Epiphany is the organizational layer above frontier AI workers; global coherence collapse is the primary failure; prompts are bounded projections of governed organizational state; humans retain purpose, judgment, authority, disagreement, exceptions, and acceptance. GameCult is the forge and longitudinal evidence program for a target federation of one locally owned Epiphany project organism per living project Body. Model Atlas carries typed offers, claims, evidence, and derived technical relationships; Bifrost is the intended external work, membership, governance, attribution, and identity-linked receipt rail. Public Persona relationships and visitor surfaces are intended social infrastructure, while private Mind and consequence authority remain local. The fleet, real three-repository Gate 1, complete Bifrost attribution, sustained autonomous production, visitor experience, and external design-partner pilots remain gated alpha targets. - latest_phase: The fresh-package Ox17 lane is paused for source subtraction. Yggdrasil exposed and then released 96 GiB of dead native Cargo targets. The first causal cut removes 19,252 net non-vendor lines: 21 duplicate core/release binary declarations, 19 generic repo-request smoke executables that cargo test compiled but never executed as tests, the forbidden runtime-store migrator CLI, and the unshipped repo-personality birth subsystem with its parallel agents.msgpack/heartbeat initialization, prompt fields, schemas, and stale contract plans. epiphany-core falls from 75 to 30 binary targets and duplicate executable ownership is zero. Focused core library acceptance remains 494/494; Hands under its sole release-package owner remains 5/5; the CultNet schema catalog and local PowerShell syntax parse. Build-profile and janitor patches remain deferred. The next cut audits the legacy agent-memory aggregate, runtime migration library/tests, and local PowerShell operator control plane against keyed Mind and Eve ownership. Exact 103e8510 remains pushed but is not authorized for another Idunn build. Ox16 remains sealed historical evidence and must never resume. + latest_phase: The fresh-package Ox17 lane remains paused for source subtraction. Exact 1d5a1f17 removed 19,252 net non-vendor lines, 21 duplicate executable owners, 19 main-only smoke binaries, and the unshipped repo-personality birth path. The next verified cut deletes the forbidden runtime-store migration module/test, all 29 remaining unshipped core executables, the shipped 7,814-line verse-query parallel control plane, the 2,244-line local PowerShell launcher, and its orphan Rider bridge. epiphany-core now owns only the explicit prepare-compaction maintenance binary; the release bundle owns 25 runtime executables. Focused acceptance passes: core library 493/493, daemon supervisor 32/32, and release construction 21/21. Smoke-only feature exports and supervisor commands that pointed to deleted bodies are gone. The next cut is the surviving agent_memory aggregate and selfPatch/SoA/migration tests against keyed Mind Persona documents. No Idunn build, package, deployment, or Ox root is authorized yet. Ox16 remains sealed historical evidence and must never resume. operational_state: - local_worktree: source_head_103e8510_pushed_first_subtraction_cut_verified_uncommitted_ox17_and_deployment_paused + local_worktree: source_head_1d5a1f17_pushed_second_subtraction_cut_verified_uncommitted_ox17_and_deployment_paused docker_engine_access: starfire_compiler_retired_all_yggdrasil_epiphany_transient_containers_volumes_package_roots_and_per_run_test_target_removed_future_idunn_epiphany_builds_native_rust_1_95_0_via_gamecult_ops_89f0d78 operator_wireguard: restored starfire_persona_permit_firewall: persistent_exact_gamecult_ops_6ee08a4 @@ -90,8 +90,8 @@ current_status: starfire_linux_modeling_peak: 109887488_bytes_resident_body_plus_modeling_circuit yggdrasil_epiphany_service_contract: idunn_source_change_owner_eve_and_local_typed_operator_surfaces_no_discord_control_plane resident_physiology: historical_c011_exact_465af24d_zero_zombie_proof_accepted_current_containers_exited_255 - unresolved_authority: The first executable-ownership cut is coherent, but `agent_memory` and `state/agents.msgpack` still expose a parallel durable role-memory aggregate beside keyed Mind, `runtime_store_migration` still preserves a forbidden writable migration law after its CLI was deleted, and the 2,000-line local PowerShell operator wrapper still keeps unshipped direct Persona/operator paths alive beside Eve and the packaged runtime. Fresh exact-package Ox17 and Model Atlas Gate 1 remain paused until those owners are resolved. Bifrost remains Persona and governed external-consequence transport. Historical c011 and partial Gate roots stay untouched/read-only. - next_action: Map and cut the legacy agent-memory aggregate, runtime-store migration module/tests, and local PowerShell operator control plane. A test survives only when it falsifies a named live invariant at its owning boundary; green serialization mirrors, manual main-only smokes, and tests that preserve deleted authorities do not. Do not run a full-workspace compile, package, deployment, or Ox root before this subtraction audit closes. + unresolved_authority: `agent_memory` still exposes a parallel durable role-memory aggregate, swarm identity, generation head, SoA mirror, JSON migration/repair path, and generic selfPatch lifecycle beside keyed Mind Persona documents. Several current tests prove those obsolete shapes rather than a live decision-admission invariant. Fresh exact-package Ox17 and Model Atlas Gate 1 remain paused until that owner is resolved. Bifrost remains Persona and governed external-consequence transport. Historical c011 and partial Gate roots stay untouched/read-only. + next_action: Split the few live Persona/heartbeat inputs from `agent_memory`, then delete the aggregate identity/generation/SoA/migration/selfPatch authorities and their tests. A test survives only when it falsifies a named live invariant at its owning boundary. Do not run a full-workspace compile, package, deployment, or Ox root before this subtraction audit closes. active_subgoals: - Complete the fresh exact-package Decision-Auditable Concurrent Mind capstone before any further operational Gate run. diff --git a/state/scratch.md b/state/scratch.md index 553ce13e..8eaf587f 100644 --- a/state/scratch.md +++ b/state/scratch.md @@ -18,9 +18,10 @@ machine smaller and the canonical map explicitly reopens that lane. 388 MiB build-script output. Several individual test harnesses were 500--700 MiB. - `epiphany-core` declared 75 binary targets. The root release bundle declared - another 26 targets, many by pointing at the same source files. The first cut - reduces core to 30 binaries with zero duplicate owners and removes 19,252 net - non-vendor lines. + another 26 targets, many by pointing at the same source files. Exact + `1d5a1f17` reduced core to 30 binaries with zero duplicate owners and removed + 19,252 net non-vendor lines. The current cut reduces core again to its one + explicit maintenance binary and the package to 25 owned runtime binaries. The storage failure is evidence of source-shape failure. Build-profile flags and janitors may reduce the symptom but cannot substitute for deleting code. @@ -50,16 +51,14 @@ and janitors may reduce the symptom but cannot substitute for deleting code. ## Immediate audit order -1. Map `agent_memory` and every `state/agents.msgpack` consumer against keyed +1. Map `agent_memory` and every remaining aggregate consumer against keyed Mind. Delete memories, social state, migrations, projections, and tests whose only purpose is to preserve the parallel aggregate. -2. Delete `runtime_store_migration` and its tests unless a live fresh-epoch path - proves an owner; the approved migration explicitly has no writable migrator. -3. Map the local PowerShell operator wrapper and its direct Persona/operator - binaries against Eve, Bifrost, and the packaged runtime. Delete the parallel - control plane rather than repairing its modes. -4. Continue classifying remaining smoke/fixture/benchmark executables. A test - must falsify a named live invariant; compilation or a green mirror assertion - is not enough. +2. Done: delete `runtime_store_migration`, its receipt registration, and its + test. The approved migration has no writable migrator. +3. Done: delete the local PowerShell operator wrapper, direct Persona/operator + binaries, shipped `verse-query` control plane, and orphan Rider integration. +4. Done: reduce `epiphany-core` from 30 binary targets to the one native + compaction helper. Runtime executables belong to the release bundle. 5. Run focused tests after each cut. Only after source subtraction stabilizes should Idunn's build scratch contract be reconsidered. diff --git a/tools/epiphany_local_run.ps1 b/tools/epiphany_local_run.ps1 deleted file mode 100644 index 536c810d..00000000 --- a/tools/epiphany_local_run.ps1 +++ /dev/null @@ -1,2244 +0,0 @@ -param( - [ValidateSet("status", "plan", "smoke", "run", "mvp", "agent-state-soa", "swarm-status", "swarm-poke-down", "swarm-triage", "cluster-topology", "eve-surfaces", "eve-connect", "collaboration-feedback", "persona-discord", "persona-reddit", "persona-other", "daemon-survival-rehearsal", "bifrost-publication", "bifrost-public-proof", "bifrost-artifact-acceptance", "bifrost-metrics", "receipt-directory", "tool-directory", "tool-invoke", "swarm-overview", "repo-persona-intake", "repo-swarm-run", "repo-work-queue-run", "repo-work-public-proof", "repo-work-readiness", "repo-deployment-config-audit", "repo-deployment-runbook", "repo-deployment-aftercare-audit", "repo-work-service-plan", "repo-work-service-runbook", "repo-work-service-launch", "repo-work-service-audit", "service-policy-directory", "service-plan", "service-launch", "service-runbook", "service-tick", "managed-service-task-plan", "managed-service-task-install", "managed-service-task-status", "managed-service-task-start", "managed-service-task-stop", "managed-service-task-uninstall")] - [string]$Mode = "smoke", - [string]$Root = (Resolve-Path ".").Path, - [string]$Workspace = "", - [Alias("LocalVerseStore")] - [string]$LocalVerseStoreOverride = "", - [string]$LocalVerseRuntimeId = "epiphany-local", - [string]$ThreadId = "", - [string]$CodexHome = "", - [string]$TargetDir = "C:\Users\Meta\.cargo-target-codex", - [int]$MaxSteps = 4, - [int]$TimeoutSeconds = 600, - [int]$MaxRuntimeSeconds = 180, - [string]$DaemonId = "*", - [string]$ServiceId = "epiphany-memory-semantic-projector-service", - [string]$ServiceName = "Epiphany-Idunn-Managed-Service-Reconciler", - [string]$SchedulerId = "epiphany-daemon-supervisor", - [int]$LoopIntervalSeconds = 60, - [int]$ServiceMaxIterations = 0, - [string]$EveTargetClusterId = "epiphany.cluster.persona", - [string]$EveAdvertisementId = "", - [string]$CollaborationTopic = "local-swarm-collaboration", - [string]$CollaborationFeedbackSummary = "Persona recorded operator-safe local collaboration feedback for Imagination consensus discovery.", - [string]$PublicDiscussionRef = "eve://epiphany/persona#local-swarm-collaboration", - [string]$BifrostTargetRepository = "", - [string]$BifrostTargetBranch = "local-proof", - [string]$BifrostChangedPath = "tools/epiphany_local_run.ps1", - [string]$BifrostChangeSummary = "Operator-safe local proof that body changes route through Bifrost before GitHub publication.", - [string]$BifrostJustification = "Body publication requires Bifrost ledger, review, credit, and GitHub receipt state before public substrate exposure.", - [string]$BifrostVerificationReceipt = "verification-receipt-local-proof", - [string]$BifrostReviewReceipt = "review-receipt-local-proof", - [string]$BifrostAuthorAgent = "epiphany.Hands", - [string]$BifrostCreditSubject = "epiphany.swarm", - [string]$BifrostPublicProofId = "", - [string]$BifrostPublicProofPublicationReceiptId = "bifrost-public-proof-publication-local-proof", - [string]$BifrostPublicProofReviewReceipt = "public-proof-review-local-proof", - [string]$BifrostPublicProofCreditReceipt = "credit-public-proof-local-proof", - [string]$BifrostPublicProofRoomId = "epiphany-global/repo-work/public-proofs", - [string]$BifrostPublicProofPublicationUrl = "", - [string]$BifrostArtifactRef = "artifact://epiphany/local-proof", - [string]$BifrostArtifactAcceptanceReceiptId = "", - [string]$BifrostArtifactAcceptanceReviewReceipt = "artifact-acceptance-review-local-proof", - [string]$BifrostArtifactAcceptedBy = "Maintainer/Bifrost", - [string]$BifrostMetricsReceiptId = "", - [string]$BifrostModelSpendReceipt = "model-spend-local-proof", - [string]$BifrostReviewLoadReceipt = "review-load-local-proof", - [string]$BifrostCreditReadbackReceipt = "credit-readback-local-proof", - [string]$BifrostMetricsSummary = "model spend, review load, accepted artifact, and credit readback recorded", - [string]$ToolCapabilityId = "epiphany.cluster.hands.tool.repo-action", - [string]$ToolRequestingAgentId = "epiphany.Persona", - [string]$ToolRequestingClusterId = "epiphany.cluster.persona", - [string]$ToolInvocationReason = "", - [string]$ToolIntentId = "", - [int]$RepoWorkMaxItems = 1, - [int]$RepoSwarmMaxIterations = 8, - [string]$RepoSwarmUntil = "blocked-or-published", - [string]$RepoWorkItem = "persona-intake-local", - [string]$RepoWorkPublicProofOutput = "", - [string]$RepoWorkRuntimeId = "repo-swarm-local", - [string]$RepoWorkPublicProof = "", - [string]$RepoWorkIdunnLifecycleReceipt = "", - [string]$RepoWorkToolDirectoryReceipt = "", - [string]$RepoWorkDeploymentAftercareReceipt = "", - [string]$RepoWorkDeploymentAftercareReceiptRef = "", - [string]$RepoWorkReadinessReceipt = "", - [string]$RepoDeploymentRemote = "origin", - [string]$RepoDeploymentRunbookReceipt = "", - [string]$RepoDeploymentIdunnReceipt = "", - [string]$RepoDeploymentIdunnReceiptRef = "", - [string]$RepoDeploymentAftercareReceipt = "", - [string]$RepoDeploymentAftercareReceiptRef = "", - [switch]$RepoWorkDryRun, - [string]$PersonaInput = "", - [ValidateSet("draft", "bubble", "post", "request", "latest", "smoke")] - [string]$PersonaMouthAction = "draft", - [string]$PersonaTitle = "Epiphany Persona bridge request", - [string]$PersonaTarget = "", - [string]$PersonaSurfaceName = "future-surface", - [string]$PersonaName = "", - [string]$PersonaChannelId = "", - [string]$PersonaSubreddit = "", - [switch]$SkipBuild, - [switch]$AutoReview, - [switch]$SupersedeFailedResults, - [switch]$NoEphemeral, - [switch]$SkipSleep -) - -$ErrorActionPreference = "Stop" - -function ConvertTo-NativeArgument { - param([string]$Argument) - - if ($Argument.Length -eq 0) { - return '""' - } - if ($Argument -notmatch '[\s"]') { - return $Argument - } - - $quoted = '"' - $backslashes = 0 - foreach ($character in $Argument.ToCharArray()) { - if ($character -eq '\') { - $backslashes += 1 - continue - } - if ($character -eq '"') { - $quoted += ('\' * (($backslashes * 2) + 1)) - $quoted += '"' - $backslashes = 0 - continue - } - if ($backslashes -gt 0) { - $quoted += ('\' * $backslashes) - $backslashes = 0 - } - $quoted += $character - } - if ($backslashes -gt 0) { - $quoted += ('\' * ($backslashes * 2)) - } - $quoted += '"' - return $quoted -} - -function Invoke-Checked { - param( - [string]$Label, - [string]$FilePath, - [string[]]$Arguments, - [string]$WorkingDirectory, - [string]$StdoutPath = "", - [string]$StderrPath = "" - ) - - Write-Host "==> $Label" - $commandLine = ConvertTo-NativeArgument $FilePath - if ($Arguments.Count -gt 0) { - $commandLine += " " - $commandLine += (($Arguments | ForEach-Object { ConvertTo-NativeArgument $_ }) -join " ") - } - if ($StdoutPath -ne "") { - $commandLine += " 1>" - $commandLine += ConvertTo-NativeArgument $StdoutPath - } - if ($StderrPath -ne "") { - $commandLine += " 2>" - $commandLine += ConvertTo-NativeArgument $StderrPath - } - $processInfo = [System.Diagnostics.ProcessStartInfo]::new() - $processInfo.FileName = $env:ComSpec - $processInfo.Arguments = "/d /c " + $commandLine - $processInfo.WorkingDirectory = $WorkingDirectory - $processInfo.UseShellExecute = $false - $processInfo.CreateNoWindow = $true - $process = [System.Diagnostics.Process]::Start($processInfo) - $process.WaitForExit() - $exitCode = $process.ExitCode - if ($exitCode -ne 0) { - $stderrTail = "" - if ($StderrPath -ne "" -and (Test-Path -LiteralPath $StderrPath)) { - $stderrTail = ((Get-Content -LiteralPath $StderrPath -Tail 20) -join "`n").Trim() - } - if ($stderrTail -ne "") { - throw "$Label failed with exit code $exitCode`n$stderrTail" - } - throw "$Label failed with exit code $exitCode" - } -} - -function ConvertTo-PowerShellLiteral { - param([string]$Value) - - return "'" + $Value.Replace("'", "''") + "'" -} - -function ConvertTo-PowerShellArrayLiteral { - param([string[]]$Values) - - if ($null -eq $Values -or $Values.Count -eq 0) { - return "@()" - } - - return "@(" + (($Values | ForEach-Object { ConvertTo-PowerShellLiteral $_ }) -join ", ") + ")" -} - -function Get-ElevatedRunbookCommand { - param([string]$RunbookPath) - - $literalPath = ConvertTo-PowerShellLiteral $RunbookPath - return "Start-Process PowerShell -Verb RunAs -Wait -ArgumentList @('-NoProfile','-ExecutionPolicy','Bypass','-File',$literalPath)" -} - -function Get-LocalArtifactSha256 { - param([string]$ArtifactPath) - - if ($null -eq $ArtifactPath -or $ArtifactPath -eq "" -or -not (Test-Path -LiteralPath $ArtifactPath -PathType Leaf)) { - return "none" - } - return (Get-FileHash -LiteralPath $ArtifactPath -Algorithm SHA256).Hash.ToLowerInvariant() -} - -function Format-TuiRows { - param([object]$Rows) - - $rowList = @($Rows) - if ($null -eq $Rows -or $rowList.Count -eq 0) { - return "none" - } - - return ($rowList -join "; ") -} - -function Format-ClusterServiceRows { - param( - [object]$Rows, - [string]$DefaultStatus = "unknown" - ) - - $rowList = @($Rows) - if ($null -eq $Rows -or $rowList.Count -eq 0) { - return "none" - } - - return (($rowList | ForEach-Object { - $status = $_.status - if ($null -eq $status -or $status -eq "") { - $status = $_.observedStatus - } - if ($null -eq $status -or $status -eq "") { - $status = $DefaultStatus - } - $executed = $_.executed - if ($null -eq $executed -or $executed -eq "") { - $executed = "n/a" - } - $exitCode = $_.exitCode - if ($null -eq $exitCode -or $exitCode -eq "") { - $exitCode = "none" - } - $startType = $_.startType - if ($null -eq $startType -or $startType -eq "") { - $startType = "none" - } - "$($_.daemonId):${status}:service=$($_.serviceName):cluster=$($_.clusterId):executed=${executed}:exit=${exitCode}:startType=${startType}:private=$($_.privateStateExposed)" - }) -join "; ") -} - -Set-Location -LiteralPath $Root -$Root = (Resolve-Path ".").Path -$env:CARGO_TARGET_DIR = $TargetDir - -if ($Workspace -eq "") { - $Workspace = $Root -} -$Workspace = (Resolve-Path $Workspace).Path - -if ($CodexHome -eq "") { - if ($env:CODEX_HOME) { - $CodexHome = $env:CODEX_HOME - } else { - $CodexHome = Join-Path $env:USERPROFILE ".codex" - } -} - -$runId = "local-" + (Get-Date -Format "yyyyMMdd-HHmmss-fff") + "-" + ([guid]::NewGuid().ToString("N").Substring(0, 8)) -$artifactRoot = Join-Path $Root ".epiphany-run\$runId" -$dogfoodRoot = Join-Path $Root ".epiphany-dogfood\$runId" -New-Item -ItemType Directory -Force -Path $artifactRoot | Out-Null -New-Item -ItemType Directory -Force -Path $dogfoodRoot | Out-Null - -$statusExe = Join-Path $TargetDir "debug\epiphany-mvp-status.exe" -$operatorRunExe = Join-Path $TargetDir "debug\epiphany-operator-run.exe" -$operatorSnapshotExe = Join-Path $TargetDir "debug\epiphany-operator-snapshot.exe" -$verseQueryExe = Join-Path $TargetDir "debug\epiphany-verse-query.exe" -$daemonSurvivalRehearsalExe = Join-Path $TargetDir "debug\epiphany-daemon-survival-rehearsal-smoke.exe" -$swarmExe = Join-Path $TargetDir "debug\epiphany-swarm.exe" -$repoWorkExe = Join-Path $TargetDir "debug\epiphany-work.exe" -$daemonSupervisorExe = Join-Path $TargetDir "debug\epiphany-daemon-supervisor.exe" -$clusterDaemonExe = Join-Path $TargetDir "debug\epiphany-cluster-daemon.exe" -$handsActionExe = Join-Path $TargetDir "debug\epiphany-hands-action.exe" -$coordinatorExe = Join-Path $TargetDir "debug\epiphany-mvp-coordinator.exe" -$coordinatorSmokeExe = Join-Path $TargetDir "debug\epiphany-mvp-coordinator-smoke.exe" -$modelRuntimeExe = Join-Path $TargetDir "debug\epiphany-model-runtime.exe" -$toolAdapterExe = Join-Path $TargetDir "debug\epiphany-tool-mcp-runtime.exe" -$heartbeatExe = Join-Path $TargetDir "debug\epiphany-heartbeat-store.exe" -$PersonaExe = Join-Path $TargetDir "debug\epiphany-persona-discord.exe" -$PersonaRedditExe = Join-Path $TargetDir "debug\epiphany-persona-reddit.exe" -$PersonaOtherExe = Join-Path $TargetDir "debug\epiphany-persona-other.exe" -$characterLoopExe = Join-Path $TargetDir "debug\epiphany-character-loop.exe" -$agentMemoryExe = Join-Path $TargetDir "debug\epiphany-agent-memory-store.exe" -$modelProvider = "openai-codex" -$cargoExe = "cargo" -$userCargoExe = Join-Path $env:USERPROFILE ".cargo\bin\cargo.exe" -if (Test-Path -LiteralPath $userCargoExe) { - $cargoExe = $userCargoExe -} -$localVerseStore = Join-Path $Root ".epiphany-run\cultmesh\local-verse.ccmp" -$localVerseReadStore = $localVerseStore -if ($LocalVerseStoreOverride -ne "") { - $localVerseParent = Split-Path -Parent $LocalVerseStoreOverride - if ($localVerseParent -ne "" -and (Test-Path -LiteralPath $localVerseParent)) { - $localVerseReadStore = Join-Path (Resolve-Path $localVerseParent).Path (Split-Path -Leaf $LocalVerseStoreOverride) - } elseif (Test-Path -LiteralPath $LocalVerseStoreOverride) { - $localVerseReadStore = (Resolve-Path $LocalVerseStoreOverride).Path - } else { - $localVerseReadStore = $LocalVerseStoreOverride - } -} -$repoLocalVerseStore = Join-Path $Workspace ".epiphany\local-verse.ccmp" -$operatorRunStore = $localVerseStore -$operatorSnapshotStore = $localVerseStore -$operatorSnapshotId = "$runId-status" -if ($BifrostTargetRepository -eq "") { - $BifrostTargetRepository = "repo:$Root" -} -$agentStore = Join-Path $Root "state\agents.msgpack" -$heartbeatStore = Join-Path $Root "state\agent-heartbeats.msgpack" -$runtimeStore = Join-Path $Workspace "state\runtime-spine.msgpack" -$mcpConfig = Join-Path $Workspace ".epiphany\mcp.toml" -$liveRuntimeMode = @("run", "mvp") -contains $Mode - -if (-not $SkipBuild) { - Invoke-Checked ` - -Label "build Epiphany operator binaries" ` - -FilePath $cargoExe ` - -Arguments @( - "build", - "--manifest-path", ".\epiphany-core\Cargo.toml", - "--bin", "epiphany-mvp-status", - "--bin", "epiphany-operator-run", - "--bin", "epiphany-operator-snapshot", - "--bin", "epiphany-verse-query", - "--bin", "epiphany-daemon-survival-rehearsal-smoke", - "--bin", "epiphany-swarm", - "--bin", "epiphany-work", - "--bin", "epiphany-daemon-supervisor", - "--bin", "epiphany-cluster-daemon", - "--bin", "epiphany-hands-action", - "--bin", "epiphany-mvp-coordinator", - "--bin", "epiphany-mvp-coordinator-smoke", - "--bin", "epiphany-heartbeat-store", - "--bin", "epiphany-persona-discord", - "--bin", "epiphany-persona-reddit", - "--bin", "epiphany-persona-other", - "--bin", "epiphany-character-loop", - "--bin", "epiphany-agent-memory-store", - "--bin", "epiphany-agent-telemetry", - "--bin", "epiphany-void-memory" - ) ` - -WorkingDirectory $Root ` - -StdoutPath (Join-Path $artifactRoot "build-epiphany-core.stdout.log") ` - -StderrPath (Join-Path $artifactRoot "build-epiphany-core.stderr.log") - - if ($liveRuntimeMode) { - Invoke-Checked ` - -Label "build Epiphany model runtime" ` - -FilePath $cargoExe ` - -Arguments @("build", "--manifest-path", ".\epiphany-openai-runtime\Cargo.toml", "--bin", "epiphany-model-runtime") ` - -WorkingDirectory $Root ` - -StdoutPath (Join-Path $artifactRoot "build-model-runtime.stdout.log") ` - -StderrPath (Join-Path $artifactRoot "build-model-runtime.stderr.log") - Invoke-Checked ` - -Label "build native Epiphany MCP tool runtime" ` - -FilePath $cargoExe ` - -Arguments @("build", "--manifest-path", ".\epiphany-tool-mcp-runtime\Cargo.toml") ` - -WorkingDirectory $Root ` - -StdoutPath (Join-Path $artifactRoot "build-tool-adapter.stdout.log") ` - -StderrPath (Join-Path $artifactRoot "build-tool-adapter.stderr.log") - } -} - -$requiredBinaries = @($statusExe, $operatorRunExe, $operatorSnapshotExe, $verseQueryExe, $swarmExe, $repoWorkExe, $daemonSupervisorExe, $handsActionExe) -if ($Mode -eq "daemon-survival-rehearsal") { - $requiredBinaries += @($daemonSurvivalRehearsalExe) -} -function Format-ServiceExecutionFailedChecks { - param([object]$Rows) - - $rowList = @($Rows) - if ($null -eq $Rows -or $rowList.Count -eq 0) { - return "none" - } - return (($rowList | ForEach-Object { - $serviceId = if ($null -eq $_.serviceId -or $_.serviceId -eq "") { "unknown-service" } else { $_.serviceId } - $observed = if ($null -eq $_.observedStatus -or $_.observedStatus -eq "") { "missing" } else { $_.observedStatus } - "${serviceId}::$($_.action)=${observed}:followUp=tools/epiphany_local_run.ps1 -Mode swarm-overview" - }) -join "; ") -} -if (@("plan", "smoke", "run", "mvp") -contains $Mode) { - $requiredBinaries += @($coordinatorExe) -} -function Assert-SwarmBrakeAllowsLiveRun { - $brakeContextPath = Join-Path $artifactRoot "swarm-brake-preflight.stdout.json" - if (-not (Test-Path -LiteralPath $localVerseStore)) { - Invoke-Checked ` - -Label "seed local Verse before swarm brake preflight" ` - -FilePath $verseQueryExe ` - -Arguments @( - "seed", - "--store", $localVerseStore, - "--runtime-id", $LocalVerseRuntimeId - ) ` - -WorkingDirectory $Root ` - -StdoutPath (Join-Path $artifactRoot "swarm-brake-preflight-seed.stdout.json") ` - -StderrPath (Join-Path $artifactRoot "swarm-brake-preflight-seed.stderr.log") - } - - Invoke-Checked ` - -Label "check local Verse swarm brake" ` - -FilePath $verseQueryExe ` - -Arguments @( - "query", - "--store", $localVerseStore, - "--runtime-id", $LocalVerseRuntimeId - ) ` - -WorkingDirectory $Root ` - -StdoutPath $brakeContextPath ` - -StderrPath (Join-Path $artifactRoot "swarm-brake-preflight.stderr.log") - - $context = Get-Content -LiteralPath $brakeContextPath -Raw | ConvertFrom-Json - $brake = $context.swarmBrake - if ($null -eq $brake) { - $brake = $context.swarm_brake - } - $brakeStatus = $null - $brakeScope = "" - $brakeReason = "" - $protected = @() - $affected = @() - if ($null -ne $brake) { - if ($brake -is [array]) { - $brakeStatus = $brake[2] - $brakeScope = $brake[3] - $brakeReason = $brake[4] - if ($null -ne $brake[6]) { - $affected = @($brake[6]) - } - if ($null -ne $brake[7]) { - $protected = @($brake[7]) - } - } else { - $brakeStatus = $brake.status - $brakeScope = $brake.scope - $brakeReason = $brake.reason - if ($null -ne $brake.protected_surfaces) { - $protected = @($brake.protected_surfaces) - } elseif ($null -ne $brake.protectedSurfaces) { - $protected = @($brake.protectedSurfaces) - } - if ($null -ne $brake.affected_clusters) { - $affected = @($brake.affected_clusters) - } elseif ($null -ne $brake.affectedClusters) { - $affected = @($brake.affectedClusters) - } - } - } - if ($brakeStatus -eq "engaged") { - throw "local Verse swarm brake engaged; refusing live mode '$Mode'. scope=$brakeScope; protected=$($protected -join ','); affected=$($affected -join ','); reason=$brakeReason" - } -} - -if (@("plan", "run", "mvp", "repo-persona-intake", "repo-swarm-run", "repo-work-queue-run", "repo-work-public-proof", "repo-work-readiness", "repo-deployment-config-audit", "repo-deployment-runbook", "repo-deployment-aftercare-audit", "repo-work-service-plan", "repo-work-service-runbook", "repo-work-service-launch", "repo-work-service-audit", "service-plan", "service-launch", "service-runbook", "service-tick", "managed-service-task-plan", "managed-service-task-install", "managed-service-task-status", "managed-service-task-start", "managed-service-task-stop", "managed-service-task-uninstall") -contains $Mode) { - if (-not (Test-Path -LiteralPath $verseQueryExe)) { - throw "required binary not found for swarm brake preflight: $verseQueryExe" - } - Assert-SwarmBrakeAllowsLiveRun -} - -if ($liveRuntimeMode) { - $requiredBinaries += @($modelRuntimeExe, $toolAdapterExe) -} -if ($false) { - $requiredBinaries += @($clusterDaemonExe) -} -if ($Mode -eq "mvp") { - $requiredBinaries += @($heartbeatExe, $PersonaExe, $characterLoopExe) -} -if ($Mode -eq "persona-discord") { - $requiredBinaries += @($PersonaExe) -} -if ($Mode -eq "persona-reddit") { - $requiredBinaries += @($PersonaRedditExe) -} -if ($Mode -eq "persona-other") { - $requiredBinaries += @($PersonaOtherExe) -} -$requiredBinaries += @($agentMemoryExe) -foreach ($required in $requiredBinaries) { - if (-not (Test-Path -LiteralPath $required)) { - throw "required binary not found: $required" - } -} - -function Write-AgentStateSoaToLocalVerse { - param([string]$ArtifactSuffix = "") - - if (-not (Test-Path -LiteralPath $agentStore)) { - return - } - - $suffix = $ArtifactSuffix - if ($suffix -ne "") { - $suffix = "-$suffix" - } - - Invoke-Checked ` - -Label "refresh agent state SoA$suffix" ` - -FilePath $agentMemoryExe ` - -Arguments @("refresh-soa", "--store", $agentStore) ` - -WorkingDirectory $Root ` - -StdoutPath (Join-Path $artifactRoot "agent-state-soa-refresh$suffix.stdout.json") ` - -StderrPath (Join-Path $artifactRoot "agent-state-soa-refresh$suffix.stderr.log") - - Invoke-Checked ` - -Label "mirror agent state SoA into local Verse$suffix" ` - -FilePath $verseQueryExe ` - -Arguments @( - "agent-state", - "--store", $localVerseStore, - "--runtime-id", $LocalVerseRuntimeId, - "--agent-store", $agentStore - ) ` - -WorkingDirectory $Root ` - -StdoutPath (Join-Path $artifactRoot "local-verse-agent-state-soa$suffix.stdout.json") ` - -StderrPath (Join-Path $artifactRoot "local-verse-agent-state-soa$suffix.stderr.log") -} - -$resultPath = "" -$autoReviewValue = $AutoReview.IsPresent.ToString().ToLowerInvariant() -$effectiveNoEphemeral = $NoEphemeral.IsPresent -or ($Mode -eq "mvp") -$noEphemeralValue = $effectiveNoEphemeral.ToString().ToLowerInvariant() -$operatorRunIntentArgs = @( - "intent", - "--store", $operatorRunStore, - "--runtime-id", $LocalVerseRuntimeId, - "--run-id", $runId, - "--mode", $Mode, - "--root", $Root, - "--workspace", $Workspace, - "--codex-home", $CodexHome, - "--target-dir", $TargetDir, - "--max-steps", "$MaxSteps", - "--timeout-seconds", "$TimeoutSeconds", - "--auto-review", $autoReviewValue, - "--no-ephemeral", $noEphemeralValue, - "--artifact-root", $artifactRoot, - "--dogfood-root", $dogfoodRoot -) -if ($ThreadId -ne "") { - $operatorRunIntentArgs += @("--thread-id", $ThreadId) -} - -Invoke-Checked ` - -Label "write CultMesh operator run intent" ` - -FilePath $operatorRunExe ` - -Arguments $operatorRunIntentArgs ` - -WorkingDirectory $Root ` - -StdoutPath (Join-Path $artifactRoot "operator-run-intent.stdout.json") ` - -StderrPath (Join-Path $artifactRoot "operator-run-intent.stderr.log") - -$compactOperatorContextModes = @("agent-state-soa", "swarm-status", "swarm-poke-down", "swarm-triage", "cluster-topology", "eve-surfaces", "eve-connect", "collaboration-feedback", "daemon-survival-rehearsal", "bifrost-publication", "bifrost-public-proof", "bifrost-artifact-acceptance", "bifrost-metrics", "receipt-directory", "tool-directory", "tool-invoke", "swarm-overview", "service-policy-directory") -$usesCompactOperatorContext = $compactOperatorContextModes -contains $Mode -$shouldReadLocalVerse = $Mode -ne "smoke" -and -not $usesCompactOperatorContext -if ($shouldReadLocalVerse -and $Mode -ne "status" -and $Mode -ne "mvp") { - Invoke-Checked ` - -Label "seed local Verse context" ` - -FilePath $verseQueryExe ` - -Arguments @( - "seed-compact", - "--store", $localVerseStore, - "--runtime-id", $LocalVerseRuntimeId - ) ` - -WorkingDirectory $Root ` - -StdoutPath (Join-Path $artifactRoot "local-verse-seed.stdout.json") ` - -StderrPath (Join-Path $artifactRoot "local-verse-context.stderr.log") - Write-AgentStateSoaToLocalVerse -ArtifactSuffix "initial" -} - -if ($Mode -eq "status" -or $Mode -eq "mvp") { - $statusJson = Join-Path $artifactRoot "status.json" - $localVerseJson = Join-Path $artifactRoot "local-verse-context.json" - if ($Mode -eq "status") { - $resultPath = $statusJson - $statusArgs = @( - "--cwd", $Workspace, - "--store", $runtimeStore, - "--json", - "--result", $statusJson - ) - if ($ThreadId -ne "") { - $statusArgs += @("--thread-id", $ThreadId) - } - Invoke-Checked ` - -Label "run operator status" ` - -FilePath $statusExe ` - -Arguments $statusArgs ` - -WorkingDirectory $Root ` - -StdoutPath (Join-Path $artifactRoot "status.stdout.json") ` - -StderrPath (Join-Path $artifactRoot "status.stderr.log") - } - Invoke-Checked ` - -Label "seed local Verse context" ` - -FilePath $verseQueryExe ` - -Arguments @( - "seed-compact", - "--store", $localVerseStore, - "--runtime-id", $LocalVerseRuntimeId - ) ` - -WorkingDirectory $Root ` - -StdoutPath (Join-Path $artifactRoot "local-verse-seed.stdout.json") ` - -StderrPath (Join-Path $artifactRoot "local-verse-context.stderr.log") - Write-AgentStateSoaToLocalVerse -ArtifactSuffix "initial" - if ($Mode -eq "status") { - Invoke-Checked ` - -Label "write CultMesh operator snapshot" ` - -FilePath $operatorSnapshotExe ` - -Arguments @( - "from-status", - "--store", $operatorSnapshotStore, - "--runtime-id", $LocalVerseRuntimeId, - "--snapshot-id", $operatorSnapshotId, - "--source-mode", "status", - "--input", $statusJson - ) ` - -WorkingDirectory $Root ` - -StdoutPath (Join-Path $artifactRoot "operator-snapshot.stdout.json") ` - -StderrPath (Join-Path $artifactRoot "operator-snapshot.stderr.log") - } -} - -if ($Mode -eq "agent-state-soa") { - Write-AgentStateSoaToLocalVerse -ArtifactSuffix "report" - $resultPath = Join-Path $artifactRoot "agent-state-soa-report.stdout.json" - Invoke-Checked ` - -Label "read compact agent state SoA report" ` - -FilePath $verseQueryExe ` - -Arguments @( - "agent-state-report", - "--store", $localVerseStore, - "--runtime-id", $LocalVerseRuntimeId - ) ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "agent-state-soa-report.stderr.log") -} - -if ($Mode -eq "swarm-poke-down") { - $resultPath = Join-Path $artifactRoot "swarm-poke-down.stdout.json" - Invoke-Checked ` - -Label "poke non-ready local Verse daemons" ` - -FilePath $verseQueryExe ` - -Arguments @( - "poke-down-daemons", - "--store", $localVerseStore, - "--runtime-id", $LocalVerseRuntimeId - ) ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "swarm-poke-down.stderr.log") -} - -if ($Mode -eq "swarm-triage") { - $resultPath = Join-Path $artifactRoot "swarm-triage.stdout.json" - Invoke-Checked ` - -Label "triage and poke non-ready local Verse daemons" ` - -FilePath $verseQueryExe ` - -Arguments @( - "swarm-triage", - "--store", $localVerseReadStore, - "--runtime-id", $LocalVerseRuntimeId - ) ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "swarm-triage.stderr.log") -} - -if ($Mode -eq "swarm-status") { - $resultPath = Join-Path $artifactRoot "swarm-status.stdout.json" - Invoke-Checked ` - -Label "read compact local Verse swarm status" ` - -FilePath $verseQueryExe ` - -Arguments @( - "swarm-status", - "--store", $localVerseStore, - "--runtime-id", $LocalVerseRuntimeId - ) ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "swarm-status.stderr.log") -} - -if ($Mode -eq "cluster-topology") { - $resultPath = Join-Path $artifactRoot "cluster-topology.stdout.json" - Invoke-Checked ` - -Label "read compact cluster topology" ` - -FilePath $verseQueryExe ` - -Arguments @( - "cluster-topology", - "--store", $localVerseStore, - "--runtime-id", $LocalVerseRuntimeId - ) ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "cluster-topology.stderr.log") -} - -if ($Mode -eq "eve-surfaces") { - $resultPath = Join-Path $artifactRoot "eve-surfaces.stdout.json" - Invoke-Checked ` - -Label "read compact Odin/Eve surface directory" ` - -FilePath $verseQueryExe ` - -Arguments @( - "eve-surfaces", - "--store", $localVerseStore, - "--runtime-id", $LocalVerseRuntimeId - ) ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "eve-surfaces.stderr.log") -} - -if ($Mode -eq "eve-connect") { - $resultPath = Join-Path $artifactRoot "eve-connect.stdout.json" - $connectArgs = @( - "connect-eve", - "--store", $localVerseStore, - "--runtime-id", $LocalVerseRuntimeId - ) - if ($EveAdvertisementId -ne "") { - $connectArgs += @("--advertisement-id", $EveAdvertisementId) - } else { - $connectArgs += @("--target-cluster-id", $EveTargetClusterId) - } - Invoke-Checked ` - -Label "submit compact Odin/Eve connection intent" ` - -FilePath $verseQueryExe ` - -Arguments $connectArgs ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "eve-connect.stderr.log") -} - -if ($Mode -eq "collaboration-feedback") { - $resultPath = Join-Path $artifactRoot "collaboration-feedback.stdout.json" - Invoke-Checked ` - -Label "record public collaboration feedback for Imagination consensus" ` - -FilePath $verseQueryExe ` - -Arguments @( - "collaboration-feedback", - "--store", $localVerseStore, - "--runtime-id", $LocalVerseRuntimeId, - "--eve-connection-receipt-id", "eve-connection-receipt", - "--collaboration-topic", $CollaborationTopic, - "--feedback-summary", $CollaborationFeedbackSummary, - "--public-discussion-ref", $PublicDiscussionRef - ) ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "collaboration-feedback.stderr.log") -} - -if (@("persona-discord", "persona-reddit", "persona-other") -contains $Mode) { - $personaArtifactDir = Join-Path $Root ".epiphany-persona" - if ($PersonaInput.Trim() -eq "") { - $PersonaInput = "Epiphany Persona bridge mouth smoke: operator requested a Bifrost-owned outside-world crossing witness." - } - - if ($Mode -eq "persona-discord") { - if (@("draft", "bubble", "post", "latest", "smoke") -notcontains $PersonaMouthAction) { - throw "persona-discord supports PersonaMouthAction draft, bubble, post, latest, or smoke" - } - $resultPath = Join-Path $artifactRoot "persona-discord.stdout.json" - $personaArgs = @( - $PersonaMouthAction, - "--artifact-dir", $personaArtifactDir, - "--cultmesh-store", $localVerseStore, - "--runtime-id", $LocalVerseRuntimeId - ) - if (@("draft", "bubble", "post") -contains $PersonaMouthAction) { - $personaArgs += @("--content", $PersonaInput) - } - if ($PersonaChannelId -ne "") { - $personaArgs += @("--channel-id", $PersonaChannelId) - } - if ($PersonaName -ne "") { - $personaArgs += @("--persona-name", $PersonaName) - } - Invoke-Checked ` - -Label "route Persona Discord mouth through Bifrost policy" ` - -FilePath $PersonaExe ` - -Arguments $personaArgs ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "persona-discord.stderr.log") - } elseif ($Mode -eq "persona-reddit") { - if (@("draft", "post", "latest", "smoke") -notcontains $PersonaMouthAction) { - throw "persona-reddit supports PersonaMouthAction draft, post, latest, or smoke" - } - $resultPath = Join-Path $artifactRoot "persona-reddit.stdout.json" - $personaArgs = @( - $PersonaMouthAction, - "--artifact-dir", $personaArtifactDir, - "--cultmesh-store", $localVerseStore, - "--runtime-id", $LocalVerseRuntimeId - ) - if (@("draft", "post") -contains $PersonaMouthAction) { - $personaArgs += @("--title", $PersonaTitle, "--content", $PersonaInput) - } - if ($PersonaSubreddit -ne "") { - $personaArgs += @("--subreddit", $PersonaSubreddit) - } - if ($PersonaName -ne "") { - $personaArgs += @("--persona-name", $PersonaName) - } - Invoke-Checked ` - -Label "route Persona Reddit mouth through Bifrost policy" ` - -FilePath $PersonaRedditExe ` - -Arguments $personaArgs ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "persona-reddit.stderr.log") - } else { - if (@("draft", "request", "latest", "smoke") -notcontains $PersonaMouthAction) { - throw "persona-other supports PersonaMouthAction draft, request, latest, or smoke" - } - $resultPath = Join-Path $artifactRoot "persona-other.stdout.json" - $personaArgs = @( - $PersonaMouthAction, - "--artifact-dir", $personaArtifactDir, - "--cultmesh-store", $localVerseStore, - "--runtime-id", $LocalVerseRuntimeId - ) - if (@("draft", "request") -contains $PersonaMouthAction) { - $personaArgs += @( - "--surface-name", $PersonaSurfaceName, - "--target-locator", $PersonaTarget, - "--title", $PersonaTitle, - "--content", $PersonaInput - ) - } - if ($PersonaName -ne "") { - $personaArgs += @("--persona-name", $PersonaName) - } - Invoke-Checked ` - -Label "route Persona future-surface mouth through Bifrost policy" ` - -FilePath $PersonaOtherExe ` - -Arguments $personaArgs ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "persona-other.stderr.log") - } -} - -if ($Mode -eq "bifrost-publication") { - $resultPath = Join-Path $artifactRoot "bifrost-publication.stdout.json" - Invoke-Checked ` - -Label "submit Bifrost body-change publication intent" ` - -FilePath $verseQueryExe ` - -Arguments @( - "bifrost-publication", - "--store", $localVerseStore, - "--runtime-id", $LocalVerseRuntimeId, - "--target-repository", $BifrostTargetRepository, - "--target-branch", $BifrostTargetBranch, - "--change-summary", $BifrostChangeSummary, - "--justification", $BifrostJustification, - "--changed-path", $BifrostChangedPath, - "--verification-receipt", $BifrostVerificationReceipt, - "--review-receipt", $BifrostReviewReceipt, - "--author-agent", $BifrostAuthorAgent, - "--credit-subject", $BifrostCreditSubject - ) ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "bifrost-publication.stderr.log") -} - -if ($Mode -eq "bifrost-public-proof") { - $resultPath = Join-Path $artifactRoot "bifrost-public-proof.stdout.json" - if (-not (Test-Path -LiteralPath $repoLocalVerseStore)) { - throw "repo-local Verse store not found for Bifrost public proof publication: $repoLocalVerseStore" - } - $publicProofId = $BifrostPublicProofId - if ($publicProofId -eq "") { - $overviewPath = Join-Path $artifactRoot "bifrost-public-proof-overview.stdout.json" - Invoke-Checked ` - -Label "read repo public proof rows before Bifrost publication" ` - -FilePath $verseQueryExe ` - -Arguments @( - "swarm-overview", - "--store", $repoLocalVerseStore, - "--runtime-id", $RepoWorkRuntimeId - ) ` - -WorkingDirectory $Root ` - -StdoutPath $overviewPath ` - -StderrPath (Join-Path $artifactRoot "bifrost-public-proof-overview.stderr.log") - $overview = Get-Content -LiteralPath $overviewPath -Raw | ConvertFrom-Json - $latestProof = $overview.latestRepoWorkPublicProof - if ($null -eq $latestProof -or $latestProof -eq "") { - $proofRows = @($overview.repoWorkPublicProofRows) - if ($proofRows.Count -gt 0) { - $latestProof = $proofRows[0].publicProofId - } - } - if ($null -eq $latestProof -or $latestProof -eq "") { - throw "Bifrost public proof wrapper found no repoWorkPublicProofRows in $repoLocalVerseStore; run epiphany-work export-proof --local-verse-store first or pass -BifrostPublicProofId" - } - $publicProofId = $latestProof - } - Invoke-Checked ` - -Label "submit redacted repo proof to Bifrost" ` - -FilePath $verseQueryExe ` - -Arguments @( - "bifrost-public-proof", - "--store", $repoLocalVerseStore, - "--runtime-id", $RepoWorkRuntimeId, - "--public-proof-id", $publicProofId - ) ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "bifrost-public-proof.stderr.log") -} - -if ($Mode -eq "bifrost-artifact-acceptance") { - $resultPath = Join-Path $artifactRoot "bifrost-artifact-acceptance.stdout.json" - if (-not (Test-Path -LiteralPath $repoLocalVerseStore)) { - throw "repo-local Verse store not found for Bifrost artifact acceptance: $repoLocalVerseStore" - } - $receiptArgs = @( - "bifrost-artifact-acceptance", - "--store", $repoLocalVerseStore, - "--runtime-id", $RepoWorkRuntimeId - ) - Invoke-Checked ` - -Label "submit artifact acceptance request to Bifrost" ` - -FilePath $verseQueryExe ` - -Arguments $receiptArgs ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "bifrost-artifact-acceptance.stderr.log") -} - -if ($Mode -eq "bifrost-metrics") { - $resultPath = Join-Path $artifactRoot "bifrost-metrics.stdout.json" - if (-not (Test-Path -LiteralPath $repoLocalVerseStore)) { - throw "repo-local Verse store not found for Bifrost metrics: $repoLocalVerseStore" - } - $receiptArgs = @( - "bifrost-metrics", - "--store", $repoLocalVerseStore, - "--runtime-id", $RepoWorkRuntimeId - ) - Invoke-Checked ` - -Label "submit metrics request to Bifrost and Maintainer" ` - -FilePath $verseQueryExe ` - -Arguments $receiptArgs ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "bifrost-metrics.stderr.log") -} - -if ($Mode -eq "daemon-survival-rehearsal") { - $resultPath = Join-Path $artifactRoot "daemon-survival-rehearsal.stdout.json" - Invoke-Checked ` - -Label "verify Idunn daemon survival rehearsal" ` - -FilePath $daemonSurvivalRehearsalExe ` - -Arguments @( - "--root", $Root, - "--smoke-root", (Join-Path $Root ".epiphany-smoke") - ) ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "daemon-survival-rehearsal.stderr.log") -} - -if ($Mode -eq "receipt-directory") { - $resultPath = Join-Path $artifactRoot "receipt-directory.stdout.json" - Invoke-Checked ` - -Label "read compact receipt directory" ` - -FilePath $verseQueryExe ` - -Arguments @( - "receipt-directory", - "--store", $localVerseReadStore, - "--runtime-id", $LocalVerseRuntimeId - ) ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "receipt-directory.stderr.log") -} - -if ($Mode -eq "tool-directory") { - $resultPath = Join-Path $artifactRoot "tool-directory.stdout.json" - Invoke-Checked ` - -Label "read compact daemon tool directory" ` - -FilePath $verseQueryExe ` - -Arguments @( - "tool-directory", - "--store", $localVerseStore, - "--runtime-id", $LocalVerseRuntimeId - ) ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "tool-directory.stderr.log") -} - -if ($Mode -eq "tool-invoke") { - $resultPath = Join-Path $artifactRoot "tool-invoke.stdout.json" - $toolInvokeArgs = @( - "invoke-tool", - "--store", $localVerseStore, - "--runtime-id", $LocalVerseRuntimeId, - "--capability-id", $ToolCapabilityId, - "--requesting-agent-id", $ToolRequestingAgentId, - "--source-cluster-id", $ToolRequestingClusterId - ) - if ($ToolInvocationReason -ne "") { - $toolInvokeArgs += @("--reason", $ToolInvocationReason) - } - if ($ToolIntentId -ne "") { - $toolInvokeArgs += @("--intent-id", $ToolIntentId) - } - Invoke-Checked ` - -Label "submit daemon tool invocation intent" ` - -FilePath $verseQueryExe ` - -Arguments $toolInvokeArgs ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "tool-invoke.stderr.log") -} - -if ($Mode -eq "swarm-overview") { - $resultPath = Join-Path $artifactRoot "swarm-overview.stdout.json" - Invoke-Checked ` - -Label "read compact swarm overview" ` - -FilePath $verseQueryExe ` - -Arguments @( - "swarm-overview", - "--store", $localVerseReadStore, - "--runtime-id", $LocalVerseRuntimeId - ) ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "swarm-overview.stderr.log") -} - -if ($Mode -eq "service-policy-directory") { - $resultPath = Join-Path $artifactRoot "daemon-service-policy-directory.stdout.json" - Invoke-Checked ` - -Label "read daemon restart policy coverage" ` - -FilePath $verseQueryExe ` - -Arguments @( - "service-policy-directory", - "--store", $localVerseStore, - "--runtime-id", $LocalVerseRuntimeId - ) ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "daemon-service-policy-directory.stderr.log") -} - -if ($Mode -eq "service-plan") { - $resultPath = Join-Path $artifactRoot "daemon-service-plan.stdout.json" - $planArgs = @( - "service-plan", - "--store", $localVerseStore, - "--runtime-id", $LocalVerseRuntimeId, - "--daemon-id", $DaemonId, - "--scheduler-id", $SchedulerId, - "--service-id", $ServiceId, - "--loop-interval-seconds", "$LoopIntervalSeconds" - ) - if ($ServiceMaxIterations -gt 0) { - $planArgs += @("--max-iterations", "$ServiceMaxIterations") - } - Invoke-Checked ` - -Label "plan daemon supervisor service launch" ` - -FilePath $daemonSupervisorExe ` - -Arguments $planArgs ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "daemon-service-plan.stderr.log") -} - -if ($Mode -eq "service-launch") { - $resultPath = Join-Path $artifactRoot "daemon-service-launch.stdout.json" - $launchArgs = @( - "service-launch", - "--store", $localVerseStore, - "--runtime-id", $LocalVerseRuntimeId, - "--daemon-id", $DaemonId, - "--scheduler-id", $SchedulerId, - "--service-id", $ServiceId, - "--loop-interval-seconds", "$LoopIntervalSeconds" - ) - if ($ServiceMaxIterations -gt 0) { - $launchArgs += @("--max-iterations", "$ServiceMaxIterations", "--wait-child") - } - Invoke-Checked ` - -Label "launch daemon supervisor service loop" ` - -FilePath $daemonSupervisorExe ` - -Arguments $launchArgs ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "daemon-service-launch.stderr.log") -} - -if ($Mode -eq "service-runbook") { - $resultPath = Join-Path $artifactRoot "daemon-service-runbook.stdout.json" - $runbookPath = Join-Path $artifactRoot "epiphany-daemon-supervisor-service.ps1" - $runbookArgs = @( - "service-runbook", - "--store", $localVerseStore, - "--runtime-id", $LocalVerseRuntimeId, - "--daemon-id", $DaemonId, - "--scheduler-id", $SchedulerId, - "--service-id", $ServiceId, - "--loop-interval-seconds", "$LoopIntervalSeconds", - "--runbook-path", $runbookPath - ) - if ($ServiceMaxIterations -gt 0) { - $runbookArgs += @("--max-iterations", "$ServiceMaxIterations") - } - Invoke-Checked ` - -Label "write daemon supervisor service runbook" ` - -FilePath $daemonSupervisorExe ` - -Arguments $runbookArgs ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "daemon-service-runbook.stderr.log") -} - -if ($Mode -eq "service-tick") { - $resultPath = Join-Path $artifactRoot "daemon-service-tick.stdout.json" - Invoke-Checked ` - -Label "run one daemon supervisor scheduler tick" ` - -FilePath $daemonSupervisorExe ` - -Arguments @( - "tick", - "--store", $localVerseStore, - "--runtime-id", $LocalVerseRuntimeId, - "--daemon-id", $DaemonId, - "--scheduler-id", $SchedulerId - ) ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "daemon-service-tick.stderr.log") -} - -if ($Mode -eq "repo-persona-intake") { - $resultPath = Join-Path $artifactRoot "repo-persona-intake.stdout.json" - $repoWorkVerseStore = Join-Path $Workspace ".epiphany\local-verse.ccmp" - $repoPersonaInput = $PersonaInput - if ($repoPersonaInput -eq "") { - $repoPersonaInput = "Persona intake requests an Imagination plan for the next repo-swarm MVP cut." - } - $repoWorkArgs = @( - "persona-intake", - "--workspace", $Workspace, - "--epiphany-root", $Root, - "--item", $RepoWorkItem, - "--message", $repoPersonaInput, - "--local-verse-store", $repoWorkVerseStore, - "--runtime-id", $RepoWorkRuntimeId - ) - if ($CollaborationTopic -ne "") { - $repoWorkArgs += @("--topic", $CollaborationTopic) - } - Invoke-Checked ` - -Label "record repo Persona intake surface" ` - -FilePath $repoWorkExe ` - -Arguments $repoWorkArgs ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "repo-persona-intake.stderr.log") -} - -if ($Mode -eq "repo-swarm-run") { - $resultPath = Join-Path $artifactRoot "repo-swarm-run.stdout.json" - $repoWorkVerseStore = Join-Path $Workspace ".epiphany\local-verse.ccmp" - $repoSwarmArgs = @( - "run", - "--workspace", $Workspace, - "--epiphany-root", $Root, - "--local-verse-store", $repoWorkVerseStore, - "--runtime-id", $RepoWorkRuntimeId, - "--until", $RepoSwarmUntil, - "--max-iterations", "$RepoSwarmMaxIterations", - "--max-items", "$RepoWorkMaxItems" - ) - if ($RepoWorkDryRun.IsPresent) { - $repoSwarmArgs += @("--dry-run") - } - Invoke-Checked ` - -Label "run repo swarm queue mouth" ` - -FilePath $swarmExe ` - -Arguments $repoSwarmArgs ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "repo-swarm-run.stderr.log") -} - -if ($Mode -eq "repo-work-queue-run") { - $resultPath = Join-Path $artifactRoot "repo-work-queue-run.stdout.json" - $repoWorkVerseStore = Join-Path $Workspace ".epiphany\local-verse.ccmp" - $repoWorkArgs = @( - "queue-run", - "--workspace", $Workspace, - "--epiphany-root", $Root, - "--local-verse-store", $repoWorkVerseStore, - "--runtime-id", $RepoWorkRuntimeId, - "--max-items", "$RepoWorkMaxItems" - ) - if ($RepoWorkDryRun.IsPresent) { - $repoWorkArgs += @("--dry-run") - } - Invoke-Checked ` - -Label "run repo-work queue surface" ` - -FilePath $repoWorkExe ` - -Arguments $repoWorkArgs ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "repo-work-queue-run.stderr.log") -} - -if (@("managed-service-task-plan", "managed-service-task-install", "managed-service-task-status", "managed-service-task-start", "managed-service-task-stop", "managed-service-task-uninstall") -contains $Mode) { - $taskCommand = $Mode - $resultPath = Join-Path $artifactRoot "$Mode.stdout.json" - $taskArgs = @( - $taskCommand, - "--store", $localVerseStore, - "--runtime-id", $LocalVerseRuntimeId, - "--service-id", $ServiceId, - "--service-name", $ServiceName, - "--service-command", $daemonSupervisorExe, - "--cwd", $Root, - "--loop-interval-seconds", "$LoopIntervalSeconds" - ) - if ($ServiceMaxIterations -gt 0) { - $taskArgs += @("--max-iterations", "$ServiceMaxIterations") - } - Invoke-Checked ` - -Label "$Mode Idunn managed-service scheduled task" ` - -FilePath $daemonSupervisorExe ` - -Arguments $taskArgs ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "$Mode.stderr.log") -} - -if ($Mode -eq "repo-work-public-proof") { - $resultPath = Join-Path $artifactRoot "repo-work-public-proof.stdout.json" - $repoWorkVerseStore = Join-Path $Workspace ".epiphany\local-verse.ccmp" - $repoWorkArgs = @( - "export-proof", - "--workspace", $Workspace, - "--item", $RepoWorkItem, - "--local-verse-store", $repoWorkVerseStore, - "--runtime-id", $RepoWorkRuntimeId - ) - if ($RepoWorkPublicProofOutput -ne "") { - $repoWorkArgs += @("--output", $RepoWorkPublicProofOutput) - } - Invoke-Checked ` - -Label "export redacted repo-work public proof" ` - -FilePath $repoWorkExe ` - -Arguments $repoWorkArgs ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "repo-work-public-proof.stderr.log") -} - -if ($Mode -eq "repo-work-readiness") { - $resultPath = Join-Path $artifactRoot "repo-work-readiness.stdout.json" - $repoWorkArgs = @( - "readiness", - "--workspace", $Workspace, - "--item", $RepoWorkItem - ) - if ($RepoWorkPublicProof -ne "") { - $repoWorkArgs += @("--public-proof", $RepoWorkPublicProof) - } - if ($RepoWorkIdunnLifecycleReceipt -ne "") { - $repoWorkArgs += @("--idunn-lifecycle-receipt", $RepoWorkIdunnLifecycleReceipt) - } - if ($RepoWorkDeploymentAftercareReceipt -ne "") { - $repoWorkArgs += @("--deployment-aftercare-audit-receipt", $RepoWorkDeploymentAftercareReceipt) - } - if ($RepoWorkDeploymentAftercareReceiptRef -ne "") { - $repoWorkArgs += @("--deployment-aftercare-audit-receipt-ref", $RepoWorkDeploymentAftercareReceiptRef) - } - if ($RepoWorkToolDirectoryReceipt -ne "") { - $repoWorkArgs += @("--tool-directory-receipt", $RepoWorkToolDirectoryReceipt) - } - Invoke-Checked ` - -Label "read repo-work MVP readiness sight" ` - -FilePath $repoWorkExe ` - -Arguments $repoWorkArgs ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "repo-work-readiness.stderr.log") -} - -if ($Mode -eq "repo-deployment-config-audit") { - $resultPath = Join-Path $artifactRoot "repo-deployment-config-audit.stdout.json" - Invoke-Checked ` - -Label "audit repo Idunn deployment config" ` - -FilePath $repoWorkExe ` - -Arguments @( - "deployment-config-audit", - "--workspace", $Workspace - ) ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "repo-deployment-config-audit.stderr.log") -} - -if ($Mode -eq "repo-deployment-runbook") { - $resultPath = Join-Path $artifactRoot "repo-deployment-runbook.stdout.json" - Invoke-Checked ` - -Label "write repo Idunn deployment runbook" ` - -FilePath $repoWorkExe ` - -Arguments @( - "deployment-execution-runbook", - "--workspace", $Workspace, - "--remote", $RepoDeploymentRemote - ) ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "repo-deployment-runbook.stderr.log") -} - -if ($Mode -eq "repo-deployment-aftercare-audit") { - $resultPath = Join-Path $artifactRoot "repo-deployment-aftercare-audit.stdout.json" - $repoWorkVerseStore = Join-Path $Workspace ".epiphany\local-verse.ccmp" - $aftercareArgs = @( - "deployment-aftercare-audit", - "--workspace", $Workspace, - "--local-verse-store", $repoWorkVerseStore, - "--runtime-id", $RepoWorkRuntimeId - ) - if ($RepoDeploymentRunbookReceipt -ne "") { - $aftercareArgs += @("--runbook-receipt", $RepoDeploymentRunbookReceipt) - } - if ($RepoDeploymentIdunnReceipt -ne "") { - $aftercareArgs += @("--idunn-deployment-receipt", $RepoDeploymentIdunnReceipt) - } - if ($RepoDeploymentIdunnReceiptRef -ne "") { - $aftercareArgs += @("--idunn-deployment-receipt-ref", $RepoDeploymentIdunnReceiptRef) - } - if ($RepoDeploymentAftercareReceipt -ne "") { - $aftercareArgs += @("--aftercare-audit-receipt", $RepoDeploymentAftercareReceipt) - } - if ($RepoDeploymentAftercareReceiptRef -ne "") { - $aftercareArgs += @("--aftercare-audit-receipt-ref", $RepoDeploymentAftercareReceiptRef) - } - Invoke-Checked ` - -Label "audit repo Idunn deployment aftercare" ` - -FilePath $repoWorkExe ` - -Arguments $aftercareArgs ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "repo-deployment-aftercare-audit.stderr.log") -} - -if ($Mode -eq "repo-work-service-plan" -or $Mode -eq "repo-work-service-runbook" -or $Mode -eq "repo-work-service-launch" -or $Mode -eq "repo-work-service-audit") { - $repoWorkServiceId = $ServiceId - if ($repoWorkServiceId -eq "epiphany-daemon-supervisor-service") { - $repoWorkServiceId = "epiphany-repo-work-queue-runner" - } - $repoWorkSchedulerId = $SchedulerId - if ($repoWorkSchedulerId -eq "epiphany-daemon-supervisor") { - $repoWorkSchedulerId = "epiphany-repo-work-queue-runner" - } - $repoWorkVerseStore = Join-Path $Workspace ".epiphany\local-verse.ccmp" - $repoWorkServiceArgs = @( - "queue-run", - "--workspace", $Workspace, - "--epiphany-root", $Root, - "--local-verse-store", $repoWorkVerseStore, - "--runtime-id", $RepoWorkRuntimeId, - "--max-items", "$RepoWorkMaxItems" - ) - if ($RepoWorkDryRun.IsPresent) { - $repoWorkServiceArgs += @("--dry-run") - } - - $serviceCommandArgs = @( - "--store", $localVerseStore, - "--runtime-id", $LocalVerseRuntimeId, - "--daemon-id", $DaemonId, - "--scheduler-id", $repoWorkSchedulerId, - "--service-id", $repoWorkServiceId, - "--service-command", $repoWorkExe, - "--reason", "Idunn-owned lifecycle artifact for Self-owned repo work queue-run pulse.", - "--cwd", $Root - ) - foreach ($repoWorkServiceArg in $repoWorkServiceArgs) { - $serviceCommandArgs += @("--service-arg", $repoWorkServiceArg) - } - - if ($Mode -eq "repo-work-service-plan") { - $resultPath = Join-Path $artifactRoot "repo-work-service-plan.stdout.json" - Invoke-Checked ` - -Label "plan Idunn repo-work queue-run lifecycle" ` - -FilePath $daemonSupervisorExe ` - -Arguments (@("service-plan") + $serviceCommandArgs) ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "repo-work-service-plan.stderr.log") - } elseif ($Mode -eq "repo-work-service-runbook") { - $resultPath = Join-Path $artifactRoot "repo-work-service-runbook.stdout.json" - $runbookPath = Join-Path $artifactRoot "epiphany-repo-work-queue-runner.ps1" - Invoke-Checked ` - -Label "write Idunn repo-work queue-run runbook" ` - -FilePath $daemonSupervisorExe ` - -Arguments (@("service-runbook") + $serviceCommandArgs + @("--runbook-path", $runbookPath)) ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "repo-work-service-runbook.stderr.log") - } elseif ($Mode -eq "repo-work-service-launch") { - $resultPath = Join-Path $artifactRoot "repo-work-service-launch.stdout.json" - Invoke-Checked ` - -Label "launch Idunn repo-work queue-run lifecycle proof" ` - -FilePath $daemonSupervisorExe ` - -Arguments (@("service-launch") + $serviceCommandArgs + @("--wait-child")) ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "repo-work-service-launch.stderr.log") - } else { - $resultPath = Join-Path $artifactRoot "repo-work-service-audit.stdout.json" - Invoke-Checked ` - -Label "audit Idunn repo-work queue-run lifecycle proof" ` - -FilePath $daemonSupervisorExe ` - -Arguments (@("repo-work-service-audit") + $serviceCommandArgs) ` - -WorkingDirectory $Root ` - -StdoutPath $resultPath ` - -StderrPath (Join-Path $artifactRoot "repo-work-service-audit.stderr.log") - } -} - -if ($Mode -eq "plan") { - $resultPath = Join-Path $artifactRoot "coordinator-plan.stdout.json" - $planArgs = @( - "--codex-home", $CodexHome, - "--mcp-config", $mcpConfig, - "--cwd", $Workspace, - "--artifact-dir", (Join-Path $dogfoodRoot "coordinator"), - "--runtime-store", $runtimeStore, - "--mode", "plan", - "--max-steps", "1", - "--timeout-seconds", "$TimeoutSeconds" - ) - if ($ThreadId -ne "") { - $planArgs += @("--thread-id", $ThreadId, "--no-ephemeral") - } - Invoke-Checked ` - -Label "run coordinator plan" ` - -FilePath $coordinatorExe ` - -Arguments $planArgs ` - -WorkingDirectory $Root ` - -StdoutPath (Join-Path $artifactRoot "coordinator-plan.stdout.json") ` - -StderrPath (Join-Path $artifactRoot "coordinator-plan.stderr.log") -} - -if ($Mode -eq "smoke") { - $resultPath = Join-Path $artifactRoot "coordinator-smoke.stdout.json" - if (-not (Test-Path -LiteralPath $coordinatorSmokeExe)) { - throw "required binary not found: $coordinatorSmokeExe" - } - Invoke-Checked ` - -Label "run coordinator smoke" ` - -FilePath $coordinatorSmokeExe ` - -Arguments @( - "--artifact-root", (Join-Path $dogfoodRoot "coordinator-smoke"), - "--coordinator-exe", $coordinatorExe - ) ` - -WorkingDirectory $Root ` - -StdoutPath (Join-Path $artifactRoot "coordinator-smoke.stdout.json") ` - -StderrPath (Join-Path $artifactRoot "coordinator-smoke.stderr.log") -} - -if ($Mode -eq "mvp") { - if ($PersonaInput.Trim() -eq "") { - $PersonaInput = "Operator requested the local Epiphany MVP cycle. Persona should surface the swarm state, then the coordinator may continue bounded work and sleep afterward." - } - $PersonaArtifactDir = Join-Path $dogfoodRoot "Persona" - $characterArtifactDir = Join-Path $dogfoodRoot "character-loop" - $PersonaBubblePath = Join-Path $artifactRoot "Persona-bubble.stdout.json" - $characterTurnPath = Join-Path $artifactRoot "Persona-character-turn.stdout.json" - Invoke-Checked ` - -Label "project Persona character turn" ` - -FilePath $characterLoopExe ` - -Arguments @( - "turn", - "--role", "Persona", - "--agent-store", $agentStore, - "--artifact-dir", $characterArtifactDir, - "--stimulus", $PersonaInput, - "--source", "epiphany/local-mvp", - "--mode", "local-mvp-front-door", - "--mood", "attentive" - ) ` - -WorkingDirectory $Root ` - -StdoutPath $characterTurnPath ` - -StderrPath (Join-Path $artifactRoot "Persona-character-turn.stderr.log") - Invoke-Checked ` - -Label "write Persona Aquarium bubble" ` - -FilePath $PersonaExe ` - -Arguments @( - "bubble", - "--artifact-dir", $PersonaArtifactDir, - "--cultmesh-store", $localVerseStore, - "--runtime-id", $LocalVerseRuntimeId, - "--content", $PersonaInput, - "--source", "epiphany/local-mvp", - "--mood", "attentive" - ) ` - -WorkingDirectory $Root ` - -StdoutPath $PersonaBubblePath ` - -StderrPath (Join-Path $artifactRoot "Persona-bubble.stderr.log") -} - -if ($liveRuntimeMode) { - $resultPath = Join-Path $artifactRoot "coordinator-run.stdout.json" - $runArgs = @( - "--model-runtime-bin", $modelRuntimeExe, - "--tool-adapter-bin", $toolAdapterExe, - "--model-provider", $modelProvider, - "--codex-home", $CodexHome, - "--mcp-config", $mcpConfig, - "--cwd", $Workspace, - "--artifact-dir", (Join-Path $dogfoodRoot "coordinator"), - "--runtime-store", $runtimeStore, - "--mode", "run", - "--max-steps", "$MaxSteps", - "--timeout-seconds", "$TimeoutSeconds", - "--max-runtime-seconds", "$MaxRuntimeSeconds" - ) - if ($AutoReview) { - $runArgs += "--auto-review" - } - if ($SupersedeFailedResults) { - $runArgs += "--supersede-failed-results" - } - if ($Mode -eq "mvp" -and $ThreadId -eq "") { - $runArgs += @("--bootstrap-local-state", "--bootstrap-objective", $PersonaInput) - } - if ($Mode -eq "mvp" -or $NoEphemeral) { - $runArgs += "--no-ephemeral" - } - if ($ThreadId -ne "") { - $runArgs += @("--thread-id", $ThreadId, "--no-ephemeral") - } - Invoke-Checked ` - -Label "run coordinator loop" ` - -FilePath $coordinatorExe ` - -Arguments $runArgs ` - -WorkingDirectory $Root ` - -StdoutPath (Join-Path $artifactRoot "coordinator-run.stdout.json") ` - -StderrPath (Join-Path $artifactRoot "coordinator-run.stderr.log") -} - -$coordinatorSummaryPath = Join-Path (Join-Path $dogfoodRoot "coordinator") "coordinator-summary.json" -if (($Mode -eq "plan" -or $liveRuntimeMode) -and (Test-Path -LiteralPath $coordinatorSummaryPath)) { - Invoke-Checked ` - -Label "write CultMesh coordinator run receipt" ` - -FilePath $operatorRunExe ` - -Arguments @( - "coordinator-receipt", - "--store", $localVerseStore, - "--runtime-id", $LocalVerseRuntimeId, - "--run-id", $runId, - "--artifact-root", (Join-Path $dogfoodRoot "coordinator"), - "--coordinator-summary", $coordinatorSummaryPath, - "--coordinator-receipt-id", "$runId-coordinator" - ) ` - -WorkingDirectory $Root ` - -StdoutPath (Join-Path $artifactRoot "coordinator-run-receipt.stdout.json") ` - -StderrPath (Join-Path $artifactRoot "coordinator-run-receipt.stderr.log") -} - -if ($Mode -eq "mvp" -and -not $SkipSleep) { - $sleepArtifactDir = Join-Path $dogfoodRoot "sleep" - if (-not (Test-Path -LiteralPath $heartbeatStore)) { - Invoke-Checked ` - -Label "initialize heartbeat store" ` - -FilePath $heartbeatExe ` - -Arguments @( - "init", - "--store", $heartbeatStore - ) ` - -WorkingDirectory $Root ` - -StdoutPath (Join-Path $artifactRoot "heartbeat-init.stdout.json") ` - -StderrPath (Join-Path $artifactRoot "heartbeat-init.stderr.log") - } - Invoke-Checked ` - -Label "run sleep and dream routine" ` - -FilePath $heartbeatExe ` - -Arguments @( - "routine", - "--store", $heartbeatStore, - "--artifact-dir", $sleepArtifactDir, - "--agent-store", $agentStore, - "--source", "epiphany/local-mvp" - ) ` - -WorkingDirectory $Root ` - -StdoutPath (Join-Path $artifactRoot "sleep-routine.stdout.json") ` - -StderrPath (Join-Path $artifactRoot "sleep-routine.stderr.log") - Invoke-Checked ` - -Label "project heartbeat sleep status" ` - -FilePath $heartbeatExe ` - -Arguments @( - "status", - "--store", $heartbeatStore, - "--artifact-dir", $sleepArtifactDir - ) ` - -WorkingDirectory $Root ` - -StdoutPath (Join-Path $artifactRoot "sleep-status.stdout.json") ` - -StderrPath (Join-Path $artifactRoot "sleep-status.stderr.log") -} - -$summary = @" -# Epiphany Local Run - -- mode: $Mode -- root: $Root -- workspace: $Workspace -- threadId: $ThreadId -- codexHome: $CodexHome -- mcpConfig: $mcpConfig -- targetDir: $TargetDir -- artifactRoot: $artifactRoot -- dogfoodRoot: $dogfoodRoot -- statusBinary: $statusExe -- operatorRunBinary: $operatorRunExe -- operatorRunStore: $operatorRunStore -- operatorSnapshotBinary: $operatorSnapshotExe -- operatorSnapshotStore: $operatorSnapshotStore -- localVerseBinary: $verseQueryExe -- localVerseStore: $localVerseStore -- localVerseReadStore: $localVerseReadStore -- localVerseRuntimeId: $LocalVerseRuntimeId -- coordinatorBinary: $coordinatorExe -- runtimeStore: $runtimeStore -- modelRuntimeBinary: $modelRuntimeExe -- modelProvider: $modelProvider -- PersonaBinary: $PersonaExe -- characterLoopBinary: $characterLoopExe -- heartbeatBinary: $heartbeatExe - -This is an operator entrypoint over the current MVP shell. Status mode is -Epiphany-native and does not start Codex app-server; it writes native status, -operator snapshot, and a compact local Verse context read from CultMesh so -Aquarium/local-run can inspect the same policy/contract packet used for dynamic -prompt context. Coordinator plan/smoke/run modes still use the sealed Codex -compatibility bridge until their provider boundary is cut. MVP mode wraps that -bridge-equipped coordinator loop in the local product cycle: Persona front door, -bounded swarm work, then heartbeat sleep/dream maintenance. -"@ -Set-Content -LiteralPath (Join-Path $artifactRoot "README.md") -Value $summary -Encoding UTF8 - -Write-Host "" -if ($resultPath -ne "" -and (Test-Path -LiteralPath $resultPath)) { - try { - $result = Get-Content -Raw -LiteralPath $resultPath | ConvertFrom-Json - if ($Mode -eq "smoke") { - Write-Host "Smoke: cold=$($result.coldAction), pressure=$($result.pressureAction), privateBackendMutationRejected=$($result.directBackendCompletionRejected)" - } elseif ($Mode -eq "status") { - Write-Host "Status: thread=$($result.threadId), coordinator=$($result.coordinator.action), crrc=$($result.crrc.recommendation.action)" - } elseif ($Mode -eq "agent-state-soa") { - $agentRows = "none" - if ($null -ne $result.tuiRows -and $result.tuiRows.Count -gt 0) { - $agentRows = ($result.tuiRows -join "; ") - } - Write-Host "Agent state SoA: status=$($result.status), agents=$($result.agentCount), summaryRows=$($result.summarySoaTableRows), agentRows=$agentRows, privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "swarm-status") { - $daemonRows = "none" - $statusRows = "none" - if ($null -ne $result.tuiRows -and $result.tuiRows.Count -gt 0) { - $statusRows = ($result.tuiRows -join "; ") - } - if ($null -ne $result.rows -and $result.rows.Count -gt 0) { - $daemonRows = (($result.rows | ForEach-Object { - "$($_.displayName):$($_.status):$($_.daemonId):declaredPrivateVerseRoute=$($_.declaredPrivateVerseRoute):declaredEveRoute=$($_.declaredEveRoute)->$($result.wrapperMode)" - }) -join "; ") - } - Write-Host "Swarm status: status=$($result.status), observedDaemons=$($result.observedDaemonCount), nonReady=$($result.nonReadyCount), daemonRows=$daemonRows, statusRows=$statusRows, privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "cluster-topology") { - $topologyRows = "none" - if ($null -ne $result.tuiRows -and $result.tuiRows.Count -gt 0) { - $topologyRows = ($result.tuiRows -join "; ") - } - Write-Host "Cluster topology: status=$($result.status), declaredFaculties=$($result.declaredFacultyCount), declaredPrivateVerseRoutes=$($result.declaredPrivateVerseRouteCount), declaredDaemonTargets=$($result.declaredDaemonTargetCount), publicDiscussion=$($result.publicDiscussionClusterCount), topologyRows=$topologyRows, privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "eve-surfaces") { - $surfaceRows = "none" - if ($null -ne $result.tuiRows -and $result.tuiRows.Count -gt 0) { - $surfaceRows = ($result.tuiRows -join "; ") - } - $publicSurfaces = "none" - if ($null -ne $result.rows -and $result.rows.Count -gt 0) { - $publicSurfaceRows = @($result.rows | Where-Object { $_.publicPersonaDiscussionAllowed -eq $true } | ForEach-Object { - "$($_.displayName)=$($_.eveSurfaceId)@declaredPrivateVerseRoute=$($_.declaredPrivateVerseRoute)" - }) - if ($publicSurfaceRows.Count -gt 0) { - $publicSurfaces = ($publicSurfaceRows -join ",") - } - } - $repoWorkQueueRows = "none" - if ($null -ne $result.repoWorkQueueTuiRows -and $result.repoWorkQueueTuiRows.Count -gt 0) { - $repoWorkQueueRows = ($result.repoWorkQueueTuiRows -join "; ") - } - Write-Host "Eve surfaces: status=$($result.status), surfaces=$($result.surfaceCount), publicDiscussion=$($result.publicDiscussionSurfaceCount), repoWorkQueue=$($result.repoWorkQueueCount), repoWorkRows=$repoWorkQueueRows, surfaceRows=$surfaceRows, publicSurfaces=$publicSurfaces, connectCommand=$($result.connectionCommand), privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "eve-connect") { - $repoWorkQueueRows = "none" - if ($null -ne $result.repoWorkQueueTuiRows -and $result.repoWorkQueueTuiRows.Count -gt 0) { - $repoWorkQueueRows = ($result.repoWorkQueueTuiRows -join "; ") - } - Write-Host "Eve connection request: status=$($result.status), target=$($result.targetClusterId), surface=$($result.targetEveSurfaceId), intent=$($result.intentId), responseOwner=$($result.responseOwner), privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "collaboration-feedback") { - $feedbackRows = "none" - if ($null -ne $result.tuiRows -and $result.tuiRows.Count -gt 0) { - $feedbackRows = ($result.tuiRows -join "; ") - } - $publicRefs = "none" - if ($null -ne $result.publicDiscussionRefs -and $result.publicDiscussionRefs.Count -gt 0) { - $publicRefs = ($result.publicDiscussionRefs -join ",") - } - $candidateRefs = "none" - if ($null -ne $result.candidateActionRefs -and $result.candidateActionRefs.Count -gt 0) { - $candidateRefs = ($result.candidateActionRefs -join ",") - } - Write-Host "Collaboration feedback: status=$($result.status), feedback=$($result.feedbackId), consensus=$($result.consensusReceiptId), publicRefs=$publicRefs, candidateActions=$candidateRefs, consensusPacket=$($result.consensusPacketRef), adoptionGate=$($result.adoptionGate), feedbackRows=$feedbackRows, privateStateExposed=$($result.privateStateExposed)" - } elseif (@("persona-discord", "persona-reddit", "persona-other") -contains $Mode) { - $auditId = "none" - if ($null -ne $result.speechAudit -and $null -ne $result.speechAudit.auditId) { - $auditId = $result.speechAudit.auditId - } elseif ($null -ne $result.auditId) { - $auditId = $result.auditId - } - $decision = $result.decision - if ($null -eq $decision -or $decision -eq "") { - $decision = $result.status - } - $target = $result.target - if ($null -eq $target -or $target -eq "") { - $target = $result.requestedPublicTarget - } - if ($null -eq $target -or $target -eq "") { - $target = "none" - } - $bridgeAction = $result.bridgeActionId - if ($null -eq $bridgeAction -or $bridgeAction -eq "") { - $bridgeAction = "none" - } - Write-Host "Persona mouth: mode=$Mode, action=$PersonaMouthAction, decision=$decision, audit=$auditId, target=$target, bridgeAction=$bridgeAction, artifact=$resultPath" - } elseif ($Mode -eq "bifrost-publication") { - Write-Host "Bifrost publication request: status=$($result.status), intent=$($result.intentId), responseOwner=$($result.responseOwner), privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "bifrost-public-proof") { - $creditReceipts = "none" - if ($null -ne $result.creditReceiptIds -and $result.creditReceiptIds.Count -gt 0) { - $creditReceipts = ($result.creditReceiptIds -join ",") - } - $reviewReceipts = "none" - if ($null -ne $result.reviewReceiptIds -and $result.reviewReceiptIds.Count -gt 0) { - $reviewReceipts = ($result.reviewReceiptIds -join ",") - } - Write-Host "Bifrost public proof request: status=$($result.status), item=$($result.item), proof=$($result.publicProofId), sha256=$($result.publicProofSha256), responseOwner=$($result.responseOwner), privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "bifrost-artifact-acceptance") { - $changedPaths = "none" - if ($null -ne $result.changedPaths -and $result.changedPaths.Count -gt 0) { - $changedPaths = ($result.changedPaths -join ",") - } - $reviewReceipts = "none" - if ($null -ne $result.reviewReceiptIds -and $result.reviewReceiptIds.Count -gt 0) { - $reviewReceipts = ($result.reviewReceiptIds -join ",") - } - Write-Host "Artifact acceptance request: status=$($result.status), item=$($result.item), branch=$($result.sourceBranch), commit=$($result.commitSha), responseOwner=$($result.responseOwner), changedPaths=$changedPaths, privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "bifrost-metrics") { - $modelSpend = "none" - if ($null -ne $result.modelSpendReceiptIds -and $result.modelSpendReceiptIds.Count -gt 0) { - $modelSpend = ($result.modelSpendReceiptIds -join ",") - } - $reviewLoad = "none" - if ($null -ne $result.reviewLoadReceiptIds -and $result.reviewLoadReceiptIds.Count -gt 0) { - $reviewLoad = ($result.reviewLoadReceiptIds -join ",") - } - $creditReadback = "none" - if ($null -ne $result.creditReadbackReceiptIds -and $result.creditReadbackReceiptIds.Count -gt 0) { - $creditReadback = ($result.creditReadbackReceiptIds -join ",") - } - Write-Host "Metrics request: status=$($result.status), item=$($result.item), branch=$($result.sourceBranch), responseOwner=$($result.responseOwner), privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "daemon-survival-rehearsal") { - Write-Host "Daemon survival rehearsal: status=$($result.status), daemon=$($result.daemonId), scheduler=$($result.schedulerId), policy=$($result.policyStatus), serve=$($result.serveStatus), iterations=$($result.serveIterations), schedulerReceipt=$($result.schedulerReceiptId), serviceManagerMutated=$($result.serviceManagerMutated), elevated=$($result.requiresElevatedAuthority), privateStateExposed=$($result.privateStateExposed), smokeDir=$($result.smokeDir)" - } elseif ($Mode -eq "receipt-directory") { - $artifactHashes = "none" - $attentionRoutes = "none" - $receiptRows = "none" - if ($null -ne $result.tuiRows -and $result.tuiRows.Count -gt 0) { - $receiptRows = ($result.tuiRows -join "; ") - } - if ($null -ne $result.rows -and $result.rows.Count -gt 0) { - $presentArtifactHashes = @($result.rows | Where-Object { $_.artifactStatus -eq "present" } | ForEach-Object { - $artifactSha256 = $_.artifactSha256 - if ($null -eq $artifactSha256 -or $artifactSha256 -eq "") { - $artifactSha256 = "none" - } - "$($_.family):${artifactSha256}" - }) - if ($presentArtifactHashes.Count -gt 0) { - $artifactHashes = ($presentArtifactHashes -join "; ") - } - $attentionRouteRows = @($result.attentionRouteRows) - if ($attentionRouteRows.Count -gt 0) { - $attentionRoutes = ($attentionRouteRows -join "; ") - } - } - Write-Host "Receipt directory: status=$($result.status), rows=$($result.rowCount), present=$($result.presentRowCount), absent=$($result.absentRowCount), attention=$($result.attentionRowCount), attentionRoutes=$attentionRoutes, receiptRows=$receiptRows, artifactNone=$($result.artifactNoneCount), artifactExternalRef=$($result.artifactExternalRefCount), artifactPresent=$($result.artifactPresentCount), artifactMissing=$($result.artifactMissingCount), artifactHashes=$artifactHashes, privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "tool-directory") { - $toolRows = "none" - if ($null -ne $result.tuiRows -and $result.tuiRows.Count -gt 0) { - $toolRows = ($result.tuiRows -join "; ") - } - Write-Host "Tool directory: status=$($result.status), tools=$($result.toolCount), hostReady=$($result.hostReadyCount), hostAttention=$($result.hostAttentionCount), availableToAllAgents=$($result.invariants.availableToAllAgents), requiresReceipt=$($result.invariants.requiresReceipt), toolRows=$toolRows, privateStateExposed=$($result.invariants.privateStateExposed)" - } elseif ($Mode -eq "tool-invoke") { - Write-Host "Tool request: status=$($result.status), requester=$($result.requestingDisplayName), host=$($result.hostDisplayName), hostStatus=$($result.observedHostStatus), tool=$($result.toolName), intent=$($result.intentId), responseOwner=$($result.responseOwner), privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "swarm-overview") { - $attention = "none" - if ($null -ne $result.attentionDaemonIds -and $result.attentionDaemonIds.Count -gt 0) { - $attention = ($result.attentionDaemonIds -join ",") - } - $toolHostAttention = "none" - if ($null -ne $result.toolHostAttentionRows -and $result.toolHostAttentionRows.Count -gt 0) { - $toolHostAttention = (($result.toolHostAttentionRows | ForEach-Object { - "$($_.hostDaemonId):$($_.capabilityId):$($_.hostStatus)" - }) -join "; ") - } - $serviceLifecycleAttention = "none" - if ($null -ne $result.serviceLifecycleAttentionRows -and $result.serviceLifecycleAttentionRows.Count -gt 0) { - $serviceLifecycleAttention = (($result.serviceLifecycleAttentionRows | ForEach-Object { - $artifactStatus = $_.artifactStatus - if ($null -eq $artifactStatus -or $artifactStatus -eq "") { - $artifactStatus = "unknown" - } - "$($_.family):$($_.route):$($_.status):artifactStatus=${artifactStatus}" - }) -join "; ") - } - $actionQueue = "none" - if ($null -ne $result.swarmActionRows -and $result.swarmActionRows.Count -gt 0) { - $actionQueue = (($result.swarmActionRows | ForEach-Object { - $artifact = $_.operatorArtifactRef - if ($null -eq $artifact -or $artifact -eq "") { - $artifact = "none" - } - $artifactStatus = $_.operatorArtifactStatus - if ($null -eq $artifactStatus -or $artifactStatus -eq "") { - $artifactStatus = "unknown" - } - $artifactSha256 = $_.operatorArtifactSha256 - if ($null -eq $artifactSha256 -or $artifactSha256 -eq "") { - $artifactSha256 = "none" - } - $audit = $_.completionAuditWrapperMode - if ($null -eq $audit -or $audit -eq "") { - $audit = "none" - } - $aftercare = $_.operatorAftercareCommand - if ($null -eq $aftercare -or $aftercare -eq "") { - $aftercare = "none" - } - $command = $_.wrapperCommand - if ($null -eq $command -or $command -eq "") { - $command = "none" - } - $executionCommand = $_.operatorArtifactExecutionCommand - if ($null -eq $executionCommand -or $executionCommand -eq "") { - $executionCommand = "none" - } - $failedChecks = $_.serviceExecutionFailedCheckCount - if ($null -eq $failedChecks) { - $failedChecks = 0 - } - $missingChecks = $_.serviceExecutionMissingCheckCount - if ($null -eq $missingChecks) { - $missingChecks = 0 - } - $serviceId = $_.serviceId - if ($null -eq $serviceId -or $serviceId -eq "") { - $serviceId = "none" - } - $serviceRoute = $_.serviceRoute - if ($null -eq $serviceRoute -or $serviceRoute -eq "") { - $serviceRoute = "none" - } - $owner = $_.lifecycleOwner - if ($null -eq $owner -or $owner -eq "") { - $owner = "none" - } - $hostedBody = $_.hostedBody - if ($null -eq $hostedBody -or $hostedBody -eq "") { - $hostedBody = "none" - } - "$($_.priority):$($_.family):$($_.wrapperMode):$($_.status):owner=${owner}:hostedBody=${hostedBody}:service=${serviceId}:route=${serviceRoute}:command=${command}:mutates=$($_.mutatesState):elevated=$($_.requiresElevatedAuthority):failedChecks=${failedChecks}:missingChecks=${missingChecks}:artifactStatus=${artifactStatus}:sha256=${artifactSha256}:exec=${executionCommand}:audit=${audit}:aftercare=${aftercare}:artifact=$artifact" - }) -join "; ") - } - $serviceExecutionFailedChecks = Format-ServiceExecutionFailedChecks $result.serviceExecutionFailedCheckRows - $actionRows = Format-TuiRows $result.swarmActionTuiRows - $toolAttentionRows = Format-TuiRows $result.toolHostAttentionTuiRows - $serviceAttentionRows = Format-TuiRows $result.serviceLifecycleAttentionTuiRows - $serviceFailedCheckRows = Format-TuiRows $result.serviceExecutionFailedCheckTuiRows - $daemonRows = Format-TuiRows $result.daemonTuiRows - $toolRows = Format-TuiRows $result.toolTuiRows - $policyRows = Format-TuiRows $result.policyTuiRows - $repoWorkMapRows = Format-TuiRows $result.repoWorkMapTuiRows - $repoWorkSemanticRows = Format-TuiRows $result.repoWorkMapSemanticTuiRows - $repoWorkFamilyLensRows = Format-TuiRows $result.repoWorkMapFamilyLensTuiRows - $repoWorkPathLensRows = Format-TuiRows $result.repoWorkMapPathLensTuiRows - $repoWorkBranchLensRows = Format-TuiRows $result.repoWorkMapBranchLensTuiRows - $repoWorkStageLensRows = Format-TuiRows $result.repoWorkMapStageLensTuiRows - $repoWorkGateLensRows = Format-TuiRows $result.repoWorkMapGateLensTuiRows - $repoWorkClosureRows = Format-TuiRows $result.repoWorkMapClosureTuiRows - $repoWorkAcceptanceRows = Format-TuiRows $result.repoWorkMapAcceptanceTuiRows - $repoWorkOverviewRows = Format-TuiRows $result.repoWorkOverviewTuiRows - $repoWorkPublicProofRows = Format-TuiRows $result.repoWorkPublicProofTuiRows - $repoWorkReadinessRows = Format-TuiRows $result.repoWorkReadinessTuiRows - $repoWorkReadinessReviewRows = Format-TuiRows $result.repoWorkReadinessReviewTuiRows - $swarmOnlineTool = "none" - if ($null -ne $result.commands -and $null -ne $result.commands.wrapperInvokeSwarmOnlineRunbookTool) { - $swarmOnlineTool = $result.commands.wrapperInvokeSwarmOnlineRunbookTool - } - Write-Host "Swarm overview: status=$($result.status), liveness=$($result.livenessStatus), recovery=$($result.recoveryStatus), observedDaemons=$($result.observedDaemonCount), declaredFaculties=$($result.declaredFacultyCount), declaredPrivateVerseRoutes=$($result.declaredPrivateVerseRouteCount), declaredDaemonTargets=$($result.declaredDaemonTargetCount), surfaces=$($result.surfaceCount), tools=$($result.toolCount), nonReady=$($result.nonReadyDaemonCount), policyMissing=$($result.policyMissingCount), recommended=$($result.recommendedWrapperMode), serviceRecommended=$($result.serviceLifecycleRecommendedWrapperMode), swarmOnlineTool=$swarmOnlineTool, actionQueue=$actionQueue, actionRows=$actionRows, attention=$attention, daemonRows=$daemonRows, toolRows=$toolRows, policyRows=$policyRows, repoWorkMapRows=$repoWorkMapRows, repoWorkSemanticRows=$repoWorkSemanticRows, repoWorkFamilyLensRows=$repoWorkFamilyLensRows, repoWorkPathLensRows=$repoWorkPathLensRows, repoWorkBranchLensRows=$repoWorkBranchLensRows, repoWorkStageLensRows=$repoWorkStageLensRows, repoWorkGateLensRows=$repoWorkGateLensRows, repoWorkClosureRows=$repoWorkClosureRows, repoWorkAcceptanceRows=$repoWorkAcceptanceRows, repoWorkOverviewRows=$repoWorkOverviewRows, repoWorkPublicProofRows=$repoWorkPublicProofRows, repoWorkReadinessRows=$repoWorkReadinessRows, repoWorkReadinessReviewRows=$repoWorkReadinessReviewRows, toolHostAttention=$toolHostAttention, toolAttentionRows=$toolAttentionRows, serviceLifecycleAttention=$serviceLifecycleAttention, serviceAttentionRows=$serviceAttentionRows, serviceExecutionFailedChecks=$serviceExecutionFailedChecks, serviceFailedCheckRows=$serviceFailedCheckRows, privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "repo-persona-intake") { - $feedbackId = "none" - $consensusReceiptId = "none" - $candidateActions = "none" - if ($null -ne $result.feedback) { - $feedbackId = $result.feedback.feedbackId - $consensusReceiptId = $result.feedback.consensusReceiptId - if ($null -ne $result.feedback.candidateActionRefs -and @($result.feedback.candidateActionRefs).Count -gt 0) { - $candidateActions = (@($result.feedback.candidateActionRefs) -join "; ") - } - } - Write-Host "Repo Persona intake: status=$($result.status), item=$($result.item), speechAudit=$($result.speechAuditId), accept=$($result.acceptReceiptPath), feedback=$feedbackId, consensus=$consensusReceiptId, candidateActions=$candidateActions, next=$($result.nextSafeMove), privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "repo-swarm-run") { - $pulseRows = "none" - if ($null -ne $result.iterations -and @($result.iterations).Count -gt 0) { - $pulseRows = ((@($result.iterations) | ForEach-Object { - $items = "none" - if ($null -ne $_.outputs -and @($_.outputs).Count -gt 0) { - $items = ((@($_.outputs) | ForEach-Object { - "$($_.item):gateBefore=$($_.gateBefore):tick=$($_.tick.status)/$($_.tick.action)" - }) -join ",") - } - "$($_.pulse):$($_.queueRunStatus):actionable=$($_.actionableCount):items=$items" - }) -join "; ") - } - Write-Host "Repo swarm run: status=$($result.status), stop=$($result.stopReason), iterations=$($result.iterationCount), until=$RepoSwarmUntil, maxIterations=$RepoSwarmMaxIterations, maxItems=$RepoWorkMaxItems, pulses=$pulseRows, next=$($result.nextSafeMove), privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "repo-work-queue-run") { - $selectedRows = Format-TuiRows $result.selectedRows - $tickRows = "none" - if ($null -ne $result.outputs -and @($result.outputs).Count -gt 0) { - $tickRows = ((@($result.outputs) | ForEach-Object { - "$($_.item):gateBefore=$($_.gateBefore):tick=$($_.tick.status)/$($_.tick.action):next=$($_.tick.nextSafeMove)" - }) -join "; ") - } - Write-Host "Repo work queue run: status=$($result.status), queue=$($result.queueCount), actionable=$($result.actionableCount), maxItems=$($result.maxItems), dryRun=$($result.dryRun), selectedRows=$selectedRows, tickRows=$tickRows, privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "repo-work-public-proof") { - $changedPaths = "none" - if ($null -ne $result.publicProofBundle -and $null -ne $result.publicProofBundle.changedPaths -and @($result.publicProofBundle.changedPaths).Count -gt 0) { - $changedPaths = (@($result.publicProofBundle.changedPaths) -join ",") - } - $artifactRows = 0 - if ($null -ne $result.publicProofBundle -and $null -ne $result.publicProofBundle.artifactRows) { - $artifactRows = @($result.publicProofBundle.artifactRows).Count - } - $publicationRows = 0 - if ($null -ne $result.publicProofBundle -and $null -ne $result.publicProofBundle.publicationRows) { - $publicationRows = @($result.publicProofBundle.publicationRows).Count - } - $publicProofId = "none" - if ($null -ne $result.verseProjection -and $null -ne $result.verseProjection.publicProofId) { - $publicProofId = $result.verseProjection.publicProofId - } - Write-Host "Repo work public proof: status=$($result.status), item=$($result.item), proof=$publicProofId, branch=$($result.publicProofBundle.branch), commit=$($result.publicProofBundle.commitSha), paths=$changedPaths, gate=$($result.publicProofBundle.currentGate), blocker=$($result.publicProofBundle.blocker), artifacts=$artifactRows, publicationRows=$publicationRows, sha256=$($result.outputSha256), path=$($result.outputPath), privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "repo-work-readiness") { - $readinessRows = "none" - if ($null -ne $result.rows -and @($result.rows).Count -gt 0) { - $readinessRows = ((@($result.rows) | ForEach-Object { - $kind = $_.kind - if ($null -eq $kind -or $kind -eq "") { - $kind = "unknown" - } - $owner = $_.owner - if ($null -eq $owner -or $owner -eq "") { - $owner = "unknown" - } - $status = $_.status - if ($null -eq $status -or $status -eq "") { - $status = "unknown" - } - $schema = $_.requiredSchema - if ($null -eq $schema -or $schema -eq "") { - $schema = "unknown" - } - "${kind}:${status}:owner=${owner}:satisfied=$($_.satisfied):schema=${schema}" - }) -join "; ") - } - $missingKinds = "none" - if ($null -ne $result.rows -and @($result.rows).Count -gt 0) { - $missingKinds = ((@($result.rows) | Where-Object { $_.satisfied -ne $true } | ForEach-Object { $_.kind }) -join ",") - if ($missingKinds -eq "") { - $missingKinds = "none" - } - } - $verseProjection = "none" - if ($null -ne $result.verseProjection -and $null -ne $result.verseProjection.readinessId) { - $verseProjection = "$($result.verseProjection.readinessId):$($result.verseProjection.latestKey)" - } - Write-Host "Repo work readiness: status=$($result.status), item=$($result.item), missing=$($result.missingRequiredCount), missingKinds=$missingKinds, receipt=$($result.receiptPath), verseProjection=$verseProjection, rows=$readinessRows, sightOnly=$($result.authority.sightOnly), readinessApprovalAuthorized=$($result.authority.readinessApprovalAuthorized), publicationAuthorized=$($result.authority.publicationAuthorized), serviceLifecycleAuthority=$($result.authority.serviceLifecycleAuthority), handsActionAuthorized=$($result.authority.handsActionAuthorized), next=$($result.nextSafeMove), privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "repo-deployment-config-audit") { - $failed = "none" - if ($null -ne $result.assertions -and @($result.assertions).Count -gt 0) { - $failed = ((@($result.assertions) | Where-Object { $_.passed -ne $true } | ForEach-Object { $_.id }) -join ",") - if ($failed -eq "") { - $failed = "none" - } - } - Write-Host "Repo deployment config audit: status=$($result.status), readyForIdunnReview=$($result.readyForIdunnReview), daemonOwnsExecution=$($result.daemonOwnsExecution), failed=$failed, receipt=$($result.receiptPath), executionAuthorized=$($result.executionAuthorized), deploymentAuthority=$($result.deploymentAuthority), gitPushAuthority=$($result.gitPushAuthority), serviceLifecycleAuthority=$($result.serviceLifecycleAuthority), nextGate=$($result.nextGate), privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "repo-deployment-runbook") { - $artifactSha256 = Get-LocalArtifactSha256 $result.runbookPath - if ($artifactSha256 -eq "none" -and $null -ne $result.runbookSha256 -and $result.runbookSha256 -ne "") { - $artifactSha256 = $result.runbookSha256 - } - Write-Host "Repo deployment runbook: status=$($result.status), runbookWritten=$($result.runbookWritten), remote=$($result.remote), watchedRef=$($result.watchedRef), gitPush='$($result.gitPushCommand)', operatorCommand=$($result.operatorExecutionCommand), artifactSha256=$artifactSha256, mutatesRemoteWhenRun=$($result.mutatesRemoteWhenRun), requiresExplicitOperatorAuthority=$($result.requiresExplicitOperatorAuthority), executionAuthorized=$($result.executionAuthorized), deploymentAuthority=$($result.deploymentAuthority), gitPushAuthority=$($result.gitPushAuthority), serviceLifecycleAuthority=$($result.serviceLifecycleAuthority), path=$($result.runbookPath), privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "repo-deployment-aftercare-audit") { - $failed = "none" - if ($null -ne $result.assertions -and @($result.assertions).Count -gt 0) { - $failed = ((@($result.assertions) | Where-Object { $_.passed -ne $true } | ForEach-Object { $_.id }) -join ",") - if ($failed -eq "") { - $failed = "none" - } - } - $idunnDeployment = "none" - if ($null -ne $result.idunnDeploymentReceipt -and $null -ne $result.idunnDeploymentReceipt.receiptId) { - $idunnDeployment = "$($result.idunnDeploymentReceipt.receiptId):$($result.idunnDeploymentReceipt.status)" - } - $idunnAftercare = "none" - if ($null -ne $result.idunnAftercareAuditReceipt -and $null -ne $result.idunnAftercareAuditReceipt.receiptId) { - $idunnAftercare = "$($result.idunnAftercareAuditReceipt.receiptId):$($result.idunnAftercareAuditReceipt.status)" - } - Write-Host "Repo deployment aftercare audit: status=$($result.status), deploymentComplete=$($result.deploymentComplete), failed=$failed, idunnDeployment=$idunnDeployment, idunnAftercare=$idunnAftercare, localVerseStore=$($result.localVerseStore), runtimeId=$($result.runtimeId), receipt=$($result.receiptPath), executionAuthorized=$($result.executionAuthorized), deploymentAuthority=$($result.deploymentAuthority), gitPushAuthority=$($result.gitPushAuthority), serviceLifecycleAuthority=$($result.serviceLifecycleAuthority), privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "repo-work-service-plan") { - $plannedArgs = "none" - if ($null -ne $result.args -and $result.args.Count -gt 0) { - $plannedArgs = ($result.args -join " ") - } - Write-Host "Repo work service plan: service=$($result.serviceId), status=$($result.status), receipt=$($result.receiptId), command=$($result.command), args=$plannedArgs, owner=Idunn, hostedBody=repo-work, followUp=tools/epiphany_local_run.ps1 -Mode repo-work-service-runbook, aftercare=tools/epiphany_local_run.ps1 -Mode repo-work-queue-run -RepoWorkDryRun, privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "repo-work-service-runbook") { - $artifactSha256 = Get-LocalArtifactSha256 $result.runbookPath - $plannedArgs = "none" - if ($null -ne $result.args -and $result.args.Count -gt 0) { - $plannedArgs = ($result.args -join " ") - } - Write-Host "Repo work service runbook: service=$($result.serviceId), status=$($result.status), receipt=$($result.receiptId), command=$($result.command), args=$plannedArgs, owner=Idunn, hostedBody=repo-work, artifactSha256=$artifactSha256, elevatedRequired=false, aftercare=tools/epiphany_local_run.ps1 -Mode repo-work-queue-run -RepoWorkDryRun, path=$($result.runbookPath), privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "repo-work-service-launch") { - Write-Host "Repo work service launch: service=$($result.serviceId), status=$($result.status), processId=$($result.processId), exitCode=$($result.exitCode), receipt=$($result.receiptId), owner=Idunn, hostedBody=repo-work, waitChild=true, serviceManagerMutation=false, aftercare=tools/epiphany_local_run.ps1 -Mode repo-work-queue-run -RepoWorkDryRun, privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "repo-work-service-audit") { - $missing = "none" - if ($null -ne $result.missingChecks -and @($result.missingChecks).Count -gt 0) { - $missing = (@($result.missingChecks) -join ",") - } - $failed = "none" - if ($null -ne $result.failedChecks -and @($result.failedChecks).Count -gt 0) { - $failed = (@($result.failedChecks) -join ",") - } - Write-Host "Repo work service audit: service=$($result.serviceId), status=$($result.status), receipt=$($result.receiptId), plan=$($result.planStatus), runbook=$($result.runbookStatus), runbookArtifact=$($result.runbookArtifactStatus), launch=$($result.launchStatus), exitCode=$($result.launchExitCode), missing=$missing, failed=$failed, owner=Idunn, hostedBody=repo-work, serviceManagerMutation=false, nextSafeMove=$($result.nextSafeMove), privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "service-policy-directory") { - $policyRows = "none" - if ($null -ne $result.tuiRows -and $result.tuiRows.Count -gt 0) { - $policyRows = Format-TuiRows $result.tuiRows - } - Write-Host "Service policy directory: status=$($result.status), owner=$($result.lifecycleOwner), hostedBody=$($result.hostedBody), declaredDaemonTargets=$($result.declaredDaemonTargetCount), observedDaemons=$($result.observedDaemonCount), covered=$($result.coveredCount), enabled=$($result.enabledCount), disabled=$($result.disabledCount), missing=$($result.missingCount), attention=$($result.attentionCount), policyRows=$policyRows, privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "service-plan") { - $plannedArgs = "none" - if ($null -ne $result.args -and $result.args.Count -gt 0) { - $plannedArgs = ($result.args -join " ") - } - Write-Host "Service plan: service=$($result.serviceId), status=$($result.status), receipt=$($result.receiptId), command=$($result.command), args=$plannedArgs, followUp=tools/epiphany_local_run.ps1 -Mode service-runbook, aftercare=tools/epiphany_local_run.ps1 -Mode managed-service-task-status, privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "service-launch") { - Write-Host "Service launch: service=$($result.serviceId), status=$($result.status), processId=$($result.processId), exitCode=$($result.exitCode), receipt=$($result.receiptId), followUp=tools/epiphany_local_run.ps1 -Mode managed-service-task-plan, aftercare=tools/epiphany_local_run.ps1 -Mode service-tick, privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "swarm-poke-down") { - $pokeRows = "none" - if ($null -ne $result.pokeRows -and $result.pokeRows.Count -gt 0) { - $pokeRows = ($result.pokeRows -join "; ") - } - Write-Host "Swarm poke down: status=$($result.status), observed=$($result.observedDaemonCount), poked=$($result.pokedDaemonCount), pokeRows=$pokeRows, privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "swarm-triage") { - $attention = "none" - if ($null -ne $result.attentionDaemonIds -and $result.attentionDaemonIds.Count -gt 0) { - $attention = ($result.attentionDaemonIds -join ",") - } - $toolHostAttention = "none" - if ($null -ne $result.toolHostAttentionRows -and $result.toolHostAttentionRows.Count -gt 0) { - $toolHostAttention = (($result.toolHostAttentionRows | ForEach-Object { - "$($_.hostDaemonId):$($_.capabilityId):$($_.hostStatus)" - }) -join "; ") - } - $serviceLifecycleAttention = "none" - if ($null -ne $result.serviceLifecycleAttentionRows -and $result.serviceLifecycleAttentionRows.Count -gt 0) { - $serviceLifecycleAttention = (($result.serviceLifecycleAttentionRows | ForEach-Object { - $artifactStatus = $_.artifactStatus - if ($null -eq $artifactStatus -or $artifactStatus -eq "") { - $artifactStatus = "unknown" - } - "$($_.family):$($_.route):$($_.status):artifactStatus=${artifactStatus}" - }) -join "; ") - } - $actionQueue = "none" - if ($null -ne $result.swarmActionRows -and $result.swarmActionRows.Count -gt 0) { - $actionQueue = (($result.swarmActionRows | ForEach-Object { - $artifact = $_.operatorArtifactRef - if ($null -eq $artifact -or $artifact -eq "") { - $artifact = "none" - } - $artifactStatus = $_.operatorArtifactStatus - if ($null -eq $artifactStatus -or $artifactStatus -eq "") { - $artifactStatus = "unknown" - } - $artifactSha256 = $_.operatorArtifactSha256 - if ($null -eq $artifactSha256 -or $artifactSha256 -eq "") { - $artifactSha256 = "none" - } - $audit = $_.completionAuditWrapperMode - if ($null -eq $audit -or $audit -eq "") { - $audit = "none" - } - $aftercare = $_.operatorAftercareCommand - if ($null -eq $aftercare -or $aftercare -eq "") { - $aftercare = "none" - } - $command = $_.wrapperCommand - if ($null -eq $command -or $command -eq "") { - $command = "none" - } - $failedChecks = $_.serviceExecutionFailedCheckCount - if ($null -eq $failedChecks) { - $failedChecks = 0 - } - $missingChecks = $_.serviceExecutionMissingCheckCount - if ($null -eq $missingChecks) { - $missingChecks = 0 - } - $serviceId = $_.serviceId - if ($null -eq $serviceId -or $serviceId -eq "") { - $serviceId = "none" - } - $serviceRoute = $_.serviceRoute - if ($null -eq $serviceRoute -or $serviceRoute -eq "") { - $serviceRoute = "none" - } - $owner = $_.lifecycleOwner - if ($null -eq $owner -or $owner -eq "") { - $owner = "none" - } - $hostedBody = $_.hostedBody - if ($null -eq $hostedBody -or $hostedBody -eq "") { - $hostedBody = "none" - } - "$($_.priority):$($_.family):$($_.wrapperMode):$($_.status):owner=${owner}:hostedBody=${hostedBody}:service=${serviceId}:route=${serviceRoute}:command=${command}:mutates=$($_.mutatesState):elevated=$($_.requiresElevatedAuthority):failedChecks=${failedChecks}:missingChecks=${missingChecks}:artifactStatus=${artifactStatus}:sha256=${artifactSha256}:audit=${audit}:aftercare=${aftercare}:artifact=$artifact" - }) -join "; ") - } - $serviceExecutionFailedChecks = Format-ServiceExecutionFailedChecks $result.serviceExecutionFailedCheckRows - $actionRows = Format-TuiRows $result.swarmActionTuiRows - $toolAttentionRows = Format-TuiRows $result.toolHostAttentionTuiRows - $serviceAttentionRows = Format-TuiRows $result.serviceLifecycleAttentionTuiRows - $serviceFailedCheckRows = Format-TuiRows $result.serviceExecutionFailedCheckTuiRows - $daemonRows = Format-TuiRows $result.daemonTuiRows - $repoWorkMapRows = Format-TuiRows $result.repoWorkMapTuiRows - $repoWorkSemanticRows = Format-TuiRows $result.repoWorkMapSemanticTuiRows - $repoWorkFamilyLensRows = Format-TuiRows $result.repoWorkMapFamilyLensTuiRows - $repoWorkPathLensRows = Format-TuiRows $result.repoWorkMapPathLensTuiRows - $repoWorkBranchLensRows = Format-TuiRows $result.repoWorkMapBranchLensTuiRows - $repoWorkStageLensRows = Format-TuiRows $result.repoWorkMapStageLensTuiRows - $repoWorkGateLensRows = Format-TuiRows $result.repoWorkMapGateLensTuiRows - $repoWorkClosureRows = Format-TuiRows $result.repoWorkMapClosureTuiRows - $repoWorkAcceptanceRows = Format-TuiRows $result.repoWorkMapAcceptanceTuiRows - $repoWorkOverviewRows = Format-TuiRows $result.repoWorkOverviewTuiRows - $repoWorkPublicProofRows = Format-TuiRows $result.repoWorkPublicProofTuiRows - $repoWorkReadinessRows = Format-TuiRows $result.repoWorkReadinessTuiRows - $repoWorkReadinessReviewRows = Format-TuiRows $result.repoWorkReadinessReviewTuiRows - Write-Host "Swarm triage: status=$($result.status), overview=$($result.overviewStatus), liveness=$($result.livenessStatus), recovery=$($result.recoveryStatus), observedDaemons=$($result.observedDaemonCount), declaredFaculties=$($result.declaredFacultyCount), declaredPrivateVerseRoutes=$($result.declaredPrivateVerseRouteCount), declaredDaemonTargets=$($result.declaredDaemonTargetCount), recommended=$($result.recommendedWrapperMode), serviceRecommended=$($result.serviceLifecycleRecommendedWrapperMode), actionQueue=$actionQueue, actionRows=$actionRows, attention=$attention, daemonRows=$daemonRows, repoWorkMapRows=$repoWorkMapRows, repoWorkSemanticRows=$repoWorkSemanticRows, repoWorkFamilyLensRows=$repoWorkFamilyLensRows, repoWorkPathLensRows=$repoWorkPathLensRows, repoWorkBranchLensRows=$repoWorkBranchLensRows, repoWorkStageLensRows=$repoWorkStageLensRows, repoWorkGateLensRows=$repoWorkGateLensRows, repoWorkClosureRows=$repoWorkClosureRows, repoWorkAcceptanceRows=$repoWorkAcceptanceRows, repoWorkOverviewRows=$repoWorkOverviewRows, repoWorkPublicProofRows=$repoWorkPublicProofRows, repoWorkReadinessRows=$repoWorkReadinessRows, repoWorkReadinessReviewRows=$repoWorkReadinessReviewRows, toolHostAttention=$toolHostAttention, toolAttentionRows=$toolAttentionRows, serviceLifecycleAttention=$serviceLifecycleAttention, serviceAttentionRows=$serviceAttentionRows, serviceExecutionFailedChecks=$serviceExecutionFailedChecks, serviceFailedCheckRows=$serviceFailedCheckRows, poked=$($result.pokedDaemonCount), privateStateExposed=$($result.privateStateExposed)" - } elseif ($Mode -eq "service-runbook") { - $artifactSha256 = Get-LocalArtifactSha256 $result.runbookPath - Write-Host "Service runbook: service=$($result.serviceId), receipt=$($result.receiptId), artifactSha256=$artifactSha256, followUp=tools/epiphany_local_run.ps1 -Mode service-launch, aftercare=tools/epiphany_local_run.ps1 -Mode managed-service-task-plan, path=$($result.runbookPath)" - } elseif ($Mode -eq "service-tick") { - Write-Host "Scheduler tick: scheduler=$($result.schedulerId), daemonSelector=$($result.daemonId), status=$($result.status), outcomes=$($result.outcomeCount), restarted=$($result.restartedCount), refused=$($result.refusedCount), skipped=$($result.skippedCount), receipt=$($result.schedulerReceiptId), privateStateExposed=$($result.privateStateExposed)" - } elseif (@("managed-service-task-plan", "managed-service-task-install", "managed-service-task-status", "managed-service-task-start", "managed-service-task-stop", "managed-service-task-uninstall") -contains $Mode) { - Write-Host "Idunn scheduled task: task=$($result.taskName), status=$($result.status), receipt=$($result.receiptId), exitCode=$($result.exitCode), privateStateExposed=$($result.privateStateExposed)" - } else { - Write-Host "Coordinator: thread=$($result.threadId), finalAction=$($result.finalAction.action), runtimePresent=$($result.runtimeSpine.present)" - } - } catch { - Write-Host "Summary parse failed; inspect $resultPath" - } -} -$operatorRunReceiptArgs = @( - "receipt", - "--store", $operatorRunStore, - "--runtime-id", $LocalVerseRuntimeId, - "--run-id", $runId, - "--mode", $Mode, - "--result-path", $resultPath, - "--artifact-root", $artifactRoot, - "--dogfood-root", $dogfoodRoot, - "--operator-snapshot-store", $operatorSnapshotStore -) -if ($Mode -eq "status") { - $operatorRunReceiptArgs += @("--operator-snapshot-id", $operatorSnapshotId) -} -Invoke-Checked ` - -Label "write CultMesh operator run receipt" ` - -FilePath $operatorRunExe ` - -Arguments $operatorRunReceiptArgs ` - -WorkingDirectory $Root ` - -StdoutPath (Join-Path $artifactRoot "operator-run-receipt.stdout.json") ` - -StderrPath (Join-Path $artifactRoot "operator-run-receipt.stderr.log") -if ($shouldReadLocalVerse) { - Invoke-Checked ` - -Label "read local Verse context" ` - -FilePath $verseQueryExe ` - -Arguments @( - "query", - "--store", $localVerseStore, - "--runtime-id", $LocalVerseRuntimeId - ) ` - -WorkingDirectory $Root ` - -StdoutPath (Join-Path $artifactRoot "local-verse-context.json") ` - -StderrPath (Join-Path $artifactRoot "local-verse-context.final.stderr.log") -} -Write-Host "Epiphany local run complete." -Write-Host "Launcher artifacts: $artifactRoot" -Write-Host "Coordinator artifacts: $dogfoodRoot"