From 5caa80f13657653b50c28ea7d3f7a0f17872d2a8 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 12 Jul 2026 21:13:07 +0100 Subject: [PATCH] Separate daemon supervision from bootstrap --- .../src/bin/epiphany-daemon-supervisor.rs | 208 +++++++++--------- ...onceptual-substitution-audit-2026-07-12.md | 15 ++ notes/epiphany-current-algorithmic-map.md | 4 + notes/fresh-workspace-handoff.md | 3 + state/ledgers.msgpack | Bin 435536 -> 436006 bytes state/map.yaml | 2 +- 6 files changed, 124 insertions(+), 108 deletions(-) diff --git a/epiphany-core/src/bin/epiphany-daemon-supervisor.rs b/epiphany-core/src/bin/epiphany-daemon-supervisor.rs index bebdf286..0b76d2f3 100644 --- a/epiphany-core/src/bin/epiphany-daemon-supervisor.rs +++ b/epiphany-core/src/bin/epiphany-daemon-supervisor.rs @@ -9,8 +9,10 @@ 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_cluster_topology; use epiphany_core::load_epiphany_cultmesh_managed_service_policies; use epiphany_core::load_epiphany_cultmesh_managed_service_policy; +use epiphany_core::load_epiphany_cultmesh_status; 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; @@ -134,12 +136,46 @@ fn main() -> Result<()> { } } -fn reconcile(args: Args) -> Result<()> { +fn require_supervisor_bootstrap(args: &Args) -> Result<()> { + load_epiphany_cultmesh_status(&args.store, args.runtime_id.clone())? + .with_context(|| { + format!( + "local Verse is not bootstrapped at {}; run explicit bootstrap before daemon-supervisor 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 explicit bootstrap before daemon-supervisor commands", + args.store.display() + ); + } + Ok(()) +} + +fn seed_supervisor_smoke_fixture(args: &Args) -> Result<()> { + if !args + .store + .components() + .any(|component| component.as_os_str() == ".epiphany-smoke") + { + anyhow::bail!( + "daemon-supervisor smoke fixtures may write only beneath .epiphany-smoke" + ); + } seed_epiphany_local_verse_context( &args.store, args.runtime_id.clone(), Utc::now().to_rfc3339(), - )?; + ) +} + +fn reconcile(args: Args) -> Result<()> { + + require_supervisor_bootstrap(&args)?; + let context = query_epiphany_local_verse_context(&args.store, args.runtime_id.clone())?; let daemon_status = context .daemon_statuses @@ -381,11 +417,9 @@ fn service_launch(args: Args) -> Result<()> { } fn managed_service_policy(args: Args) -> Result<()> { - seed_epiphany_local_verse_context( - &args.store, - args.runtime_id.clone(), - Utc::now().to_rfc3339(), - )?; + + require_supervisor_bootstrap(&args)?; + let command = service_command_path(&args)?; let stdout_artifact = args.stdout_artifact.clone().unwrap_or_else(|| { PathBuf::from(format!( @@ -614,11 +648,9 @@ fn managed_service_reconcile(mut args: Args) -> Result<()> { } fn service_runbook(args: Args) -> Result<()> { - seed_epiphany_local_verse_context( - &args.store, - args.runtime_id.clone(), - Utc::now().to_rfc3339(), - )?; + + 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(); @@ -670,11 +702,9 @@ fn service_runbook(args: Args) -> Result<()> { } fn repo_work_service_audit(args: Args) -> Result<()> { - seed_epiphany_local_verse_context( - &args.store, - args.runtime_id.clone(), - Utc::now().to_rfc3339(), - )?; + + 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(); @@ -867,11 +897,9 @@ fn service_artifact_status(artifact_ref: &str) -> &'static str { } fn cluster_daemon_runbook(args: Args) -> Result<()> { - seed_epiphany_local_verse_context( - &args.store, - args.runtime_id.clone(), - Utc::now().to_rfc3339(), - )?; + + 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(); @@ -940,11 +968,9 @@ fn cluster_daemon_runbook(args: Args) -> Result<()> { } fn cluster_windows_service_install(args: Args) -> Result<()> { - seed_epiphany_local_verse_context( - &args.store, - args.runtime_id.clone(), - Utc::now().to_rfc3339(), - )?; + + 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(); @@ -1072,11 +1098,9 @@ fn cluster_windows_service_install(args: Args) -> Result<()> { } fn cluster_windows_service_audit(args: Args) -> Result<()> { - seed_epiphany_local_verse_context( - &args.store, - args.runtime_id.clone(), - Utc::now().to_rfc3339(), - )?; + + 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(); @@ -1182,11 +1206,9 @@ fn cluster_windows_service_audit(args: Args) -> Result<()> { } fn cluster_windows_service_control(args: Args, control: &str) -> Result<()> { - seed_epiphany_local_verse_context( - &args.store, - args.runtime_id.clone(), - Utc::now().to_rfc3339(), - )?; + + 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(); @@ -1323,11 +1345,9 @@ fn cluster_windows_service_control(args: Args, control: &str) -> Result<()> { } fn windows_service_install(args: Args) -> Result<()> { - seed_epiphany_local_verse_context( - &args.store, - args.runtime_id.clone(), - Utc::now().to_rfc3339(), - )?; + + 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(); @@ -1421,11 +1441,9 @@ fn windows_service_install(args: Args) -> Result<()> { } fn windows_service_status(args: Args) -> Result<()> { - seed_epiphany_local_verse_context( - &args.store, - args.runtime_id.clone(), - Utc::now().to_rfc3339(), - )?; + + 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(); @@ -1480,11 +1498,9 @@ fn windows_service_status(args: Args) -> Result<()> { } fn windows_service_execution_readiness(args: Args) -> Result<()> { - seed_epiphany_local_verse_context( - &args.store, - args.runtime_id.clone(), - Utc::now().to_rfc3339(), - )?; + + 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(); @@ -1535,11 +1551,9 @@ fn windows_service_execution_readiness(args: Args) -> Result<()> { } fn cluster_windows_service_execution_readiness(args: Args) -> Result<()> { - seed_epiphany_local_verse_context( - &args.store, - args.runtime_id.clone(), - Utc::now().to_rfc3339(), - )?; + + 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(); @@ -1604,11 +1618,9 @@ fn cluster_windows_service_execution_readiness(args: Args) -> Result<()> { } fn windows_service_execution_audit(args: Args) -> Result<()> { - seed_epiphany_local_verse_context( - &args.store, - args.runtime_id.clone(), - Utc::now().to_rfc3339(), - )?; + + 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(); @@ -1668,11 +1680,9 @@ fn windows_service_execution_audit(args: Args) -> Result<()> { } fn windows_service_execution_audit_smoke(args: Args) -> Result<()> { - seed_epiphany_local_verse_context( - &args.store, - args.runtime_id.clone(), - Utc::now().to_rfc3339(), - )?; + + seed_supervisor_smoke_fixture(&args)?; + let started_at = Utc::now(); for (action, status) in [ ("windows-service-execution-runbook", "written"), @@ -1761,11 +1771,9 @@ fn windows_service_execution_audit_smoke(args: Args) -> Result<()> { } fn cluster_windows_service_execution_runbook(args: Args) -> Result<()> { - seed_epiphany_local_verse_context( - &args.store, - args.runtime_id.clone(), - Utc::now().to_rfc3339(), - )?; + + 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(); @@ -1854,11 +1862,9 @@ fn cluster_windows_service_execution_runbook(args: Args) -> Result<()> { } fn cluster_windows_service_execution_audit(args: Args) -> Result<()> { - seed_epiphany_local_verse_context( - &args.store, - args.runtime_id.clone(), - Utc::now().to_rfc3339(), - )?; + + 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(); @@ -1917,11 +1923,9 @@ fn cluster_windows_service_execution_audit(args: Args) -> Result<()> { } fn cluster_windows_service_execution_audit_smoke(args: Args) -> Result<()> { - seed_epiphany_local_verse_context( - &args.store, - args.runtime_id.clone(), - Utc::now().to_rfc3339(), - )?; + + seed_supervisor_smoke_fixture(&args)?; + let started_at = Utc::now(); for (action, status) in [ ("cluster-windows-service-execution-runbook", "written"), @@ -2067,11 +2071,9 @@ fn elevated_powershell_runbook_command(path: &str) -> String { } fn windows_service_execution_runbook(args: Args) -> Result<()> { - seed_epiphany_local_verse_context( - &args.store, - args.runtime_id.clone(), - Utc::now().to_rfc3339(), - )?; + + 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(); @@ -2163,11 +2165,9 @@ fn windows_service_execution_runbook(args: Args) -> Result<()> { } fn windows_service_reconcile(args: Args) -> Result<()> { - seed_epiphany_local_verse_context( - &args.store, - args.runtime_id.clone(), - Utc::now().to_rfc3339(), - )?; + + 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(); @@ -2264,11 +2264,9 @@ fn windows_service_reconcile(args: Args) -> Result<()> { } fn windows_service_control(args: Args, control: &str) -> Result<()> { - seed_epiphany_local_verse_context( - &args.store, - args.runtime_id.clone(), - Utc::now().to_rfc3339(), - )?; + + 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(); @@ -2357,11 +2355,9 @@ fn windows_service_control(args: Args, control: &str) -> Result<()> { fn run_tick(args: &Args, iteration: u64, next_wake_utc: Option) -> Result { let tick_started = Utc::now(); - seed_epiphany_local_verse_context( - &args.store, - args.runtime_id.clone(), - Utc::now().to_rfc3339(), - )?; + + require_supervisor_bootstrap(&args)?; + let mut context = query_epiphany_local_verse_context(&args.store, args.runtime_id.clone())?; assert_swarm_brake_allows_scheduler_tick(&context)?; @@ -2513,11 +2509,9 @@ fn run_tick(args: &Args, iteration: u64, next_wake_utc: Option) -> Resul } fn write_policy(args: Args) -> Result<()> { - seed_epiphany_local_verse_context( - &args.store, - args.runtime_id.clone(), - Utc::now().to_rfc3339(), - )?; + + require_supervisor_bootstrap(&args)?; + let context = query_epiphany_local_verse_context(&args.store, args.runtime_id.clone())?; let daemon_status = context .daemon_statuses diff --git a/notes/conceptual-substitution-audit-2026-07-12.md b/notes/conceptual-substitution-audit-2026-07-12.md index 83b954e4..4fac5925 100644 --- a/notes/conceptual-substitution-audit-2026-07-12.md +++ b/notes/conceptual-substitution-audit-2026-07-12.md @@ -138,3 +138,18 @@ owning daemon's provider state, and write only its heartbeat. An unbootstrapped daemon fails before creating the CultCache store and tells the operator to run explicit bootstrap. The wrapper already performs that bootstrap at the operator boundary. + +## Supervisor/bootstrap split + +`epiphany-daemon-supervisor` repeated the same authority collapse across twenty +production lifecycle, policy, scheduler, runbook, audit, status, and control +paths. Every command seeded shared policy, topology, contracts, brake, and +operator status before performing Idunn work. + +Production supervisor paths now require persisted Epiphany status and cluster +topology through a read-only prerequisite. Missing bootstrap fails before a +CultCache store is created. The two synthetic execution-audit smoke commands +retain explicit fixture initialization, but it is isolated behind +`seed_supervisor_smoke_fixture`, which refuses every store outside an +`.epiphany-smoke` path before writing. Idunn consumes shared initialization; it +does not own or repair it. diff --git a/notes/epiphany-current-algorithmic-map.md b/notes/epiphany-current-algorithmic-map.md index 56c5f7d3..1e0bacb2 100644 --- a/notes/epiphany-current-algorithmic-map.md +++ b/notes/epiphany-current-algorithmic-map.md @@ -98,6 +98,10 @@ context. They require persisted topology, read narrow liveness, then write only their bounded provider state and heartbeat. Explicit operator bootstrap owns policy/topology/contract initialization. +- `epiphany-daemon-supervisor` also requires explicit persisted bootstrap for + every production lifecycle, policy, scheduler, runbook, audit, status, and + control path. Only its two synthetic audit-smoke commands may initialize + fixtures, and only beneath `.epiphany-smoke`. - Store/runtime overrides fail before fixture seeding or receipt construction. - Negative proof: targeting `state/local-verse.ccmp` was rejected and its SHA-256 did not change. - Positive proof: the built-in quarantined smoke completes successfully and reports its quarantine coordinates. diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index 8d3ce0cb..04d20ac8 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -24,6 +24,9 @@ The confirmed conceptual substitutions have been cut: - cluster daemons do not bootstrap or query the full Verse. Missing persisted topology is a pre-write refusal; explicit operator bootstrap owns shared policy, topology, contracts, brake initialization, and operator status. +- daemon-supervisor production commands likewise require persisted bootstrap + and cannot create shared state. Its two audit-smoke fixture initializers are + hard-confined beneath `.epiphany-smoke`. The presentation boundary is now plain: `swarm overview` is a generic compact read-only projection. Gjallar is a downstream TUI application on Nightwing and is not an Epiphany organ, provider, owner, runtime, or architectural dependency. Eve/CultUI graphs may be lowered or composited downstream without Epiphany caring which presentation client does it. diff --git a/state/ledgers.msgpack b/state/ledgers.msgpack index f0bba0d36d8670ef4b089f10b880e376e33a8597..47af1a6d67cfc0f2ae65f7abc74d7922afd1a0c7 100644 GIT binary patch delta 438 zcmYL_y-UPE5XH@%rtqT>8#yfowA70p2%@!MDPm=}mrO2;$?m!{@k}LRq5r^<#=;M{ zTClLNx3Cav{~9MMSUmRQ&HQ-0&-c#wqw};F%xv~{wziMoCc<(1c_lavg6XSK>w44* zM9tY2O^T*oMy>Gmp|1DCxvAd9&StN^Uk;YmzJl=~7WNDi(1pu&tQja%Ufz9cnb<1_ zska66HtNfp_QHkpHYsD))__x-Q5O#We{!8-T*Wy?&I+uGD9$h`bM%ofjSx>FRVJDv zV#Oszu{G>%4#p0_<7ufqf@qD#IDFj^QT^P+?DG!y_wg0MGw=NcB0F4FXZv1CX$( po;6K1XoQzReRmsn=0W+smZE$Gto#7E9zKSv^L;B5e`u$Oy5AJQpfCUc delta 57 zcmZ2>PU^xbsSOg0+y<6bhUQVn7}%ERZdPV2WMt%Qp32ldm5C9EK{PWEvj8#c_Nh#4 HyB7ff6AuyG diff --git a/state/map.yaml b/state/map.yaml index b5a9fa35..962fe2bd 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -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. Snapshot promotion, synthetic smoke escape, unbound operator completion, hidden service execution flags, generic-bootstrap publication of provider advertisements/Eve surfaces/hosted tools, the central topology-derived Odin publisher, all-provider state writers, and daemon-side generic bootstrap are cut. Provider absence survives bootstrap; the query CLI cannot proclaim seven providers active over RUDP; each cluster daemon requires persisted topology and writes only its own advertisement, surface, tools, and heartbeat through narrow reads. Next remove or test-gate dead public receipt constructors where provider binaries are absent and audit remaining projections, caches, schedulers, adapters, and non-receipt smokes for borrowed authority or path escape. + - 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, unbound operator completion, hidden service execution flags, generic-bootstrap publication of provider advertisements/Eve surfaces/hosted tools, the central topology-derived Odin publisher, all-provider state writers, cluster-daemon bootstrap, and daemon-supervisor production bootstrap are cut. Provider absence survives bootstrap; the query CLI cannot proclaim seven providers active over RUDP; each cluster daemon writes only its own presence and pulse; Idunn production paths require shared initialization rather than repainting it; supervisor audit-smoke seeding is confined beneath `.epiphany-smoke`. Next remove or test-gate dead public receipt constructors where provider binaries are absent and audit remaining projections, caches, schedulers, adapters, and non-receipt smokes for borrowed authority or path escape. - `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.