Derive cluster daemon heartbeat status

This commit is contained in:
Meta Vens 2026-07-12 23:50:39 +01:00
commit c247709647
5 changed files with 14 additions and 18 deletions

View file

@ -81,7 +81,7 @@ fn write_heartbeat(args: &Args, iteration: u64) -> Result<EpiphanyCultMeshDaemon
],
});
let mut next = current.clone();
next.status = args.daemon_status.clone();
next.status = "ready".to_string();
next.last_heartbeat_utc = Utc::now().to_rfc3339();
next.operator_action = if next.status == "ready" {
"none".to_string()
@ -94,9 +94,6 @@ fn write_heartbeat(args: &Args, iteration: u64) -> Result<EpiphanyCultMeshDaemon
format!("Daemon body domain: {}", current.body_domain),
format!("Heartbeat iteration: {iteration}"),
];
if let Some(note) = args.note.as_ref() {
next.notes.push(note.clone());
}
write_epiphany_cultmesh_daemon_status(&args.store, args.runtime_id.clone(), next)
}
@ -133,10 +130,8 @@ struct Args {
store: PathBuf,
runtime_id: String,
daemon_id: String,
daemon_status: String,
interval_seconds: u64,
max_iterations: u64,
note: Option<String>,
}
impl Args {
@ -146,10 +141,8 @@ impl Args {
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 daemon_status = "ready".to_string();
let mut interval_seconds = 60_u64;
let mut max_iterations = 1_u64;
let mut note = None;
while let Some(arg) = values.next() {
match arg.as_str() {
@ -160,9 +153,6 @@ impl Args {
"--daemon-id" => {
daemon_id = Some(values.next().context("missing --daemon-id value")?)
}
"--daemon-status" => {
daemon_status = values.next().context("missing --daemon-status value")?
}
"--interval-seconds" => {
interval_seconds = values
.next()
@ -175,24 +165,18 @@ impl Args {
.context("missing --max-iterations value")?
.parse()?;
}
"--note" => note = Some(values.next().context("missing --note value")?),
other => anyhow::bail!("unknown argument {other:?}"),
}
}
let daemon_id = daemon_id.context("cluster daemon requires --daemon-id")?;
if !matches!(daemon_status.as_str(), "ready" | "degraded" | "down") {
anyhow::bail!("--daemon-status must be ready, degraded, or down");
}
Ok(Self {
command,
store,
runtime_id,
daemon_id,
daemon_status,
interval_seconds,
max_iterations,
note,
})
}
}

View file

@ -420,3 +420,12 @@ matching runtime job, binding, role, authority scope, allowed operation, and
path coverage. Patch, command, and commit receipts also require the matching
approved Hands review. Missing grants, mismatched paths, and attempts to mutate
under a read-only planning grant are negative-tested refusals.
## Caller-selected daemon liveness
`epiphany-cluster-daemon` accepted `--daemon-status ready|degraded|down` and
wrote that caller choice as the owning daemon's liveness. It also accepted an
unproven note for the status document. Both inputs are deleted. Reaching the
heartbeat write derives `ready`; command failure writes no heartbeat, while
degradation/down must come from timeout, failed restart, or supervisor
observation rather than a caller describing the desired dashboard color.

View file

@ -106,6 +106,9 @@ The confirmed conceptual substitutions have been cut:
- Hands persistence resolves its grant rather than trusting an ID label. Grant
identity/scope/operations/paths and approved review must match each intent
and patch/command/commit consequence; grants are immutable.
- cluster-daemon heartbeat no longer accepts caller liveness or note fields.
A successful owning-body heartbeat derives `ready`; degraded/down require
observed supervisor or timeout evidence.
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

@ -660,4 +660,4 @@ current_status:
- 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.
last_completed_repo_swarm_cut: Huginn live-fire has crossed the first real repo Body from Persona pressure to branch-local Hands commit and Soul/Mind closure, and now has repeatable closure and redacted-public-proof mouths. On `E:\Projects\Huginn` branch `epiphany/huginn/first-awakening`, `tools/epiphany_local_run.ps1 -Mode repo-persona-intake` accepted item `huginn-inspection-readback-livefire`, Weksa/Persona intake remained sealed, and `epiphany-work derive-plan` lowered it to model-authored safe family `repo.status_section` for `README.md` only. `tools/epiphany_local_run.ps1 -Mode repo-swarm-run` advanced four bounded pulses: run-from-plan, adopt-from-plan, execute-from-plan, and close-from-execute. Huginn commit `e9b07c38fceabbdc89ea35c2156326c80c0393a2` updated only `README.md`; closure receipt `.epiphany\work\work-close-huginn-inspection-readback-livefire.json` records status `closed`, Soul verdict `passed`, Mind state commit `repo-work-close-huginn-inspection-readback-livefire-mind-commit`, safe family `repo.status_section`, path scope `README.md`, Bifrost publication gate, and `privateStateExposed=false`. Verification also ran Huginn's TypeScript compiler through concrete Node path: `C:\Program Files\nodejs\node.exe E:\Projects\Huginn\node_modules\typescript\bin\tsc -p tsconfig.json` passed. Native verifier `epiphany-repo-livefire-closure-smoke --workspace E:\Projects\Huginn --item huginn-inspection-readback-livefire --expected-path README.md --expected-family repo.status_section --expected-gate awaiting-publication --expected-blocker bifrost-publication-missing` checks closure receipt, Mind map entry, overview gate/blocker, authority seals, and `git show` changed paths, returning `status=ok`, `soulVerdict=passed`, `publicationGate=Bifrost`, and `privateStateExposed=false`. Wrapper `tools/epiphany_local_run.ps1 -Mode repo-livefire-closure -Workspace E:\Projects\Huginn -RepoWorkItem huginn-inspection-readback-livefire -SkipBuild` exposes the same read-only proof through the local operator mouth; artifact `local-20260621-190156-803-2f85ba15` printed commit, path, gate, blocker, Soul verdict, Mind commit receipt, and `privateStateExposed=False` without push, merge, publication, or target mutation. New wrapper `tools/epiphany_local_run.ps1 -Mode repo-work-public-proof -Workspace E:\Projects\Huginn -RepoWorkItem huginn-inspection-readback-livefire -SkipBuild` exports the redacted proof bundle and repo-local Verse index without publishing; artifact `local-20260621-190645-345-4555b1dc` wrote `.epiphany\public\proof-bundles\repo-work-public-proof-huginn-inspection-readback-livefire.json`, proof id `repo-work-public-proof-huginn-inspection-readback-livefire`, SHA-256 `857545b199f3e37eb06c2e757e5cfc201958e46667bb1f8403fd333a22620f63`, nine artifact rows, zero publication rows, and `privateStateExposed=False`. swarm overview readback `local-20260621-190701-140-b9519ec6` sees the `REPO-WORK-PUBLIC-PROOF` row and action row from Huginn's repo-local Verse with the same SHA-256 while Bifrost publication remains missing. Launcher compact modes are now named compact operator-context modes instead of read-only modes, and `bifrost-public-proof` summaries print item, target public Verse, room, proof SHA-256, review receipts, and credit receipts so publication receipts are auditable without claiming upstream sync.
next_action: Continue the conceptual-substitution modeling pass beyond receipt writers. Publication and closure authority is single-sourced; Substrate Gate owns fixed grant policies; Hands intents and consequences now resolve immutable grants plus approved reviews and enforce identity/operation/path scope. Inspect daemon lifecycle writers next, then remaining projections/caches/adapters for substituted authority.
next_action: Continue the conceptual-substitution modeling pass beyond receipt writers. Publication/closure/Hands authority is single-sourced; successful cluster-daemon heartbeat now derives ready and cannot accept caller liveness prose. Continue auditing daemon-supervisor and verse-query lifecycle writers, especially fixture-only lifecycle receipts and any status that is not derived from an observed process/service consequence; then inspect remaining projections/caches/adapters.