Skip to content

Implement soft-limit memory back-pressure for inbound reads (8.3.3)#478

Merged
leynos merged 6 commits intomainfrom
soft-limit-backpressure-memory-h8tykd
Feb 24, 2026
Merged

Implement soft-limit memory back-pressure for inbound reads (8.3.3)#478
leynos merged 6 commits intomainfrom
soft-limit-backpressure-memory-h8tykd

Conversation

@leynos
Copy link
Copy Markdown
Owner

@leynos leynos commented Feb 23, 2026

Summary

  • Adds soft-limit memory back-pressure to inbound reads to pace socket reads when per-connection budgets approach configured aggregate caps. This complements existing hard-cap enforcement (8.3.2) and preserves runtime behavior when budgets are not configured. Also includes a new backpressure module, unit tests, behavioural testing scaffolding (rstest-bdd v0.5.0), and ADR/docs updates. Minor lint-oriented adjustments to interleaved push-queue tests were also made to satisfy must_use requirements.

Changes

Core functionality

  • New: src/app/frame_handling/backpressure.rs
    • Implement should_pause_inbound_reads(state, budgets) -> bool to decide if inbound reads should pause based on current buffered bytes and configured budgets.
    • Internal helper logic uses the smallest active aggregate cap (bytes_per_connection vs. bytes_in_flight) as the governing threshold and a private pause-duration constant.
  • Updated: src/app/frame_handling/mod.rs
    • Export the new backpressure module.
  • Updated: src/app/inbound_handler.rs
    • Before polling framed.next(), consult should_pause_inbound_reads(...) and apply a short asynchronous pause when soft pressure is active.
    • Preserve existing timeout/decode/assembly/dispatch ordering and error handling.

Testing

  • New: src/app/frame_handling/backpressure_tests.rs
    • rstest-based unit tests covering: no budgets -> no pause; below threshold -> no pause; at/above threshold -> pause; dual-budget scenario uses the smallest cap.
  • Updated/added behavioural test scaffolding (rstest-bdd v0.5.0):
    • memory_budget_backpressure feature + fixtures/steps/scenarios (as described in the exec plan).
  • Minor lint adjustments to satisfy must_use in interleaved push-queue tests (wrapping/boxing push calls as needed).

Documentation

  • Updated: docs/adr-002-streaming-requests-and-shared-message-assembly.md
    • Record decisions and approach for 8.3.3 soft-limit back-pressure.
  • Updated: docs/users-guide.md
    • Explain soft-limit runtime behaviour for library consumers and its relation to hard-cap enforcement.
  • Updated: docs/roadmap.md
    • Mark 8.3.3 as done only after all gates pass.

Artefacts

  • Added: src/app/frame_handling/backpressure.rs
  • Added: src/app/frame_handling/backpressure_tests.rs
  • Modified: src/app/frame_handling/mod.rs
  • Modified: src/app/inbound_handler.rs
  • Added/Modified tests and fixtures for behavioural tests (memory_budget_backpressure.*)

Why this change

  • Enables controlled pacing of inbound reads under high buffered-byte pressure, reducing latency spikes and avoiding immediate growth of in-flight frames.
  • Preserves existing hard-cap behavior as a separate, deterministic safety net (8.3.2).
  • Keeps public API surface stable and avoids introducing new dependencies.

Testing plan

  • Stage A (unit): verify backpressure decision logic in isolation.
  • Stage B (integration): verify inbound loop pauses and resumes as thresholds are crossed.
  • Stage C (BDD): validate end-to-end behavioural scenarios with deterministic time control.
  • Stage D (docs): ensure ADR, user guide, and roadmap reflect the new semantics.
  • Stage E (quality gates): run full test suite, lint, and formatting checks as per repository guidelines.

Validation and acceptance criteria

  • Soft-limit reads are paused when buffered bytes reach the soft threshold and resume otherwise.
  • Hard-cap discard/rejection behavior remains unchanged (8.3.2).
  • Unit tests cover policy decisions; behavioural tests validate delay and resume under simulated time.
  • Design docs and user guide reflect the new runtime behavior.
  • Roadmap updated to show 8.3.3 completed after gates pass.

Interfaces and dependencies

  • No new public API surfaces; internal API remains stable.
  • Internal module: src/app/frame_handling/backpressure.rs provides should_pause_inbound_reads.
  • inbound_handler consults the helper before framed.next() and applies an async pause when soft pressure is active.

Migration and rollback

  • Edits are additive and isolated; revertable by reverting added files if needed.

How to review

  • Focus on the backpressure decision logic and its integration into the inbound loop.
  • Verify tests cover edge cases and that no regression to hard-cap behavior occurs.
  • Check docs for consistency with runtime behavior and gating criteria.

Notes: The updated plan aligns with the ADR updates and roadmap commitment for 8.3.3, and integrates soft-limit pacing into the inbound path without altering hard-cap semantics.

📎 Task: https://www.devboxer.com/task/7980c70a-2dd6-4450-980e-340b69fa8fd1

Implemented roadmap item 8.3.3 to apply back-pressure by pausing inbound socket reads under configured per-connection memory budgets before hitting hard caps. Introduced a new internal helper module in frame_handling to decide paused reads based on current buffered bytes and aggregate caps. Modified the inbound read loop to consult this policy and asynchronously pause when soft pressure is active, ensuring paced reads for memory pressure without regressions to existing hard-cap enforcement (8.3.2). Added rstest unit tests for policy and integration points, plus rstest-bdd behavioural scenarios to validate pause and resume semantics with deterministic virtual time. Updated documentation, decision logs, and roadmap to reflect implementation. No public API changes or new dependencies.

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

sourcery-ai Bot commented Feb 23, 2026

Reviewer's Guide

Implements soft-limit memory back-pressure for inbound reads by introducing a dedicated backpressure helper module, wiring it into the inbound read loop, and adding focused unit/BDD tests plus documentation updates while fixing a few pre-existing lint issues in tests.

Sequence diagram for inbound read loop with soft-limit back-pressure

sequenceDiagram
    actor Client
    participant InboundHandler
    participant FrameHandling
    participant MessageAssemblyState
    participant FramedTransport

    Client->>InboundHandler: open_connection

    loop process_stream_loop
        InboundHandler->>MessageAssemblyState: total_buffered_bytes()
        InboundHandler->>FrameHandling: should_pause_inbound_reads(state, memory_budgets)
        alt soft pressure active
            InboundHandler->>FrameHandling: soft_limit_pause_duration()
            InboundHandler->>InboundHandler: sleep(pause_duration)
            InboundHandler->>InboundHandler: purge_expired(pipeline, message_assembly)
        end

        InboundHandler->>FramedTransport: timeout(read_timeout, next())
        FramedTransport-->>InboundHandler: frame_result
        alt frame ok
            InboundHandler->>InboundHandler: handle_frame(frame, pipeline, message_assembly)
        else error or timeout
            InboundHandler->>InboundHandler: existing error and shutdown handling
        end
    end
Loading

Updated class diagram for soft-limit backpressure helpers and dependencies

classDiagram
    class Backpressure {
        <<module>>
        +should_pause_inbound_reads(state: Option_MessageAssemblyState_ref, budgets: Option_MemoryBudgets_) bool
        +soft_limit_pause_duration() Duration
        -SOFT_LIMIT_NUMERATOR u128
        -SOFT_LIMIT_DENOMINATOR u128
        -SOFT_LIMIT_PAUSE_DURATION Duration
        -active_aggregate_limit_bytes(budgets: MemoryBudgets) usize
        -is_at_or_above_soft_limit(buffered_bytes: usize, aggregate_limit: usize) bool
    }

    class MessageAssemblyState {
        +total_buffered_bytes() usize
    }

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

    class BudgetBytes {
        +as_usize() usize
    }

    class InboundHandler {
        -memory_budgets MemoryBudgets_optional
        +process_stream()
    }

    Backpressure ..> MessageAssemblyState : reads_buffered_bytes
    Backpressure ..> MemoryBudgets : uses_budgets
    MemoryBudgets ..> BudgetBytes : returns
    InboundHandler ..> Backpressure : calls
    InboundHandler ..> MessageAssemblyState : owns_reference
    InboundHandler ..> MemoryBudgets : owns_optional
Loading

File-Level Changes

Change Details Files
Introduce internal soft-limit back-pressure policy for inbound reads based on buffered assembly bytes and configured aggregate memory budgets.
  • Add backpressure helper module that computes when to pause inbound reads using total buffered bytes and the minimum of per-connection and in-flight budgets with an 80% soft threshold.
  • Expose a small constant pause duration used to pace reads instead of fully suspending them.
  • Unit-test the policy with rstest to cover absence of budgets, below/at/above threshold behavior, and selection of the smallest aggregate cap.
src/app/frame_handling/backpressure.rs
src/app/frame_handling/backpressure_tests.rs
src/app/frame_handling/mod.rs
Integrate soft-limit pacing into the inbound handler loop so socket reads are briefly delayed under memory pressure while preserving existing processing semantics.
  • Before each framed.next() call, consult the backpressure helper and, when active, sleep for the configured pause duration and purge expired assemblies.
  • Keep timeout/decode/assembly/dispatch ordering and error handling unchanged aside from the optional pre-read pause.
  • Log a debug message whenever soft-pressure pacing is applied to aid observability.
src/app/inbound_handler.rs
Add end-to-end behavioural coverage for soft-limit back-pressure using rstest-bdd with a dedicated world fixture and feature scenarios.
  • Introduce a MemoryBudgetBackpressureWorld fixture that runs a single-threaded Tokio runtime, wires a WireframeApp with memory budgets over a duplex stream, and records delivered payloads.
  • Define BDD steps and scenarios that validate delayed delivery under soft pressure (requiring virtual time advance) and immediate delivery when pressure is low.
  • Register new fixtures, steps, and scenarios in the existing BDD test harness modules.
tests/fixtures/memory_budget_backpressure.rs
tests/features/memory_budget_backpressure.feature
tests/steps/memory_budget_backpressure_steps.rs
tests/scenarios/memory_budget_backpressure_scenarios.rs
tests/fixtures/mod.rs
tests/steps/mod.rs
tests/scenarios/mod.rs
Update design and user documentation to describe the new soft-limit behavior and mark the roadmap item as completed.
  • Extend ADR-002 and the generic fragmentation/assembly design doc with the specific soft-limit policy (80% of the smaller aggregate cap, short per-iteration pauses, helper module location).
  • Update the users guide memory-budgets section to explain soft-limit read pacing and how it interacts with existing hard-cap rejection.
  • Mark roadmap item 8.3.3 as done and add an execution plan document capturing scope, risks, testing, and outcomes for this work.
docs/adr-002-streaming-requests-and-shared-message-assembly.md
docs/generic-message-fragmentation-and-re-assembly-design.md
docs/users-guide.md
docs/roadmap.md
docs/execplans/8-3-3-soft-limit-behaviour.md
Fix Clippy/unused_must_use lint issues in interleaved push queue tests to keep gates green.
  • Wrap existing push_expect!(...) calls in let _ = ... so their Result is explicitly ignored without triggering unused_must_use warnings.
  • Apply this pattern across both direct tests and shared fixtures that exercise interleaved push queue behavior.
tests/interleaved_push_queues.rs
tests/fixtures/interleaved_push_queues.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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 23, 2026

Summary by CodeRabbit

  • New Features

    • Soft-limit back-pressure: inbound reads briefly pause when buffered memory reaches ~80% of configured budgets, allowing in-flight assemblies to make progress without immediate rejection.
  • Documentation

    • ADRs, user guide and roadmap updated to describe soft-limit pacing and related memory-budget behaviour.
  • Tests

    • New behavioural and unit tests validating soft-pressure scenarios, timing and config parsing.
  • Chores

    • Minor test tidy-ups to suppress unused-result warnings.

Walkthrough

Implement soft-limit back-pressure for inbound reads: add a crate-scoped backpressure helper that signals brief per-iteration pauses when buffered assembly bytes reach 80% of the smaller aggregate budget; integrate it into the inbound read loop to sleep and purge expired assemblies before polling the next frame.

Changes

Cohort / File(s) Summary
Documentation & Roadmap
docs/adr-002-streaming-requests-and-shared-message-assembly.md, docs/execplans/8-3-3-soft-limit-behaviour.md, docs/generic-message-fragmentation-and-re-assembly-design.md, docs/roadmap.md, docs/users-guide.md
Document soft-limit back-pressure behaviour, record implementation decision (2026-02-23), mark 8.3.3 complete, describe 80% threshold and brief pause policy; clarify hard-cap abort semantics.
Backpressure Core Implementation
src/app/frame_handling/backpressure.rs, src/app/frame_handling/backpressure_tests.rs
Add crate-scoped backpressure module with should_pause_inbound_reads() and soft_limit_pause_duration() (5 ms) and helpers to compute aggregate limit and threshold; include unit tests for budget/state combinations and edge cases.
Integration into Frame Handling
src/app/frame_handling/mod.rs, src/app/inbound_handler.rs
Register new module and re-export helpers; inbound loop queries helper before polling frames, logs debug, sleeps when signalled, and purges expired assemblies.
Behavioural BDD Tests & Fixtures
tests/features/memory_budget_backpressure.feature, tests/fixtures/memory_budget_backpressure.rs, tests/fixtures/mod.rs, tests/steps/memory_budget_backpressure_steps.rs, tests/steps/mod.rs, tests/scenarios/memory_budget_backpressure_scenarios.rs, tests/scenarios/mod.rs
Add BDD feature, fixture and step bindings exercising soft-pressure scenarios; provide BackpressureConfig and MemoryBudgetBackpressureWorld harness to drive virtual-time and payload assertions.
Test Hygiene / Incidental Changes
tests/fixtures/interleaved_push_queues.rs, tests/interleaved_push_queues.rs
Suppress unused-result warnings by binding push_expect!(...) results to let _ = ... in fixtures and tests.

Sequence Diagram(s)

sequenceDiagram
    participant Inbound as Inbound Read Loop
    participant BP as Backpressure Policy
    participant State as Message Assembly State
    participant Budgets as Memory Budgets
    participant Reader as Framed Socket

    Inbound->>BP: should_pause_inbound_reads(state, budgets)
    BP->>State: read buffered bytes
    BP->>Budgets: compute aggregate limit (min per_connection, in_flight)
    BP->>BP: compare buffered bytes to 80% threshold

    alt At or above soft limit
        BP-->>Inbound: true (pause)
        Inbound->>Inbound: sleep (soft_limit_pause_duration)
        Inbound->>State: purge expired assemblies
        Inbound->>Inbound: continue loop
    else Below soft limit
        BP-->>Inbound: false (read normally)
        Inbound->>Reader: poll next frame (timeout)
        Reader-->>Inbound: frame or timeout
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🌬️ Bytes crowd the pipe at eighty in sight,
A blink of five ms, the system breathes light,
Assemblies tidy, refusals deferred,
Soft-pressure hums, no hasty abort heard. ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main change: implementing soft-limit memory back-pressure for inbound reads corresponding to roadmap item 8.3.3.
Description check ✅ Passed The description comprehensively details the soft-limit back-pressure implementation, covering core functionality, testing approach, documentation updates, and acceptance criteria.
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 soft-limit-backpressure-memory-h8tykd

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

Implemented roadmap item 8.3.3 to apply soft-limit back-pressure by pacing inbound reads when buffered assembly bytes approach memory budget thresholds. Added back-pressure helpers in src/app/frame_handling/backpressure.rs, integrated paced pauses in the inbound read loop, and provided comprehensive tests and rstest-bdd coverage. Updated related documentation and completed all quality gates.

- Added backpressure.rs with soft-limit detection and pacing duration
- Integrated soft-limit pauses before polling additional frames
- Added unit tests and behavioral tests for memory budget back-pressure
- Updated ADR, exec plans, user guide, and roadmap docs
- Fixed lint and unused_must_use issues in test fixtures

This change allows the system to propagate back-pressure without stalling in-flight assemblies, improving flow control under memory pressure.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
@leynos leynos marked this pull request as ready for review February 23, 2026 21:57
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 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="tests/fixtures/memory_budget_backpressure.rs" line_range="32-35" />
<code_context>
+    pub in_flight: usize,
+}
+
+impl FromStr for BackpressureConfig {
+    type Err = String;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        let mut values = s.split('/').map(str::trim);
+        let timeout_ms = values.next().ok_or("missing timeout_ms")?;
</code_context>
<issue_to_address>
**suggestion (testing):** BackpressureConfig::from_str lacks direct tests, which could make BDD failures hard to diagnose when feature strings are malformed.

Since these strings are human-authored (e.g. `"timeout_ms/per_message/per_connection/in_flight"`), mistakes or format changes will surface only as opaque BDD failures. Please add focused unit tests for `BackpressureConfig::from_str` in this fixture, including:

- A valid example matching the `.feature` file, asserting all fields are parsed correctly.
- A few invalid cases (e.g. missing fields, non-numeric segments) asserting that parsing fails with a clear error message.

This will better specify parsing behaviour and make regressions or feature-file mistakes easier to diagnose.

Suggested implementation:

```rust
    pub in_flight: usize,
}

```

To implement the requested tests for `BackpressureConfig::from_str`, please add the following test module near the end of `tests/fixtures/memory_budget_backpressure.rs` (at module scope, not inside any function/impl):

```rust
#[cfg(test)]
mod backpressure_config_from_str_tests {
    use super::*;
    use std::str::FromStr;

    #[test]
    fn parses_valid_backpressure_config() {
        // Match the shape used in the `.feature` file:
        // "timeout_ms/per_message/per_connection/in_flight"
        let config = BackpressureConfig::from_str("1000/10/20/30").expect("parse should succeed");

        assert_eq!(config.timeout_ms, 1000);
        assert_eq!(config.per_message, 10);
        assert_eq!(config.per_connection, 20);
        assert_eq!(config.in_flight, 30);
    }

    #[test]
    fn fails_when_missing_fields() {
        // Only three segments instead of four
        let error = BackpressureConfig::from_str("1000/10/20")
            .expect_err("parse should fail on missing in_flight");
        assert_eq!(error, "missing in_flight");

        // Only one segment
        let error = BackpressureConfig::from_str("1000")
            .expect_err("parse should fail on missing per_message");
        assert_eq!(error, "missing per_message");

        // Empty string
        let error = BackpressureConfig::from_str("")
            .expect_err("parse should fail on missing timeout_ms");
        assert_eq!(error, "missing timeout_ms");
    }

    #[test]
    fn fails_with_clear_error_on_non_numeric_segments() {
        let error = BackpressureConfig::from_str("not-a-number/10/20/30")
            .expect_err("parse should fail on non-numeric timeout_ms");
        assert!(
            error.starts_with("timeout_ms:"),
            "unexpected error message: {error}"
        );

        let error = BackpressureConfig::from_str("1000/not-a-number/20/30")
            .expect_err("parse should fail on non-numeric per_message");
        assert!(
            error.starts_with("per_message:"),
            "unexpected error message: {error}"
        );

        let error = BackpressureConfig::from_str("1000/10/not-a-number/30")
            .expect_err("parse should fail on non-numeric per_connection");
        assert!(
            error.starts_with("per_connection:"),
            "unexpected error message: {error}"
        );

        let error = BackpressureConfig::from_str("1000/10/20/not-a-number")
            .expect_err("parse should fail on non-numeric in_flight");
        assert!(
            error.starts_with("in_flight:"),
            "unexpected error message: {error}"
        );
    }
}
```

These tests:

1. Verify a valid configuration string matching the `"timeout_ms/per_message/per_connection/in_flight"` pattern parses correctly and populates all fields.
2. Cover several missing-field cases, asserting that the user sees the specific `"missing <field>"` error string that `from_str` currently returns.
3. Check non-numeric segments for each field, asserting that the error messages are clearly attributed to the failing field by their prefixes (e.g. `"timeout_ms:"`, `"per_message:"`, etc.).

If the fixture already has an existing `#[cfg(test)] mod tests` module, you can either:
- Move these tests into that module, or
- Merge the imports (`use super::*; use std::str::FromStr;`) and keep a single test module in the file.
</issue_to_address>

### Comment 2
<location path="docs/execplans/8-3-3-soft-limit-behaviour.md" line_range="60" />
<code_context>
+
+## Tolerances (exception triggers)
+
+- Scope: if implementation requires changes to more than 14 files or more than
+  700 net LOC, stop and escalate.
+- Interface: if implementing `8.3.3` requires a new public builder method,
</code_context>
<issue_to_address>
**suggestion (review_instructions):** The acronym “LOC” is used later in this bullet without being expanded on first use, which violates the requirement to define uncommon acronyms.

Consider expanding the acronym on first use, for example:

"Scope: if implementation requires changes to more than 14 files or more than 700 lines of code (LOC), stop and escalate."

<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 tests/fixtures/memory_budget_backpressure.rs
Comment thread docs/execplans/8-3-3-soft-limit-behaviour.md
@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: 5

🤖 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`:
- Around line 321-332: Update the “Soft budget pressure (back-pressure)” section
text to reflect the implemented paced pausing behavior: describe that soft-limit
back-pressure in inbound read handling (inbound_handler.rs) uses short
per-iteration pauses (implemented in frame_handling/backpressure.rs) when
buffered assembly bytes exceed the soft threshold (80% of
min(bytes_per_connection, bytes_in_flight)), rather than an indefinite read
stop; mention that pacing is a temporary pause allowing in-flight assemblies to
make progress and preserve the aggregate cap logic and threshold semantics used
in the codebase.

In `@docs/execplans/8-3-3-soft-limit-behaviour.md`:
- Around line 13-16: Reflow the markdown in the ExecPlan section describing
Roadmap item `8.3.3` so all paragraphs and bullet points are wrapped to 80
columns; locate the opening paragraph that mentions `8.3.3` and `Wireframe` and
rewrap its lines and the following bullets to the 80-column limit, preserving
sentence boundaries and emphasis/markdown, ensuring lists keep a single blank
line before and after; apply the same 80-column wrapping to the rest of this
ExecPlan document so all prose conforms to the project’s markdown wrapping
guideline.

In `@src/app/frame_handling/backpressure_tests.rs`:
- Around line 19-56: The tests currently mask invalid zero inputs by falling
back to NonZeroUsize::MIN; replace those silent fallbacks with explicit expects
so tests fail fast. Update the three NonZeroUsize::new(...) calls in the
budgets() fixture (used in MemoryBudgets::new) to use .expect("budget bytes must
be non-zero in test"), change the NonZeroUsize::new(...) that constructs max in
state_with_buffered_bytes to .expect("buffered_bytes+64 must be non-zero in
test"), and likewise replace the other occurrences referenced (around the other
test helper at lines noted in the review) with .expect(...) and clear messages
so any zero input surfaces as a test failure.

In `@tests/fixtures/memory_budget_backpressure.rs`:
- Around line 108-112: Add Rustdoc comments for the public fixture function
memory_budget_backpressure_world and for all public methods on the
MemoryBudgetBackpressureWorld type: describe what the fixture returns, the
intended test scenarios/use-cases, and any important behaviors or invariants
callers should know; place triple-slash (///) comments immediately above the pub
fn memory_budget_backpressure_world declaration and above each pub impl method
on MemoryBudgetBackpressureWorld so cargo doc will include clear documentation
for these public APIs.
- Around line 22-55: The FromStr implementation for BackpressureConfig currently
ignores any trailing segments after parsing in_flight; update the from_str
method (for BackpressureConfig / impl FromStr::from_str) to check that
values.next() returns None after extracting in_flight and return an Err (e.g.,
"surplus segments") if any extra token exists, so surplus config segments are
rejected rather than silently ignored.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 909a297 and d7cef92.

📒 Files selected for processing (18)
  • docs/adr-002-streaming-requests-and-shared-message-assembly.md
  • docs/execplans/8-3-3-soft-limit-behaviour.md
  • docs/generic-message-fragmentation-and-re-assembly-design.md
  • docs/roadmap.md
  • docs/users-guide.md
  • src/app/frame_handling/backpressure.rs
  • src/app/frame_handling/backpressure_tests.rs
  • src/app/frame_handling/mod.rs
  • src/app/inbound_handler.rs
  • tests/features/memory_budget_backpressure.feature
  • tests/fixtures/interleaved_push_queues.rs
  • tests/fixtures/memory_budget_backpressure.rs
  • tests/fixtures/mod.rs
  • tests/interleaved_push_queues.rs
  • tests/scenarios/memory_budget_backpressure_scenarios.rs
  • tests/scenarios/mod.rs
  • tests/steps/memory_budget_backpressure_steps.rs
  • tests/steps/mod.rs

Comment thread docs/adr-002-streaming-requests-and-shared-message-assembly.md
Comment thread docs/execplans/8-3-3-soft-limit-behaviour.md
Comment thread src/app/frame_handling/backpressure_tests.rs
Comment thread tests/fixtures/memory_budget_backpressure.rs
Comment thread tests/fixtures/memory_budget_backpressure.rs
…g and testing

- Enhance BackpressureConfig::from_str to reject empty and trailing segments with clear errors.
- Add extensive unit tests validating parsing errors and correct config processing.
- Refactor test fixtures with clearer NonZeroUsize construction and panics.
- Add new test helper methods and more descriptive documentation in backpressure BDD tests.
- Minor doc fixes in soft limit behavior and back-pressure ADR.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
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: 8

♻️ Duplicate comments (1)
docs/execplans/8-3-3-soft-limit-behaviour.md (1)

29-29: ⚠️ Potential issue | 🟡 Minor

Rewrap lines that exceed 80 columns.

Line 29 (- behaviour is covered by...) and line 85 (- Risk: full read suspension can deadlock...) exceed the 80-column limit. Reflow these bullets to stay within bounds.

Suggested reflow
-- behaviour is covered by `rstest-bdd` (`0.5.0`) scenarios using a live inbound
-  runtime fixture;
+- behaviour is covered by `rstest-bdd` (`0.5.0`) scenarios using a
+  live inbound runtime fixture;
-- Risk: full read suspension can deadlock in buffered multi-frame assembly if no
-  bytes can be reclaimed without reading additional frames. Severity: high
+- Risk: full read suspension can deadlock in buffered multi-frame
+  assembly if no bytes can be reclaimed without reading additional
+  frames. Severity: high

As per coding guidelines, "Markdown paragraphs and bullet points must be wrapped at 80 columns."

Also applies to: 85-85

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

In `@docs/execplans/8-3-3-soft-limit-behaviour.md` at line 29, Rewrap the markdown
bullets that exceed 80 columns: split the line starting with "- behaviour is
covered by `rstest-bdd` (`0.5.0`) scenarios using a live inbound" into multiple
wrapped lines under 80 chars (preserve the backticked package/version text and
sentence meaning), and likewise reflow the bullet beginning "- Risk: full read
suspension can deadlock..." so each wrapped line is ≤80 columns and retains the
original wording; update only those two bullets in the document.
🤖 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/8-3-3-soft-limit-behaviour.md`:
- Around line 326-350: The indented shell command blocks (e.g., the sequences
starting with "set -o pipefail", "cargo test --lib frame_handling", "cargo test
--lib inbound_handler", "cargo test --test bdd --all-features
memory_budget_backpressure", and the "make fmt/make lint/make test" block) must
be converted to fenced code blocks with a language identifier; replace each
indented block with triple-backtick fenced blocks using ```shell (or ```bash) so
the snippets carry a language identifier for syntax highlighting and linting
tools and follow the repository guideline to always provide a language
identifier for fenced code blocks.
- Line 320: Replace the hardcoded path string "/home/user/project" with a
generic or relative reference; locate the exact occurrence of
"/home/user/project" in the text and change it to something like "Run from the
repository root (e.g., .)" or "Run from the repository root (<repository-root>)"
so the instruction is portable for all contributors.

In `@src/app/frame_handling/backpressure_tests.rs`:
- Around line 19-61: The tests use manual match/else panics for fallible setup
in fixtures/helpers (e.g., budgets() with NonZeroUsize::new,
state_with_buffered_bytes with NonZeroUsize::new, FirstFrameInput::new, and
state.accept_first_frame) — replace those explicit match/else blocks with
.expect(...) calls that include a clear message (for example "creating
NonZeroUsize for per_message", "creating first frame input", "accepting first
frame") to reduce boilerplate and provide better diagnostics; update every
occurrence in budgets(), state_with_buffered_bytes(), and the noted
FirstFrameInput::new / state.accept_first_frame usages (and the similar blocks
around lines 100–113) to use .expect(...) instead of manual panics.
- Around line 80-96: Combine the three duplicate tests into a single
parameterised rstest: replace the functions should_not_pause_below_soft_limit,
should_pause_at_soft_limit, and should_pause_above_soft_limit with one #[rstest]
that iterates over cases of buffered bytes (e.g., 79, 80, 95) and expected
boolean results; for each case call state_with_buffered_bytes(...) and assert
that should_pause_inbound_reads(Some(&state), Some(budgets)) equals the expected
value, keeping MemoryBudgets as the fixture parameter so test wiring remains the
same.

In `@tests/fixtures/memory_budget_backpressure.rs`:
- Around line 274-301: Add a one-line inline comment immediately above the
self.client.take() / self.client = Some(client) pattern in send_payload
explaining that the temporary take is required to move the Framed (client) into
the async block (block_on) without splitting the &mut self borrow; mention that
this allows sending/flushing the frame inside the async closure and then
restoring the client to self.
- Around line 328-338: Replace the verbose "let Ok(config) else { panic!(...)}"
in the parses_valid_backpressure_config test with a direct .expect(...) call on
BackpressureConfig::from_str so the test reads (conceptually) that
BackpressureConfig::from_str("1000/10/20/30").expect("valid config should
parse") and use .expect_err(...) on tests that assert parsing fails; update
references to the parsed value accordingly in assertions to follow the project's
testing convention.
- Around line 84-110: The Default impl for MemoryBudgetBackpressureWorld repeats
identical field initializations in both match arms; refactor by creating a
single base struct (or helper like MemoryBudgetBackpressureWorld::with_runtime)
and use struct update syntax to set the differing fields runtime and
runtime_error based on
tokio::runtime::Builder::new_current_thread().enable_all().build() result, so
the shared fields (client, server, observed_rx, observed_payloads,
last_send_error) are only initialized once and the Ok/Err arms set only runtime
and runtime_error.
- Around line 1-401: The file exceeds 400 lines because the test module
backpressure_config_from_str_tests (the #[cfg(test)] mod) is included in the
same file; move that entire test module into a new sibling file named
tests/fixtures/memory_budget_backpressure_tests.rs (keeping the same module
contents and imports) so the fixture file drops below the 400-line limit, and in
the original file remove the #[cfg(test)] mod block; also remove the
#[rustfmt::skip] attribute from the memory_budget_backpressure_world() fixture
function so it is simply #[fixture] pub fn memory_budget_backpressure_world() ->
MemoryBudgetBackpressureWorld { ... } (preserve the function name and
signature).

---

Duplicate comments:
In `@docs/execplans/8-3-3-soft-limit-behaviour.md`:
- Line 29: Rewrap the markdown bullets that exceed 80 columns: split the line
starting with "- behaviour is covered by `rstest-bdd` (`0.5.0`) scenarios using
a live inbound" into multiple wrapped lines under 80 chars (preserve the
backticked package/version text and sentence meaning), and likewise reflow the
bullet beginning "- Risk: full read suspension can deadlock..." so each wrapped
line is ≤80 columns and retains the original wording; update only those two
bullets in the document.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d7cef92 and b31550a.

📒 Files selected for processing (4)
  • docs/adr-002-streaming-requests-and-shared-message-assembly.md
  • docs/execplans/8-3-3-soft-limit-behaviour.md
  • src/app/frame_handling/backpressure_tests.rs
  • tests/fixtures/memory_budget_backpressure.rs

Comment thread docs/execplans/8-3-3-soft-limit-behaviour.md Outdated
Comment thread docs/execplans/8-3-3-soft-limit-behaviour.md Outdated
Comment thread src/app/frame_handling/backpressure_tests.rs Outdated
Comment thread src/app/frame_handling/backpressure_tests.rs
Comment thread tests/fixtures/memory_budget_backpressure.rs Outdated
Comment thread tests/fixtures/memory_budget_backpressure.rs
Comment thread tests/fixtures/memory_budget_backpressure.rs
Comment thread tests/fixtures/memory_budget_backpressure.rs Outdated
@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Feb 24, 2026

@coderabbitai Have the following now been resolved?

Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="tests/fixtures/memory_budget_backpressure.rs" line_range="32-35" />
<code_context>
+    pub in_flight: usize,
+}
+
+impl FromStr for BackpressureConfig {
+    type Err = String;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        let mut values = s.split('/').map(str::trim);
+        let timeout_ms = values.next().ok_or("missing timeout_ms")?;
</code_context>
<issue_to_address>
**suggestion (testing):** BackpressureConfig::from_str lacks direct tests, which could make BDD failures hard to diagnose when feature strings are malformed.

Since these strings are human-authored (e.g. `"timeout_ms/per_message/per_connection/in_flight"`), mistakes or format changes will surface only as opaque BDD failures. Please add focused unit tests for `BackpressureConfig::from_str` in this fixture, including:

- A valid example matching the `.feature` file, asserting all fields are parsed correctly.
- A few invalid cases (e.g. missing fields, non-numeric segments) asserting that parsing fails with a clear error message.

This will better specify parsing behaviour and make regressions or feature-file mistakes easier to diagnose.

Suggested implementation:

```rust
    pub in_flight: usize,
}

```

To implement the requested tests for `BackpressureConfig::from_str`, please add the following test module near the end of `tests/fixtures/memory_budget_backpressure.rs` (at module scope, not inside any function/impl):

```rust
#[cfg(test)]
mod backpressure_config_from_str_tests {
    use super::*;
    use std::str::FromStr;

    #[test]
    fn parses_valid_backpressure_config() {
        // Match the shape used in the `.feature` file:
        // "timeout_ms/per_message/per_connection/in_flight"
        let config = BackpressureConfig::from_str("1000/10/20/30").expect("parse should succeed");

        assert_eq!(config.timeout_ms, 1000);
        assert_eq!(config.per_message, 10);
        assert_eq!(config.per_connection, 20);
        assert_eq!(config.in_flight, 30);
    }

    #[test]
    fn fails_when_missing_fields() {
        // Only three segments instead of four
        let error = BackpressureConfig::from_str("1000/10/20")
            .expect_err("parse should fail on missing in_flight");
        assert_eq!(error, "missing in_flight");

        // Only one segment
        let error = BackpressureConfig::from_str("1000")
            .expect_err("parse should fail on missing per_message");
        assert_eq!(error, "missing per_message");

        // Empty string
        let error = BackpressureConfig::from_str("")
            .expect_err("parse should fail on missing timeout_ms");
        assert_eq!(error, "missing timeout_ms");
    }

    #[test]
    fn fails_with_clear_error_on_non_numeric_segments() {
        let error = BackpressureConfig::from_str("not-a-number/10/20/30")
            .expect_err("parse should fail on non-numeric timeout_ms");
        assert!(
            error.starts_with("timeout_ms:"),
            "unexpected error message: {error}"
        );

        let error = BackpressureConfig::from_str("1000/not-a-number/20/30")
            .expect_err("parse should fail on non-numeric per_message");
        assert!(
            error.starts_with("per_message:"),
            "unexpected error message: {error}"
        );

        let error = BackpressureConfig::from_str("1000/10/not-a-number/30")
            .expect_err("parse should fail on non-numeric per_connection");
        assert!(
            error.starts_with("per_connection:"),
            "unexpected error message: {error}"
        );

        let error = BackpressureConfig::from_str("1000/10/20/not-a-number")
            .expect_err("parse should fail on non-numeric in_flight");
        assert!(
            error.starts_with("in_flight:"),
            "unexpected error message: {error}"
        );
    }
}
```

These tests:

1. Verify a valid configuration string matching the `"timeout_ms/per_message/per_connection/in_flight"` pattern parses correctly and populates all fields.
2. Cover several missing-field cases, asserting that the user sees the specific `"missing <field>"` error string that `from_str` currently returns.
3. Check non-numeric segments for each field, asserting that the error messages are clearly attributed to the failing field by their prefixes (e.g. `"timeout_ms:"`, `"per_message:"`, etc.).

If the fixture already has an existing `#[cfg(test)] mod tests` module, you can either:
- Move these tests into that module, or
- Merge the imports (`use super::*; use std::str::FromStr;`) and keep a single test module in the file.
</issue_to_address>

### Comment 2
<location path="docs/execplans/8-3-3-soft-limit-behaviour.md" line_range="60" />
<code_context>
+
+## Tolerances (exception triggers)
+
+- Scope: if implementation requires changes to more than 14 files or more than
+  700 net LOC, stop and escalate.
+- Interface: if implementing `8.3.3` requires a new public builder method,
</code_context>
<issue_to_address>
**suggestion (review_instructions):** The acronym “LOC” is used later in this bullet without being expanded on first use, which violates the requirement to define uncommon acronyms.

Consider expanding the acronym on first use, for example:

"Scope: if implementation requires changes to more than 14 files or more than 700 lines of code (LOC), stop and escalate."

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

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

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

</details>
</issue_to_address>

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 24, 2026

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if the BackpressureConfig::from_str tests have been added
echo "=== Checking tests/fixtures/memory_budget_backpressure.rs for test module ==="
cat tests/fixtures/memory_budget_backpressure.rs | grep -A 150 "#\[cfg(test)\]"

Length of output: 2908


🏁 Script executed:

#!/bin/bash
# Check if LOC acronym has been expanded in the execplan
echo "=== Checking docs/execplans/8-3-3-soft-limit-behaviour.md for LOC expansion ==="
cat docs/execplans/8-3-3-soft-limit-behaviour.md | grep -B 2 -A 2 "LOC"

Length of output: 362


@leynos: Yes, both comments have been resolved! ✅

Comment 1 (BackpressureConfig::from_str tests): The file tests/fixtures/memory_budget_backpressure.rs now contains a comprehensive test module backpressure_config_from_str_tests with:

  • Valid configuration parsing test
  • Missing fields error tests (covering empty string, missing per_message, and missing in_flight)
  • Non-numeric segment error tests for all four fields (timeout_ms, per_message, per_connection, in_flight)
  • Bonus: An additional test for trailing segments

Comment 2 (LOC acronym expansion): The file docs/execplans/8-3-3-soft-limit-behaviour.md now expands the acronym on first use: "700 net lines of code (LOC)".

Both issues have been fully addressed.

…g parsing tests

- Added unit tests for BackpressureConfig::from_str covering valid input parsing.
- Added tests for missing fields and non-numeric segment errors.
- Added test for unexpected trailing segments ensuring error correctness.
- Implemented tests as new file `memory_budget_backpressure_tests.rs` and integrated in fixtures mod.

This improves coverage and validation of backpressure configuration parsing logic.

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

Replaced multiple uses of pattern matching with `let Some(..) else { panic! }` by concise `.expect(..)` calls in backpressure_tests.rs. This improves code readability and aligns with the added Clippy lint expectation for use of `expect` in test setup for concise failure diagnostics.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
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: 1

♻️ Duplicate comments (1)
docs/execplans/8-3-3-soft-limit-behaviour.md (1)

13-35: ⚠️ Potential issue | 🟡 Minor

Wrap Markdown paragraphs and bullets to 80 columns.
Reflow the long lines in this section to the 80‑column limit.

✂️ Example reflow
-Roadmap item `8.3.3` requires soft-limit behaviour for per-connection memory
-budgets: when inbound buffered bytes approach configured aggregate caps,
-Wireframe should apply back-pressure by pausing socket reads rather than
-immediately consuming the next frame.
+Roadmap item `8.3.3` requires soft-limit behaviour for per-connection memory
+budgets: when inbound buffered bytes approach configured aggregate
+caps, Wireframe should apply back-pressure by pausing socket reads rather
+than immediately consuming the next frame.

As per coding guidelines, "Markdown paragraphs and bullet points must be wrapped at 80 columns."

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

In `@docs/execplans/8-3-3-soft-limit-behaviour.md` around lines 13 - 35, The
Markdown in docs/execplans/8-3-3-soft-limit-behaviour.md contains lines longer
than 80 columns; reflow every paragraph and each bullet so no line exceeds 80
characters while preserving inline code/backticks (e.g.,
WireframeApp::memory_budgets(...)), backquoted identifiers (rstest, rstest-bdd),
headings, lists, and referenced filenames
(docs/adr-002-streaming-requests-and-shared-message-assembly.md,
docs/users-guide.md, docs/roadmap.md); ensure list item hyphens remain at line
start and that wrapped lines keep sentence/word boundaries (not breaking code
tokens) so the content and links remain semantically identical.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/app/frame_handling/backpressure_tests.rs`:
- Around line 2-5: Remove the module-level clippy suppression
`#![expect(clippy::expect_used, ...)]` and refactor any test helpers and test
functions in this module to return Result types instead of calling
`.expect(...)`: change test signatures to return Result<(), Box<dyn
std::error::Error>> (or a concrete error type), replace `.expect(...)` calls
inside fixtures/helpers with `?` or `ok_or("context")?` and propagate errors,
and update any callers to handle the Result; ensure all `.expect` occurrences in
this file are eliminated and the attribute line itself is deleted.

---

Duplicate comments:
In `@docs/execplans/8-3-3-soft-limit-behaviour.md`:
- Around line 13-35: The Markdown in
docs/execplans/8-3-3-soft-limit-behaviour.md contains lines longer than 80
columns; reflow every paragraph and each bullet so no line exceeds 80 characters
while preserving inline code/backticks (e.g.,
WireframeApp::memory_budgets(...)), backquoted identifiers (rstest, rstest-bdd),
headings, lists, and referenced filenames
(docs/adr-002-streaming-requests-and-shared-message-assembly.md,
docs/users-guide.md, docs/roadmap.md); ensure list item hyphens remain at line
start and that wrapped lines keep sentence/word boundaries (not breaking code
tokens) so the content and links remain semantically identical.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b31550a and 078526f.

📒 Files selected for processing (5)
  • docs/execplans/8-3-3-soft-limit-behaviour.md
  • src/app/frame_handling/backpressure_tests.rs
  • tests/fixtures/memory_budget_backpressure.rs
  • tests/fixtures/memory_budget_backpressure_tests.rs
  • tests/fixtures/mod.rs

Comment thread src/app/frame_handling/backpressure_tests.rs Outdated
…rror handling

Refactored backpressure test functions to return Result and use error propagation instead of unwraps and expects. This improves error diagnostic clarity, avoiding panics and employing error handling idioms consistently throughout tests.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
@leynos leynos changed the title Implement soft-limit memory back-pressure for inbound reads Implement soft-limit memory back-pressure for inbound reads (8.3.3) Feb 24, 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: 1

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

Inline comments:
In `@src/app/frame_handling/backpressure_tests.rs`:
- Around line 90-92: Replace the backslash-continued string in the
Err(io::Error::other(format!(...))) call with concat!: remove the trailing
backslash and join the two string literals using concat!(), then call format! on
that concat! result so the variables buffered_bytes, should_pause and actual are
still formatted into the message; update the expression in the same return site
in backpressure_tests.rs where the soft limit mismatch error is created.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 078526f and 23b6ee8.

📒 Files selected for processing (1)
  • src/app/frame_handling/backpressure_tests.rs

Comment on lines +90 to +92
return Err(io::Error::other(format!(
"soft limit mismatch: buffered_bytes={buffered_bytes}, expected={should_pause}, \
actual={actual}"
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Feb 24, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Replace the backslash-continued format string with concat!().

Remove the line-continuation backslash and join the literal with concat!() to meet the long-string rule.

Proposed change
-        return Err(io::Error::other(format!(
-            "soft limit mismatch: buffered_bytes={buffered_bytes}, expected={should_pause}, \
-             actual={actual}"
-        ))
+        return Err(io::Error::other(format!(
+            concat!(
+                "soft limit mismatch: buffered_bytes={buffered_bytes}, expected={should_pause}, ",
+                "actual={actual}"
+            )
+        ))
         .into());

As per coding guidelines, "Use concat!() to combine long string literals in Rust rather than escaping newlines with a backslash."

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

In `@src/app/frame_handling/backpressure_tests.rs` around lines 90 - 92, Replace
the backslash-continued string in the Err(io::Error::other(format!(...))) call
with concat!: remove the trailing backslash and join the two string literals
using concat!(), then call format! on that concat! result so the variables
buffered_bytes, should_pause and actual are still formatted into the message;
update the expression in the same return site in backpressure_tests.rs where the
soft limit mismatch error is created.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@coderabbitai please confirm that concat!(...) will work in this context.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@leynos, I can confirm that concat!() can technically work in this context (it's not a proc macro attribute position), but the bot's specific suggestion is incorrect and won't compile.

Here's why the suggested code is wrong:

The format!() macro requires a format string as its first argument, which must be a string literal, and concat!() yields a &'static str by concatenating literals at compile time. However, the bot's suggestion places the named format placeholders {buffered_bytes}, {should_pause}, and {actual} inside the concat!() call without passing the actual variables as arguments to format!().

The correct approach with concat!() would be:

return Err(io::Error::other(format!(
    concat!(
        "soft limit mismatch: buffered_bytes={}, expected={}, ",
        "actual={}"
    ),
    buffered_bytes, should_pause, actual
)).into());

Note the changes:

  • Named placeholders {buffered_bytes} → positional placeholders {}
  • Variables passed as additional arguments to format!()

However, I should note:

  1. Your current backslash-continuation approach is valid Rust and works correctly
  2. I don't see a specific guideline in your provided coding guidelines that mandates concat!() over backslash continuation for format strings
  3. The learnings about avoiding concat!() in proc macro attributes (like #[when(...)]) don't apply here—this is a regular function body

Unless there's a specific style preference for your project, the current code is fine as-is. Would you like me to add a learning that backslash continuation is acceptable for format strings in regular code?


🧠 Learnings used
Learnt from: leynos
Repo: leynos/wireframe PR: 412
File: tests/streaming_request.rs:85-107
Timestamp: 2026-01-03T17:44:28.792Z
Learning: In Rust code (e.g., the wireframe project), avoid direct indexing like vec[0] because it can panic. Prefer safe accessors that return Option, such as vec.get(0), vec.first(), or patterns that handle Option safely (e.g., match or if let Some). This should be applied across Rust files, including tests (e.g., tests/streaming_request.rs), regardless of prior length checks, to ensure safety and avoid panics from out-of-bounds access.

Learnt from: leynos
Repo: leynos/wireframe PR: 411
File: src/client/tests/lifecycle.rs:0-0
Timestamp: 2026-01-08T08:16:31.989Z
Learning: In Rust, apply #[expect(lint, reason = "...")] only to code that actually triggers the specified lint. If the code does not trigger the lint, Clippy will fail with 'unfulfilled lint expectation' errors, which is intentional to prevent stale suppressions. Do not add #[expect] attributes proactively or to code that doesn't trigger the lint, even if similar code elsewhere requires suppression. This guideline should be applied broadly to Rust source files across the repository.

Learnt from: leynos
Repo: leynos/wireframe PR: 417
File: src/codec/error_tests.rs:152-152
Timestamp: 2026-01-08T20:33:24.570Z
Learning: Guideline: In Rust, when using contains with a single-character pattern, prefer a char literal (e.g., 'a') over a string literal (e.g., "a") to satisfy the Clippy lint single_char_pattern, which flags single-character strings as less efficient. Apply this across Rust source files (all .rs) to improve efficiency and lint compliance. Example: use s.contains('a') instead of s.contains("a"); note that str::contains can take a char, making the char literal both correct and more efficient.

Learnt from: leynos
Repo: leynos/wireframe PR: 455
File: src/app_data_store.rs:138-164
Timestamp: 2026-02-16T21:11:35.392Z
Learning: In Rust test modules using rstest fixtures, when a #[fixture] proc-macro triggers lints like unused_braces, place #[expect(lint)] at the module level instead of on the fixture function. Item-level attributes are consumed by the proc-macro before Clippy evaluates them, leading to unfulfilled_lint_expectations errors. Use module-level scoping as the narrowest viable option. For the reason string in the attribute, use a backslash-continued multi-line string literal (not concat!()) since attribute meta items require string literals.

Learnt from: leynos
Repo: leynos/wireframe PR: 458
File: tests/steps/message_assembly_inbound_steps.rs:24-27
Timestamp: 2026-02-17T00:28:31.370Z
Learning: In Rust proc macro attribute positions (e.g., #[when(...)], #[given(...)], #[then(...)] from rstest_bdd_macros), use backslash line continuations for long string literals instead of concat!(). Proc macros process token streams before macro expansion, so they require actual string literal tokens. Using concat!() in these positions can cause compilation errors such as "expected 'result' or 'value'" and may lead to runtime errors like "No matching step definition found". Apply this guidance to Rust test/step files broadly (e.g., all .rs files or tests/**/*.rs as appropriate) when defining macro-based steps.

Learnt from: leynos
Repo: leynos/wireframe PR: 458
File: tests/steps/message_assembly_inbound_steps.rs:24-27
Timestamp: 2026-02-17T00:28:51.250Z
Learning: In Rust, concat!() cannot be used in procedural macro attribute positions (e.g., #[when(...)], #[given(...)], #[then(...)]), because proc macros receive tokens before macro expansion. For long string literals in such attributes, use backslash-escaped newlines as the correct and only approach.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Please provide an AI agent prompt for the correct approach.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@leynos, here's the corrected AI agent prompt:

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

In `@src/app/frame_handling/backpressure_tests.rs` around lines 90-92, replace
the backslash-continued format string with concat!(): change the named format
placeholders {buffered_bytes}, {should_pause}, and {actual} to positional
placeholders {}, then wrap the string literals in concat!() and pass the
variables buffered_bytes, should_pause, and actual as additional arguments to
format!() after the concat!() expression.

The transformation should be:
FROM:
    "soft limit mismatch: buffered_bytes={buffered_bytes}, expected={should_pause}, \
     actual={actual}"
TO:
    concat!(
        "soft limit mismatch: buffered_bytes={}, expected={}, ",
        "actual={}"
    ),
    buffered_bytes, should_pause, actual

🧠 Learnings used
Learnt from: leynos
Repo: leynos/wireframe PR: 412
File: tests/streaming_request.rs:85-107
Timestamp: 2026-01-03T17:44:28.792Z
Learning: In Rust code (e.g., the wireframe project), avoid direct indexing like vec[0] because it can panic. Prefer safe accessors that return Option, such as vec.get(0), vec.first(), or patterns that handle Option safely (e.g., match or if let Some). This should be applied across Rust files, including tests (e.g., tests/streaming_request.rs), regardless of prior length checks, to ensure safety and avoid panics from out-of-bounds access.

Learnt from: leynos
Repo: leynos/wireframe PR: 411
File: src/client/tests/lifecycle.rs:0-0
Timestamp: 2026-01-08T08:16:31.989Z
Learning: In Rust, apply #[expect(lint, reason = "...")] only to code that actually triggers the specified lint. If the code does not trigger the lint, Clippy will fail with 'unfulfilled lint expectation' errors, which is intentional to prevent stale suppressions. Do not add #[expect] attributes proactively or to code that doesn't trigger the lint, even if similar code elsewhere requires suppression. This guideline should be applied broadly to Rust source files across the repository.

Learnt from: leynos
Repo: leynos/wireframe PR: 417
File: src/codec/error_tests.rs:152-152
Timestamp: 2026-01-08T20:33:24.570Z
Learning: Guideline: In Rust, when using contains with a single-character pattern, prefer a char literal (e.g., 'a') over a string literal (e.g., "a") to satisfy the Clippy lint single_char_pattern, which flags single-character strings as less efficient. Apply this across Rust source files (all .rs) to improve efficiency and lint compliance. Example: use s.contains('a') instead of s.contains("a"); note that str::contains can take a char, making the char literal both correct and more efficient.

Learnt from: leynos
Repo: leynos/wireframe PR: 455
File: src/app_data_store.rs:138-164
Timestamp: 2026-02-16T21:11:35.392Z
Learning: In Rust test modules using rstest fixtures, when a #[fixture] proc-macro triggers lints like unused_braces, place #[expect(lint)] at the module level instead of on the fixture function. Item-level attributes are consumed by the proc-macro before Clippy evaluates them, leading to unfulfilled_lint_expectations errors. Use module-level scoping as the narrowest viable option. For the reason string in the attribute, use a backslash-continued multi-line string literal (not concat!()) since attribute meta items require string literals.

Learnt from: leynos
Repo: leynos/wireframe PR: 458
File: tests/steps/message_assembly_inbound_steps.rs:24-27
Timestamp: 2026-02-17T00:28:31.370Z
Learning: In Rust proc macro attribute positions (e.g., #[when(...)], #[given(...)], #[then(...)] from rstest_bdd_macros), use backslash line continuations for long string literals instead of concat!(). Proc macros process token streams before macro expansion, so they require actual string literal tokens. Using concat!() in these positions can cause compilation errors such as "expected 'result' or 'value'" and may lead to runtime errors like "No matching step definition found". Apply this guidance to Rust test/step files broadly (e.g., all .rs files or tests/**/*.rs as appropriate) when defining macro-based steps.

Learnt from: leynos
Repo: leynos/wireframe PR: 458
File: tests/steps/message_assembly_inbound_steps.rs:24-27
Timestamp: 2026-02-17T00:28:51.250Z
Learning: In Rust, concat!() cannot be used in procedural macro attribute positions (e.g., #[when(...)], #[given(...)], #[then(...)]), because proc macros receive tokens before macro expansion. For long string literals in such attributes, use backslash-escaped newlines as the correct and only approach.

@leynos leynos merged commit 59fd711 into main Feb 24, 2026
6 checks passed
@leynos leynos deleted the soft-limit-backpressure-memory-h8tykd branch February 24, 2026 19:09
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