Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
77bcae6
docs: add 0024 universal LE binary codec design doc
flyingrobots May 27, 2026
8471e5a
feat(codec): add i32, f32, bool, option, list primitives (Phase 2)
flyingrobots May 28, 2026
a1251a7
feat(echo-wesley-gen): emit LE binary Encode/Decode impls (Phase 4)
flyingrobots May 28, 2026
09f3e2c
test(codec): rope cross-boundary LE binary fixture proofs
flyingrobots May 28, 2026
f24b110
test(envelope): EINT cross-boundary fixture proof for jedit
flyingrobots May 28, 2026
dded4e0
test(emit): pin rope op id outputs in stable_op_id_pinned
flyingrobots May 28, 2026
cf2b4cd
docs(backlog): capture PLATFORM bad-code and cool-ideas from 0024 work
flyingrobots May 28, 2026
bb257dd
docs(backlog): no published umbrella for the WARP optics ecosystem
flyingrobots May 29, 2026
5a73a3f
docs(design): 0025 sessions as causal contexts (Phase 1 design)
flyingrobots May 29, 2026
8a21f04
docs(design): revise 0025 to integrate with existing warp-core primit…
flyingrobots May 29, 2026
a53cac9
docs(design): 0025 pre-RED cleanup patch
flyingrobots May 29, 2026
34e4984
docs(design): 0025 pre-RED clarifications
flyingrobots May 29, 2026
473d830
docs(handoff): 0025 Phase 2 handoff note
flyingrobots May 29, 2026
499ad99
docs(design): reconcile 0025 internal consistency post-review
flyingrobots May 30, 2026
7468488
Fix: canonicalize floats on decode + bound list pre-allocation
flyingrobots May 30, 2026
8ef2fc9
Fix: wesley-gen nullable lists, no_std ID encoding, and list-decoder …
flyingrobots May 30, 2026
f25e07a
Fix: address PR #382 review threads (10 fixes across wesley-gen, code…
flyingrobots May 30, 2026
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
494 changes: 492 additions & 2 deletions crates/echo-wasm-abi/src/codec.rs

Large diffs are not rendered by default.

489 changes: 489 additions & 0 deletions crates/echo-wasm-abi/tests/jedit_rope_cross_boundary.rs

Large diffs are not rendered by default.

47 changes: 47 additions & 0 deletions crates/echo-wasm-abi/tests/jedit_rope_cross_boundary_eint.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: Apache-2.0
// © James Ross Ω FLYING•ROBOTS <https://github.com/flyingrobots>
#![allow(clippy::unwrap_used, clippy::expect_used)]
//! Cross-boundary fixture proofs for the EINT envelope.
//!
//! These tests assert that `echo_wasm_abi::pack_intent_v1` produces byte
//! sequences that are bytewise identical to the literal hex vectors asserted
//! by `jedit/spec/eint.spec.mjs`. The hex literals here MUST stay in lockstep
//! with that TS spec — they are the cross-boundary contract for the EINT
//! envelope itself (the wrapper that carries LE-binary vars across the WASM
//! boundary).

use echo_wasm_abi::pack_intent_v1;

#[test]
fn pack_intent_v1_op1_three_byte_vars_matches_ts_spec() {
let bytes = pack_intent_v1(1, &[0x01, 0x02, 0x03]).unwrap();
let expected: Vec<u8> = vec![
0x45, 0x49, 0x4e, 0x54, // "EINT"
0x01, 0x00, 0x00, 0x00, // op_id = 1, u32 LE
0x03, 0x00, 0x00, 0x00, // vars_len = 3
0x01, 0x02, 0x03, // vars
];
assert_eq!(bytes, expected);
}

#[test]
fn pack_intent_v1_deadbeef_empty_vars_matches_ts_spec() {
let bytes = pack_intent_v1(0xdead_beef, &[]).unwrap();
let expected: Vec<u8> = vec![
0x45, 0x49, 0x4e, 0x54, // "EINT"
0xef, 0xbe, 0xad, 0xde, // op_id = 0xdeadbeef LE
0x00, 0x00, 0x00, 0x00, // vars_len = 0
];
assert_eq!(bytes, expected);
}

#[test]
fn pack_intent_v1_op_one_empty_vars_matches_ts_spec() {
let bytes = pack_intent_v1(1, &[]).unwrap();
let expected: Vec<u8> = vec![
0x45, 0x49, 0x4e, 0x54, // "EINT"
0x01, 0x00, 0x00, 0x00, // op_id = 1
0x00, 0x00, 0x00, 0x00, // vars_len = 0
];
assert_eq!(bytes, expected);
}
527 changes: 484 additions & 43 deletions crates/echo-wesley-gen/src/main.rs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"ir_version": "echo-ir/v1",
"schema_sha256": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"codec_id": "cbor-canon-v1",
"codec_id": "le-binary-v1",
"registry_version": 1,
"types": [
{
Expand Down
73 changes: 59 additions & 14 deletions crates/echo-wesley-gen/tests/generation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ type Mutation {
assert!(stdout.contains("pub struct CounterValue"));
assert!(stdout.contains("pub struct IncrementInput"));
assert!(stdout.contains("pub const ECHO_CONTRACT_ABI_VERSION: u32 = 1"));
assert!(stdout.contains("pub const CODEC_ID: &str = \"cbor-canon-v1\""));
assert!(stdout.contains("pub const CODEC_ID: &str = \"le-binary-v1\""));
assert!(stdout.contains("pub const REGISTRY_VERSION: u32 = 1"));
assert!(stdout.contains("pub const WESLEY_GENERATOR_VERSION: &str = \"echo-wesley-gen/0.1.0\""));
assert!(stdout.contains("pub const CONTRACT_HOST_HELPER_API_VERSION: u32 = 1"));
Expand Down Expand Up @@ -237,6 +237,7 @@ mod tests {
SchedulerState, SchedulerStatus, WorkState, WorldlineId, WorldlineTick, ABI_VERSION,
};
use echo_wasm_abi::{decode_cbor, encode_cbor, unpack_intent_v1};
use echo_wasm_abi::codec::decode_from_bytes;

#[derive(Default)]
struct ToyKernel {
Expand Down Expand Up @@ -266,7 +267,7 @@ mod tests {
message: error.to_string(),
})?;
assert_eq!(op_id, OP_INCREMENT);
let decoded: IncrementVars = decode_cbor(vars).map_err(|error| AbiError {
let decoded: IncrementVars = decode_from_bytes(vars).map_err(|error| AbiError {
code: 2,
message: error.to_string(),
})?;
Expand Down Expand Up @@ -295,7 +296,7 @@ mod tests {
};
assert_eq!(*query_id, OP_COUNTER_VALUE);
let _decoded: CounterValueVars =
decode_cbor(vars_bytes).map_err(|error| AbiError {
decode_from_bytes(vars_bytes).map_err(|error| AbiError {
code: 3,
message: error.to_string(),
})?;
Expand Down Expand Up @@ -428,7 +429,7 @@ mod tests {
.unwrap();
let (op_id, vars) = unpack_intent_v1(&intent).unwrap();
assert_eq!(op_id, OP_INCREMENT);
let decoded: IncrementVars = decode_cbor(vars).unwrap();
let decoded: IncrementVars = decode_from_bytes(vars).unwrap();
assert_eq!(decoded.input.amount, 42);

let mut kernel = ToyKernel::default();
Expand Down Expand Up @@ -547,7 +548,7 @@ mod tests {
let OpticIntentPayload::EintV1 { bytes } = &dispatch.payload;
let (op_id, vars_bytes) = unpack_intent_v1(bytes).unwrap();
assert_eq!(op_id, OP_INCREMENT);
let decoded: IncrementVars = decode_cbor(vars_bytes).unwrap();
let decoded: IncrementVars = decode_from_bytes(vars_bytes).unwrap();
assert_eq!(decoded.input.amount, 42);
}

Expand Down Expand Up @@ -1301,7 +1302,7 @@ fn test_reserved_control_op_id_fails_closed() {
let ir = r#"{
"ir_version": "echo-ir/v1",
"schema_sha256": "abc123",
"codec_id": "cbor-canon-v1",
"codec_id": "le-binary-v1",
"registry_version": 1,
"types": [
{ "name": "RunStatus", "kind": "OBJECT", "fields": [
Expand Down Expand Up @@ -1354,7 +1355,7 @@ fn test_generate_from_json() {
let ir = r#"{
"ir_version": "echo-ir/v1",
"schema_sha256": "abc123",
"codec_id": "cbor-canon-v1",
"codec_id": "le-binary-v1",
"registry_version": 7,
"types": [
{
Expand Down Expand Up @@ -1404,7 +1405,7 @@ fn test_generate_from_json() {
assert!(stdout.contains("pub theme: Theme"));
assert!(stdout.contains("pub tags: Option<Vec<String>>"));
assert!(stdout.contains("pub const SCHEMA_SHA256: &str = \"abc123\""));
assert!(stdout.contains("pub const CODEC_ID: &str = \"cbor-canon-v1\""));
assert!(stdout.contains("pub const CODEC_ID: &str = \"le-binary-v1\""));
Comment thread
coderabbitai[bot] marked this conversation as resolved.
assert!(stdout.contains("pub const REGISTRY_VERSION: u32 = 7"));
assert!(stdout.contains("pub const OP_SET_THEME: u32 = 111"));
assert!(stdout.contains("pub const OP_APP_STATE: u32 = 222"));
Expand Down Expand Up @@ -1531,7 +1532,7 @@ fn test_footprint_artifact_hash_changes_when_generated_args_change() {
let without_arg = r#"{
"ir_version": "echo-ir/v1",
"schema_sha256": "abc123",
"codec_id": "cbor-canon-v1",
"codec_id": "le-binary-v1",
"registry_version": 1,
"types": [
{ "name": "CounterValue", "kind": "OBJECT", "fields": [
Expand All @@ -1557,7 +1558,7 @@ fn test_footprint_artifact_hash_changes_when_generated_args_change() {
let with_arg = r#"{
"ir_version": "echo-ir/v1",
"schema_sha256": "abc123",
"codec_id": "cbor-canon-v1",
"codec_id": "le-binary-v1",
"registry_version": 1,
"types": [
{ "name": "CounterValue", "kind": "OBJECT", "fields": [
Expand Down Expand Up @@ -1613,7 +1614,7 @@ fn test_query_only_contract_does_not_import_intent_packer() {
let ir = r#"{
"ir_version": "echo-ir/v1",
"schema_sha256": "abc123",
"codec_id": "cbor-canon-v1",
"codec_id": "le-binary-v1",
"registry_version": 1,
"types": [],
"ops": [
Expand Down Expand Up @@ -1642,7 +1643,7 @@ fn test_operation_vars_type_collision_uses_helper_namespace() {
let ir = r#"{
"ir_version": "echo-ir/v1",
"schema_sha256": "abc123",
"codec_id": "cbor-canon-v1",
"codec_id": "le-binary-v1",
"registry_version": 1,
"types": [
{
Expand Down Expand Up @@ -1680,7 +1681,7 @@ fn test_generated_intent_error_user_type_does_not_collide_with_helper_error() {
let ir = r#"{
"ir_version": "echo-ir/v1",
"schema_sha256": "abc123",
"codec_id": "cbor-canon-v1",
"codec_id": "le-binary-v1",
"registry_version": 1,
"types": [
{
Expand Down Expand Up @@ -1718,7 +1719,7 @@ fn test_query_mutation_operation_name_collision_fails_with_clear_diagnostic() {
let ir = r#"{
"ir_version": "echo-ir/v1",
"schema_sha256": "abc123",
"codec_id": "cbor-canon-v1",
"codec_id": "le-binary-v1",
"registry_version": 1,
"types": [],
"ops": [
Expand Down Expand Up @@ -1811,6 +1812,50 @@ fn test_toy_contract_no_std_generated_output_checks_in_consumer_crate() {
));
}

#[test]
fn test_no_std_id_list_field_compiles_in_consumer_crate() {
// Regression: scalar_list_element_{encoder,decoder} used to treat ID as
// String unconditionally, so under --no-std a [ID] field generated
// v.as_str() on [u8; 32] (encode) and read_string into Vec<[u8; 32]>
// (decode), neither of which compile. The list element helpers must
// honor the same no_std ID -> [u8; 32] mapping as the scalar helpers.
let ir = r#"{
"ir_version": "echo-ir/v1",
"types": [
{
"name": "TagBag",
"kind": "INPUT_OBJECT",
"fields": [
{ "name": "tags", "type": "ID", "required": true, "list": true },
{ "name": "optional_tags", "type": "ID", "required": false, "list": true }
]
}
],
"ops": []
}"#;
let output = run_wesley_gen_with_args(ir, &["--no-std"]);
assert!(
output.status.success(),
"CLI failed: {}",
String::from_utf8_lossy(&output.stderr)
);
let generated = String::from_utf8_lossy(&output.stdout);
// The generated struct must declare list elements as [u8; 32], not String.
assert!(
generated.contains("pub tags: Vec<[u8; 32]>"),
"expected Vec<[u8; 32]> for required [ID] under no_std, got:\n{generated}"
);
assert!(
generated.contains("pub optional_tags: Option<Vec<[u8; 32]>>"),
"expected Option<Vec<[u8; 32]>> for nullable [ID] under no_std, got:\n{generated}"
);
assert_generated_crate_checks(&write_basic_generated_crate(
generated.as_ref(),
"no-std-id-list",
true,
));
}

#[test]
fn test_contract_host_generation_rejects_no_std() {
let output = run_wesley_gen_with_args(TOY_COUNTER_IR, &["--no-std", "--contract-host"]);
Expand Down
Loading
Loading