diff --git a/epiphany-core/src/resident_self.rs b/epiphany-core/src/resident_self.rs index 19d81605..317f9b78 100644 --- a/epiphany-core/src/resident_self.rs +++ b/epiphany-core/src/resident_self.rs @@ -547,6 +547,7 @@ pub enum ResidentSelfGrantFulfillment { pub fn coordinator_argv( policy: &ResidentSelfPolicy, + runtime_id: &str, turn_id: &str, wake: &ResidentSelfWake, ) -> Vec { @@ -559,7 +560,7 @@ pub fn coordinator_argv( "--model-provider".into(), policy.model_provider.clone(), "--runtime-id".into(), - policy.release_runtime_id.clone(), + runtime_id.into(), "--thread-id".into(), turn_id.into(), "--cwd".into(), @@ -598,10 +599,30 @@ pub fn resident_coordinator_thread_id(runtime_id: &str) -> Result { Ok(format!("resident-self-thread-{runtime_id}")) } +struct ResidentCoordinatorBinding { + runtime_id: String, + thread_id: String, + typed_request: Option<(String, String)>, +} + +pub fn resident_cognitive_runtime_id(runtime_store: &Path) -> Result { + let mut cache = crate::runtime_spine_cache(runtime_store)?; + cache.pull_all_backing_stores()?; + Ok(cache + .get::(crate::RUNTIME_IDENTITY_KEY)? + .ok_or_else(|| anyhow!("resident runtime store lost its immutable identity"))? + .runtime_id) +} + fn resident_coordinator_binding_for_grant( policy: &ResidentSelfPolicy, grant: &ResidentSelfHeartbeatGrant, -) -> Result<(String, Option<(String, String)>)> { +) -> Result { + let mut cache = crate::runtime_spine_cache(&policy.runtime_store)?; + cache.pull_all_backing_stores()?; + let runtime_identity = cache + .get::(crate::RUNTIME_IDENTITY_KEY)? + .ok_or_else(|| anyhow!("resident typed request runtime store lost its identity"))?; let (prefix, request_flag) = match grant.pressure_kind.as_str() { "imagination-consideration" => ( "cultcache://imagination-consideration/", @@ -616,10 +637,11 @@ fn resident_coordinator_binding_for_grant( "--proposal-modeling-request-id", ), _ => { - return Ok(( - resident_coordinator_thread_id(&policy.release_runtime_id)?, - None, - )); + return Ok(ResidentCoordinatorBinding { + thread_id: resident_coordinator_thread_id(&runtime_identity.runtime_id)?, + runtime_id: runtime_identity.runtime_id, + typed_request: None, + }); } }; let request_id = grant @@ -627,8 +649,6 @@ fn resident_coordinator_binding_for_grant( .strip_prefix(prefix) .filter(|value| !value.trim().is_empty()) .ok_or_else(|| anyhow!("consideration grant lost exact request provenance"))?; - let mut cache = crate::runtime_spine_cache(&policy.runtime_store)?; - cache.pull_all_backing_stores()?; let (runtime_id, thread_id) = match grant.pressure_kind.as_str() { "imagination-consideration" => { let request = cache @@ -649,12 +669,19 @@ fn resident_coordinator_binding_for_grant( (request.runtime_id, request.thread_id) } }; - if runtime_id != policy.release_runtime_id || thread_id.trim().is_empty() { + if runtime_id != runtime_identity.runtime_id || thread_id.trim().is_empty() { return Err(anyhow!( - "resident typed request escaped its runtime or thread authority" + "resident typed request {request_id} kind {} belongs to runtime {runtime_id} with thread-present={}, but mounted runtime store belongs to {}", + grant.pressure_kind, + !thread_id.trim().is_empty(), + runtime_identity.runtime_id, )); } - Ok((thread_id, Some((request_flag.into(), request_id.into())))) + Ok(ResidentCoordinatorBinding { + runtime_id: runtime_identity.runtime_id, + thread_id, + typed_request: Some((request_flag.into(), request_id.into())), + }) } fn prepared_coordinator_thread_id(argv: &[String]) -> Result { @@ -839,7 +866,7 @@ fn reconcile_resident_self( let launch = CoordinatorLaunch { turn_id: turn_id.clone(), wake: wake.clone(), - argv: coordinator_argv(policy, &turn_id, &wake), + argv: coordinator_argv(policy, &policy.release_runtime_id, &turn_id, &wake), }; let process = ports.launch_coordinator(&launch)?; let process_id = process.process_id; @@ -1348,12 +1375,12 @@ pub fn prepare_resident_self_launch( let Some(mut grant) = pending_resident_self_grant(path)? else { return Ok(None); }; - let (turn_id, typed_request) = resident_coordinator_binding_for_grant(policy, &grant)?; + let binding = resident_coordinator_binding_for_grant(policy, &grant)?; let wake = ResidentSelfWake::Explicit { objective: grant.objective.clone(), }; - let mut argv = coordinator_argv(policy, &turn_id, &wake); - if let Some((request_flag, request_id)) = typed_request { + let mut argv = coordinator_argv(policy, &binding.runtime_id, &binding.thread_id, &wake); + if let Some((request_flag, request_id)) = binding.typed_request { let objective = argv.pop(); let flag = argv.pop(); if objective.is_none() || flag.as_deref() != Some("--objective") { @@ -2244,12 +2271,36 @@ mod tests { release_witness_sha256: "sha256:test-manifest".into(), } } + fn seed_runtime_identity(store: &Path, runtime_id: &str) -> Result<()> { + let mut cache = crate::runtime_spine_cache(store)?; + cache.pull_all_backing_stores()?; + if cache + .get::(crate::RUNTIME_IDENTITY_KEY)? + .is_some() + { + return Ok(()); + } + let identity = crate::EpiphanyRuntimeIdentity { + schema_version: crate::RUNTIME_SPINE_SCHEMA_VERSION.into(), + runtime_id: runtime_id.into(), + display_name: "Resident test runtime".into(), + runtime_kind: "resident-test".into(), + created_at: "2026-07-18T00:00:00Z".into(), + updated_at: "2026-07-18T00:00:00Z".into(), + supported_document_types: Vec::new(), + metadata: BTreeMap::new(), + }; + let (entry, _) = cache.prepare_entry(crate::RUNTIME_IDENTITY_KEY, &identity)?; + SingleFileMessagePackBackingStore::new(store).push(&entry)?; + Ok(()) + } fn seed_model_direction_request( store: &Path, request_id: &str, runtime_id: &str, thread_id: &str, ) -> Result<()> { + seed_runtime_identity(store, runtime_id)?; let cache = crate::runtime_spine_cache(store)?; let request = crate::AdmittedModelDirectionConsiderationRequest { schema_version: crate::ADMITTED_MODEL_DIRECTION_CONSIDERATION_REQUEST_SCHEMA_VERSION @@ -2346,6 +2397,7 @@ mod tests { thread_id: &str, selected_at: &str, ) -> Result<()> { + seed_runtime_identity(store, runtime_id)?; let cache = crate::runtime_spine_cache(store)?; let request = crate::RepoFrontierProposalModelingRequest { schema_version: crate::REPO_FRONTIER_PROPOSAL_MODELING_REQUEST_SCHEMA_VERSION.into(), @@ -2473,7 +2525,7 @@ mod tests { seed_model_direction_request( &policy.runtime_store, "request-model-1", - &policy.release_runtime_id, + "cognitive-runtime", "implementation-thread-1", )?; let pressure = ResidentSelfPressure { @@ -2506,6 +2558,18 @@ mod tests { "--admitted-model-direction-consideration-request-id", "request-model-1" ])); + assert!( + prepared + .argv + .windows(2) + .any(|pair| pair == ["--runtime-id", "cognitive-runtime"]) + ); + assert!( + !prepared + .argv + .windows(2) + .any(|pair| pair == ["--runtime-id", policy.release_runtime_id.as_str()]) + ); assert_eq!( resident_prepared_launch_thread_id(&prepared)?, "implementation-thread-1" @@ -2581,7 +2645,7 @@ mod tests { .into(), result_id: "result-model-1".into(), request_id: "request-model-1".into(), - runtime_id: policy.release_runtime_id.clone(), + runtime_id: "cognitive-runtime".into(), thread_id: "implementation-thread-1".into(), model_revision: 1, model_hash: "sha256:model-1".into(), @@ -2624,6 +2688,96 @@ mod tests { Ok(()) } + #[test] + fn typed_request_must_belong_to_its_mounted_runtime_store_not_release_runtime() -> Result<()> { + let temp = tempfile::tempdir()?; + let store = temp.path().join("resident-self.cc"); + let coordinator = temp.path().join("epiphany-mvp-coordinator"); + std::fs::write(&coordinator, b"witnessed executable")?; + let mut policy = policy(); + policy.coordinator_bin = coordinator; + policy.runtime_store = temp.path().join("runtime.cc"); + seed_runtime_identity(&policy.runtime_store, "cognitive-runtime")?; + seed_model_direction_request( + &policy.runtime_store, + "request-foreign-runtime", + "foreign-runtime", + "implementation-thread-foreign", + )?; + enqueue_resident_self_pressure( + &store, + &ResidentSelfPressure { + schema_version: RESIDENT_SELF_PRESSURE_SCHEMA_VERSION.into(), + pressure_id: "pressure-foreign-runtime".into(), + kind: "admitted-model-direction-consideration".into(), + provenance_ref: + "cultcache://admitted-model-direction-consideration/request-foreign-runtime" + .into(), + objective: "This request must remain in its foreign runtime.".into(), + created_at_millis: 1, + status: "pending".into(), + consumed_by_grant_id: None, + private_state_exposed: false, + }, + )?; + heartbeat_issue_resident_self_grant(&store, "heartbeat-foreign", "action-foreign", 2)? + .expect("foreign request grant fixture"); + let error = prepare_resident_self_launch(&store, &policy, 3) + .expect_err("foreign request must fail closed"); + assert!( + error + .to_string() + .contains("belongs to runtime foreign-runtime") + ); + assert!( + error + .to_string() + .contains("mounted runtime store belongs to cognitive-runtime") + ); + Ok(()) + } + + #[test] + fn plain_objective_uses_mounted_runtime_identity_for_thread_and_argv() -> Result<()> { + let temp = tempfile::tempdir()?; + let store = temp.path().join("resident-self.cc"); + let coordinator = temp.path().join("epiphany-mvp-coordinator"); + std::fs::write(&coordinator, b"witnessed executable")?; + let mut policy = policy(); + policy.coordinator_bin = coordinator; + policy.runtime_store = temp.path().join("runtime.cc"); + seed_runtime_identity(&policy.runtime_store, "cognitive-runtime")?; + enqueue_resident_self_pressure( + &store, + &ResidentSelfPressure { + schema_version: RESIDENT_SELF_PRESSURE_SCHEMA_VERSION.into(), + pressure_id: "operator-pressure".into(), + kind: "operator-objective".into(), + provenance_ref: "operator://objective".into(), + objective: "Run one bounded planning turn.".into(), + created_at_millis: 1, + status: "pending".into(), + consumed_by_grant_id: None, + private_state_exposed: false, + }, + )?; + heartbeat_issue_resident_self_grant(&store, "heartbeat-plain", "action-plain", 2)? + .expect("plain objective grant"); + let prepared = + prepare_resident_self_launch(&store, &policy, 3)?.expect("plain objective preparation"); + assert!( + prepared + .argv + .windows(2) + .any(|pair| pair == ["--runtime-id", "cognitive-runtime"]) + ); + assert_eq!( + resident_prepared_launch_thread_id(&prepared)?, + "resident-self-thread-cognitive-runtime" + ); + Ok(()) + } + #[test] fn model_direction_grant_waits_for_exact_terminal_imagination_result() -> Result<()> { let temp = tempfile::tempdir()?; diff --git a/notes/epiphany-current-algorithmic-map.md b/notes/epiphany-current-algorithmic-map.md index 057af362..910f2f57 100644 --- a/notes/epiphany-current-algorithmic-map.md +++ b/notes/epiphany-current-algorithmic-map.md @@ -1304,7 +1304,29 @@ Typed pressure does not own a new coordinator thread. Imagination consideration, admitted-model direction consideration, and autonomous proposal Modeling requests each carry the implementation thread that owns their causal state. Resident preparation reloads that exact request from the runtime store, -verifies its runtime identity, and binds its thread into the coordinator argv. +verifies its runtime identity against the immutable identity of that mounted +runtime store, and binds its thread into the coordinator argv. The release +publication runtime is a separate authority: it authenticates the packaged +binary body but does not decide which cognitive runtime owns a typed request. +This distinction matters in the live Starfire topology, where release runtime +`epiphany-starfire-linux` mounts the Eyes cognition store whose identity is +`epiphany-starfire-linux-eyes-tools`. Comparing a request to the release +runtime falsely rejects valid local cognition; comparing it to its own store +still refuses a request imported from a foreign runtime. + +The live identity matrix is explicit: + +- packaged-release witness runtime authenticates the binary publication; +- mounted runtime-store identity owns coordinator execution, request identity, + runtime mutation, and the coordinator's `--runtime-id`; +- local-Verse brake identity is the namespace read by the process using that + Verse store and must contain the canonical brake entry; +- Persona feedback target identity belongs to the feedback crossing contract + and is not silently rewritten when cognition mounts a narrower runtime. + +Plain operator objectives also execute against the mounted cognitive runtime +and derive `resident-self-thread-{cognitive-runtime}`. They do not get a second +runtime truth merely because no typed domain request supplied one. The prepared argv is then the single source for child launch and lease thread identity. Only plain operator objectives use the resident runtime thread. `epiphany-swarm` no longer reconstructs a fixed thread after preparation. diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index 92741d51..74330d66 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -1114,6 +1114,45 @@ requeue. This cut is not packaged yet. Commit and publish it, package the exact source with the persistent Starfire cache, authenticate all 24 binaries and witness, then use only that release for active cognition endurance. +Commit `30a63d63` packaged after a failed-closed construction-cache repair. +The clone initially lacked declared submodules; after exact initialization the +constructor found the known stale +`source-fb4097c8f489c830678f32a39e818cf462bff5c1fe853bc12d106184c9bfd571` +worktree whose Git common-dir named a deleted clone. Only that verified 57 MiB +source directory was removed; Cargo home and target caches survived. The same +container then completed in 8m26s. Exact release is +`sha256-a1e8a0985573f7bacf5316c62080ed4bfb8b00c7214331e0baf775d24a7a50a4`; +witness is +`sha256-473482113ac16e6656e2cb02582ee5696818b3255a7184960e8839aa291bf3b9`. +Independent packaged inspection accepted exact source and all 24 Linux ELF +binaries with zero warnings. + +The first real active-cognition attempt is preserved under +`.epiphany-run/active-cognition-30a63d63`. Packaged publication into the copied +Verse and both canonical brake releases succeeded. Heartbeat moved from +braked to `active-coordinator-turn`, but Self failed before preparation with +`resident typed request escaped its runtime or thread authority`; no worker or +external consequence launched. A quarantined read-only typed-state diagnostic +proved the request was valid, unresolved, and owned by runtime +`epiphany-starfire-linux-eyes-tools` on thread `linux-eyes-tools-c005`, while +the policy's `release_runtime_id` was `epiphany-starfire-linux`. The code had +collapsed binary-publication identity and cognitive-store identity. Both +brakes were re-engaged. Self exited 1; the manually stopped Heartbeat required +kill after its 15-second stop window and exited 137, so this run is failed +evidence, not endurance acceptance. + +The source cut now loads the mounted runtime store's immutable +`EpiphanyRuntimeIdentity`; that one owner supplies typed request validation, +plain-work thread identity, and the coordinator's `--runtime-id`. Release +runtime identity remains binary-authentication authority only. Persona feedback +target identity remains crossing-owned rather than being mass-renamed. The +diagnostic error now names safe request/kind/runtime identities, a negative test +proves a request from a foreign runtime still fails closed, and differing-ID +tests require both typed and plain prepared argv to name the cognitive runtime. +Twenty-four resident and eight swarm binary tests pass. Commit, package, +authenticate, then rerun from a fresh copy of the accepted idle baseline; do +not reuse the failed active body. + Resident lifecycle retention is now implemented in source. Resident Self's serve/once owner invokes a configurable `--retained-closed-lifecycles` bound (default 256). Only Heartbeat-consumed terminal lifecycles whose pressure still diff --git a/state/map.yaml b/state/map.yaml index d003f063..9e45526e 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -36,7 +36,7 @@ invariants: current_status: summary: Epiphany is a supervised engineering alpha on Starfire, not deployment-ready. Exact b8138a6b active-child SIGTERM and restart, exact 9f369528 deterministic warm packaging, exact c930f505 cross-container ownership, exact ed15f83a resident lifecycle retention, and exact 16c4b19f coordinator receipt and Heartbeat artifact retention are proven. Gamecult-ops 059821f makes journald the sole Yggdrasil supervisor/projector stream-retention owner. The exact 16c4b19f genuine-idle control completed 900 refused pulses with constant state, zero artifacts, flat RSS, and clean direct-PID exits. Typed active cognition endurance, broad session/model/event/tool retention, and stable Starfire firewall ownership remain open. Yggdrasil is the small public crossing body, never the release forge. - latest_phase: Resident closure source now separates process termination from cognitive fulfillment. A zero-exit coordinator with its exact receipt retains the active lease as awaiting-fulfillment until the runtime-owned worker result and exact typed companion prove the grant terminal; brake, shutdown, or timeout cancels and requeues once through the existing CAS. The raw completion writer is private to that settling seam, and Imagination candidates cannot predate their request. Twenty-two resident tests, eight swarm binary tests, and eight Imagination consideration tests pass. The cut remains uncommitted and unpackaged. + latest_phase: Exact 30a63d63 packaged as release sha256-a1e8a0985573f7bacf5316c62080ed4bfb8b00c7214331e0baf775d24a7a50a4 with witness sha256-473482113ac16e6656e2cb02582ee5696818b3255a7184960e8839aa291bf3b9, 24 Linux ELF binaries, and zero warnings. The first real active-cognition attempt failed closed before launch because resident preparation compared the valid Eyes cognitive-runtime request identity epiphany-starfire-linux-eyes-tools to the separate release-publication runtime epiphany-starfire-linux. Source now makes the mounted runtime store's immutable identity own request validation, coordinator --runtime-id, and plain-work thread identity; release identity remains binary authentication only and Persona feedback target identity remains crossing-owned. Twenty-four resident and eight swarm binary tests pass; the cut is uncommitted and unpackaged. operational_state: local_worktree: coordinator_receipt_retention_packaged_proven operator_wireguard: restored @@ -69,7 +69,7 @@ current_status: yggdrasil_epiphany_service_contract: repaired_in_gamecult_ops_812b427_not_installed resident_physiology: authenticated_9b8df0b4_c005_bound_to_eyes_tools_nervous_system_with_217dd_and_bd96305e_helpers_running unresolved_authority: Coordinator receipt, Heartbeat pulse-artifact, deployed supervisor/projector log retention, and the genuine-idle resource plateau are accepted. Broader semantic/runtime histories remain unbounded pending a typed archived-session owner. Active cognition fulfillment and return-to-plateau remain unproven on an exact package. Repository Body observation still performs two authenticated full-tree Git index/manifest passes. Execute-mode workers require a persistent process body. Keyed storage has not earned live migration. One Discord consequence remains blocked by the absent Yggdrasil credential. - next_action: Commit and push the tested resident typed-fulfillment ownership cut, then package and authenticate that exact source with the persistent Starfire cache. Use only the authenticated repaired release on copied state for the mapped real admitted-model-direction active cognition endurance pass; prove pending-to-typed fulfillment, exact acknowledgement, cooldown plateau, bounded stores/artifacts, no external consequence, and clean direct-PID shutdown. Preserve active c005. Do not compact session/model/event/tool history until typed archived-session ownership exists. + next_action: Commit and push the tested cognitive-runtime identity ownership cut, then package and authenticate that exact source with the persistent Starfire cache. Rerun active cognition from a fresh accepted-idle copy, never the failed active body; prove the valid Eyes-runtime request launches, coordinator exit cannot acknowledge before typed fulfillment, one exact result closes one grant, cooldown returns to plateau, stores/artifacts remain bounded, no external consequence occurs, and direct-PID shutdown is clean. Preserve active c005. Do not compact session/model/event/tool history until typed archived-session ownership exists. active_subgoals: - Prove Persona, Self, Eyes, Modeling, Imagination, Hands, Soul, Mind, Continuity, and nervous-system behavior individually.