Skip to content

feat(trogon-nats): extract release logic from CRON#115

Merged
yordis merged 1 commit intomainfrom
yordis/trogon-nats-releasing
Apr 11, 2026
Merged

feat(trogon-nats): extract release logic from CRON#115
yordis merged 1 commit intomainfrom
yordis/trogon-nats-releasing

Conversation

@yordis
Copy link
Copy Markdown
Member

@yordis yordis commented Apr 10, 2026

Summary

  • extract the rsworkspace/crates/trogon-nats lease/release implementation from origin/CRON
  • add the new lease module and re-exports in lib.rs
  • enable async-nats kv feature required by lease release flow

Testing

  • mise exec -- env CARGO_TARGET_DIR=/tmp/madison-cargo-target cargo test -p trogon-nats (run from rsworkspace/)

@cursor
Copy link
Copy Markdown

cursor Bot commented Apr 10, 2026

PR Summary

Medium Risk
Introduces new leader-election/lease logic backed by JetStream KeyValue and upgrades NATS/async-nats feature flags, which could affect distributed coordination behavior and JetStream compatibility.

Overview
Adds a new lease module to trogon-nats implementing KV-backed leases and leader election (LeaderElection, NatsKvLease) with provision/renew/release flows, config validation, and dedicated error types.

Extends the JetStream abstraction layer to support KeyValue operations (create/get bucket, status, create-with-ttl, update, delete-with-expected-revision) and adds corresponding mocks for testing.

Bumps the docker-compose NATS image to nats:2.11-alpine and enables async-nats KV + server_2_11 features to match the new lease implementation.

Reviewed by Cursor Bugbot for commit 148458b. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 10, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a KV-backed lease and distributed leadership system to the trogon-nats crate, including validated lease identifiers, TTL/renew timing types, async lease traits and a NATS JetStream KV implementation, plus a LeaderElection state machine. Also enables the kv feature for the async-nats dependency.

Changes

Cohort / File(s) Summary
Dependency Configuration
rsworkspace/crates/trogon-nats/Cargo.toml
Enabled the kv feature for the async-nats dependency (added "kv" alongside "ring", "nkeys", "jetstream", "object-store").
Crate Root Exports
rsworkspace/crates/trogon-nats/src/lib.rs
Added pub mod lease; and re-exported lease-related types and errors (leadership, lease config, operations, and result/error types).
Lease Module
rsworkspace/crates/trogon-nats/src/lease/mod.rs
New lease implementation: config types (LeaseBucket, LeaseKey, LeaseTiming, NatsKvLeaseConfig), async traits (TryAcquireLease, RenewLease, ReleaseLease), NatsKvLease implementation with provision(), and LeaderElection<L> state machine with ensure/release logic and unit tests.
Timing Types
rsworkspace/crates/trogon-nats/src/lease/renew_interval.rs, rsworkspace/crates/trogon-nats/src/lease/ttl.rs
Added LeaseRenewInterval and LeaseTtl wrappers around NonZeroDuration, their error types (ZeroDuration), constructors, conversions, and accessors.

Sequence Diagram(s)

sequenceDiagram
    actor App as Application
    participant LE as LeaderElection
    participant NKL as NatsKvLease
    participant KV as NATS_KV
    
    App->>LE: new(lock, node_id, timing)
    App->>LE: ensure_leader()
    alt Not leader
        LE->>NKL: try_acquire(value)
        NKL->>KV: Put/create key (expect create)
        KV-->>NKL: revision
        NKL-->>LE: Ok(revision)
        LE->>LE: set is_leader, store revision
        LE-->>App: Ok(true)
    else Leader & renew needed
        LE->>NKL: renew(value, revision)
        NKL->>KV: Update with expected revision
        KV-->>NKL: new_revision or WrongLastRevision
        NKL-->>LE: Ok(new_revision) / Err
        LE->>LE: update revision / clear state on error
        LE-->>App: Ok(true) / Err(EnsureLeaderError::Renew)
    end

    App->>LE: release()
    alt leader with revision
        LE->>NKL: release(revision)
        NKL->>KV: Delete with expected revision
        KV-->>NKL: Ok() or WrongLastRevision
        NKL-->>LE: Result
        LE->>LE: clear leader state
        LE-->>App: Ok(())
    else not leader or no revision
        LE->>LE: clear leader state
        LE-->>App: Ok(())
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 I hopped to claim a tiny lease,
On JetStream winds and KV trees,
I guard the node with timely cheer,
Renew, release — the path is clear,
A rabbit's nibble keeps it near. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.38% 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 title accurately summarizes the main change: extracting release/lease logic from CRON branch into the trogon-nats crate.
Description check ✅ Passed The description clearly relates to the changeset, explaining the extraction of lease/release implementation, new module additions, and feature enablement.

✏️ 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 yordis/trogon-nats-releasing

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
Copy link
Copy Markdown

github-actions Bot commented Apr 10, 2026

badge

Code Coverage Summary

Details
Filename                                                                      Stmts    Miss  Cover    Missing
--------------------------------------------------------------------------  -------  ------  -------  ---------------------------------------------------------------------------------------------
crates/trogon-std/src/fs/mem.rs                                                 220      10  95.45%   61-63, 77-79, 133-135, 158
crates/trogon-std/src/fs/system.rs                                              102       0  100.00%
crates/acp-nats/src/nats/subjects/responses/ext_ready.rs                         15       0  100.00%
crates/acp-nats/src/nats/subjects/responses/cancelled.rs                         18       0  100.00%
crates/acp-nats/src/nats/subjects/responses/response.rs                          20       0  100.00%
crates/acp-nats/src/nats/subjects/responses/update.rs                            27       0  100.00%
crates/acp-nats/src/nats/subjects/responses/prompt_response.rs                   27       0  100.00%
crates/trogon-source-slack/src/config.rs                                         17       0  100.00%
crates/trogon-source-slack/src/server.rs                                        954       0  100.00%
crates/trogon-source-slack/src/signature.rs                                      80       0  100.00%
crates/acp-nats/src/nats/subjects/commands/cancel.rs                             18       0  100.00%
crates/acp-nats/src/nats/subjects/commands/resume.rs                             18       0  100.00%
crates/acp-nats/src/nats/subjects/commands/set_model.rs                          18       0  100.00%
crates/acp-nats/src/nats/subjects/commands/load.rs                               18       0  100.00%
crates/acp-nats/src/nats/subjects/commands/prompt.rs                             18       0  100.00%
crates/acp-nats/src/nats/subjects/commands/fork.rs                               18       0  100.00%
crates/acp-nats/src/nats/subjects/commands/set_mode.rs                           18       0  100.00%
crates/acp-nats/src/nats/subjects/commands/set_config_option.rs                  18       0  100.00%
crates/acp-nats/src/nats/subjects/commands/close.rs                              18       0  100.00%
crates/acp-telemetry/src/trace.rs                                                32       3  90.62%   23-24, 32
crates/acp-telemetry/src/log.rs                                                  71       1  98.59%   43
crates/acp-telemetry/src/lib.rs                                                 169      32  81.07%   28-34, 56-63, 98, 103, 108, 122-137, 174, 177, 180, 186
crates/acp-telemetry/src/signal.rs                                                7       1  85.71%   43
crates/acp-telemetry/src/service_name.rs                                         49       0  100.00%
crates/acp-telemetry/src/metric.rs                                               35       3  91.43%   30-31, 39
crates/acp-nats-agent/src/connection.rs                                        1434       1  99.93%   686
crates/trogon-std/src/http.rs                                                    19       0  100.00%
crates/trogon-std/src/duration.rs                                                45       0  100.00%
crates/trogon-std/src/secret_string.rs                                           35       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-nats/src/telemetry/metrics.rs                                         65       0  100.00%
crates/acp-nats/src/client/ext_session_prompt_response.rs                       157       0  100.00%
crates/acp-nats/src/client/mod.rs                                              2987       0  100.00%
crates/acp-nats/src/client/terminal_wait_for_exit.rs                            396       0  100.00%
crates/acp-nats/src/client/request_permission.rs                                338       0  100.00%
crates/acp-nats/src/client/terminal_output.rs                                   223       0  100.00%
crates/acp-nats/src/client/fs_write_text_file.rs                                451       0  100.00%
crates/acp-nats/src/client/fs_read_text_file.rs                                 384       0  100.00%
crates/acp-nats/src/client/session_update.rs                                     55       0  100.00%
crates/acp-nats/src/client/rpc_reply.rs                                          71       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_release.rs                                  357       0  100.00%
crates/acp-nats/src/client/terminal_kill.rs                                     309       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/trogon-source-discord/src/gateway.rs                                     449       1  99.78%   133
crates/trogon-source-discord/src/config.rs                                      109       0  100.00%
crates/trogon-source-discord/src/server.rs                                      645       0  100.00%
crates/trogon-source-discord/src/signature.rs                                   103       0  100.00%
crates/acp-nats/src/agent/prompt.rs                                             633       0  100.00%
crates/acp-nats/src/agent/js_request.rs                                         304       0  100.00%
crates/acp-nats/src/agent/bridge.rs                                             123       4  96.75%   109-112
crates/acp-nats/src/agent/logout.rs                                              49       0  100.00%
crates/acp-nats/src/agent/resume_session.rs                                     102       0  100.00%
crates/acp-nats/src/agent/cancel.rs                                             105       0  100.00%
crates/acp-nats/src/agent/fork_session.rs                                       106       0  100.00%
crates/acp-nats/src/agent/set_session_model.rs                                   71       0  100.00%
crates/acp-nats/src/agent/mod.rs                                                 65       0  100.00%
crates/acp-nats/src/agent/set_session_mode.rs                                    71       0  100.00%
crates/acp-nats/src/agent/initialize.rs                                          83       0  100.00%
crates/acp-nats/src/agent/set_session_config_option.rs                           71       0  100.00%
crates/acp-nats/src/agent/test_support.rs                                       299       0  100.00%
crates/acp-nats/src/agent/new_session.rs                                         91       0  100.00%
crates/acp-nats/src/agent/ext_notification.rs                                    88       0  100.00%
crates/acp-nats/src/agent/ext_method.rs                                          92       0  100.00%
crates/acp-nats/src/agent/authenticate.rs                                        52       0  100.00%
crates/acp-nats/src/agent/list_sessions.rs                                       50       0  100.00%
crates/acp-nats/src/agent/load_session.rs                                       101       0  100.00%
crates/acp-nats/src/agent/close_session.rs                                       67       0  100.00%
crates/trogon-nats/src/lease/lease_bucket.rs                                     19       0  100.00%
crates/trogon-nats/src/lease/acquire.rs                                           8       8  0.00%    9-18
crates/trogon-nats/src/lease/lease_timing.rs                                     21       0  100.00%
crates/trogon-nats/src/lease/provision.rs                                       210      10  95.24%   84-94
crates/trogon-nats/src/lease/nats_kv_lease_config.rs                             30       0  100.00%
crates/trogon-nats/src/lease/renew.rs                                           263      19  92.78%   21-27, 46-57
crates/trogon-nats/src/lease/lease_config_error.rs                               13       0  100.00%
crates/trogon-nats/src/lease/mod.rs                                             603      13  97.84%   182-195
crates/trogon-nats/src/lease/ttl.rs                                              76       0  100.00%
crates/trogon-nats/src/lease/lease_key.rs                                        19       0  100.00%
crates/trogon-nats/src/lease/renew_interval.rs                                   61       0  100.00%
crates/trogon-nats/src/lease/release.rs                                           5       5  0.00%    8-12
crates/trogon-source-incidentio/src/config.rs                                    16       0  100.00%
crates/trogon-source-incidentio/src/incidentio_signing_secret.rs                 67       0  100.00%
crates/trogon-source-incidentio/src/signature.rs                                455       0  100.00%
crates/trogon-source-incidentio/src/incidentio_event_type.rs                     67       0  100.00%
crates/trogon-source-incidentio/src/server.rs                                   365       0  100.00%
crates/trogon-source-github/src/config.rs                                        17       0  100.00%
crates/trogon-source-github/src/server.rs                                       351       0  100.00%
crates/trogon-source-github/src/signature.rs                                     64       0  100.00%
crates/trogon-std/src/dirs/fixed.rs                                              84       0  100.00%
crates/trogon-std/src/dirs/system.rs                                             76       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/terminal_release.rs                 15       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/session_update.rs                   15       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/fs_read_text_file.rs                15       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/terminal_output.rs                  15       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/terminal_wait_for_exit.rs           15       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/fs_write_text_file.rs               15       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/session_request_permission.rs       15       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/terminal_create.rs                  15       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/terminal_kill.rs                    15       0  100.00%
crates/acp-nats-ws/src/upgrade.rs                                                57       2  96.49%   59, 90
crates/acp-nats-ws/src/config.rs                                                 83       0  100.00%
crates/acp-nats-ws/src/connection.rs                                            166      35  78.92%   75-82, 87-98, 114, 116-117, 122, 133-135, 142, 146, 150, 153-161, 172, 176, 179, 182-186, 220
crates/acp-nats-ws/src/main.rs                                                  189      18  90.48%   89, 209-230, 308
crates/trogon-source-linear/src/server.rs                                       392       0  100.00%
crates/trogon-source-linear/src/config.rs                                        17       0  100.00%
crates/trogon-source-linear/src/signature.rs                                     54       1  98.15%   16
crates/acp-nats/src/nats/subjects/subscriptions/all_client.rs                    11       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/all_session.rs                   11       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/global_all.rs                    11       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/one_session.rs                   18       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/all_agent_ext.rs                 11       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/all_agent.rs                     11       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/one_client.rs                    18       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/one_agent.rs                     18       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/prompt_wildcard.rs               11       0  100.00%
crates/trogon-nats/src/jetstream/claim_check.rs                                 368       0  100.00%
crates/trogon-nats/src/jetstream/publish.rs                                      64       0  100.00%
crates/trogon-nats/src/jetstream/traits.rs                                       43      43  0.00%    140-208
crates/trogon-nats/src/jetstream/mocks.rs                                       740      44  94.05%   368-382, 388-396, 411-422, 436-446, 514-516
crates/trogon-nats/src/jetstream/stream_max_age.rs                               18       0  100.00%
crates/trogon-std/src/time/system.rs                                             35       0  100.00%
crates/trogon-std/src/time/mock.rs                                              129       0  100.00%
crates/acp-nats-stdio/src/main.rs                                               141      27  80.85%   64, 116-123, 129-131, 148, 179-200
crates/acp-nats-stdio/src/config.rs                                              72       0  100.00%
crates/acp-nats/src/config.rs                                                   204       0  100.00%
crates/acp-nats/src/in_flight_slot_guard.rs                                      32       0  100.00%
crates/acp-nats/src/jsonrpc.rs                                                    6       0  100.00%
crates/acp-nats/src/ext_method_name.rs                                           70       0  100.00%
crates/acp-nats/src/pending_prompt_waiters.rs                                   141       0  100.00%
crates/acp-nats/src/req_id.rs                                                    39       0  100.00%
crates/acp-nats/src/lib.rs                                                       73       0  100.00%
crates/acp-nats/src/error.rs                                                     84       0  100.00%
crates/acp-nats/src/client_proxy.rs                                             200       0  100.00%
crates/acp-nats/src/session_id.rs                                                72       0  100.00%
crates/acp-nats/src/acp_prefix.rs                                                51       0  100.00%
crates/acp-nats/src/jetstream/streams.rs                                        194       4  97.94%   254-256, 266
crates/acp-nats/src/jetstream/provision.rs                                       61       0  100.00%
crates/acp-nats/src/jetstream/consumers.rs                                       99       0  100.00%
crates/acp-nats/src/jetstream/ext_policy.rs                                      26       0  100.00%
crates/trogon-nats/src/messaging.rs                                             552       2  99.64%   132, 142
crates/trogon-nats/src/mocks.rs                                                 304       0  100.00%
crates/trogon-nats/src/nats_token.rs                                            161       0  100.00%
crates/trogon-nats/src/connect.rs                                               105      11  89.52%   22-24, 37, 49, 68-73
crates/trogon-nats/src/token.rs                                                   8       0  100.00%
crates/trogon-nats/src/auth.rs                                                  119       0  100.00%
crates/trogon-nats/src/client.rs                                                 25      25  0.00%    50-89
crates/trogon-source-telegram/src/config.rs                                      17       0  100.00%
crates/trogon-source-telegram/src/signature.rs                                   38       0  100.00%
crates/trogon-source-telegram/src/server.rs                                     387       0  100.00%
crates/acp-nats/src/nats/subjects/global/logout.rs                                8       0  100.00%
crates/acp-nats/src/nats/subjects/global/session_list.rs                          8       0  100.00%
crates/acp-nats/src/nats/subjects/global/initialize.rs                            8       0  100.00%
crates/acp-nats/src/nats/subjects/global/authenticate.rs                          8       0  100.00%
crates/acp-nats/src/nats/subjects/global/ext_notify.rs                           12       0  100.00%
crates/acp-nats/src/nats/subjects/global/session_new.rs                           8       0  100.00%
crates/acp-nats/src/nats/subjects/global/ext.rs                                  12       0  100.00%
crates/trogon-source-gitlab/src/signature.rs                                     30       0  100.00%
crates/trogon-source-gitlab/src/config.rs                                        17       0  100.00%
crates/trogon-source-gitlab/src/server.rs                                       431       0  100.00%
crates/trogon-gateway/src/http.rs                                                97       0  100.00%
crates/trogon-gateway/src/main.rs                                                 4       0  100.00%
crates/trogon-gateway/src/streams.rs                                             64       0  100.00%
crates/trogon-gateway/src/config.rs                                            1241       0  100.00%
crates/acp-nats/src/nats/parsing.rs                                             285       1  99.65%   153
crates/acp-nats/src/nats/mod.rs                                                  23       0  100.00%
crates/acp-nats/src/nats/extensions.rs                                            3       0  100.00%
crates/acp-nats/src/nats/subjects/stream.rs                                      58       0  100.00%
crates/acp-nats/src/nats/subjects/mod.rs                                        380       0  100.00%
TOTAL                                                                         25535     332  98.70%

Diff against main

Filename                                                Stmts    Miss  Cover
----------------------------------------------------  -------  ------  --------
crates/trogon-nats/src/lease/lease_bucket.rs              +19       0  +100.00%
crates/trogon-nats/src/lease/acquire.rs                    +8      +8  +100.00%
crates/trogon-nats/src/lease/lease_timing.rs              +21       0  +100.00%
crates/trogon-nats/src/lease/provision.rs                +210     +10  +95.24%
crates/trogon-nats/src/lease/nats_kv_lease_config.rs      +30       0  +100.00%
crates/trogon-nats/src/lease/renew.rs                    +263     +19  +92.78%
crates/trogon-nats/src/lease/lease_config_error.rs        +13       0  +100.00%
crates/trogon-nats/src/lease/mod.rs                      +603     +13  +97.84%
crates/trogon-nats/src/lease/ttl.rs                       +76       0  +100.00%
crates/trogon-nats/src/lease/lease_key.rs                 +19       0  +100.00%
crates/trogon-nats/src/lease/renew_interval.rs            +61       0  +100.00%
crates/trogon-nats/src/lease/release.rs                    +5      +5  +100.00%
crates/trogon-nats/src/jetstream/traits.rs                +43     +43  +100.00%
crates/trogon-nats/src/jetstream/mocks.rs                +189     +44  -5.95%
TOTAL                                                   +1560    +142  -0.51%

Results for commit: 148458b

Minimum allowed coverage is 95%

♻️ This comment has been updated with latest results

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

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

Inline comments:
In `@rsworkspace/crates/trogon-nats/src/lease.rs`:
- Around line 172-190: The Lease trait currently groups three operations
(try_acquire, renew, release) making downstream bounds too broad; split it into
three single-operation traits—e.g., TryAcquireLease with fn try_acquire(&self,
value: Bytes) -> impl Future<Output = Result<TryAcquireOutcome, Self::Error>> +
Send, RenewLease with fn renew(&self, value: Bytes, revision: u64) -> impl
Future<Output = Result<u64, Self::Error>> + Send, and ReleaseLease with fn
release(&self, revision: u64) -> impl Future<Output = Result<(), Self::Error>> +
Send—each trait should keep the same associated type constraint (type Error:
std::error::Error + Send + Sync) and Send+Sync+Clone+'static bounds as needed;
then replace uses of the composite Lease bound with the minimal combination of
these new traits where only specific operations are required.
- Around line 18-25: Update the LeaseBucket::new and LeaseKey::new constructors
to validate names at construction time: reject empty values and enforce NATS KV
character and length rules (bucket: only ASCII alphanumeric, dash and
underscore; key: must match ^[-/_=\.a-zA-Z0-9]+\z and reasonable nonzero
length). Return a descriptive LeaseConfigError (add variants such as
InvalidBucketName and InvalidKeyName if needed) when validation fails instead of
allowing invalid instances; implement the checks with a simple char-class/regex
test in LeaseBucket::new and LeaseKey::new so invalid identifiers are
unrepresentable.
- Around line 193-255: The code currently wraps SDK errors in the custom
LeaseError and mutates SDK semantics; remove the LeaseError wrapper and change
NatsKvLease to expose the SDK error types directly via the trait associated
types (set type Error to the kv SDK error type(s) used and add type RevisionOut
to match SDK return values), then update function signatures/return types to
return the SDK errors (remove map_err in renew() that maps to LeaseError and any
other map_errs that wrap to LeaseError), and stop converting
DeleteErrorKind::WrongLastRevision into Ok(()) inside map_release_result() —
instead return the SDK DeleteError as-is so higher layers can handle semantics;
locate and modify the LeaseError enum and its helper methods (provision_source,
acquire_source, renew_source, release_source), the renew() implementation that
calls map_err, and the map_release_result() helper to remove wrapping and pass
through SDK errors.
🪄 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: 556c11bd-b914-4e26-a250-82a27d8715df

📥 Commits

Reviewing files that changed from the base of the PR and between c05ebcb and 2e20ad1.

📒 Files selected for processing (3)
  • rsworkspace/crates/trogon-nats/Cargo.toml
  • rsworkspace/crates/trogon-nats/src/lease.rs
  • rsworkspace/crates/trogon-nats/src/lib.rs

Comment thread rsworkspace/crates/trogon-nats/src/lease/mod.rs Outdated
Comment thread rsworkspace/crates/trogon-nats/src/lease.rs Outdated
Comment thread rsworkspace/crates/trogon-nats/src/lease/mod.rs
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In `@rsworkspace/crates/trogon-nats/src/lease.rs`:
- Around line 428-444: In release(), treat kv::DeleteErrorKind::NotFound as an
idempotent success similar to WrongLastRevision: after calling
self.lock.release(revision).await and before returning Err(source), add a branch
that checks if source.kind() == kv::DeleteErrorKind::NotFound and return Ok(())
for that case; keep the existing Ok(()) and WrongLastRevision branches and leave
other errors propagated as Err(source).
🪄 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: 9d20e10e-64c3-476a-8f15-43dbeba9e1f6

📥 Commits

Reviewing files that changed from the base of the PR and between 2e20ad1 and 3c26d0f.

📒 Files selected for processing (2)
  • rsworkspace/crates/trogon-nats/src/lease.rs
  • rsworkspace/crates/trogon-nats/src/lib.rs

Comment thread rsworkspace/crates/trogon-nats/src/lease/mod.rs
@yordis yordis added the rust:coverage-baseline-reset Relax Rust coverage gate to establish a new baseline label Apr 10, 2026
@yordis yordis force-pushed the yordis/trogon-nats-releasing branch from 5c0ba0a to 8f8924c Compare April 10, 2026 23:12
@yordis yordis removed the rust:coverage-baseline-reset Relax Rust coverage gate to establish a new baseline label Apr 10, 2026
Copy link
Copy Markdown

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

♻️ Duplicate comments (1)
rsworkspace/crates/trogon-nats/src/lease/mod.rs (1)

450-455: ⚠️ Potential issue | 🟠 Major

Treat NotFound as an idempotent release outcome.

release() clears local leadership state before matching the SDK result. If the lease entry has already expired or been removed, surfacing DeleteErrorKind::NotFound here turns a completed cleanup into a false failure. Handle it the same way as WrongLastRevision.

💡 Proposed fix
         match result {
             Ok(()) => Ok(()),
             Err(source) if source.kind() == kv::DeleteErrorKind::WrongLastRevision => Ok(()),
+            Err(source) if source.kind() == kv::DeleteErrorKind::NotFound => Ok(()),
             Err(source) => Err(source),
         }
In async-nats 0.47.0 JetStream KV, what `DeleteErrorKind` does `Store::delete_expect_revision` return when the key is already missing or has expired? Is `DeleteErrorKind::NotFound` the expected case, and should callers treat it as an idempotent success during cleanup?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rsworkspace/crates/trogon-nats/src/lease/mod.rs` around lines 450 - 455, The
release path currently treats kv::DeleteErrorKind::WrongLastRevision as an
idempotent success but not kv::DeleteErrorKind::NotFound; update the match in
the release() flow (the block after calling self.lock.release(revision).await
and self.clear_state()) to treat Err(source) where source.kind() ==
kv::DeleteErrorKind::NotFound the same as WrongLastRevision (return Ok(())), so
that already-missing or expired keys don't surface as errors; keep the existing
Err(source) => Err(source) fallback for other kinds.
🧹 Nitpick comments (1)
rsworkspace/crates/trogon-nats/src/lease/mod.rs (1)

100-106: Keep lease timing accessors typed.

These getters unwrap LeaseTtl and LeaseRenewInterval back to raw Duration, which weakens the value-object boundary you just introduced. Prefer returning the domain types here and converting to Duration only at the JetStream boundary.

♻️ Proposed refactor
 impl LeaseTiming {
-    pub fn ttl(self) -> Duration {
-        self.ttl.as_duration()
+    pub fn ttl(self) -> LeaseTtl {
+        self.ttl
     }
 
-    pub fn renew_interval(self) -> Duration {
-        self.renew_interval.as_duration()
+    pub fn renew_interval(self) -> LeaseRenewInterval {
+        self.renew_interval
     }
 }
 ...
-    pub fn ttl(&self) -> Duration {
-        self.timing.ttl()
+    pub fn ttl(&self) -> LeaseTtl {
+        self.timing.ttl()
     }
 
-    pub fn renew_interval(&self) -> Duration {
-        self.timing.renew_interval()
+    pub fn renew_interval(&self) -> LeaseRenewInterval {
+        self.timing.renew_interval()
     }
 }
As per coding guidelines, "Prefer domain-specific value objects over primitives (e.g., `AcpPrefix` not `String`)."

Also applies to: 146-151

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

In `@rsworkspace/crates/trogon-nats/src/lease/mod.rs` around lines 100 - 106, The
ttl() and renew_interval() accessors currently unwrap LeaseTtl and
LeaseRenewInterval to raw Duration, breaking the value-object boundary; change
their signatures to return the domain types (e.g., pub fn ttl(&self) -> LeaseTtl
and pub fn renew_interval(&self) -> LeaseRenewInterval) instead of Duration (and
take &self rather than consuming self), and update call sites (notably the
JetStream boundary code) to call .as_duration() where a raw Duration is actually
required; apply the same change to the other similar accessors noted in the
file.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@rsworkspace/crates/trogon-nats/src/lease/mod.rs`:
- Around line 450-455: The release path currently treats
kv::DeleteErrorKind::WrongLastRevision as an idempotent success but not
kv::DeleteErrorKind::NotFound; update the match in the release() flow (the block
after calling self.lock.release(revision).await and self.clear_state()) to treat
Err(source) where source.kind() == kv::DeleteErrorKind::NotFound the same as
WrongLastRevision (return Ok(())), so that already-missing or expired keys don't
surface as errors; keep the existing Err(source) => Err(source) fallback for
other kinds.

---

Nitpick comments:
In `@rsworkspace/crates/trogon-nats/src/lease/mod.rs`:
- Around line 100-106: The ttl() and renew_interval() accessors currently unwrap
LeaseTtl and LeaseRenewInterval to raw Duration, breaking the value-object
boundary; change their signatures to return the domain types (e.g., pub fn
ttl(&self) -> LeaseTtl and pub fn renew_interval(&self) -> LeaseRenewInterval)
instead of Duration (and take &self rather than consuming self), and update call
sites (notably the JetStream boundary code) to call .as_duration() where a raw
Duration is actually required; apply the same change to the other similar
accessors noted in the file.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6c3e0a92-8518-4695-9a8e-743f422c7630

📥 Commits

Reviewing files that changed from the base of the PR and between 1746541 and 4fb661f.

📒 Files selected for processing (5)
  • rsworkspace/crates/trogon-nats/Cargo.toml
  • rsworkspace/crates/trogon-nats/src/lease/mod.rs
  • rsworkspace/crates/trogon-nats/src/lease/renew_interval.rs
  • rsworkspace/crates/trogon-nats/src/lease/ttl.rs
  • rsworkspace/crates/trogon-nats/src/lib.rs
✅ Files skipped from review due to trivial changes (2)
  • rsworkspace/crates/trogon-nats/Cargo.toml
  • rsworkspace/crates/trogon-nats/src/lib.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • rsworkspace/crates/trogon-nats/src/lease/renew_interval.rs
  • rsworkspace/crates/trogon-nats/src/lease/ttl.rs

@yordis yordis added rust:coverage-baseline-reset Relax Rust coverage gate to establish a new baseline and removed rust:coverage-baseline-reset Relax Rust coverage gate to establish a new baseline labels Apr 11, 2026
Comment thread rsworkspace/crates/trogon-nats/src/lease/mod.rs Outdated
@yordis yordis force-pushed the yordis/trogon-nats-releasing branch 13 times, most recently from 15f4032 to 79173c2 Compare April 11, 2026 06:59
Copy link
Copy Markdown

@cursor cursor Bot left a comment

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 1 potential issue.

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.

Reviewed by Cursor Bugbot for commit 79173c2. Configure here.

Comment thread rsworkspace/crates/trogon-nats/src/lease/renew.rs
@yordis yordis force-pushed the yordis/trogon-nats-releasing branch 2 times, most recently from 8386cd8 to ed897f1 Compare April 11, 2026 07:19
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
@yordis yordis force-pushed the yordis/trogon-nats-releasing branch from ed897f1 to 148458b Compare April 11, 2026 07:23
@yordis yordis added the rust:coverage-baseline-reset Relax Rust coverage gate to establish a new baseline label Apr 11, 2026
@yordis yordis merged commit 9331e01 into main Apr 11, 2026
9 of 10 checks passed
@yordis yordis deleted the yordis/trogon-nats-releasing branch April 11, 2026 07:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

rust:coverage-baseline-reset Relax Rust coverage gate to establish a new baseline

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant