Separate worker launch from bootstrap

This commit is contained in:
Meta Vens 2026-07-12 21:25:38 +01:00
commit f17a6d4585
7 changed files with 90 additions and 11 deletions

View file

@ -20,15 +20,17 @@ use epiphany_core::query_epiphany_local_verse_context;
use epiphany_core::render_epiphany_prompt_context;
use epiphany_core::seed_epiphany_local_verse_context;
use serde_json::json;
use std::env;
use std::path::PathBuf;
fn main() -> Result<()> {
let store = env::args().nth(1).map(PathBuf::from).unwrap_or_else(|| {
PathBuf::from(".epiphany-smoke")
.join("cultmesh")
.join("epiphany-prompt-context.ccmp")
});
if std::env::args().nth(1).is_some() {
anyhow::bail!(
"prompt-context smoke accepts no arguments and writes only beneath .epiphany-smoke"
);
}
let store = PathBuf::from(".epiphany-smoke")
.join("cultmesh")
.join("epiphany-prompt-context.ccmp");
if let Some(parent) = store.parent() {
std::fs::create_dir_all(parent)?;
}

View file

@ -6,6 +6,8 @@ use crate::EpiphanyMemoryContextQuery;
use crate::EpiphanyMemoryProfile;
use crate::EpiphanyPromptContextInput;
use crate::RuntimeHandsReceiptChainSummary;
use crate::load_epiphany_cultmesh_cluster_topology;
use crate::load_epiphany_cultmesh_status;
use crate::load_latest_epiphany_cultmesh_work_loop_telemetry;
use crate::load_memory_graph_snapshot;
use crate::memory_graph_from_epiphany_graphs;
@ -16,7 +18,6 @@ use crate::runtime_hands_command_receipt;
use crate::runtime_hands_commit_receipt;
use crate::runtime_hands_patch_receipt;
use crate::runtime_latest_hands_receipt_chain_after;
use crate::seed_epiphany_local_verse_context;
use crate::write_epiphany_cultmesh_work_loop_telemetry;
use crate::write_memory_graph_snapshot;
use chrono::SecondsFormat;
@ -72,17 +73,35 @@ pub fn render_launch_dynamic_prompt_context(
focus: String,
) -> Result<String, String> {
let local_verse_store = local_verse_store_path(runtime_store_path);
seed_epiphany_local_verse_context(
load_epiphany_cultmesh_status(&local_verse_store, EPIPHANY_LOCAL_VERSE_RUNTIME_ID)
.map_err(|error| {
format!(
"failed to inspect local Verse context store {}: {error}",
local_verse_store.display()
)
})?
.ok_or_else(|| {
format!(
"local Verse is not bootstrapped at {}; initialize it before building worker launch context",
local_verse_store.display()
)
})?;
let topology = load_epiphany_cultmesh_cluster_topology(
&local_verse_store,
EPIPHANY_LOCAL_VERSE_RUNTIME_ID,
Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true),
)
.map_err(|error| {
format!(
"failed to seed local Verse context store {}: {error}",
"failed to inspect local Verse topology store {}: {error}",
local_verse_store.display()
)
})?;
if topology.is_empty() {
return Err(format!(
"local Verse has no persisted cluster topology at {}; initialize it before building worker launch context",
local_verse_store.display()
));
}
let local_verse =
query_epiphany_local_verse_context(&local_verse_store, EPIPHANY_LOCAL_VERSE_RUNTIME_ID)
.map_err(|error| {
@ -681,6 +700,7 @@ mod tests {
use crate::put_hands_commit_receipt;
use crate::put_hands_patch_receipt;
use crate::runtime_worker_launch_request;
use crate::seed_epiphany_local_verse_context;
use epiphany_state_model::EpiphanyAcceptanceReceipt;
use std::fs;
use uuid::Uuid;
@ -713,6 +733,11 @@ mod tests {
objective: Some("Test launch context.".to_string()),
..Default::default()
};
seed_epiphany_local_verse_context(
local_verse_store_path(&runtime_store),
EPIPHANY_LOCAL_VERSE_RUNTIME_ID,
"2026-07-12T00:00:00Z",
)?;
let rendered = render_launch_dynamic_prompt_context(
&runtime_store,
@ -771,6 +796,33 @@ mod tests {
Ok(())
}
#[test]
fn launch_context_refuses_to_bootstrap_shared_state() -> anyhow::Result<()> {
let temp = std::env::temp_dir().join(format!(
"epiphany-launch-context-unbootstrapped-{}",
Uuid::new_v4()
));
fs::create_dir_all(&temp)?;
let runtime_store = temp.join("runtime-spine.msgpack");
let local_verse_store = local_verse_store_path(&runtime_store);
let state = EpiphanyThreadState {
revision: 1,
objective: Some("Prove launch assembly cannot initialize shared state.".to_string()),
..Default::default()
};
let error = render_launch_dynamic_prompt_context(
&runtime_store,
&state,
role_launch_context_focus(&state, "modeling"),
)
.expect_err("unbootstrapped launch context must fail closed");
assert!(error.contains("local Verse is not bootstrapped"));
assert!(!local_verse_store.exists());
Ok(())
}
#[test]
fn verification_launch_context_includes_hands_receipt_chain() -> anyhow::Result<()> {
let temp = std::env::temp_dir().join(format!("epiphany-hands-context-{}", Uuid::new_v4()));

View file

@ -169,3 +169,21 @@ paths may initialize the local Verse. Every other mutating query command passes
and refuses before store creation when either is absent. Requesters now write
into an existing body; they cannot manufacture the body as a side effect of
asking it for work.
## Launch-context/bootstrap split
The final implicit production caller was
`render_launch_dynamic_prompt_context`. Building a worker launch packet seeded
the sibling local Verse before reading it, so launch preparation owned shared
policy, topology, contracts, brake initialization, and operator status.
Launch-context assembly now requires persisted Epiphany status and topology and
fails before creating the Verse store when they are absent. Tests and the
prompt-context smoke initialize their fixtures explicitly. The smoke no longer
accepts an arbitrary output path; it writes only
`.epiphany-smoke/cultmesh/epiphany-prompt-context.ccmp`.
The remaining non-test seed callers are now limited to the explicit Verse seed
commands and two named, path-confined smoke fixture initializers. No production
provider, supervisor, requester, diagnostic, or launch path bootstraps shared
state as a convenience side effect.

View file

@ -106,6 +106,10 @@
quarantined smoke initialize local Verse state. Every operator/requester
mutation requires persisted status and topology before writing its bounded
brake, poke, tool, Eve, Bifrost, metrics, artifact, or feedback document.
- Coordinator launch-context assembly also requires persisted status and
topology. Rendering a worker prompt cannot initialize shared state. Its
standalone smoke accepts no path arguments and writes 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

@ -30,6 +30,9 @@ The confirmed conceptual substitutions have been cut:
- query-CLI requester/operator mutations also require persisted bootstrap.
Only explicit seed commands and the fixed aggregate smoke may initialize the
shared local Verse.
- worker launch-context rendering requires the existing Verse and cannot seed
it. The prompt-context smoke accepts no destination and is fixed 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, cluster-daemon bootstrap, daemon-supervisor production bootstrap, and requester/operator query-side bootstrap are cut. Provider absence survives bootstrap; only explicit seed/fixture paths initialize; each cluster daemon writes its own presence and pulse; Idunn and requester paths require the shared body rather than repainting it; synthetic seeding is quarantined. 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, provider/bootstrap collapse, the central topology-derived Odin publisher, all-provider writers, cluster-daemon bootstrap, daemon-supervisor production bootstrap, requester/operator query bootstrap, and launch-context bootstrap are cut. Only explicit seed commands and named path-confined fixtures initialize; providers, Idunn, requesters, diagnostics, and worker-launch assembly require the existing shared body. 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.