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
15 changes: 8 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

### Added

- Local verification now maps `warp-core` optic artifact and causal fact source
changes to the exact integration test targets they exercise, avoiding broad
Cargo name-filter runs while preserving targeted smoke coverage.
- `warp-core` now publishes in-memory causal graph facts from optic artifact
registration. Successful registration emits `GraphFact::ArtifactRegistered`,
computes a deterministic `FactDigest`, and links that digest from an
Expand All @@ -18,16 +21,14 @@
publication boundary: facts are world statements, receipts explain
publication/refusal boundaries, and registration facts are the first
in-memory proof that Echo can describe its own runtime decisions.
- `echo-wesley-gen` now imports real `wesley-core` 0.0.3 runtime optic
- `echo-wesley-gen` now imports real `wesley-core` 0.0.4 runtime optic
artifacts into `warp-core` registration structs, preserving Wesley artifact
hashes, schema ids, operation ids, requirements digests, and registration
descriptors while keeping `warp-core` free of a Wesley dependency. Echo still
owns opaque runtime-local `OpticArtifactHandle` issuance, and the imported
requirements bytes are staged through an explicit adapter-local v0
canonicalization helper for registration proof only, not enforcement.
`warp-core` stores those requirements as opaque bytes and must not interpret
them for admission until Wesley exposes canonical requirements bytes and a
durable codec.
owns opaque runtime-local `OpticArtifactHandle` issuance. Imported admission
requirements now preserve Wesley-owned canonical requirement bytes, codec id,
and digest directly from `OpticAdmissionRequirementsArtifact`; downstream
runtimes must not serialize Wesley structs to create admission truth.
- `docs/procedures/DIRECT-MAIN-EXCEPTION-LOG.md` records the 2026-05-14
docs-only direct-main exception for the Echo graph model checkpoint, including
authorization context, exact commits, validation, changed files, and the
Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/echo-wesley-gen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ serde_json = "1.0"
syn = { version = "2.0", features = ["full", "extra-traits"] }
prettyplease = "0.2"
warp-core = { workspace = true }
wesley-core = "0.0.3"
wesley-core = "0.0.4"


[lints]
Expand Down
4 changes: 4 additions & 0 deletions crates/echo-wesley-gen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,7 @@ cat ir.json | cargo run -p echo-wesley-gen -- --out generated.rs
retained reading identity.
- Optional fields become `Option<T>`; lists become `Vec<T>` (wrapped in Option when not required).
- Unknown scalar names are emitted as identifiers as-is (so ensure upstream IR types are valid Rust idents).
- Runtime optic artifact imports preserve Wesley-owned canonical admission
requirement bytes, codec id, and digest directly from
`OpticAdmissionRequirementsArtifact`; Echo stores them as opaque registry
payload and does not reserialize Wesley requirement structs.
117 changes: 13 additions & 104 deletions crates/echo-wesley-gen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,10 @@
//! runtime registration and opaque handles. This crate is the dependency seam
//! that may see both sides.
//!
//! The v0 adapter stores Wesley admission requirements as deterministic
//! `serde_json` bytes in `warp-core`. Those bytes are registry payload only;
//! enforcement, grant validation, admission tickets, witnesses, and execution
//! are intentionally out of scope for this adapter.

/// Adapter-local staging codec for imported Wesley admission requirements.
///
/// This is not runtime admission truth. It is a v0 canonical staging format so
/// Echo can store opaque requirements bytes while Wesley grows a durable
/// canonical requirements byte/codec surface.
pub const WESLEY_REQUIREMENTS_STAGING_CODEC_V0: &str =
"echo-wesley-gen/wesley-requirements-canonical-json-staging/v0";
//! The adapter imports Wesley-owned canonical admission requirement bytes,
//! codec ids, and digests into `warp-core` without reserializing Wesley
//! requirement structs. Enforcement, grant validation, admission tickets,
//! witnesses, and execution are intentionally out of scope for this adapter.

/// Imported Wesley runtime optic artifact ready for Echo registration.
#[derive(Debug, Clone, PartialEq, Eq)]
Expand All @@ -37,40 +29,31 @@ pub struct ImportedRuntimeOpticArtifact {
pub fn import_runtime_optic_artifact(
artifact: &wesley_core::OpticArtifact,
) -> anyhow::Result<ImportedRuntimeOpticArtifact> {
let requirements_bytes = canonicalize_wesley_requirements_v0(&artifact.requirements)?;
if artifact.requirements_digest != artifact.requirements_artifact.digest {
anyhow::bail!(
"Wesley artifact requirements digest does not match requirements artifact digest"
);
}

Ok(ImportedRuntimeOpticArtifact {
artifact: warp_core::OpticArtifact {
artifact_id: artifact.artifact_id.clone(),
artifact_hash: artifact.artifact_hash.clone(),
schema_id: artifact.schema_id.clone(),
requirements_digest: artifact.requirements_digest.clone(),
requirements_digest: artifact.requirements_artifact.digest.clone(),
operation: warp_core::OpticArtifactOperation {
operation_id: artifact.operation.operation_id.clone(),
},
requirements: warp_core::OpticAdmissionRequirements {
bytes: requirements_bytes,
codec: artifact.requirements_artifact.codec.clone(),
digest: artifact.requirements_artifact.digest.clone(),
bytes: artifact.requirements_artifact.bytes.clone(),
},
},
descriptor: import_registration_descriptor(&artifact.registration),
})
}

/// Canonicalizes Wesley admission requirements for adapter-local v0 staging.
///
/// The helper deliberately names the seam: these bytes are not permanent
/// artifact truth and `warp-core` must not interpret them for admission.
/// Durable admission should eventually consume Wesley-owned canonical
/// requirements bytes plus an explicit codec id.
pub fn canonicalize_wesley_requirements_v0(
requirements: &wesley_core::OpticAdmissionRequirements,
) -> anyhow::Result<Vec<u8>> {
let value = serde_json::to_value(requirements)?;
let mut bytes = Vec::new();
write_canonical_json_value(&value, &mut bytes)?;
Ok(bytes)
}

/// Imports a Wesley registration descriptor into Echo's registration shape.
pub fn import_registration_descriptor(
descriptor: &wesley_core::OpticRegistrationDescriptor,
Expand All @@ -83,77 +66,3 @@ pub fn import_registration_descriptor(
requirements_digest: descriptor.requirements_digest.clone(),
}
}

fn write_canonical_json_value(
value: &serde_json::Value,
bytes: &mut Vec<u8>,
) -> anyhow::Result<()> {
match value {
serde_json::Value::Null => bytes.extend_from_slice(b"null"),
serde_json::Value::Bool(true) => bytes.extend_from_slice(b"true"),
serde_json::Value::Bool(false) => bytes.extend_from_slice(b"false"),
serde_json::Value::Number(number) => {
bytes.extend_from_slice(serde_json::to_string(number)?.as_bytes());
}
serde_json::Value::String(text) => {
bytes.extend_from_slice(serde_json::to_string(text)?.as_bytes());
}
serde_json::Value::Array(values) => {
bytes.push(b'[');
for (index, item) in values.iter().enumerate() {
if index > 0 {
bytes.push(b',');
}
write_canonical_json_value(item, bytes)?;
}
bytes.push(b']');
}
serde_json::Value::Object(object) => {
let mut entries: Vec<_> = object.iter().collect();
entries.sort_by(|(left_key, _), (right_key, _)| left_key.cmp(right_key));

bytes.push(b'{');
for (index, (key, item)) in entries.into_iter().enumerate() {
if index > 0 {
bytes.push(b',');
}
bytes.extend_from_slice(serde_json::to_string(key)?.as_bytes());
bytes.push(b':');
write_canonical_json_value(item, bytes)?;
}
bytes.push(b'}');
}
}

Ok(())
}

#[cfg(test)]
mod tests {
use super::write_canonical_json_value;

#[test]
fn canonical_json_writer_sorts_object_keys_recursively() -> anyhow::Result<()> {
let left = serde_json::json!({
"z": [{"b": 2, "a": 1}],
"a": {"d": false, "c": true}
});
let right = serde_json::json!({
"a": {"c": true, "d": false},
"z": [{"a": 1, "b": 2}]
});
let mut left_bytes = Vec::new();
let mut right_bytes = Vec::new();

write_canonical_json_value(&left, &mut left_bytes)?;
write_canonical_json_value(&right, &mut right_bytes)?;

assert_eq!(left_bytes, right_bytes);
assert_eq!(
left_bytes,
br#"{"a":{"c":true,"d":false},"z":[{"a":1,"b":2}]}"#
);

Ok(())
}
}
2 changes: 1 addition & 1 deletion crates/echo-wesley-gen/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use ir::{OpKind, TypeKind, WesleyIR};
const ECHO_IR_VERSION: &str = "echo-ir/v1";
const DEFAULT_CODEC_ID: &str = "cbor-canon-v1";
const DEFAULT_REGISTRY_VERSION: u32 = 1;
const WESLEY_CORE_VERSION: &str = "0.0.3";
const WESLEY_CORE_VERSION: &str = "0.0.4";

#[derive(Parser)]
#[command(
Expand Down
95 changes: 72 additions & 23 deletions crates/echo-wesley-gen/tests/runtime_optic_import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
#![allow(clippy::expect_used)]
//! Proof that `echo-wesley-gen` adapts real Wesley runtime optic artifacts into Echo.

use echo_wesley_gen::{
canonicalize_wesley_requirements_v0, import_runtime_optic_artifact,
WESLEY_REQUIREMENTS_STAGING_CODEC_V0,
};
use echo_wesley_gen::import_runtime_optic_artifact;
use warp_core::{
OpticArtifactRegistrationError, OpticArtifactRegistry, OPTIC_ARTIFACT_HANDLE_KIND,
};
Expand Down Expand Up @@ -139,6 +136,14 @@ fn imports_real_wesley_runtime_optic_artifact() {
!imported.artifact.requirements.bytes.is_empty(),
"Wesley requirements must be imported into Echo registry bytes"
);
assert_eq!(
imported.artifact.requirements.codec,
wesley_artifact.requirements_artifact.codec
);
assert_eq!(
imported.artifact.requirements.digest,
wesley_artifact.requirements_artifact.digest
);
}

#[test]
Expand Down Expand Up @@ -171,18 +176,21 @@ fn registers_imported_wesley_artifact_and_returns_opaque_handle() {

#[test]
fn imported_requirements_bytes_are_deterministic() {
let wesley_artifact = compile_fixture_artifact();
let first = canonicalize_wesley_requirements_v0(&wesley_artifact.requirements)
.expect("requirements canonicalization should succeed");
let second = canonicalize_wesley_requirements_v0(&wesley_artifact.requirements)
.expect("requirements canonicalization should repeat");
let first_artifact = compile_fixture_artifact();
let second_artifact = compile_fixture_artifact();
let first_import =
import_runtime_optic_artifact(&wesley_artifact).expect("artifact import should succeed");
import_runtime_optic_artifact(&first_artifact).expect("artifact import should succeed");
let second_import =
import_runtime_optic_artifact(&wesley_artifact).expect("artifact import should repeat");
import_runtime_optic_artifact(&second_artifact).expect("artifact import should repeat");

assert_eq!(first, second);
assert_eq!(first_import.artifact.requirements.bytes, first);
assert_eq!(
first_artifact.requirements_artifact,
second_artifact.requirements_artifact
);
assert_eq!(
first_import.artifact.requirements.bytes,
first_artifact.requirements_artifact.bytes
);
assert_eq!(
first_import.artifact.requirements.bytes,
second_import.artifact.requirements.bytes
Expand All @@ -192,23 +200,64 @@ fn imported_requirements_bytes_are_deterministic() {
#[test]
fn imported_requirements_bytes_are_not_empty() {
let wesley_artifact = compile_fixture_artifact();
let bytes = canonicalize_wesley_requirements_v0(&wesley_artifact.requirements)
.expect("requirements canonicalization should succeed");

assert!(!bytes.is_empty());
assert!(!wesley_artifact.requirements_artifact.bytes.is_empty());
}

#[test]
fn imports_wesley_owned_requirements_artifact_without_reserializing_requirements() {
let wesley_artifact = compile_fixture_artifact();
let imported =
import_runtime_optic_artifact(&wesley_artifact).expect("artifact import should succeed");

assert_eq!(
imported.artifact.requirements_digest,
wesley_artifact.requirements_artifact.digest
);
assert_eq!(
imported.artifact.requirements.digest,
wesley_artifact.requirements_artifact.digest
);
assert_eq!(
imported.artifact.requirements.codec,
wesley_artifact.requirements_artifact.codec
);
assert_eq!(
imported.artifact.requirements.bytes,
wesley_artifact.requirements_artifact.bytes
);
}

#[test]
fn imported_requirements_bytes_are_adapter_canonical_staging() {
fn imported_requirements_artifact_is_wesley_owned() {
let wesley_artifact = compile_fixture_artifact();
let imported =
import_runtime_optic_artifact(&wesley_artifact).expect("artifact import should succeed");
let staged_bytes = canonicalize_wesley_requirements_v0(&wesley_artifact.requirements)
.expect("requirements canonicalization should succeed");

assert_eq!(imported.artifact.requirements.bytes, staged_bytes);
assert!(WESLEY_REQUIREMENTS_STAGING_CODEC_V0.contains("staging"));
assert!(WESLEY_REQUIREMENTS_STAGING_CODEC_V0.ends_with("/v0"));
assert_eq!(
imported.artifact.requirements.bytes,
wesley_artifact.requirements_artifact.bytes
);
assert_eq!(
imported.artifact.requirements.codec,
wesley_artifact.requirements_artifact.codec
);
assert_eq!(
imported.artifact.requirements.digest,
wesley_artifact.requirements_artifact.digest
);
assert_eq!(
imported.artifact.requirements.digest,
imported.artifact.requirements_digest
);
}

#[test]
fn rejects_wesley_artifact_with_mismatched_requirements_artifact_digest() {
let mut wesley_artifact = compile_fixture_artifact();
wesley_artifact.requirements_digest = "tampered-requirements-digest".to_owned();

assert!(import_runtime_optic_artifact(&wesley_artifact).is_err());
}

#[test]
Expand Down Expand Up @@ -302,6 +351,6 @@ fn warp_core_does_not_depend_on_serde_json_for_wesley_requirements() {

assert!(
active_serde_json_lines.is_empty(),
"warp-core must not depend on serde_json for Wesley requirements; JSON staging belongs in echo-wesley-gen"
"warp-core must not depend on serde_json for Wesley requirements; canonical requirements bytes belong to Wesley"
);
}
7 changes: 5 additions & 2 deletions crates/warp-core/src/optic_artifact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,11 @@ pub struct OpticArtifactOperation {
/// must not provide replacement requirements or footprint law.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct OpticAdmissionRequirements {
/// Opaque requirement bytes. Later slices can replace this fixture-shaped
/// payload with shared Wesley/Continuum types without changing ownership.
/// Explicit codec id for the opaque requirement bytes.
pub codec: String,
/// Wesley-computed digest of the opaque requirement bytes.
pub digest: String,
/// Opaque requirement bytes emitted by Wesley.
pub bytes: Vec<u8>,
}

Expand Down
2 changes: 2 additions & 0 deletions crates/warp-core/tests/causal_fact_publication_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ fn fixture_artifact() -> OpticArtifact {
operation_id: "operation:textWindow:v0".to_owned(),
},
requirements: OpticAdmissionRequirements {
codec: "wesley.requirements.canonical-json.v0".to_owned(),
digest: "requirements-digest:stack-witness-0001".to_owned(),
bytes: b"fixture admission requirements".to_vec(),
},
}
Expand Down
2 changes: 2 additions & 0 deletions crates/warp-core/tests/optic_artifact_registry_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ fn fixture_artifact() -> OpticArtifact {
operation_id: "operation:textWindow:v0".to_owned(),
},
requirements: OpticAdmissionRequirements {
codec: "wesley.requirements.canonical-json.v0".to_owned(),
digest: "requirements-digest:stack-witness-0001".to_owned(),
bytes: b"fixture admission requirements".to_vec(),
},
}
Expand Down
Loading
Loading