Expose service audit runbook command

This commit is contained in:
Meta Vens 2026-06-19 02:45:17 +01:00
commit d29565e60d
5 changed files with 86 additions and 3 deletions

View file

@ -11,6 +11,7 @@ use epiphany_core::EpiphanyCultMeshDaemonSchedulerReceiptEntry;
use epiphany_core::EpiphanyCultMeshDaemonServiceLifecycleReceiptEntry;
use epiphany_core::EpiphanyCultMeshDaemonStatusEntry;
use epiphany_core::EpiphanyLocalVerseContext;
use epiphany_core::EpiphanyServiceExecutionAuditReport;
use epiphany_core::epiphany_cluster_service_execution_audit_report;
use epiphany_core::epiphany_cultmesh_daemon_poke_intent_from_status;
use epiphany_core::epiphany_cultmesh_daemon_poke_receipt_for_intent;
@ -27,6 +28,8 @@ use epiphany_core::write_epiphany_cultmesh_daemon_service_lifecycle_receipt;
use epiphany_core::write_epiphany_cultmesh_daemon_status;
use serde_json::Value;
use serde_json::json;
use sha2::Digest;
use sha2::Sha256;
use std::env;
use std::fs;
use std::path::PathBuf;
@ -1092,6 +1095,8 @@ fn windows_service_execution_audit(args: Args) -> Result<()> {
.filter(|receipt| receipt.service_id == args.service_id)
.collect::<Vec<_>>();
let report = epiphany_service_execution_audit_report(&receipts);
let runbook_witness =
service_execution_runbook_witness(&report, "windows-service-execution-runbook");
let receipt = service_lifecycle_receipt(
&args,
"windows-service-execution-audit",
@ -1124,6 +1129,12 @@ fn windows_service_execution_audit(args: Args) -> Result<()> {
"receiptCount": report.receipt_count,
"missingCount": report.missing_count,
"failedCount": report.failed_count,
"runbookPath": runbook_witness.runbook_path,
"runbookSha256": runbook_witness.runbook_sha256,
"elevatedCommand": runbook_witness.elevated_command,
"requiresElevatedAuthority": runbook_witness.requires_elevated_authority,
"aftercareMode": "service-execution-audit",
"aftercareCommand": "tools/epiphany_local_run.ps1 -Mode service-execution-audit",
"checks": report.checks,
"privateStateExposed": written.private_state_exposed || report.private_state_exposed,
}))?
@ -1334,6 +1345,8 @@ fn cluster_windows_service_execution_audit(args: Args) -> Result<()> {
.filter(|receipt| receipt.service_id == args.service_id)
.collect::<Vec<_>>();
let report = epiphany_cluster_service_execution_audit_report(&receipts);
let runbook_witness =
service_execution_runbook_witness(&report, "cluster-windows-service-execution-runbook");
let receipt = service_lifecycle_receipt(
&args,
"cluster-windows-service-execution-audit",
@ -1365,6 +1378,12 @@ fn cluster_windows_service_execution_audit(args: Args) -> Result<()> {
"receiptCount": report.receipt_count,
"missingCount": report.missing_count,
"failedCount": report.failed_count,
"runbookPath": runbook_witness.runbook_path,
"runbookSha256": runbook_witness.runbook_sha256,
"elevatedCommand": runbook_witness.elevated_command,
"requiresElevatedAuthority": runbook_witness.requires_elevated_authority,
"aftercareMode": "cluster-service-execution-audit",
"aftercareCommand": "tools/epiphany_local_run.ps1 -Mode cluster-service-execution-audit",
"checks": report.checks,
"privateStateExposed": written.private_state_exposed || report.private_state_exposed,
}))?
@ -1471,6 +1490,57 @@ fn cluster_windows_service_execution_audit_smoke(args: Args) -> Result<()> {
Ok(())
}
struct ServiceExecutionRunbookWitness {
runbook_path: String,
runbook_sha256: String,
elevated_command: String,
requires_elevated_authority: bool,
}
fn service_execution_runbook_witness(
report: &EpiphanyServiceExecutionAuditReport,
runbook_action: &str,
) -> ServiceExecutionRunbookWitness {
let runbook_path = report
.checks
.iter()
.find(|check| check.action == runbook_action)
.and_then(|check| check.operator_artifact_ref.clone())
.unwrap_or_else(|| "none".to_string());
let runbook_sha256 = local_file_sha256(&runbook_path).unwrap_or_else(|| "none".to_string());
let elevated_command = if runbook_sha256 == "none" {
"none".to_string()
} else {
elevated_powershell_runbook_command(&runbook_path)
};
ServiceExecutionRunbookWitness {
runbook_path,
runbook_sha256,
elevated_command,
requires_elevated_authority: true,
}
}
fn local_file_sha256(path: &str) -> Option<String> {
if path.trim().is_empty() || path == "none" {
return None;
}
let path = PathBuf::from(path);
if !path.is_file() {
return None;
}
let bytes = fs::read(path).ok()?;
let digest = Sha256::digest(&bytes);
Some(format!("{digest:x}"))
}
fn elevated_powershell_runbook_command(path: &str) -> String {
let escaped = path.replace('\'', "''");
format!(
"Start-Process PowerShell -Verb RunAs -Wait -ArgumentList @('-NoProfile','-ExecutionPolicy','Bypass','-File','{escaped}')"
)
}
fn windows_service_execution_runbook(args: Args) -> Result<()> {
seed_epiphany_local_verse_context(
&args.store,
@ -2901,6 +2971,7 @@ impl Args {
let mut values = env::args().skip(1);
let command = values.next().unwrap_or_else(|| "reconcile".to_string());
let mut store = PathBuf::from(".epiphany-run/cultmesh/local-verse.ccmp");
let mut store_explicit = false;
let mut runtime_id = "epiphany-local".to_string();
let mut daemon_id = None;
let mut restart_command = None;
@ -2935,7 +3006,10 @@ impl Args {
while let Some(arg) = values.next() {
match arg.as_str() {
"--store" => store = PathBuf::from(values.next().context("missing --store value")?),
"--store" => {
store = PathBuf::from(values.next().context("missing --store value")?);
store_explicit = true;
}
"--runtime-id" => {
runtime_id = values.next().context("missing --runtime-id value")?
}
@ -3059,6 +3133,13 @@ impl Args {
}
}
if !store_explicit && command.contains("smoke") {
store = PathBuf::from(format!(
".epiphany-smoke/daemon-supervisor-{}/local-verse.ccmp",
sanitize_id(&command)
));
}
if let Some(parent) = store.parent() {
std::fs::create_dir_all(parent)?;
}

View file

@ -119,6 +119,7 @@ remembers doctrine; the branch remembers the blade.
- `tool-invoke` now embeds compact readbacks for common daemon-hosted tools. Status calls return `daemonStatusReadback`, Eve calls return `eveConnectionReadback`, Self `service-health` returns Idunn-owned `serviceHealthReadback`, and Hands/Soul authority tools return `authorityToolReadback` with gate, input contract, receipt contract, intent/receipt routes, host cluster, and private-state seal. This proves global tool ergonomics without moving action, verification, or daemon lifecycle ownership into the wrapper; live proofs routed Persona to Hands `repo-action` (`local-20260619-021628-143-7bf7f2e0`) and Soul `verify` (`local-20260619-021701-458-852cfa6f`) with `privateStateExposed=false`.
- `gjallar` is the non-mutating whole-Verse herald to run first when reorienting; `swarm-overview` remains its compatibility name. It locates agents, cluster/private-Verse topology, daemon liveness, Eve surfaces, and daemon tools in one compact report without opening private Verse state or full-context debug serialization.
- Idunn, not Codex/Self/Gjallar, owns daemon survival and service lifecycle physiology. `tools/epiphany_local_run.ps1 -Mode swarm-online-runbook` now generates an Idunn-owned service lifecycle runbook for the Epiphany hosted body: the artifact verifies each sealed child runbook artifact exists and matches its advertised SHA-256 before launching it with `Start-Process ... -PassThru`, records child exit codes, continues after child failure, runs aftercare audits through `tools\epiphany_local_run.ps1` with explicit arguments instead of string evaluation, and exits nonzero if any child or aftercare step fails. Its wrapper JSON and one-line summary print the exact swarm-level `elevatedCommand=Start-Process PowerShell -Verb RunAs -Wait ...`, parent artifact SHA-256, `verifiesChildArtifactSha256=True`, `usesExplicitAftercareArguments=True`, child/aftercare row counts, aftercare commands, owner, hosted body, and private-state seal; JSON now includes structured `childRunbookRows` and `aftercareRows` with service ids, routes, SHA-256s, audit modes, authority gates, failed-check counts, and private-state seals. Proof `local-20260619-013021-159-c93088be` generated and inspected that guard without mutating services. It still requires explicit elevated operator authority before service mutation.
- Service execution audits now expose the exact Idunn runbook route at the audit mouth: `cluster-service-execution-audit` and `service-execution-audit` JSON and wrapper summaries include the sealed runbook path, SHA-256, elevated `Start-Process PowerShell -Verb RunAs -Wait ...` command, aftercare command, and `requiresElevatedAuthority=True`. Daemon-supervisor `*smoke` commands default to `.epiphany-smoke/daemon-supervisor-<command>/local-verse.ccmp` unless `--store` is explicit, keeping synthetic smoke receipts out of the shared live local Verse.
- `swarm-triage` is the compact responsibility rite after sight: it no-ops when liveness is ready, carries blocked tool-host/service lifecycle attention plus an ordered action queue as sealed sight, and emits topology-aware typed daemon poke receipts only for current daemon liveness attention rows.
- `epiphany-openai-runtime` worker result ingress now parses assistant output directly into typed role/reorient ingress structs selected from the durable launch document, stores nested `statePatch` and `selfPatch` as typed MessagePack in `EpiphanyRuntimeRoleWorkerResult`, and no longer passes worker result data through generic `serde_json::Value`. JSON survives there only as the OpenAI text-output edge before typed deserialization.
- Agent self-memory review/application now has typed `AgentSelfPatch` core APIs. JSON-value selfPatch functions are compatibility wrappers for CLI/artifact ingress; native callers should parse once at the edge and then call `review_agent_self_patch_document` / `apply_agent_self_patch_document`. Role-result self-persistence no longer exports a JSON-value reviewer; the live API is `review_role_self_patch_document`.

Binary file not shown.

View file

@ -627,6 +627,7 @@ current_status:
- Wrapper `swarm-overview` and `swarm-triage` summaries now forward the Rust-owned compact TUI row families instead of rebuilding the global sight surface in PowerShell: `actionRows`, `daemonRows`, `toolRows` where present, `policyRows` where present, `toolAttentionRows`, `serviceAttentionRows`, and `serviceFailedCheckRows`. The older compatibility summaries still surface lifecycle receipt `artifactStatus`, local runbook `sha256`, operator aftercare command, and compact service execution failed-check anatomy. Live artifacts `local-20260618-210646-478-88b58f07` and `local-20260618-210646-474-2fb504ff` prove overview/triage print seven READY daemon rows, sixteen globally invokable receipt-gated tool rows, seven enabled restart-policy rows, two ordered service action rows, two service lifecycle attention rows, five sealed failed service-execution check rows, and `privateStateExposed=false`. The Rust-owned action queue now enumerates every sealed service-lifecycle attention receipt instead of only the selected global recommendation: live wrapper proof `local-20260618-214044-049-a63d008c` printed priority 40 `cluster-service-execution-audit` and priority 41 `service-execution-audit` non-mutating readback rows with `artifactStatus=external-ref`, `sha256=none`, service-manager artifact refs for cluster execution audit and single-service execution audit, and `privateStateExposed=false`, while leaving the elevated runbook authority rows unchanged. The failed-check anatomy now aggregates cluster and single-service execution audit reports too, carries service identity, and renders `followUp=<wrapper command>` for each failed/missing check so the compact row names the receipt-producing or readback route without opening raw audit payloads. Live wrapper proof `local-20260618-224925-221-8e114063` printed cluster readiness/install/start/audit/stop failures with follow-ups such as `cluster-service-execution-readiness`, `cluster-service-install-execute`, and `cluster-service-execution-audit`, plus supervisor-service missing checks with follow-ups such as `service-execution-readiness`, `service-install-execute`, `service-status`, and `service-reconcile`, all sealed with `privateStateExposed=false`. The action queue now carries scoped service-execution wound counters on service-lifecycle readback rows as well as elevated authority rows, so a compact row that says `attention` also says why: live Gjallar proof `local-20260619-012440-577-3f18a143` printed priority 40 `cluster-service-execution-audit` with `failedChecks=5` and priority 41 `service-execution-audit` with `failedChecks=6`, while both rows stayed non-mutating, non-elevated, Idunn-owned, and `privateStateExposed=false`. Source smoke seeds both cluster and single-service runbook witnesses and proves distinct authority rows. Live overview/triage proofs `local-20260618-221339-826-b1981064` and `local-20260618-221340-099-4b4baba8` printed priority 50 `cluster-service-execution-runbook` with `failedChecks=5`, `missingChecks=0`, SHA-256 `3239ac17008b1ce857970e40b17d37a1ecf1631a3a6636bb2b8678312fb604a0`, and aftercare `cluster-service-execution-audit`, plus priority 51 `service-execution-runbook` with `failedChecks=6`, `missingChecks=6`, SHA-256 `b213a3c11a8f11a03061fcd743f507edaa59bc6767257b79cef63329596375e7`, and aftercare `service-execution-audit`, with `privateStateExposed=false`. `SwarmActionRow` now exposes explicit `serviceId` and `serviceRoute` fields so agents can identify the body/action without parsing artifact paths or prose; Rust TUI and wrapper action queues render `service=<id>` and `route=<service::action>` for service lifecycle and service authority rows, while non-service rows render `none`. Source smoke guards both fields, and live overview/triage proofs `local-20260618-221912-231-805389bf` and `local-20260618-221912-282-a79ba8bd` showed the cluster and supervisor-service identities on readback and elevated runbook rows with `privateStateExposed=false`. `ReceiptDirectoryRow` now owns the same explicit `serviceId`/`serviceRoute` fields, so action rows consume typed service identity from the receipt-directory organ instead of parsing `route`; non-service receipt families carry `none`, while service lifecycle/runbook rows carry the typed body and route. Source smoke guards the fields, and live receipt-directory proof `local-20260618-222641-218-60106c79` printed `service=epiphany-cluster-daemon-services` and `service=epiphany-daemon-supervisor-service` on service attention/readiness rows and runbook witnesses with no private-state exposure.
- `SwarmActionRow` now carries `lifecycleOwner` and `hostedBody` so Gjallar/overview/triage action queues distinguish sight from daemon-keeper authority. Daemon liveness, restart-policy, service-lifecycle readback, and elevated service-control rows carry `Idunn` / `Epiphany`; non-lifecycle rows carry `none`. Rust TUI rows plus wrapper `actionQueue` render `owner=Idunn` and `hostedBody=Epiphany` for service readback and elevated service-control rows. Focused live proofs `local-20260619-004523-347-620b40e9` (`gjallar`) and `local-20260619-004523-003-a44ad06d` (`swarm-triage`) show the ownership fields on all service action rows with no daemon poke when liveness is ready and no private-state exposure. The broad `epiphany-verse-query smoke` Windows debug stack-overflow wound is now cut by running the CLI body on a larger worker-thread stack; direct smoke returns `status=ok` with seven clusters, seven daemon statuses, seventeen daemon tools, and the sealed latest receipt ids.
- Direct wrapper audit summaries now expose the same follow-up ergonomics as the global sight rows. `tools/epiphany_local_run.ps1 -Mode cluster-service-execution-audit` and `-Mode service-execution-audit` print `failed check rows` with `serviceId::action=status`, artifact ref, local artifact SHA-256 when present, and `followUp=<wrapper command>` for each failed/missing check. Live proofs `local-20260618-225923-468-d818951a` and `local-20260618-225923-529-7cfa2bb3` showed cluster readiness/install/start/audit/stop failures and supervisor-service readiness/install/start/status/reconcile/stop missing checks with actionable follow-up wrappers and `privateStateExposed=false`.
- Service execution audits now carry their own elevated runbook readback instead of relying on Gjallar or wrapper memory. `epiphany-daemon-supervisor windows-service-execution-audit` and `cluster-windows-service-execution-audit` return `runbookPath`, `runbookSha256`, `elevatedCommand`, `requiresElevatedAuthority`, `aftercareMode`, and `aftercareCommand` from the sealed runbook witness; the wrapper summaries print those fields directly for `service-execution-audit` and `cluster-service-execution-audit`. Live proofs `local-20260619-024011-505-a18982fa` and `local-20260619-024010-980-842fc3a6` showed incomplete audits with repaired runbook hashes, exact `Start-Process PowerShell -Verb RunAs -Wait ... -File <runbook>` commands, explicit aftercare, and no private-state exposure. `epiphany-daemon-supervisor` smoke commands now default to `.epiphany-smoke/daemon-supervisor-<command>/local-verse.ccmp` unless `--store` is explicit, so synthetic smoke receipts cannot become the latest live local Verse service proof by accident.
- Service lifecycle receipt history is now event-only: `load_epiphany_cultmesh_daemon_service_lifecycle_receipts` filters out the `epiphany-local/daemon-service-lifecycle-receipt/latest` mirror, while `load_latest_epiphany_cultmesh_daemon_service_lifecycle_receipt` remains the latest-state reader. Focused unit test `service_lifecycle_receipt_history_excludes_latest_mirror` proves two per-receipt writes return exactly two history records and latest points at the second. Fresh smoke `verse-query-lifecycle-history-filter-smoke` and live overview/triage `local-20260618-145632-497-efac6f0d` / `local-20260618-145632-321-867947af` prove compact swarm sights still expose service lifecycle attention plus the present elevated runbook artifact without daemon pokes or service mutation. Do not use a broad unscoped cargo test filter for this check on Windows; it fans out across bin harnesses and hit a broken pipe during this pass.
- `epiphany-verse-query seed-compact` is the compact seed receipt for wrappers and read-only rites. The older `seed` command still prints the full local Verse context for explicit debugging, but compact wrapper modes now avoid full `seed`/final `query` readback after the combined overview proof exposed Windows debug stack overflow from full-context serialization pressure.
next_action: Continue the swarm-readiness cuts toward elevated operator install/start execution and completed audit/readiness for the seven installed services. Run generated service execution/install/control runbooks only when the operator grants that authority. The current local Verse proves liveness readiness, scheduler-policy coverage, daemon-body invocation, per-organ launch artifact generation, per-organ install-plan generation, non-elevated install/start/stop execution refusal, read-only installed-service audit, cluster-wide service control planning, service-lifecycle attention surfacing, and cluster execution audit acceptance criteria: `swarm-overview` now reports `status=attention`, `livenessStatus=ready`, `recoveryStatus=attention`, `recommendedWrapperMode=swarm-online-runbook`, and `recommendedWrapperCommand=tools/epiphany_local_run.ps1 -Mode swarm-online-runbook`; `service-tick` can refresh all seven daemon heartbeats through sealed scheduler receipts; `cluster-service-runbook` writes seven explicit `epiphany-cluster-daemon serve` commands with `privateStateExposed=false`; `cluster-service-install-plan` writes seven distinct Windows service install commands with `privateStateExposed=false`; `cluster-service-install-execute`, `cluster-service-start-execute`, and `cluster-service-stop-execute` refuse as `execution-refused-not-elevated` with `executed=false` when the shell lacks service authority; `cluster-service-start-plan` and `cluster-service-stop-plan` emit seven non-mutating planned rows; `cluster-service-audit` currently reports `incomplete` with seven missing services in this non-installed environment; `service-execution-runbook` and `cluster-service-execution-runbook` write elevated wrapper sequences that continue after per-step failure, run final audits in `finally`, exit nonzero after audit when any step failed, and seal typed runbook receipts with aftercare args (`stepFailurePolicy`, `continueAfterStepFailure`, `nonzeroExitFailsStep`, `exitsNonzeroAfterFinalAudit`); and `service-execution-audit` plus `cluster-service-execution-audit` now require those runbook witnesses before considering elevated-ready/install/start/audit/status/reconcile/stop effect receipts. Preserve the existing typed local Verse surfaces: `service-plan`, `service-launch`, `service-runbook`, `cluster-service-runbook`, `cluster-service-install-plan`, `cluster-service-install-execute`, `cluster-service-audit`, `cluster-service-start-plan`, `cluster-service-stop-plan`, `cluster-service-start-execute`, `cluster-service-stop-execute`, `cluster-service-execution-readiness`, `cluster-service-execution-runbook`, `cluster-service-execution-audit`, `service-execution-runbook`, `service-execution-audit`, `service-tick`, `service-policy-directory`, `agent-state-soa`, `swarm-status`, `swarm-poke-down`, `swarm-triage`, `cluster-topology`, `eve-surfaces`, `eve-connect`, `collaboration-feedback`, `bifrost-publication`, `bifrost-ledger`, `receipt-directory`, `tool-directory`, `tool-invoke`, `swarm-overview`, `swarm-online-runbook`, `seed-compact`, service manager status/reconcile/readiness/control wrappers, compact daemon scheduler/policy receipt rows, daemon liveness-gated tool routing, Bifrost/Imagination chains, coordinator-run receipts, Hands gates, role review mirrors, and sealed agent-state SoA summary. Keep the seven-standing-faculty SoA, stale-turn repair, memory lifecycle, shared local Verse witnesses, and typed CultMesh Persona speech gate intact.

View file

@ -2322,7 +2322,7 @@ if ($resultPath -ne "" -and (Test-Path -LiteralPath $resultPath)) {
$artifactSha256 = Get-LocalArtifactSha256 $result.runbookPath
Write-Host "Cluster daemon service execution runbook: service=$($result.serviceId), status=$($result.status), finalAuditInFinally=$($result.finalAuditRunsInFinally), continueAfterStepFailure=$($result.continueAfterStepFailure), nonzeroExitFailsStep=$($result.nonzeroExitFailsStep), exitsNonzeroAfterFinalAudit=$($result.exitsNonzeroAfterFinalAudit), artifactSha256=$artifactSha256, elevatedCommand=$elevatedCommand, aftercare=tools/epiphany_local_run.ps1 -Mode cluster-service-execution-audit, path=$($result.runbookPath)"
} elseif ($Mode -eq "cluster-service-execution-audit") {
Write-Host "Cluster daemon service execution audit: service=$($result.serviceId), status=$($result.status), missing=$($result.missingCount), failed=$($result.failedCount), receipt=$($result.receiptId)"
Write-Host "Cluster daemon service execution audit: service=$($result.serviceId), status=$($result.status), missing=$($result.missingCount), failed=$($result.failedCount), receipt=$($result.receiptId), runbookSha256=$($result.runbookSha256), elevatedCommand=$($result.elevatedCommand), aftercare=$($result.aftercareCommand), requiresElevatedAuthority=$($result.requiresElevatedAuthority)"
$failedCheckRows = Format-ServiceExecutionFailedChecks @($result.checks | Where-Object { -not $_.ok })
$runbookWitnessChecks = @($result.checks | Where-Object { $_.ok -and $null -ne $_.operatorArtifactRef -and $_.operatorArtifactRef -ne "" -and $_.operatorArtifactRef -ne "none" })
if ($runbookWitnessChecks.Count -gt 0) {
@ -2372,7 +2372,7 @@ if ($resultPath -ne "" -and (Test-Path -LiteralPath $resultPath)) {
} elseif ($Mode -eq "service-execution-readiness") {
Write-Host "Service execution readiness: service=$($result.serviceId), name=$($result.serviceName), status=$($result.status), elevated=$($result.elevated), receipt=$($result.receiptId), followUp=tools/epiphany_local_run.ps1 -Mode service-execution-runbook, aftercare=tools/epiphany_local_run.ps1 -Mode service-execution-audit"
} elseif ($Mode -eq "service-execution-audit") {
Write-Host "Service execution audit: service=$($result.serviceId), name=$($result.serviceName), status=$($result.status), missing=$($result.missingCount), failed=$($result.failedCount), receipt=$($result.receiptId)"
Write-Host "Service execution audit: service=$($result.serviceId), name=$($result.serviceName), status=$($result.status), missing=$($result.missingCount), failed=$($result.failedCount), receipt=$($result.receiptId), runbookSha256=$($result.runbookSha256), elevatedCommand=$($result.elevatedCommand), aftercare=$($result.aftercareCommand), requiresElevatedAuthority=$($result.requiresElevatedAuthority)"
$failedCheckRows = Format-ServiceExecutionFailedChecks @($result.checks | Where-Object { -not $_.ok })
$runbookWitnessChecks = @($result.checks | Where-Object { $_.ok -and $null -ne $_.operatorArtifactRef -and $_.operatorArtifactRef -ne "" -and $_.operatorArtifactRef -ne "none" })
if ($runbookWitnessChecks.Count -gt 0) {