Make service command identity own execution intent

This commit is contained in:
Meta Vens 2026-07-12 20:37:37 +01:00
commit b03ab6265f
7 changed files with 175 additions and 124 deletions

View file

@ -3,27 +3,14 @@ use anyhow::Result;
use chrono::DateTime;
use chrono::Duration;
use chrono::Utc;
use epiphany_core::EPIPHANY_CULTMESH_DAEMON_RESTART_POLICY_SCHEMA_VERSION;
use epiphany_core::EPIPHANY_CULTMESH_DAEMON_SCHEDULER_RECEIPT_SCHEMA_VERSION;
use epiphany_core::EPIPHANY_CULTMESH_DAEMON_SERVICE_LIFECYCLE_RECEIPT_SCHEMA_VERSION;
use epiphany_core::EPIPHANY_CULTMESH_MANAGED_SERVICE_POLICY_SCHEMA_VERSION;
use epiphany_core::EpiphanyCultMeshDaemonRestartPolicyEntry;
use epiphany_core::EpiphanyCultMeshDaemonSchedulerReceiptEntry;
use epiphany_core::EpiphanyCultMeshDaemonServiceLifecycleReceiptEntry;
use epiphany_core::EpiphanyCultMeshManagedServicePolicyEntry;
use epiphany_core::EpiphanyCultMeshSwarmBrakeEntry;
use epiphany_core::EpiphanyCultMeshDaemonStatusEntry;
use epiphany_core::EpiphanyLocalVerseContext;
use epiphany_core::EpiphanyProcessObservation as ProcessObservation;
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;
use epiphany_core::epiphany_service_execution_audit_report;
use epiphany_core::load_epiphany_cultmesh_daemon_restart_policy;
use epiphany_core::load_epiphany_cultmesh_daemon_service_lifecycle_receipts;
use epiphany_core::load_epiphany_cultmesh_managed_service_policy;
use epiphany_core::load_epiphany_cultmesh_managed_service_policies;
use epiphany_core::load_epiphany_cultmesh_managed_service_policy;
use epiphany_core::load_epiphany_cultmesh_swarm_brake;
use epiphany_core::observe_native_process as observe_process;
use epiphany_core::query_epiphany_local_verse_context;
@ -33,10 +20,23 @@ 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_scheduler_receipt;
use epiphany_core::write_epiphany_cultmesh_daemon_service_lifecycle_receipt;
use epiphany_core::write_epiphany_cultmesh_managed_service_policy;
use epiphany_core::write_epiphany_cultmesh_daemon_status;
use serde_json::Value;
use epiphany_core::write_epiphany_cultmesh_managed_service_policy;
use epiphany_core::EpiphanyCultMeshDaemonRestartPolicyEntry;
use epiphany_core::EpiphanyCultMeshDaemonSchedulerReceiptEntry;
use epiphany_core::EpiphanyCultMeshDaemonServiceLifecycleReceiptEntry;
use epiphany_core::EpiphanyCultMeshDaemonStatusEntry;
use epiphany_core::EpiphanyCultMeshManagedServicePolicyEntry;
use epiphany_core::EpiphanyCultMeshSwarmBrakeEntry;
use epiphany_core::EpiphanyLocalVerseContext;
use epiphany_core::EpiphanyProcessObservation as ProcessObservation;
use epiphany_core::EpiphanyServiceExecutionAuditReport;
use epiphany_core::EPIPHANY_CULTMESH_DAEMON_RESTART_POLICY_SCHEMA_VERSION;
use epiphany_core::EPIPHANY_CULTMESH_DAEMON_SCHEDULER_RECEIPT_SCHEMA_VERSION;
use epiphany_core::EPIPHANY_CULTMESH_DAEMON_SERVICE_LIFECYCLE_RECEIPT_SCHEMA_VERSION;
use epiphany_core::EPIPHANY_CULTMESH_MANAGED_SERVICE_POLICY_SCHEMA_VERSION;
use serde_json::json;
use serde_json::Value;
use sha2::Digest;
use sha2::Sha256;
use std::env;
@ -55,7 +55,7 @@ fn main() -> Result<()> {
"tick" | "schedule" | "reconcile-all" => tick(args),
"serve" | "loop" | "daemon" => serve(args),
"managed-service-serve" | "service-desired-state-serve" => managed_service_serve(args),
"service-plan" | "install-service" => service_plan(args),
"service-plan" => service_plan(args),
"service-launch" | "launch-service" | "start-service" => service_launch(args),
"managed-service-policy" | "service-desired-state" => managed_service_policy(args),
"managed-service-read" | "service-desired-state-read" => managed_service_read(args),
@ -67,10 +67,16 @@ fn main() -> Result<()> {
| "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-windows-service-install"
| "cluster-service-install-plan"
| "cluster-service-install-execute"
| "cluster-daemon-install-plan" => cluster_windows_service_install(args),
"cluster-service-install-plan" | "cluster-daemon-install-plan" => {
let mut args = args;
args.execute_install = false;
cluster_windows_service_install(args)
}
"cluster-service-install-execute" => {
let mut args = args;
args.execute_install = true;
cluster_windows_service_install(args)
}
"cluster-windows-service-audit" | "cluster-service-audit" | "cluster-service-readiness" => {
cluster_windows_service_audit(args)
}
@ -93,7 +99,14 @@ fn main() -> Result<()> {
| "cluster-service-execution-audit-smoke" => {
cluster_windows_service_execution_audit_smoke(args)
}
"windows-service-install" | "service-install-windows" | "service-install-plan" => {
"service-install-plan" => {
let mut args = args;
args.execute_install = false;
windows_service_install(args)
}
"service-install-execute" => {
let mut args = args;
args.execute_install = true;
windows_service_install(args)
}
"windows-service-execution-readiness"
@ -192,23 +205,24 @@ fn managed_service_serve(args: Args) -> Result<()> {
let mut iteration = 0_u64;
loop {
iteration = iteration.saturating_add(1);
let policies = load_epiphany_cultmesh_managed_service_policies(
&args.store,
args.runtime_id.clone(),
)?;
let policies =
load_epiphany_cultmesh_managed_service_policies(&args.store, args.runtime_id.clone())?;
for policy in &policies {
let mut service_args = args.clone();
service_args.service_id = policy.service_id.clone();
managed_service_reconcile(service_args)?;
}
println!("{}", serde_json::to_string_pretty(&json!({
"schemaVersion": "epiphany.cultmesh.managed_service_scheduler_pulse.v0",
"status": "completed",
"owner": "Idunn",
"iteration": iteration,
"policyCount": policies.len(),
"privateStateExposed": false,
}))?);
println!(
"{}",
serde_json::to_string_pretty(&json!({
"schemaVersion": "epiphany.cultmesh.managed_service_scheduler_pulse.v0",
"status": "completed",
"owner": "Idunn",
"iteration": iteration,
"policyCount": policies.len(),
"privateStateExposed": false,
}))?
);
if args.max_iterations != 0 && iteration >= args.max_iterations {
break;
}
@ -374,10 +388,16 @@ fn managed_service_policy(args: Args) -> Result<()> {
)?;
let command = service_command_path(&args)?;
let stdout_artifact = args.stdout_artifact.clone().unwrap_or_else(|| {
PathBuf::from(format!(".epiphany-run/services/{}.stdout.log", args.service_id))
PathBuf::from(format!(
".epiphany-run/services/{}.stdout.log",
args.service_id
))
});
let stderr_artifact = args.stderr_artifact.clone().unwrap_or_else(|| {
PathBuf::from(format!(".epiphany-run/services/{}.stderr.log", args.service_id))
PathBuf::from(format!(
".epiphany-run/services/{}.stderr.log",
args.service_id
))
});
let latest = load_epiphany_cultmesh_daemon_service_lifecycle_receipts(
&args.store,
@ -417,21 +437,24 @@ fn managed_service_policy(args: Args) -> Result<()> {
args.runtime_id.clone(),
policy,
)?;
println!("{}", serde_json::to_string_pretty(&json!({
"schemaVersion": written.schema_version,
"status": "written",
"policyId": written.policy_id,
"serviceId": written.service_id,
"ownerDaemonId": written.owner_daemon_id,
"enabled": written.enabled,
"restartMode": written.restart_mode,
"command": written.command,
"args": written.args,
"stdoutArtifact": written.stdout_artifact,
"stderrArtifact": written.stderr_artifact,
"latestLifecycleReceiptId": latest.as_ref().map(|receipt| receipt.receipt_id.as_str()),
"privateStateExposed": written.private_state_exposed,
}))?);
println!(
"{}",
serde_json::to_string_pretty(&json!({
"schemaVersion": written.schema_version,
"status": "written",
"policyId": written.policy_id,
"serviceId": written.service_id,
"ownerDaemonId": written.owner_daemon_id,
"enabled": written.enabled,
"restartMode": written.restart_mode,
"command": written.command,
"args": written.args,
"stdoutArtifact": written.stdout_artifact,
"stderrArtifact": written.stderr_artifact,
"latestLifecycleReceiptId": latest.as_ref().map(|receipt| receipt.receipt_id.as_str()),
"privateStateExposed": written.private_state_exposed,
}))?
);
Ok(())
}
@ -455,31 +478,34 @@ fn managed_service_read(args: Args) -> Result<()> {
.map(observe_process)
.transpose()?
.unwrap_or(ProcessObservation::Missing);
println!("{}", serde_json::to_string_pretty(&json!({
"schemaVersion": "epiphany.cultmesh.managed_service_readback.v0",
"status": "desired-state-ready",
"serviceId": policy.service_id,
"ownerDaemonId": policy.owner_daemon_id,
"desired": {
"enabled": policy.enabled,
"restartMode": policy.restart_mode,
"command": policy.command,
"args": policy.args,
"cwd": policy.cwd,
"stdoutArtifact": policy.stdout_artifact,
"stderrArtifact": policy.stderr_artifact,
},
"latestLifecycle": latest.as_ref().map(|receipt| json!({
"receiptId": receipt.receipt_id,
"status": receipt.status,
"action": receipt.action,
"processId": receipt.process_id,
"startedAtUtc": receipt.started_at_utc,
"completedAtUtc": receipt.completed_at_utc,
})),
"processObservation": process_observation.label(),
"privateStateExposed": false,
}))?);
println!(
"{}",
serde_json::to_string_pretty(&json!({
"schemaVersion": "epiphany.cultmesh.managed_service_readback.v0",
"status": "desired-state-ready",
"serviceId": policy.service_id,
"ownerDaemonId": policy.owner_daemon_id,
"desired": {
"enabled": policy.enabled,
"restartMode": policy.restart_mode,
"command": policy.command,
"args": policy.args,
"cwd": policy.cwd,
"stdoutArtifact": policy.stdout_artifact,
"stderrArtifact": policy.stderr_artifact,
},
"latestLifecycle": latest.as_ref().map(|receipt| json!({
"receiptId": receipt.receipt_id,
"status": receipt.status,
"action": receipt.action,
"processId": receipt.process_id,
"startedAtUtc": receipt.started_at_utc,
"completedAtUtc": receipt.completed_at_utc,
})),
"processObservation": process_observation.label(),
"privateStateExposed": false,
}))?
);
Ok(())
}
@ -504,26 +530,32 @@ fn managed_service_reconcile(mut args: Args) -> Result<()> {
.transpose()?
.unwrap_or(ProcessObservation::Missing);
if !policy.enabled || policy.restart_mode == "never" {
println!("{}", serde_json::to_string_pretty(&json!({
"schemaVersion": "epiphany.cultmesh.managed_service_reconcile.v0",
"status": "disabled",
"serviceId": policy.service_id,
"processObservation": observation.label(),
"restarted": false,
"privateStateExposed": false,
}))?);
println!(
"{}",
serde_json::to_string_pretty(&json!({
"schemaVersion": "epiphany.cultmesh.managed_service_reconcile.v0",
"status": "disabled",
"serviceId": policy.service_id,
"processObservation": observation.label(),
"restarted": false,
"privateStateExposed": false,
}))?
);
return Ok(());
}
if observation == ProcessObservation::Alive {
println!("{}", serde_json::to_string_pretty(&json!({
"schemaVersion": "epiphany.cultmesh.managed_service_reconcile.v0",
"status": "observed-alive",
"serviceId": policy.service_id,
"processId": latest.as_ref().and_then(|receipt| receipt.process_id),
"processObservation": observation.label(),
"restarted": false,
"privateStateExposed": false,
}))?);
println!(
"{}",
serde_json::to_string_pretty(&json!({
"schemaVersion": "epiphany.cultmesh.managed_service_reconcile.v0",
"status": "observed-alive",
"serviceId": policy.service_id,
"processId": latest.as_ref().and_then(|receipt| receipt.process_id),
"processObservation": observation.label(),
"restarted": false,
"privateStateExposed": false,
}))?
);
return Ok(());
}
if policy.restart_mode == "on-failure"
@ -531,14 +563,17 @@ fn managed_service_reconcile(mut args: Args) -> Result<()> {
receipt.status == "completed" && receipt.exit_code.unwrap_or_default() == 0
})
{
println!("{}", serde_json::to_string_pretty(&json!({
"schemaVersion": "epiphany.cultmesh.managed_service_reconcile.v0",
"status": "completed-no-restart",
"serviceId": policy.service_id,
"processObservation": observation.label(),
"restarted": false,
"privateStateExposed": false,
}))?);
println!(
"{}",
serde_json::to_string_pretty(&json!({
"schemaVersion": "epiphany.cultmesh.managed_service_reconcile.v0",
"status": "completed-no-restart",
"serviceId": policy.service_id,
"processObservation": observation.label(),
"restarted": false,
"privateStateExposed": false,
}))?
);
return Ok(());
}
if !args.force && policy.cooldown_seconds > 0 {
@ -548,16 +583,19 @@ fn managed_service_reconcile(mut args: Args) -> Result<()> {
{
let elapsed = Utc::now().signed_duration_since(started.with_timezone(&Utc));
if elapsed < Duration::seconds(policy.cooldown_seconds) {
println!("{}", serde_json::to_string_pretty(&json!({
"schemaVersion": "epiphany.cultmesh.managed_service_reconcile.v0",
"status": "cooling-down",
"serviceId": policy.service_id,
"processObservation": observation.label(),
"cooldownSeconds": policy.cooldown_seconds,
"elapsedSeconds": elapsed.num_seconds(),
"restarted": false,
"privateStateExposed": false,
}))?);
println!(
"{}",
serde_json::to_string_pretty(&json!({
"schemaVersion": "epiphany.cultmesh.managed_service_reconcile.v0",
"status": "cooling-down",
"serviceId": policy.service_id,
"processObservation": observation.label(),
"cooldownSeconds": policy.cooldown_seconds,
"elapsedSeconds": elapsed.num_seconds(),
"restarted": false,
"privateStateExposed": false,
}))?
);
return Ok(());
}
}
@ -3265,7 +3303,8 @@ fn service_lifecycle_receipt(
let receipt_id = args.receipt_id.clone().unwrap_or_else(|| {
let base = format!(
"daemon-service-lifecycle-receipt-{}-{}",
sanitize_id(&args.service_id), sanitize_id(action)
sanitize_id(&args.service_id),
sanitize_id(action)
);
if action == "launch" {
format!("{base}-{}", started_at.timestamp_millis())

View file

@ -93,6 +93,14 @@
- Receipt status is derived as `completed`; callers cannot submit status.
- Forbidden evidence: missing, non-JSON, pre-intent, out-of-root, or mismatched-run artifacts.
## Daemon-supervisor install authority boundary (2026-07-12)
- Plan commands force non-execution and may only write planned artifacts/receipts.
- Execute commands force execution intent and must pass the existing elevation gate before service-manager mutation.
- Wrapper command identity matches the Rust command exactly; no hidden `--execute-install` switch selects reality.
- Ambiguous install aliases are not accepted.
- Negative proof: hostile plan flag cannot mutate; execute without elevation produces an explicit refusal receipt.
This is the source-grounded map of the live machine. Historical route and
bridge anatomy belongs in git history and evidence ledgers, not here.

View file

@ -44,6 +44,10 @@ The monolithic `epiphany-verse-query smoke` fixture writer can no longer accept
`epiphany-operator-run receipt` no longer accepts `--status`. It loads the latest operator intent and requires matching run id/mode, canonicalizes the result and artifact-root paths, requires the result to be a file contained under the artifact root, parses it as JSON, and rejects it if its modification time predates the intent. Only then does it derive `status=completed`. The wrapper no longer passes a completion assertion. Negative proof rejects caller status and out-of-root artifacts; positive proof uses a post-intent JSON artifact. Next audit daemon-supervisor planning/rehearsal receipts against actual service/process evidence.
## 2026-07-12 daemon-supervisor plan/execute identity
Install command names now determine execution authority. `service-install-plan` and `cluster-service-install-plan` force `execute_install=false` even if a hostile flag is supplied. `service-install-execute` and `cluster-service-install-execute` force `execute_install=true` without a separate flag. Ambiguous aliases were deleted, and the wrapper invokes exact command names instead of always invoking plan plus `--execute-install`. Proof in the non-elevated shell: hostile plan remained `planned` / `executed=false`; execute without a flag returned `execution-refused-not-elevated` / `executed=false`. Receipt status now follows an inspectable command boundary rather than hidden argument combination.
This is the re-entry rite for `E:\Projects\EpiphanyAgent`: the waking chant for
the local machine-spirit before it touches the forge.

View file

@ -19,9 +19,9 @@ A forged operator-status artifact containing `intentId=forged-intent`, `receiptI
## Remaining scrutiny
1. Inspect daemon-supervisor plan/rehearsal receipts separately from execution receipts so planning cannot masquerade as service mutation.
2. Extend the same store quarantine invariant to standalone smoke binaries that accept output paths.
3. Distill or remove dead public receipt constructors once every named provider executable exists.
1. Extend the same store quarantine invariant to standalone smoke binaries that accept output paths.
2. Distill or remove dead public receipt constructors once every named provider executable exists.
3. Distill obsolete historical authority claims in `state/map.yaml`.
## Smoke quarantine update
@ -30,3 +30,9 @@ A forged operator-status artifact containing `intentId=forged-intent`, `receiptI
## Operator-run evidence binding update
The standalone `receipt` command previously accepted caller status and path strings, then wrote `completed` evidence without inspecting the run. It now requires the latest persisted intent to match run id and mode, derives status internally as `completed`, requires an existing valid JSON result contained by the canonical artifact root, and requires the result modification time to be no earlier than the intent request time. `--status` no longer exists. The PowerShell orchestrator supplies only the result coordinates after its checked subprocess completes.
## Daemon-supervisor command authority update
Lifecycle receipt status values already distinguished planned, refused, observed, and executed outcomes, but install command aliases allowed command names and the `--execute-install` flag to disagree. Dispatch now makes command identity authoritative: `service-install-plan` and `cluster-service-install-plan` forcibly disable execution; `service-install-execute` and `cluster-service-install-execute` force execution intent and reach the elevation gate without a side-channel flag. Ambiguous `install-service`, `windows-service-install`, `service-install-windows`, and `cluster-windows-service-install` aliases are removed. The wrapper invokes the exact plan/execute command and no longer appends `--execute-install`.
Negative proof passed both directions: a plan command given hostile `--execute-install` remained `planned` with `executed=false`; an execute command without the flag reached `execution-refused-not-elevated` with `executed=false` in the non-elevated shell.

Binary file not shown.

View file

@ -70,7 +70,7 @@ data_flow:
- move historical proof to git, commits, smoke artifacts, or distilled evidence rather than plan prose
active_subgoals:
- Use `notes/conceptual-substitution-audit-2026-07-12.md` plus `notes/receipt-writer-provenance-audit-2026-07-12.md` as the current authority-corrosion maps. Operator-snapshot promotion is cut; verse-query smoke is quarantined. Operator-run completion now binds to a matching persisted intent and a fresh valid JSON result contained under the canonical artifact root; callers cannot supply completion status. Next audit daemon-supervisor plan/rehearsal versus execution provenance, then extend quarantine to standalone smoke binaries.
- Use `notes/conceptual-substitution-audit-2026-07-12.md` plus `notes/receipt-writer-provenance-audit-2026-07-12.md` as the current authority-corrosion maps. Snapshot promotion, synthetic smoke escape, and unbound operator completion are cut. Daemon-supervisor install command identity now owns plan versus execute intent; plan forcibly cannot mutate, execute always reaches the elevation gate, ambiguous aliases and side-channel wrapper flags are removed. Next quarantine standalone smoke binaries, remove dead public receipt constructors where providers are missing, and distill obsolete historical authority claims.
- `swarm-overview` is an Epiphany-local, renderer-neutral diagnostic projection over provider-owned typed state. It owns no lifecycle, discovery, composition, or mutation authority. Epiphany publishes clean provider-owned Eve surfaces; Odin may discover them; downstream lowerers and compositors remain outside Epiphany's concern.
- Idunn owns daemon survival and service lifecycle physiology. Codex/operator sessions may inspect, generate runbooks, and route explicit operator authority, but keeping daemons alive belongs to Idunn-owned service lifecycle receipts, scheduler policy, audits, and aftercare. Do not let Self, diagnostic projections, or ad hoc wrapper summaries become a second daemon-keeper. Global tool readbacks for Self `service-health`, `service-policy-directory`, and `swarm-online-runbook` state `lifecycleOwner=Idunn` and `hostedBody=Epiphany` without service mutation or private-state exposure.
- Use `notes/epiphany-cultmesh-dreaming-roadmap.md` as the design authority for distributed Epiphany dreaming. Public dreams are separately authored typed documents distributed through CultMesh/CultNet; private state remains local and cannot be shared by flipping a visibility flag. Foreign dreams are external thought weather until a reviewed local adoption receipt digests them into memory, planning, doctrine, or governance.

View file

@ -1592,7 +1592,7 @@ if ($Mode -eq "cluster-service-install-plan" -or $Mode -eq "cluster-service-inst
$clusterServiceId = "epiphany-cluster-daemon-services"
}
$installArgs = @(
"cluster-service-install-plan",
"cluster-service-install-$clusterInstallModeName",
"--store", $localVerseStore,
"--runtime-id", $LocalVerseRuntimeId,
"--daemon-id", $DaemonId,
@ -1612,9 +1612,6 @@ if ($Mode -eq "cluster-service-install-plan" -or $Mode -eq "cluster-service-inst
if ($ServiceMaxIterations -gt 0) {
$installArgs += @("--max-iterations", "$ServiceMaxIterations")
}
if ($Mode -eq "cluster-service-install-execute") {
$installArgs += @("--execute-install")
}
Invoke-Checked `
-Label "write cluster daemon Windows service install $clusterInstallModeName" `
-FilePath $daemonSupervisorExe `
@ -1982,7 +1979,7 @@ if ($Mode -eq "service-install-plan" -or $Mode -eq "service-install-execute") {
$resultPath = Join-Path $artifactRoot "daemon-service-install-$installModeName.stdout.json"
$installScriptPath = Join-Path $artifactRoot "epiphany-daemon-supervisor-install.ps1"
$installArgs = @(
"windows-service-install",
"service-install-$installModeName",
"--store", $localVerseStore,
"--runtime-id", $LocalVerseRuntimeId,
"--daemon-id", $DaemonId,
@ -2001,9 +1998,6 @@ if ($Mode -eq "service-install-plan" -or $Mode -eq "service-install-execute") {
if ($ServiceMaxIterations -gt 0) {
$installArgs += @("--max-iterations", "$ServiceMaxIterations")
}
if ($Mode -eq "service-install-execute") {
$installArgs += @("--execute-install")
}
Invoke-Checked `
-Label "write daemon supervisor Windows service install $installModeName" `
-FilePath $daemonSupervisorExe `