Delegate reorientation acceptance

This commit is contained in:
Meta Vens 2026-07-11 20:44:05 +01:00
commit fe563ee271
8 changed files with 165 additions and 148 deletions

View file

@ -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<EpiphanyLaunchOrganContract> {
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<EpiphanyReceiptEffectKind> {
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);
}
}

View file

@ -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<u64>,
reference_turn_id: Option<String>,
accepted_at: String,
nonce: &str,
update_scratch: bool,
update_investigation_checkpoint: bool,
) -> anyhow::Result<EpiphanyNativeReorientAcceptance> {
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,

View file

@ -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<u64>,
reference_turn_id: Option<String>,
accepted_at: String,
nonce: &str,
update_scratch: bool,
update_investigation_checkpoint: bool,
) -> Result<EpiphanyNativeReorientAcceptance> {
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 [

View file

@ -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;

View file

@ -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

View file

@ -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

Binary file not shown.

View file

@ -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.