Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,13 @@ Applied, Rejected, Obstructed}` with receipt evidence and typed contract
commit keeps a stable `QueryReadingIdentity` even if unrelated runtime-owned
tick progress changes the observation freshness watermark.
- `warp-core` now publishes observation artifacts under observation contract
version 3 and `echo:observation-artifact:v3` because contract evidence and
query reading identity are now part of the canonical reading envelope hashed
into observation artifacts.
- `echo-wasm-abi` now reports `ABI_VERSION` 11 for the expanded
version 4 and `echo:observation-artifact:v4` because contract evidence, query
reading identity, and retained-evidence posture are now part of the canonical
reading envelope hashed into observation artifacts.
- `echo-wasm-abi` now reports `ABI_VERSION` 12 for the expanded
`ReadingEnvelope` response shape. Legacy retained reading envelopes that omit
the new optional contract/query identity fields decode those fields as
`None`.
the new optional contract, query identity, and retained-evidence fields
decode those fields as `None` or empty vectors.
- `warp-core` now attaches contract package evidence to installed contract
readings and receipt correlations. Installed QueryView readings carry
package id, package name/version, artifact hash, schema hash, codec identity,
Expand Down
72 changes: 70 additions & 2 deletions crates/echo-wasm-abi/src/kernel_port.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
//!
//! # ABI Version
//!
//! The current ABI version is [`ABI_VERSION`] (11). All response types are
//! The current ABI version is [`ABI_VERSION`] (12). All response types are
//! CBOR-encoded using the canonical rules defined in `docs/spec/js-cbor-mapping.md`.
//! Breaking changes to response shapes or error codes require a bump to the
//! ABI version.
Expand Down Expand Up @@ -40,7 +40,7 @@ use serde::{
///
/// Increment when response types, error codes, or method signatures change
/// in a backward-incompatible way.
pub const ABI_VERSION: u32 = 11;
pub const ABI_VERSION: u32 = 12;

fn deserialize_opaque_id<'de, D>(deserializer: D) -> Result<[u8; 32], D::Error>
where
Expand Down Expand Up @@ -1666,6 +1666,71 @@ pub struct ContractEvidenceIdentity {
pub op_kind: ContractOperationKind,
}

/// Semantic role of retained contract evidence.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RetainedEvidenceRole {
/// Generated contract artifact bytes.
ContractArtifact,
/// Scheduler receipt or receipt-adjacent material.
ContractReceipt,
/// Law witness or witness-adjacent material.
Witness,
/// Reading payload bytes.
ReadingPayload,
/// Encoded reading envelope bytes.
ReadingEnvelope,
/// Generated observer artifact bytes.
ObserverArtifact,
}

/// Semantic coordinate for retained contract evidence.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RetainedEvidenceCoordinate {
/// Installed contract evidence identity that owns the coordinate.
pub contract: ContractEvidenceIdentity,
/// Retained evidence role.
pub role: RetainedEvidenceRole,
/// Domain-separated semantic digest for the exact receipt, witness, reading,
/// or artifact coordinate.
pub semantic_digest: Vec<u8>,
/// Stable coordinate id used for missing-retention obstruction subjects.
pub coordinate_id: Vec<u8>,
}

/// First-class reference to retained contract evidence bytes.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RetainedEvidenceRef {
/// Semantic coordinate that explains what the retained bytes answer.
pub coordinate: RetainedEvidenceCoordinate,
/// Content-only hash for the retained bytes.
pub content_hash: Vec<u8>,
/// Retained byte length.
pub byte_len: u64,
/// Stable id that binds semantic coordinate, content hash, and byte length.
pub evidence_ref_id: Vec<u8>,
}

/// Retained evidence availability posture.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum RetainedEvidencePosture {
/// The retained evidence is locally available.
Available {
/// Available retained evidence reference.
reference: Box<RetainedEvidenceRef>,
},
/// Required retained evidence is missing.
MissingRetention {
/// Coordinate that should eventually name retained evidence, if known.
coordinate: Option<Box<RetainedEvidenceCoordinate>>,
/// Exact retained reference whose content is missing, if known.
reference: Option<Box<RetainedEvidenceRef>>,
/// Missing-retention id surfaced by the obstruction.
retention_id: Vec<u8>,
},
}

/// Stable identity of the QueryView question answered by a reading.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct QueryReadingIdentity {
Expand Down Expand Up @@ -1698,6 +1763,9 @@ pub struct ReadingEnvelope {
/// Stable query reading identity, when this envelope answered QueryView.
#[serde(default)]
pub query_identity: Option<QueryReadingIdentity>,
/// Retained evidence refs or missing-retention posture for this reading.
#[serde(default)]
pub retained_evidence: Vec<RetainedEvidencePosture>,
/// Witnesses or shell references that support the reading.
pub witness_refs: Vec<ReadingWitnessRef>,
/// Read-side parent/strand basis posture.
Expand Down
60 changes: 58 additions & 2 deletions crates/echo-wasm-abi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ mod tests {

#[test]
fn kernel_port_abi_version_tracks_reading_envelope_contract_fields() {
assert_eq!(kernel_port::ABI_VERSION, 11);
assert_eq!(kernel_port::ABI_VERSION, 12);
}

#[test]
Expand Down Expand Up @@ -1014,6 +1014,7 @@ mod tests {
observer_basis: ReadingObserverBasis::CommitBoundary,
contract: None,
query_identity: None,
retained_evidence: Vec::new(),
witness_refs: vec![ReadingWitnessRef::ResolvedCommit { reference }],
parent_basis_posture: ObservationBasisPosture::Worldline,
budget_posture: ReadingBudgetPosture::UnboundedOneShot,
Expand All @@ -1033,7 +1034,10 @@ mod tests {
entries.retain(|(key, _)| {
!matches!(
key,
Value::Text(text) if text == "contract" || text == "query_identity"
Value::Text(text)
if text == "contract"
|| text == "query_identity"
|| text == "retained_evidence"
)
});
}
Expand All @@ -1042,6 +1046,7 @@ mod tests {
assert_eq!(legacy_reading, envelope.reading);
assert_eq!(legacy_reading.contract, None);
assert_eq!(legacy_reading.query_identity, None);
assert_eq!(legacy_reading.retained_evidence, Vec::new());

let optic_result = ObserveOpticResult::Reading(Box::new(OpticReading {
envelope: envelope.reading.clone(),
Expand Down Expand Up @@ -1120,6 +1125,57 @@ mod tests {
));
}

#[test]
fn test_retained_evidence_posture_round_trip() {
use crate::kernel_port::{
ContractEvidenceIdentity, ContractOperationKind, RetainedEvidenceCoordinate,
RetainedEvidencePosture, RetainedEvidenceRef, RetainedEvidenceRole,
};
use alloc::boxed::Box;

let contract = ContractEvidenceIdentity {
package_id: vec![20; 32],
echo_abi_version: 1,
package_name: "fixture-contract".into(),
package_version: "0.1.0".into(),
artifact_hash_hex: "aa".repeat(32),
codec_id: "fixture-codec".into(),
registry_version: 1,
wesley_generator_version: "echo-wesley-gen/0.1.0".into(),
helper_api_version: 1,
schema_sha256_hex: "bb".repeat(32),
op_id: 7,
op_kind: ContractOperationKind::Query,
};
let coordinate = RetainedEvidenceCoordinate {
contract,
role: RetainedEvidenceRole::ReadingPayload,
semantic_digest: vec![21; 32],
coordinate_id: vec![22; 32],
};
let reference = RetainedEvidenceRef {
coordinate: coordinate.clone(),
content_hash: vec![23; 32],
byte_len: 12,
evidence_ref_id: vec![24; 32],
};

let postures = vec![
RetainedEvidencePosture::Available {
reference: Box::new(reference.clone()),
},
RetainedEvidencePosture::MissingRetention {
coordinate: Some(Box::new(coordinate)),
reference: Some(Box::new(reference)),
retention_id: vec![25; 32],
},
];

let decoded: Vec<RetainedEvidencePosture> =
decode_cbor(&encode_cbor(&postures).unwrap()).unwrap();
assert_eq!(decoded, postures);
}

#[test]
fn test_optic_intent_dispatch_result_variants_round_trip() {
use crate::kernel_port::{
Expand Down
1 change: 1 addition & 0 deletions crates/echo-wesley-gen/tests/generation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ mod tests {
budget_posture: ReadingBudgetPosture::UnboundedOneShot,
rights_posture: ReadingRightsPosture::KernelPublic,
residual_posture: ReadingResidualPosture::Complete,
retained_evidence: Vec::new(),
},
frame: request.frame,
projection: request.projection,
Expand Down
Loading
Loading