From 0005ef4ed2d747fb177dc57a4d1cfdbba22c35cd Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Tue, 12 May 2026 04:05:05 +0100 Subject: [PATCH] Quarantine heartbeat cognition state --- .../src/bin/epiphany-heartbeat-store.rs | 4 + epiphany-core/src/heartbeat_state.rs | 308 ++++++++++++++---- epiphany-core/src/lib.rs | 7 + ...hany.agent-heartbeat-cognition.schema.json | 65 ++++ .../epiphany.agent-heartbeat.schema.json | 34 +- schemas/cultnet/index.json | 11 + 6 files changed, 333 insertions(+), 96 deletions(-) create mode 100644 schemas/cultnet/epiphany.agent-heartbeat-cognition.schema.json diff --git a/epiphany-core/src/bin/epiphany-heartbeat-store.rs b/epiphany-core/src/bin/epiphany-heartbeat-store.rs index 015c1287..7a89487c 100644 --- a/epiphany-core/src/bin/epiphany-heartbeat-store.rs +++ b/epiphany-core/src/bin/epiphany-heartbeat-store.rs @@ -520,6 +520,10 @@ fn run_smoke(agent_store: &Path) -> Result { && routine["routine"]["bridge"]["decision"]["reason"] .as_str() .is_some_and(|reason| !reason.is_empty()) + && routine_status["cognitionQuarantine"]["schema_version"] + == "epiphany.agent_heartbeat_cognition.v0" + && routine_status["cognitionQuarantine"]["latestRunId"].as_str() + == routine["routine"]["runId"].as_str() && routine_status["participants"] .as_array() .and_then(|participants| { diff --git a/epiphany-core/src/heartbeat_state.rs b/epiphany-core/src/heartbeat_state.rs index c0d5564d..6de7f897 100644 --- a/epiphany-core/src/heartbeat_state.rs +++ b/epiphany-core/src/heartbeat_state.rs @@ -17,6 +17,9 @@ use std::path::Path; pub const HEARTBEAT_STATE_TYPE: &str = "epiphany.agent_heartbeat"; pub const HEARTBEAT_STATE_KEY: &str = "default"; pub const HEARTBEAT_STATE_SCHEMA_VERSION: &str = "epiphany.agent_heartbeat.v0"; +pub const HEARTBEAT_COGNITION_TYPE: &str = "epiphany.agent_heartbeat_cognition"; +pub const HEARTBEAT_COGNITION_KEY: &str = "default"; +pub const HEARTBEAT_COGNITION_SCHEMA_VERSION: &str = "epiphany.agent_heartbeat_cognition.v0"; pub const HEARTBEAT_STATUS_SCHEMA_VERSION: &str = "epiphany.agent_heartbeat_status.v0"; pub const INITIATIVE_SCHEMA_VERSION: &str = "ghostlight.initiative_schedule.v0"; pub const VOID_ROUTINE_SCHEMA_VERSION: &str = "epiphany.void_routine.v0"; @@ -57,26 +60,86 @@ pub struct EpiphanyHeartbeatStateEntry { pub participants: Vec, #[cultcache(key = 6)] pub history: Vec, - #[cultcache(key = 7, default)] - pub sleep_cycle: Option, - #[cultcache(key = 8, default)] - pub memory_resonance: Option, - #[cultcache(key = 9, default)] - pub incubation: Option, - #[cultcache(key = 10, default)] - pub thought_lanes: Option, - #[cultcache(key = 11, default)] - pub bridge: Option, - #[cultcache(key = 12, default)] - pub candidate_interventions: Option, - #[cultcache(key = 13, default)] - pub appraisals: Option, - #[cultcache(key = 14, default)] - pub reactions: Option, #[cultcache(key = 15, default)] pub extra: BTreeMap, } +#[derive(Clone, Debug, PartialEq, DatabaseEntry)] +#[cultcache( + type = "epiphany.agent_heartbeat_cognition", + schema = "EpiphanyHeartbeatCognitionEntry" +)] +pub struct EpiphanyHeartbeatCognitionEntry { + #[cultcache(key = 0)] + pub schema_version: String, + #[cultcache(key = 1)] + pub updated_at: String, + #[cultcache(key = 2, default)] + pub latest_run_id: Option, + #[cultcache(key = 3, default)] + pub latest_artifact_ref: Option, + #[cultcache(key = 4, default)] + pub source: Option, + #[cultcache(key = 5, default)] + pub sleep_cycle: Option, + #[cultcache(key = 6, default)] + pub memory_resonance: Option, + #[cultcache(key = 7, default)] + pub incubation: Option, + #[cultcache(key = 8, default)] + pub thought_lanes: Option, + #[cultcache(key = 9, default)] + pub bridge: Option, + #[cultcache(key = 10, default)] + pub candidate_interventions: Option, + #[cultcache(key = 11, default)] + pub appraisals: Option, + #[cultcache(key = 12, default)] + pub reactions: Option, + #[cultcache(key = 13, default)] + pub extra: BTreeMap, +} + +#[derive(Clone, Debug, PartialEq, DatabaseEntry)] +#[cultcache( + type = "epiphany.agent_heartbeat", + schema = "EpiphanyHeartbeatStateEntry" +)] +struct LegacyHeartbeatStateWithCognition { + #[cultcache(key = 0)] + schema_version: String, + #[cultcache(key = 1)] + target_heartbeat_rate: f64, + #[cultcache(key = 2)] + scene_clock: f64, + #[cultcache(key = 3)] + selection_policy: HeartbeatSelectionPolicy, + #[cultcache(key = 4)] + pacing_policy: HeartbeatPacingPolicy, + #[cultcache(key = 5)] + participants: Vec, + #[cultcache(key = 6)] + history: Vec, + #[cultcache(key = 7, default)] + sleep_cycle: Option, + #[cultcache(key = 8, default)] + memory_resonance: Option, + #[cultcache(key = 9, default)] + incubation: Option, + #[cultcache(key = 10, default)] + thought_lanes: Option, + #[cultcache(key = 11, default)] + bridge: Option, + #[cultcache(key = 12, default)] + candidate_interventions: Option, + #[cultcache(key = 13, default)] + appraisals: Option, + #[cultcache(key = 14, default)] + reactions: Option, + #[cultcache(key = 15, default)] + extra: BTreeMap, +} + #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] pub struct HeartbeatSelectionPolicy { pub mode: String, @@ -257,6 +320,16 @@ struct HeartbeatAction { pub fn heartbeat_state_cache(store_path: impl AsRef) -> Result { let mut cache = CultCache::new(); cache.register_entry_type::()?; + cache.register_entry_type::()?; + cache.add_generic_backing_store(SingleFileMessagePackBackingStore::new(store_path.as_ref())); + cache.pull_all_backing_stores()?; + Ok(cache) +} + +fn legacy_heartbeat_state_cache(store_path: impl AsRef) -> Result { + let mut cache = CultCache::new(); + cache.register_entry_type::()?; + cache.register_entry_type::()?; cache.add_generic_backing_store(SingleFileMessagePackBackingStore::new(store_path.as_ref())); cache.pull_all_backing_stores()?; Ok(cache) @@ -278,6 +351,90 @@ pub fn write_heartbeat_state_entry( cache.put(HEARTBEAT_STATE_KEY, state) } +pub fn load_heartbeat_cognition_entry( + store_path: impl AsRef, +) -> Result> { + let store_path = store_path.as_ref(); + let cache = heartbeat_state_cache(store_path)?; + if let Some(cognition) = cache.get::(HEARTBEAT_COGNITION_KEY)? + { + return Ok(Some(cognition)); + } + let legacy_cache = legacy_heartbeat_state_cache(store_path)?; + let Some(legacy) = + legacy_cache.get::(HEARTBEAT_STATE_KEY)? + else { + return Ok(None); + }; + legacy_heartbeat_cognition_entry(legacy) +} + +pub fn write_heartbeat_cognition_entry( + store_path: impl AsRef, + cognition: &EpiphanyHeartbeatCognitionEntry, +) -> Result { + validate_heartbeat_cognition(cognition)?; + let mut cache = heartbeat_state_cache(store_path)?; + cache.put(HEARTBEAT_COGNITION_KEY, cognition) +} + +pub fn validate_heartbeat_cognition(cognition: &EpiphanyHeartbeatCognitionEntry) -> Result<()> { + if cognition.schema_version != HEARTBEAT_COGNITION_SCHEMA_VERSION { + return Err(anyhow!( + "heartbeat cognition schema_version is {:?}, expected {:?}", + cognition.schema_version, + HEARTBEAT_COGNITION_SCHEMA_VERSION + )); + } + Ok(()) +} + +fn legacy_heartbeat_cognition_entry( + legacy: LegacyHeartbeatStateWithCognition, +) -> Result> { + if legacy.sleep_cycle.is_none() + && legacy.memory_resonance.is_none() + && legacy.incubation.is_none() + && legacy.thought_lanes.is_none() + && legacy.bridge.is_none() + && legacy.candidate_interventions.is_none() + && legacy.appraisals.is_none() + && legacy.reactions.is_none() + { + return Ok(None); + } + let routine = legacy.extra.get("voidRoutine"); + let latest_run_id = routine + .and_then(|value| value.get("lastRunId")) + .and_then(Value::as_str) + .map(str::to_string); + let source = routine + .and_then(|value| value.get("source")) + .and_then(Value::as_str) + .map(str::to_string); + let updated_at = routine + .and_then(|value| value.get("lastRunAt")) + .and_then(Value::as_str) + .map(str::to_string) + .unwrap_or_else(now_iso); + Ok(Some(EpiphanyHeartbeatCognitionEntry { + schema_version: HEARTBEAT_COGNITION_SCHEMA_VERSION.to_string(), + updated_at, + latest_run_id, + latest_artifact_ref: None, + source, + sleep_cycle: legacy.sleep_cycle, + memory_resonance: legacy.memory_resonance, + incubation: legacy.incubation, + thought_lanes: legacy.thought_lanes, + bridge: legacy.bridge, + candidate_interventions: legacy.candidate_interventions, + appraisals: legacy.appraisals, + reactions: legacy.reactions, + extra: BTreeMap::new(), + })) +} + pub fn validate_heartbeat_state(state: &EpiphanyHeartbeatStateEntry) -> Result<()> { if state.schema_version != HEARTBEAT_STATE_SCHEMA_VERSION { return Err(anyhow!( @@ -363,14 +520,6 @@ pub fn default_heartbeat_state(target_heartbeat_rate: f64) -> EpiphanyHeartbeatS .map(|role_id| default_participant(role_id)) .collect(), history: Vec::new(), - sleep_cycle: None, - memory_resonance: None, - incubation: None, - thought_lanes: None, - bridge: None, - candidate_interventions: None, - appraisals: None, - reactions: None, extra: BTreeMap::new(), } } @@ -446,6 +595,7 @@ pub fn heartbeat_status_projection( "availableActions": ["init", "tick", "pump", "complete", "status"], })); }; + let cognition = load_heartbeat_cognition_entry(store_path)?; let history: Vec<_> = state .history .iter() @@ -471,14 +621,15 @@ pub fn heartbeat_status_projection( "latestEvent": history.last().cloned(), "history": history, "latestArtifacts": latest_json_artifacts(artifact_dir, artifact_limit), - "sleepCycle": state.sleep_cycle, - "memoryResonance": state.memory_resonance, - "incubation": state.incubation, - "thoughtLanes": state.thought_lanes, - "bridge": state.bridge, - "candidateInterventions": state.candidate_interventions, - "appraisals": state.appraisals, - "reactions": state.reactions, + "cognitionQuarantine": cognition.as_ref().map(heartbeat_cognition_status_json), + "sleepCycle": cognition.as_ref().and_then(|entry| entry.sleep_cycle.clone()), + "memoryResonance": cognition.as_ref().and_then(|entry| entry.memory_resonance.clone()), + "incubation": cognition.as_ref().and_then(|entry| entry.incubation.clone()), + "thoughtLanes": cognition.as_ref().and_then(|entry| entry.thought_lanes.clone()), + "bridge": cognition.as_ref().and_then(|entry| entry.bridge.clone()), + "candidateInterventions": cognition.as_ref().and_then(|entry| entry.candidate_interventions.clone()), + "appraisals": cognition.as_ref().and_then(|entry| entry.appraisals.clone()), + "reactions": cognition.as_ref().and_then(|entry| entry.reactions.clone()), "adaptivePacing": state.extra.get("adaptivePacing"), "availableActions": ["init", "tick", "pump", "complete", "status", "routine"], })) @@ -493,28 +644,45 @@ pub fn run_void_routine_store( let mut state = load_heartbeat_state_entry(store_path)?.unwrap_or_else(|| default_heartbeat_state(1.0)); patch_missing_participants(&mut state); + let previous_cognition = load_heartbeat_cognition_entry(store_path)?; let memory_records = collect_role_memory_records(options.agent_store.as_deref())?; let appraisal_profiles = collect_role_appraisal_profiles(options.agent_store.as_deref())?; apply_personality_timing_profiles(&mut state, &appraisal_profiles); let resonance = build_memory_resonance(&memory_records); let incubation = build_incubation( - &state.incubation, - &state.bridge, - &state.candidate_interventions, + &previous_cognition.as_ref().and_then(|entry| entry.incubation.clone()), + &previous_cognition.as_ref().and_then(|entry| entry.bridge.clone()), + &previous_cognition + .as_ref() + .and_then(|entry| entry.candidate_interventions.clone()), &resonance, &memory_records, ); let thought_lanes = build_thought_lanes(&resonance, &incubation, &memory_records); - let bridge = build_thought_bridge(&state.bridge, &thought_lanes, &resonance, &incubation); + let bridge = build_thought_bridge( + &previous_cognition.as_ref().and_then(|entry| entry.bridge.clone()), + &thought_lanes, + &resonance, + &incubation, + ); let candidate_interventions = build_candidate_interventions(&bridge, &incubation); let appraisals = build_agent_appraisals(&appraisal_profiles, &thought_lanes, &incubation, &bridge); let reactions = build_agent_reactions(&appraisals, &bridge); apply_mood_timing_from_appraisals(&mut state, &appraisals); - let sleep_cycle = - update_sleep_cycle(state.sleep_cycle.as_ref(), &incubation, options.allow_dream); + let sleep_cycle = update_sleep_cycle( + previous_cognition + .as_ref() + .and_then(|entry| entry.sleep_cycle.as_ref()), + &incubation, + options.allow_dream, + ); let run_id = format!("epiphany-void-routine-{}", now_stamp()); + let artifact_dir = artifact_dir.as_ref(); + fs::create_dir_all(artifact_dir) + .with_context(|| format!("failed to create {}", artifact_dir.display()))?; + let artifact_path = artifact_dir.join(format!("{run_id}.routine.json")); let routine = serde_json::json!({ "schema_version": VOID_ROUTINE_SCHEMA_VERSION, "runId": run_id, @@ -540,30 +708,27 @@ pub fn run_void_routine_store( ], }); - state.sleep_cycle = Some(sleep_cycle); - state.memory_resonance = Some(resonance); - state.incubation = Some(incubation); - state.thought_lanes = Some(thought_lanes); - state.bridge = Some(bridge); - state.candidate_interventions = Some(candidate_interventions); - state.appraisals = Some(appraisals); - state.reactions = Some(reactions); - state.extra.insert( - "voidRoutine".to_string(), - serde_json::json!({ - "lastRunId": run_id, - "lastRunAt": now_iso(), - "source": options.source, - "referenceLineage": "VoidBot reference; Epiphany-native implementation.", - }), - ); - write_heartbeat_state_entry(store_path, &state)?; - - let artifact_dir = artifact_dir.as_ref(); - fs::create_dir_all(artifact_dir) - .with_context(|| format!("failed to create {}", artifact_dir.display()))?; - let artifact_path = artifact_dir.join(format!("{run_id}.routine.json")); write_json_artifact(&artifact_path, &routine)?; + write_heartbeat_cognition_entry( + store_path, + &EpiphanyHeartbeatCognitionEntry { + schema_version: HEARTBEAT_COGNITION_SCHEMA_VERSION.to_string(), + updated_at: now_iso(), + latest_run_id: Some(run_id), + latest_artifact_ref: Some(artifact_path.display().to_string()), + source: Some(options.source), + sleep_cycle: Some(sleep_cycle), + memory_resonance: Some(resonance), + incubation: Some(incubation), + thought_lanes: Some(thought_lanes), + bridge: Some(bridge), + candidate_interventions: Some(candidate_interventions), + appraisals: Some(appraisals), + reactions: Some(reactions), + extra: BTreeMap::new(), + }, + )?; + write_heartbeat_state_entry(store_path, &state)?; Ok(serde_json::json!({ "ok": true, @@ -589,7 +754,9 @@ pub fn tick_heartbeat_store( let appraisal_profiles = collect_role_appraisal_profiles(Some(agent_store))?; apply_personality_timing_profiles(&mut state, &appraisal_profiles); } - if let Some(appraisals) = state.appraisals.clone() { + if let Some(appraisals) = + load_heartbeat_cognition_entry(store_path)?.and_then(|entry| entry.appraisals) + { apply_mood_timing_from_appraisals(&mut state, &appraisals); } let result = tick_once(&mut state, &options)?; @@ -634,7 +801,9 @@ pub fn pump_heartbeat_store( let appraisal_profiles = collect_role_appraisal_profiles(Some(agent_store))?; apply_personality_timing_profiles(&mut state, &appraisal_profiles); } - if let Some(appraisals) = state.appraisals.clone() { + if let Some(appraisals) = + load_heartbeat_cognition_entry(store_path)?.and_then(|entry| entry.appraisals) + { apply_mood_timing_from_appraisals(&mut state, &appraisals); } @@ -1468,6 +1637,19 @@ fn cultcache_status(store_path: &Path) -> Value { "modifiedAt": modified_at, "entryType": HEARTBEAT_STATE_TYPE, "entryKey": HEARTBEAT_STATE_KEY, + "cognitionEntryType": HEARTBEAT_COGNITION_TYPE, + "cognitionEntryKey": HEARTBEAT_COGNITION_KEY, + }) +} + +fn heartbeat_cognition_status_json(cognition: &EpiphanyHeartbeatCognitionEntry) -> Value { + serde_json::json!({ + "schema_version": cognition.schema_version, + "updatedAt": cognition.updated_at, + "latestRunId": cognition.latest_run_id, + "latestArtifactRef": cognition.latest_artifact_ref, + "source": cognition.source, + "contract": "Experimental Void/Ghostlight thought-weather is quarantined outside stable heartbeat scheduling state. Status may project it for inspection, but scheduler policy owns only typed timing physiology.", }) } diff --git a/epiphany-core/src/lib.rs b/epiphany-core/src/lib.rs index e568d416..98dcdfb0 100644 --- a/epiphany-core/src/lib.rs +++ b/epiphany-core/src/lib.rs @@ -37,7 +37,11 @@ pub use distillation::EpiphanyDistillInput; pub use distillation::EpiphanyDistillProposal; pub use distillation::distill_observation; pub use heartbeat_state::EpiphanyHeartbeatStateEntry; +pub use heartbeat_state::EpiphanyHeartbeatCognitionEntry; pub use heartbeat_state::GhostlightSceneParticipantSeed; +pub use heartbeat_state::HEARTBEAT_COGNITION_KEY; +pub use heartbeat_state::HEARTBEAT_COGNITION_SCHEMA_VERSION; +pub use heartbeat_state::HEARTBEAT_COGNITION_TYPE; pub use heartbeat_state::HEARTBEAT_STATE_KEY; pub use heartbeat_state::HEARTBEAT_STATE_SCHEMA_VERSION; pub use heartbeat_state::HEARTBEAT_STATE_TYPE; @@ -57,11 +61,14 @@ pub use heartbeat_state::heartbeat_state_cache; pub use heartbeat_state::heartbeat_status_projection; pub use heartbeat_state::initialize_ghostlight_scene_heartbeat_store; pub use heartbeat_state::initialize_heartbeat_store; +pub use heartbeat_state::load_heartbeat_cognition_entry; pub use heartbeat_state::load_heartbeat_state_entry; pub use heartbeat_state::pump_heartbeat_store; pub use heartbeat_state::run_void_routine_store; pub use heartbeat_state::tick_heartbeat_store; +pub use heartbeat_state::validate_heartbeat_cognition; pub use heartbeat_state::validate_heartbeat_state; +pub use heartbeat_state::write_heartbeat_cognition_entry; pub use heartbeat_state::write_heartbeat_state_entry; pub use promotion::EpiphanyPromotionDecision; pub use promotion::EpiphanyPromotionInput; diff --git a/schemas/cultnet/epiphany.agent-heartbeat-cognition.schema.json b/schemas/cultnet/epiphany.agent-heartbeat-cognition.schema.json new file mode 100644 index 00000000..cd18b3ff --- /dev/null +++ b/schemas/cultnet/epiphany.agent-heartbeat-cognition.schema.json @@ -0,0 +1,65 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://gamecult.dev/epiphany/cultnet/epiphany.agent-heartbeat-cognition.schema.json", + "title": "Epiphany Heartbeat Cognition Quarantine", + "description": "Experimental Void/Ghostlight routine thought-weather kept outside stable heartbeat scheduler state. These values are inspectable receipts, not project truth or scheduling authority.", + "type": "object", + "additionalProperties": true, + "properties": { + "schemaVersion": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "latestRunId": { + "type": ["string", "null"] + }, + "latestArtifactRef": { + "type": ["string", "null"] + }, + "source": { + "type": ["string", "null"] + }, + "sleepCycle": { + "type": "object", + "additionalProperties": true + }, + "memoryResonance": { + "type": "object", + "additionalProperties": true + }, + "incubation": { + "type": "object", + "additionalProperties": true + }, + "thoughtLanes": { + "type": "object", + "additionalProperties": true + }, + "bridge": { + "type": "object", + "additionalProperties": true + }, + "candidateInterventions": { + "type": "object", + "additionalProperties": true + }, + "appraisals": { + "type": "object", + "additionalProperties": true + }, + "reactions": { + "type": "object", + "additionalProperties": true + }, + "extra": { + "type": "object", + "additionalProperties": true + } + }, + "required": [ + "schemaVersion", + "updatedAt" + ] +} diff --git a/schemas/cultnet/epiphany.agent-heartbeat.schema.json b/schemas/cultnet/epiphany.agent-heartbeat.schema.json index 44ec2f3b..33fdad17 100644 --- a/schemas/cultnet/epiphany.agent-heartbeat.schema.json +++ b/schemas/cultnet/epiphany.agent-heartbeat.schema.json @@ -2,7 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://gamecult.dev/epiphany/cultnet/epiphany.agent-heartbeat.schema.json", "title": "Epiphany Heartbeat State", - "description": "Typed heartbeat store entry for initiative, rumination, and routine physiology.", + "description": "Stable typed heartbeat scheduler state for initiative, readiness, pacing, pending turns, and history. Experimental cognition thought-weather lives in epiphany.agent-heartbeat-cognition.", "type": "object", "additionalProperties": true, "properties": { @@ -37,38 +37,6 @@ "additionalProperties": true } }, - "sleepCycle": { - "type": "object", - "additionalProperties": true - }, - "memoryResonance": { - "type": "object", - "additionalProperties": true - }, - "incubation": { - "type": "object", - "additionalProperties": true - }, - "thoughtLanes": { - "type": "object", - "additionalProperties": true - }, - "bridge": { - "type": "object", - "additionalProperties": true - }, - "candidateInterventions": { - "type": "object", - "additionalProperties": true - }, - "appraisals": { - "type": "object", - "additionalProperties": true - }, - "reactions": { - "type": "object", - "additionalProperties": true - }, "extra": { "type": "object", "additionalProperties": true diff --git a/schemas/cultnet/index.json b/schemas/cultnet/index.json index 8d0f3de0..29e7ea65 100644 --- a/schemas/cultnet/index.json +++ b/schemas/cultnet/index.json @@ -78,6 +78,17 @@ "title": "Epiphany Heartbeat State", "path": "epiphany.agent-heartbeat.schema.json" }, + { + "schemaId": "https://gamecult.dev/epiphany/cultnet/epiphany.agent-heartbeat-cognition.schema.json", + "kind": "document_payload", + "wireContracts": [ + "cultnet.schema.v0" + ], + "schemaVersion": "epiphany.agent_heartbeat_cognition.v0", + "documentType": "epiphany.agent_heartbeat_cognition", + "title": "Epiphany Heartbeat Cognition Quarantine", + "path": "epiphany.agent-heartbeat-cognition.schema.json" + }, { "schemaId": "https://gamecult.dev/epiphany/cultnet/epiphany.state-ledger.schema.json", "kind": "document_payload",