From fe563ee271fc465370339712c04dd17cce27767a Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sat, 11 Jul 2026 20:44:05 +0100 Subject: [PATCH] Delegate reorientation acceptance --- epiphany-codex-bridge/src/mutation_service.rs | 167 +++--------------- epiphany-core/src/coordinator_acceptance.rs | 98 ++++++++++ epiphany-core/src/coordinator_service.rs | 30 +++- epiphany-core/src/lib.rs | 2 + notes/fresh-workspace-handoff.md | 10 +- notes/native-coordinator-liberation-map.md | 4 +- state/ledgers.msgpack | Bin 405011 -> 405407 bytes state/map.yaml | 2 +- 8 files changed, 165 insertions(+), 148 deletions(-) diff --git a/epiphany-codex-bridge/src/mutation_service.rs b/epiphany-codex-bridge/src/mutation_service.rs index 9defeb80..f000a411 100644 --- a/epiphany-codex-bridge/src/mutation_service.rs +++ b/epiphany-codex-bridge/src/mutation_service.rs @@ -3,16 +3,13 @@ use chrono::Utc; use std::path::Path; use std::path::PathBuf; -use epiphany_core::CONTINUITY_RECOVERY_RECEIPT_TYPE; use epiphany_core::EPIPHANY_REORIENT_LAUNCH_BINDING_ID; use epiphany_core::EpiphanyJobInterruptRequest; use epiphany_core::EpiphanyJobInterruptResult; use epiphany_core::EpiphanyJobLaunchRequest; use epiphany_core::EpiphanyJobLaunchResult; use epiphany_core::EpiphanyJobView; -use epiphany_core::EpiphanyLaunchOrganContract; use epiphany_core::EpiphanyPromotionInput; -use epiphany_core::EpiphanyReceiptEffectKind; use epiphany_core::EpiphanyReorientDecision; use epiphany_core::EpiphanyReorientFindingInterpretation; use epiphany_core::EpiphanyReorientStateStatus; @@ -22,15 +19,12 @@ use epiphany_core::EpiphanyRoleStatePatchDocument; use epiphany_core::EpiphanyStateUpdate; use epiphany_core::EpiphanyStateUpdatedField; use epiphany_core::EpiphanyTokenUsageSnapshot; -use epiphany_core::MIND_GATEWAY_REVIEW_TYPE; use epiphany_core::apply_coordinator_state_update_to_state; use epiphany_core::build_epiphany_reorient_launch_request_with_dynamic_context; use epiphany_core::build_epiphany_role_launch_request_with_dynamic_context; use epiphany_core::clear_epiphany_job_binding_backend; -use epiphany_core::continuity_recovery_receipt_from_reorient_finding; use epiphany_core::epiphany_role_label; use epiphany_core::evaluate_promotion; -use epiphany_core::mind_state_commit_receipt; use epiphany_state_model::EpiphanyEvidenceRecord; use epiphany_state_model::EpiphanyJobKind as CoreEpiphanyJobKind; use epiphany_state_model::EpiphanyRetrievalState; @@ -48,7 +42,6 @@ use crate::launch_context::append_verification_hands_receipt_context; use crate::launch_context::render_launch_dynamic_prompt_context; use crate::launch_context::reorient_launch_context_focus; use crate::launch_context::role_launch_context_focus; -use crate::mutation::build_reorient_acceptance_update; use crate::mutation::epiphany_job_launch_changed_fields; use crate::mutation::epiphany_promote_changed_fields; use crate::mutation::epiphany_update_patch_changed_fields; @@ -57,7 +50,6 @@ use crate::pressure::derive_epiphany_pressure; use crate::reorient::EpiphanyFreshnessWatcherSnapshot; use crate::reorient::derive_epiphany_freshness_view; use crate::reorient::derive_epiphany_reorient; -use crate::runtime_results::load_completed_core_epiphany_reorient_finding; use uuid::Uuid; #[allow(async_fn_in_trait)] @@ -373,94 +365,33 @@ pub async fn apply_thread_epiphany_reorient_accept( .to_string(), ) })?; - validate_expected_revision(expected_revision, state.revision)?; - if update_investigation_checkpoint && state.investigation_checkpoint.is_none() { - return Err(EpiphanyBridgeError::InvalidRequest( - "cannot update investigation checkpoint because this thread has no durable checkpoint" - .to_string(), - )); - } - let runtime_store_path = thread.epiphany_runtime_spine_store_path().await; - let finding = load_completed_core_epiphany_reorient_finding( - Some(runtime_store_path.as_path()), - &state, - binding_id, - )?; - let acceptance_update = build_reorient_acceptance_update( - expected_revision, - binding_id, - &finding, - format!("ev-reorient-{}", Uuid::new_v4()), - format!("obs-reorient-{}", Uuid::new_v4()), - Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true), - update_scratch, - update_investigation_checkpoint, - state.investigation_checkpoint.clone(), - ) - .map_err(EpiphanyBridgeError::InvalidRequest)?; - let organ_contract = - launch_contract_for_binding(runtime_store_path.as_path(), &state, binding_id, "reorient")?; - - let accepted_receipt_id = acceptance_update.accepted_receipt_id.clone(); - let accepted_observation_id = acceptance_update.accepted_observation_id.clone(); - let accepted_evidence_id = acceptance_update.accepted_evidence_id.clone(); - let changed_fields = acceptance_update.changed_fields.clone(); - let mut available_document_types = vec![MIND_GATEWAY_REVIEW_TYPE.to_string()]; - let recovery_receipt = continuity_recovery_receipt_from_reorient_finding( - format!("continuity-recovery-{accepted_receipt_id}"), - binding_id.to_string(), - &finding, - Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true), - ); - available_document_types.push(CONTINUITY_RECOVERY_RECEIPT_TYPE.to_string()); - enforce_current_receipt_proofs( - &organ_contract, - &reorient_acceptance_claimed_effects(), - &available_document_types, - &[ - MIND_GATEWAY_REVIEW_TYPE.to_string(), - CONTINUITY_RECOVERY_RECEIPT_TYPE.to_string(), - ], - )?; - let reference_turn_id = thread.epiphany_reference_turn_id().await; - let epiphany_state = epiphany_core::apply_coordinator_state_update_to_state( - &state, - acceptance_update.state_update, - reference_turn_id, - ) - .map_err(|error| EpiphanyBridgeError::InvalidRequest(error.to_string()))?; - let commit_receipt = mind_state_commit_receipt( - format!("mind-commit-{accepted_receipt_id}"), - &acceptance_update.mind_review, - epiphany_state.revision, - changed_fields - .iter() - .map(|field| format!("{field:?}")) - .collect(), - Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true), - ); let thread_id = thread.epiphany_thread_id().await; - epiphany_core::commit_state_with_mind_witness( - runtime_store_path.as_path(), - &thread_id, - &epiphany_state, - &acceptance_update.mind_review, - &commit_receipt, - &[epiphany_core::EpiphanyAcceptancePrerequisite::Continuity( - recovery_receipt, - )], - ) - .map_err(|error| EpiphanyBridgeError::Fatal(error.to_string()))?; - + let service = epiphany_core::EpiphanyCoordinatorService::new( + runtime_store_path.clone(), + runtime_store_path, + ); + let accepted = service + .accept_reorient( + &thread_id, + &state, + binding_id, + expected_revision, + thread.epiphany_reference_turn_id().await, + Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true), + Uuid::new_v4().to_string().as_str(), + update_scratch, + update_investigation_checkpoint, + ) + .map_err(|error| EpiphanyBridgeError::InvalidRequest(error.to_string()))?; Ok(EpiphanyReorientAcceptApplied { - revision: epiphany_state.revision, - changed_fields, - epiphany_state, - accepted_receipt_id, - accepted_observation_id, - accepted_evidence_id, - finding, + revision: accepted.state.revision, + changed_fields: accepted.update.changed_fields, + epiphany_state: accepted.state, + accepted_receipt_id: accepted.update.accepted_receipt_id, + accepted_observation_id: accepted.update.accepted_observation_id, + accepted_evidence_id: accepted.update.accepted_evidence_id, + finding: accepted.finding, }) } @@ -674,53 +605,6 @@ fn validate_expected_revision( Ok(()) } -fn launch_contract_for_binding( - runtime_store_path: &Path, - state: &EpiphanyThreadState, - binding_id: &str, - expected_document_kind: &str, -) -> BridgeResult { - epiphany_core::acceptance_launch_contract_for_binding( - runtime_store_path, - state, - binding_id, - expected_document_kind, - ) - .map_err(map_coordinator_admission_error) -} - -fn enforce_current_receipt_proofs( - contract: &EpiphanyLaunchOrganContract, - claimed_effects: &[EpiphanyReceiptEffectKind], - available_document_types: &[String], - enforceable_document_types: &[String], -) -> BridgeResult<()> { - epiphany_core::enforce_acceptance_receipt_proofs( - contract, - claimed_effects, - available_document_types, - enforceable_document_types, - ) - .map_err(map_coordinator_admission_error) -} - -fn reorient_acceptance_claimed_effects() -> Vec { - epiphany_core::reorient_acceptance_claimed_effects() -} - -fn map_coordinator_admission_error( - error: epiphany_core::EpiphanyCoordinatorAdmissionError, -) -> EpiphanyBridgeError { - match error { - epiphany_core::EpiphanyCoordinatorAdmissionError::InvalidRequest(message) => { - EpiphanyBridgeError::InvalidRequest(message) - } - epiphany_core::EpiphanyCoordinatorAdmissionError::Store(message) => { - EpiphanyBridgeError::Fatal(message) - } - } -} - #[cfg(test)] mod acceptance_architecture_tests { #[test] @@ -776,8 +660,9 @@ mod acceptance_architecture_tests { } assert_eq!( acceptance.matches("commit_state_with_mind_witness").count(), - 1 + 0 ); assert_eq!(acceptance.matches(".accept_role(").count(), 1); + assert_eq!(acceptance.matches(".accept_reorient(").count(), 1); } } diff --git a/epiphany-core/src/coordinator_acceptance.rs b/epiphany-core/src/coordinator_acceptance.rs index d8a229e7..bbc9bd77 100644 --- a/epiphany-core/src/coordinator_acceptance.rs +++ b/epiphany-core/src/coordinator_acceptance.rs @@ -53,6 +53,104 @@ pub struct EpiphanyNativeRoleAcceptance { pub update: EpiphanyRoleAcceptanceUpdate, } +#[derive(Debug, Clone)] +pub struct EpiphanyNativeReorientAcceptance { + pub state: EpiphanyThreadState, + pub finding: EpiphanyReorientFindingInterpretation, + pub update: EpiphanyReorientAcceptanceUpdate, +} + +pub fn accept_coordinator_reorient_finding( + store: &Path, + thread_id: &str, + state: &EpiphanyThreadState, + binding_id: &str, + expected_revision: Option, + reference_turn_id: Option, + accepted_at: String, + nonce: &str, + update_scratch: bool, + update_investigation_checkpoint: bool, +) -> anyhow::Result { + if update_investigation_checkpoint && state.investigation_checkpoint.is_none() { + return Err(anyhow::anyhow!( + "cannot update investigation checkpoint because state has no durable checkpoint" + )); + } + let snapshot = read_reorient_result_snapshot(Some(state), Some(store), binding_id); + if snapshot.status != EpiphanyCrrcResultStatus::Completed { + return Err(anyhow::anyhow!( + "reorientation finding is not completed: {}", + snapshot.note + )); + } + let finding = snapshot + .finding + .ok_or_else(|| anyhow::anyhow!("completed reorientation result has no typed finding"))?; + let update = build_native_reorient_acceptance_update( + expected_revision, + binding_id, + &finding, + format!("ev-reorient-{nonce}"), + format!("obs-reorient-{nonce}"), + accepted_at.clone(), + update_scratch, + update_investigation_checkpoint, + state.investigation_checkpoint.clone(), + ) + .map_err(anyhow::Error::msg)?; + let contract = acceptance_launch_contract_for_binding(store, state, binding_id, "reorient") + .map_err(|error| anyhow::anyhow!(error.to_string()))?; + enforce_acceptance_receipt_proofs( + &contract, + &reorient_acceptance_claimed_effects(), + &[ + MIND_GATEWAY_REVIEW_TYPE.to_string(), + CONTINUITY_RECOVERY_RECEIPT_TYPE.to_string(), + ], + &[ + MIND_GATEWAY_REVIEW_TYPE.to_string(), + CONTINUITY_RECOVERY_RECEIPT_TYPE.to_string(), + ], + ) + .map_err(|error| anyhow::anyhow!(error.to_string()))?; + let recovery = continuity_recovery_receipt_from_reorient_finding( + format!("continuity-recovery-{}", update.accepted_receipt_id), + binding_id.to_string(), + &finding, + accepted_at.clone(), + ); + let next_state = apply_coordinator_state_update_to_state( + state, + update.state_update.clone(), + reference_turn_id, + )?; + let commit = mind_state_commit_receipt( + format!("mind-commit-{}", update.accepted_receipt_id), + &update.mind_review, + next_state.revision, + update + .changed_fields + .iter() + .map(|field| format!("{field:?}")) + .collect(), + accepted_at, + ); + commit_state_with_mind_witness( + store, + thread_id, + &next_state, + &update.mind_review, + &commit, + &[EpiphanyAcceptancePrerequisite::Continuity(recovery)], + )?; + Ok(EpiphanyNativeReorientAcceptance { + state: next_state, + finding, + update, + }) +} + pub fn accept_coordinator_role_finding( store: &Path, thread_id: &str, diff --git a/epiphany-core/src/coordinator_service.rs b/epiphany-core/src/coordinator_service.rs index 908d1079..6904b4f6 100644 --- a/epiphany-core/src/coordinator_service.rs +++ b/epiphany-core/src/coordinator_service.rs @@ -3,9 +3,11 @@ use crate::EpiphanyCoordinatorRoleResultSnapshot; use crate::EpiphanyCoordinatorStateApplied; use crate::EpiphanyJobLaunchRequest; use crate::EpiphanyJobLaunchResult; +use crate::EpiphanyNativeReorientAcceptance; use crate::EpiphanyNativeRoleAcceptance; use crate::EpiphanyRoleResultRoleId; use crate::EpiphanyStateUpdate; +use crate::accept_coordinator_reorient_finding; use crate::accept_coordinator_role_finding; use crate::apply_coordinator_state_update; use crate::commit_coordinator_job_launch; @@ -133,6 +135,32 @@ impl EpiphanyCoordinatorService { nonce, ) } + + pub fn accept_reorient( + &self, + thread_id: &str, + state: &EpiphanyThreadState, + binding_id: &str, + expected_revision: Option, + reference_turn_id: Option, + accepted_at: String, + nonce: &str, + update_scratch: bool, + update_investigation_checkpoint: bool, + ) -> Result { + accept_coordinator_reorient_finding( + &self.runtime_spine_store, + thread_id, + state, + binding_id, + expected_revision, + reference_turn_id, + accepted_at, + nonce, + update_scratch, + update_investigation_checkpoint, + ) + } } #[cfg(test)] @@ -142,7 +170,7 @@ mod tests { let source = include_str!("coordinator_service.rs"); let production = source.split("#[cfg(test)]").next().unwrap_or(source); assert!( - production.lines().count() < 140, + production.lines().count() < 175, "coordinator facade regrew into a host brain" ); for forbidden in [ diff --git a/epiphany-core/src/lib.rs b/epiphany-core/src/lib.rs index 78b4446d..76aa262d 100644 --- a/epiphany-core/src/lib.rs +++ b/epiphany-core/src/lib.rs @@ -126,9 +126,11 @@ pub use agent_utterance_state::derive_agent_utterance_state; pub use continuity_gateway::*; pub use coordinator_acceptance::EpiphanyAcceptancePrerequisite; pub use coordinator_acceptance::EpiphanyCoordinatorAdmissionError; +pub use coordinator_acceptance::EpiphanyNativeReorientAcceptance; pub use coordinator_acceptance::EpiphanyNativeRoleAcceptance; pub use coordinator_acceptance::EpiphanyReorientAcceptanceUpdate; pub use coordinator_acceptance::EpiphanyRoleAcceptanceUpdate; +pub use coordinator_acceptance::accept_coordinator_reorient_finding; pub use coordinator_acceptance::accept_coordinator_role_finding; pub use coordinator_acceptance::acceptance_launch_contract_for_binding; pub use coordinator_acceptance::build_native_reorient_acceptance_update; diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index 283bcae8..9234b107 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -1847,10 +1847,12 @@ policy-shaped acceptance update, launch-contract proof enforcement, Research Substrate Gate/Eyes or Verification/Soul prerequisites, Mind commit witness, and atomic state publication. Thirteen focused coordinator tests pass. Its compatibility function only loads host facts, invokes `EpiphanyCoordinatorService::accept_role`, and maps the -typed native result. Eight bridge tests and the vendored app-server check pass; -the architecture guard expects one native role acceptance call and only the -single remaining reorientation atomic commit. Move reorientation acceptance -behind the service next, then begin coordinator AppServerClient removal. +typed native result. Reorientation acceptance is native too: `accept_reorient` owns completed +typed finding admission, checkpoint preconditions, launch-contract enforcement, +Continuity recovery receipt, Mind review/commit witness, and atomic state +publication. The bridge delegates both role and reorientation acceptance and +contains zero direct witness commits; its obsolete acceptance helpers and +imports are deleted. Begin `epiphany-mvp-coordinator` AppServerClient removal. ## Immediate Re-entry Instruction diff --git a/notes/native-coordinator-liberation-map.md b/notes/native-coordinator-liberation-map.md index 09a85b21..786f4da5 100644 --- a/notes/native-coordinator-liberation-map.md +++ b/notes/native-coordinator-liberation-map.md @@ -170,7 +170,9 @@ thread identity, reference turn, timestamp, and nonce to `EpiphanyCoordinatorService::accept_role`, then maps the typed result to the compatibility response. It no longer builds evidence packets, Soul verdicts, proof sets, Mind receipts, or state commits. Reorientation acceptance is the -remaining acceptance choreography still duplicated in the bridge. +same shape: `accept_reorient` owns typed result admission, Continuity recovery +proof, Mind witness, and atomic state publication. The bridge delegates both +acceptance families and contains zero direct witness commits. ## Acceptance Transaction Wound diff --git a/state/ledgers.msgpack b/state/ledgers.msgpack index 3947aed74326efc9c2857c0b5432305738a27784..25c2199d324b67df7be681df77c6ba3e032f87c8 100644 GIT binary patch delta 332 zcmYk1ze>bF5XMc^G&X_=DF;+gE;;Vbf8<(g8?mx5?rp})V6q#ta~^kyjfGfiNGZO7 zU?J9mPa*g!ZjLJ~K4xI%`+YM{KmNX@d%OMp=^JlNqI|w_6VYhqQjrucxEgoHe^ZW#LNvK#bX$3+`ELbRsp!669 zm}xbvS3_?nn(y_2Cj}-@q8VijBFV`ZYYjlAD6a}lilHb`YL!sKH1%uC+uA?Ngc9 G>a+nXJrWE6 diff --git a/state/map.yaml b/state/map.yaml index 6069878c..079dc4df 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -659,4 +659,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`. Gjallar 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: Move reorientation acceptance into `EpiphanyCoordinatorService` and delete its bridge choreography, then wire `epiphany-mvp-coordinator` directly to typed launch/result/acceptance operations and native launch context. Role acceptance already delegates fully; reorientation is the last duplicated acceptance owner before AppServerClient removal. + next_action: Wire `epiphany-mvp-coordinator` directly to `EpiphanyCoordinatorService` typed launch/result/role-accept/reorient-accept operations and native launch context, then remove every `thread/epiphany/*` call and AppServerClient dependency. Both acceptance families now delegate fully from the bridge with zero direct witness commits.