Skip to content

Implement memory budget enforcement across message assembly (8.3.2)#474

Merged
leynos merged 5 commits intomainfrom
budget-enforcement-memory-8-3-2-foue2u
Feb 23, 2026
Merged

Implement memory budget enforcement across message assembly (8.3.2)#474
leynos merged 5 commits intomainfrom
budget-enforcement-memory-8-3-2-foue2u

Conversation

@leynos
Copy link
Copy Markdown
Owner

@leynos leynos commented Feb 21, 2026

Summary

  • Implements runtime enforcement for MemoryBudgets during message assembly across per-message, per-connection, and in-flight budgets. Offending partial assemblies are aborted and buffers are freed. Single-frame messages bypass aggregate budgets. All changes are additive and gated behind MemoryBudgets configuration.

Changes

Core functionality

  • Added budget enforcement helpers in a new module: src/message_assembler/budget.rs
    • AggregateBudgets struct to package connection and in-flight limits
    • check_aggregate_budgets to enforce budgets across all in-flight assemblies
    • check_size_limit to enforce per-message size limit (reused from existing logic)
  • Extended message assembly state to carry optional budgets and provide diagnostics
    • MessageAssemblyState::with_budgets(...) constructor (wrapping existing constructor)
    • total_buffered_bytes(&self) -> usize to report aggregate buffered bytes across all in-flight assemblies
  • Integrated budget checks into frame acceptance flow
    • Wire budget checks into accept_first_frame_at() and accept_continuation_frame_at() (abort and free partial buffers on violation)
  • Compute effective per-message limit as min(max_message_size, bytes_per_message) when budgets are configured

API / Module wiring

  • Updated error handling to surface budget-related failures
    • Added ConnectionBudgetExceeded and InFlightBudgetExceeded variants in src/message_assembler/error.rs
  • Registered new budget module in src/message_assembler/mod.rs
  • Wiring changes in integration path:
    • src/app/frame_handling/assembly.rs: new signature for new_message_assembly_state to accept optional MemoryBudgets and apply budget-sourced limits
    • src/app/connection.rs: passes self.memory_budgets into the assembly state creation
  • Documentation note in memory budgets module to reflect enforcement domain

Tests

  • Added unit tests for budget enforcement:
    • src/message_assembler/budget.rs helpers tested via src/message_assembler/budget_tests.rs
  • Behavioural tests scaffold for budget enforcement:
    • Feature file: tests/features/budget_enforcement.feature
    • World and steps: tests/fixtures/budget_enforcement.rs, tests/steps/budget_enforcement_steps.rs
    • Scenarios: tests/scenarios/budget_enforcement_scenarios.rs
    • Test glue wired into tests/fixtures/mod.rs, tests/steps/mod.rs, and tests/scenarios/mod.rs

Documentation

  • ADR/doc decisions updated to reflect hard-budget enforcement placement in MessageAssemblyState and interaction with MemoryBudgets (docs/adr-002-streaming-requests-and-shared-message-assembly.md)
  • Roadmap updated to mark 8-3-2 as done (docs/roadmap.md)
  • User guide updated to document enforcement semantics when budgets are configured (docs/users-guide.md)
  • ExecPlan for 8-3-2-budget-enforcement added (docs/execplans/8-3-2-budget-enforcement.md)

How to test locally

  • cargo test -p wireframe --lib (or specific crates as needed)
  • cargo test --lib message_assembler
  • cargo test --test budget_enforcement (BDD tests)
  • Run lint/format checks as per project workflow

Validation and acceptance criteria

  • Budget enforcement is exercised for per-message, per-connection, and in-flight budgets in unit and behavioural tests
  • Offending partial assemblies are freed; other assemblies remain unaffected
  • Backward compatibility remains when MemoryBudgets is not configured
  • Documentation and roadmap reflect completion of 8.3.2

Artefacts

New files added:

  • src/message_assembler/budget.rs
  • src/message_assembler/budget_tests.rs
  • tests/features/budget_enforcement.feature
  • tests/fixtures/budget_enforcement.rs
  • tests/steps/budget_enforcement_steps.rs
  • tests/scenarios/budget_enforcement_scenarios.rs

Modified files:

  • src/message_assembler/mod.rs
  • src/message_assembler/state.rs
  • src/message_assembler/error.rs
  • src/app/frame_handling/assembly.rs
  • src/app/connection.rs
  • src/app/memory_budgets.rs
  • docs/adr-002-streaming-requests-and-shared-message-assembly.md
  • docs/roadmap.md
  • docs/users-guide.md
  • docs/execplans/8-3-2-budget-enforcement.md

📎 Task: https://www.devboxer.com/task/200e5f5a-08ad-4d8e-95be-2332833a7927

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 21, 2026

Walkthrough

Implements runtime budget enforcement for streaming message assembly, adding per-connection, in-flight, and per-message byte limits with dedicated budget checking logic. Violations are surfaced as ConnectionBudgetExceeded or InFlightBudgetExceeded errors, whilst single-frame messages bypass aggregate limits.

Changes

Cohort / File(s) Summary
Documentation Updates
docs/adr-002-streaming-requests-and-shared-message-assembly.md, docs/execplans/8-3-2-budget-enforcement.md, docs/roadmap.md, docs/users-guide.md
Marks 8.3.2 budget enforcement as completed; documents hard-cap budget enforcement design, integration via MemoryBudgets, and per-message limit derivation; clarifies behaviour for budget rejection, partial buffer cleanup, and single-frame bypass.
Budget Enforcement Core
src/message_assembler/budget.rs
New module defining AggregateBudgets struct and enforcement helpers check_aggregate_budgets, check_size_limit for validating accumulated bytes against connection and in-flight limits with detailed error reporting.
State Integration
src/message_assembler/state.rs
Adds budgets field to MessageAssemblyState, introduces with_budgets constructor, wires budget checks into accept_first_frame_at and accept_continuation_frame_at, adds total_buffered_bytes() tracking and PartialAssembly::buffered_bytes() accounting.
Error Variants
src/message_assembler/error.rs
Adds ConnectionBudgetExceeded and InFlightBudgetExceeded variants to MessageAssemblyError enum with key, attempted bytes, and limit fields.
Module Exposure
src/message_assembler/mod.rs, src/message_assembler/tests.rs
Registers new budget module and budget_tests test suite; exposes state and types as public modules.
Assembly Integration
src/app/frame_handling/assembly.rs, src/app/inbound_handler.rs
Updates new_message_assembly_state signature to accept optional MemoryBudgets parameter; computes effective per-message limit and routes budgets to MessageAssemblyState constructor; wires memory_budgets through inbound handler.
Memory Budgets Documentation
src/app/memory_budgets.rs
Clarifies module documentation that enforcement is wired through MessageAssemblyState::with_budgets and budget violations are caught at frame acceptance time.
Core Unit Tests
src/message_assembler/budget_tests.rs, src/message_assembler/budget_enforcement_tests.rs
Comprehensive unit test suite covering total_buffered_bytes accounting, connection budget enforcement, in-flight budget enforcement, dual-budget scenarios, budget isolation, headroom reclamation, and single-frame message bypass.
BDD Test Infrastructure
tests/features/budget_enforcement.feature, tests/fixtures/budget_enforcement.rs, tests/fixtures/mod.rs, tests/scenarios/budget_enforcement_scenarios.rs, tests/scenarios/mod.rs, tests/steps/budget_enforcement_steps.rs, tests/steps/mod.rs
Introduces complete BDD test harness with feature specifications, test world fixture (BudgetEnforcementWorld), scenario bindings, and Given/When/Then step definitions for budget enforcement scenarios.

Sequence Diagram

sequenceDiagram
    participant Client
    participant InboundHandler
    participant MessageAssemblyState
    participant BudgetChecker
    participant ErrorHandler

    Client->>InboundHandler: submit frame (key, body_len)
    InboundHandler->>MessageAssemblyState: accept_first_frame_at(timestamp, input)
    
    alt Is Single Frame
        MessageAssemblyState->>Client: return AssembledMessage immediately
        Note over MessageAssemblyState: Bypass aggregate budgets
    else Is Multi-Frame
        MessageAssemblyState->>BudgetChecker: check_aggregate_budgets(key, current_total, additional_bytes)
        
        alt Budget Exceeded
            BudgetChecker->>MessageAssemblyState: return ConnectionBudgetExceeded or InFlightBudgetExceeded
            MessageAssemblyState->>MessageAssemblyState: free partial assembly
            MessageAssemblyState->>ErrorHandler: surface error as InvalidData
            ErrorHandler->>Client: frame rejected
        else Budget OK
            MessageAssemblyState->>BudgetChecker: check_size_limit(max_message_size, key, accumulated, body_len)
            
            alt Size Limit Exceeded
                BudgetChecker->>MessageAssemblyState: return MessageTooLarge
                MessageAssemblyState->>ErrorHandler: surface error
                ErrorHandler->>Client: frame rejected
            else Size OK
                MessageAssemblyState->>MessageAssemblyState: buffer frame, track buffered_bytes
                MessageAssemblyState->>Client: partial assembly stored
            end
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • PR #463 — Introduces MemoryBudgets configuration that this PR integrates and enforces through MessageAssemblyState.
  • PR #458 — Extends message assembly integration layer that this PR modifies to accept and wire budget constraints.

Suggested labels

codex

Poem

📦 Bytes now have boundaries, both wide and tight,
Per-connection, in-flight—budgets done right.
Frames that exceed them are gracefully shed,
Single-frame messages skip budgets instead! ✨
Assembly flows smartly with limits in sight.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed Title clearly and concisely describes the main change: implementing memory budget enforcement during message assembly for objective 8.3.2.
Description check ✅ Passed Description comprehensively details the implementation of memory budget enforcement, changes made across modules, testing approach, and includes local testing instructions and acceptance criteria.

✏️ 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 budget-enforcement-memory-8-3-2-foue2u

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

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Feb 21, 2026

Reviewer's Guide

Implements runtime memory budget enforcement in the message assembly pipeline, adding aggregate budget tracking to MessageAssemblyState, wiring it through the app’s assembly construction path, surfacing new budget-related errors, and covering the behavior with unit and BDD tests plus documentation updates.

Sequence diagram for frame acceptance with budget enforcement

sequenceDiagram
    participant Conn as Connection
    participant FH as frame_handling
    participant MAS as MessageAssemblyState
    participant BM as budget(check_aggregate_budgets,check_size_limit)

    Conn->>FH: new_message_assembly_state(fragmentation, frame_budget, memory_budgets)
    activate FH
    FH->>FH: compute frag_max from fragmentation or frame_budget
    alt memory_budgets is Some
        FH->>memory_budgets: bytes_per_message()
        FH->>FH: per_msg = min(frag_max, bytes_per_message)
        FH->>memory_budgets: bytes_per_connection(), bytes_in_flight()
        FH->>MAS: with_budgets(per_msg, timeout, connection_budget, in_flight_budget)
    else no memory_budgets
        FH->>MAS: new(frag_max, timeout)
    end
    deactivate FH

    Conn->>MAS: accept_first_frame_at(input, now)
    activate MAS
    MAS->>BM: check_size_limit(max_message_size, key, 0, input.body.len())
    BM-->>MAS: Ok(total_body)
    MAS->>MAS: compute incoming_bytes = body.len + metadata.len
    MAS->>MAS: total_before = total_buffered_bytes()
    MAS->>BM: check_aggregate_budgets(key, total_before, incoming_bytes, budgets)
    alt budgets exceeded
        BM-->>MAS: Err(ConnectionBudgetExceeded or InFlightBudgetExceeded)
        MAS-->>Conn: Err(MessageAssemblyError)
    else within budgets
        BM-->>MAS: Ok(())
        MAS->>MAS: insert new PartialAssembly
        MAS-->>Conn: Ok(assembly_started)
    end
    deactivate MAS

    Conn->>MAS: accept_continuation_frame_at(header, body, now)
    activate MAS
    MAS->>MAS: buffered_total = total_buffered_bytes()
    MAS->>MAS: lookup PartialAssembly by key
    MAS->>BM: check_size_limit(max_message_size, key, accumulated_len, body.len)
    alt size limit exceeded
        BM-->>MAS: Err(MessageTooLarge)
        MAS->>MAS: remove PartialAssembly
        MAS-->>Conn: Err(MessageTooLarge)
    else size ok
        BM-->>MAS: Ok(new_len)
        MAS->>BM: check_aggregate_budgets(key, buffered_total, body.len, budgets)
        alt aggregate budget exceeded
            BM-->>MAS: Err(ConnectionBudgetExceeded or InFlightBudgetExceeded)
            MAS->>MAS: remove PartialAssembly
            MAS-->>Conn: Err(MessageAssemblyError)
        else aggregate ok
            BM-->>MAS: Ok(())
            MAS->>MAS: append body to PartialAssembly
            MAS-->>Conn: Ok(maybe_completed_message)
        end
    end
    deactivate MAS
Loading

Class diagram for message assembly budget enforcement types

classDiagram
    class MessageAssemblyState {
        - NonZeroUsize max_message_size
        - Duration timeout
        - HashMap~MessageKey, PartialAssembly~ assemblies
        - AggregateBudgets budgets
        + new(max_message_size: NonZeroUsize, timeout: Duration) MessageAssemblyState
        + with_budgets(max_message_size: NonZeroUsize, timeout: Duration, connection_budget: Option~NonZeroUsize~, in_flight_budget: Option~NonZeroUsize~) MessageAssemblyState
        + total_buffered_bytes() usize
        + buffered_count() usize
        + accept_first_frame_at(input: FirstFrameInput, now: Instant) Result~Option~CompletedMessage~~
        + accept_continuation_frame_at(header: ContinuationHeader, body: Vec~u8~, now: Instant) Result~Option~CompletedMessage~~
        + purge_expired_at(now: Instant) usize
    }

    class AggregateBudgets {
        + Option~NonZeroUsize~ connection
        + Option~NonZeroUsize~ in_flight
    }

    class PartialAssembly {
        - Vec~u8~ metadata
        - Vec~u8~ body_buffer
        + accumulated_len() usize
        + buffered_bytes() usize
    }

    class MessageAssemblyError {
        <<enum>>
        Series
        DuplicateFirstFrame
        MessageTooLarge
        ConnectionBudgetExceeded
        InFlightBudgetExceeded
    }

    class BudgetModule {
        <<module>>
        + check_size_limit(max_message_size: NonZeroUsize, key: MessageKey, accumulated: usize, body_len: usize) Result~usize, MessageAssemblyError~
        + check_aggregate_budgets(key: MessageKey, total_buffered: usize, incoming_bytes: usize, budgets: AggregateBudgets) Result~(), MessageAssemblyError~
    }

    class MemoryBudgets {
        + bytes_per_message() BudgetBytes
        + bytes_per_connection() BudgetBytes
        + bytes_in_flight() BudgetBytes
    }

    class BudgetBytes {
        + get() NonZeroUsize
    }

    class AssemblyRuntimeModule {
        <<module>>
        + new_message_assembly_state(fragmentation: Option~FragmentationConfig~, frame_budget: usize, memory_budgets: Option~MemoryBudgets~) MessageAssemblyState
    }

    class Connection {
        - Option~FragmentationConfig~ fragmentation
        - usize requested_frame_length
        - Option~MemoryBudgets~ memory_budgets
        + run_inbound_loop()
    }

    MessageAssemblyState *-- PartialAssembly
    MessageAssemblyState *-- AggregateBudgets
    BudgetModule ..> MessageAssemblyError
    BudgetModule ..> AggregateBudgets
    PartialAssembly ..> MessageAssemblyState : used_by
    AssemblyRuntimeModule ..> MessageAssemblyState
    AssemblyRuntimeModule ..> MemoryBudgets
    MemoryBudgets *-- BudgetBytes
    Connection ..> AssemblyRuntimeModule
    MessageAssemblyState ..> BudgetModule
Loading

File-Level Changes

Change Details Files
Add aggregate memory budget tracking and enforcement to message assembly state.
  • Extend PartialAssembly with buffered_bytes() to account for body + metadata heap usage.
  • Add AggregateBudgets field to MessageAssemblyState and introduce with_budgets(...) constructor while keeping new() as a budgets-free wrapper.
  • Compute total_buffered_bytes() across all in-flight assemblies for diagnostics and enforcement.
  • Invoke check_aggregate_budgets before buffering first and continuation frames, aborting and removing offending assemblies on violations.
  • Snapshot immutable state (max_message_size, budgets, total_buffered_bytes) in accept_continuation_frame_at() to avoid borrow conflicts when checking budgets and size limits.
src/message_assembler/state.rs
src/message_assembler/mod.rs
Factor out budget- and size-limit helpers and extend error surface for budget violations.
  • Extract check_size_limit and check_aggregate_budgets into a new budget module, along with the AggregateBudgets struct (implementation not fully shown in diff but referenced from state).
  • Remove the old check_size_limit() implementation from MessageAssemblyState in favor of the helper in the budget module.
  • Add ConnectionBudgetExceeded and InFlightBudgetExceeded variants to MessageAssemblyError with detailed attempted/limit fields.
src/message_assembler/budget.rs
src/message_assembler/state.rs
src/message_assembler/error.rs
Wire MemoryBudgets into assembly state construction and inbound connection handling.
  • Extend new_message_assembly_state(...) to accept Option and compute the effective per-message limit as min(fragmentation_max, bytes_per_message) when budgets are present.
  • Pass connection and in-flight budget values from MemoryBudgets into MessageAssemblyState::with_budgets, falling back to MessageAssemblyState::new when budgets are absent.
  • Update inbound_handler to pass self.memory_budgets into new_message_assembly_state so per-connection assembly instances see the configured budgets.
src/app/frame_handling/assembly.rs
src/app/inbound_handler.rs
src/app/connection.rs
Add targeted unit tests for budget accounting and enforcement.
  • Introduce budget_tests.rs with fixtures for unbounded and budgeted MessageAssemblyState instances and helper functions for submitting first/continuation frames.
  • Cover total_buffered_bytes accounting, connection and in-flight budget enforcement, dual-budget precedence, backward compatibility when budgets are None, headroom reclamation after completion/purge, and single-frame bypass semantics.
  • Register the new budget_tests module in message_assembler/tests.rs.
src/message_assembler/budget_tests.rs
src/message_assembler/tests.rs
Add BDD-style behavioural coverage for budget enforcement scenarios.
  • Define a BudgetEnforcementWorld fixture that wraps MessageAssemblyState with helper methods for first/continuation/single-frame submission, error capture, and time-based purge operations.
  • Implement rstest-bdd step definitions for configuring budgets, manipulating time, submitting frames, and asserting on acceptance, error kind, buffered bytes, and assembly counts.
  • Add budget_enforcement.feature with scenarios for in-limit behavior, budget violations, headroom reclamation, and single-frame bypass, and register corresponding scenario bindings and fixture modules.
tests/features/budget_enforcement.feature
tests/fixtures/budget_enforcement.rs
tests/steps/budget_enforcement_steps.rs
tests/scenarios/budget_enforcement_scenarios.rs
tests/fixtures/mod.rs
tests/steps/mod.rs
tests/scenarios/mod.rs
Update documentation and roadmap to reflect budget enforcement implementation and design decisions.
  • Document the placement and semantics of hard-cap budget enforcement in MessageAssemblyState and its interaction with MemoryBudgets in ADR 002.
  • Update the user guide to clarify that configured budgets are enforced at frame-acceptance time, how errors surface, how the effective per-message limit is computed, and that single-frame messages bypass aggregate budgets.
  • Mark roadmap item 8.3.2 as complete and add an ExecPlan document describing the implementation plan, risks, and outcomes.
docs/adr-002-streaming-requests-and-shared-message-assembly.md
docs/users-guide.md
docs/roadmap.md
docs/execplans/8-3-2-budget-enforcement.md
src/app/memory_budgets.rs

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

codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Feb 22, 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/steps/budget_enforcement_steps.rs

Comment on file

fn given_budgeted_state(
    budget_enforcement_world: &mut BudgetEnforcementWorld,
    max_msg: usize,
    timeout: u64,
    conn: usize,
    flight: usize,
) -> TestResult {
    budget_enforcement_world.init_budgeted_state(BudgetedStateConfig {
        max_message_size: max_msg,
        timeout_secs: timeout,
        connection_budget: conn,
        in_flight_budget: flight,
    })
}

❌ New issue: Excess Number of Function Arguments
given_budgeted_state has 5 arguments, max arguments = 4

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Feb 22, 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:

src/message_assembler/budget_tests.rs

Comment on file

fn submit_first(
    state: &mut MessageAssemblyState,
    key: u64,
    body: &[u8],
    is_last: bool,
) -> Result<Option<crate::message_assembler::AssembledMessage>, MessageAssemblyError> {
    let header = first_header(key, body.len(), is_last);
    let input =
        FirstFrameInput::new(&header, EnvelopeRouting::default(), vec![], body).expect("valid");
    state.accept_first_frame(input)
}

❌ New issue: Code Duplication
The module contains 2 functions with similar structure: submit_first,submit_first_at

@coderabbitai

This comment was marked as resolved.

@coderabbitai

This comment was marked as resolved.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Feb 22, 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:

src/message_assembler/budget_tests.rs

Comment on file

fn submit_first(
    state: &mut MessageAssemblyState,
    key: u64,
    body: &[u8],
    is_last: bool,
) -> Result<Option<crate::message_assembler::AssembledMessage>, MessageAssemblyError> {
    let header = first_header(key, body.len(), is_last);
    let input =
        FirstFrameInput::new(&header, EnvelopeRouting::default(), vec![], body).expect("valid");
    state.accept_first_frame(input)
}

❌ New issue: Code Duplication
The module contains 2 functions with similar structure: submit_first,submit_first_at

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Feb 22, 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/steps/budget_enforcement_steps.rs

Comment on file

fn given_budgeted_state(
    budget_enforcement_world: &mut BudgetEnforcementWorld,
    max_msg: usize,
    timeout: u64,
    conn: usize,
    flight: usize,
) -> TestResult {
    budget_enforcement_world.init_budgeted_state(BudgetedStateConfig {
        max_message_size: max_msg,
        timeout_secs: timeout,
        connection_budget: conn,
        in_flight_budget: flight,
    })
}

❌ New issue: Excess Number of Function Arguments
given_budgeted_state has 5 arguments, max arguments = 4

@coderabbitai

This comment was marked as resolved.

@coderabbitai

This comment was marked as resolved.

codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos leynos changed the title Implement per-connection memory budget enforcement (8.3.2) Enforce memory budgets during message assembly (8.3.2) Feb 22, 2026
leynos and others added 2 commits February 22, 2026 22:51
…gets during assembly

Implement runtime budget enforcement for message assembly covering bytes per message, bytes per connection, and bytes in flight. Frames exceeding configured budgets are rejected, partial assemblies freed, and errors surfaced via existing deserialization failure paths.

- Add budget enforcement helpers in a new module `message_assembler/budget.rs`.
- Extend `MessageAssemblyState` with optional connection and in-flight budget fields.
- Introduce `with_budgets()` constructor to initialize budgets.
- Wire budget checks into frame acceptance methods for first and continuation frames.
- Add new error variants `ConnectionBudgetExceeded` and `InFlightBudgetExceeded`.
- Update integration layer to pass `MemoryBudgets` to assembly state.
- Include extensive unit and behavioral tests covering all budget enforcement scenarios.
- Update user guide, design docs, and roadmap to reflect enforcement implementation.

Closes roadmap item 8.3.2.

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

Encapsulate the construction and validation of FirstFrameHeader and FirstFrameInput into the `create_first_frame_input!` macro. This reduces code duplication and ensures consistent setup of first-frame submission helpers in budget_tests.rs.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
@devboxerhub devboxerhub Bot force-pushed the budget-enforcement-memory-8-3-2-foue2u branch from 695b8b7 to 7cc96db Compare February 22, 2026 22:52
@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.

… tests

- Implement budget enforcement checks for connection and in-flight aggregate limits.
- Add `is_enabled` method on `AggregateBudgets` to optimize budget enforcement gating.
- Abort and free partial assemblies on budget violations, surfacing errors.
- Add extensive behavioural tests covering budget enforcement scenarios:
  connection limits, in-flight limits, dual budgets, isolation, headroom reclamation,
  and single-frame message bypass.
- Update documentation to reflect new budget enforcement behavior and terminology.
- Refactor existing tests to move enforcement coverage into separate submodule.
- Ensure backward compatibility where no budgets mean no enforcement.
- Minor fixes to documentation for clarity and style.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
@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 if the record_result method sets last_completed = false in Err branch
echo "=== Checking record_result in budget_enforcement.rs ==="
rg -n -A 20 "fn record_result" tests/fixtures/budget_enforcement.rs

echo -e "\n=== Checking BudgetEnforcementWorld::default implementation ==="
rg -n -A 15 "impl.*Default.*for.*BudgetEnforcementWorld" tests/fixtures/budget_enforcement.rs

echo -e "\n=== Checking acronym expansions in execplan ==="
head -n 60 docs/execplans/8-3-2-budget-enforcement.md

echo -e "\n=== Checking deserialization spelling in users-guide.md ==="
rg -n "deserialization\|deserialisation" docs/users-guide.md

Length of output: 4463


🏁 Script executed:

#!/bin/bash
# Check the specific area around line 573 in users-guide.md
echo "=== Checking context around line 573 in users-guide.md ==="
sed -n '560,590p' docs/users-guide.md

Length of output: 1353


@leynos, I've verified the status of all the review comments. Here's the summary:

✅ Resolved (5 out of 6)

  1. record_result bug fix: The Err branch now correctly sets self.last_completed = false; on Line 111 of tests/fixtures/budget_enforcement.rs.

  2. ExecPlan acronym expansion: Line 3 of docs/execplans/8-3-2-budget-enforcement.md now reads "This execution plan (ExecPlan) is a living document."

  3. BDD acronym expansion: Line 39 now includes "Behavioural tests (behaviour-driven development, via the rstest-bdd v0.5.0 crate)".

  4. ADR acronym expansion: Line 55 now reads "per architecture decision record (ADR) 0002 guidance on compatible values".

  5. British spelling: Line 573 of docs/users-guide.md now uses "deserialisation-failure policy" with the en-GB spelling.

❌ Not Resolved (1 remaining)

BudgetEnforcementWorld::default uses NonZeroUsize::MIN: Lines 51-53 of tests/fixtures/budget_enforcement.rs still show:

let max = NonZeroUsize::MIN;
Self {
    state: MessageAssemblyState::new(max, Duration::from_secs(30)),

This means any uninitialised world accepting a multi-byte body will fail with MessageTooLarge. The original comment suggested using a more forgiving default (e.g., NonZeroUsize::new(64 * 1024).unwrap()) or enforcing an explicit init-only pattern to make test failures easier to interpret.

Changed the default budget limit in BudgetEnforcementWorld to 64 KB from the minimum possible value. This more forgiving default allows test scenarios that forget to initialize budgeted state to fail with a clear budget error instead of a confusing MessageTooLarge error on multi-byte bodies.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
@leynos leynos changed the title Enforce memory budgets during message assembly (8.3.2) Implement per-connection memory budget enforcement (8.3.2) Feb 23, 2026
@coderabbitai coderabbitai Bot added the codex label Feb 23, 2026
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: 6

🤖 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/adr-002-streaming-requests-and-shared-message-assembly.md`:
- Line 311: The sentence "On budget violation the offending partial assembly is
freed and the error" needs a comma before the coordinating conjunction; update
the text in docs/ADR to read "On budget violation the offending partial assembly
is freed, and the error" so the two independent clauses are clearly separated
(locate the sentence starting "On budget violation the offending partial
assembly..." and insert the comma before "and").

In `@docs/execplans/8-3-2-budget-enforcement.md`:
- Around line 14-26: The prose in docs/execplans/8-3-2-budget-enforcement.md
(notably the Purpose and Success sections) contains long lines that violate the
80-column wrap rule; reflow those paragraphs and any long bullet lines to 80
characters or less so markdownlint passes, preserving existing wording and
bullet structure and keeping headings and the
`8.3.2`/`MemoryBudgets`/`WireframeApp` references intact.
- Around line 28-31: Replace British -ise spellings with Oxford -ize forms in
the documentation: change every occurrence of "deserialisation" to
"deserialization" (including the phrase "deserialization-failure policy") and
"optimisation" to "optimization" (and any other -ise variants like
"optimise"/"optimize") throughout the file; update the affected sentences to use
the -ize spelling while preserving original punctuation and casing.

In `@docs/users-guide.md`:
- Around line 570-577: Reflow the paragraph about the message assembly subsystem
to wrap at ~80 characters per line, standardise spelling to the en-GB-oxendict
form using “-ize” variants (e.g., "standardize" style) throughout the paragraph,
and add a comma before "since" in the final sentence so it reads "...are never
counted against aggregate budgets, since they do not buffer."; locate the block
containing the text starting "When budgets are configured, the message assembly
subsystem..." and apply these edits to that paragraph.

In `@src/message_assembler/budget_enforcement_tests.rs`:
- Around line 28-147: Collapse the duplicated connection/in-flight budget tests
into parameterised rstest cases by replacing the near-identical blocks that use
the fixtures connection_budgeted_state and in_flight_budgeted_state with a
single #[rstest] that iterates over (state_fixture, expected_error_variant)
pairs; update the tests that call submit_first, accept_continuation_frame,
continuation_header and assert on
state.buffered_count()/state.total_buffered_bytes() to use the parameterised
state (MessageAssemblyState) and match expected
MessageAssemblyError::ConnectionBudgetExceeded vs
MessageAssemblyError::InFlightBudgetExceeded accordingly; ensure helper names
submit_first, accept_continuation_frame, continuation_header, buffered_count and
total_buffered_bytes are used unchanged and that match patterns reference
MessageAssemblyError::ConnectionBudgetExceeded and
MessageAssemblyError::InFlightBudgetExceeded so behavior and assertions remain
identical.

In `@tests/steps/budget_enforcement_steps.rs`:
- Around line 16-34: The test step function given_budgeted_state currently takes
four separate params (max_msg, timeout, conn, flight) and immediately builds a
BudgetedStateConfig, triggering the clippy too-many-arguments lint and requiring
an #[expect] suppression; change the step to accept a single BudgetedStateConfig
parameter (parsed from the Cucumber step or by updating the feature to supply a
structured input) and pass it straight into
BudgetEnforcementWorld::init_budgeted_state, then remove the
#[expect(clippy::too_many_arguments, ...)] annotation so the lint no longer
applies.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4616fb7 and a26373f.

📒 Files selected for processing (21)
  • docs/adr-002-streaming-requests-and-shared-message-assembly.md
  • docs/execplans/8-3-2-budget-enforcement.md
  • docs/roadmap.md
  • docs/users-guide.md
  • src/app/frame_handling/assembly.rs
  • src/app/inbound_handler.rs
  • src/app/memory_budgets.rs
  • src/message_assembler/budget.rs
  • src/message_assembler/budget_enforcement_tests.rs
  • src/message_assembler/budget_tests.rs
  • src/message_assembler/error.rs
  • src/message_assembler/mod.rs
  • src/message_assembler/state.rs
  • src/message_assembler/tests.rs
  • tests/features/budget_enforcement.feature
  • tests/fixtures/budget_enforcement.rs
  • tests/fixtures/mod.rs
  • tests/scenarios/budget_enforcement_scenarios.rs
  • tests/scenarios/mod.rs
  • tests/steps/budget_enforcement_steps.rs
  • tests/steps/mod.rs

Comment thread docs/adr-002-streaming-requests-and-shared-message-assembly.md Outdated
Comment thread docs/execplans/8-3-2-budget-enforcement.md
Comment thread docs/execplans/8-3-2-budget-enforcement.md
Comment thread docs/users-guide.md Outdated
Comment thread src/message_assembler/budget_enforcement_tests.rs Outdated
Comment thread tests/steps/budget_enforcement_steps.rs Outdated
@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Feb 23, 2026

@coderabbitai Have the following now been resolved?

❌ Not Resolved (1 remaining)

BudgetEnforcementWorld::default uses NonZeroUsize::MIN: Lines 51-53 of tests/fixtures/budget_enforcement.rs still show:

let max = NonZeroUsize::MIN;
Self {
    state: MessageAssemblyState::new(max, Duration::from_secs(30)),

This means any uninitialised world accepting a multi-byte body will fail with MessageTooLarge. The original comment suggested using a more forgiving default (e.g., NonZeroUsize::new(64 * 1024).unwrap()) or enforcing an explicit init-only pattern to make test failures easier to interpret.

@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 the current state of BudgetEnforcementWorld::default implementation
echo "=== Checking BudgetEnforcementWorld::default implementation ==="
rg -n -A 10 "impl.*Default.*for.*BudgetEnforcementWorld" tests/fixtures/budget_enforcement.rs

echo -e "\n=== Checking for NonZeroUsize::MIN usage ==="
rg -n "NonZeroUsize::MIN" tests/fixtures/budget_enforcement.rs

echo -e "\n=== Checking for 64 KiB or similar default ==="
rg -n "64.*1024\|KiB\|max_message_size" tests/fixtures/budget_enforcement.rs | head -20

Length of output: 817


@leynos, yes! ✅ This issue has been resolved.

The current implementation at lines 51-54 of tests/fixtures/budget_enforcement.rs now shows:

// Use a forgiving default so that scenarios which forget to call
// init_budgeted_state fail with a clear budget error rather than a
// confusing MessageTooLarge on any multi-byte body.
let max = NonZeroUsize::new(64 * 1024).unwrap_or(NonZeroUsize::MIN);

The default max_message_size is now 64 KiB (exactly as recommended in the original review comment), with a clear explanatory comment. This prevents confusing MessageTooLarge failures when test scenarios accept multi-byte bodies without explicitly initialising the budgeted state.

All review comments from the original code review have now been addressed. 🎉

🐇

     />o__o<\
    (  •ω• )
    />    <\
   Hooray!

…nection and in-flight budgets

Refactor budget enforcement tests to use parameterized rstest cases covering both connection and in-flight budget dimensions. Introduce helper functions for constructing states and asserting budget exceeded errors uniformly. This reduces duplicated code and improves test coverage. Additional cleanup in test comments and feature steps to parse budget config strings.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
@leynos leynos changed the title Implement per-connection memory budget enforcement (8.3.2) Implement memory budget enforcement across message assembly (8.3.2) Feb 23, 2026
@leynos leynos merged commit 14c35c4 into main Feb 23, 2026
6 checks passed
@leynos leynos deleted the budget-enforcement-memory-8-3-2-foue2u branch February 23, 2026 18:31
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