Move acceptance proof policy into core

This commit is contained in:
Meta Vens 2026-07-11 19:40:04 +01:00
commit 88cb165747
7 changed files with 132 additions and 49 deletions

View file

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

View file

@ -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<EpiphanyLaunchOrganContract, EpiphanyCoordinatorAdmissionError> {
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<EpiphanyReceiptEffectKind> {
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<String> {
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<EpiphanyReceiptEffectKind> {
vec![
EpiphanyReceiptEffectKind::StateAdmission,
EpiphanyReceiptEffectKind::ContinuityRecovery,
]
}
pub fn build_native_role_acceptance_update(
expected_revision: Option<u64>,
role_id: EpiphanyRoleResultRoleId,

View file

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

View file

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

View file

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

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: 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`.