effect-runtime 0.1.0

Typed side-effect intent, execution receipt, and policy primitives for RecursiveIntell runtime gates
Documentation
#![allow(clippy::expect_used)]

use effect_runtime::{
    BlastRadiusCeilingV1, BudgetSufficiencyResultV1, CheckResultV1, ClosureRecommendationV1,
    CommitAtomicityV1, CompensationClassV1, CompensationExecutionReceiptV1, CompensationPlanV1,
    EffectClassV1, EffectCommitDecisionV1, EffectCommitDispositionV1, EffectExecutionReceiptV1,
    EffectIntentV1, EffectObservationBundleV1, EffectPreflightDispositionV1,
    EffectPreflightReportV1, EffectWindowV1, ExecutionStateV1, ExternalEffectLedgerEntryV1,
    ExternalObservationStateV1, ObservationStateV1, PublicationStatusV1, RetryOwnerV1,
    ReversibilityClassV1, RunModeV1, V25ConstitutionCitation, V25ObligationRefs,
};
use stack_ids::{
    ApplicabilityContextId, ApprovalGrantId, CompiledObligationSetId, CompositionConflictSetId,
    CompositionReceiptId, EffectCommitDecisionId, EffectExecutionReceiptId, EffectIntentId,
    EffectObservationBundleId, EffectPreflightReportId, EffectWindowId, ExecutionPermitId,
    ProfileExceptionBundleId, ProfileSetId,
};

#[test]
fn v25_effect_artifacts_roundtrip_with_citation_payload() {
    let citation = V25ConstitutionCitation {
        applicability_context_id: ApplicabilityContextId::new("ac-v25"),
        profile_set_id: ProfileSetId::new("ps-v25"),
        composition_receipt_id: CompositionReceiptId::new("cr-v25"),
        effective_constitution_id: stack_ids::EffectiveConstitutionId::new("ec-v25"),
        compiled_obligation_set_id: CompiledObligationSetId::new("cos-v25"),
        composition_conflict_set_id: Some(CompositionConflictSetId::new("ccs-v25")),
        profile_exception_bundle_ids: vec![ProfileExceptionBundleId::new("peb-v25")],
    };

    let obligation_refs = V25ObligationRefs {
        required_obligation_refs: vec!["obligation:required:v25".into()],
        blocking_obligation_refs: vec!["obligation:blocking:v25".into()],
        monitoring_obligation_refs: vec!["obligation:monitoring:v25".into()],
        decision_basis_obligation_refs: vec!["obligation:decision:v25".into()],
    };

    let intent = EffectIntentV1::builder(
        EffectIntentId::new("fxi-v25"),
        EffectClassV1::ExternalWrite,
        "service/checkout",
        "apply safe state transition",
        vec!["artifact:runbook".to_string()],
        "cap-change",
        "project-alpha",
        BlastRadiusCeilingV1::SingleTenant,
        ReversibilityClassV1::Compensatable,
        "idem-v25",
        EffectWindowId::new("fxw-v25"),
        vec!["obs:metric-latency".to_string()],
        vec!["approval:policy".to_string()],
        RunModeV1::Live,
        PublicationStatusV1::Required,
    )
    .build()
    .expect("build intent");

    let preflight = EffectPreflightReportV1::builder(
        EffectPreflightReportId::new("fxp-v25"),
        EffectIntentId::new("fxi-v25"),
        citation.clone(),
        obligation_refs.clone(),
        CheckResultV1::Pass,
        CheckResultV1::Pass,
        CheckResultV1::Pass,
        BudgetSufficiencyResultV1::Sufficient,
        EffectPreflightDispositionV1::CommitEligible,
        "2026-03-18T00:00:00Z",
        vec!["compositional lane attached".to_string()],
    )
    .build()
    .expect("build preflight");

    let commit = EffectCommitDecisionV1::builder(
        EffectCommitDecisionId::new("fxc-v25"),
        EffectIntentId::new("fxi-v25"),
        EffectPreflightReportId::new("fxp-v25"),
        citation.clone(),
        obligation_refs.clone(),
        vec!["approval:review".to_string()],
        EffectCommitDispositionV1::Authorized,
        vec!["person:policy_officer".to_string()],
        Some(ExecutionPermitId::new("permit-v25")),
        Some(ApprovalGrantId::new("grant-v25")),
        "",
        "2026-03-18T00:00:30Z",
    )
    .build()
    .expect("build commit");

    let execution = EffectExecutionReceiptV1::builder(
        EffectExecutionReceiptId::new("fxe-v25"),
        EffectCommitDecisionId::new("fxc-v25"),
        citation.clone(),
        vec!["runtime-orchestrator".to_string()],
        ExecutionStateV1::Completed,
        false,
        "",
        false,
        vec!["effect:change:v25".to_string()],
        "external-handle-v25",
        "2026-03-18T00:01:00Z",
    )
    .build()
    .expect("build execution");

    let observation = EffectObservationBundleV1::builder(
        EffectObservationBundleId::new("fxo-v25"),
        EffectExecutionReceiptId::new("fxe-v25"),
        citation.clone(),
        "2026-03-18T00:01:00Z/2026-03-18T00:02:00Z",
        ObservationStateV1::Complete,
        "change applied",
        "no drift",
        vec!["evidence:metric-latency".to_string()],
        ClosureRecommendationV1::CloseSuccessfully,
        "2026-03-18T00:02:00Z",
    )
    .build()
    .expect("build observation");

    let window = EffectWindowV1::builder(
        EffectWindowId::new("fxw-v25"),
        "2026-03-18T00:00:00Z",
        "2026-03-18T00:00:05Z",
        "2026-03-18T00:05:00Z",
        CommitAtomicityV1::ExactlyOnce,
        RetryOwnerV1::Verification,
        effect_runtime::CloseMidflightBehaviorV1::FailClosed,
    )
    .build()
    .expect("build window");

    let compensation_plan = CompensationPlanV1::builder(
        stack_ids::CompensationPlanId::new("cpl-v25"),
        EffectIntentId::new("fxi-v25"),
        citation.clone(),
        false,
        CompensationClassV1::LocalRollback,
        vec!["no action needed".to_string()],
        vec!["verified outcome".to_string()],
        "team:runtime",
        "2026-03-18T00:02:00Z",
        false,
    )
    .build()
    .expect("build compensation plan");

    let compensation_receipt = CompensationExecutionReceiptV1::builder(
        stack_ids::CompensationExecutionReceiptId::new("cex-v25"),
        stack_ids::CompensationPlanId::new("cpl-v25"),
        citation.clone(),
        ExecutionStateV1::Completed,
        vec!["restored".to_string()],
        Vec::new(),
        "none",
        "2026-03-18T00:03:00Z",
    )
    .build()
    .expect("build compensation receipt");

    let ledger = ExternalEffectLedgerEntryV1::builder(
        stack_ids::ExternalEffectLedgerEntryId::new("xel-v25"),
        EffectExecutionReceiptId::new("fxe-v25"),
        citation.clone(),
        "core",
        "ext-handle-v25",
        ExternalObservationStateV1::Committed,
        "2026-03-18T00:01:30Z",
        true,
    )
    .build()
    .expect("build ledger");

    let value = serde_json::to_string(&intent).unwrap();
    let intent_rt: EffectIntentV1 = serde_json::from_str(&value).unwrap();
    intent_rt.validate().unwrap();
    let value = serde_json::to_string(&preflight).unwrap();
    let preflight_rt: EffectPreflightReportV1 = serde_json::from_str(&value).unwrap();
    preflight_rt.validate().unwrap();
    let value = serde_json::to_string(&commit).unwrap();
    let commit_rt: EffectCommitDecisionV1 = serde_json::from_str(&value).unwrap();
    commit_rt.validate().unwrap();
    let value = serde_json::to_string(&execution).unwrap();
    let execution_rt: EffectExecutionReceiptV1 = serde_json::from_str(&value).unwrap();
    execution_rt.validate().unwrap();
    let value = serde_json::to_string(&observation).unwrap();
    let observation_rt: EffectObservationBundleV1 = serde_json::from_str(&value).unwrap();
    observation_rt.validate().unwrap();
    let value = serde_json::to_string(&compensation_plan).unwrap();
    let compensation_plan_rt: CompensationPlanV1 = serde_json::from_str(&value).unwrap();
    compensation_plan_rt.validate().unwrap();
    let value = serde_json::to_string(&compensation_receipt).unwrap();
    let compensation_receipt_rt: CompensationExecutionReceiptV1 =
        serde_json::from_str(&value).unwrap();
    compensation_receipt_rt.validate().unwrap();
    let value = serde_json::to_string(&window).unwrap();
    let window_rt: EffectWindowV1 = serde_json::from_str(&value).unwrap();
    window_rt.validate().unwrap();
    let value = serde_json::to_string(&ledger).unwrap();
    let ledger_rt: ExternalEffectLedgerEntryV1 = serde_json::from_str(&value).unwrap();
    ledger_rt.validate().unwrap();

    assert_eq!(
        execution.citation.composition_receipt_id,
        citation.composition_receipt_id
    );
    assert_eq!(
        commit.obligation_refs.required_obligation_refs,
        obligation_refs.required_obligation_refs
    );
}