From 88cb165747b5ff9ceeb9eb2b640c993687a2ef70 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sat, 11 Jul 2026 19:40:04 +0100 Subject: [PATCH] Move acceptance proof policy into core --- epiphany-codex-bridge/src/mutation_service.rs | 71 ++++++---------- epiphany-core/src/coordinator_acceptance.rs | 78 ++++++++++++++++++ epiphany-core/src/lib.rs | 5 ++ notes/fresh-workspace-handoff.md | 9 ++ notes/native-coordinator-liberation-map.md | 16 ++++ state/ledgers.msgpack | Bin 400925 -> 401294 bytes state/map.yaml | 2 +- 7 files changed, 132 insertions(+), 49 deletions(-) diff --git a/epiphany-codex-bridge/src/mutation_service.rs b/epiphany-codex-bridge/src/mutation_service.rs index 4aee562a..e86d3ab5 100644 --- a/epiphany-codex-bridge/src/mutation_service.rs +++ b/epiphany-codex-bridge/src/mutation_service.rs @@ -38,7 +38,6 @@ use epiphany_core::continuity_recovery_receipt_from_reorient_finding; use epiphany_core::epiphany_role_label; use epiphany_core::epiphany_state_update_validation_errors; use epiphany_core::evaluate_promotion; -use epiphany_core::evaluate_receipt_proof_profiles; use epiphany_core::eyes_evidence_packet_from_research_finding; use epiphany_core::mind_state_commit_receipt; use epiphany_core::open_runtime_spine_heartbeat_job; @@ -49,7 +48,6 @@ use epiphany_core::put_mind_gateway_review; use epiphany_core::put_mind_state_commit_receipt; use epiphany_core::put_soul_verdict_receipt; use epiphany_core::put_substrate_gate_repo_access_grant_receipt; -use epiphany_core::receipt_proof_evaluation_errors; use epiphany_core::replace_or_append_epiphany_job_binding; use epiphany_core::runtime_continuity_recovery_receipt; use epiphany_core::runtime_eyes_evidence_packet; @@ -88,10 +86,8 @@ 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::latest_epiphany_runtime_link_for_binding; use crate::runtime_results::load_completed_core_epiphany_reorient_finding; use crate::runtime_results::load_completed_core_epiphany_role_finding; -use crate::runtime_results::load_launch_organ_contract_for_runtime_job; use uuid::Uuid; #[allow(async_fn_in_trait)] @@ -922,17 +918,13 @@ fn launch_contract_for_binding( binding_id: &str, expected_document_kind: &str, ) -> BridgeResult { - let link = latest_epiphany_runtime_link_for_binding(state, binding_id).ok_or_else(|| { - EpiphanyBridgeError::InvalidRequest(format!( - "cannot prove receipt profile for binding {:?}: no runtime link exists", - binding_id - )) - })?; - load_launch_organ_contract_for_runtime_job( + epiphany_core::acceptance_launch_contract_for_binding( runtime_store_path, - link.runtime_job_id.as_str(), + state, + binding_id, expected_document_kind, ) + .map_err(map_coordinator_admission_error) } fn enforce_current_receipt_proofs( @@ -941,21 +933,13 @@ fn enforce_current_receipt_proofs( available_document_types: &[String], enforceable_document_types: &[String], ) -> BridgeResult<()> { - let evaluations = evaluate_receipt_proof_profiles( + epiphany_core::enforce_acceptance_receipt_proofs( contract, claimed_effects, available_document_types, enforceable_document_types, - ); - let errors = receipt_proof_evaluation_errors(&evaluations); - if errors.is_empty() { - Ok(()) - } else { - Err(EpiphanyBridgeError::InvalidRequest(format!( - "receipt proof profile rejected state admission: {}", - errors.join("; ") - ))) - } + ) + .map_err(map_coordinator_admission_error) } async fn admit_epiphany_state_with_mind_commit( @@ -1109,35 +1093,26 @@ fn role_acceptance_claimed_effects( role_id: EpiphanyRoleResultRoleId, changed_fields: &[EpiphanyStateUpdatedField], ) -> Vec { - let mut effects = vec![EpiphanyReceiptEffectKind::StateAdmission]; - if changed_fields.iter().any(|field| { - matches!( - field, - EpiphanyStateUpdatedField::Evidence | EpiphanyStateUpdatedField::Observations - ) - }) { - effects.push(EpiphanyReceiptEffectKind::EvidencePromotion); - } - if matches!(role_id, EpiphanyRoleResultRoleId::Verification) { - effects.push(EpiphanyReceiptEffectKind::Verification); - } - effects + epiphany_core::role_acceptance_claimed_effects(role_id, changed_fields) } fn role_acceptance_enforceable_receipts(role_id: EpiphanyRoleResultRoleId) -> Vec { - let mut receipts = vec![MIND_GATEWAY_REVIEW_TYPE.to_string()]; - if role_id == EpiphanyRoleResultRoleId::Research { - receipts.push(SUBSTRATE_GATE_REPO_ACCESS_GRANT_RECEIPT_TYPE.to_string()); - receipts.push(EYES_EVIDENCE_PACKET_TYPE.to_string()); - } else if role_id == EpiphanyRoleResultRoleId::Verification { - receipts.push(SOUL_VERDICT_RECEIPT_TYPE.to_string()); - } - receipts + epiphany_core::role_acceptance_enforceable_receipts(role_id) } fn reorient_acceptance_claimed_effects() -> Vec { - vec![ - EpiphanyReceiptEffectKind::StateAdmission, - EpiphanyReceiptEffectKind::ContinuityRecovery, - ] + 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) + } + } } diff --git a/epiphany-core/src/coordinator_acceptance.rs b/epiphany-core/src/coordinator_acceptance.rs index 7efe4b4a..80b17838 100644 --- a/epiphany-core/src/coordinator_acceptance.rs +++ b/epiphany-core/src/coordinator_acceptance.rs @@ -178,6 +178,84 @@ pub fn load_launch_organ_contract( Ok(request.organ_launch_contract) } +pub fn acceptance_launch_contract_for_binding( + runtime_store_path: &Path, + state: &EpiphanyThreadState, + binding_id: &str, + expected_document_kind: &str, +) -> Result { + let link = latest_runtime_link(state, binding_id).ok_or_else(|| { + EpiphanyCoordinatorAdmissionError::InvalidRequest(format!( + "cannot prove receipt profile for binding {binding_id:?}: no runtime link exists" + )) + })?; + load_launch_organ_contract( + runtime_store_path, + &link.runtime_job_id, + expected_document_kind, + ) +} + +pub fn enforce_acceptance_receipt_proofs( + contract: &EpiphanyLaunchOrganContract, + claimed_effects: &[EpiphanyReceiptEffectKind], + available_document_types: &[String], + enforceable_document_types: &[String], +) -> Result<(), EpiphanyCoordinatorAdmissionError> { + let evaluations = evaluate_receipt_proof_profiles( + contract, + claimed_effects, + available_document_types, + enforceable_document_types, + ); + let errors = receipt_proof_evaluation_errors(&evaluations); + if errors.is_empty() { + Ok(()) + } else { + Err(EpiphanyCoordinatorAdmissionError::InvalidRequest(format!( + "receipt proof profile rejected state admission: {}", + errors.join("; ") + ))) + } +} + +pub fn role_acceptance_claimed_effects( + role_id: EpiphanyRoleResultRoleId, + changed_fields: &[EpiphanyStateUpdatedField], +) -> Vec { + let mut effects = vec![EpiphanyReceiptEffectKind::StateAdmission]; + if changed_fields.iter().any(|field| { + matches!( + field, + EpiphanyStateUpdatedField::Evidence | EpiphanyStateUpdatedField::Observations + ) + }) { + effects.push(EpiphanyReceiptEffectKind::EvidencePromotion); + } + if role_id == EpiphanyRoleResultRoleId::Verification { + effects.push(EpiphanyReceiptEffectKind::Verification); + } + effects +} + +pub fn role_acceptance_enforceable_receipts(role_id: EpiphanyRoleResultRoleId) -> Vec { + let mut receipts = vec![MIND_GATEWAY_REVIEW_TYPE.to_string()]; + if role_id == EpiphanyRoleResultRoleId::Research { + receipts.push(SUBSTRATE_GATE_REPO_ACCESS_GRANT_RECEIPT_TYPE.to_string()); + receipts.push(EYES_EVIDENCE_PACKET_TYPE.to_string()); + } else if role_id == EpiphanyRoleResultRoleId::Verification { + receipts.push(SOUL_VERDICT_RECEIPT_TYPE.to_string()); + } + receipts +} + +pub fn reorient_acceptance_claimed_effects() -> Vec { + vec![ + EpiphanyReceiptEffectKind::StateAdmission, + EpiphanyReceiptEffectKind::ContinuityRecovery, + ] +} + pub fn build_native_role_acceptance_update( expected_revision: Option, role_id: EpiphanyRoleResultRoleId, diff --git a/epiphany-core/src/lib.rs b/epiphany-core/src/lib.rs index 643edf0e..8819c0f6 100644 --- a/epiphany-core/src/lib.rs +++ b/epiphany-core/src/lib.rs @@ -125,11 +125,16 @@ pub use continuity_gateway::*; pub use coordinator_acceptance::EpiphanyCoordinatorAdmissionError; pub use coordinator_acceptance::EpiphanyReorientAcceptanceUpdate; pub use coordinator_acceptance::EpiphanyRoleAcceptanceUpdate; +pub use coordinator_acceptance::acceptance_launch_contract_for_binding; pub use coordinator_acceptance::build_native_reorient_acceptance_update; pub use coordinator_acceptance::build_native_role_acceptance_update; pub use coordinator_acceptance::completed_reorient_finding; pub use coordinator_acceptance::completed_role_finding; +pub use coordinator_acceptance::enforce_acceptance_receipt_proofs; pub use coordinator_acceptance::load_launch_organ_contract; +pub use coordinator_acceptance::reorient_acceptance_claimed_effects; +pub use coordinator_acceptance::role_acceptance_claimed_effects; +pub use coordinator_acceptance::role_acceptance_enforceable_receipts; pub use coordinator_results::EpiphanyCoordinatorReorientResultSnapshot; pub use coordinator_results::EpiphanyCoordinatorRoleResultSnapshot; pub use coordinator_results::read_reorient_result_snapshot; diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index 7b06410d..7811e94d 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -710,6 +710,15 @@ tests and all eight bridge tests pass. Resume receipt persistence/proof enforcement extraction through the acceptance organ; do not add policy back to the facade. +Pure receipt-proof policy is now native: acceptance launch-contract lookup, +claimed effects, enforceable receipt families, and proof-profile evaluation +live in `coordinator_acceptance.rs`; bridge helpers delegate. Do not move the +remaining persistence sequence unchanged. It writes thread state before the +Mind state-commit receipt in another store, so a final receipt failure can +leave unwitnessed accepted state. The transaction/recovery law is specified in +`notes/native-coordinator-liberation-map.md` and must be solved before final +acceptance persistence moves. + Bridge identity correction from 2026-06-22: Epiphany owns swarm execution, repo Body work, branch-local lanes, memory, evidence, and Persona cognition. Bifrost owns GameCult identity, governed crossings into GitHub/Discord/Reddit diff --git a/notes/native-coordinator-liberation-map.md b/notes/native-coordinator-liberation-map.md index 56fb54f9..76ecf3ae 100644 --- a/notes/native-coordinator-liberation-map.md +++ b/notes/native-coordinator-liberation-map.md @@ -154,6 +154,22 @@ Each organ owns one invariant and must be testable against injected paths and typed inputs without starting Codex. A source guard should fail if the façade regrows policy bodies or if any native organ imports app-server protocol types. +## Acceptance Transaction Wound + +The compatibility bridge currently persists prerequisite receipts, writes +thread state, then writes the Mind state-commit receipt in a separate +runtime-spine store. If the final write fails, accepted durable state survives +without its promised commit witness. Moving that sequence into core unchanged +is forbidden. + +Before final acceptance persistence moves, define one typed transaction law: +either co-locate state admission and its commit witness in one CultCache +transaction boundary, or persist a prepared acceptance transaction whose +recovery protocol can deterministically finish or refuse admission after +interruption. Best-effort rollback across two files is not atomicity. A negative +smoke must inject failure after prerequisite receipts and after state write and +prove no unwitnessed accepted state becomes authoritative. + ## Invariants - There is one durable thread-state owner and one runtime lifecycle owner. diff --git a/state/ledgers.msgpack b/state/ledgers.msgpack index 5d5ad1cf241f905fe570a07a12e050648cc41cdf..f9970535b89612c12ff6e52c9c7b322bd8fb52c9 100644 GIT binary patch delta 368 zcmYL^KT88a5XIeSt`S5~Bw&yMTTKF5MH|0?g_U5j*`14n>+ZR`=aI%gSeVN0xJt0K z6D+h4?EDbwhjA`OEFLqk`+oD@{afqqz4g=*^V_|CZ|m@5CRsaqUKeLVyxx?J+p-~8 z^_*qRv6?SsBN?w&S1ZY4b7HElzr<4aQ918XMbw{ScR%+CQYp-$v(N5Y&J23F1hX@)C4rRp9fxTQ)gw*9$gYqtf%@7^qqL(&E70FpB>eoKRV~XCT)x}@X^bn#4k5P%MT;Szo`&OPYEB^~D-Cc3q%1;)K2Ts7 pr%xpr4tDS#pv&L5g&%>DMSa=20+h7Ql48j3{Rv~?4^p=doge7*gc<+< delta 57 zcmeC%FEMwI#0CjQZevp`W6P*x3~VyLHY+n0GBR#&p32ldm5C9EK{PWEvj8#c_Nh#4 G9nt_9ToJ4Q diff --git a/state/map.yaml b/state/map.yaml index b89be6f7..2a7173d2 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: Resume native coordinator liberation through the separated organs defined in `notes/native-coordinator-liberation-map.md`. The production facade is now 81 policy-free lines; state, acceptance, and results have separate modules. Move receipt persistence/proof enforcement into the acceptance organ, extract launch into its own organ, then remove `thread/epiphany/*` JSON-RPC from `epiphany-mvp-coordinator` and demote Codex routes to compatibility wrappers. + next_action: Solve the acceptance transaction wound specified in `notes/native-coordinator-liberation-map.md`. Pure receipt proof policy is native, but compatibility acceptance writes thread state before its Mind commit receipt in another store. Introduce a typed co-located transaction or deterministic prepared/recovery protocol and prove injected failures cannot leave unwitnessed accepted state. Then move persistence into the acceptance organ, extract launch, and remove `thread/epiphany/*` JSON-RPC from `epiphany-mvp-coordinator`.