Separate daemon supervision from bootstrap

This commit is contained in:
Meta Vens 2026-07-12 21:13:07 +01:00
commit 5caa80f136
6 changed files with 124 additions and 108 deletions

View file

@ -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<String>) -> Result<Value> {
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<String>) -> 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

View file

@ -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.

View file

@ -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.

View file

@ -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.

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. 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.