0024 universal LE binary codec + 0025 sessions design (design-only)#382
Conversation
Extends echo-wasm-abi::codec with the missing GraphQL scalar coverage required by the universal LE binary codec: write_i32_le / read_i32_le — for GraphQL Int write_f32_le / read_f32_le — for GraphQL Float (with inline canonicalization) write_bool / read_bool — for GraphQL Boolean (u8 0x00/0x01) write_option / read_option — presence tag (u8) + conditional payload write_list / read_list — u32 LE count + elements Also adds `canonicalize_f32()` as a public helper that replicates the F32Scalar::new() invariant without depending on warp-math: NaN (any) → 0x7fc00000, subnormal → +0.0, -0.0 → +0.0. Adds CodecError::InvalidBoolTag for malformed bool/option presence bytes. 28 new unit tests cover roundtrips, wire byte layout, and f32 edge cases (NaN variants, subnormals, -0.0, +Infinity). Part of 0024-universal-le-binary-codec Phase 2.
Replace CBOR vars encoding with echo_wasm_abi::codec LE binary. Generate impl Encode + Decode for all InputObject and Enum types. Generate impl Encode + Decode for all Vars structs. Always force CODEC_ID = "le-binary-v1" (hard cutover, no migration). Update encode/decode helpers and error types to use codec::CodecError. Update generation tests and smoke crate to match new behavior. Also adds CodecError::Trailing and Reader::remaining() to codec.rs so decode_from_bytes rejects trailing bytes after a complete decode. Part of 0024-universal-le-binary-codec Phase 4.
Adds tests/jedit_rope_cross_boundary.rs in echo-wasm-abi that uses the LE binary codec primitives to encode canonical rope-schema Vars values and asserts the produced byte sequences are bytewise identical to the literal hex vectors asserted by jedit/spec/rope-codec.spec.mjs. This locks the Rust↔TS contract for the rope codec at the primitive level: AnchorBias / CheckpointKind enum discriminants, the CreateBufferWorldlineInput layout (with required + nullable fields mixed), the ReplaceRangeAsTickInput i32 layout, and the CreateCheckpointInput enum-in-the-middle layout. 12 tests, all green. If a future change breaks parity, both ends fail simultaneously: the TS spec on one side and these Rust assertions on the other. Covers Phase 6 (cross-boundary roundtrip fixtures) of docs/design/0024-universal-le-binary-codec/design.md.
Adds tests/jedit_rope_cross_boundary_eint.rs covering pack_intent_v1 against the same hex literals asserted by jedit/spec/eint.spec.mjs. Pairs the EINT envelope contract on the Rust side with the TS packIntentV1 added in jedit's stack/jedit-rope-rename. This locks the wrapper layer (envelope) so the next phase (swapping jedit's optic-client JSON wire for EINT-wrapped LE binary vars) can proceed without worrying about envelope drift.
Adds a `stable_op_id_pinned` test module to echo-wesley-gen that asserts the FNV-1a outputs for the five rope-schema operations match the same u32 values pinned by `wesley_core::stable_op_id` (added in wesley-core ≥0.0.5). These tests catch drift between echo-wesley-gen's local stable_op_id copy and wesley-core's canonical version, which will collapse to a single source of truth when echo bumps wesley-core to 0.0.5+. Until then, this pin is the guarantee that the local copy stays bytewise identical to Wesley. Also adds a docstring on the local `stable_op_id` flagging the transitional duplication.
- bad-code/PLATFORM_echo-wesley-gen-monolith: 2000+ line file mixes type/codec/op-id/footprint/registry/intent/optic/contract-host emit. Suggested split into composable sub-emitters. - bad-code/PLATFORM_warp-wasm-cbor-debt: outer request types and all response envelopes are still CBOR; `_cbor` function names; codec_id reports cbor-canonical-v1 while EINT path is LE binary. Audited 2026-05-28; full removal is a future cycle. - cool-ideas/PLATFORM_schema-version-fingerprint-prefix: 32-byte SCHEMA_SHA256 prefix on every framed message gives hard rejection instead of silent corruption — codec_id captures format version but not schema version. - cool-ideas/PLATFORM_wesley-emitted-fixture-vectors: wire-parity hex literals are duplicated in Rust and TS specs today; Wesley should emit fixture-vectors.json and both sides read the same file.
The frame that connects Echo, Wesley, jedit, git-warp, Graft, WARPDrive, warp-ttd, and the rest exists — it's the table in docs/architecture/there-is-no-graph.md under 'Runtimes As Optics'. A first-time visitor to github.com/flyingrobots cannot see it; they see ~20 sibling repos with no ordering, status, or dependency hints. Card proposes two shapes (profile README is the lightweight win; an umbrella repo with diagram + status is the heavier path) and recommends shipping the lightweight one first. Estimated half a day for the profile README; the biggest blocker is honestly labeling which projects are foundational vs experimental vs archived. Filed in echo's backlog because echo's docs already host the WARP optics framing language. The card prompts lifting that frame from buried architecture notes to a profile-level front door.
Pull and design phase for cycle 0025, extending the 0024 LE binary cutover
with a first-class Session concept in Echo. Branched from
stack/echo-le-binary-codec rather than main because the EINT envelope under
discussion in 0024 is exactly the surface where session addressing attaches;
deferring would force a wire-shape rebuild or block jedit's observe-path
cutover.
Promotes Session from jedit's client-side scaffold (the JeditWorldlineSession
Port introduced in jedit commit 26a8f43, Slice B of 0024) to a first-class
addressable causal-context node, with the following design commitments:
- Session is a new primitive that composes Writer/Worldline/Connection/
Optic, not a generalization of any of them.
- Two-event settlement (IntentReceiptIssued + IntentEffectsQuiesced)
replaces the ambiguous IntentSettled.
- runUntilIdle takes an explicit until: receipt | quiescent mode — no
fake idle.
- Two-stage close (graceful vs abortive); detached post-close sub-lanes
explicitly out of v1.
- Cross-session worldline concurrency: v1 obstruction-only; true
concurrent lanes deferred.
- Intent.target_worldlines typed plural, v1 enforces singleton.
- PrincipalRef-shaped writer identity from day one.
- No null/default session; system sessions are explicit and named.
- Naming collision with echo-session-proto acknowledged; coordination
plan stated.
Phase 1 STOP per METHOD: presenting the design for human review before
proceeding to RED/GREEN.
…ives
Substantial Phase 1 revision after reading warp-core. The original v1
positioned Session as owning an inbox and a causal lane. That conflicts
with existing primitives:
- warp-core::head_inbox already provides worldline-scoped ingress
(IngressTarget::DefaultWriter / InboxAddress / ExactHead, plus
HeadInbox queues). A session-owned inbox would create a duplicate
admission queue.
- warp-core::playback::SessionId already exists with semantics close
to this cycle's Session ("client's authenticated context and
subscription set"). Two live SessionId concepts are forbidden.
- warp-core::optic_artifact::PrincipalRef already exists and is the
right shape for Session.writer_ref.
Revised model:
- Session is a first-class durable causal-context node with
PrincipalRef, lifecycle state, admission gate, attribution surface,
and a queryable event projection.
- Session does NOT own an inbox. Submissions route through the
existing HeadInbox after the session admission gate validates
status/capability.
- Session does NOT own a causal lane. Worldline head/tick remains the
mutation-ordering authority. The session-scoped view of work is a
derived projection (working name SessionEventLog — explicitly NOT
SessionLane to avoid implying a competing causal-ordering primitive).
- playback::SessionId is unified with this cycle's SessionId. The
shape stays; ViewSession becomes a read-side facet attached to a
Session.
- System bootstrap: system/genesis is primordial (created at genesis,
no originating intent); all other system sessions opened normally
through it.
- EINT envelope change classified as "additive to the semantic
envelope model, potentially breaking to strict encoders/decoders;
coordinate with 0024."
- PLATFORM_echo-session-proto-split moved to graveyard. The networked
inspection prototype is retired entirely; the Session name is
reclaimed for the causal-context sense.
Phase 1 STOP per METHOD: presenting the revised design for review.
Eight targeted fixes after directional approval, before Phase 2 RED:
1. principal_ref / principal_label replace writer_ref / writer_label.
Session unifies read and write contexts; principal is the canonical
name.
2. Drop target_worldlines plural. v1 uses existing IngressTarget. Atomic
multi-target submissions deferred as a future cycle with a future
shape (NonEmptyList<IngressTarget> or AtomicIngressBatch).
3. SessionEventLog ordering pinned: engine event log's deterministic
event order (LogicalTime / event sequence + content-address tie-
breaker). Explicitly not mutation order.
4. IntentRejected gains stage (SessionGate | HeadInboxAdmission |
Execution) and reason. Distinguishes unknown-session (no projection),
closed-session (appears in that session's projection), and base-head-
mismatch (attributed to the valid submitting session).
5. Abortive close grounded: IngressEnvelope must carry session_id
attribution and HeadInbox needs a cancel-by-session_id API. Fallback
scope call documented if the extension is too invasive.
6. system/genesis carries a concrete PrincipalRef::system("genesis")-
shaped identity. No null principal anywhere, even at the bottom
turtle.
7. EINT target field is an IngressAddress-shaped protocol value mapped
to warp-core::head_inbox::IngressTarget at decode. Codec does not
smuggle the Rust enum directly.
8. Quiescence registration rule: bounded child work must be registered
before observing parent quiescence; after IntentEffectsQuiesced,
no new bounded child may be registered for that intent. Makes
quiesced a one-way gate per intent. Added as core invariant #10
and to the test list.
Implementation outline expanded to 11 steps (added the HeadInbox
attribution extension and the quiescence gate to runUntilIdle).
Phase 1 STOP holds: presenting the cleaned design for Phase 2 RED green-
light.
Three pre-RED clarifications binding on Phase 2 tests:
1. Abortive close v1: commit to weaker variant (B), not the full
HeadInbox surgery. IngressEnvelope content-addressing is
determinism-critical; bundling Session introduction with
IngressEnvelope.session_id and HeadInbox cancel-by-session_id in
one cycle multiplies risk inside a strict-determinism engine.
V1 abortive close blocks new submissions and cancels interruptible
accepted work; pending-ingress cancellation is deferred to a
follow-up cycle. Tests assert the deferral explicitly so the
scope call is honest, not silent.
2. Wire/runtime naming discipline pinned for RED: IngressAddress is
the wire/EINT type; IngressTarget is the warp-core runtime type;
the decode boundary maps one to the other. Tests for the wire
surface assert on IngressAddress; tests for admission assert on
IngressTarget; neither asserts the wire serializes IngressTarget
directly.
3. IntentRejected gains a Decode stage and a MissingSession reason,
distinct from UnknownSession. Missing means "header absent";
unknown means "header present but does not resolve." MissingSession
has no SessionEventLog projection to land in; UnknownSession
likewise. ClosedSession appears in that closed session's
projection. BaseHeadMismatch is attributed to the valid submitting
session at HeadInboxAdmission.
Phase 1 STOP holds; awaiting Phase 2 RED scope confirmation.
Doc-only commit closing out Phase 1 design work cleanly. Captures the locked design decisions, the five pre-RED decisions that Phase 2 must resolve before writing tests, the full RED test matrix expressed as invariants (not tests), and an explicit reminder that no Rust tests, stubs, modules, or API surface have been introduced in this cycle. Path 1 chosen: stop after Phase 1, hand off Phase 2 explicitly, do not let RED start implicitly. RED tests against nonexistent warp-core APIs effectively create the public shape of the implementation; that crosses the design-only boundary scoped for this conversation. Phase 2 (RED + GREEN) begins only with explicit implementation greenlight.
Doc-only fixes after a strict review found contradictions and stale
language. No design changes — just bringing the doc into sync with the
decisions already locked across the cycle's prior commits.
request.md:
- Drop rejected v1 model language (Session "ingress mailbox" and
"accepted causal lane" — both rejected in 8a21f04).
- Drop Intent.target_worldlines: NonEmptyList typed acceptance criterion
(replaced with single IngressTarget in a53cac9).
- Rename "writer reference" / "writer identity" to "principal reference"
/ "principal identity" per 34e4984.
- Rewrite echo-session-proto-split non-goal as "retired entirely"
rather than "coordinated" (retirement landed in 8a21f04).
- Expand acceptance criteria to cover the IntentRejected stage/reason
surface, the one-way quiescence gate, the abortive-close deferral,
the system/genesis primordial bootstrap, the IngressAddress wire
boundary, and the no-null-session/principal invariant.
design.md:
- Close-section bullet list (was: "Cancel / reject pending ingress"
listed as a v1 feature with parenthetical link to the deferral
subsection) rewritten to match the deferral: pending-ingress
cancellation is out of v1.
- Non-goal language for atomic multi-target submissions rewritten;
no more "type admits plural; v1 enforces singleton" since the design
body now uses a single IngressTarget (a53cac9). Future cycle picks
both shape and semantics together.
- Correlation closing list (session position in SessionEventLog) and
test bullet for IntentSubmitted/IntentAccepted ordering both updated
to engine event log deterministic order — matches the Ordering
subsection. The two had silently disagreed.
- Session shape code-block alignment fixed after writer_ref →
principal_ref rename (visual columns no longer drifted).
- ADR-0008 reference hedged to "per the head_inbox.rs module comment"
since the ADR file itself wasn't verified during design.
graveyard/PLATFORM_echo-session-proto-split.md:
- Replace curly quotes with straight in the preserved original prose
for consistency with surrounding documents.
phase-2-handoff.md surveyed under the same audit; remaining
echo-session-proto-split references in it are correct historical
pointers to the graveyard location and do not need changes.
|
Warning Review limit reached
More reviews will be available in 58 minutes. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (8)
📝 WalkthroughWalkthroughThis PR implements the foundational LE binary codec infrastructure (scalar/container wire support, canonicalized f32 handling, option/list encoding) and switches the generator from Canonical CBOR to LE binary format. It establishes cross-boundary codec test fixtures asserting exact byte encodings, and introduces comprehensive design documentation for sessions as causal contexts with phase-2 readiness guidance. ChangesLE Binary Codec Implementation & Generator Conversion
Cross-Boundary Test Fixtures & Generator Integration Tests
Design Documentation: LE Codec & Sessions Framework
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 499ad99133
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Self-Code-Review (Code Rabbit, second pass) — @codex please confirmSecond pedantic pass over the full PR diff vs Issue count
Findings
Design-packet consistency auditRan the stale-term audit from the previous review ( Cross-references between VerdictEcho PR is in good shape. Phase 1 STOP boundary holds — no Rust code, no Wesley IR edits, no API surface changes in the 0025 design packet. The 0024 stack rides this PR per maintainer direction. @codex — please confirm or push back on any of the above before this lands. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 499ad99133
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Two security/determinism fixes in echo-wasm-abi codec, both flagged by Codex P2 on PR #382. codec.rs read_f32_le: Previously returned the raw decoded f32 bit pattern. The writer canonicalizes (NaN -> 0x7fc00000, subnormals -> +0.0, -0.0 -> +0.0) but the reader did not, so an untrusted EINT or query-var payload could submit a non-canonical NaN/subnormal/-0.0 and have it land in generated vars verbatim. Two distinct byte strings then represent the same intended value, breaking the deterministic-codec contract. Wrapped the return in canonicalize_f32 (idempotent on already-canonical inputs; honest senders see no behavior change). codec.rs read_list: Previously did Vec::with_capacity(count) before reading any element bytes. A malformed payload with u32 count = 0xFFFFFFFF followed by zero elements forced a multi-gigabyte pre-allocation (DoS or abort) before validation could run. Now caps initial_capacity at min(count, remaining_bytes_in_buf). Since any list element occupies at least one byte (e.g. a u8 tag), an honest count cannot exceed the byte budget; capping at the budget is looser than any real workload but still defeats the DoS amplification. Verified: cargo test -p echo-wasm-abi green (2/2 lib tests). Resolves PR threads (codex P2) #382 crates/echo-wasm-abi/src/codec.rs:275 #382 crates/echo-wasm-abi/src/codec.rs:305
…qualification
Five P2 codex findings on echo-wesley-gen + supporting reader helper in
echo-wasm-abi.
codec.rs: added pub Reader::read_byte_array<const N: usize>() so the
generator can emit fixed-size array reads for no_std ID fields without
duplicating take/try_into logic per generated crate.
main.rs encode_arg_stmt / encode_field_stmt:
- Nullable list branch was emitting w.write_list(&self.field, ...)
against an Option<Vec<_>> field. Now wraps in write_option when
not required.
- no_std mode maps GraphQL ID to [u8; 32], but emitters always
treated ID like String. Added 'ID' if args.no_std arms in both
encode_arg_stmt and encode_field_stmt that emit write_bytes
(required) or write_option + write_bytes (nullable).
main.rs decode_arg_expr / decode_field_expr:
- Nullable list now wraps with read_option + read_list.
- no_std ID arms emit r.read_byte_array::<32>() (required) or
r.read_option(|r| r.read_byte_array::<32>()) (nullable).
main.rs scalar_list_element_decoder:
- Took a super_qualified: bool parameter. Arg-context callers
pass true (Vars Decode impls live in __echo_wesley_generated;
user types need super::). Field-context callers pass false.
Previously user-defined list element types under Vars compiled
with bare 'Tag::decode' which the compiler could not resolve.
Resolves PR threads (codex P2)
#382 crates/echo-wesley-gen/src/main.rs:1804
#382 crates/echo-wesley-gen/src/main.rs:1811
#382 crates/echo-wesley-gen/src/main.rs:1882
#382 crates/echo-wesley-gen/src/main.rs:1979
Phase III — Code Lawyer Activity Summary (@codex)All 7 unresolved review threads on this PR are addressed. Two commits added on top of the existing stack. Consolidated table
Verification
StatusAll findings resolved. Phase 1 STOP boundary on 0025 design packet still holds — no design content was changed in Phase III; only the 0024-stack Rust code that codex flagged was touched. @codex — please confirm the codec hardening, the |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8ef2fc9731
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Actionable comments posted: 10
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@crates/echo-wasm-abi/src/codec.rs`:
- Around line 57-64: Add a unit test that asserts decode_from_bytes returns
Err(CodecError::Trailing) when a valid encoded payload is followed by an extra
byte: create a valid encoded byte vector for a simple Decode type (e.g., a
primitive or a small struct used elsewhere), append one extra byte, call
decode_from_bytes::<T>(&bytes) and assert it yields Err(CodecError::Trailing);
place the test in this module's #[cfg(test)] tests so the behavior is locked in.
In `@crates/echo-wasm-abi/tests/jedit_rope_cross_boundary.rs`:
- Around line 299-372: The tests currently only roundtrip via the Rust
encoder/decoder (replace_range_as_tick_vars / decode_replace_range_as_tick_input
and encode_create_checkpoint_vars / decode_create_checkpoint_input), which won't
catch drift versus the TypeScript wire contract; replace or augment these tests
by pinning fixed expected byte vectors (literal u8 arrays) for
replace_range_as_tick and create_checkpoint ops (like the existing
create_buffer_worldline test does), assert that
encode_replace_range_as_tick_vars(&input) and
encode_create_checkpoint_vars(&input) produce exactly those literals, and also
assert that decode_* still parses those literals back to the original structs so
both encoder and decoder are validated against a stable wire-format golden
vector.
In `@crates/echo-wesley-gen/src/main.rs`:
- Around line 179-194: The helper fnv1a_step and the stable_op_id implementation
currently perform FNV-1 (multiply then xor) while the comment and name claim
FNV-1a; update the code to make the contract explicit and consistent: either
rename fnv1a_step (and update the doc comment on stable_op_id) to reflect FNV-1,
or change fnv1a_step to implement true FNV-1a (xor the byte into hash first,
then multiply) and update any pinned test vectors accordingly; ensure you modify
the symbol fnv1a_step and the doc comment on stable_op_id so the function name,
implementation, and comment all match the chosen algorithm.
- Around line 159-160: The IR's codec_id must be normalized to the canonical
value before any artifact preimage or hashes are computed: update the generator
to set ir.codec_id (currently set at codec_id: Some("le-binary-v1".to_string()))
immediately after parsing/constructing the IR so that
generated_rust_artifact_hash(), observer_identity_hashes(), and
op_footprint_certificate() all see the normalized codec (not the raw JSON input
like "cbor-canon-v1"); in practice, normalize or overwrite the incoming codec_id
to the canonical "le-binary-v1" right after IR construction and before any calls
to generated_rust_artifact_hash(), observer_identity_hashes(), or
op_footprint_certificate() so hashes and certificates are computed consistently.
- Around line 2011-2048: The scalar list element helpers treat "ID" like String
but map_type/map_helper_type map no-std ID to [u8; 32], causing mismatched
codegen; update scalar_list_element_encoder and scalar_list_element_decoder to
branch on args (Args) for "ID" so that in no-std mode they emit
encoders/decoders for a fixed 32-byte array instead of String/str (use the same
no-std predicate used elsewhere), while leaving the String/ID-as-String behavior
for std mode; reference scalar_list_element_encoder,
scalar_list_element_decoder, the type_name "ID", the Args parameter and
safe_ident to locate where to change the match arms and ensure encoders
write/read fixed 32 bytes and decoders produce/consume [u8; 32] consistent with
map_type/map_helper_type.
- Around line 307-321: Add a module-level import so the generated modules can
resolve the Encode/Decode trait methods: in the top of the main generated module
and inside the __echo_wesley_generated module, bring
echo_wasm_abi::codec::Encode and echo_wasm_abi::codec::Decode into scope (import
them as underscore aliases) so impls like Encode for `#name` and nested calls like
v.encode(w) / v.decode(r) compile; update the module headers where the generated
impls (impl echo_wasm_abi::codec::Encode for `#name` and impl
echo_wasm_abi::codec::Decode for `#name`) are emitted to include this import.
In `@crates/echo-wesley-gen/tests/generation.rs`:
- Line 1408: The test is asserting pub const CODEC_ID == "le-binary-v1" but the
inline IR fixture still declares "cbor-canon-v1", so update the inline IR in
this test (and other inline IR snippets in generation.rs) to set codec_id =
"le-binary-v1" to match the expected wire contract, or alternatively make the
test fail on stale ids by asserting equality from the parsed IR rather than
accepting mismatches; locate the failing assertion assert!(stdout.contains("pub
const CODEC_ID: &str = \"le-binary-v1\"")) and the surrounding inline IR
string(s) in generation.rs and replace any "cbor-canon-v1" occurrences with
"le-binary-v1".
In `@docs/design/0024-universal-le-binary-codec/design.md`:
- Around line 84-86: Update the doc to remove or correct the claim that enums
are append-only and "Adding a new variant at the end is safe": state that enum
changes (including appending variants) are breaking for mixed-version peers
because the framing explicitly gates payloads by SCHEMA_SHA256, so any schema
change will cause rejection; replace the "Enum discriminant" paragraph with text
that explains enum discriminants are deterministic and ordered by SDL
declaration order but that all schema edits (reorder, insert, append) change
SCHEMA_SHA256 and are therefore incompatible with peers using a different schema
version, and reference SCHEMA_SHA256 and the framing/version-gate behavior to
make the incompatibility precise.
In `@docs/method/backlog/bad-code/DOCS_no-published-umbrella-for-warp-optics.md`:
- Around line 118-119: The document uses two variants for the same project name
("WARP DRIVE" vs "WARPDrive"); pick one canonical form (e.g., "WARPDrive") and
replace the mismatched occurrence at the diff fragment ("Before WARP DRIVE goes
from vapor to v0.0.1 — the umbrella story...") so it matches the canonical name,
and scan the rest of the document to normalise all instances to that single
canonical term.
In `@docs/method/backlog/cool-ideas/PLATFORM_wesley-emitted-fixture-vectors.md`:
- Around line 39-40: The markdown list contains a stale repo-root path
`echo/crates/echo-wasm-abi/tests/jedit_rope_cross_boundary.rs`; update that
entry to use the correct repo-root style (e.g.,
`crates/echo-wasm-abi/tests/jedit_rope_cross_boundary.rs`) so cross-references
like the test file name `jedit_rope_cross_boundary.rs` resolve consistently with
other paths in the document.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: f78542fa-09eb-4c07-a93f-4ef7c7ccd0ae
📒 Files selected for processing (16)
crates/echo-wasm-abi/src/codec.rscrates/echo-wasm-abi/tests/jedit_rope_cross_boundary.rscrates/echo-wasm-abi/tests/jedit_rope_cross_boundary_eint.rscrates/echo-wesley-gen/src/main.rscrates/echo-wesley-gen/tests/fixtures/toy-counter/echo-ir-v1.jsoncrates/echo-wesley-gen/tests/generation.rsdocs/design/0024-universal-le-binary-codec/design.mddocs/design/0025-sessions-as-causal-contexts/design.mddocs/design/0025-sessions-as-causal-contexts/phase-2-handoff.mddocs/design/0025-sessions-as-causal-contexts/request.mddocs/method/backlog/bad-code/DOCS_no-published-umbrella-for-warp-optics.mddocs/method/backlog/bad-code/PLATFORM_echo-wesley-gen-monolith.mddocs/method/backlog/bad-code/PLATFORM_warp-wasm-cbor-debt.mddocs/method/backlog/cool-ideas/PLATFORM_schema-version-fingerprint-prefix.mddocs/method/backlog/cool-ideas/PLATFORM_wesley-emitted-fixture-vectors.mddocs/method/graveyard/PLATFORM_echo-session-proto-split.md
…c, docs) Code Lawyer pass on the 11 unresolved review threads on PR #382. wesley-gen list element no_std ID handling (codex P2 / coderabbit Critical): 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 -> Vec<[u8; 32]> (decode), neither of which compile. Both helpers now thread args through, gate on id_is_bytes = args.no_std && type_name == "ID", and emit write_bytes / read_byte_array::<32> arms matching the scalar-helper fix from 8ef2fc9. New consumer-crate compile regression test pins this in test_no_std_id_list_field_compiles_in_consumer_crate. wesley-gen codec trait imports inside generated modules (coderabbit Critical): Generated Encode/Decode impls call .encode(w) / Type::decode(r) on nested user types via method/associated-fn syntax. Bringing echo_wasm_abi::codec::{Encode, Decode} into scope at the top of the generated module AND inside __echo_wesley_generated removes a trait-resolution dependency on the implementing impl block at every call site. wesley-gen ir.codec_id normalization (coderabbit Major): Generator advertised CODEC_ID = "le-binary-v1" but artifact hash, observer identity, and footprint certificate preimages read ir.codec_id directly, so an IR declaring "cbor-canon-v1" produced an artifact whose hash was derived under the old codec while claiming the new one. ir.codec_id is now normalized to DEFAULT_CODEC_ID immediately after parsing. wesley-gen test fixtures: cbor-canon-v1 -> le-binary-v1 (coderabbit Major): All eight inline IR snippets in tests/generation.rs updated so the fixtures prove the new wire contract rather than relying on silent metadata rewriting. wesley-gen fnv1_step rename (coderabbit Minor): fnv1a_step multiplies before xor (FNV-1) while name/doc claimed FNV-1a. Renamed helper, updated doc to call out the misnomer, noted that stable_op_id_pinned vectors lock the contract to this FNV-1 ordering. No behavioral change. wasm-abi pinned wire vectors for rope ops (coderabbit Major): replace_range_as_tick and create_checkpoint only had local roundtrips; encoder/decoder drift could co-mutate silently. Added four pinned literal-byte fixtures covering minimal + with-author for replace_range_as_tick and manual-save-with-label + auto-save- no-label for create_checkpoint. Each both encodes -> matches the literal AND decodes the literal back to the original struct. wasm-abi CodecError::Trailing regression tests (coderabbit Trivial): Locked the public decode_from_bytes contract with three tests using a tiny OneInt(i32) Decode shape: exact-payload-accepted, one- trailing-byte-rejected, many-trailing-bytes-rejected. Doc threads: - 0024-universal-le-binary-codec/design.md: removed the "Adding a new variant at the end is safe" enum-compatibility claim; framed declaration-order rule as a same-version determinism guarantee consistent with the SCHEMA_SHA256 hard-gate. - DOCS_no-published-umbrella-for-warp-optics.md: canonicalized "WARP DRIVE" -> "WARPDrive". - PLATFORM_wesley-emitted-fixture-vectors.md: fixed stale repo-root path locator. Backlog (cool ideas): - PLATFORM_wesley-gen-test-loop-speedup.md: documents the iteration- cost sources observed during this review pass (60-120s per integration test, 4m53s pre-commit) and proposes shared target- dir for generated consumer crates, pre-built wesley-gen binary in tests, and crate-scoped pre-commit cargo check. Verified: echo-wasm-abi --lib 80/80 (incl. new Trailing tests), echo-wasm-abi --test jedit_rope_cross_boundary 16/16 (incl. four new pinned-byte tests), echo-wesley-gen --bins 1/1 (stable_op_id_pinned), and five targeted wesley-gen integration tests pass. Resolves PR #382 review threads: #382
Summary
Lands two stacked workstreams as one PR (per maintainer instruction):
77bcae6→bb257dd) — the in-flight stack that was previouslystack/echo-le-binary-codec. Adds Wesley-generated LE binary encode/decode for the rope codec across Rust and TypeScript, EINT envelope work, cross-boundary fixture proofs, and PLATFORM backlog captures.5a73a3f→499ad99) — Phase 1 design only. Promotes Session to a first-class durable causal-context node integrating with existingwarp-coreprimitives (playback::SessionId,head_inbox.rs,optic_artifact::PrincipalRef) rather than replacing them. Includes the design packet (design.md,request.md,phase-2-handoff.md), retiresPLATFORM_echo-session-proto-split.mdto graveyard, and stops cleanly at the METHOD Phase 1 STOP boundary. No Rust code, no API surface changes, no Wesley IR edits. Phase 2 RED + GREEN begin only with explicit implementation greenlight per the handoff note.The 0025 design responds to a structural smell that surfaced in the jedit Slice B EINT cutover (jedit PR #33): the wire was carrying jedit-internal session state because Echo had no name for the thing. Rather than codifying the client-side workaround, this cycle's design proposes the engine-side concept that makes the workaround scaffold-deletable.
Key locked design decisions (binding on Phase 2)
HeadInbox/IngressTargetand worldline head/tick remain authoritative).playback::SessionIdis unified with this cycle'sSessionId— two live concepts forbidden.IntentReceiptIssued,IntentEffectsQuiesced) replace the ambiguousIntentSettled;IntentEffectsQuiescedis a one-way gate per intent.IntentRejectedrecordsstage(Decode/SessionGate/HeadInboxAdmission/Execution) andreason;MissingSessionandUnknownSessionare distinct.system/genesisis primordial with a concretePrincipalRef::system("genesis")-shaped identity. No null session, no null principal anywhere.IngressTarget; atomic multi-target deferred. V1 abortive close is the weaker variant — pending-ingress cancellation inHeadInboxis deferred to a follow-up cycle.targetis anIngressAddress-shaped protocol value mapped to runtimeIngressTargetat decode (no Rust enum smuggling).Test plan
phase-2-handoff.mdfor the Phase 2 agenttarget_worldlinesplural,writer_ref,echo-session-proto-splitcoordination)playback::SessionIdunification scope is acceptable for Phase 2 implementationsystem/genesisbootstrap shape (genesis-time creation, not via intent) before Phase 2 startswesley-core = "0.0.4"pin reconciliation path before Phase 2 (Session as Wesley-emitted node type)Companion: jedit PR #33 (Slice A+B EINT cutover with first-class session port + the architectural follow-up from this design review).
Summary by CodeRabbit
Release Notes
New Features
Tests
Documentation