Skip to content

feat(trogon-nats, acp-nats): add JetStream integration foundation - #65

Merged
yordis merged 1 commit into
mainfrom
feat/jetstream-integration
Mar 28, 2026
Merged

feat(trogon-nats, acp-nats): add JetStream integration foundation#65
yordis merged 1 commit into
mainfrom
feat/jetstream-integration

Conversation

@yordis

@yordis yordis commented Mar 28, 2026

Copy link
Copy Markdown
Member

Summary

  • Add JetStream trait abstractions (JetStreamContext, JetStreamPublisher, JetStreamConsumer) and JsMessage wrapper with full ack/nak/term/in_progress signal surface to trogon-nats
  • Define 4-stream topology (COMMANDS, RESPONSES, CLIENT_OPS, NOTIFICATIONS) with idempotent provisioning and consumer config factories in acp-nats
  • Extend Bridge<N, C> to Bridge<N, C, J=()> for optional JetStream support with zero blast radius on existing code
  • Add dispatch_js_message to acp-nats-agent with ack/term signal handling per dispatch outcome

Design

Immutable, logged-everything approach: every ACP interaction is persisted as a fact in JetStream streams. Only high-frequency streaming content chunks use memory-backed ephemeral storage.

Handlers receive the full JsMessage and own the complete message lifecycle — no abstraction hides JetStream's ack/nak/term/in_progress signals.

Stream Storage Max Age Purpose
{PREFIX}_COMMANDS File 30d All inbound commands
{PREFIX}_RESPONSES File 30d All results/signals
{PREFIX}_CLIENT_OPS File 30d Runner-to-client callbacks
{PREFIX}_NOTIFICATIONS Memory 5min Streaming chunks only

Test plan

  • 752 workspace tests pass (0 failures)
  • Clippy clean
  • All existing tests unchanged — J=() default preserves backward compatibility
  • New mock infrastructure (MockJsMessage, MockJetStreamContext, MockJetStreamPublisher, MockJetStreamConsumer) with signal recording
  • Stream topology tests: names, subjects, storage types, retention, no overlaps
  • Provisioning tests: creates 4 streams, custom prefix, failure handling
  • Consumer config tests: filter subjects, deliver policies

@cursor

cursor Bot commented Mar 28, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Adds new JetStream abstractions and stream provisioning/consumer configuration that will affect messaging behavior once enabled, plus a generic Bridge refactor that touches many handlers. While currently largely opt-in/unused, mistakes in stream subjects or ack/term semantics could cause message loss or unexpected redelivery when wired up.

Overview
Adds JetStream integration scaffolding across trogon-nats, acp-nats, and acp-nats-agent. trogon-nats now exposes a jetstream module with trait abstractions (JetStreamContext/JetStreamPublisher/JetStreamConsumer), a JsMessage wrapper (ack/nak/term/in-progress surface), and test mocks, and enables the async-nats jetstream feature.

acp-nats introduces a JetStream topology/config layer: factories for consumer configs, stream config definitions for four streams (commands/responses/client ops/notifications) plus provision_streams() to create them. The agent::Bridge is generalized from Bridge<N, C> to Bridge<N, C, J=()> with optional JetStream context plumbing, and all agent handlers are updated to accept the new generic bridge.

acp-nats-agent adds a JetStream-aware dispatch path (dispatch_js_message) that acks/terms messages based on parse/deserialization/handler outcomes, preparing for a future JetStream-driven serve loop. Dependency lockfile updates pull in new supporting crates (e.g., time, tryhard, serde_nanos).

Written by Cursor Bugbot for commit d6a3590. This will update automatically on new commits. Configure here.

@coderabbitai

coderabbitai Bot commented Mar 28, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@yordis has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 13 minutes and 10 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 13 minutes and 10 seconds.

⌛ 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.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 61184258-ae37-4c2e-9f6a-27c7ef1f6d35

📥 Commits

Reviewing files that changed from the base of the PR and between ac72017 and d6a3590.

⛔ Files ignored due to path filters (1)
  • rsworkspace/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (32)
  • rsworkspace/crates/acp-nats-agent/Cargo.toml
  • rsworkspace/crates/acp-nats-agent/src/connection.rs
  • rsworkspace/crates/acp-nats/Cargo.toml
  • rsworkspace/crates/acp-nats/src/agent/authenticate.rs
  • rsworkspace/crates/acp-nats/src/agent/bridge.rs
  • rsworkspace/crates/acp-nats/src/agent/cancel.rs
  • rsworkspace/crates/acp-nats/src/agent/close_session.rs
  • rsworkspace/crates/acp-nats/src/agent/ext_method.rs
  • rsworkspace/crates/acp-nats/src/agent/ext_notification.rs
  • rsworkspace/crates/acp-nats/src/agent/fork_session.rs
  • rsworkspace/crates/acp-nats/src/agent/initialize.rs
  • rsworkspace/crates/acp-nats/src/agent/list_sessions.rs
  • rsworkspace/crates/acp-nats/src/agent/load_session.rs
  • rsworkspace/crates/acp-nats/src/agent/new_session.rs
  • rsworkspace/crates/acp-nats/src/agent/prompt.rs
  • rsworkspace/crates/acp-nats/src/agent/resume_session.rs
  • rsworkspace/crates/acp-nats/src/agent/set_session_config_option.rs
  • rsworkspace/crates/acp-nats/src/agent/set_session_mode.rs
  • rsworkspace/crates/acp-nats/src/agent/set_session_model.rs
  • rsworkspace/crates/acp-nats/src/constants.rs
  • rsworkspace/crates/acp-nats/src/jetstream/consumers.rs
  • rsworkspace/crates/acp-nats/src/jetstream/mod.rs
  • rsworkspace/crates/acp-nats/src/jetstream/provision.rs
  • rsworkspace/crates/acp-nats/src/jetstream/streams.rs
  • rsworkspace/crates/acp-nats/src/lib.rs
  • rsworkspace/crates/trogon-nats/Cargo.toml
  • rsworkspace/crates/trogon-nats/src/jetstream/client.rs
  • rsworkspace/crates/trogon-nats/src/jetstream/message.rs
  • rsworkspace/crates/trogon-nats/src/jetstream/mocks.rs
  • rsworkspace/crates/trogon-nats/src/jetstream/mod.rs
  • rsworkspace/crates/trogon-nats/src/jetstream/traits.rs
  • rsworkspace/crates/trogon-nats/src/lib.rs

Walkthrough

This pull request adds JetStream support to the NATS-based agent system. It enables the jetstream feature flag across multiple crates, introduces new JetStream abstraction layers (traits, clients, message handling) in the trogon-nats crate, adds stream and consumer configuration modules in acp-nats, generalizes the Bridge type with an optional JetStream parameter, updates all agent handler signatures to accept the new generic Bridge, and adds JetStream-specific message dispatch logic.

Changes

Cohort / File(s) Summary
Cargo Feature Flags
rsworkspace/crates/acp-nats-agent/Cargo.toml, rsworkspace/crates/acp-nats/Cargo.toml, rsworkspace/crates/trogon-nats/Cargo.toml
Enabled jetstream feature in async-nats dependency across three crates.
Bridge Generalization
rsworkspace/crates/acp-nats/src/agent/bridge.rs
Generalized Bridge<N, C> to Bridge<N, C, J = ()> with optional JetStream field. Added with_jetstream constructor and updated Agent trait implementation to accept the new generic parameter.
Agent Handler Signature Updates
rsworkspace/crates/acp-nats/src/agent/authenticate.rs, cancel.rs, close_session.rs, ext_method.rs, ext_notification.rs, fork_session.rs, initialize.rs, list_sessions.rs, load_session.rs, new_session.rs, prompt.rs, resume_session.rs, set_session_config_option.rs, set_session_mode.rs, set_session_model.rs
Updated all 15 handler function signatures to accept generic type parameter J and reference Bridge<N, C, J> instead of Bridge<N, C>.
JetStream Connection Dispatch
rsworkspace/crates/acp-nats-agent/src/connection.rs
Added dispatch_js_message function to handle JetStream messages: routes by subject, terminates on parse/deserialization errors, acknowledges on success, and sends protocol error replies on handler failures.
JetStream Traits
rsworkspace/crates/trogon-nats/src/jetstream/traits.rs
Defined four public traits (JetStreamContext, JetStreamPublisher, JetStreamConsumerFactory, JetStreamConsumer) with async methods for stream creation, message publishing, consumer management, and message streaming.
JetStream Message Abstractions
rsworkspace/crates/trogon-nats/src/jetstream/message.rs
Added JsMessage wrapper around async_nats::jetstream::Message with accessors and async methods (ack, nak, term, etc.). Introduced JsSignal enum representing acknowledgement operations.
JetStream Client Implementation
rsworkspace/crates/trogon-nats/src/jetstream/client.rs
Implemented NatsJetStreamClient and NatsJetStreamConsumer wrapping async-nats JetStream context and consumer with trait implementations for context creation, publishing, consumer factory, and message consumption.
JetStream Mocks
rsworkspace/crates/trogon-nats/src/jetstream/mocks.rs
Added comprehensive mock implementations: MockJsMessage, MockJetStreamContext, MockJetStreamPublisher, MockJetStreamConsumerFactory, MockJetStreamConsumer with signal recording and failure injection capabilities for testing.
JetStream Module Organization
rsworkspace/crates/trogon-nats/src/jetstream/mod.rs, rsworkspace/crates/trogon-nats/src/lib.rs
Created jetstream module with public re-exports of traits, clients, messages, and mocks (when test-support feature enabled).
JetStream Stream Configuration
rsworkspace/crates/acp-nats/src/jetstream/streams.rs
Added stream configuration builders for four stream types (COMMANDS, RESPONSES, CLIENT_OPS, NOTIFICATIONS) with explicit subject patterns, storage policies, retention settings, and all_configs factory function.
JetStream Consumer Configuration
rsworkspace/crates/acp-nats/src/jetstream/consumers.rs
Added consumer configuration builders for prompt notifications, prompt responses, and runner commands with explicit delivery, acknowledgement, and replay policies, plus corresponding unit tests.
JetStream Provisioning
rsworkspace/crates/acp-nats/src/jetstream/provision.rs
Implemented provision_streams async function that creates or retrieves streams via JetStreamContext, with custom error type and info-level logging per provisioned stream.
JetStream Module Organization
rsworkspace/crates/acp-nats/src/jetstream/mod.rs, rsworkspace/crates/acp-nats/src/lib.rs
Created jetstream module exposing consumers, provision, and streams submodules.
Constants
rsworkspace/crates/acp-nats/src/constants.rs
Added three new public constants: SESSION_ID_HEADER, CAUSATION_ID_HEADER, and ENV_JETSTREAM_ENABLED.

Sequence Diagram(s)

sequenceDiagram
    participant Agent as Agent<br/>(Bridge)
    participant Provisioner as provision_streams()
    participant JsContext as JetStreamContext
    participant NatsJs as async-nats
    
    Agent->>Provisioner: provision_streams(js, prefix)
    Provisioner->>Provisioner: Get all configs<br/>(COMMANDS, RESPONSES,<br/>CLIENT_OPS, NOTIFICATIONS)
    
    loop for each stream config
        Provisioner->>JsContext: get_or_create_stream(config)
        JsContext->>NatsJs: fetch or create stream
        NatsJs-->>JsContext: success/error
        JsContext-->>Provisioner: Result<()>
        alt Stream created successfully
            Provisioner->>Provisioner: Log info event
        else Stream creation failed
            Provisioner->>Provisioner: Map error with stream name
        end
    end
    
    Provisioner-->>Agent: Result<(), ProvisionError>
Loading
sequenceDiagram
    participant Connection as NATS Connection
    participant Dispatcher as dispatch_js_message()
    participant Handler as Agent Handler<br/>(e.g., prompt)
    participant Bridge as Bridge<N,C,J>
    
    Connection->>Dispatcher: JetStream message received
    Dispatcher->>Dispatcher: Parse subject to method
    
    alt Subject parse successful
        Dispatcher->>Dispatcher: Deserialize request body
        alt Deserialization successful
            Dispatcher->>Handler: Call handler(bridge, args)
            Handler->>Bridge: Access NATS/JetStream clients
            Bridge-->>Handler: Response/Result
            Handler-->>Dispatcher: Result<Response>
            
            alt Handler successful
                Dispatcher->>Dispatcher: ack() message
            else Handler failed
                Dispatcher->>Dispatcher: Send error reply
                Dispatcher->>Dispatcher: ack() message
            end
        else Deserialization failed
            Dispatcher->>Dispatcher: term() message
        end
    else Subject parse failed
        Dispatcher->>Dispatcher: term() message
    end
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly Related PRs

  • PR #4: Introduces the trogon-nats crate foundation that is being extended here with JetStream-specific modules, traits, clients, and implementations.
  • PR #10: Introduces the Bridge type that is being generalized in this PR to include a JetStream type parameter and optional field.
  • PR #13: Modifies the authenticate handler whose signature is updated in this PR to accept the new generic Bridge parameter.

Poem

🐰 A rabbit hops through streams of messages so bright,
JetStream flows now dispatched with delight,
Bridges generalized in triples of grace,
Handlers aligned in their type-safe place,
Consumer configs bloom in the trait-bound space! 🌿

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 52.76% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly summarizes the main objective: adding JetStream integration foundation to trogon-nats and acp-nats libraries with specific features and abstractions.
Description check ✅ Passed The PR description is comprehensive and directly related to the changeset, detailing the design approach, stream topology, test results, and linking the description content to the actual file modifications throughout the changeset.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/jetstream-integration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

github-actions Bot commented Mar 28, 2026

Copy link
Copy Markdown

badge

Code Coverage Summary

Details
Filename                                                     Stmts    Miss  Cover    Missing
---------------------------------------------------------  -------  ------  -------  ---------------------------------------------------------------------------------------------
crates/trogon-std/src/fs/system.rs                              29      12  58.62%   17-19, 31-45
crates/trogon-std/src/fs/mem.rs                                220      10  95.45%   61-63, 77-79, 133-135, 158
crates/acp-nats/src/telemetry/metrics.rs                        65       0  100.00%
crates/acp-nats/src/jetstream/streams.rs                       159       0  100.00%
crates/acp-nats/src/jetstream/provision.rs                      56       0  100.00%
crates/acp-nats/src/jetstream/consumers.rs                      53       0  100.00%
crates/trogon-nats/src/jetstream/mocks.rs                      340       0  100.00%
crates/acp-nats/src/agent/close_session.rs                      65       0  100.00%
crates/acp-nats/src/agent/test_support.rs                      242       0  100.00%
crates/acp-nats/src/agent/load_session.rs                      107       0  100.00%
crates/acp-nats/src/agent/authenticate.rs                       52       0  100.00%
crates/acp-nats/src/agent/set_session_mode.rs                   72       0  100.00%
crates/acp-nats/src/agent/fork_session.rs                      110       0  100.00%
crates/acp-nats/src/agent/mod.rs                                61       0  100.00%
crates/acp-nats/src/agent/resume_session.rs                    106       0  100.00%
crates/acp-nats/src/agent/bridge.rs                             87       0  100.00%
crates/acp-nats/src/agent/new_session.rs                        91       0  100.00%
crates/acp-nats/src/agent/set_session_config_option.rs          69       0  100.00%
crates/acp-nats/src/agent/cancel.rs                            104       0  100.00%
crates/acp-nats/src/agent/list_sessions.rs                      50       0  100.00%
crates/acp-nats/src/agent/ext_notification.rs                   88       0  100.00%
crates/acp-nats/src/agent/set_session_model.rs                  69       0  100.00%
crates/acp-nats/src/agent/initialize.rs                         82       0  100.00%
crates/acp-nats/src/agent/prompt.rs                            218       0  100.00%
crates/acp-nats/src/agent/ext_method.rs                         92       0  100.00%
crates/acp-nats-agent/src/connection.rs                        562       0  100.00%
crates/trogon-std/src/args.rs                                   10       0  100.00%
crates/trogon-std/src/json.rs                                   30       0  100.00%
crates/acp-telemetry/src/lib.rs                                153      22  85.62%   39-46, 81, 86, 91, 105-120
crates/acp-telemetry/src/service_name.rs                        16       0  100.00%
crates/acp-telemetry/src/log.rs                                 70       2  97.14%   39-40
crates/acp-telemetry/src/metric.rs                              35       4  88.57%   30-31, 38-39
crates/acp-telemetry/src/trace.rs                               32       4  87.50%   23-24, 31-32
crates/acp-telemetry/src/signal.rs                               3       3  0.00%    4-43
crates/trogon-nats/src/mocks.rs                                304       0  100.00%
crates/trogon-nats/src/messaging.rs                            533       4  99.25%   141-146, 156-157
crates/trogon-nats/src/auth.rs                                 114       3  97.37%   45-47
crates/trogon-nats/src/client.rs                                25      25  0.00%    50-89
crates/trogon-nats/src/connect.rs                               96      16  83.33%   22-24, 37, 49, 68-151
crates/trogon-std/src/time/mock.rs                             123       0  100.00%
crates/trogon-std/src/time/system.rs                            24       0  100.00%
crates/acp-nats/src/client/mod.rs                             2978       0  100.00%
crates/acp-nats/src/client/ext_session_prompt_response.rs      149       0  100.00%
crates/acp-nats/src/client/terminal_output.rs                  223       0  100.00%
crates/acp-nats/src/client/rpc_reply.rs                         71       0  100.00%
crates/acp-nats/src/client/terminal_wait_for_exit.rs           396       0  100.00%
crates/acp-nats/src/client/fs_read_text_file.rs                384       0  100.00%
crates/acp-nats/src/client/fs_write_text_file.rs               451       0  100.00%
crates/acp-nats/src/client/terminal_release.rs                 357       0  100.00%
crates/acp-nats/src/client/session_update.rs                    55       0  100.00%
crates/acp-nats/src/client/request_permission.rs               338       0  100.00%
crates/acp-nats/src/client/ext.rs                              365       8  97.81%   193-204, 229-240
crates/acp-nats/src/client/terminal_create.rs                  294       0  100.00%
crates/acp-nats/src/client/terminal_kill.rs                    309       0  100.00%
crates/acp-nats-ws/src/connection.rs                           162      35  78.40%   71-78, 83-94, 110, 112-113, 118, 129-131, 138, 142, 146, 149-157, 168, 172, 175, 178-182, 216
crates/acp-nats-ws/src/config.rs                                83       0  100.00%
crates/acp-nats-ws/src/upgrade.rs                               57       2  96.49%   59, 90
crates/acp-nats-ws/src/main.rs                                 157       2  98.73%   84, 247
crates/acp-nats/src/nats/token.rs                                8       0  100.00%
crates/acp-nats/src/nats/subjects.rs                           284       0  100.00%
crates/acp-nats/src/nats/parsing.rs                            285       1  99.65%   148
crates/acp-nats/src/nats/extensions.rs                           3       0  100.00%
crates/trogon-std/src/env/system.rs                             17       0  100.00%
crates/trogon-std/src/env/in_memory.rs                          81       0  100.00%
crates/acp-nats/src/acp_prefix.rs                               63       0  100.00%
crates/acp-nats/src/lib.rs                                      73       0  100.00%
crates/acp-nats/src/pending_prompt_waiters.rs                  112       0  100.00%
crates/acp-nats/src/jsonrpc.rs                                   6       0  100.00%
crates/acp-nats/src/error.rs                                    84       0  100.00%
crates/acp-nats/src/session_id.rs                               88       0  100.00%
crates/acp-nats/src/ext_method_name.rs                          85       0  100.00%
crates/acp-nats/src/client_proxy.rs                            196       0  100.00%
crates/acp-nats/src/in_flight_slot_guard.rs                     32       0  100.00%
crates/acp-nats/src/config.rs                                  200       0  100.00%
crates/trogon-std/src/dirs/system.rs                            98      11  88.78%   57, 65, 67, 75, 77, 85, 87, 96, 98, 109, 154
crates/trogon-std/src/dirs/fixed.rs                             84       0  100.00%
crates/acp-nats-stdio/src/config.rs                             72       0  100.00%
crates/acp-nats-stdio/src/main.rs                              113      11  90.27%   58, 106-113, 119-121, 138
TOTAL                                                        13630     175  98.72%

Diff against main

Filename                                      Stmts    Miss  Cover
------------------------------------------  -------  ------  --------
crates/acp-nats/src/jetstream/streams.rs       +159       0  +100.00%
crates/acp-nats/src/jetstream/provision.rs      +56       0  +100.00%
crates/acp-nats/src/jetstream/consumers.rs      +53       0  +100.00%
crates/trogon-nats/src/jetstream/mocks.rs      +340       0  +100.00%
crates/acp-nats/src/agent/bridge.rs              +1       0  +100.00%
TOTAL                                          +609       0  +0.06%

Results for commit: d6a3590

Minimum allowed coverage is 95%

♻️ This comment has been updated with latest results

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 4 potential issues.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Comment thread rsworkspace/crates/acp-nats/src/jetstream/streams.rs
Comment thread rsworkspace/crates/acp-nats-agent/src/connection.rs
Comment thread rsworkspace/crates/acp-nats-agent/src/connection.rs
Comment thread rsworkspace/crates/acp-nats/src/constants.rs
@yordis
yordis force-pushed the feat/jetstream-integration branch from ac72017 to 9a1e4ed Compare March 28, 2026 02:16

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
rsworkspace/crates/acp-nats-agent/src/connection.rs (1)

326-412: Extract the routing table once.

dispatch_js_message now copies the full AgentMethod match from dispatch_message. The next method addition or dispatch-policy tweak will need two edits and these paths will drift quickly. Pull the shared routing into one helper that returns Result<(), DispatchError>, then keep only the post-dispatch ack policy separate here.

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

In `@rsworkspace/crates/acp-nats-agent/src/connection.rs` around lines 326 - 412,
dispatch_js_message duplicates the full AgentMethod routing logic from
dispatch_message; extract that shared routing into a single helper (e.g.,
route_agent_method or dispatch_agent_method) which takes (&JsMessage, &A, &N) or
the parsed subject and returns Result<(), DispatchError> (where DispatchError
models the per-dispatch failures), move all handle_request/handle_notification
match arms into that helper, and have dispatch_js_message call the helper then
perform only the post-dispatch ACK/term policy (i.e., keep the js_msg.term()/ack
handling in dispatch_js_message). Update both dispatch_js_message and
dispatch_message to call the new helper so future method additions only change
the single routing function.
rsworkspace/crates/trogon-nats/src/jetstream/mocks.rs (1)

287-313: messages() always fails — document this trait implementation quirk.

The JetStreamConsumer trait requires messages(), but MockJetStreamConsumer always returns an error, requiring tests to use raw_messages() instead. While the comment explains this is because MockJsMessage cannot convert to JsMessage, this means code using the trait abstraction cannot be tested with this mock.

Consider adding a doc comment on MockJetStreamConsumer explaining this limitation and the intended testing pattern (use raw_messages() directly rather than the trait method).

📝 Suggested documentation
+/// Mock JetStream consumer for testing.
+///
+/// **Note:** The [`JetStreamConsumer::messages()`] trait method always returns an error
+/// because `MockJsMessage` cannot be converted to `JsMessage` without a real JetStream message.
+/// Use [`raw_messages()`](Self::raw_messages) directly in tests to receive `MockJsMessage` values
+/// and assert on signal recordings.
 pub struct MockJetStreamConsumer {
     rx: Mutex<Option<mpsc::UnboundedReceiver<MockJsMessage>>>,
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rsworkspace/crates/trogon-nats/src/jetstream/mocks.rs` around lines 287 -
313, Add a doc comment to the MockJetStreamConsumer type explaining that its
JetStreamConsumer::messages() implementation always returns an error because
MockJsMessage cannot be converted into a real jetstream::Message, and that tests
should call MockJetStreamConsumer::raw_messages() (which returns the underlying
MockJsMessage stream) for signal assertions; reference the methods messages(),
raw_messages(), the type MockJetStreamConsumer and MockJsMessage in the comment
so callers know this mock cannot be used via the JetStreamConsumer trait for
message-based tests.
rsworkspace/crates/acp-nats/src/jetstream/provision.rs (1)

79-85: Test name doesn't match actual idempotency semantics.

The test asserts ctx.created_streams().len() == 8 after two provision_streams calls, which verifies the mock records every call. However, true idempotent behavior would mean calling the function multiple times produces the same end state (4 streams, not 8). The underlying get_or_create_stream is idempotent in production, but this test verifies mock call recording rather than idempotency.

Consider renaming to provision_calls_create_for_all_streams_each_time or adjusting the assertion/comment to clarify intent.

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

In `@rsworkspace/crates/acp-nats/src/jetstream/provision.rs` around lines 79 - 85,
The test named provision_is_idempotent is misleading because it calls
provision_streams twice but asserts ctx.created_streams().len() == 8 (recording
both calls) instead of verifying idempotent end-state; either rename the test to
something like provision_calls_create_for_all_streams_each_time to reflect that
the mock records every call, or change the assertion to verify idempotency by
asserting the final stream set size (e.g., 4) after two provision_streams calls
and/or assert against MockJetStreamContext behavior consistent with
get_or_create_stream so the test truly validates idempotent behavior of
provision_streams.
rsworkspace/crates/trogon-nats/src/jetstream/client.rs (1)

84-101: Consider documenting stream existence precondition.

create_consumer calls get_stream which will fail if the stream hasn't been provisioned. Since JetStreamConsumerFactory and JetStreamContext are separate traits (correctly following one-trait-per-operation), there's no compile-time enforcement that provision_streams runs before create_consumer.

Adding a doc comment clarifying this runtime precondition would help users avoid subtle initialization ordering bugs.

📝 Suggested documentation
 impl JetStreamConsumerFactory for NatsJetStreamClient {
     type Error = JetStreamError;
     type Consumer = NatsJetStreamConsumer;

+    /// Creates a pull consumer for the specified stream.
+    ///
+    /// # Errors
+    /// Returns an error if the stream does not exist. Ensure streams are
+    /// provisioned via [`provision_streams`] before creating consumers.
     async fn create_consumer(
         &self,
         stream_name: &str,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rsworkspace/crates/trogon-nats/src/jetstream/client.rs` around lines 84 -
101, Document that create_consumer has a runtime precondition: it calls
self.context.get_stream and will return an error if the named stream does not
exist, so callers must ensure streams are provisioned (e.g., via
JetStreamConsumerFactory::provision_streams or the JetStreamContext provisioning
routine) before invoking create_consumer; add this note as a doc comment on the
create_consumer method (mentioning get_stream, JetStreamConsumerFactory,
JetStreamContext, and provision_streams) so users know the required
initialization ordering.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@rsworkspace/crates/acp-nats-agent/src/connection.rs`:
- Around line 420-434: The catch-all Err(_) currently ack()s all non-handled
errors and thus also acknowledges DispatchError::Reply, dropping requests when
reply publishing fails; change the match to explicitly handle
DispatchError::Reply (match Err(DispatchError::Reply)) and call
js_msg.term().await (with the existing warn logging on error) instead of ack(),
leaving the remaining Err(_) arm to ack() only true handler errors; update the
match arms referencing DispatchError::Reply, js_msg.term(), and js_msg.ack()
accordingly.

---

Nitpick comments:
In `@rsworkspace/crates/acp-nats-agent/src/connection.rs`:
- Around line 326-412: dispatch_js_message duplicates the full AgentMethod
routing logic from dispatch_message; extract that shared routing into a single
helper (e.g., route_agent_method or dispatch_agent_method) which takes
(&JsMessage, &A, &N) or the parsed subject and returns Result<(), DispatchError>
(where DispatchError models the per-dispatch failures), move all
handle_request/handle_notification match arms into that helper, and have
dispatch_js_message call the helper then perform only the post-dispatch ACK/term
policy (i.e., keep the js_msg.term()/ack handling in dispatch_js_message).
Update both dispatch_js_message and dispatch_message to call the new helper so
future method additions only change the single routing function.

In `@rsworkspace/crates/acp-nats/src/jetstream/provision.rs`:
- Around line 79-85: The test named provision_is_idempotent is misleading
because it calls provision_streams twice but asserts ctx.created_streams().len()
== 8 (recording both calls) instead of verifying idempotent end-state; either
rename the test to something like
provision_calls_create_for_all_streams_each_time to reflect that the mock
records every call, or change the assertion to verify idempotency by asserting
the final stream set size (e.g., 4) after two provision_streams calls and/or
assert against MockJetStreamContext behavior consistent with
get_or_create_stream so the test truly validates idempotent behavior of
provision_streams.

In `@rsworkspace/crates/trogon-nats/src/jetstream/client.rs`:
- Around line 84-101: Document that create_consumer has a runtime precondition:
it calls self.context.get_stream and will return an error if the named stream
does not exist, so callers must ensure streams are provisioned (e.g., via
JetStreamConsumerFactory::provision_streams or the JetStreamContext provisioning
routine) before invoking create_consumer; add this note as a doc comment on the
create_consumer method (mentioning get_stream, JetStreamConsumerFactory,
JetStreamContext, and provision_streams) so users know the required
initialization ordering.

In `@rsworkspace/crates/trogon-nats/src/jetstream/mocks.rs`:
- Around line 287-313: Add a doc comment to the MockJetStreamConsumer type
explaining that its JetStreamConsumer::messages() implementation always returns
an error because MockJsMessage cannot be converted into a real
jetstream::Message, and that tests should call
MockJetStreamConsumer::raw_messages() (which returns the underlying
MockJsMessage stream) for signal assertions; reference the methods messages(),
raw_messages(), the type MockJetStreamConsumer and MockJsMessage in the comment
so callers know this mock cannot be used via the JetStreamConsumer trait for
message-based tests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9bf89aee-1948-4012-9f7e-7d6a26d5ca92

📥 Commits

Reviewing files that changed from the base of the PR and between ef26c8c and ac72017.

⛔ Files ignored due to path filters (1)
  • rsworkspace/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (32)
  • rsworkspace/crates/acp-nats-agent/Cargo.toml
  • rsworkspace/crates/acp-nats-agent/src/connection.rs
  • rsworkspace/crates/acp-nats/Cargo.toml
  • rsworkspace/crates/acp-nats/src/agent/authenticate.rs
  • rsworkspace/crates/acp-nats/src/agent/bridge.rs
  • rsworkspace/crates/acp-nats/src/agent/cancel.rs
  • rsworkspace/crates/acp-nats/src/agent/close_session.rs
  • rsworkspace/crates/acp-nats/src/agent/ext_method.rs
  • rsworkspace/crates/acp-nats/src/agent/ext_notification.rs
  • rsworkspace/crates/acp-nats/src/agent/fork_session.rs
  • rsworkspace/crates/acp-nats/src/agent/initialize.rs
  • rsworkspace/crates/acp-nats/src/agent/list_sessions.rs
  • rsworkspace/crates/acp-nats/src/agent/load_session.rs
  • rsworkspace/crates/acp-nats/src/agent/new_session.rs
  • rsworkspace/crates/acp-nats/src/agent/prompt.rs
  • rsworkspace/crates/acp-nats/src/agent/resume_session.rs
  • rsworkspace/crates/acp-nats/src/agent/set_session_config_option.rs
  • rsworkspace/crates/acp-nats/src/agent/set_session_mode.rs
  • rsworkspace/crates/acp-nats/src/agent/set_session_model.rs
  • rsworkspace/crates/acp-nats/src/constants.rs
  • rsworkspace/crates/acp-nats/src/jetstream/consumers.rs
  • rsworkspace/crates/acp-nats/src/jetstream/mod.rs
  • rsworkspace/crates/acp-nats/src/jetstream/provision.rs
  • rsworkspace/crates/acp-nats/src/jetstream/streams.rs
  • rsworkspace/crates/acp-nats/src/lib.rs
  • rsworkspace/crates/trogon-nats/Cargo.toml
  • rsworkspace/crates/trogon-nats/src/jetstream/client.rs
  • rsworkspace/crates/trogon-nats/src/jetstream/message.rs
  • rsworkspace/crates/trogon-nats/src/jetstream/mocks.rs
  • rsworkspace/crates/trogon-nats/src/jetstream/mod.rs
  • rsworkspace/crates/trogon-nats/src/jetstream/traits.rs
  • rsworkspace/crates/trogon-nats/src/lib.rs

Comment thread rsworkspace/crates/acp-nats-agent/src/connection.rs
@yordis
yordis force-pushed the feat/jetstream-integration branch from 9a1e4ed to 08cd5b0 Compare March 28, 2026 02:22
Enable the async-nats `jetstream` feature and introduce the foundational
abstractions for an immutable, logged-everything NATS JetStream system.

trogon-nats:
- JetStream traits (JetStreamContext, JetStreamPublisher, JetStreamConsumer)
- JsMessage wrapper exposing full ack/nak/term/in_progress signal surface
- Production impls wrapping async_nats::jetstream
- Mock impls with signal recording for testing

acp-nats:
- 4-stream topology (COMMANDS, RESPONSES, CLIENT_OPS, NOTIFICATIONS)
- Stream provisioning with idempotent create-or-update
- Consumer config factories for prompt and runner flows
- Bridge<N, C, J> generic with J=() default for backward compatibility
- New headers: X-Session-Id, X-Causation-Id, ACP_JETSTREAM_ENABLED

acp-nats-agent:
- dispatch_js_message with ack/term signal handling per outcome

Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
@yordis
yordis force-pushed the feat/jetstream-integration branch from 08cd5b0 to d6a3590 Compare March 28, 2026 02:27
@yordis
yordis merged commit 71f69c6 into main Mar 28, 2026
7 checks passed
@yordis
yordis deleted the feat/jetstream-integration branch March 28, 2026 02:30
yordis added a commit that referenced this pull request Jun 16, 2026
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
yordis added a commit that referenced this pull request Jun 22, 2026
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
yordis added a commit that referenced this pull request Jun 22, 2026
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
yordis added a commit that referenced this pull request Jun 23, 2026
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
yordis added a commit that referenced this pull request Jun 23, 2026
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
yordis added a commit that referenced this pull request Jun 23, 2026
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
yordis added a commit that referenced this pull request Jun 25, 2026
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
yordis added a commit that referenced this pull request Jun 25, 2026
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
yordis added a commit that referenced this pull request Jun 26, 2026
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
yordis added a commit that referenced this pull request Jun 26, 2026
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
yordis added a commit that referenced this pull request Jun 26, 2026
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
yordis added a commit that referenced this pull request Jun 26, 2026
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
yordis added a commit that referenced this pull request Jun 26, 2026
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant