Skip to content

#453 test(rpc): add tests for RPC client retry/backoff edge case FIXED - #648

Closed
Kappa16 wants to merge 912 commits into
TegoLabs:mainfrom
Kappa16:#453-test(rpc)--add-tests-for-RPC-client-retry/backoff-edge-case-FIX
Closed

Kappa16 wants to merge 912 commits into
TegoLabs:mainfrom
Kappa16:#453-test(rpc)--add-tests-for-RPC-client-retry/backoff-edge-case-FIX

Conversation

@Kappa16

@Kappa16 Kappa16 commented Jul 31, 2026 •

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds comprehensive test coverage for executeWithRetry retry/backoff logic in src/rpc/client.ts. Covers retry-then-succeed with exponential backoff verification (1000ms, 2000ms, 4000ms), non-retryable 4xx short-circuit, and exhausted-retries preserving original error context – as described in issue. Closes retry/backoff test-coverage issue.

Only file touched: tests/rpc/client.test.ts (+385 lines, 16 new tests, total 61 tests in file).

Why?

src/rpc/client.ts wraps every Stellar RPC call the daemon makes. Its retry/backoff behavior (MAX_RETRIES=3, initial 1s, multiplier 2x, retryable: ETIMEDOUT/ECONNRESET/timeout message/429/5xx, fatal: 4xx) looks correct in code review but is exactly the logic that breaks against a flaky RPC endpoint in production. Existing tests only had 3 basic cases and did not assert increasing delay or context preservation. This PR provides deterministic, fake-timer-based tests matching tests/alerts/webhook.test.ts patterns.

Does this touch secret-key handling or transaction submission?

  • Yes — see notes above
  • No – only tests/rpc/client.test.ts was modified. src/rpc/client.ts was NOT touched per scope.

Checklist

  • Tests pass npm test – 1209 passed, 1 skipped, 89 files; 61/61 in client.test.ts
  • Type check passes npx tsc --noEmit – no errors
  • Lint passes npm run lint – 0 errors (286 pre-existing any warnings)
  • Tests cover the new functionality (TDD preferred — see CONTRIBUTING.md)
    • Retry-then-succeed returns successful result with correct retry count & increasing delay
    • Non-retryable error (400-422) not retried, short-circuits immediately
    • Exhausting all retries throws with original error's context preserved (message, code, response)
    • Additional: 429, 500/502/503/504/599, ETIMEDOUT/ECONNRESET, timeout message, mocked fetch pattern, mixed errors
  • No unnecessary dependencies added
  • Commit messages follow conventional format
  • No console.log in core logic
  • ADR added if this is a significant design decision – N/A, test-only
  • E2E sandbox tested, if this touches RPC or daemon behavior – N/A, test-only (mocked RPC server)

CLOSE #453

The-Big-Danny and others added 30 commits July 2, 2026 17:23
# Conflicts:
#	src/core/extension.ts
#	src/rpc/client.ts
#	tests/core/extension.test.ts
AbdulmalikAlayande and others added 15 commits July 31, 2026 06:58
…ted)

Adds src/alerts/matrix.ts (real Matrix Client-Server API, token auth,
room-scoped delivery) and its tests, per TegoLabs#310.

The PR never registered the channel in builtins.ts, so `--type matrix`
was unreachable from the CLI despite the sender being fully implemented
and tested. Completed the registration (targetOption: "channel", since
the sender's target is a Matrix room ID) plus the matching builtins.test.ts
coverage, following the exact pattern used by every other lazily-imported
channel (discord/telegram/opsgenie). Verified `alerts add --type matrix`
end-to-end at the CLI.
… fixed + completed)

Adds src/alerts/teams.ts (Adaptive Card payload, severity coloring per
event type) and its tests, per TegoLabs#311.

Two things fixed before merging:
- CodeRabbit correctly flagged validateWebhookUrl()'s hostname check:
  `hostname.includes("webhook.office.com")` accepts any hostname that
  merely contains that substring, e.g. an attacker-controlled
  "x.webhook.office.com.evil.com" would pass, silently sending real
  alert content (contract IDs, TTL data) to an attacker-controlled
  server. Changed to an exact/suffix match on the real hostname, plus
  an explicit https-only check. Added regression tests for both.
- The PR never registered the channel in builtins.ts, so `--type teams`
  was unreachable from the CLI. Completed the registration
  (targetOption: "url", matching webhook/discord's pattern) and the
  matching builtins.test.ts coverage.
…#585, fixed + completed)

Adds src/alerts/email.ts (nodemailer SMTP transport, env/config token
resolution, password redaction on error) and its tests, per TegoLabs#312.

Fixed before merging:
- Bumped nodemailer ^7.0.7 -> ^9.0.3 (major version, verified tsc still
  compiles clean and all tests pass unchanged): the pinned 7.x range had
  six high-severity advisories, including SMTP/CRLF command injection
  and an SSRF via the raw-message option. This is a production
  dependency, so `npm audit --omit=dev` would have failed on it.
- The channel was registered in builtins.ts, but src/commands/alerts.ts
  still had a special-cased `--type email` branch printing "Email
  alerting is not yet implemented" *before* the registry was ever
  consulted - making the new channel completely unreachable from the
  CLI. Removed the special case.
- Updated the one existing test that asserted the old "not implemented"
  behavior, and added a real success-path test (registers a config with
  --channel <email>).
- Fixed a lint error (preserve-caught-error) the right way: the error
  handler already redacts the SMTP password from the thrown message,
  but attaching the raw caught error as `cause` would have smuggled the
  unredacted password back in via the cause chain. Redact the caught
  error's own message in place before using it as cause, so the
  guarantee holds through the whole chain.
…s#597, trimmed to scope)

Adds src/alerts/googlechat.ts and registers it in builtins.ts, per TegoLabs#313.
Registration and the sender itself were already correct.

Trimmed from the original PR before merging: a bundled Grafana/Prometheus
observability stack (devops/grafana/*, devops/prometheus/*, docker-compose.
observability.yml, docs/observability.md) - unrelated to this issue, shared
branch lineage with several other open PRs (TegoLabs#594, TegoLabs#595, TegoLabs#596) that also
carry the identical bundle. Left tests/docker/docker-compose.test.ts
untouched by reverting to main's version.

Updated tests/alerts/builtins.test.ts for the 10th channel (the PR's
branch predated matrix/teams/email, so its own copy of this file didn't
know about them).

Note for a follow-up: src/alerts/discord.ts has the same hostname-
validation weakness fixed in TegoLabs#557 (`hostname.includes("discord")`,
even looser than Teams's check) - pre-existing, not part of this PR,
flagging separately.
…egoLabs#318)

PR TegoLabs#568 referenced ChannelDefinition.maxRetries in dispatcher.ts but
never added the field to the interface, so the branch failed to
compile (TS2339). It also shipped a new dispatcher test that called
registerAlertChannel("webhook", { maxRetries: 2 }) — a two-argument
overload that doesn't exist on the real one-argument, throw-on-duplicate
registerAlertChannel API, so the test could never have run against
this codebase.

- Add optional maxRetries/retryBackoffMs to ChannelDefinition
  (registry.ts); dispatcher.ts's lookup already existed from the
  merge and needed no further changes.
- Give telegram a maxRetries: 3 default, matching the issue's own
  rationale (Telegram's Bot API rate limits are stricter than a
  generic webhook's) — every other channel keeps the global
  MAX_RETRY_COUNT default, preserving existing behavior exactly.
- Rewrite the broken test to swap in a real ChannelDefinition
  override via the actual registry API, and restore the registry to
  normal built-in state afterward so it doesn't leak into other
  tests. Added a companion test asserting webhook has no override by
  default.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…egoLabs#555)

Issue TegoLabs#319's scope explicitly excludes the orphaned src/alerts/*.test.ts
files ("reconciling those is phase-8's job, not this issue's") — they
aren't in vitest.config.ts's include glob (tests/**/*.test.ts), so any
tests added there never actually run. Keeping the docs update and the
contract suite applied to webhook/slack, whose real test files live
under tests/alerts/. Applying the contract suite to pagerduty/discord/
telegram is deferred to their dedicated orphan-reconciliation issues
(TegoLabs#354-TegoLabs#356).
…yStats (TegoLabs#536)

The PR's src/db/repositories.ts shipped literal backslash-escaped
backticks (\`...\`) instead of real template literals in the days-filter
branch of getChannelDeliveryStats — invalid TypeScript syntax that
made the entire file fail to parse (tsc reported ~30 cascading errors
past this point, and CI's build-and-test check was correctly failing).
Replaced with proper template literals. Also tightened the query
params array from any[] to Array<number | string>.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 31, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Kappa16, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 54 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: fb8ea4b6-2f5e-46fd-a329-396cffbde095

📥 Commits

Reviewing files that changed from the base of the PR and between 7716cac and cec9bf2.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (4)
  • README.md
  • package.json
  • src/cli/program.ts
  • tests/db/database.test.ts
📝 Walkthrough

Summary by CodeRabbit

  • Documentation

    • Expanded installation, quick-start, alerting, restore, configuration, FAQ, and roadmap guidance.
    • Added examples for alert channels, webhooks, Slack, resource management, budgets, and database operations.
    • Improved architecture and alert-channel contribution documentation.
  • Improvements

    • Added support for configurable polling intervals, alert signing secrets, quiet hours, and enhanced monitoring visibility.
    • Improved alert delivery and channel configuration workflows.
  • Tests

    • Expanded coverage for alerts, RPC operations, retries, rate limiting, transaction submission, and monitoring scenarios.

Walkthrough

The pull request expands documentation, adds RPC and alert test coverage, and modifies CLI, alert, monitor, daemon, repository, and observability code. Several changes duplicate existing logic or content, including parsing, cycle execution, alert delivery, metric registration, and documentation sections.

Changes

Documentation updates

Layer / File(s) Summary
README command and alerting guides
README.md, README.pt.md
Adds installation, command, restore, alerting, Slack, configuration, FAQ, roadmap, and channel content. Some sections are duplicated.
Contributor documentation structure
docs/ARCHITECTURE.md, docs/adding-an-alert-channel.md
Adds spacing and headings around observability and alert-channel contribution guidance. Duplicate test and documentation headings remain.

Runtime and persistence changes

Layer / File(s) Summary
Alert configuration and delivery paths
src/alerts/*, src/cli/program.ts, src/commands/alerts.ts, src/core/monitor.ts, src/db/repositories.ts
Adds channel validation, target extraction, signing-secret generation, resolution delivery, quiet-hours persistence fields, and alert-history selections. Several additions duplicate existing initialization or delivery logic.
Daemon cycle and polling execution
src/daemon/loop.ts, src/core/monitor.ts
Adds startup and shutdown handling, polling resolution, cost aggregation, and monitor execution paths. The displayed changes include duplicate cycle and auto-extension execution.
CLI initialization and metric wiring
src/index.ts, src/lib.ts, src/observability/registry.ts, src/commands/daemon.ts, src/db/schema.sql, package.json
Adds CLI initialization and synchronous parsing, spacing changes, and duplicate budget metric registration. Dependency versions and schema behavior remain unchanged.

Test coverage updates

Layer / File(s) Summary
RPC transaction and retry coverage
tests/rpc/client.test.ts
Adds coverage for transaction submission, rate limiting, footprint simulation, fee parsing, retry classification, backoff, exhaustion, and error preservation.
Alert channel and command coverage
tests/alerts/builtins.test.ts, tests/commands/alerts.test.ts
Adds channel registry, signing, removal, delivery, Slack, dry-run, webhook-secret, and identifier-validation cases. Several cases duplicate existing tests.
Supporting suite formatting
tests/core/monitor.test.ts, tests/daemon/loop.test.ts, tests/db/database.test.ts
Adds spacing-only changes to existing test suites.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested reviewers: abdulmalikalayande

Poem

A rabbit checks the alerts at night,
Tests retry paths by lantern light.
Webhooks hum and Slack bells ring,
Daemon cycles do their thing.
Docs grow tall beside the burrow,
While duplicate steps cause furrow.

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The RPC tests cover the linked issue objectives, but the pull request also changes many files outside the issue's permitted test-only scope. Revert all unrelated changes and retain only the required updates to tests/rpc/client.test.ts.
Out of Scope Changes check ⚠️ Warning The pull request includes unrelated documentation, formatting, production logic, database, CLI, daemon, and alert changes beyond the linked test-only objective. Remove unrelated files and production changes, including duplicate alert, monitor, daemon, repository, registry, and CLI logic.
Docstring Coverage ⚠️ Warning Docstring coverage is 52.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 clearly identifies the RPC retry and backoff test changes, although the trailing “FIXED” is unnecessary.
Description check ✅ Passed The description directly explains the RPC retry and backoff test coverage added by this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@drips-wave

drips-wave Bot commented Jul 31, 2026

Copy link
Copy Markdown

@Kappa16 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@gitguardian

gitguardian Bot commented Jul 31, 2026

Copy link
Copy Markdown

⚠️ GitGuardian has uncovered 1 secret following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

Since your pull request originates from a forked repository, GitGuardian is not able to associate the secrets uncovered with secret incidents on your GitGuardian dashboard.
Skipping this check run and merging your pull request will create secret incidents on your GitGuardian dashboard.

🔎 Detected hardcoded secret in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
- - Generic High Entropy Secret ded54f4 tests/commands/guard-cli-export-import.test.ts View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secret safely. Learn here the best practices.
  3. Revoke and rotate this secret.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@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: 18

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (7)
README.md (1)

186-204: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove duplicated documentation blocks from the README files.

  • README.md#L186-L204: remove the repeated watch section.
  • README.md#L220-L262: remove the repeated entry-discovery, status, and daemon sections.
  • README.md#L406-L498: remove the copied alert and guard sections from costs.
  • README.md#L517-L537: keep one restore section.
  • README.md#L581-L655: keep one Alerting section and one authoritative channel list.
  • README.md#L910-L927: keep one storage and configuration section.
  • README.md#L1032-L1047: keep one testing conclusion and FAQ heading.
  • README.md#L1080-L1084: merge the Prometheus roadmap bullets.
  • README.pt.md#L362-L363: remove the repeated alert overview.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 186 - 204, Remove duplicated documentation while
preserving one authoritative version of each section: delete the repeated watch
section in README.md (186-204), repeated entry-discovery/status/daemon sections
(220-262), copied costs alert/guard sections (406-498), and repeated alert
overview in README.pt.md (362-363); retain one restore section (README.md
517-537), one Alerting section and channel list (581-655), one
storage/configuration section (910-927), one testing conclusion and FAQ heading
(1032-1047), and merge the Prometheus roadmap bullets (1080-1084).

Source: Linters/SAST tools

src/db/repositories.ts (2)

899-909: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Remove the dead channelType/channelTarget columns in getAlertHistory.

The SELECT lists ac.channel_type AS channelType, ac.channel_target AS channelTarget and then, right after, COALESCE(af.channel_type, ac.channel_type) AS channelType, COALESCE(af.channel_target, ac.channel_target) AS channelTarget. Since the row objects built from .all() keep the last value for a duplicate key, the first pair is dead and only wastes a redundant column evaluation. Remove it to avoid confusing future readers about which expression is authoritative.

♻️ Proposed cleanup
         SELECT
             af.id              AS alertFiredId,
-
-            ac.channel_type    AS channelType,
-            ac.channel_target  AS channelTarget,
-
             COALESCE(af.channel_type, ac.channel_type)    AS channelType,
             COALESCE(af.channel_target, ac.channel_target)  AS channelTarget,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/db/repositories.ts` around lines 899 - 909, Remove the initial
ac.channel_type and ac.channel_target selections in getAlertHistory, keeping
only the subsequent COALESCE expressions aliased as channelType and
channelTarget as the authoritative output fields.

301-337: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Remove the duplicated insertAlertConfig body.

src/db/repositories.ts declares two competing insertAlertConfig functions back-to-back, and floating parameter declarations appear between them. Keep only the number-returning body, including return info.lastInsertRowid as number;, so callers and tests can continue to use the function as written.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/db/repositories.ts` around lines 301 - 337, Remove the first duplicate
insertAlertConfig declaration and its floating parameter block, leaving the
number-returning insertAlertConfig implementation as the sole definition.
Preserve its existing database insert logic and return info.lastInsertRowid as
number so callers and tests retain the current contract.
src/core/monitor.ts (2)

119-164: 🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Critical: runAutoExtensions executes twice per monitor cycle.

Lines 142-164 duplicate Lines 119-140 verbatim — same comment, same call, same result assignment, same anomaly logging. runAutoExtensions submits real extension transactions, spends contract budget, and consumes the hourly extension rate limit (per src/core/extension.ts). Running it twice per cycle doubles those network calls and side effects, and the second run's result.extensionsTriggered/extensionErrors silently overwrite the first run's, so the returned MonitorCycleResult no longer reflects the true amount of work performed.

🐛 Proposed fix to remove the duplicate phase
-    // Auto-extension phase: check extension_policies and submit transactions for
-    // entries whose TTL fell below the configured threshold.
-    try {
-        const ext = await runAutoExtensions(db, network, rpcUrl, feeSponsorSecret);
-        result.extensionsTriggered = ext.entriesExtended;
-        result.extensionErrors = ext.errors;
-        if (ext.entriesExtended > 0) {
-            logger.info(
-                `Auto-extensions — contracts: ${ext.contractsExtended}, ` +
-                `entries: ${ext.entriesExtended}`,
-            );
-        }
-        for (const e of ext.extensions) {
-            if (e.isAnomaly) {
-                logger.warn(`Cost anomaly — contract: ${e.contractId}: ${e.anomalyDetails}`);
-            }
-        }
-    } catch (err: unknown) {
-        const msg = err instanceof Error ? err.message : String(err);
-        result.extensionErrors = [msg];
-        logger.error("runAutoExtensions threw unexpectedly", err);
-    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/monitor.ts` around lines 119 - 164, Remove the duplicated
auto-extension phase in the monitor cycle, keeping only one invocation of
runAutoExtensions and its associated result assignments, anomaly logging, and
error handling. Ensure MonitorCycleResult reflects the single set of extension
transactions performed.

310-341: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Critical: resolved alerts are delivered twice to the primary channel.

Line 311-320 add a standalone deliverSingleAlert call using config.channel_type/config.channel_target, right before the existing loop over allTargets (Line 322-340), whose first element is already { channel_type: config.channel_type, channel_target: config.channel_target }. Every alert resolution therefore delivers a duplicate notification to the primary channel — the user receives the same "alert resolved" message twice for that channel.

🐛 Proposed fix to remove the redundant standalone delivery
-                        // Best-effort — log failures so they're visible, but don't block.
-                        deliverSingleAlert(
-                            config.channel_type,
-                            config.channel_target,
-                            event,
-                            config.webhook_secret,
-                        ).catch((err: unknown) => {
-                            logger.warn(
-                                `Resolution notification failed for config ${configId}: ${err instanceof Error ? err.message : String(err)}`,
-                            );
-                        });
-
                         const additionalTargets = getAlertConfigTargets(db, config.id);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/monitor.ts` around lines 310 - 341, Remove the standalone
deliverSingleAlert call and its associated catch block before the allTargets
loop. Keep the allTargets construction and loop unchanged so the primary channel
is delivered once through the first target entry, along with any additional
targets.
src/cli/program.ts (1)

23-77: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Fix duplicate createProgram declaration and broken option chain.

This file has a duplicate, unclosed export function createProgram() { ... } opening at Line 23-27, followed by top-level import statements nested inside it. ES modules do not allow two exports with the same name, and import declarations cannot appear inside a function body. Separately, the Commander chain at Line 63-70 ends with ; and then Line 71 continues with a dangling .option(...), which is a syntax error. This file fails to parse as written.

🐛 Proposed fix to remove the duplicate declaration and restore one option chain
-
-
-export function createProgram() {
-  const program = new Command();
-
 import { registerMetricsCommand } from "../commands/metrics.js";
 import { registerAuditLogCommand } from "../commands/audit-log.js";
   program
     .name("sorokeep")
     .description(
       "Sorokeep — The missing operations layer for deployed Soroban smart contracts",
     )
     .version("0.1.2")
-
-    .option("--extension-jitter-ms <ms>", "Jitter window in ms applied to extension submissions", parseInt);
-
     .option("--extension-jitter-ms <ms>", "Jitter window in ms applied to extension submissions", parseInt)
     .option(
       "--channel-plugin <package>",
       "Load an external npm package that registers an alert channel (repeatable)",
       collectRepeatedOption,
       [] as string[],
     );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cli/program.ts` around lines 23 - 77, Remove the stray, incomplete
createProgram declaration and its unmatched opening so the imports and helper
declarations remain at module scope, leaving only the complete export function
createProgram definition. In the Commander setup inside createProgram, remove
the premature semicolon after the version/first option chain so the
channel-plugin option remains part of the same chained expression.
src/commands/alerts.ts (1)

104-242: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Fix the unclosed insertAlertConfig object literal.

src/commands/alerts.ts:223-227 opens an alert config object with insertAlertConfig(db, { ... channel_target: target,, but the literal is not closed before src/commands/alerts.ts:228 starts a new const alertConfigId = insertAlertConfig(...). This makes src/commands/alerts.ts fail to build; close the first insert if it is required, or remove it if alertConfigId is the intended primary insertion.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/alerts.ts` around lines 104 - 242, Remove the obsolete,
incomplete insertAlertConfig call that uses options.type and target, leaving the
complete alertConfigId assignment as the sole primary insertion. Ensure the
remaining insertAlertConfig object closes correctly and preserves primaryType,
primaryTarget, threshold, signing, and quiet-hours fields.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/adding-an-alert-channel.md`:
- Around line 49-54: In docs/adding-an-alert-channel.md:49-54, merge the
duplicated registration guidance into one paragraph including built-in,
embedded, and --channel-plugin options; at 80-82, remove the conflicting section
3 heading; at 124-136, retain only one section 4 and one section 5; and at
181-183, retain only one section 6, preserving the intended section sequence.

In `@README.md`:
- Line 631: Update the fenced code block in README.md to specify the text
language by changing the unlabeled fence to a text-labeled fence, resolving
markdownlint MD040.
- Around line 469-480: Update the README guard command examples to remove secret
values passed through --keypair, including the dry-run and immediate-extension
examples. Use --keypair-env with STELLAR_SECRET_KEY consistently, and state that
--keypair-env is the preferred method for supplying secret material.

In `@src/commands/alerts.ts`:
- Around line 273-289: Remove the duplicate channel_type and channel_target
entries in the alert configuration object, choosing one consistent variable pair
for persistence. Update the adjacent success message to report that same
selected type and target, matching the existing insertAlertConfig approach.

In `@src/daemon/loop.ts`:
- Around line 164-166: Remove the untraced runMonitorCycle call and its const
result declaration in the cycle block, retaining the existing traced call and
its result variable. Ensure runMonitorCycle executes only once per monitoring
cycle and preserve the surrounding result handling.
- Around line 80-84: Remove the duplicate startup initialization and logging
block containing lastVacuumAt = Date.now() and the “Daemon starting”
logger().info call, preserving the identical block at Lines 85-86 so startup
initializes once and emits one log.

In `@src/db/schema.sql`:
- Around line 57-60: Remove the extra consecutive blank line immediately before
the enabled column in the schema definition, leaving exactly one blank line
between quiet_hours_timezone and enabled to satisfy SQLFluff LT15.

In `@src/index.ts`:
- Around line 8-11: Remove the synchronous program.parse(process.argv)
invocation and retain one awaited program.parseAsync(process.argv) call so the
Commander command tree and daemon lifecycle execute only once with asynchronous
handlers properly awaited.

In `@tests/alerts/builtins.test.ts`:
- Around line 73-110: Remove the stale “registers exactly the ten built-in
channel names” and “only webhook supports HMAC signing” tests from the builtins
test suite, including their malformed partial bodies. Keep the up-to-date
eleven-channel assertion and the “webhook and webhook2” signing assertion,
ensuring each remaining it block is properly closed and the idempotency length
expectation is 11.

In `@tests/commands/alerts.test.ts`:
- Around line 734-792: Remove the alert insertion, remove-command invocation,
and related assertions from the beforeEach in the “alerts test” suite. Preserve
the webhookConfigId setup needed by the delivery tests, and leave remove-command
coverage exclusively in the existing “alerts remove” suite.
- Around line 1027-1250: Remove the duplicated single-alert delivery test suite
from the alerts test describe block, retaining one complete copy of the cases
covered by the block around mockDeliverSingleAlert and dry-run behavior. Then
rebalance the surrounding describe/it braces so tests/commands/alerts.test.ts
parses successfully, including the final closing structure.

In `@tests/rpc/client.test.ts`:
- Around line 522-528: Move the duplicated dummyKey and secretKey fixture
definitions from the test case near the later describe block, together with
their existing counterparts, into a shared scope above both describe blocks.
Update both test sections to reuse these shared fixtures and remove the
duplicate declarations, preserving their current values and behavior.
- Around line 912-945: Remove the two redundant tests around executeWithRetry:
the 4-attempt exhaustion case and the single-failure-then-success case. Preserve
the existing equivalent coverage elsewhere, unless replacing them with a
genuinely distinct retry boundary scenario that is not already tested.
- Around line 994-999: The test is redundant because it documents retry
configuration in comments without asserting new behavior. Delete the entire test
covering configurable retry parameters, and move its MAX_RETRIES, delay,
multiplier, and retryability documentation to a comment on executeWithRetry in
the client implementation; retain the existing concrete timing and
classification tests.
- Around line 458-486: Update the test around StellarRpcClient.checkHealth to
validate the rate limiter’s start-rate behavior rather than simultaneous
in-flight requests: rename the test accordingly and record/assert request start
timestamps within the sliding one-second window, removing the maxInFlight-based
guarantee while preserving the six-call assertion.
- Around line 438-455: Update the rate-limited checkHealth test around server
and responses so each queued response has a distinct index or identity, and make
the fallback distinct as well. Replace the broad health-status assertions with
an exact ordered assertion against the expected four responses, verifying
queueing preserves both response count and sequence.
- Around line 572-581: Update the top-level `@stellar/stellar-sdk` import to
include Account and SorobanDataBuilder, then replace the inline await
import(...) usages in the mockClient server setup with those static symbols
while preserving the existing constructor behavior.
- Around line 1011-1028: In the retryable examples setup, remove the unused
action mock and its implementation, and remove the conditional err.message
reassignment; retain Object.assign(err, example) and trackingAction as the sole
error setup and retry callback used by executeWithRetry.

---

Outside diff comments:
In `@README.md`:
- Around line 186-204: Remove duplicated documentation while preserving one
authoritative version of each section: delete the repeated watch section in
README.md (186-204), repeated entry-discovery/status/daemon sections (220-262),
copied costs alert/guard sections (406-498), and repeated alert overview in
README.pt.md (362-363); retain one restore section (README.md 517-537), one
Alerting section and channel list (581-655), one storage/configuration section
(910-927), one testing conclusion and FAQ heading (1032-1047), and merge the
Prometheus roadmap bullets (1080-1084).

In `@src/cli/program.ts`:
- Around line 23-77: Remove the stray, incomplete createProgram declaration and
its unmatched opening so the imports and helper declarations remain at module
scope, leaving only the complete export function createProgram definition. In
the Commander setup inside createProgram, remove the premature semicolon after
the version/first option chain so the channel-plugin option remains part of the
same chained expression.

In `@src/commands/alerts.ts`:
- Around line 104-242: Remove the obsolete, incomplete insertAlertConfig call
that uses options.type and target, leaving the complete alertConfigId assignment
as the sole primary insertion. Ensure the remaining insertAlertConfig object
closes correctly and preserves primaryType, primaryTarget, threshold, signing,
and quiet-hours fields.

In `@src/core/monitor.ts`:
- Around line 119-164: Remove the duplicated auto-extension phase in the monitor
cycle, keeping only one invocation of runAutoExtensions and its associated
result assignments, anomaly logging, and error handling. Ensure
MonitorCycleResult reflects the single set of extension transactions performed.
- Around line 310-341: Remove the standalone deliverSingleAlert call and its
associated catch block before the allTargets loop. Keep the allTargets
construction and loop unchanged so the primary channel is delivered once through
the first target entry, along with any additional targets.

In `@src/db/repositories.ts`:
- Around line 899-909: Remove the initial ac.channel_type and ac.channel_target
selections in getAlertHistory, keeping only the subsequent COALESCE expressions
aliased as channelType and channelTarget as the authoritative output fields.
- Around line 301-337: Remove the first duplicate insertAlertConfig declaration
and its floating parameter block, leaving the number-returning insertAlertConfig
implementation as the sole definition. Preserve its existing database insert
logic and return info.lastInsertRowid as number so callers and tests retain the
current contract.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: 048291fd-d540-4029-87ed-177d1534b304

📥 Commits

Reviewing files that changed from the base of the PR and between 1e4a880 and 7716cac.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (22)
  • README.md
  • README.pt.md
  • docs/ARCHITECTURE.md
  • docs/adding-an-alert-channel.md
  • package.json
  • src/alerts/builtins.ts
  • src/cli/program.ts
  • src/commands/alerts.ts
  • src/commands/daemon.ts
  • src/core/monitor.ts
  • src/daemon/loop.ts
  • src/db/repositories.ts
  • src/db/schema.sql
  • src/index.ts
  • src/lib.ts
  • src/observability/registry.ts
  • tests/alerts/builtins.test.ts
  • tests/commands/alerts.test.ts
  • tests/core/monitor.test.ts
  • tests/daemon/loop.test.ts
  • tests/db/database.test.ts
  • tests/rpc/client.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: GitGuardian Security Checks
⚠️ CI failures not shown inline (4)

GitHub Actions: CI Pipeline / build-and-test (24.x): #453 test(rpc): add tests for RPC client retry/backoff edge case FIXED

Conclusion: failure

View job details

##[group]Run npm ci
 �[36;1mnpm ci�[0m
 shell: /usr/bin/bash -e {0}
 ##[endgroup]
 npm error code EUSAGE
 npm error
 npm error The `npm ci` command can only install with an existing package-lock.json or
 npm error npm-shrinkwrap.json with lockfileVersion >= 1. Run an install with npm@5 or
 npm error later to generate a package-lock.json file, then try again.
 npm error
 npm error Clean install a project
 npm error
 npm error Usage:
 npm error npm ci
 npm error
 npm error Options:
 npm error [--install-strategy <hoisted|nested|shallow|linked>] [--legacy-bundling]
 npm error [--global-style] [--omit <dev|optional|peer> [--omit <dev|optional|peer> ...]]
 npm error [--include <prod|dev|optional|peer> [--include <prod|dev|optional|peer> ...]]
 npm error [--strict-peer-deps] [--foreground-scripts] [--ignore-scripts]
 npm error [--allow-directory <all|none|root>] [--allow-file <all|none|root>]
 npm error [--allow-git <all|none|root>] [--allow-remote <all|none|root>]
 npm error [--allow-scripts <package-list> [--allow-scripts <package-list> ...]]
 npm error [--strict-allow-scripts] [--dangerously-allow-all-scripts] [--no-audit]
 npm error [--no-bin-links] [--no-fund] [--dry-run]
 npm error [-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
 npm error [--workspaces] [--include-workspace-root] [--install-links]
 npm error
 npm error   --install-strategy
 npm error     Sets the strategy for installing packages in node_modules.
 npm error
 npm error   --legacy-bundling
 npm error     Instead of hoisting package installs in `node_modules`, install packages
 npm error
 npm error   --global-style
 npm error     Only install direct dependencies in the top level `node_modules`,
 npm error
 npm error   --omit
 npm error     Dependency types to omit from the installation tree on disk.
 npm error
 npm error   --include
 npm error     Option that allows for defining which types of dependencies to install.
 npm error
 npm error   --strict-peer-deps
 npm error     ...

GitHub Actions: CI Pipeline / 1_build-and-test (22.x).txt: #453 test(rpc): add tests for RPC client retry/backoff edge case FIXED

Conclusion: failure

View job details

##[group]Setting up auth
 [command]/usr/bin/git config --local --name-only --get-regexp core\.sshCommand
 [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :"
 [command]/usr/bin/git config --local --name-only --get-regexp http\.https\:\/\/github\.com\/\.extraheader
 [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'http\.https\:\/\/github\.com\/\.extraheader' && git config --local --unset-all 'http.https://github.com/.extraheader' || :"
 [command]/usr/bin/git config --local --name-only --get-regexp ^includeIf\.gitdir:
 ##[error]The operation was canceled.

GitHub Actions: CI Pipeline / build-and-test (22.x): #453 test(rpc): add tests for RPC client retry/backoff edge case FIXED

Conclusion: failure

View job details

##[group]Setting up auth
 [command]/usr/bin/git config --local --name-only --get-regexp core\.sshCommand
 [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :"
 [command]/usr/bin/git config --local --name-only --get-regexp http\.https\:\/\/github\.com\/\.extraheader
 [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'http\.https\:\/\/github\.com\/\.extraheader' && git config --local --unset-all 'http.https://github.com/.extraheader' || :"
 [command]/usr/bin/git config --local --name-only --get-regexp ^includeIf\.gitdir:
 ##[error]The operation was canceled.

GitHub Actions: CI Pipeline / 0_build-and-test (24.x).txt: #453 test(rpc): add tests for RPC client retry/backoff edge case FIXED

Conclusion: failure

View job details

##[group]Run npm ci
 �[36;1mnpm ci�[0m
 shell: /usr/bin/bash -e {0}
 ##[endgroup]
 npm error code EUSAGE
 npm error
 npm error The `npm ci` command can only install with an existing package-lock.json or
 npm error npm-shrinkwrap.json with lockfileVersion >= 1. Run an install with npm@5 or
 npm error later to generate a package-lock.json file, then try again.
 npm error
 npm error Clean install a project
 npm error
 npm error Usage:
 npm error npm ci
 npm error
 npm error Options:
 npm error [--install-strategy <hoisted|nested|shallow|linked>] [--legacy-bundling]
 npm error [--global-style] [--omit <dev|optional|peer> [--omit <dev|optional|peer> ...]]
 npm error [--include <prod|dev|optional|peer> [--include <prod|dev|optional|peer> ...]]
 npm error [--strict-peer-deps] [--foreground-scripts] [--ignore-scripts]
 npm error [--allow-directory <all|none|root>] [--allow-file <all|none|root>]
 npm error [--allow-git <all|none|root>] [--allow-remote <all|none|root>]
 npm error [--allow-scripts <package-list> [--allow-scripts <package-list> ...]]
 npm error [--strict-allow-scripts] [--dangerously-allow-all-scripts] [--no-audit]
 npm error [--no-bin-links] [--no-fund] [--dry-run]
 npm error [-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
 npm error [--workspaces] [--include-workspace-root] [--install-links]
 npm error
 npm error   --install-strategy
 npm error     Sets the strategy for installing packages in node_modules.
 npm error
 npm error   --legacy-bundling
 npm error     Instead of hoisting package installs in `node_modules`, install packages
 npm error
 npm error   --global-style
 npm error     Only install direct dependencies in the top level `node_modules`,
 npm error
 npm error   --omit
 npm error     Dependency types to omit from the installation tree on disk.
 npm error
 npm error   --include
 npm error     Option that allows for defining which types of dependencies to install.
 npm error
 npm error   --strict-peer-deps
 npm error     ...
🧰 Additional context used
🪛 Biome (2.5.5)
tests/alerts/builtins.test.ts

[error] 96-96: Expected a statement but instead found ')'.

(parse)

tests/commands/alerts.test.ts

[error] 1248-1248: Expected a statement but instead found '})'.

(parse)

src/commands/alerts.ts

[error] 228-228: expected : but instead found alertConfigId

(parse)


[error] 238-238: expected , but instead found ;

(parse)


[error] 274-274: This property is later overwritten by an object member with the same name.

(lint/suspicious/noDuplicateObjectKeys)


[error] 275-275: This property is later overwritten by an object member with the same name.

(lint/suspicious/noDuplicateObjectKeys)

🪛 LanguageTool
README.pt.md

[style] ~362-~362: Para conferir mais clareza ao seu texto, busque usar uma linguagem mais concisa.
Context: ... ## Alertas O Sorokeep entrega alertas através de múltiplos canais: webhooks, **Slack...

(ATRAVES_DE_POR_VIA)

🪛 markdownlint-cli2 (0.23.1)
README.md

[warning] 449-449: Multiple headings with the same content

(MD024, no-duplicate-heading)


[warning] 490-490: Multiple headings with the same content

(MD024, no-duplicate-heading)


[warning] 631-631: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 923-923: Multiple headings with the same content

(MD024, no-duplicate-heading)


[warning] 1043-1043: Multiple headings with the same content

(MD024, no-duplicate-heading)

🪛 SQLFluff (4.2.2)
src/db/schema.sql

[error] 58-59: Too many consecutive blank lines.

(LT15)

🔇 Additional comments (27)
tests/core/monitor.test.ts (1)

11-13: LGTM!

Also applies to: 1151-1151, 1185-1185

tests/daemon/loop.test.ts (1)

55-58: LGTM!

tests/db/database.test.ts (1)

90-90: LGTM!

Also applies to: 211-211

tests/rpc/client.test.ts (5)

409-432: LGTM!

Also applies to: 488-519


530-562: LGTM!

Also applies to: 564-591


596-603: LGTM!

Also applies to: 605-627, 629-648, 650-659, 661-669, 671-711, 713-745, 747-777, 779-792, 794-808, 810-828, 830-853, 855-876, 878-894, 896-910, 947-967, 969-992


398-404: 🎯 Functional Correctness

No type-check issue

tsconfig.json excludes test files from the TS build, so line 400’s private-dot access is not rejected in this project’s configured type check.

			> Likely an incorrect or invalid review comment.

408-412: 🩺 Stability & Availability

No timer restoration needed here.

The parent describe already runs vi.useRealTimers() in its afterEach, so the subsequent ExtendFootprintTTLOp tests do not inherit these fake timers.

			> Likely an incorrect or invalid review comment.
README.md (1)

57-129: LGTM!

Also applies to: 303-303, 810-810, 894-894

README.pt.md (1)

384-384: LGTM!

docs/ARCHITECTURE.md (1)

87-90: LGTM!

src/daemon/loop.ts (3)

8-15: LGTM!

Also applies to: 99-99, 124-128, 177-177, 246-248


290-307: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Remove the duplicate resolvePollIntervalMs implementation.

src/daemon/loop.ts declares resolvePollIntervalMs both at lines 294 and 308. Keep one definition so TypeScript type-checks the file.

Proposed fix
-function resolvePollIntervalMs(db: Database.Database, network: string, fallbackIntervalMs: number): number {
-    const contracts = getAllContracts(db).filter((contract) => contract.network === network);
-    const overrides = contracts
-        .map((contract) => contract.poll_interval_seconds)
-        .filter((value): value is number => typeof value === "number" && value > 0);
-
-    if (overrides.length === 0) {
-        return fallbackIntervalMs;
-    }
-
-    return Math.min(...overrides) * 1000;
-}
			> Likely an incorrect or invalid review comment.

206-214: 🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

Remove the duplicate cost aggregation.

The added call at Lines 209-213 is followed by the existing traced call at Lines 218-225. aggregateDailyCostSnapshots scans history and upserts snapshot rows, so each cycle performs this work twice. Keep the traced block and remove the uninstrumented block.

Proposed fix
-        // Step 3: aggregate daily cost snapshots for past extension history.
-        try {
-            aggregateDailyCostSnapshots(db);
-        } catch (snapshotErr: unknown) {
-            logger().error("aggregateDailyCostSnapshots threw unexpectedly", snapshotErr);
-        }
			> Likely an incorrect or invalid review comment.
src/alerts/builtins.ts (1)

3-6: LGTM!

Also applies to: 36-36, 45-45

src/cli/program.ts (1)

94-94: LGTM!

Also applies to: 113-117

src/commands/alerts.ts (2)

10-19: LGTM!


55-66: 🎯 Functional Correctness

No duplicate option declarations to remove.

The alerts add command has one requiredOption("--type ...") declaration and does not redeclare --type; the repeated declarations visible in the snippet are absent in the current source.

			> Likely an incorrect or invalid review comment.
src/core/monitor.ts (1)

106-108: LGTM!

src/db/repositories.ts (1)

44-46: LGTM!

Also applies to: 57-60, 358-358, 489-489, 531-531

tests/alerts/builtins.test.ts (1)

6-8: LGTM!

Also applies to: 20-24, 129-136, 186-190

tests/commands/alerts.test.ts (1)

12-14: LGTM!

Also applies to: 98-98

src/lib.ts (1)

23-26: LGTM!

src/observability/registry.ts (1)

42-42: LGTM!

src/commands/daemon.ts (1)

15-25: LGTM!

Also applies to: 39-39, 49-49, 101-103

src/db/schema.sql (1)

81-85: LGTM!

package.json (1)

55-69: LGTM!

Comment thread docs/adding-an-alert-channel.md
Comment thread README.md
Comment thread README.md
Comment thread src/commands/alerts.ts
Comment thread src/daemon/loop.ts
Comment thread tests/rpc/client.test.ts
Comment thread tests/rpc/client.test.ts
Comment thread tests/rpc/client.test.ts
Comment thread tests/rpc/client.test.ts
Comment thread tests/rpc/client.test.ts
@Kappa16

Kappa16 commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

@AbdulmalikAlayande PLS REVIEW

@Kappa16

Kappa16 commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

@AbdulmalikAlayande PLS REVIEW

AbdulmalikAlayande added a commit that referenced this pull request Aug 8, 2026
…#648)

Adds 19 tests to the existing executeWithRetry describe block: exact
backoff-timing verification (1000/2000/4000ms), immediate-success
with no delay, error-context preservation through exhausted retries,
parametrized coverage of retryable (429, 500-series, ETIMEDOUT,
ECONNRESET, message-based timeout detection) vs non-retryable (4xx)
statuses, and a documentation test enumerating the implementation's
configurable parameters.

The three acceptance-criteria cases (retry-then-succeed with correct
count, non-retryable short-circuit, exhausted-retries error
preservation) were already covered by 3 existing tests in this file —
this PR's value is the much more thorough edge-case coverage beyond
that baseline, which is genuinely additive, not duplicate.
@AbdulmalikAlayande

Copy link
Copy Markdown
Collaborator

Merged via 1eebd17 on main. The 3 acceptance-criteria cases were already covered by pre-existing tests in this file, but your 19 tests add genuinely new, non-duplicate edge-case coverage (exact backoff timing, error-context preservation, parametrized retryable/non-retryable status coverage). All pass against current client.ts.

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.

test(rpc): add tests for RPC client retry/backoff edge cases