Move acceptance proof policy into core
This commit is contained in:
parent
f8ac056369
commit
88cb165747
7 changed files with 132 additions and 49 deletions
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
|
@ -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`.
|
||||
|
|
|
|||
Reference in a new issue