Skip to content

Implement push queue DLQ#198

Merged
leynos merged 9 commits intomainfrom
codex/implement-optional-dead-letter-queue
Jul 10, 2025
Merged

Implement push queue DLQ#198
leynos merged 9 commits intomainfrom
codex/implement-optional-dead-letter-queue

Conversation

@leynos
Copy link
Copy Markdown
Owner

@leynos leynos commented Jul 8, 2025

Summary

  • add optional dead letter queue for push queues
  • document DLQ configuration in builder
  • route dropped frames to the DLQ and log errors on overflow
  • test DLQ routing and error logging
  • tick roadmap item for DLQ support

Testing

  • make lint
  • make test

https://chatgpt.com/codex/tasks/task_e_686c5393d5bc8322b119c3a598ec70cf

Summary by Sourcery

Add optional dead letter queue (DLQ) support for push queues, including builder APIs, frame routing, error handling, documentation, and tests.

New Features:

  • Add a Dead Letter Queue option to PushQueues and WireframeApp via a with_push_dlq/bounded_with_rate_dlq API
  • Introduce PushConfigError to validate custom rate limits
  • Provide a route_to_dlq mechanism that sends dropped frames to the configured DLQ

Enhancements:

  • Enhance PushPolicy::DropIfFull and WarnAndDropIfFull to forward dropped frames to the DLQ and log errors on DLQ overflow or closure
  • Convert bounded_with_rate and related constructors to return Result and panic only on internal invariants

Build:

  • Add logtest as a test-only dependency and introduce a logging fixture module in wireframe_testing

Documentation:

  • Document DLQ configuration in the builder API, add a DLQ routing sequence diagram in the outbound messaging design, and mark DLQ support as complete in the roadmap
  • Update hardening guide and other examples to unwrap new rate-limit results

Tests:

  • Add tests covering dropped-frame routing to the DLQ and error logging when the DLQ is full or closed
  • Introduce a serialized global logger fixture using logtest for deterministic log assertions

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Jul 8, 2025

Reviewer's Guide

This PR adds optional dead letter queue (DLQ) support to push queues by refactoring constructors to validate rates, introducing a PushConfigError type, extending PushHandle to route dropped frames to a DLQ with logging, updating the WireframeApp builder, and expanding tests and documentation accordingly.

Sequence diagram for push queue DLQ routing

sequenceDiagram
    participant Producer
    participant PushHandle
    participant HighPrioQueue
    participant DLQ as DeadLetterQueue
    Producer->>PushHandle: try_push(frame, priority, DropIfFull)
    PushHandle->>HighPrioQueue: try_send(frame)
    alt Queue full
        PushHandle->>DLQ: try_send(frame)
        alt DLQ full
            PushHandle->>PushHandle: log error (frame lost)
        else DLQ has space
            DLQ-->>PushHandle: frame accepted
        end
    else Queue has space
        HighPrioQueue-->>PushHandle: frame accepted
    end
    PushHandle-->>Producer: Ok or logs
Loading

Class diagram for WireframeApp builder DLQ support

classDiagram
    class WireframeApp {
        -push_dlq: Option<mpsc::Sender<Vec<u8>>>
        +with_push_dlq(dlq: mpsc::Sender<Vec<u8>>): Self
    }
Loading

File-Level Changes

Change Details Files
Refactor push queue constructors with rate validation and DLQ-aware API
  • Introduce PushConfigError enum for invalid rates
  • Change bounded_with_rate to return Result and validate rate
  • Add new bounded_with_rate_dlq method supporting optional DLQ
  • Update bounded and bounded_no_rate_limit to delegate to DLQ-aware implementation
src/push.rs
Extend PushHandle to route dropped frames to an optional DLQ
  • Add dlq_tx field in PushHandleInner
  • Implement route_to_dlq method with error logging on Full/Closed
  • Update try_push logic to invoke route_to_dlq for DropIfFull and WarnAndDropIfFull policies
src/push.rs
Add builder support for configuring a DLQ in WireframeApp
  • Add push_dlq field to WireframeApp state
  • Implement with_push_dlq builder method with docs and example
  • Propagate push_dlq through build() and finalize methods
src/app.rs
Enhance tests for DLQ behavior, rate constructors, and logging
  • Update existing tests to unwrap Result-based constructors
  • Add tests for dropped-frame routing and DLQ error scenarios
  • Introduce LoggerHandle fixture and logging utilities
  • Update Cargo.toml to include logtest dependency
tests/push_policies.rs
tests/push.rs
wireframe_testing/src/logging.rs
wireframe_testing/Cargo.toml
Document DLQ configuration and mark roadmap items complete
  • Add sequence diagram and DLQ config docs in async design guide
  • Document with_push_dlq in app builder docs
  • Mark DLQ support as done in development roadmaps
  • Adjust rstest fixtures and resilience guide examples accordingly
docs/asynchronous-outbound-messaging-design.md
docs/asynchronous-outbound-messaging-roadmap.md
docs/wireframe-1-0-detailed-development-roadmap.md
docs/rust-testing-with-rstest-fixtures.md
docs/hardening-wireframe-a-guide-to-production-resilience.md

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 Jul 8, 2025

Warning

Rate limit exceeded

@leynos has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 0 minutes and 1 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between cafdf38 and 62cb8f2.

📒 Files selected for processing (2)
  • wireframe_testing/src/lib.rs (2 hunks)
  • wireframe_testing/src/logging.rs (1 hunks)

Summary by CodeRabbit

New Features

  • Added optional Dead Letter Queue (DLQ) support for handling dropped push frames, including configuration via builder methods and routing logic for full queues.

Bug Fixes

  • Improved error handling for invalid rate values when creating rate-limited push queues.

Documentation

  • Enhanced and clarified documentation with new diagrams, updated usage examples, and improved table formatting for better readability.
  • Marked completion of DLQ-related production hardening tasks in roadmap documents.

Tests

  • Expanded and refactored test coverage for push queue policies, including DLQ scenarios and error conditions.
  • Introduced shared logger utilities for consistent and thread-safe logging in tests.

Chores

  • Added new dependencies and reorganised test utilities for logging.

Summary by CodeRabbit

  • New Features
    • Introduced optional Dead Letter Queue (DLQ) support for handling dropped push frames when queues are full.
    • Added configuration options and builder methods to enable DLQ routing for push queues.
  • Bug Fixes
    • Improved test isolation and reliability for push queue policies by enforcing serial test execution and clearing logger buffers.
  • Documentation
    • Enhanced documentation with clearer formatting, updated tables, and examples illustrating DLQ usage.
    • Marked the DLQ checklist item as complete in the production hardening roadmap.
  • Tests
    • Added tests to verify correct routing of dropped frames to the DLQ and proper error logging when the DLQ is full.

Walkthrough

This update introduces an optional Dead Letter Queue (DLQ) feature to the push queue system, allowing dropped frames to be forwarded to a DLQ channel when the push queue is full. The change includes updates to the application struct, push queue constructors, and test coverage for the new DLQ behaviour, alongside documentation and formatting improvements.

Changes

Files/Paths Change Summary
src/app.rs Added optional push_dlq field and builder method to configure a DLQ sender for dropped push frames.
src/push.rs Added optional DLQ sender to PushHandleInner; updated constructors and try_push to support DLQ.
tests/push_policies.rs Added and updated tests for DLQ routing and error logging; improved test isolation and serialisation.
tests/push.rs Updated tests to unwrap bounded_with_rate results reflecting new return type.
docs/asynchronous-outbound-messaging-roadmap.md Marked DLQ checklist item as complete.
docs/rust-testing-with-rstest-fixtures.md Editorial and formatting improvements to bullet points and tables; no semantic changes.
docs/wireframe-1-0-detailed-development-roadmap.md Markdown table formatting and alignment improvements only.
docs/hardening-wireframe-a-guide-to-production-resilience.md Updated example to unwrap bounded_with_rate result; no logic change.
docs/asynchronous-outbound-messaging-design.md Added sequence diagram illustrating DLQ routing logic.
tests/connection_actor.rs Replaced local logger handle with shared logger fixture from wireframe_testing.
wireframe_testing/src/logging.rs Added global thread-safe logger handle and fixture for tests.
wireframe_testing/src/lib.rs Added logging module and re-exported logger utilities.
wireframe_testing/Cargo.toml Added logtest crate dependency for logging tests.

Sequence Diagram(s)

sequenceDiagram
    participant Producer as PushHandle
    participant Queue as PushQueues
    participant DLQ as DeadLetterQueue (DLQ)
    participant Consumer

    Producer->>Queue: try_push(frame, priority, policy)
    alt Queue not full
        Queue-->>Consumer: frame accepted
    else Queue full and DLQ configured
        alt Policy is DropIfFull or WarnAndDropIfFull
            Producer->>DLQ: send dropped frame
            alt DLQ full
                DLQ-->>Producer: log error (DLQ full)
            else
                DLQ-->>Producer: frame accepted by DLQ
            end
        else
            Producer-->>Producer: frame dropped (no DLQ)
        end
    end
Loading

Possibly related PRs

Poem

A frame in a queue, with nowhere to go,
Now finds a DLQ, where dropped bits can flow.
If the queue is too full, don’t despair or rue—
The rabbit will catch what’s lost and renew!
🐇✨

“No frame left behind,” the bunny decrees,
In the land of the queues and the DLQs!

✨ Finishing Touches
  • 📝 Docstrings were successfully generated. (🔄 Check again to generate docstrings again)- [ ] Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/implement-optional-dead-letter-queue

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai auto-generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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 @leynos - I've reviewed your changes - here's some feedback:

  • The DLQ routing only logs on TrySendError::Full but ignores TrySendError::Closed; handle or log the closed case to avoid silently dropping frames when the DLQ is disconnected.
  • Consider extracting the DLQ routing logic into its own helper function to simplify the try_push match arm and improve readability by avoiding nested if let patterns.
  • The bounded_with_rate_dlq builder panics on an invalid rate via assert!; consider returning a Result or explicit error instead of panicking for safer configuration handling.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The DLQ routing only logs on `TrySendError::Full` but ignores `TrySendError::Closed`; handle or log the closed case to avoid silently dropping frames when the DLQ is disconnected.
- Consider extracting the DLQ routing logic into its own helper function to simplify the `try_push` match arm and improve readability by avoiding nested `if let` patterns.
- The `bounded_with_rate_dlq` builder panics on an invalid rate via `assert!`; consider returning a `Result` or explicit error instead of panicking for safer configuration handling.

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.

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

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eaeab9b and 6229597.

📒 Files selected for processing (6)
  • docs/asynchronous-outbound-messaging-roadmap.md (1 hunks)
  • docs/rust-testing-with-rstest-fixtures.md (3 hunks)
  • docs/wireframe-1-0-detailed-development-roadmap.md (1 hunks)
  • src/app.rs (6 hunks)
  • src/push.rs (7 hunks)
  • tests/push_policies.rs (4 hunks)
🧰 Additional context used
📓 Path-based instructions (9)
`docs/**/*.md`: Documentation must use en-GB-oxendict spelling and grammar (with the exception of "license" which is to be left unchanged for community consistency).

docs/**/*.md: Documentation must use en-GB-oxendict spelling and grammar (with the exception of "license" which is to be left unchanged for community consistency).

📄 Source: CodeRabbit Inference Engine (AGENTS.md)

List of files the instruction was applied to:

  • docs/asynchronous-outbound-messaging-roadmap.md
  • docs/wireframe-1-0-detailed-development-roadmap.md
  • docs/rust-testing-with-rstest-fixtures.md
`**/*.md`: Validate Markdown files using `markdownlint *.md **/*.md`. Run `mdfor...

**/*.md: Validate Markdown files using markdownlint *.md **/*.md.
Run mdformat-all after any documentation changes to format all Markdown files and fix table markup.
Validate Markdown Mermaid diagrams using the nixie CLI. The tool is already installed; run nixie *.md **/*.md directly instead of using npx.
Markdown paragraphs and bullet points must be wrapped at 80 columns.
Code blocks must be wrapped at 120 columns.
Tables and headings must not be wrapped.

📄 Source: CodeRabbit Inference Engine (AGENTS.md)

List of files the instruction was applied to:

  • docs/asynchronous-outbound-messaging-roadmap.md
  • docs/wireframe-1-0-detailed-development-roadmap.md
  • docs/rust-testing-with-rstest-fixtures.md
`docs/**/*.md`: Provide user guides and examples demonstrating server-initiated messaging.

docs/**/*.md: Provide user guides and examples demonstrating server-initiated messaging.

📄 Source: CodeRabbit Inference Engine (docs/asynchronous-outbound-messaging-roadmap.md)

List of files the instruction was applied to:

  • docs/asynchronous-outbound-messaging-roadmap.md
  • docs/wireframe-1-0-detailed-development-roadmap.md
  • docs/rust-testing-with-rstest-fixtures.md
`docs/**/*.md`: Conventions for writing project documentation should follow the rules outlined in the documentation style guide.

docs/**/*.md: Conventions for writing project documentation should follow the rules outlined in the documentation style guide.

📄 Source: CodeRabbit Inference Engine (docs/contents.md)

List of files the instruction was applied to:

  • docs/asynchronous-outbound-messaging-roadmap.md
  • docs/wireframe-1-0-detailed-development-roadmap.md
  • docs/rust-testing-with-rstest-fixtures.md
`docs/**/*.md`: Use British English based on the Oxford English Dictionary (en-o...

docs/**/*.md: Use British English based on the Oxford English Dictionary (en-oxendict) for documentation.
The word "outwith" is acceptable in documentation.
Keep US spelling when used in an API, for example color.
Use the Oxford comma in documentation.
Company names are treated as collective nouns (e.g., "Lille Industries are expanding").
Write headings in sentence case in documentation.
Use Markdown headings (#, ##, ###, etc.) in order without skipping levels.
Follow markdownlint recommendations for Markdown files.
Provide code blocks and lists using standard Markdown syntax.
Always use fenced code blocks with a language identifier; use plaintext for non-code text.
Use - as the first level bullet and renumber lists when items change.
Prefer inline links using [text](url) or angle brackets around the URL in Markdown.
Expand any uncommon acronym on first use, for example, Continuous Integration (CI).
Wrap paragraphs at 80 columns in documentation.
Wrap code at 120 columns in documentation.
Do not wrap tables in documentation.
Use footnotes referenced with [^label] in documentation.
Include Mermaid diagrams in documentation where it adds clarity.
When embedding figures in documentation, use ![alt text](path/to/image) and provide concise alt text describing the content.
Add a short description before each Mermaid diagram in documentation so screen readers can understand it.

📄 Source: CodeRabbit Inference Engine (docs/documentation-style-guide.md)

List of files the instruction was applied to:

  • docs/asynchronous-outbound-messaging-roadmap.md
  • docs/wireframe-1-0-detailed-development-roadmap.md
  • docs/rust-testing-with-rstest-fixtures.md
`docs/**/*.md`: Write the official documentation for the new features. Create se...

docs/**/*.md: Write the official documentation for the new features. Create separate guides for "Duplex Messaging & Pushes", "Streaming Responses", and "Message Fragmentation". Each guide must include runnable examples and explain the relevant concepts and APIs.

📄 Source: CodeRabbit Inference Engine (docs/wireframe-1-0-detailed-development-roadmap.md)

List of files the instruction was applied to:

  • docs/asynchronous-outbound-messaging-roadmap.md
  • docs/wireframe-1-0-detailed-development-roadmap.md
  • docs/rust-testing-with-rstest-fixtures.md
`**/*.md`: * Avoid 2nd person or 1st person pronouns ("I", "you", "we") * Use en...

**/*.md: * Avoid 2nd person or 1st person pronouns ("I", "you", "we")

  • Use en-oxendic spelling and grammar.
  • Paragraphs and bullets must be wrapped to 80 columns, except where a long URL would prevent this (in which case, silence MD013 for that line)
  • Code blocks should be wrapped to 120 columns.
  • Headings must not be wrapped.

⚙️ Source: CodeRabbit Configuration File

List of files the instruction was applied to:

  • docs/asynchronous-outbound-messaging-roadmap.md
  • docs/wireframe-1-0-detailed-development-roadmap.md
  • docs/rust-testing-with-rstest-fixtures.md
`**/*.rs`: Comment why, not what. Explain assumptions, edge cases, trade-offs, o...

**/*.rs: Comment why, not what. Explain assumptions, edge cases, trade-offs, or complexity. Don't echo the obvious.
Comments must use en-GB-oxendict spelling and grammar.
Function documentation must include clear examples.
Every module must begin with a module level (//!) comment explaining the module's purpose and utility.
Document public APIs using Rustdoc comments (///) so documentation can be generated with cargo doc.
Place function attributes after doc comments.
Do not use return in single-line functions.
Use predicate functions for conditional criteria with more than two branches.
Lints must not be silenced except as a last resort.
Lint rule suppressions must be tightly scoped and include a clear reason.
Prefer expect over allow.
Prefer .expect() over .unwrap().
Clippy warnings MUST be disallowed.
Fix any warnings emitted during tests in the code itself rather than silencing them.
Where a function is too long, extract meaningfully named helper functions adhering to separation of concerns and CQRS.
Where a function has too many parameters, group related parameters in meaningfully named structs.
Where a function is returning a large error consider using Arc to reduce the amount of data returned.
Write unit and behavioural tests for new functionality. Run both before and after making any change.
Prefer immutable data and avoid unnecessary mut bindings.
Handle errors with the Result type instead of panicking where feasible.
Avoid unsafe code unless absolutely necessary and document any usage clearly.

📄 Source: CodeRabbit Inference Engine (AGENTS.md)

List of files the instruction was applied to:

  • tests/push_policies.rs
  • src/app.rs
  • src/push.rs
`**/*.rs`: * Seek to keep the cyclomatic complexity of functions no more than 12...

**/*.rs: * Seek to keep the cyclomatic complexity of functions no more than 12.

  • Adhere to single responsibility and CQRS

  • Place function attributes after doc comments.

  • Do not use return in single-line functions.

  • Move conditionals with >2 branches into a predicate function.

  • Avoid unsafe unless absolutely necessary.

  • Every module must begin with a //! doc comment that explains the module's purpose and utility.

  • Comments must use en-GB-oxendict spelling and grammar.

  • Lints must not be silenced except as a last resort.

    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Use rstest fixtures for shared setup and to avoid repetition between tests.

  • Replace duplicated tests with #[rstest(...)] parameterised cases.

  • Prefer mockall for mocks/stubs.

  • Prefer .expect() over .unwrap()

  • Ensure that any API or behavioural changes are reflected in the documentation in docs/

  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/

⚙️ Source: CodeRabbit Configuration File

List of files the instruction was applied to:

  • tests/push_policies.rs
  • src/app.rs
  • src/push.rs
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
PR: leynos/wireframe#0
File: docs/the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md:0-0
Timestamp: 2025-06-30T23:08:36.501Z
Learning: Applies to docs/src/**/*.rs : For push mechanisms, offer an optional Dead Letter Queue (DLQ) pattern: if a push fails due to a full queue, route the frame to a separate channel for later inspection or reprocessing.
docs/rust-testing-with-rstest-fixtures.md (11)
Learnt from: CR
PR: leynos/femtologging#0
File: docs/rust-testing-with-rstest-fixtures.md:0-0
Timestamp: 2025-06-25T00:05:36.858Z
Learning: Compared to standard Rust #[test], rstest provides declarative fixture injection and parameterization, reducing boilerplate and improving test clarity, especially for complex setups and input combinations.
Learnt from: CR
PR: leynos/mxd#0
File: docs/rust-testing-with-rstest-fixtures.md:0-0
Timestamp: 2025-06-25T23:40:09.111Z
Learning: In Rust, the rstest crate provides a declarative, macro-based approach to fixture-based and parameterized testing, reducing boilerplate and improving test readability.
Learnt from: CR
PR: leynos/ddlint#0
File: docs/rust-testing-with-rstest-fixtures.md:0-0
Timestamp: 2025-06-28T15:32:52.327Z
Learning: For small, simple unit tests with no shared setup or parameterization, standard #[test] may suffice; rstest is most beneficial for complex setups, parameterized testing, and DRY test code.
Learnt from: CR
PR: leynos/lille#0
File: docs/rust-testing-with-rstest-fixtures.md:0-0
Timestamp: 2025-06-24T18:32:30.955Z
Learning: rstest is most beneficial for complex setups, parameterized testing, and when aiming for readable, DRY, and maintainable test suites; for trivial tests, standard #[test] may suffice.
Learnt from: CR
PR: leynos/lille#0
File: docs/rust-testing-with-rstest-fixtures.md:0-0
Timestamp: 2025-07-07T22:05:30.492Z
Learning: Use `rstest` for tests with complex setup, parameterization, or where improved readability and reduced boilerplate are desired; for simple unit tests, standard `#[test]` may suffice.
Learnt from: CR
PR: leynos/femtologging#0
File: docs/rust-testing-with-rstest-fixtures.md:0-0
Timestamp: 2025-06-26T00:20:29.033Z
Learning: In Rust, the rstest crate enables declarative fixture-based and parameterized testing using procedural macros like #[rstest] and #[fixture], which inject dependencies as function arguments, improving readability and reducing boilerplate.
Learnt from: CR
PR: leynos/femtologging#0
File: docs/rust-testing-with-rstest-fixtures.md:0-0
Timestamp: 2025-06-25T00:05:37.557Z
Learning: rstest is most beneficial for complex setups, parameterized testing, and when aiming for readable, DRY, and maintainable test suites; for simple unit tests, standard #[test] may suffice.
Learnt from: CR
PR: leynos/mdtablefix#0
File: docs/rust-testing-with-rstest-fixtures.md:0-0
Timestamp: 2025-06-24T23:09:43.342Z
Learning: Standard Rust #[test] functions require manual setup and parameterization, leading to more boilerplate and less readable tests compared to rstest's declarative approach.
Learnt from: CR
PR: leynos/ddlint#0
File: docs/rust-testing-with-rstest-fixtures.md:0-0
Timestamp: 2025-06-28T15:32:52.327Z
Learning: In Rust, the rstest crate enables fixture-based and parameterized testing using procedural macros like #[rstest] and #[fixture], allowing dependencies to be injected as function arguments for improved readability and reduced boilerplate.
Learnt from: CR
PR: leynos/femtologging#0
File: docs/rust-testing-with-rstest-fixtures.md:0-0
Timestamp: 2025-06-25T00:05:36.857Z
Learning: In Rust, the rstest crate enables fixture-based and parameterized testing using procedural macros like #[rstest] and #[fixture], allowing dependencies to be injected as function arguments for improved readability and reduced boilerplate.
Learnt from: CR
PR: leynos/wireframe#0
File: docs/rust-testing-with-rstest-fixtures.md:0-0
Timestamp: 2025-06-30T23:08:10.890Z
Learning: Applies to docs/**/*.rs : Use the `#[rstest]` attribute to mark test functions that utilize fixture injection or parameterization.
🪛 LanguageTool
docs/wireframe-1-0-detailed-development-roadmap.md

[grammar] ~23-~23: The verb ‘depend’ requires the preposition ‘on’ (or ‘upon’).
Context: ... | Size | Depends | | ------ | --------------------------...

(DEPEND_ON)


[typographical] ~27-~27: To join two clauses or introduce examples, consider using an em dash.
Context: .... | Small | - | | 1.2 | Priority Push Channel...

(DASH_RULE)


[typographical] ~28-~28: To join two clauses or introduce examples, consider using an em dash.
Context: ... | Medium | - | | 1.3 | Connection Actor Writ...

(DASH_RULE)


[style] ~29-~29: Consider using the typographical ellipsis character here instead.
Context: ...stateful connection actors. Implement a select!(biased; ...) loop that polls for shutdown signals,...

(ELLIPSIS)


[uncategorized] ~29-~29: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...p that polls for shutdown signals, high/low priority pushes and the handler response stream ...

(EN_COMPOUND_ADJECTIVE_INTERNAL)


[typographical] ~30-~30: To join two clauses or introduce examples, consider using an em dash.
Context: ... | Medium | - | | 1.5 | Basic FragmentAdapter...

(DASH_RULE)


[uncategorized] ~31-~31: Possible missing comma found.
Context: ...for a single, non-multiplexed stream of fragments and the outbound logic for splitting a ...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~81-~81: Do not mix variants of the same word (‘finalise’ and ‘finalize’) within a single text.
Context: ...Changelog & 1.0 Release | Finalise the CHANGELOG.md with a comprehensive...

(EN_WORD_COHERENCY)

docs/rust-testing-with-rstest-fixtures.md

[style] ~1174-~1174: Consider using the typographical ellipsis character here instead.
Context: ...le distinct #[test] functions. | #[case(...)] attributes on #[rstest] function. ...

(ELLIPSIS)


[style] ~1175-~1175: Consider using the typographical ellipsis character here instead.
Context: ...complex manual generation. | #[values(...)] attributes on arguments of #[rstest] ...

(ELLIPSIS)


[misspelling] ~1340-~1340: Use “a” instead of ‘an’ if the following word doesn’t start with a vowel sound, e.g. ‘a sentence’, ‘a university’.
Context: ... | Marks a function as an rstest test; enables fixture injection ...

(EN_A_VS_AN)


[style] ~1342-~1342: Consider using the typographical ellipsis character here instead.
Context: ...ervices). | | #[case(...)] | Defines a single pa...

(ELLIPSIS)


[style] ~1343-~1343: Consider using the typographical ellipsis character here instead.
Context: ...s. | | #[values(...)] | Defines a list of val...

(ELLIPSIS)


[style] ~1348-~1348: Consider using the typographical ellipsis character here instead.
Context: ...n. | | #[with(...)] | Overrides default a...

(ELLIPSIS)


[style] ~1349-~1349: Consider using the typographical ellipsis character here instead.
Context: ... | | #[default(...)] | Provides default value...

(ELLIPSIS)


[style] ~1350-~1350: Consider using the typographical ellipsis character here instead.
Context: ... | | #[timeout(...)] | Sets a timeout for an ...

(ELLIPSIS)


[style] ~1351-~1351: Consider using the typographical ellipsis character here instead.
Context: ... | | #[files("glob_pattern",...)] | Injects file paths (or contents, wi...

(ELLIPSIS)

⏰ Context from checks skipped due to timeout of 240000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-test
🔇 Additional comments (18)
docs/rust-testing-with-rstest-fixtures.md (1)

543-549: Documentation formatting improvements look excellent.

The numbering corrections and table alignment enhance readability whilst maintaining the technical accuracy of the rstest documentation.

docs/wireframe-1-0-detailed-development-roadmap.md (1)

23-82: Table formatting improvements enhance readability.

The proper header separators and column alignment make the roadmap tables much more readable whilst preserving all content.

docs/asynchronous-outbound-messaging-roadmap.md (1)

47-48: Correctly marks the DLQ feature as implemented.

The roadmap update accurately reflects the completion of the optional Dead Letter Queue functionality, maintaining consistency between documentation and implementation status.

src/app.rs (4)

17-20: Clean import addition for DLQ functionality.

The tokio::sync::mpsc import is appropriately added to support the DLQ sender type.


88-88: Well-designed DLQ field integration.

The push_dlq field is properly typed and fits well within the WireframeApp structure. Making it optional maintains backwards compatibility.


401-418: Excellent builder method implementation.

The with_push_dlq method follows the established builder pattern perfectly. The documentation includes a practical code example, and the method signature is clean and intuitive.


365-365: Consistent field propagation across builder methods.

The push_dlq field is properly propagated through all builder transformation methods (on_connection_setup and serializer), ensuring the DLQ configuration is maintained throughout the builder chain.

Also applies to: 467-467

tests/push_policies.rs (4)

7-7: Appropriate imports for enhanced testing.

The serial_test::serial and tokio::sync::mpsc imports support the new DLQ testing functionality and prevent test interference.

Also applies to: 10-10


63-66: Excellent test isolation improvements.

Adding #[serial] attributes and logger clearing prevents race conditions and ensures clean test state. This follows testing best practices for shared resources.

Also applies to: 85-88


109-124: Comprehensive DLQ routing test.

The dropped_frame_goes_to_dlq test excellently verifies that dropped frames are correctly routed to the DLQ when the main queue is full. The test setup and assertions are clear and thorough.


126-155: Thorough DLQ overflow error handling test.

The dlq_full_logs_error test properly validates error logging when the DLQ itself is full. The test correctly pre-fills the DLQ and verifies both the error log content and that no additional frames are accepted.

src/push.rs (7)

72-72: Well-structured field addition for DLQ support.

The optional DLQ sender field is appropriately typed and positioned within the struct, maintaining consistency with the existing pattern of optional features.


155-157: Clear documentation update for DLQ behaviour.

The documentation accurately describes the new DLQ routing behaviour for drop policies, enhancing API clarity for users.


162-176: Comprehensive example demonstrating DLQ usage.

The updated example clearly illustrates how dropped frames are routed to the DLQ, providing practical guidance for users implementing this feature.


240-240: Excellent backward compatibility preservation.

The delegation to bounded_with_rate_dlq with None for DLQ maintains complete backward compatibility whilst enabling the new feature.


258-258: Consistent delegation pattern maintained.

The no-rate-limit constructor properly delegates to the new method, preserving existing behaviour whilst enabling DLQ support.


285-291: Clean refactoring of existing constructor.

The bounded_with_rate method now cleanly delegates to the new bounded_with_rate_dlq method, maintaining the same API whilst enabling DLQ functionality.


293-361: Well-implemented DLQ constructor with comprehensive documentation.

The new constructor method is well-designed with:

  • Proper parameter validation matching existing patterns
  • Clear documentation explaining DLQ behaviour
  • Comprehensive example demonstrating usage
  • Consistent field initialisation

The implementation maintains the same validation logic as existing constructors and properly initialises the DLQ field.

Comment thread src/push.rs
Copy link
Copy Markdown

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

Choose a reason for hiding this comment

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

Gates Failed
Enforce advisory code health rules (2 files with Complex Conditional, Code Duplication)

Gates Passed
5 Quality Gates Passed

See analysis details in CodeScene

Reason for failure
Enforce advisory code health rules Violations Code Health Impact
push_policies.rs 1 advisory rule 10.00 → 9.39 Suppress
push.rs 1 advisory rule 10.00 → 9.69 Suppress

Quality Gate Profile: Pay Down Tech Debt
Want more control? Customize Code Health rules or catch issues early with our IDE extension and CLI tool.

Comment thread src/push.rs
Comment on lines +371 to +372
if let Some(r) = rate
&& (r == 0 || r > MAX_PUSH_RATE)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

❌ New issue: Complex Conditional
PushQueues.bounded_with_rate_dlq has 1 complex conditionals with 2 branches, threshold = 2

Suppress

Comment thread tests/push_policies.rs Outdated
Comment thread tests/push_policies.rs Outdated
Copy link
Copy Markdown

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

Choose a reason for hiding this comment

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

Gates Failed
Enforce advisory code health rules (2 files with Complex Conditional, Code Duplication)

Gates Passed
5 Quality Gates Passed

See analysis details in CodeScene

Reason for failure
Enforce advisory code health rules Violations Code Health Impact
push_policies.rs 1 advisory rule 10.00 → 9.39 Suppress
push.rs 1 advisory rule 10.00 → 9.69 Suppress

Quality Gate Profile: Pay Down Tech Debt
Want more control? Customize Code Health rules or catch issues early with our IDE extension and CLI tool.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Jul 9, 2025

@sourcery-ai review

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

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 244ae7f and f8aa4dc.

📒 Files selected for processing (1)
  • docs/asynchronous-outbound-messaging-design.md (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
`docs/**/*.md`: Documentation must use en-GB-oxendict spelling and grammar (with the exception of "license" which is to be left unchanged for community consistency).

docs/**/*.md: Documentation must use en-GB-oxendict spelling and grammar (with the exception of "license" which is to be left unchanged for community consistency).

📄 Source: CodeRabbit Inference Engine (AGENTS.md)

List of files the instruction was applied to:

  • docs/asynchronous-outbound-messaging-design.md
`**/*.md`: Validate Markdown files using `markdownlint *.md **/*.md`. Run `mdfor...

**/*.md: Validate Markdown files using markdownlint *.md **/*.md.
Run mdformat-all after any documentation changes to format all Markdown files and fix table markup.
Validate Markdown Mermaid diagrams using the nixie CLI. The tool is already installed; run nixie *.md **/*.md directly instead of using npx.
Markdown paragraphs and bullet points must be wrapped at 80 columns.
Code blocks must be wrapped at 120 columns.
Tables and headings must not be wrapped.

📄 Source: CodeRabbit Inference Engine (AGENTS.md)

List of files the instruction was applied to:

  • docs/asynchronous-outbound-messaging-design.md
`docs/**/*.md`: Provide user guides and examples demonstrating server-initiated messaging.

docs/**/*.md: Provide user guides and examples demonstrating server-initiated messaging.

📄 Source: CodeRabbit Inference Engine (docs/asynchronous-outbound-messaging-roadmap.md)

List of files the instruction was applied to:

  • docs/asynchronous-outbound-messaging-design.md
`docs/**/*.md`: Conventions for writing project documentation should follow the rules outlined in the documentation style guide.

docs/**/*.md: Conventions for writing project documentation should follow the rules outlined in the documentation style guide.

📄 Source: CodeRabbit Inference Engine (docs/contents.md)

List of files the instruction was applied to:

  • docs/asynchronous-outbound-messaging-design.md
`docs/**/*.md`: Use British English based on the Oxford English Dictionary (en-o...

docs/**/*.md: Use British English based on the Oxford English Dictionary (en-oxendict) for documentation.
The word "outwith" is acceptable in documentation.
Keep US spelling when used in an API, for example color.
Use the Oxford comma in documentation.
Company names are treated as collective nouns (e.g., "Lille Industries are expanding").
Write headings in sentence case in documentation.
Use Markdown headings (#, ##, ###, etc.) in order without skipping levels.
Follow markdownlint recommendations for Markdown files.
Provide code blocks and lists using standard Markdown syntax.
Always use fenced code blocks with a language identifier; use plaintext for non-code text.
Use - as the first level bullet and renumber lists when items change.
Prefer inline links using [text](url) or angle brackets around the URL in Markdown.
Expand any uncommon acronym on first use, for example, Continuous Integration (CI).
Wrap paragraphs at 80 columns in documentation.
Wrap code at 120 columns in documentation.
Do not wrap tables in documentation.
Use footnotes referenced with [^label] in documentation.
Include Mermaid diagrams in documentation where it adds clarity.
When embedding figures in documentation, use ![alt text](path/to/image) and provide concise alt text describing the content.
Add a short description before each Mermaid diagram in documentation so screen readers can understand it.

📄 Source: CodeRabbit Inference Engine (docs/documentation-style-guide.md)

List of files the instruction was applied to:

  • docs/asynchronous-outbound-messaging-design.md
`docs/**/*.md`: Write the official documentation for the new features. Create se...

docs/**/*.md: Write the official documentation for the new features. Create separate guides for "Duplex Messaging & Pushes", "Streaming Responses", and "Message Fragmentation". Each guide must include runnable examples and explain the relevant concepts and APIs.

📄 Source: CodeRabbit Inference Engine (docs/wireframe-1-0-detailed-development-roadmap.md)

List of files the instruction was applied to:

  • docs/asynchronous-outbound-messaging-design.md
`**/*.md`: * Avoid 2nd person or 1st person pronouns ("I", "you", "we") * Use en...

**/*.md: * Avoid 2nd person or 1st person pronouns ("I", "you", "we")

  • Use en-oxendic spelling and grammar.
  • Paragraphs and bullets must be wrapped to 80 columns, except where a long URL would prevent this (in which case, silence MD013 for that line)
  • Code blocks should be wrapped to 120 columns.
  • Headings must not be wrapped.

⚙️ Source: CodeRabbit Configuration File

List of files the instruction was applied to:

  • docs/asynchronous-outbound-messaging-design.md
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
PR: leynos/wireframe#0
File: docs/the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md:0-0
Timestamp: 2025-06-30T23:08:36.501Z
Learning: Applies to docs/src/**/*.rs : For push mechanisms, offer an optional Dead Letter Queue (DLQ) pattern: if a push fails due to a full queue, route the frame to a separate channel for later inspection or reprocessing.
⏰ Context from checks skipped due to timeout of 240000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-test

Comment on lines +600 to +602
The following sequence diagram illustrates how frames are routed when a DLQ is
configured:

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.

🛠️ Refactor suggestion

Wrap the intro line and add a <description> tag for accessibility compliance

Documentation guidelines require paragraphs to be wrapped at 80 columns and each
Mermaid diagram to be preceded by a short description wrapped in a <description>
tag for screen-reader support (see earlier diagrams in this file).

Suggested patch:

-The following sequence diagram illustrates how frames are routed when a DLQ is configured:
+<description>The diagram shows how a frame moves from the producer to the
+`PushHandle`, falls back to the Dead Letter Queue (DLQ) when the primary queue
+is full, and is ultimately logged if both queues are saturated.</description>
+
+The following sequence diagram illustrates how frames are routed when a DLQ is
+configured:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
The following sequence diagram illustrates how frames are routed when a DLQ is
configured:
<description>The diagram shows how a frame moves from the producer to the
`PushHandle`, falls back to the Dead Letter Queue (DLQ) when the primary queue
is full, and is ultimately logged if both queues are saturated.</description>
The following sequence diagram illustrates how frames are routed when a DLQ is
configured:
🤖 Prompt for AI Agents
In docs/asynchronous-outbound-messaging-design.md around lines 600 to 602, the
introductory line before the sequence diagram is not wrapped at 80 columns and
lacks a <description> tag for accessibility. Wrap the introductory text to
ensure no line exceeds 80 characters and add a <description> tag immediately
before the Mermaid diagram with a concise summary of the diagram's content to
support screen readers.

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 @leynos - I've reviewed your changes - here's some feedback:

  • Consider adding #[must_use] to bounded_with_rate_dlq (and/or retain it on the rate-limited constructors) so callers don’t accidentally drop the returned queue and handle.
  • The markdown tables in wireframe-1-0-detailed-development-roadmap.md look malformed—with extra separator rows—and the DLQ item is still unmarked; please clean up the separators and mark the DLQ row as completed.
  • The let-chains in if let Some(r) = rate && (r == 0 || r > MAX_PUSH_RATE) can be harder to parse; consider a nested if let Some(r) = rate { if r == 0 || r > MAX_PUSH_RATE { … } } for clarity.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Consider adding #[must_use] to bounded_with_rate_dlq (and/or retain it on the rate-limited constructors) so callers don’t accidentally drop the returned queue and handle.
- The markdown tables in wireframe-1-0-detailed-development-roadmap.md look malformed—with extra separator rows—and the DLQ item is still unmarked; please clean up the separators and mark the DLQ row as completed.
- The let-chains in `if let Some(r) = rate && (r == 0 || r > MAX_PUSH_RATE)` can be harder to parse; consider a nested `if let Some(r) = rate { if r == 0 || r > MAX_PUSH_RATE { … } }` for clarity.

## Individual Comments

### Comment 1
<location> `tests/push.rs:59` </location>
<code_context>
 #[tokio::test]
 async fn rate_limiter_blocks_when_exceeded(#[case] priority: PushPriority) {
     time::pause();
-    let (mut queues, handle) = PushQueues::bounded_with_rate(2, 2, Some(1));
+    let (mut queues, handle) = PushQueues::bounded_with_rate(2, 2, Some(1)).unwrap();

     match priority {
</code_context>

<issue_to_address>
Update to handle Result from new API is correct.

Please add a test that covers the error case for invalid rate values, such as zero or values above MAX_PUSH_RATE, to ensure error handling is tested.

Suggested implementation:

```rust
#[tokio::test]
async fn rate_limiter_blocks_when_exceeded(#[case] priority: PushPriority) {
    time::pause();
    let (mut queues, handle) = PushQueues::bounded_with_rate(2, 2, Some(1)).unwrap();

    match priority {
        PushPriority::High => handle.push_high_priority(1u8).await.unwrap(),

```

```rust
#[tokio::test]
async fn rate_limiter_allows_after_wait() {
    time::pause();
    let (mut queues, handle) = PushQueues::bounded_with_rate(2, 2, Some(1)).unwrap();
    handle.push_high_priority(1u8).await.unwrap();
    time::advance(Duration::from_secs(1)).await;
    handle.push_high_priority(2u8).await.unwrap();
}

#[test]
fn rate_limiter_invalid_rate_returns_error() {
    // Test zero rate
    let result = PushQueues::bounded_with_rate(2, 2, Some(0));
    assert!(result.is_err(), "Expected error for zero rate");

    // Test rate above MAX_PUSH_RATE
    let above_max = MAX_PUSH_RATE + 1;
    let result = PushQueues::bounded_with_rate(2, 2, Some(above_max));
    assert!(result.is_err(), "Expected error for rate above MAX_PUSH_RATE");
}

```

- Ensure that `MAX_PUSH_RATE` is accessible in this test module. If not, you may need to import or define it appropriately.
- If `PushQueues::bounded_with_rate` returns a custom error type, you may want to assert on the error variant for more precise testing.
</issue_to_address>

### Comment 2
<location> `src/push.rs:365` </location>
<code_context>
+    ///     assert_eq!(dlq_rx.recv().await.unwrap(), 2);
+    /// }
+    /// ```
+    pub fn bounded_with_rate_dlq(
+        high_capacity: usize,
+        low_capacity: usize,
</code_context>

<issue_to_address>
Consider introducing a builder pattern to unify queue constructors and inline DLQ routing for simpler, more extensible code.

```markdown
You’ve correctly centralized all of the “real” work into `bounded_with_rate_dlq` and `try_push`, but we can collapse most of the surface‐level APIs and remove the extra `route_to_dlq` indirection—and make extension easier in the future—by introducing a small builder.  

1) Define a `PushQueuesBuilder`, drop `route_to_dlq()`, and inline DLQ routing in `try_push`:

```rust
pub struct PushQueuesBuilder<F> {
    high_capacity: usize,
    low_capacity: usize,
    rate: Option<usize>,
    dlq:   Option<mpsc::Sender<F>>,
}

impl<F: FrameLike> PushQueuesBuilder<F> {
    pub fn new(high_capacity: usize, low_capacity: usize) -> Self {
        Self { high_capacity, low_capacity, rate: None, dlq: None }
    }

    pub fn with_rate(mut self, rate: usize) -> Self {
        self.rate = Some(rate);
        self
    }

    pub fn with_dlq(mut self, dlq_tx: mpsc::Sender<F>) -> Self {
        self.dlq = Some(dlq_tx);
        self
    }

    pub fn build(self) -> Result<(PushQueues<F>, PushHandle<F>), PushConfigError> {
        // reuse your existing bounded_with_rate_dlq logic:
        PushQueues::bounded_with_rate_dlq(
            self.high_capacity,
            self.low_capacity,
            self.rate,
            self.dlq,
        )
    }
}
```

2) Replace your four public constructors with wrappers around the builder:

```rust
impl<F: FrameLike> PushQueues<F> {
    /// no rate, no DLQ
    pub fn bounded(high: usize, low: usize) -> (Self, PushHandle<F>) {
        PushQueuesBuilder::new(high, low)
            .with_rate(DEFAULT_PUSH_RATE)
            .build()
            .expect("DEFAULT_PUSH_RATE is valid")
    }

    pub fn bounded_no_rate_limit(high: usize, low: usize) -> (Self, PushHandle<F>) {
        PushQueuesBuilder::new(high, low)
            .build()
            .unwrap()
    }

    pub fn bounded_with_rate(
        high: usize,
        low: usize,
        rate: usize,
    ) -> Result<(Self, PushHandle<F>), PushConfigError> {
        PushQueuesBuilder::new(high, low).with_rate(rate).build()
    }

    pub fn bounded_with_rate_dlq(
        high: usize,
        low: usize,
        rate: Option<usize>,
        dlq: Option<mpsc::Sender<F>>,
    ) -> Result<(Self, PushHandle<F>), PushConfigError> {
        PushQueuesBuilder::new(high, low).with_rate_opt(rate)?.with_dlq_opt(dlq).build()
    }
}
```

3) Inline the DLQ routing inside `try_push` and remove `route_to_dlq`:

```rust
pub fn try_push(
    &self,
    frame: F,
    priority: PushPriority,
    policy: PushPolicy,
) -> Result<(), PushError> {
    let tx = match priority {
        PushPriority::High => &self.0.high_prio_tx,
        PushPriority::Low  => &self.0.low_prio_tx,
    };

    match tx.try_send(frame) {
        Ok(()) => Ok(()),
        Err(mpsc::error::TrySendError::Full(f)) => {
            if matches!(policy, PushPolicy::WarnAndDropIfFull) {
                log::warn!("push queue full; dropping {:?}", priority);
            }
            if let Some(dlq) = &self.0.dlq_tx {
                if let Err(e) = dlq.try_send(f) {
                    log::error!("DLQ send failed ({:?}); frame lost", e);
                }
            }
            match policy {
                PushPolicy::ReturnErrorIfFull => Err(PushError::QueueFull),
                _ => Ok(()),
            }
        }
        Err(mpsc::error::TrySendError::Closed(_)) => Err(PushError::Closed),
    }
}
```

This:

- Collapses four entry points into a single builder + `build()`
- Removes `route_to_dlq()` and its nesting
- Keeps all existing behavior and adds one clear extension point for future options
```
</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/push.rs
#[tokio::test]
async fn rate_limiter_blocks_when_exceeded(#[case] priority: PushPriority) {
time::pause();
let (mut queues, handle) = PushQueues::bounded_with_rate(2, 2, Some(1));
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.

suggestion (testing): Update to handle Result from new API is correct.

Please add a test that covers the error case for invalid rate values, such as zero or values above MAX_PUSH_RATE, to ensure error handling is tested.

Suggested implementation:

#[tokio::test]
async fn rate_limiter_blocks_when_exceeded(#[case] priority: PushPriority) {
    time::pause();
    let (mut queues, handle) = PushQueues::bounded_with_rate(2, 2, Some(1)).unwrap();

    match priority {
        PushPriority::High => handle.push_high_priority(1u8).await.unwrap(),
#[tokio::test]
async fn rate_limiter_allows_after_wait() {
    time::pause();
    let (mut queues, handle) = PushQueues::bounded_with_rate(2, 2, Some(1)).unwrap();
    handle.push_high_priority(1u8).await.unwrap();
    time::advance(Duration::from_secs(1)).await;
    handle.push_high_priority(2u8).await.unwrap();
}

#[test]
fn rate_limiter_invalid_rate_returns_error() {
    // Test zero rate
    let result = PushQueues::bounded_with_rate(2, 2, Some(0));
    assert!(result.is_err(), "Expected error for zero rate");

    // Test rate above MAX_PUSH_RATE
    let above_max = MAX_PUSH_RATE + 1;
    let result = PushQueues::bounded_with_rate(2, 2, Some(above_max));
    assert!(result.is_err(), "Expected error for rate above MAX_PUSH_RATE");
}
  • Ensure that MAX_PUSH_RATE is accessible in this test module. If not, you may need to import or define it appropriately.
  • If PushQueues::bounded_with_rate returns a custom error type, you may want to assert on the error variant for more precise testing.

Comment thread src/push.rs
/// assert_eq!(dlq_rx.recv().await.unwrap(), 2);
/// }
/// ```
pub fn bounded_with_rate_dlq(
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.

issue (complexity): Consider introducing a builder pattern to unify queue constructors and inline DLQ routing for simpler, more extensible code.

You’ve correctly centralized all of the “real” work into `bounded_with_rate_dlq` and `try_push`, but we can collapse most of the surface‐level APIs and remove the extra `route_to_dlq` indirection—and make extension easier in the future—by introducing a small builder.  

1) Define a `PushQueuesBuilder`, drop `route_to_dlq()`, and inline DLQ routing in `try_push`:

```rust
pub struct PushQueuesBuilder<F> {
    high_capacity: usize,
    low_capacity: usize,
    rate: Option<usize>,
    dlq:   Option<mpsc::Sender<F>>,
}

impl<F: FrameLike> PushQueuesBuilder<F> {
    pub fn new(high_capacity: usize, low_capacity: usize) -> Self {
        Self { high_capacity, low_capacity, rate: None, dlq: None }
    }

    pub fn with_rate(mut self, rate: usize) -> Self {
        self.rate = Some(rate);
        self
    }

    pub fn with_dlq(mut self, dlq_tx: mpsc::Sender<F>) -> Self {
        self.dlq = Some(dlq_tx);
        self
    }

    pub fn build(self) -> Result<(PushQueues<F>, PushHandle<F>), PushConfigError> {
        // reuse your existing bounded_with_rate_dlq logic:
        PushQueues::bounded_with_rate_dlq(
            self.high_capacity,
            self.low_capacity,
            self.rate,
            self.dlq,
        )
    }
}
  1. Replace your four public constructors with wrappers around the builder:
impl<F: FrameLike> PushQueues<F> {
    /// no rate, no DLQ
    pub fn bounded(high: usize, low: usize) -> (Self, PushHandle<F>) {
        PushQueuesBuilder::new(high, low)
            .with_rate(DEFAULT_PUSH_RATE)
            .build()
            .expect("DEFAULT_PUSH_RATE is valid")
    }

    pub fn bounded_no_rate_limit(high: usize, low: usize) -> (Self, PushHandle<F>) {
        PushQueuesBuilder::new(high, low)
            .build()
            .unwrap()
    }

    pub fn bounded_with_rate(
        high: usize,
        low: usize,
        rate: usize,
    ) -> Result<(Self, PushHandle<F>), PushConfigError> {
        PushQueuesBuilder::new(high, low).with_rate(rate).build()
    }

    pub fn bounded_with_rate_dlq(
        high: usize,
        low: usize,
        rate: Option<usize>,
        dlq: Option<mpsc::Sender<F>>,
    ) -> Result<(Self, PushHandle<F>), PushConfigError> {
        PushQueuesBuilder::new(high, low).with_rate_opt(rate)?.with_dlq_opt(dlq).build()
    }
}
  1. Inline the DLQ routing inside try_push and remove route_to_dlq:
pub fn try_push(
    &self,
    frame: F,
    priority: PushPriority,
    policy: PushPolicy,
) -> Result<(), PushError> {
    let tx = match priority {
        PushPriority::High => &self.0.high_prio_tx,
        PushPriority::Low  => &self.0.low_prio_tx,
    };

    match tx.try_send(frame) {
        Ok(()) => Ok(()),
        Err(mpsc::error::TrySendError::Full(f)) => {
            if matches!(policy, PushPolicy::WarnAndDropIfFull) {
                log::warn!("push queue full; dropping {:?}", priority);
            }
            if let Some(dlq) = &self.0.dlq_tx {
                if let Err(e) = dlq.try_send(f) {
                    log::error!("DLQ send failed ({:?}); frame lost", e);
                }
            }
            match policy {
                PushPolicy::ReturnErrorIfFull => Err(PushError::QueueFull),
                _ => Ok(()),
            }
        }
        Err(mpsc::error::TrySendError::Closed(_)) => Err(PushError::Closed),
    }
}

This:

  • Collapses four entry points into a single builder + build()
  • Removes route_to_dlq() and its nesting
  • Keeps all existing behavior and adds one clear extension point for future options

Copy link
Copy Markdown

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

Choose a reason for hiding this comment

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

Gates Failed
Enforce advisory code health rules (1 file with Complex Conditional)

Gates Passed
5 Quality Gates Passed

See analysis details in CodeScene

Reason for failure
Enforce advisory code health rules Violations Code Health Impact
push.rs 1 advisory rule 10.00 → 9.69 Suppress

Quality Gate Profile: Pay Down Tech Debt
Want more control? Customize Code Health rules or catch issues early with our IDE extension and CLI tool.

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)
tests/push_policies.rs (1)

144-186: Excellent implementation addressing code duplication concerns.

The parameterised test using rstest effectively eliminates the code duplication mentioned in the past review comments whilst maintaining comprehensive test coverage for both DLQ full and DLQ closed scenarios.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f8aa4dc and e4fc549.

📒 Files selected for processing (1)
  • tests/push_policies.rs (4 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
`**/*.rs`: Comment why, not what. Explain assumptions, edge cases, trade-offs, o...

**/*.rs: Comment why, not what. Explain assumptions, edge cases, trade-offs, or complexity. Don't echo the obvious.
Comments must use en-GB-oxendict spelling and grammar.
Function documentation must include clear examples.
Every module must begin with a module level (//!) comment explaining the module's purpose and utility.
Document public APIs using Rustdoc comments (///) so documentation can be generated with cargo doc.
Place function attributes after doc comments.
Do not use return in single-line functions.
Use predicate functions for conditional criteria with more than two branches.
Lints must not be silenced except as a last resort.
Lint rule suppressions must be tightly scoped and include a clear reason.
Prefer expect over allow.
Prefer .expect() over .unwrap().
Clippy warnings MUST be disallowed.
Fix any warnings emitted during tests in the code itself rather than silencing them.
Where a function is too long, extract meaningfully named helper functions adhering to separation of concerns and CQRS.
Where a function has too many parameters, group related parameters in meaningfully named structs.
Where a function is returning a large error consider using Arc to reduce the amount of data returned.
Write unit and behavioural tests for new functionality. Run both before and after making any change.
Prefer immutable data and avoid unnecessary mut bindings.
Handle errors with the Result type instead of panicking where feasible.
Avoid unsafe code unless absolutely necessary and document any usage clearly.

📄 Source: CodeRabbit Inference Engine (AGENTS.md)

List of files the instruction was applied to:

  • tests/push_policies.rs
`**/*.rs`: * Seek to keep the cyclomatic complexity of functions no more than 12...

**/*.rs: * Seek to keep the cyclomatic complexity of functions no more than 12.

  • Adhere to single responsibility and CQRS

  • Place function attributes after doc comments.

  • Do not use return in single-line functions.

  • Move conditionals with >2 branches into a predicate function.

  • Avoid unsafe unless absolutely necessary.

  • Every module must begin with a //! doc comment that explains the module's purpose and utility.

  • Comments must use en-GB-oxendict spelling and grammar.

  • Lints must not be silenced except as a last resort.

    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Use rstest fixtures for shared setup and to avoid repetition between tests.

  • Replace duplicated tests with #[rstest(...)] parameterised cases.

  • Prefer mockall for mocks/stubs.

  • Prefer .expect() over .unwrap()

  • Ensure that any API or behavioural changes are reflected in the documentation in docs/

  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/

⚙️ Source: CodeRabbit Configuration File

List of files the instruction was applied to:

  • tests/push_policies.rs
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
PR: leynos/wireframe#0
File: docs/the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md:0-0
Timestamp: 2025-06-30T23:08:36.501Z
Learning: Applies to docs/src/**/*.rs : For push mechanisms, offer an optional Dead Letter Queue (DLQ) pattern: if a push fails due to a full queue, route the frame to a separate channel for later inspection or reprocessing.
⏰ Context from checks skipped due to timeout of 240000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-test
🔇 Additional comments (5)
tests/push_policies.rs (5)

5-11: Import additions look appropriate for the new DLQ functionality.

The new imports support the async helper functions (BoxFuture) and test serialisation (serial), which are necessary for the DLQ testing implementation.


64-67: Good practice: Adding test serialisation and logger clearing.

The #[serial] attribute prevents race conditions with the shared logger state, and the logger clearing ensures clean test state between runs.


86-89: Consistent application of serialisation pattern.

The same logger serialisation and clearing pattern is correctly applied to maintain test isolation.


128-142: Add documentation for helper functions.

The helper functions lack documentation explaining their purpose and usage, which violates the coding guidelines requiring clear documentation for functions.

+/// Sets up a DLQ sender in a full state by pre-filling it with a test frame.
+/// This simulates the condition where the DLQ cannot accept additional frames.
 fn setup_dlq_full(tx: &mpsc::Sender<u8>, _rx: &mut Option<mpsc::Receiver<u8>>) {
     tx.try_send(99).unwrap();
 }

+/// Sets up a DLQ in a closed state by dropping the receiver.
+/// This simulates the condition where the DLQ is no longer accepting frames.
 fn setup_dlq_closed(_: &mpsc::Sender<u8>, rx: &mut Option<mpsc::Receiver<u8>>) { drop(rx.take()); }

+/// Asserts that the DLQ received the pre-filled frame and cannot receive more.
+/// Verifies the DLQ full condition by checking frame reception and queue state.
 fn assert_dlq_full(rx: &mut Option<mpsc::Receiver<u8>>) -> BoxFuture<'_, ()> {
     Box::pin(async move {
         let receiver = rx.as_mut().expect("receiver missing");
         assert_eq!(receiver.recv().await.unwrap(), 99);
         assert!(receiver.try_recv().is_err());
     })
 }

+/// No-op assertion for the DLQ closed scenario.
+/// When the DLQ is closed, there are no additional assertions to perform.
 fn assert_dlq_closed(_: &mut Option<mpsc::Receiver<u8>>) -> BoxFuture<'_, ()> { Box::pin(async {}) }

Likely an incorrect or invalid review comment.


110-126: Existing tests already cover WarnAndDropIfFull DLQ routing

The WarnAndDropIfFull policy is exercised in tests/push_policies.rs, so no additional test is required:

  • Line 93: .try_push(4u8, PushPriority::Low, PushPolicy::WarnAndDropIfFull)
  • Line 145: rstest case dlq_full for PushPolicy::WarnAndDropIfFull, which asserts routing to the DLQ

Please disregard the suggestion to add another DLQ test for WarnAndDropIfFull.

Likely an incorrect or invalid review comment.

Comment thread tests/push_policies.rs
Comment on lines +156 to +157
Setup: FnOnce(&mpsc::Sender<u8>, &mut Option<mpsc::Receiver<u8>>),
AssertFn: FnOnce(&mut Option<mpsc::Receiver<u8>>) -> BoxFuture<'_, ()>,
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Jul 9, 2025

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Consider simplifying the generic constraints.

The current approach using BoxFuture adds complexity. Consider using async closures or a simpler approach that doesn't require explicit lifetime management.

// Alternative approach using async closures
where
    Setup: FnOnce(&mpsc::Sender<u8>, &mut Option<mpsc::Receiver<u8>>),
    AssertFn: for<'a> FnOnce(&'a mut Option<mpsc::Receiver<u8>>) -> impl std::future::Future<Output = ()> + 'a,
🤖 Prompt for AI Agents
In tests/push_policies.rs around lines 156 to 157, the generic constraint for
AssertFn uses BoxFuture which adds unnecessary complexity and explicit lifetime
management. Replace the BoxFuture return type with an async closure by changing
the constraint to use a higher-ranked trait bound with for<'a> and returning an
impl Future instead. This simplifies the code by leveraging async closures and
removes the need for boxing and explicit lifetimes.

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 create a GitHub issue for this.

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.

🧩 Analysis chain

@leynos I'll create a GitHub issue for this suggestion to simplify the generic constraints.


🏁 Script executed:

#!/bin/bash
gh issue create \
  --title "Simplify generic constraints in push policy tests" \
  --body "A suggestion was made to simplify the generic constraints in the \`dlq_error_scenarios\` function in \`tests/push_policies.rs\` around lines 156-157. 

The current approach using \`BoxFuture\` adds complexity and explicit lifetime management. The suggestion is to replace the \`BoxFuture\` return type with async closures using higher-ranked trait bounds (for<'a>) and returning an impl Future instead.

This would simplify the code by leveraging async closures and removing the need for boxing and explicit lifetimes.

**References:**
- PR: https://github.com/leynos/wireframe/pull/198
- Comment: https://github.com/leynos/wireframe/pull/198#discussion_r2195929726
- Requested by: @leynos" \
  --assignee leynos

Length of output: 836


@leynos I've successfully created GitHub issue #199 for the suggestion to simplify the generic constraints in the push policy tests. The issue includes details about replacing BoxFuture with async closures and has been assigned to you.

Issue URL: #199


🧬 Code Graph Analysis Results

Relevant snippets from tests/connection_actor.rs (lines 372-381, 387, 391, 399)

fn new() -> Self {
    static LOGGER: OnceLock<Mutex<Logger>> = OnceLock::new();

    let logger = LOGGER.get_or_init(|| Mutex::new(Logger::start()));
    let guard = logger
        .lock()
        .expect("failed to acquire global logger lock; a previous test may still hold it");

    Self { guard }
}

fn deref(&self) -> &Self::Target { &self.guard }

fn deref_mut(&mut self) -> &mut Self::Target { &mut self.guard }

fn logger() -> LoggerHandle { LoggerHandle::new() }

These snippets define a LoggerHandle struct with methods to create a global logger instance guarded by a mutex, and implement Deref and DerefMut traits for convenient access. They also provide a fixture function logger() returning a LoggerHandle. This is essentially the same pattern used in tests/push_policies.rs for managing the global logger.


Relevant snippets from src/push.rs (lines 274-277, 365-399, 419-425)

pub fn bounded(high_capacity: usize, low_capacity: usize) -> (Self, PushHandle<F>) {
    Self::bounded_with_rate_dlq(high_capacity, low_capacity, Some(DEFAULT_PUSH_RATE), None)
        .expect("DEFAULT_PUSH_RATE is always valid")
}

pub fn bounded_with_rate_dlq(
    high_capacity: usize,
    low_capacity: usize,
    rate: Option<usize>,
    dlq: Option<mpsc::Sender<F>>,
) -> Result<(Self, PushHandle<F>), PushConfigError> {
    if let Some(r) = rate
        && (r == 0 || r > MAX_PUSH_RATE)
    {
        return Err(PushConfigError::InvalidRate(r));
    }
    let (high_tx, high_rx) = mpsc::channel(high_capacity);
    let (low_tx, low_rx) = mpsc::channel(low_capacity);
    let limiter = rate.map(|r| {
        RateLimiter::builder()
            .initial(r)
            .refill(r)
            .interval(Duration::from_secs(1))
            .max(r)
            .build()
    });
    let inner = PushHandleInner {
        high_prio_tx: high_tx,
        low_prio_tx: low_tx,
        limiter,
        dlq_tx: dlq,
    };
    Ok((
        Self {
            high_priority_rx: high_rx,
            low_priority_rx: low_rx,
        },
        PushHandle(Arc::new(inner)),
    ))
}

pub async fn recv(&mut self) -> Option<(PushPriority, F)> {
    tokio::select! {
        biased;
        res = self.high_priority_rx.recv() => res.map(|f| (PushPriority::High, f)),
        res = self.low_priority_rx.recv() => res.map(|f| (PushPriority::Low, f)),
    }
}

These snippets define the PushQueues and PushHandle creation and behavior:

  • bounded creates push queues with default rate limiting and no dead-letter queue (DLQ).
  • bounded_with_rate_dlq creates push queues with optional rate limiting and optional DLQ sender. It validates the rate and sets up channels for high and low priority.
  • recv asynchronously receives from the high priority queue first, then low priority queue.

This is directly relevant to the tests in tests/push_policies.rs which create bounded push queues and test pushing behavior, including DLQ handling.


Summary

The relevant code snippets provide:

  • The global logger setup and locking mechanism used in the tests.
  • The construction and behavior of the push queues and handles, including bounded queues, rate limiting, and DLQ support.
  • The asynchronous receiving of pushed items from the queues.

These are essential to understand the test file tests/push_policies.rs and the behavior it verifies regarding push policies and DLQ handling.

Copy link
Copy Markdown

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

Choose a reason for hiding this comment

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

Gates Failed
Enforce advisory code health rules (2 files with Complex Conditional, Code Duplication)

Gates Passed
5 Quality Gates Passed

See analysis details in CodeScene

Reason for failure
Enforce advisory code health rules Violations Code Health Impact
push_policies.rs 1 advisory rule 10.00 → 9.39 Suppress
push.rs 1 advisory rule 10.00 → 9.69 Suppress

Quality Gate Profile: Pay Down Tech Debt
Want more control? Customize Code Health rules or catch issues early with our IDE extension and CLI tool.

Comment thread tests/push_policies.rs
Comment thread tests/push_policies.rs
@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Jul 10, 2025

@sourcery-ai review

Copy link
Copy Markdown

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

Choose a reason for hiding this comment

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

Gates Failed
Enforce advisory code health rules (1 file with Complex Conditional)

Gates Passed
5 Quality Gates Passed

See analysis details in CodeScene

Reason for failure
Enforce advisory code health rules Violations Code Health Impact
push.rs 1 advisory rule 10.00 → 9.69 Suppress

Quality Gate Profile: Pay Down Tech Debt
Want more control? Customize Code Health rules or catch issues early with our IDE extension and CLI tool.

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 @leynos - I've reviewed your changes and found some issues that need to be addressed.

Blocking issues:

  • Add behavioural and unit tests for the new Dead Letter Queue (DLQ) feature in PushHandle and PushQueues. (link)

General comments:

  • The bounded and bounded_no_rate_limit constructors still panic via unwrap/expect on invalid defaults—consider making them return a Result or explicitly documenting these panics so callers aren’t surprised by hidden panics.
  • The new route_to_dlq method swallows the DLQ send result; consider exposing a Result or flag in the public API so callers (or metrics) can know if a frame actually made it into the dead letter queue.
  • The if let Some(r) = rate && (r == 0 || r > MAX_PUSH_RATE) let-chain requires Rust 1.65+—make sure the crate’s rust-version is bumped accordingly or revert to a more compatible pattern.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The `bounded` and `bounded_no_rate_limit` constructors still panic via `unwrap`/`expect` on invalid defaults—consider making them return a `Result` or explicitly documenting these panics so callers aren’t surprised by hidden panics.
- The new `route_to_dlq` method swallows the DLQ send result; consider exposing a `Result` or flag in the public API so callers (or metrics) can know if a frame actually made it into the dead letter queue.
- The `if let Some(r) = rate && (r == 0 || r > MAX_PUSH_RATE)` let-chain requires Rust 1.65+—make sure the crate’s `rust-version` is bumped accordingly or revert to a more compatible pattern.

## Individual Comments

### Comment 1
<location> `src/push.rs:298` </location>
<code_context>
         low_capacity: usize,
     ) -> (Self, PushHandle<F>) {
-        Self::bounded_with_rate(high_capacity, low_capacity, None)
+        Self::bounded_with_rate_dlq(high_capacity, low_capacity, None, None).unwrap()
     }

</code_context>

<issue_to_address>
Unwrapping here could panic if future changes introduce invalid parameters.

While safe now, future changes could make this unwrap unsafe. Please handle the error explicitly or document the required invariants.

Suggested implementation:

```rust
    ///
    /// # Errors
    ///
    /// Returns [`PushConfigError`] if the provided parameters are invalid.
    #[must_use]
    pub fn bounded_no_rate_limit(
        high_capacity: usize,
        low_capacity: usize,
    ) -> Result<(Self, PushHandle<F>), PushConfigError> {
        Self::bounded_with_rate_dlq(high_capacity, low_capacity, None, None)
    }

```

You will need to update all call sites of `bounded_no_rate_limit` to handle the new `Result` return type, either by propagating the error or handling it appropriately.
</issue_to_address>

### Comment 2
<location> `src/push.rs:274` </location>
<code_context>
+    ///
+    /// Panics if an internal invariant is violated. This should never occur.
     #[must_use]
     pub fn bounded(high_capacity: usize, low_capacity: usize) -> (Self, PushHandle<F>) {
-        Self::bounded_with_rate(high_capacity, low_capacity, Some(DEFAULT_PUSH_RATE))
+        Self::bounded_with_rate_dlq(high_capacity, low_capacity, Some(DEFAULT_PUSH_RATE), None)
</code_context>

<issue_to_address>
Consider refactoring the four `bounded*` constructors into a single builder pattern with a rate validation helper to centralize logic and reduce duplication.

Consider collapsing the four `bounded*` constructors into a single builder + a tiny rate‐validator helper. That removes duplication, centralizes the `if let Some(r) && (r==0 || r>…)` logic, and keeps the DLQ wiring in one place. For example:

```rust
/// centralize rate checks
fn validate_rate(r: Option<usize>) -> Result<Option<usize>, PushConfigError> {
    if let Some(n) = r {
        if n == 0 || n > MAX_PUSH_RATE {
            return Err(PushConfigError::InvalidRate(n));
        }
    }
    Ok(r)
}

/// builder for all `bounded*` variants
pub struct PushQueuesBuilder<F> {
    high: usize,
    low: usize,
    rate: Option<usize>,
    dlq: Option<mpsc::Sender<F>>,
}

impl<F: FrameLike> PushQueuesBuilder<F> {
    pub fn new(high: usize, low: usize) -> Self {
        Self { high, low, rate: Some(DEFAULT_PUSH_RATE), dlq: None }
    }

    pub fn no_rate_limit(mut self) -> Self {
        self.rate = None;
        self
    }

    pub fn rate(mut self, r: usize) -> Self {
        self.rate = Some(r);
        self
    }

    pub fn dlq(mut self, dlq_tx: mpsc::Sender<F>) -> Self {
        self.dlq = Some(dlq_tx);
        self
    }

    pub fn build(self) -> Result<(PushQueues<F>, PushHandle<F>), PushConfigError> {
        let rate = validate_rate(self.rate)?;
        let (high_tx, high_rx) = mpsc::channel(self.high);
        let (low_tx, low_rx)   = mpsc::channel(self.low);
        let limiter = rate.map(|r| {
            RateLimiter::builder().initial(r).refill(r)
                .interval(Duration::from_secs(1)).max(r).build()
        });
        let inner = PushHandleInner {
            high_prio_tx: high_tx,
            low_prio_tx: low_tx,
            limiter,
            dlq_tx: self.dlq,
        };
        Ok((
            PushQueues { high_priority_rx: high_rx, low_priority_rx: low_rx },
            PushHandle(Arc::new(inner)),
        ))
    }
}
```

Then replace your four methods with thin wrappers:

```rust
impl<F: FrameLike> PushQueues<F> {
    pub fn bounded(h: usize, l: usize)
        -> (Self, PushHandle<F>)
    {
        PushQueuesBuilder::new(h, l)
            .build().expect("DEFAULT_PUSH_RATE is valid")
    }

    pub fn bounded_no_rate_limit(h: usize, l: usize)
        -> (Self, PushHandle<F>)
    {
        PushQueuesBuilder::new(h, l)
            .no_rate_limit()
            .build().unwrap()
    }

    pub fn bounded_with_rate(h: usize, l: usize, r: usize)
        -> Result<(Self, PushHandle<F>), PushConfigError>
    {
        PushQueuesBuilder::new(h, l)
            .rate(r)
            .build()
    }

    pub fn bounded_with_rate_dlq(
        h: usize, l: usize, r: Option<usize>, dlq: Option<mpsc::Sender<F>>
    ) -> Result<(Self, PushHandle<F>), PushConfigError> {
        let mut b = PushQueuesBuilder::new(h, l);
        if let Some(r) = r { b = b.rate(r) } else { b = b.no_rate_limit() }
        if let Some(d) = dlq { b = b.dlq(d) }
        b.build()
    }
}
```

This keeps all public APIs, centralizes the “invalid rate” logic, and removes boilerplate from each overload.
</issue_to_address>

### Comment 3
<location> `src/push.rs:168` </location>
<code_context>
         self.push_with_priority(frame, PushPriority::Low).await
     }

+    /// Send a frame to the configured dead letter queue if available.
+    fn route_to_dlq(&self, frame: F) {
+        if let Some(dlq) = &self.0.dlq_tx {
</code_context>

<issue_to_address>
Add behavioural and unit tests for the new Dead Letter Queue (DLQ) feature in PushHandle and PushQueues.

The new DLQ routing logic in PushHandle and PushQueues is a significant feature addition. There must be both behavioural and unit tests demonstrating that frames are routed to the DLQ under the correct conditions (e.g., when queues are full and DropIfFull/WarnAndDropIfFull is used). Please add comprehensive tests covering these scenarios.
</issue_to_address>

### Comment 4
<location> `src/push.rs:71` </location>
<code_context>
+/// Errors returned when creating push queues.
+#[derive(Debug)]
+pub enum PushConfigError {
+    /// The provided rate was zero or exceeded [`MAX_PUSH_RATE`].
+    InvalidRate(usize),
+}
</code_context>

<issue_to_address>
Add tests for PushConfigError and error handling in PushQueues constructors.

The new PushConfigError type and error-returning constructors (e.g., bounded_with_rate, bounded_with_rate_dlq) require unit and behavioural tests to verify correct error handling for invalid rates. Please add tests that cover these error cases.
</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 src/push.rs
low_capacity: usize,
) -> (Self, PushHandle<F>) {
Self::bounded_with_rate(high_capacity, low_capacity, None)
Self::bounded_with_rate_dlq(high_capacity, low_capacity, None, None).unwrap()
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.

suggestion (bug_risk): Unwrapping here could panic if future changes introduce invalid parameters.

While safe now, future changes could make this unwrap unsafe. Please handle the error explicitly or document the required invariants.

Suggested implementation:

    ///
    /// # Errors
    ///
    /// Returns [`PushConfigError`] if the provided parameters are invalid.
    #[must_use]
    pub fn bounded_no_rate_limit(
        high_capacity: usize,
        low_capacity: usize,
    ) -> Result<(Self, PushHandle<F>), PushConfigError> {
        Self::bounded_with_rate_dlq(high_capacity, low_capacity, None, None)
    }

You will need to update all call sites of bounded_no_rate_limit to handle the new Result return type, either by propagating the error or handling it appropriately.

Comment thread src/push.rs
///
/// Panics if an internal invariant is violated. This should never occur.
#[must_use]
pub fn bounded(high_capacity: usize, low_capacity: usize) -> (Self, PushHandle<F>) {
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.

issue (complexity): Consider refactoring the four bounded* constructors into a single builder pattern with a rate validation helper to centralize logic and reduce duplication.

Consider collapsing the four bounded* constructors into a single builder + a tiny rate‐validator helper. That removes duplication, centralizes the if let Some(r) && (r==0 || r>…) logic, and keeps the DLQ wiring in one place. For example:

/// centralize rate checks
fn validate_rate(r: Option<usize>) -> Result<Option<usize>, PushConfigError> {
    if let Some(n) = r {
        if n == 0 || n > MAX_PUSH_RATE {
            return Err(PushConfigError::InvalidRate(n));
        }
    }
    Ok(r)
}

/// builder for all `bounded*` variants
pub struct PushQueuesBuilder<F> {
    high: usize,
    low: usize,
    rate: Option<usize>,
    dlq: Option<mpsc::Sender<F>>,
}

impl<F: FrameLike> PushQueuesBuilder<F> {
    pub fn new(high: usize, low: usize) -> Self {
        Self { high, low, rate: Some(DEFAULT_PUSH_RATE), dlq: None }
    }

    pub fn no_rate_limit(mut self) -> Self {
        self.rate = None;
        self
    }

    pub fn rate(mut self, r: usize) -> Self {
        self.rate = Some(r);
        self
    }

    pub fn dlq(mut self, dlq_tx: mpsc::Sender<F>) -> Self {
        self.dlq = Some(dlq_tx);
        self
    }

    pub fn build(self) -> Result<(PushQueues<F>, PushHandle<F>), PushConfigError> {
        let rate = validate_rate(self.rate)?;
        let (high_tx, high_rx) = mpsc::channel(self.high);
        let (low_tx, low_rx)   = mpsc::channel(self.low);
        let limiter = rate.map(|r| {
            RateLimiter::builder().initial(r).refill(r)
                .interval(Duration::from_secs(1)).max(r).build()
        });
        let inner = PushHandleInner {
            high_prio_tx: high_tx,
            low_prio_tx: low_tx,
            limiter,
            dlq_tx: self.dlq,
        };
        Ok((
            PushQueues { high_priority_rx: high_rx, low_priority_rx: low_rx },
            PushHandle(Arc::new(inner)),
        ))
    }
}

Then replace your four methods with thin wrappers:

impl<F: FrameLike> PushQueues<F> {
    pub fn bounded(h: usize, l: usize)
        -> (Self, PushHandle<F>)
    {
        PushQueuesBuilder::new(h, l)
            .build().expect("DEFAULT_PUSH_RATE is valid")
    }

    pub fn bounded_no_rate_limit(h: usize, l: usize)
        -> (Self, PushHandle<F>)
    {
        PushQueuesBuilder::new(h, l)
            .no_rate_limit()
            .build().unwrap()
    }

    pub fn bounded_with_rate(h: usize, l: usize, r: usize)
        -> Result<(Self, PushHandle<F>), PushConfigError>
    {
        PushQueuesBuilder::new(h, l)
            .rate(r)
            .build()
    }

    pub fn bounded_with_rate_dlq(
        h: usize, l: usize, r: Option<usize>, dlq: Option<mpsc::Sender<F>>
    ) -> Result<(Self, PushHandle<F>), PushConfigError> {
        let mut b = PushQueuesBuilder::new(h, l);
        if let Some(r) = r { b = b.rate(r) } else { b = b.no_rate_limit() }
        if let Some(d) = dlq { b = b.dlq(d) }
        b.build()
    }
}

This keeps all public APIs, centralizes the “invalid rate” logic, and removes boilerplate from each overload.

Comment thread src/push.rs
self.push_with_priority(frame, PushPriority::Low).await
}

/// Send a frame to the configured dead letter queue if available.
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.

issue (review_instructions): Add behavioural and unit tests for the new Dead Letter Queue (DLQ) feature in PushHandle and PushQueues.

The new DLQ routing logic in PushHandle and PushQueues is a significant feature addition. There must be both behavioural and unit tests demonstrating that frames are routed to the DLQ under the correct conditions (e.g., when queues are full and DropIfFull/WarnAndDropIfFull is used). Please add comprehensive tests covering these scenarios.

Review instructions:

Path patterns: **/*

Instructions:
For any new feature or change to an existing feature, both behavioural and unit tests are required.

Comment thread src/push.rs
/// Errors returned when creating push queues.
#[derive(Debug)]
pub enum PushConfigError {
/// The provided rate was zero or exceeded [`MAX_PUSH_RATE`].
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.

issue (review_instructions): Add tests for PushConfigError and error handling in PushQueues constructors.

The new PushConfigError type and error-returning constructors (e.g., bounded_with_rate, bounded_with_rate_dlq) require unit and behavioural tests to verify correct error handling for invalid rates. Please add tests that cover these error cases.

Review instructions:

Path patterns: **/*

Instructions:
For any new feature or change to an existing feature, both behavioural and unit tests are required.

Copy link
Copy Markdown

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

Choose a reason for hiding this comment

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

Gates Failed
Enforce advisory code health rules (1 file with Complex Conditional)

Gates Passed
5 Quality Gates Passed

See analysis details in CodeScene

Reason for failure
Enforce advisory code health rules Violations Code Health Impact
push.rs 1 advisory rule 10.00 → 9.69 Suppress

Quality Gate Profile: Pay Down Tech Debt
Want more control? Customize Code Health rules or catch issues early with our IDE extension and CLI tool.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
tests/push_policies.rs (1)

91-99: DLQ assertion helpers complete the test infrastructure.

These functions provide the necessary assertions for the DLQ error scenarios. Note that issue #199 tracks the suggestion to simplify the generic constraints using async closures instead of BoxFuture.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between abe10e1 and cafdf38.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • tests/connection_actor.rs (1 hunks)
  • tests/push_policies.rs (3 hunks)
  • wireframe_testing/Cargo.toml (1 hunks)
  • wireframe_testing/src/lib.rs (2 hunks)
  • wireframe_testing/src/logging.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
`**/*.rs`: Comment why, not what. Explain assumptions, edge cases, trade-offs, o...

**/*.rs: Comment why, not what. Explain assumptions, edge cases, trade-offs, or complexity. Don't echo the obvious.
Comments must use en-GB-oxendict spelling and grammar.
Function documentation must include clear examples.
Every module must begin with a module level (//!) comment explaining the module's purpose and utility.
Document public APIs using Rustdoc comments (///) so documentation can be generated with cargo doc.
Place function attributes after doc comments.
Do not use return in single-line functions.
Use predicate functions for conditional criteria with more than two branches.
Lints must not be silenced except as a last resort.
Lint rule suppressions must be tightly scoped and include a clear reason.
Prefer expect over allow.
Prefer .expect() over .unwrap().
Clippy warnings MUST be disallowed.
Fix any warnings emitted during tests in the code itself rather than silencing them.
Where a function is too long, extract meaningfully named helper functions adhering to separation of concerns and CQRS.
Where a function has too many parameters, group related parameters in meaningfully named structs.
Where a function is returning a large error consider using Arc to reduce the amount of data returned.
Write unit and behavioural tests for new functionality. Run both before and after making any change.
Prefer immutable data and avoid unnecessary mut bindings.
Handle errors with the Result type instead of panicking where feasible.
Avoid unsafe code unless absolutely necessary and document any usage clearly.

📄 Source: CodeRabbit Inference Engine (AGENTS.md)

List of files the instruction was applied to:

  • wireframe_testing/src/lib.rs
  • tests/connection_actor.rs
  • wireframe_testing/src/logging.rs
  • tests/push_policies.rs
`**/*.rs`: * Seek to keep the cyclomatic complexity of functions no more than 12...

**/*.rs: * Seek to keep the cyclomatic complexity of functions no more than 12.

  • Adhere to single responsibility and CQRS

  • Place function attributes after doc comments.

  • Do not use return in single-line functions.

  • Move conditionals with >2 branches into a predicate function.

  • Avoid unsafe unless absolutely necessary.

  • Every module must begin with a //! doc comment that explains the module's purpose and utility.

  • Comments must use en-GB-oxendict spelling and grammar.

  • Lints must not be silenced except as a last resort.

    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Use rstest fixtures for shared setup and to avoid repetition between tests.

  • Replace duplicated tests with #[rstest(...)] parameterised cases.

  • Prefer mockall for mocks/stubs.

  • Prefer .expect() over .unwrap()

  • Ensure that any API or behavioural changes are reflected in the documentation in docs/

  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/

⚙️ Source: CodeRabbit Configuration File

List of files the instruction was applied to:

  • wireframe_testing/src/lib.rs
  • tests/connection_actor.rs
  • wireframe_testing/src/logging.rs
  • tests/push_policies.rs
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
PR: leynos/wireframe#0
File: docs/wireframe-1-0-detailed-development-roadmap.md:0-0
Timestamp: 2025-06-29T18:04:58.493Z
Learning: An optional Dead Letter Queue (DLQ) mechanism should be provided, allowing failed pushes (due to a full queue) to be routed to a user-provided DLQ channel sender instead of being dropped.
Learnt from: CR
PR: leynos/wireframe#0
File: docs/the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md:0-0
Timestamp: 2025-06-30T23:08:36.501Z
Learning: Applies to docs/src/**/*.rs : For push mechanisms, offer an optional Dead Letter Queue (DLQ) pattern: if a push fails due to a full queue, route the frame to a separate channel for later inspection or reprocessing.
🧬 Code Graph Analysis (3)
wireframe_testing/src/lib.rs (1)
wireframe_testing/src/logging.rs (1)
  • logger (41-41)
tests/connection_actor.rs (1)
wireframe_testing/src/logging.rs (1)
  • logger (41-41)
tests/push_policies.rs (2)
wireframe_testing/src/logging.rs (1)
  • logger (41-41)
src/push.rs (3)
  • bounded (274-277)
  • bounded_with_rate_dlq (365-399)
  • recv (419-425)
🪛 GitHub Actions: CI
wireframe_testing/src/lib.rs

[warning] 32-32: Prettier formatting check failed: code style differences detected. Run 'cargo fmt --all -- --write' to fix code style issues.

wireframe_testing/src/logging.rs

[warning] 35-35: Prettier formatting check failed: code style differences detected. Run 'cargo fmt --all -- --write' to fix code style issues.

🪛 GitHub Check: build-test
wireframe_testing/src/logging.rs

[warning] 35-35:
Diff in /home/runner/work/wireframe/wireframe/wireframe_testing/src/logging.rs

🔇 Additional comments (9)
wireframe_testing/Cargo.toml (1)

12-12: LGTM!

The addition of logtest dependency is appropriate for the new logging test utilities.

tests/connection_actor.rs (1)

362-362: Good refactoring!

Removing the local logger implementation in favour of the centralized one from wireframe_testing improves maintainability and reduces code duplication.

wireframe_testing/src/logging.rs (2)

6-24: Well-designed thread-safe logger implementation.

The use of OnceLock for singleton initialization and MutexGuard for exclusive access is appropriate for preventing test interference.


36-41: Clean fixture implementation.

Good use of the #[allow] attribute with a clear reason for the false positive.

tests/push_policies.rs (5)

3-12: Imports aligned with DLQ functionality.

The additional imports support the new DLQ tests and centralized logging.


26-65: Excellent refactoring using parameterized tests.

The merged test eliminates duplication while maintaining comprehensive coverage of both push policies. Good use of #[serial] to prevent log interference between tests.


67-83: Clear test for DLQ functionality.

This test effectively validates that dropped frames are routed to the DLQ as expected.


85-89: Well-structured DLQ setup helpers.

These functions clearly establish the DLQ error conditions needed for comprehensive testing.


101-143: Comprehensive DLQ error scenario testing.

Excellent use of parameterized testing to cover both DLQ full and closed scenarios. The test validates error logging and maintains good separation of concerns with the helper functions.

Comment thread wireframe_testing/src/lib.rs Outdated
Comment thread wireframe_testing/src/logging.rs
Comment thread wireframe_testing/src/logging.rs Outdated
Copy link
Copy Markdown

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

Choose a reason for hiding this comment

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

Gates Failed
Enforce advisory code health rules (1 file with Complex Conditional)

Gates Passed
5 Quality Gates Passed

See analysis details in CodeScene

Reason for failure
Enforce advisory code health rules Violations Code Health Impact
push.rs 1 advisory rule 10.00 → 9.69 Suppress

Quality Gate Profile: Pay Down Tech Debt
Want more control? Customize Code Health rules or catch issues early with our IDE extension and CLI tool.

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

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

Choose a reason for hiding this comment

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

Gates Failed
Enforce advisory code health rules (1 file with Complex Conditional)

Gates Passed
5 Quality Gates Passed

See analysis details in CodeScene

Reason for failure
Enforce advisory code health rules Violations Code Health Impact
push.rs 1 advisory rule 10.00 → 9.69 Suppress

Quality Gate Profile: Pay Down Tech Debt
Want more control? Customize Code Health rules or catch issues early with our IDE extension and CLI tool.

coderabbitai Bot added a commit that referenced this pull request Jul 10, 2025
Docstrings generation was requested by @leynos.

* #198 (comment)

The following files were modified:

* `src/app.rs`
* `src/push.rs`
* `tests/push.rs`
* `tests/push_policies.rs`
* `wireframe_testing/src/logging.rs`
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jul 10, 2025

Note

Generated docstrings for this pull request at #200

@leynos leynos merged commit 1ea1710 into main Jul 10, 2025
4 of 5 checks passed
@leynos leynos deleted the codex/implement-optional-dead-letter-queue branch July 10, 2025 23:38
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