Skip to content

Decouple Message Encoding from Bincode: Adapters, Context & SerdeBridge#475

Merged
leynos merged 12 commits intomainfrom
decouple-message-encoding-bincode-cpjk3l
Feb 23, 2026
Merged

Decouple Message Encoding from Bincode: Adapters, Context & SerdeBridge#475
leynos merged 12 commits intomainfrom
decouple-message-encoding-bincode-cpjk3l

Conversation

@leynos
Copy link
Copy Markdown
Owner

@leynos leynos commented Feb 21, 2026

Summary

  • Decouple message encoding from bincode-specific traits by introducing serializer-agnostic adapters, explicit deserialization context, and an optional Serde bridge behind a feature gate. This preserves existing bincode paths while enabling serializer-agnostic flows and groundwork for version negotiation.

Changes

Core functionality

  • Introduce serializer-agnostic adapters to encode/decode messages independently of bincode trait bounds.
  • Add a DeserializeContext object to carry frame metadata into deserializers for version negotiation and related features.
  • Refactor serializer integration to use adapter traits, enabling a serializer-agnostic boundary while preserving current bincode paths via compatibility shims.
  • Ensure existing bincode-based routes continue to compile and run with zero or minimal changes for users who rely on the default path.

API surface

  • New adapter traits and context types (e.g., EncodeWith, DecodeWith, DeserializeContext) to replace direct bincode-bound message traits in serializers.
  • Public re-exports and small compatibility shims to keep current call sites working while enabling the new surface.
  • Optional Serde bridge behind a feature gate to reduce boilerplate for serializer integration without adding mandatory dependencies.

Documentation & ADRs

  • Update docs/adr-005-serializer-abstraction.md to document the chosen serializer-adapter design and trade-offs.
  • Update docs/users-guide.md with migration guidance for existing bincode users and explain the new adapter surface.
  • Align docs/message-versioning.md to reflect the new deserialization context semantics.
  • Update docs/roadmap.md to reflect 9.5.1 status and acceptance criteria after validation.
  • Add docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md with the execution plan.

Tests

  • Extend unit tests to cover adapter surface paths and compatibility shims with existing bincode types.
  • Extend metadata tests to exercise the deserialization context into decode paths.
  • Prepare rstest-bdd behavioural tests to exercise serializer boundary scenarios when the optional Serde bridge is enabled.
  • Ensure tests compile with existing setup and may be run as part of --all-features builds where applicable.

ExecPlan & Validation

  • Add docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md as the execution plan for this work.
  • Update docs/roadmap.md to reflect completed status for 9.5.1 upon gate validations.

References

  • docs/adr-005-serializer-abstraction.md
  • docs/users-guide.md
  • docs/message-versioning.md
  • docs/roadmap.md
  • docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md

Validation & acceptance criteria

  • Public APIs no longer require direct bincode traits at serializer boundaries; adapter traits are used instead.
  • Existing bincode users retain a clear migration path and compatibility behavior through shims.
  • Optional Serde bridge is available behind the feature flag and covered by tests.
  • Frame metadata is exposed to deserialization through a documented, testable context contract that supports version negotiation.
  • Unit tests use rstest; behavioural tests use rstest-bdd v0.5.0.
  • Docs ADR/Users Guide/ Roadmap reflect the changes and status; 9.5.1 is marked done after gate validations.

Validation plan (commands)

  • Baseline: cargo check --all-targets --all-features
  • Stage B/C: cargo test message --all-features; cargo test metadata --all-features; make test-bdd
  • Final: make fmt; make check-fmt; make markdownlint; make lint; make test-doc; make test

Risks & mitigations

  • Complexity of new adapter trait bounds could impact ergonomics; mitigation: provide helper aliases and migration notes.
  • Potential perf impact if metadata context introduces allocations; mitigations: keep context as borrowed where possible and verify in tests.
  • Bridge feature drift; mitigations: feature-gate tests and targeted unit tests.

Artifacts & notes

  • Documentation updates will be kept alongside code changes (docs/adr-005*, docs/users-guide.md, docs/roadmap.md).
  • ExecPlan doc documenting the approach is added under docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md.

Task

Summary by Sourcery

Introduce serializer-agnostic message encoding/decoding adapters and deserialization context while preserving existing bincode flows and adding an optional Serde bridge.

New Features:

  • Add EncodeWith and DecodeWith adapter traits plus DeserializeContext to decouple message APIs from bincode-specific traits.
  • Expose an optional Serde-based serializer bridge behind the serializer-serde feature, including SerdeMessage and helper traits for ergonomic integration.

Enhancements:

  • Refine the Serializer trait to support context-aware deserialization and use adapter-based bounds, keeping BincodeSerializer as the default implementation via compatibility shims.
  • Relax Packet trait bounds to remove direct Message requirements and update client/server/runtime APIs to depend on the new adapter traits instead.
  • Enhance metadata handling so parsed frame metadata is forwarded into deserialization, enabling version-aware decode paths without changing existing framing behaviour.

Build:

  • Enable bincode's serde feature and add a serializer-serde crate feature to gate the optional Serde bridge implementation.

Documentation:

  • Update ADR-005 and the user guide to document serializer adapters, deserialization context semantics, Serde bridge usage, and migration paths for existing bincode users.
  • Mark roadmap item 9.5.1 as completed and add an execution plan document describing stages, risks, and validation steps for decoupling message encoding from bincode.

Tests:

  • Extend unit tests and add rstest-bdd scenarios to cover adapter-based round trips, metadata context propagation, and Serde bridge behaviour under the new serializer boundaries.

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Feb 21, 2026

Reviewer's Guide

Introduce serializer-agnostic message adapters and an explicit deserialization context, refactor all serializer/client/server boundaries to use them while keeping bincode compatibility via shims, and add an optional Serde bridge plus tests and docs to validate and document the new design.

Sequence diagram for inbound frame parsing and context-aware deserialization

sequenceDiagram

    actor Client
    participant Network
    participant ServerConnection as Server_connection
    participant Serializer as Serializer_S
    participant Envelope

    Client->>Network: send frame bytes
    Network->>ServerConnection: incoming frame bytes

    ServerConnection->>Serializer: parse(payload)
    alt metadata parse succeeds
        Serializer-->>ServerConnection: (parsed_envelope, metadata_bytes_consumed)
        ServerConnection->>ServerConnection: build DeserializeContext
        ServerConnection->>Serializer: deserialize_with_context~Envelope~(payload, context)
        alt context-aware decode succeeds
            Serializer-->>ServerConnection: (decoded_envelope, consumed)
            ServerConnection->>ServerConnection: use decoded_envelope
        else context-aware decode fails
            ServerConnection->>ServerConnection: fall back to parsed_envelope
        end
    else metadata parse fails
        ServerConnection->>Serializer: deserialize_with_context~Envelope~(payload, empty_context)
        Serializer-->>ServerConnection: (decoded_envelope, consumed)
    end

    ServerConnection-->>Client: application handler processes Envelope
Loading

Sequence diagram for Serde bridge encode/decode via adapters

sequenceDiagram

    actor App as Application_code
    participant Ser as BincodeSerializer
    participant SM as SerdeMessage_T
    participant Serde as SerdeSerializerBridge_impl

    App->>App: payload: T implements Serialize + DeserializeOwned
    App->>SM: into_serde_message(payload)
    SM-->>App: serde_message

    App->>Ser: serialize(serde_message)
    activate Ser
    Ser->>SM: encode_with(self)
    activate SM
    SM->>Serde: serialize_serde(self.as_ref())
    Serde-->>SM: Vec<u8>
    SM-->>Ser: Vec<u8>
    deactivate SM
    Ser-->>App: Vec<u8> bytes
    deactivate Ser

    App->>Ser: deserialize~SerdeMessage~T~~(bytes)
    activate Ser
    Ser->>SM: decode_with(self, bytes, empty_context)
    activate SM
    SM->>Serde: deserialize_serde(bytes, context)
    Serde-->>SM: (T, consumed)
    SM-->>Ser: (SerdeMessage~T~, consumed)
    deactivate SM
    Ser-->>App: (SerdeMessage~T~, consumed)
    deactivate Ser

    App->>SM: into_inner()
    SM-->>App: T value
Loading

Class diagram for serializer adapters, context, and Serde bridge

classDiagram
    direction LR

    class DeserializeContext~'a~ {
        +Option~&'a u8[]~ frame_metadata
        +Option~u32~ message_id
        +Option~u64~ correlation_id
        +Option~usize~ metadata_bytes_consumed
        +empty() DeserializeContext
    }

    class EncodeWith~S~ {
        <<interface>>
        +encode_with(serializer S) Result~Vec~u8~~
    }

    class DecodeWith~S~ {
        <<interface>>
        +decode_with(serializer S, bytes u8[], context DeserializeContext) Result~(Self, usize)~
    }

    class Message {
        <<interface>>
        +to_bytes() Result~Vec~u8~~
        +from_bytes(bytes u8[]) Result~Self~
    }

    class Serializer {
        <<interface>>
        +serialize~M~(value M) Result~Vec~u8~~
        +deserialize~M~(bytes u8[]) Result~(M, usize)~
        +deserialize_with_context~M~(bytes u8[], context DeserializeContext) Result~(M, usize)~
    }

    class BincodeSerializer {
        +serialize~M~(value M) Result~Vec~u8~~
        +deserialize~M~(bytes u8[]) Result~(M, usize)~
        +deserialize_with_context~M~(bytes u8[], context DeserializeContext) Result~(M, usize)~
    }

    class SerdeSerializerBridge {
        <<interface>>
        +serialize_serde~T~(value T) Result~Vec~u8~~
        +deserialize_serde~T~(bytes u8[], context DeserializeContext) Result~(T, usize)~
    }

    class SerdeMessage~T~ {
        -T value
        +new(value T) SerdeMessage~T~
        +as_ref() &T
        +into_inner() T
    }

    class IntoSerdeMessage {
        <<interface>>
        +into_serde_message() SerdeMessage~Self~
    }

    class Packet {
        <<interface>>
        +id() u32
        +correlation_id() Option~u64~
        +set_correlation_id(cid Option~u64~)
        +into_parts() PacketParts
        +from_parts(parts PacketParts) Self
    }

    class Envelope {
        +u32 id
        +Option~u64~ correlation_id
        +payload
    }

    %% Relationships
    Serializer <|.. BincodeSerializer

    %% Bincode compatibility: Message types get adapter impls
    Message <|.. EncodeWith
    Message <|.. DecodeWith

    %% BincodeSerializer uses adapters, not Message directly
    BincodeSerializer ..> EncodeWith : serialize
    BincodeSerializer ..> DecodeWith : deserialize*

    %% Context flows into DecodeWith and Serializer
    DeserializeContext ..> DecodeWith : passed_to
    Serializer ..> DeserializeContext : uses

    %% Serde bridge
    SerdeSerializerBridge <|.. BincodeSerializer
    EncodeWith <|.. SerdeMessage
    DecodeWith <|.. SerdeMessage
    IntoSerdeMessage <|.. SerdeMessage
    IntoSerdeMessage <|.. SerdeMessage~T~

    %% Packet no longer extends Message
    Packet ..> Envelope : wraps
    Packet ..> CorrelatableFrame

    class CorrelatableFrame {
        <<interface>>
        +correlation_id() Option~u64~
        +set_correlation_id(cid Option~u64~)
    }
Loading

File-Level Changes

Change Details Files
Add serializer-agnostic message adapter traits and deserialization context, with default bincode-backed implementations and an optional Serde bridge.
  • Introduce DeserializeContext carrying frame metadata into deserializers, including helper constructors and tests.
  • Define EncodeWith and DecodeWith traits as serializer-agnostic adapters, with blanket impls for existing Message types to preserve bincode behaviour.
  • Add optional serde_bridge module behind the serializer-serde feature with SerdeMessage wrapper, IntoSerdeMessage helper, and SerdeSerializerBridge integration for serializers like BincodeSerializer.
  • Extend message tests to cover adapter round-trips, empty context behaviour, and Serde bridge round-trips under the feature flag.
src/message.rs
Refactor the core Serializer trait and BincodeSerializer to use adapter traits and support context-aware deserialization, including a Serde bridge implementation.
  • Update Serializer trait to be generic over EncodeWith/DecodeWith bounds, add deserialize_with_context with a default fallback to deserialize, and ensure Sized where required.
  • Refactor BincodeSerializer to delegate to EncodeWith/DecodeWith and implement deserialize_with_context to accept DeserializeContext.
  • Add SerdeSerializerBridge trait and implement it for BincodeSerializer using bincode::serde encode/decode helpers under the serializer-serde feature.
  • Adjust Cargo.toml to enable bincode's serde feature and add a serializer-serde feature flag for the bridge.
src/serializer.rs
Cargo.toml
Plumb DeserializeContext through frame handling so parsed frame metadata is forwarded into deserializers and validated by tests and examples.
  • Update inbound_handler envelope parsing to build a DeserializeContext from FrameMetadata::parse results, passing it into Serializer::deserialize_with_context and falling back to parsed envelopes on success or default context on failure.
  • Adjust server connection spawning, accept loop, runtime, and codec driver generics to require Envelope types implement EncodeWith/DecodeWith for the active Serializer.
  • Update example HeaderSerializer and various test serializers (CountingSerializer, FallbackSerializer, ContextCapturingSerializer) to use the adapter traits and, where appropriate, override deserialize_with_context to assert or capture metadata.
  • Extend metadata tests and add new tests to assert that metadata is forwarded correctly into DeserializeContext, including counts of parse vs deserialize calls and captured context fields.
src/app/inbound_handler.rs
src/app/codec_driver.rs
src/server/connection_spawner.rs
src/server/runtime/accept.rs
src/server/runtime.rs
examples/metadata_routing.rs
tests/metadata.rs
Change client-facing messaging APIs to depend on adapter traits instead of the bincode-bound Message trait while keeping Packet semantics.
  • Update client runtime send/receive/call methods to use EncodeWith/DecodeWith bounds instead of Message, preserving behaviour while decoupling from bincode.
  • Update Client messaging envelope APIs (send_envelope, receive_envelope, call_correlated, call_streaming, receive_streaming) and ResponseStream generics so envelopes/packets implement EncodeWith/DecodeWith with the active Serializer.
  • Adjust test-only serializers such as FailingSerializer to implement the new trait-based Serializer interface.
  • Relax Packet trait to no longer require Message, keeping correlation and routing semantics unchanged while serialization is handled via adapters.
src/client/runtime.rs
src/client/messaging.rs
src/client/streaming.rs
src/client/response_stream.rs
src/client/tests/helpers.rs
src/app/envelope.rs
Expose the new adapter and context APIs in the prelude and update documentation (ADR, user guide, roadmap, execplan) plus BDD fixtures to cover serializer boundaries and metadata context behaviour.
  • Re-export EncodeWith, DecodeWith, DeserializeContext, and (behind feature) SerdeMessage/IntoSerdeMessage from the crate prelude for easier adoption.
  • Update users-guide vocabulary and add migration guidance from Message-only bounds to adapter-based bounds, including mention of the optional Serde bridge.
  • Mark ADR-005 as accepted, document the concrete adapter/context design and remaining decisions, and mark roadmap item 9.5.1 plus its sub-bullets as completed.
  • Add an execution plan document for 9.5.1 describing stages, risks, validations, and final interfaces.
  • Introduce new rstest-bdd feature, fixture, step, and scenario files for serializer_boundaries that validate legacy bincode round-trips through adapters and that metadata context is forwarded to deserializers.
src/prelude.rs
docs/users-guide.md
docs/adr-005-serializer-abstraction.md
docs/roadmap.md
docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md
tests/fixtures/mod.rs
tests/fixtures/serializer_boundaries.rs
tests/steps/mod.rs
tests/steps/serializer_boundaries_steps.rs
tests/scenarios/mod.rs
tests/scenarios/serializer_boundaries_scenarios.rs
tests/features/serializer_boundaries.feature

Possibly linked issues

  • Fix conditional syntax in CI workflow #29: The PR replaces the incorrect bincode::Decode-based bounds in server/serializer code with adapter traits, fixing the bincode 2.x compilation issue while adding broader serializer abstraction features.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 21, 2026

Summary by CodeRabbit

  • New Features

    • Serializer‑agnostic encode/decode traits and an optional Serde bridge (feature‑gated)
    • Frame metadata exposed to deserialisation context for version negotiation
    • Public prelude expanded with new helper types for serializer interoperability
  • Documentation

    • User guide, ADR and roadmap updated with migration guidance and accepted design
  • Tests

    • New BDD and unit tests validating serializer boundaries and context forwarding
  • Chores

    • Public test helpers and runtime testing utilities added; stricter compile‑time serializer bounds introduced impacting callers

Walkthrough

Introduce serializer-agnostic EncodeWith/DecodeWith traits and DeserializeContext, add an optional Serde bridge feature, update ADRs and docs, change many public API bounds from Message to EncodeWith/DecodeWith, add test fixtures and harness helpers; no runtime behavioural changes beyond trait-bound requirements.

Changes

Cohort / File(s) Summary
Design & Docs
docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md, docs/adr-005-serializer-abstraction.md, docs/roadmap.md, docs/users-guide.md
Add ExecPlan and migration guidance; mark ADR accepted; document adapter surface, DeserializeContext, staged rollout and Serde bridge feature.
Cargo / Features
Cargo.toml
Add serializer-serde feature and enable serde feature for bincode.
Core message API & Prelude
src/message.rs, src/prelude.rs
Add EncodeWith<S>, DecodeWith<S>, DeserializeContext; blanket adapters for legacy Message; feature-gated SerdeMessage/IntoSerdeMessage; export new items.
Serializer surface
src/serializer.rs
Generalise Serializer to use EncodeWith/DecodeWith, add deserialize_with_context and should_deserialize_after_parse; add MessageCompatibilitySerializer marker and feature-gated SerdeSerializerBridge; update BincodeSerializer.
API signature changes
src/app/..., src/client/..., src/server/...
Replace many Message bounds with EncodeWith<S> / DecodeWith<S>; add Envelope encode/decode bounds; remove Message supertrait from Packet.
Examples
examples/metadata_routing.rs
Switch example to use EncodeWith/DecodeWith; implement MessageCompatibilitySerializer for header serializer.
Tests & fixtures
tests/*, tests/fixtures/*, tests/features/*, tests/scenarios/*, tests/steps/*
Add BDD feature and scenarios for serializer boundaries; introduce fixtures, steps and scenario bindings; add ContextCapturingSerializer and metadata-forwarding tests.
Test harness & helpers
wireframe_testing/src/*, wireframe_testing/src/helpers/*, wireframe_testing/src/macros.rs
Broaden TestSerializer bound to require MessageCompatibilitySerializer; add codec/drive/payloads/runtime helper modules, utility macros (push_expect!, recv_expect!) and public drive/run helpers; expose helper tests.
Test error propagation
tests/*.rs
Convert many test helper calls to propagate errors (?) and alter some test signatures to return TestResult where required.

Sequence Diagram(s)

sequenceDiagram
    participant Frame as "Frame (LengthDelimited)"
    participant Parser as "Frame Parser"
    participant App as "App / Envelope"
    participant Serializer as "Serializer (Bincode / Bridge)"
    participant Decoder as "DecodeWith impl"

    Frame->>Parser: receive frame bytes
    Parser->>App: parse envelope and expose frame metadata
    alt serializer.should_deserialize_after_parse() == true
        App->>Serializer: deserialize_with_context(bytes, DeserializeContext{frame metadata,...})
        Serializer->>Decoder: Decoder::decode_with(serializer, bytes, context)
        Decoder-->>Serializer: (value, consumed)
        Serializer-->>App: deserialised value
    else
        App->>Serializer: deserialize(bytes)
        Serializer->>Decoder: Decoder::decode_with(serializer, bytes, DeserializeContext::empty())
        Decoder-->>Serializer: (value, consumed)
        Serializer-->>App: deserialised value
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

codex

Suggested reviewers

  • codescene-delta-analysis

Poem

Loosen bincode's tight embrace,
Traits march out to take their place,
Context travels with each frame,
Bridges idle, adapters tame,
Tests applaud the new interface.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title concisely summarises the primary change: decoupling message encoding from bincode via adapters, context, and a Serde bridge.
Description check ✅ Passed The description thoroughly details the motivation, changes, documentation updates, testing approach, and validation criteria related to the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch decouple-message-encoding-bincode-cpjk3l

Comment @coderabbitai help to get the list of available commands and usage tips.

@leynos leynos marked this pull request as ready for review February 22, 2026 12:35
Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 4 issues, and left some high level feedback:

  • The DeserializeContext example at the end hard-codes fields like message_id: Option<u32> and correlation_id: Option<u64>; consider aligning these types and names directly with FrameMetadata (or referencing that type) to avoid drift between the plan and the actual metadata model.
  • There is a lot of overlap between this ExecPlan and docs/adr-005-serializer-abstraction.md/docs/users-guide.md; you might want to clearly mark which document is the single source of truth for the adapter traits and bridge behaviour to reduce the risk of inconsistent guidance later.
  • Since this file is intended as a living plan, it may help to add a short note near Status: DRAFT explicitly describing how and when the Progress, Decision Log, and Outcomes & Retrospective sections should be updated (e.g., per stage or per PR) so that future edits follow a consistent process.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `DeserializeContext` example at the end hard-codes fields like `message_id: Option<u32>` and `correlation_id: Option<u64>`; consider aligning these types and names directly with `FrameMetadata` (or referencing that type) to avoid drift between the plan and the actual metadata model.
- There is a lot of overlap between this ExecPlan and `docs/adr-005-serializer-abstraction.md`/`docs/users-guide.md`; you might want to clearly mark which document is the single source of truth for the adapter traits and bridge behaviour to reduce the risk of inconsistent guidance later.
- Since this file is intended as a living plan, it may help to add a short note near `Status: DRAFT` explicitly describing how and when the `Progress`, `Decision Log`, and `Outcomes & Retrospective` sections should be updated (e.g., per stage or per PR) so that future edits follow a consistent process.

## Individual Comments

### Comment 1
<location> `docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md:3` </location>
<code_context>
+# 9.5.1 Decouple message encoding from bincode-specific traits
+
+This ExecPlan is a living document. The sections `Constraints`, `Tolerances`,
+`Risks`, `Progress`, `Surprises & Discoveries`, `Decision Log`, and
+`Outcomes & Retrospective` must be kept up to date as work proceeds.
</code_context>

<issue_to_address>
**issue (review_instructions):** ExecPlan appears as an acronym without being expanded on first use.

To comply with the guideline on defining uncommon acronyms, expand ExecPlan the first time it appears, for example “This Execution Plan (ExecPlan) is a living document.”

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*.md`

**Instructions:**
Define uncommon acronyms on first use.

</details>
</issue_to_address>

### Comment 2
<location> `docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md:26` </location>
<code_context>
+  serializer-agnostic adapter contract instead of direct `bincode` trait bounds.
+- Existing bincode message types continue to work with minimal or zero source
+  changes through compatibility shims.
+- A feature-gated Serde bridge is available as an optional path to reduce
+  manual adapter implementations.
+- Deserializers can inspect frame metadata through an explicit context object,
</code_context>

<issue_to_address>
**issue (review_instructions):** Serde is introduced as an acronym/name without being defined on first use.

The instructions require uncommon acronyms to be defined on first use. Consider expanding this to something like “a feature-gated Serde (serialization/deserialization) bridge” the first time it is mentioned.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*.md`

**Instructions:**
Define uncommon acronyms on first use.

</details>
</issue_to_address>

### Comment 3
<location> `docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md:91` </location>
<code_context>
+
+- Risk: behavioural tests may assert implementation details rather than user
+  observable outcomes. Severity: medium. Likelihood: medium. Mitigation: keep
+  BDD scenarios focused on round-trip compatibility, migration-visible
+  behaviour, and version negotiation outcomes.
+
</code_context>

<issue_to_address>
**issue (review_instructions):** BDD is used without being expanded on first use.

To align with the requirement to define uncommon acronyms on first use, please expand BDD here, for example “behaviour-driven development (BDD) scenarios”.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*.md`

**Instructions:**
Define uncommon acronyms on first use.

</details>
</issue_to_address>

### Comment 4
<location> `docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md:141` </location>
<code_context>
+
+## Decision Log
+
+- Decision: implement ADR-005 Option B (serializer-agnostic boundary plus
+  adapters) using compatibility-first layering. Rationale: satisfies roadmap
+  goals while minimizing disruption for current users. Date/Author: 2026-02-21
</code_context>

<issue_to_address>
**issue (review_instructions):** ADR is referenced in “ADR-005” without explaining the acronym on first use in prose.

Although the file name contains `adr-005`, the prose reference to “ADR-005 Option B” should still expand the acronym on first use, for example “Architecture Decision Record (ADR-005) Option B…”.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*.md`

**Instructions:**
Define uncommon acronyms on first use.

</details>
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md Outdated
Comment thread docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md Outdated
Comment thread docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md Outdated
Comment thread docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md`:
- Around line 423-449: The long trait/type signatures in the Markdown code block
exceed 120 columns; reformat the signatures for EncodeWith::encode_with,
DecodeWith::decode_with, and Serializer::{serialize, deserialize_with_context}
by breaking parameter and return types across lines to wrap at ≤120 columns
(e.g., place generic bounds and long return types on their own indented lines),
preserving the same types (Box<dyn std::error::Error + Send + Sync>,
DeserializeContext<'_>, S: Serializer, M: EncodeWith<Self>, M: DecodeWith<Self>,
Sized) and method names so the semantics are unchanged while keeping each line
under the 120‑column limit.
- Around line 39-40: The sentence mentioning WireframeApp and WireframeClient
needs a comma inserted before "so" to fix the compound sentence: update the line
that reads "Keep `WireframeApp` and `WireframeClient` defaults on
bincode-compatible behaviour so existing applications do not need immediate
serializer rewrites." to include a comma after "behaviour" so it reads
"...behaviour, so existing applications...".
- Around line 13-33: Reflow the prose for roadmap item `9.5.1` so every
paragraph and each bullet point is wrapped to a maximum of 80 columns; update
the long sentences describing the goals (preserve bincode flows, add
serializer-agnostic adapter surface, optional Serde bridge, frame metadata for
deserialization, testing and docs requirements) into shorter lines and rewrap
the bullet list entries to 80 columns per line while preserving the original
wording and list order.

Comment thread docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md
Comment thread docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md Outdated
Comment thread docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md`:
- Around line 417-450: Add the missing Rust language identifier to the fenced
code block (change the opening ``` to ```rust) and reflow the long function
signature for the EncodeWith trait's method encode_with so it doesn't exceed 120
columns; specifically, locate the pub trait EncodeWith<S: Serializer> and break
fn encode_with(&self, serializer: &S) -> Result<Vec<u8>, Box<dyn
std::error::Error + Send + Sync>>; across multiple lines (e.g., each parameter
and the return type on its own line) to keep lines <=120 chars while preserving
types and bounds.
- Line 16: Update the prose in
docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md to use British
English (en-GB-oxendict) spelling: change occurrences of "serializer" in
descriptive text to "serialiser" (but do not change code identifiers such as the
trait name `Serializer`), change "serialization/deserialization" to
"serialisation/deserialisation", and convert -ize/-yze suffixes to -ise/-yse
(e.g., "realize"→"realise", "analyze"→"analyse") across the noted lines
(including 16, 23, 27, 41, 152, 203, 224, 278, 328, 378); ensure only prose is
modified and leave filenames, feature flags, and code identifiers unchanged.
- Line 348: Replace the three fenced code blocks that contain the shell commands
with language-tagged fences: locate the blocks containing the exact command
lines "set -o pipefail && cargo check --all-targets --all-features 2>&1 | tee
/tmp/9-5-1-check-baseline.log", "set -o pipefail && cargo test message
--all-features 2>&1 | tee /tmp/9-5-1-message-tests.log", and "set -o pipefail &&
make fmt 2>&1 | tee /tmp/9-5-1-fmt.log" and change their opening fence from ```
to ```shell so each block is fenced as shell code (leave the closing fence
as-is).

---

Duplicate comments:
In `@docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md`:
- Around line 23-34: Reflow the bullet list in
docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md (the five bullets
describing adapter contract, compatibility shims, Serde bridge, deserializer
context, and testing/docs/roadmap) so every line in each bullet wraps at or
below 80 columns; edit the paragraph text for each bullet (not semantics) to
insert line breaks and preserve words and punctuation, ensuring no line exceeds
80 characters and the bullets remain in the same order and content.

Comment thread docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md Outdated
Comment thread docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md
Comment thread docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md Outdated
@leynos

This comment was marked as resolved.

@coderabbitai

This comment was marked as resolved.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 22, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read the current state of the ExecPlan file to verify if overall comments have been addressed
cat docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md

Length of output: 20618


@leynos, yes — all three overall comments have now been resolved! ✓

Status Summary

DeserializeContext alignment ✓ Resolved

The document now includes explicit alignment guidance before the code example (lines ~440-445):

DeserializeContext fields must be projections of the canonical metadata model returned by FrameMetadata::parse (FrameMetadata::Frame). If the metadata model changes, update this context shape in lockstep (or expose that model directly) to avoid drift.

Plus an inline comment within the struct definition referencing FrameMetadata::Frame.

Document overlap/source of truth ✓ Resolved

Lines 32-38 now define clear authority boundaries:

  • This ExecPlan is the implementation staging guide and task tracker
  • docs/adr-005-serializer-abstraction.md is the source of truth for serializer-boundary architecture and trade-offs
  • docs/users-guide.md is the source of truth for public API contracts and migration guidance

Status update process ✓ Resolved

Lines 9-11 now specify the maintenance cadence:

Maintenance cadence: update Progress immediately after each stage completes, record architecture and scope choices in Decision Log when they are made, and write Outcomes & Retrospective at Stage E completion.

All original review concerns have been addressed.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md`:
- Line 278: Replace the ambiguous phrase "`src/frame/metadata.rs` and/or
`src/serializer.rs`" with the clearer "`src/frame/metadata.rs` or
`src/serializer.rs`" in the document so it reads unambiguously; update that
exact phrase wherever it appears in the file
docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md (referencing the
snippet containing `src/frame/metadata.rs` and/or `src/serializer.rs`) to use
"or" instead of "and/or".
- Line 394: Update the sentence "Acceptance is complete only when all of the
following are true:" by removing the extraneous word "of" so it reads
"Acceptance is complete only when all the following are true:"; locate this
exact sentence in the document (the heading/paragraph containing "Acceptance is
complete only when all of the following are true:") and replace it accordingly
for conciseness.
- Around line 18-495: Change American English prose occurrences of "serializer",
"serialization" and "deserialization" to British English "serialiser",
"serialisation" and "deserialisation" throughout the document text (paragraphs,
bullets and inline prose) while explicitly preserving code identifiers and
literals (e.g. trait/struct names like Serializer, BincodeSerializer,
DeserializeContext, FrameMetadata::parse, FrameMetadata::Frame), file paths,
feature flags, and everything inside fenced code blocks; update the specific
prose instances flagged (e.g. "serializer-agnostic message adapter" →
"serialiser-agnostic message adapter", "Serde (serialization/deserialization)" →
"Serde (serialisation/deserialisation)", "immediate serializer rewrites" →
"immediate serialiser rewrites", and the other listed occurrences) and run a
quick pass to catch any other descriptive occurrences of those three words so
only natural-language text is altered.

Comment thread docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md
Comment thread docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md Outdated
Comment thread docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md Outdated
@leynos leynos marked this pull request as draft February 22, 2026 23:19
codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos leynos changed the title Decouple Message Encoding from Bincode: serializer adapters & metadata context Decouple Message Encoding from Bincode: Adapters & Context Feb 22, 2026
@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Feb 23, 2026

@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix:

tests/fixtures/serializer_boundaries.rs

Comment on file

    pub fn assert_captured_message_id(&self, expected: u32) -> TestResult {
        let captured = self
            .captured_context
            .lock()
            .ok()
            .and_then(|state| *state)
            .ok_or("captured context not available")?;
        if captured.message_id != Some(expected) {
            return Err(format!(
                "expected captured message id {:?}, got {:?}",
                Some(expected),
                captured.message_id
            )
            .into());
        }
        Ok(())
    }

❌ New issue: Code Duplication
The module contains 2 functions with similar structure: SerializerBoundariesWorld.assert_captured_correlation_id,SerializerBoundariesWorld.assert_captured_message_id

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Feb 23, 2026

@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix:

tests/metadata.rs

Comment on file

    let deserialize_calls = Arc::new(AtomicUsize::new(0));
    let serializer = CountingSerializer(counter.clone(), deserialize_calls.clone());

❌ New issue: Code Duplication
The module contains 5 functions with similar structure: Serializer.deserialize,Serializer.deserialize,Serializer.deserialize,falls_back_to_deserialize_after_parse_error and 1 more functions

@coderabbitai

This comment was marked as resolved.

leynos and others added 5 commits February 23, 2026 09:14
…ode traits

Introduce a comprehensive ExecPlan document for roadmap item 9.5.1 that outlines the strategy to remove bincode-specific trait coupling from message encoding boundaries. This plan details adapter trait design, compatibility layers, deserialization context with metadata exposure, optional Serde bridge, testing approaches, progress checkpoints, risks, decisions, and documentation updates to ensure a serializer-agnostic interface while preserving existing bincode workflows.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
…cument

- Expanded acronyms on first mention for clarity.
- Improved line breaks and punctuation for readability.
- Reworded several risk and decision log items for better comprehension.
- Updated terminology to avoid ambiguity and enhance professionalism.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
Applied Rust syntax highlighting to the code snippets in the
9-5-1-decouple-message-encoding-from-bincode.md document to improve
readability and clarity of the message encoding and decoding
interface definitions.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
…ity boundaries

Added guidance on maintenance cadence including updates to Progress, Decision Log, and Outcomes & Retrospective. Clarified authority boundaries for documentation artifacts, specifying roles of ExecPlan, ADR-005, and users-guide documents. Also included details on DeserializeContext fields alignment with metadata model to prevent drift.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
- Introduce serializer-agnostic encoding and decoding adapter traits `EncodeWith` and `DecodeWith` to replace the previous bincode-only `Message` trait dependency.
- Add `DeserializeContext` struct to pass frame metadata and context to deserializers supporting version negotiation.
- Enhance `Serializer` trait with `deserialize_with_context` method, defaulting to `deserialize` for backward compatibility.
- Update core components (app, client, server, serializer implementations) to use the new serializer-agnostic traits.
- Provide an optional Serde bridge behind the `serializer-serde` feature via `SerdeMessage`, `IntoSerdeMessage`, and `SerdeSerializerBridge` trait to reduce boilerplate for Serde serializers.
- Update documentation, ADR, execution plans, user guides, examples, and roadmap to reflect the new serializer abstraction and migration guidance.
- Introduce comprehensive tests, fixtures, and scenarios verifying legacy bincode compatibility, metadata context propagation, and serializer boundary behavior.

This change enables greater flexibility for supporting alternative serializers and version-aware message decoding without breaking existing bincode-compatible code.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
@devboxerhub devboxerhub Bot force-pushed the decouple-message-encoding-bincode-cpjk3l branch from 2398c1f to 925c373 Compare February 23, 2026 09:15
codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Feb 23, 2026

@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix:

Code Duplication

tests/fixtures/serializer_boundaries.rs:

What lead to degradation?

The module contains 2 functions with similar structure: SerializerBoundariesWorld.assert_captured_correlation_id,SerializerBoundariesWorld.assert_captured_message_id

Why does this problem occur?

Duplicated code often leads to code that's harder to change since the same logical change has to be done in multiple functions. More duplication gives lower code health.

How to fix it?

A certain degree of duplicated code might be acceptable. The problems start when it is the same behavior that is duplicated across the functions in the module, ie. a violation of the Don't Repeat Yourself (DRY) principle. DRY violations lead to code that is changed together in predictable patterns, which is both expensive and risky. DRY violations can be identified using CodeScene's X-Ray analysis to detect clusters of change coupled functions with high code similarity. Read More
Once you have identified the similarities across functions, look to extract and encapsulate the concept that varies into its own function(s). These shared abstractions can then be re-used, which minimizes the amount of duplication and simplifies change.

Refactored assertion methods in serializer_boundaries test fixture to use a
new generic helper method `assert_captured_field`. This removes repetitive
code for checking captured context fields like message_id and correlation_id,
improving code maintainability and clarity.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos leynos marked this pull request as ready for review February 23, 2026 09:50
sourcery-ai[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

… Message support

Introduce the marker trait `MessageCompatibilitySerializer` to explicitly opt-in serializers to legacy `Message` compatibility. This allows `T: Message` values to automatically satisfy `EncodeWith<Self>` and `DecodeWith<Self>`, enabling back-compatibility with existing `Message`-based encoding/decoding.

Update existing serializers and tests to implement this trait as appropriate. Adjust inbound handling logic to optimize deserialization based on serializers' capabilities.

Update documentation and examples to reflect this new explicit opt-in pattern and migration guidance.

This change paves the way for more robust and serializer-specific encoding/decoding boundaries, improving flexibility and future extensibility.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
codescene-delta-analysis[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as resolved.

@leynos

This comment was marked as resolved.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 23, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check Serializer trait for Self: Sized bounds and any documentation about object safety
echo "=== Checking src/serializer.rs for Self: Sized and documentation ==="
grep -n -A5 -B5 "Self: Sized" src/serializer.rs || echo "No Self: Sized found"
echo ""
echo "=== Checking for dyn Serializer or object safety documentation ==="
rg -n "dyn Serializer|object.safe|trait object" src/serializer.rs || echo "No object safety documentation found"

echo ""
echo "=== Checking for ContextCapturingSerializer in tests/metadata.rs ==="
rg -n "struct ContextCapturingSerializer" tests/metadata.rs

echo ""
echo "=== Checking for ContextCapturingSerializer in tests/fixtures/serializer_boundaries.rs ==="
rg -n "struct ContextCapturingSerializer" tests/fixtures/serializer_boundaries.rs

echo ""
echo "=== Checking adapter vs adaptor spelling in ADR-005 ==="
rg -n "adapter|adaptor" docs/adr-005-serializer-abstraction.md | head -30

echo ""
echo "=== Checking for SerdeMessage and blanket impls in src/message.rs ==="
rg -n "impl.*EncodeWith.*for T|struct SerdeMessage" src/message.rs

echo ""
echo "=== Reading BDD feature file ==="
cat tests/features/serializer_boundaries.feature

echo ""
echo "=== Reading BDD scenarios file ==="
cat tests/scenarios/serializer_boundaries_scenarios.rs

echo ""
echo "=== Checking 'your' pronoun in users-guide.md around line 390 ==="
sed -n '385,400p' docs/users-guide.md

Length of output: 5007


🏁 Script executed:

#!/bin/bash
# Read the blanket impl section and Serde bridge in src/message.rs
echo "=== Checking blanket impls in src/message.rs ==="
sed -n '110,180p' src/message.rs

echo ""
echo "=== Checking step implementations ==="
cat tests/steps/serializer_boundaries_steps.rs

Length of output: 4189


@leynos, I've checked all six items. Here's the current status:

✓ Resolved (3/6)

Individual Comment 1: adapter/adaptor spelling

Status: ✓ Resolved

The ADR now uses "adaptor" consistently throughout (lines 52, 59, 79, 80, 88, 97, 120, 121, 125, 145). No instances of "adapter" remain.

Individual Comment 3: BDD test coverage

Status: ✓ Resolved

The BDD tests are fully implemented:

  • tests/features/serializer_boundaries.feature defines two scenarios (legacy round-trip and metadata context forwarding)
  • tests/steps/serializer_boundaries_steps.rs implements all Given/When/Then steps
  • tests/scenarios/serializer_boundaries_scenarios.rs wires the scenarios to the test harness
  • Coverage includes both adapter traits and metadata-aware deserialization via DeserializeContext

Individual Comment 4: "your" pronoun

Status: ✓ Resolved

Line 391 of docs/users-guide.md now reads "If existing code previously relied on…" instead of "If your code previously relied on…"


✗ Not Resolved (3/6)

Overall Comment 1: Self: Sized object safety documentation

Status: ✗ Not resolved

The Serializer trait retains Self: Sized bounds on serialize, deserialize, and deserialize_with_context (lines 60, 70, 87 of src/serializer.rs), but there is no documentation explaining:

  • That these methods are not object-safe
  • Whether dyn Serializer usage is supported or deprecated
  • Migration guidance for code relying on trait objects

Consider adding a trait-level doc comment such as:

/// # Object Safety
///
/// This trait is not object-safe due to `Self: Sized` bounds on serialization methods.
/// Code requiring `dyn Serializer` should migrate to concrete types or use a type-erased wrapper.

Overall Comment 2: ContextCapturingSerializer duplication

Status: ✗ Not resolved

ContextCapturingSerializer is implemented independently in two locations:

  • tests/metadata.rs:164
  • tests/fixtures/serializer_boundaries.rs:25

Both share similar logic for capturing DeserializeContext into a Mutex<Option<CapturedContext>>. Consider extracting a reusable test utility (e.g., in wireframe_testing) to avoid duplication and ensure consistent behaviour.

Individual Comment 2: SerdeMessage wrapper complexity

Status: ✗ Not resolved

The Serde bridge still requires explicit wrapping with SerdeMessage:

  • Line 152 defines pub struct SerdeMessage<T>(T)
  • Lines 174-182 implement EncodeWith<S> for SerdeMessage<T> where T: Serialize
  • The blanket impl at line 116 is for T: Message, not for all T: Serialize/DeserializeOwned

The suggested blanket impls:

impl<S, T> EncodeWith<S> for T
where S: SerdeSerializerBridge + ?Sized, T: serde::Serialize

are not present, so callers must still wrap/unwrap with SerdeMessage.


Would you like me to help draft the object safety documentation or create an issue for the ContextCapturingSerializer extraction?

…s, runtime, and tests

- Extract framing codec helpers into helpers/codec.rs
- Move in-memory frame driving helpers to helpers/drive.rs
- Move payload-oriented helpers to helpers/payloads.rs
- Move runtime-level helpers for app execution to helpers/runtime.rs
- Add comprehensive tests in helpers/tests/
- Expose helper functions via re-exports in helpers/mod.rs
- Remove redundant code and macros from helpers.rs to the new modules
- Add macros for push_expect! and recv_expect! in macros.rs
- Update docs and tests accordingly

This improves code modularity, readability, and maintainability of the wireframe_testing crate.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
codescene-delta-analysis[bot]

This comment was marked as outdated.

…coverage

- Introduce ContextCapturingSerializer to capture DeserializeContext metadata during deserialization in tests.
- Replace duplicate context capturing implementations with a shared module.
- Update existing tests to use the new context capturing serializer for better consistency and metadata verification.
- Add detailed docs and usage for SerdeMessage wrapper to clarify Serde bridge pattern.
- Document non-object safe nature of Serializer trait and recommend usage patterns.

This enhances the ability to test and verify context propagation through the serialization framework.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
codescene-delta-analysis[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as resolved.

…anicking

- Changed `push_expect!` and `recv_expect!` macros to return contextualized `io::Error` results rather than panicking on failure.
- Updated test code to propagate errors using `?` operator instead of unwrapping.
- Improved error diagnostics with file and line information in macro-generated errors.
- Modified helper functions in `wireframe_testing` to return `io::Result` for decode and encode operations, improving error handling.
- Updated various test files to handle new error returns consistently.

This refactoring enhances robustness of test code by avoiding panics, providing better error handling and diagnostics.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos leynos changed the title Decouple Message Encoding from Bincode: Adapters, Context & Serde Bridge Decouple Message Encoding from Bincode: Adapters, Context & SerdeBridge Feb 23, 2026
@coderabbitai coderabbitai Bot added the codex label Feb 23, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 23, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

echo "=== 1. Checking Serializer trait for Self: Sized documentation ==="
echo "Looking for object safety documentation in src/serializer.rs:"
rg -n -A10 "pub trait Serializer" src/serializer.rs | head -30

echo ""
echo "Checking for 'object' mentions in Serializer docs:"
rg -n "object|Object" src/serializer.rs || echo "No object safety mentions found"

echo ""
echo "=== 2. Checking ContextCapturingSerializer locations ==="
echo "In tests/metadata.rs:"
rg -n "struct ContextCapturingSerializer" tests/metadata.rs || echo "Not found in tests/metadata.rs"

echo ""
echo "In tests/fixtures/serializer_boundaries.rs:"
rg -n "struct ContextCapturingSerializer" tests/fixtures/serializer_boundaries.rs || echo "Not found in tests/fixtures/serializer_boundaries.rs"

echo ""
echo "In tests/common or other test locations:"
fd -t f "\.rs$" tests/ -x rg -l "struct ContextCapturingSerializer" {}

echo ""
echo "In wireframe_testing:"
fd -t f "\.rs$" wireframe_testing/ -x rg -l "struct ContextCapturingSerializer" {}

echo ""
echo "=== 3. Re-checking adapter/adaptor spelling in ADR-005 ==="
rg -n "adapter[^s]|adaptor" docs/adr-005-serializer-abstraction.md | head -20

echo ""
echo "=== 4. Checking SerdeMessage and blanket impls ==="
echo "Looking for blanket EncodeWith impl for T: Serialize:"
rg -n "impl.*EncodeWith.*for T|impl<S, T> EncodeWith<S> for T" src/message.rs || echo "No blanket impl found"

echo ""
echo "Checking SerdeMessage still exists:"
rg -n "pub struct SerdeMessage" src/message.rs || echo "SerdeMessage not found"

echo ""
echo "=== 5. Checking BDD test implementation ==="
wc -l tests/features/serializer_boundaries.feature tests/steps/serializer_boundaries_steps.rs tests/scenarios/serializer_boundaries_scenarios.rs

echo ""
echo "=== 6. Checking 'your' pronoun in users-guide.md ==="
sed -n '388,395p' docs/users-guide.md

Length of output: 2790


🏁 Script executed:

#!/bin/bash

echo "=== Checking the blanket impl at line 116 in src/message.rs ==="
sed -n '114,135p' src/message.rs

echo ""
echo "=== Also checking if there's a Serde-specific blanket impl ==="
rg -n -A8 "#\[cfg\(feature = \"serializer-serde\"\)\]" src/message.rs | rg -A8 "impl.*EncodeWith.*for T"

Length of output: 761


@leynos, I've checked all six items. Here's the current status:

✓ Resolved (5/6)

Overall Comment 1: Self: Sized object safety documentation

Status: ✓ Resolved

Lines 52-60 of src/serializer.rs now include clear documentation:

/// # Object Safety
///
/// This trait is not object-safe. Its core methods include `Self: Sized`
/// bounds that prevent use with `dyn Serializer`. Code requiring dynamic
/// dispatch should use a concrete type-erased wrapper that provides 
/// object-safe forwarding methods.

Overall Comment 2: ContextCapturingSerializer duplication

Status: ✓ Resolved

ContextCapturingSerializer has been extracted to a shared module at tests/common/context_capturing_serializer.rs. It no longer appears in tests/metadata.rs or tests/fixtures/serializer_boundaries.rs, eliminating the duplication.

Individual Comment 1: adapter/adaptor spelling

Status: ✓ Resolved (previously resolved)

All instances in docs/adr-005-serializer-abstraction.md consistently use "adaptor" (lines 52, 59, 79-81, 88, 97, 120-121, 125, 145).

Individual Comment 3: BDD test coverage

Status: ✓ Resolved (previously resolved)

BDD tests are fully implemented with 15 lines in the feature file, 60 lines of step definitions, and 21 lines of scenario wiring.

Individual Comment 4: "your" pronoun

Status: ✓ Resolved (previously resolved)

Line 390 of docs/users-guide.md now reads "If existing code previously relied on…" instead of "If your code previously relied on…"


✗ Not Resolved (1/6)

Individual Comment 2: SerdeMessage blanket impls

Status: ✗ Not resolved

The blanket impl at line 118 of src/message.rs is for T: Message, not for all T: Serialize:

impl<S, T> EncodeWith<S> for T
where
    S: crate::serializer::MessageCompatibilitySerializer + ?Sized,
    T: Message,  // ← Still requires Message trait

The requested feature-gated blanket impls for T: serde::Serialize and T: serde::DeserializeOwned with S: SerdeSerializerBridge are not present, so callers must still explicitly wrap types with SerdeMessage.

Would you like me to open an issue to track the Serde blanket impl enhancement?

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md (1)

18-59: ⚠️ Potential issue | 🟡 Minor

Align remaining American spellings with en-GB-oxendict.

Replace remaining prose occurrences such as “serializer-boundary”, “deserialization”, and “Deserializers” with “serialiser-boundary”, “deserialisation”, and “Deserialisers” (and audit the rest of the document for the same pattern).
As per coding guidelines: “Use British English based on the Oxford English Dictionary (en-GB-oxendict)”.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md` around lines
18 - 59, Change American English spellings to en-GB-oxendict variants throughout
this document: replace "serializer-boundary" with "serialiser-boundary",
"deserialization" with "serialisation", "Deserializers" (and any capitalised
forms) with "Serialisers", and similarly convert "serializer"/"deserializer"
occurrences to "serialiser"/"serialiser" as appropriate; audit the entire text
for other US spellings and update them consistently (e.g.,
"serialize"/"serialize" → "serialise"/"serialise") while preserving punctuation
and meaning.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/users-guide.md`:
- Around line 381-387: The prose uses US spelling
"deserialization"/"deserializing" but our guideline requires British English;
update the occurrences in this paragraph (the sentence referencing
FrameMetadata::parse and the fallback to full deserialization, and the phrase
"without deserializing the payload") to British spelling: change "full
deserialization" to "full deserialisation" and "deserializing" to
"deserialising"; the surrounding symbols to check for context are
FrameMetadata::parse, EncodeWith<S>, DecodeWith<S>, Message, and the helper
names to_bytes/from_bytes to ensure only prose is altered.

In `@wireframe_testing/src/helpers/tests/helper_tests.rs`:
- Around line 35-73: The test drive_with_payloads_wraps_frames currently uses
expect() and panics; replace each expect() on WireframeApp::new, the .route(...)
call, serializer.serialize(&env), and serializer.deserialize::<Envelope>(first)
with map_err(|e| io::Error::new(io::ErrorKind::InvalidData, format!("...: {}",
e)))? (matching the pattern used elsewhere in the file) so each Result is
converted to an io::Error and propagated with the ? operator; ensure the error
messages include context (e.g., "failed to create app", "route registration
should succeed", "failed to serialize envelope", "failed to deserialise
envelope") to mirror the prior expect strings.

---

Duplicate comments:
In `@docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md`:
- Around line 18-59: Change American English spellings to en-GB-oxendict
variants throughout this document: replace "serializer-boundary" with
"serialiser-boundary", "deserialization" with "serialisation", "Deserializers"
(and any capitalised forms) with "Serialisers", and similarly convert
"serializer"/"deserializer" occurrences to "serialiser"/"serialiser" as
appropriate; audit the entire text for other US spellings and update them
consistently (e.g., "serialize"/"serialize" → "serialise"/"serialise") while
preserving punctuation and meaning.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a786d26 and 17de269.

📒 Files selected for processing (17)
  • docs/execplans/9-5-1-decouple-message-encoding-from-bincode.md
  • docs/users-guide.md
  • src/serializer.rs
  • tests/connection_actor_errors.rs
  • tests/connection_actor_fairness.rs
  • tests/connection_actor_shutdown.rs
  • tests/lifecycle.rs
  • tests/middleware_order.rs
  • tests/push.rs
  • tests/response.rs
  • tests/routes.rs
  • wireframe_testing/README.md
  • wireframe_testing/src/helpers/codec.rs
  • wireframe_testing/src/helpers/payloads.rs
  • wireframe_testing/src/helpers/tests/helper_tests.rs
  • wireframe_testing/src/helpers/tests/mod.rs
  • wireframe_testing/src/macros.rs

Comment thread docs/users-guide.md
Comment thread wireframe_testing/src/helpers/tests/helper_tests.rs
@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Feb 23, 2026

@coderabbitai the engineer writes:

The finding is accurate in current code:

  • src/message.rs:116 and src/message.rs:127 still provide blanket EncodeWith/DecodeWith for T: Message.
  • src/message.rs:145 explicitly documents why SerdeMessage<T> exists.
  • No blanket impls exist for all T: serde::Serialize / T: serde::de::DeserializeOwned.

I did not apply a code change because this is a Rust coherence conflict, not an accidental omission: adding those Serde blanket impls would overlap with the existing T: Message blanket impls (E0119) for types that satisfy both bounds.

If you want me to force this through, I can implement one breaking alternative:

  1. Remove/replace T: Message blanket adapters (breaks current compatibility model), or
  2. Restrict one adapter family to wrapper types/marker types (keeps compatibility but not “true blanket for all T”).

1 similar comment
@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Feb 23, 2026

@coderabbitai the engineer writes:

The finding is accurate in current code:

  • src/message.rs:116 and src/message.rs:127 still provide blanket EncodeWith/DecodeWith for T: Message.
  • src/message.rs:145 explicitly documents why SerdeMessage<T> exists.
  • No blanket impls exist for all T: serde::Serialize / T: serde::de::DeserializeOwned.

I did not apply a code change because this is a Rust coherence conflict, not an accidental omission: adding those Serde blanket impls would overlap with the existing T: Message blanket impls (E0119) for types that satisfy both bounds.

If you want me to force this through, I can implement one breaking alternative:

  1. Remove/replace T: Message blanket adapters (breaks current compatibility model), or
  2. Restrict one adapter family to wrapper types/marker types (keeps compatibility but not “true blanket for all T”).

Changed unwraps and expects to return io::Result with descriptive io::Error errors in helper_tests.rs tests. This improves error reporting by mapping creation, routing, serialization, and deserialization failures into io::Errors instead of panicking.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
Copy link
Copy Markdown

@codescene-delta-analysis codescene-delta-analysis Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gates Passed
6 Quality Gates Passed

See analysis details in CodeScene

Absence of Expected Change Pattern

  • wireframe/src/message.rs is usually changed with: wireframe/src/preamble.rs

Quality Gate Profile: Pay Down Tech Debt
Install CodeScene MCP: safeguard and uplift AI-generated code. Catch issues early with our IDE extension and CLI tool.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Feb 23, 2026

@coderabbitai the engineer writes:

The finding is accurate in current code:

  • src/message.rs:116 and src/message.rs:127 still provide blanket EncodeWith/DecodeWith for T: Message.
  • src/message.rs:145 explicitly documents why SerdeMessage<T> exists.
  • No blanket impls exist for all T: serde::Serialize / T: serde::de::DeserializeOwned.

I did not apply a code change because this is a Rust coherence conflict, not an accidental omission: adding those Serde blanket impls would overlap with the existing T: Message blanket impls (E0119) for types that satisfy both bounds.

If you want me to force this through, I can implement one breaking alternative:

  1. Remove/replace T: Message blanket adapters (breaks current compatibility model), or
  2. Restrict one adapter family to wrapper types/marker types (keeps compatibility but not “true blanket for all T”).

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 23, 2026

Tip

For best results, initiate chat on the files or code changes.

@leynos, thank you for the clarification — the engineer's analysis is absolutely correct, and I apologise for the incomplete review comment.

Why the Original Suggestion Was Flawed

My earlier suggestion to add blanket EncodeWith<S>/DecodeWith<S> implementations for all T: Serialize/T: DeserializeOwned overlooked Rust's coherence rules. Adding those blanket impls would indeed conflict with the existing blanket impls for T: Message (at lines 116 and 127), causing error E0119 whenever a type implements both traits.

The Current Design Is Correct

The SerdeMessage<T> wrapper (documented at line 145) is the standard Rust pattern for working around this coherence constraint. It's not a temporary workaround — it's the appropriate solution. Other libraries in the ecosystem use similar wrapper types to bridge between trait families (e.g., serde_json's approach with certain wrapper types).

The wrapper:

  • Avoids the coherence conflict by providing a distinct type
  • Keeps the existing T: Message compatibility layer intact
  • Gives users explicit control over which serialisation path to use when both are available
  • Adds minimal boilerplate via the IntoSerdeMessage helper trait

Recommendation

Do not implement either breaking alternative. The current design with SerdeMessage<T> wrapper is correct and should remain as-is. The documentation at line 145 already explains the rationale clearly.

I'm marking Individual Comment 2 (SerdeMessage wrapper complexity) as resolved — no code change is needed, and the original review suggestion was based on incomplete analysis of Rust's trait coherence constraints.

@leynos leynos merged commit 909a297 into main Feb 23, 2026
5 checks passed
@leynos leynos deleted the decouple-message-encoding-bincode-cpjk3l branch February 23, 2026 18:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant