use std::collections::BTreeMap;
use lifeloop::router::{
AdapterRegistry, AdapterResolution, CapabilityKind, CapabilityRequest, CapabilityRequirement,
LifeloopReceiptEmitter, ReceiptContext, ReceiptError, RoutingPlan, negotiate, route,
};
use lifeloop::{
AcceptablePlacement, AdapterManifest, AdapterRole, CallbackRequest, CallbackResponse,
ConformanceLevel, FailureClass, FrameContext, IntegrationMode, LifecycleEventKind,
ManifestContextPressure, ManifestPlacementClass, ManifestPlacementSupport, ManifestReceipts,
NegotiationOutcome, PayloadEnvelope, PayloadRef, PlacementClass, ReceiptStatus,
RegisteredAdapter, RequirementLevel, RetryClass, SCHEMA_VERSION, SupportState, Warning,
};
const FAKE_ID: &str = "fake-adapter";
const FAKE_VERSION: &str = "0.0.1";
const CLIENT_ID: &str = "fake-client";
fn manifest() -> AdapterManifest {
let mut placement = BTreeMap::new();
placement.insert(
ManifestPlacementClass::PreFrameTrailing,
ManifestPlacementSupport {
support: SupportState::Native,
max_bytes: None,
},
);
AdapterManifest {
contract_version: SCHEMA_VERSION.to_string(),
adapter_id: FAKE_ID.into(),
adapter_version: FAKE_VERSION.into(),
display_name: "Fake".into(),
role: AdapterRole::PrimaryWorker,
integration_modes: vec![IntegrationMode::NativeHook],
lifecycle_events: BTreeMap::new(),
placement,
context_pressure: ManifestContextPressure {
support: SupportState::Native,
evidence: None,
},
receipts: ManifestReceipts {
native: false,
lifeloop_synthesized: true,
receipt_ledger: SupportState::Unavailable,
},
session_identity: None,
session_rename: None,
renewal: None,
approval_surface: None,
failure_modes: Vec::new(),
telemetry_sources: Vec::new(),
known_degradations: Vec::new(),
}
}
struct Fixture(AdapterManifest);
impl AdapterRegistry for Fixture {
fn resolve(&self, id: &str, version: &str) -> AdapterResolution {
if id != self.0.adapter_id {
return AdapterResolution::UnknownId;
}
if version != self.0.adapter_version {
return AdapterResolution::VersionMismatch {
registered_version: self.0.adapter_version.clone(),
};
}
AdapterResolution::Found(RegisteredAdapter {
manifest: self.0.clone(),
conformance: ConformanceLevel::PreConformance,
})
}
}
fn frame_request_with(idem: Option<&str>, run_id: Option<&str>, event_id: &str) -> CallbackRequest {
CallbackRequest {
schema_version: SCHEMA_VERSION.to_string(),
event: LifecycleEventKind::FrameOpening,
event_id: event_id.into(),
adapter_id: FAKE_ID.into(),
adapter_version: FAKE_VERSION.into(),
integration_mode: IntegrationMode::NativeHook,
invocation_id: format!("inv-{event_id}"),
harness_session_id: Some("sess-1".into()),
harness_run_id: run_id.map(String::from),
harness_task_id: None,
frame_context: Some(FrameContext::top_level("frm-1")),
capability_snapshot_ref: None,
payload_refs: vec![PayloadRef {
payload_id: "pay-1".into(),
payload_kind: "instruction_frame".into(),
content_digest: None,
byte_size: Some(11),
}],
sequence: None,
idempotency_key: idem.map(String::from),
metadata: serde_json::Map::new(),
}
}
fn build_plan(idem: Option<&str>, run_id: Option<&str>, event_id: &str) -> RoutingPlan {
let fx = Fixture(manifest());
route(&frame_request_with(idem, run_id, event_id), &fx).expect("plan builds")
}
fn opaque_payload(body: &str) -> PayloadEnvelope {
PayloadEnvelope {
schema_version: SCHEMA_VERSION.to_string(),
payload_id: "pay-1".into(),
client_id: CLIENT_ID.into(),
payload_kind: "instruction_frame".into(),
format: "client-defined".into(),
content_encoding: "utf8".into(),
body: Some(body.into()),
body_ref: None,
byte_size: body.len() as u64,
content_digest: None,
acceptable_placements: vec![AcceptablePlacement {
placement: PlacementClass::PrePromptFrame,
requirement: RequirementLevel::Preferred,
}],
idempotency_key: None,
expires_at_epoch_s: None,
redaction: None,
metadata: serde_json::Map::new(),
}
}
fn satisfied_request() -> CapabilityRequest {
CapabilityRequest::new().with(CapabilityRequirement::preferred(
CapabilityKind::ContextPressure,
SupportState::Native,
))
}
fn unsupported_request() -> CapabilityRequest {
CapabilityRequest::new().with(CapabilityRequirement::required(
CapabilityKind::ReceiptLedger,
SupportState::Native,
))
}
fn requires_operator_request() -> CapabilityRequest {
CapabilityRequest::new()
}
fn ctx(receipt_id: &str, run_id: Option<&str>) -> ReceiptContext {
ReceiptContext {
client_id: CLIENT_ID.into(),
receipt_id: receipt_id.into(),
parent_receipt_id: None,
at_epoch_s: 1_700_000_000,
harness_session_id: Some("sess-1".into()),
harness_run_id: run_id.map(String::from),
harness_task_id: None,
}
}
#[test]
fn delivered_status_when_satisfied_and_client_delivered() {
let plan = build_plan(Some("idem-1"), Some("run-A"), "evt-1");
let payloads = vec![opaque_payload("hello")];
let neg = negotiate(&plan, &satisfied_request(), &payloads);
let response = CallbackResponse::ok(ReceiptStatus::Delivered);
let emitter = LifeloopReceiptEmitter::in_memory();
let receipt = emitter
.synthesize_and_emit(&neg, &response, &ctx("rec-1", Some("run-A")))
.expect("emit ok");
assert_eq!(receipt.status, ReceiptStatus::Delivered);
assert_eq!(receipt.failure_class, None);
assert_eq!(receipt.retry_class, None);
}
#[test]
fn degraded_status_when_negotiation_degraded() {
let req = CapabilityRequest::new().with(CapabilityRequirement::preferred(
CapabilityKind::SessionRename,
SupportState::Native,
));
let plan = build_plan(Some("idem-2"), Some("run-A"), "evt-2");
let payloads = vec![opaque_payload("hello")];
let neg = negotiate(&plan, &req, &payloads);
let response = CallbackResponse::ok(ReceiptStatus::Delivered);
let emitter = LifeloopReceiptEmitter::in_memory();
let receipt = emitter
.synthesize_and_emit(&neg, &response, &ctx("rec-2", Some("run-A")))
.expect("emit ok");
assert_eq!(receipt.status, ReceiptStatus::Degraded);
assert!(!receipt.capability_degradations.is_empty());
}
#[test]
fn skipped_status_when_client_skipped() {
let plan = build_plan(Some("idem-3"), Some("run-A"), "evt-3");
let payloads = vec![opaque_payload("hello")];
let neg = negotiate(&plan, &satisfied_request(), &payloads);
let response = CallbackResponse::ok(ReceiptStatus::Skipped);
let emitter = LifeloopReceiptEmitter::in_memory();
let receipt = emitter
.synthesize_and_emit(&neg, &response, &ctx("rec-3", Some("run-A")))
.expect("emit ok");
assert_eq!(receipt.status, ReceiptStatus::Skipped);
}
#[test]
fn observed_status_when_client_observed() {
let plan = build_plan(Some("idem-4"), Some("run-A"), "evt-4");
let payloads = vec![opaque_payload("hello")];
let neg = negotiate(&plan, &satisfied_request(), &payloads);
let response = CallbackResponse::ok(ReceiptStatus::Observed);
let emitter = LifeloopReceiptEmitter::in_memory();
let receipt = emitter
.synthesize_and_emit(&neg, &response, &ctx("rec-4", Some("run-A")))
.expect("emit ok");
assert_eq!(receipt.status, ReceiptStatus::Observed);
}
#[test]
fn failed_status_when_negotiation_unsupported() {
let plan = build_plan(Some("idem-5"), Some("run-A"), "evt-5");
let payloads = vec![opaque_payload("hello")];
let neg = negotiate(&plan, &unsupported_request(), &payloads);
let response = CallbackResponse::ok(ReceiptStatus::Delivered);
let emitter = LifeloopReceiptEmitter::in_memory();
let receipt = emitter
.synthesize_and_emit(&neg, &response, &ctx("rec-5", Some("run-A")))
.expect("emit ok");
assert_eq!(receipt.status, ReceiptStatus::Failed);
assert_eq!(
receipt.failure_class,
Some(FailureClass::CapabilityUnsupported)
);
assert!(receipt.retry_class.is_some());
}
#[test]
fn renewal_payload_delivery_requirement_fails_when_manifest_omits_renewal() {
let plan = build_plan(
Some("idem-renewal-unsupported"),
Some("run-A"),
"evt-renewal-unsupported",
);
let req = CapabilityRequest::new().with(CapabilityRequirement::required(
CapabilityKind::RenewalContinuationPayloadDelivery,
SupportState::Partial,
));
let neg = negotiate(&plan, &req, &[opaque_payload("renewal continuation")]);
assert_eq!(neg.outcome, NegotiationOutcome::Unsupported);
let response = CallbackResponse::ok(ReceiptStatus::Delivered);
let emitter = LifeloopReceiptEmitter::in_memory();
let receipt = emitter
.synthesize_and_emit(
&neg,
&response,
&ctx("rec-renewal-unsupported", Some("run-A")),
)
.expect("emit ok");
assert_eq!(receipt.status, ReceiptStatus::Failed);
assert_eq!(
receipt.failure_class,
Some(FailureClass::CapabilityUnsupported)
);
assert_eq!(receipt.retry_class, Some(RetryClass::DoNotRetry));
assert_eq!(
receipt.warnings[0].capability.as_deref(),
Some("renewal.continuation.payload_delivery")
);
}
#[test]
fn failed_status_when_client_returns_failure() {
let plan = build_plan(Some("idem-6"), Some("run-A"), "evt-6");
let payloads = vec![opaque_payload("hello")];
let neg = negotiate(&plan, &satisfied_request(), &payloads);
let response = CallbackResponse::failed(FailureClass::TransportError);
let emitter = LifeloopReceiptEmitter::in_memory();
let receipt = emitter
.synthesize_and_emit(&neg, &response, &ctx("rec-6", Some("run-A")))
.expect("emit ok");
assert_eq!(receipt.status, ReceiptStatus::Failed);
assert_eq!(receipt.failure_class, Some(FailureClass::TransportError));
}
#[test]
fn requires_operator_failed_status_with_operator_required_failure_class() {
let mut m = manifest();
m.session_identity = Some(lifeloop::ManifestSessionIdentity {
harness_session_id: SupportState::Manual,
harness_run_id: SupportState::Native,
harness_task_id: SupportState::Native,
});
let plan = {
let fx = Fixture(m);
route(
&frame_request_with(Some("idem-7"), Some("run-A"), "evt-7"),
&fx,
)
.expect("plan builds")
};
let req = CapabilityRequest::new().with(CapabilityRequirement::required(
CapabilityKind::HarnessSessionId,
SupportState::Native,
));
let payloads = vec![opaque_payload("hello")];
let neg = negotiate(&plan, &req, &payloads);
assert_eq!(
neg.outcome,
lifeloop::NegotiationOutcome::RequiresOperator,
"precondition: negotiation produces RequiresOperator"
);
let response = CallbackResponse::ok(ReceiptStatus::Delivered);
let emitter = LifeloopReceiptEmitter::in_memory();
let receipt = emitter
.synthesize_and_emit(&neg, &response, &ctx("rec-7", Some("run-A")))
.expect("emit ok");
assert_eq!(receipt.status, ReceiptStatus::Failed);
assert_eq!(receipt.failure_class, Some(FailureClass::OperatorRequired));
}
#[test]
fn failed_placement_decision_defensively_fails_receipt_status() {
let plan = build_plan(Some("idem-place-fail"), Some("run-A"), "evt-place-fail");
let mut payload = opaque_payload("hello");
payload.acceptable_placements[0].placement = PlacementClass::DeveloperEquivalentFrame;
let mut neg = negotiate(&plan, &satisfied_request(), std::slice::from_ref(&payload));
assert_eq!(
neg.failure_class,
Some(FailureClass::PlacementUnavailable),
"precondition: negotiation recorded the placement failure"
);
neg.outcome = NegotiationOutcome::Satisfied;
neg.failure_class = None;
let response = CallbackResponse::ok(ReceiptStatus::Delivered);
let emitter = LifeloopReceiptEmitter::in_memory();
let receipt = emitter
.synthesize_and_emit(&neg, &response, &ctx("rec-place-fail", Some("run-A")))
.expect("emit ok");
assert_eq!(receipt.status, ReceiptStatus::Failed);
assert_eq!(
receipt.failure_class,
Some(FailureClass::PlacementUnavailable)
);
}
#[test]
fn receipt_merges_negotiation_and_response_warnings() {
let req = CapabilityRequest::new().with(CapabilityRequirement::preferred(
CapabilityKind::SessionRename,
SupportState::Native,
));
let plan = build_plan(Some("idem-warn"), Some("run-A"), "evt-warn");
let payloads = vec![opaque_payload("hello")];
let neg = negotiate(&plan, &req, &payloads);
assert_eq!(neg.warnings.len(), 1);
let mut response = CallbackResponse::ok(ReceiptStatus::Delivered);
response.warnings.push(Warning {
code: "client_warning".into(),
message: "client surfaced a warning".into(),
capability: None,
});
let emitter = LifeloopReceiptEmitter::in_memory();
let receipt = emitter
.synthesize_and_emit(&neg, &response, &ctx("rec-warn", Some("run-A")))
.expect("emit ok");
let warning_codes: Vec<_> = receipt.warnings.iter().map(|w| w.code.as_str()).collect();
assert_eq!(warning_codes, vec!["capability_degraded", "client_warning"]);
}
#[test]
fn idempotent_replay_returns_prior_receipt_unchanged() {
let plan = build_plan(Some("idem-replay"), Some("run-A"), "evt-r1");
let payloads = vec![opaque_payload("hello")];
let neg = negotiate(&plan, &satisfied_request(), &payloads);
let response = CallbackResponse::ok(ReceiptStatus::Delivered);
let emitter = LifeloopReceiptEmitter::in_memory();
let first = emitter
.synthesize_and_emit(&neg, &response, &ctx("rec-replay", Some("run-A")))
.expect("first emit");
let second = emitter
.synthesize_and_emit(&neg, &response, &ctx("rec-replay", Some("run-A")))
.expect("replay should be no-op");
assert_eq!(first, second, "replay must return the prior receipt");
}
#[test]
fn idempotency_conflict_on_different_content() {
let plan = build_plan(Some("idem-conflict"), Some("run-A"), "evt-c1");
let payloads = vec![opaque_payload("hello")];
let neg = negotiate(&plan, &satisfied_request(), &payloads);
let response_ok = CallbackResponse::ok(ReceiptStatus::Delivered);
let response_fail = CallbackResponse::failed(FailureClass::TransportError);
let emitter = LifeloopReceiptEmitter::in_memory();
emitter
.synthesize_and_emit(&neg, &response_ok, &ctx("rec-c1", Some("run-A")))
.expect("first emit");
let err = emitter
.synthesize_and_emit(&neg, &response_fail, &ctx("rec-c2", Some("run-A")))
.expect_err("conflicting content must be rejected");
assert!(
matches!(err, ReceiptError::Conflict { .. }),
"expected ReceiptError::Conflict, got {err:?}"
);
}
#[test]
fn sequencer_peek_and_store_size_track_emitted_receipts() {
let emitter = LifeloopReceiptEmitter::in_memory();
assert_eq!(emitter.sequencer().peek("run-A"), 0);
assert_eq!(emitter.store().len(), 0);
assert!(emitter.store().is_empty());
let plan = build_plan(Some("idem-store"), Some("run-A"), "evt-store");
let payloads = vec![opaque_payload("hello")];
let neg = negotiate(&plan, &satisfied_request(), &payloads);
let response = CallbackResponse::ok(ReceiptStatus::Delivered);
let receipt = emitter
.synthesize_and_emit(&neg, &response, &ctx("rec-store", Some("run-A")))
.expect("emit ok");
assert_eq!(receipt.sequence, Some(1));
assert_eq!(emitter.sequencer().peek("run-A"), 1);
assert_eq!(emitter.store().len(), 1);
assert!(!emitter.store().is_empty());
}
#[test]
fn sequence_scopes_per_lifeloop_run_id() {
let emitter = LifeloopReceiptEmitter::in_memory();
let response = CallbackResponse::ok(ReceiptStatus::Delivered);
let payloads = vec![opaque_payload("hello")];
let plan_a1 = build_plan(Some("idem-A1"), Some("run-A"), "evt-A1");
let neg_a1 = negotiate(&plan_a1, &satisfied_request(), &payloads);
let r_a1 = emitter
.synthesize_and_emit(&neg_a1, &response, &ctx("rec-A1", Some("run-A")))
.expect("emit A1");
let plan_b1 = build_plan(Some("idem-B1"), Some("run-B"), "evt-B1");
let neg_b1 = negotiate(&plan_b1, &satisfied_request(), &payloads);
let r_b1 = emitter
.synthesize_and_emit(&neg_b1, &response, &ctx("rec-B1", Some("run-B")))
.expect("emit B1");
let plan_a2 = build_plan(Some("idem-A2"), Some("run-A"), "evt-A2");
let neg_a2 = negotiate(&plan_a2, &satisfied_request(), &payloads);
let r_a2 = emitter
.synthesize_and_emit(&neg_a2, &response, &ctx("rec-A2", Some("run-A")))
.expect("emit A2");
assert_eq!(r_a1.sequence, Some(1));
assert_eq!(r_b1.sequence, Some(1));
assert_eq!(r_a2.sequence, Some(2));
}
#[test]
fn sequence_is_none_without_run_id() {
let plan = build_plan(Some("idem-norun"), None, "evt-nr");
let payloads = vec![opaque_payload("hello")];
let neg = negotiate(&plan, &satisfied_request(), &payloads);
let response = CallbackResponse::ok(ReceiptStatus::Delivered);
let emitter = LifeloopReceiptEmitter::in_memory();
let receipt = emitter
.synthesize_and_emit(&neg, &response, &ctx("rec-nr", None))
.expect("emit ok");
assert_eq!(receipt.sequence, None);
}
#[test]
fn receipt_emitted_event_is_rejected_at_emit_time() {
let mut req = frame_request_with(Some("idem-rej"), Some("run-A"), "evt-rej");
req.event = LifecycleEventKind::ReceiptEmitted;
req.frame_context = None; let fx = Fixture(manifest());
let plan = match route(&req, &fx) {
Ok(p) => p,
Err(_) => return,
};
let payloads = vec![opaque_payload("hello")];
let neg = negotiate(&plan, &satisfied_request(), &payloads);
let response = CallbackResponse::ok(ReceiptStatus::Delivered);
let emitter = LifeloopReceiptEmitter::in_memory();
let err = emitter
.synthesize_and_emit(&neg, &response, &ctx("rec-rej", Some("run-A")))
.expect_err("receipt.emitted must not produce a receipt");
assert!(
matches!(err, ReceiptError::ReceiptEmittedNotEmittable),
"expected ReceiptEmittedNotEmittable, got {err:?}"
);
}
#[test]
fn payload_body_bytes_pass_through_emission_unchanged() {
let plan = build_plan(Some("idem-opa"), Some("run-A"), "evt-opa");
let original = "opaque-payload-bytes-do-not-mutate";
let payload = opaque_payload(original);
let payload_clone = payload.clone();
let neg = negotiate(
&plan,
&satisfied_request(),
std::slice::from_ref(&payload_clone),
);
let response = CallbackResponse::ok(ReceiptStatus::Delivered);
let emitter = LifeloopReceiptEmitter::in_memory();
let _receipt = emitter
.synthesize_and_emit(&neg, &response, &ctx("rec-opa", Some("run-A")))
.expect("emit ok");
assert_eq!(payload.body.as_deref(), Some(original));
assert_eq!(payload_clone.body.as_deref(), Some(original));
}
#[test]
fn empty_receipt_id_is_rejected_by_validation() {
let plan = build_plan(Some("idem-val"), Some("run-A"), "evt-val");
let payloads = vec![opaque_payload("hello")];
let neg = negotiate(&plan, &satisfied_request(), &payloads);
let response = CallbackResponse::ok(ReceiptStatus::Delivered);
let mut bad_ctx = ctx("rec-val", Some("run-A"));
bad_ctx.receipt_id = String::new();
let emitter = LifeloopReceiptEmitter::in_memory();
let err = emitter
.synthesize_and_emit(&neg, &response, &bad_ctx)
.expect_err("empty receipt_id must be rejected");
assert!(
matches!(err, ReceiptError::Invalid(_)),
"expected Invalid(_), got {err:?}"
);
}
#[test]
fn receipt_carries_required_contract_fields() {
let plan = build_plan(Some("idem-fields"), Some("run-A"), "evt-fields");
let payloads = vec![opaque_payload("hello")];
let neg = negotiate(&plan, &satisfied_request(), &payloads);
let response = CallbackResponse::ok(ReceiptStatus::Delivered);
let emitter = LifeloopReceiptEmitter::in_memory();
let r = emitter
.synthesize_and_emit(&neg, &response, &ctx("rec-fields", Some("run-A")))
.expect("emit ok");
assert_eq!(r.schema_version, SCHEMA_VERSION);
assert_eq!(r.adapter_id, FAKE_ID);
assert_eq!(r.event, LifecycleEventKind::FrameOpening);
assert_eq!(r.event_id, "evt-fields");
assert_eq!(r.client_id, CLIENT_ID);
assert_eq!(r.receipt_id, "rec-fields");
assert_eq!(r.invocation_id, "inv-evt-fields");
assert_eq!(r.harness_run_id.as_deref(), Some("run-A"));
assert_eq!(r.integration_mode, IntegrationMode::NativeHook);
assert_eq!(r.idempotency_key.as_deref(), Some("idem-fields"));
assert!(r.sequence.is_some());
assert_eq!(r.payload_receipts.len(), 1);
assert_eq!(r.payload_receipts[0].payload_id, "pay-1");
assert_eq!(r.payload_receipts[0].payload_kind, "instruction_frame");
assert_eq!(r.payload_receipts[0].byte_size, 5);
assert_eq!(r.payload_receipts[0].content_digest, None);
}
#[test]
fn payload_receipt_provenance_comes_from_negotiated_payload() {
let plan = build_plan(Some("idem-prov"), Some("run-A"), "evt-prov");
let mut payload = opaque_payload("hello");
payload.content_digest = Some("sha256:source".into());
let neg = negotiate(&plan, &satisfied_request(), std::slice::from_ref(&payload));
let mut response = CallbackResponse::ok(ReceiptStatus::Delivered);
let mut echoed = payload.clone();
echoed.payload_kind = "client.output".into();
echoed.content_digest = Some("sha256:response".into());
response.client_payloads.push(echoed);
let emitter = LifeloopReceiptEmitter::in_memory();
let r = emitter
.synthesize_and_emit(&neg, &response, &ctx("rec-prov", Some("run-A")))
.expect("emit ok");
assert_eq!(r.payload_receipts.len(), 1);
assert_eq!(r.payload_receipts[0].payload_id, "pay-1");
assert_eq!(r.payload_receipts[0].payload_kind, "instruction_frame");
assert_eq!(r.payload_receipts[0].byte_size, 5);
assert_eq!(
r.payload_receipts[0].content_digest.as_deref(),
Some("sha256:source")
);
}
#[allow(dead_code)]
fn _docs_only_helper(_: CapabilityRequest) -> CapabilityRequest {
requires_operator_request()
}