Skip to content

fix: retry transient integration tool-list failures during discovery - #4333

Merged
kwakayama merged 7 commits into
mainfrom
issue/874-integration-tool-defs-fetch
Aug 31, 2026
Merged

kwakayama merged 7 commits into
mainfrom
issue/874-integration-tool-defs-fetch

Conversation

@kwakayama

@kwakayama kwakayama commented Aug 30, 2026 •

Copy link
Copy Markdown
Contributor

Remote integration tool discovery abandoned the idempotent /integrations/tools/list request on the first transient transport failure or 5xx response, immediately emitting the error-level Failed to fetch remote integration tool definitions signature tracked by Sentry group VERYFRONT-AGENT-C. This change adds three bounded attempts with short linear backoff inside discoverRemoteIntegrationToolCatalog, so a run-scoped promise cache shares one retry sequence across concurrent callers.

Scope stays narrow:

  • Retry only the read-only tool-list request.
  • Use the established retryWithBackoff helper for attempts, deadlines, backoff, and cancellation.
  • Keep tool execution retry-free because it can be non-idempotent.
  • Keep 400/401/403 and the projectless-400 debug path non-retryable.
  • Keep response parsing failures and the 30-second per-request deadline non-retryable.
  • Preserve graceful degradation to { status: "unavailable", reason: "request_failed" }.
  • Emit the exact error-level Sentry signature once only after retry exhaustion.

Fixes veryfront/veryfront-issue-inbox#874.

Red evidence

At regression commit ee9dbf8a11f8bbcd0a4752b1e893cc6fb9c29cfa:

git switch --detach ee9dbf8a11f8bbcd0a4752b1e893cc6fb9c29cfa
deno task test:file src/integrations/remote-tools.discovery-retry.test.ts

All three retry assertions failed because discovery made one request only:

  • Transient fetch rejection: actual 1, expected 2.
  • Transient 5xx: actual 1, expected 2.
  • Persistent 5xx: no retry before degradation.

Green verification

Use the repository-pinned Deno 2.7.7:

deno task test:file src/integrations/remote-tools.test.ts
deno task test:file src/integrations/remote-tools.hardening.test.ts
deno task test:file src/integrations/
deno task fmt:check
deno task lint
deno task typecheck
deno task lint:ci

Final regression coverage proves:

  • A transport rejection recovers on retry with status ok.
  • A connection reset while streaming a successful response body recovers on retry.
  • A 5xx recovers on retry with status ok.
  • Concurrent callers share one retry sequence.
  • Recovered failures emit no error-level signature.
  • Persistent 5xx failures make exactly three attempts, degrade without throwing, and emit exactly one Failed to fetch remote integration tool definitions record.
  • Caller cancellation during backoff stops before another request.
  • Malformed response bodies fail once and are not misclassified as network failures.
  • Failure caching remains run-scoped and a later run can retry.

Revert check

Before the final test consolidation, reverting implementation commit ca5cb4c45cbefbdab16263c95043795d52897850 reproduced the three red assertions, and restoring the implementation returned the focused suite to green. The final equivalent assertions now live in src/integrations/remote-tools.test.ts and src/integrations/remote-tools.hardening.test.ts.

Acceptance criteria

  • Retry transient tool-list transport failures and recover the catalog.
  • Retry 5xx tool-list responses and recover the catalog.
  • Do not emit the error-level Sentry signature when retry recovers.
  • Bound persistent-failure attempts, degrade without throwing, and log the exact signature once.
  • Cover negative status, cancellation, concurrency, cache, malformed-response, and duplicate-log behavior.
  • Keep non-idempotent tool execution retry-free.
  • Reuse the repository retry helper instead of maintaining a second timer and cancellation loop.
  • Keep the diff dependency-free and limited to the integration discovery boundary and focused tests.

Deployment and post-deploy verification

Merge only after the exact-head Codex and Claude review gates meet the required score, all threads are resolved, and GitHub reports both quality gate (merge) and quality gate (artifact) green with no unexplained skipped checks.

After the framework release reaches the agent runtime, monitor Sentry VERYFRONT-AGENT-C through at least one normal integration-discovery traffic window. Confirm transient recoveries no longer create events and any persistent outage creates at most one event per exhausted discovery sequence. Keep issue #874 open until the deployed group is quiet or explicitly classified with evidence.

Kentaro Wakayama added 3 commits August 31, 2026 00:40
…-inbox#874)

Sentry VERYFRONT-AGENT-C: a single transient failure of the idempotent
integration tool-list request abandons discovery immediately and emits
the error-level 'Failed to fetch remote integration tool definitions'
signature. Red tests assert bounded retry for transient network errors
and 5xx responses, recovery without the error log, and single-log
degradation on persistent failure.
The tool-list request is read-only and idempotent, yet a single dropped
connection or 5xx response abandoned discovery on the first attempt and
emitted the error-level "Failed to fetch remote integration tool
definitions" log (Sentry VERYFRONT-AGENT-C).

Discovery now retries transient failures (network errors and 5xx) up to
MAX_INTEGRATION_TOOL_LIST_ATTEMPTS total attempts with a short linear
backoff that aborts as soon as the caller's signal fires. Client errors
such as 400/401/403 stay non-retried, the projectless-400 debug skip is
preserved, and tool execution remains retry-free because tool calls are
not idempotent. On exhaustion discovery still degrades to an unavailable
catalog with the error-level signature logged exactly once.
@coderabbitai

coderabbitai Bot commented Aug 30, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 1 minute.

View limit details

Limit details: You’ve used the included review currently available.

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

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 842432d0-035b-48be-b7c1-1b1f4bfe0ebf

📥 Commits

Reviewing files that changed from the base of the PR and between 0e2d942 and c7a41ef.

📒 Files selected for processing (4)
  • src/integrations/limits.ts
  • src/integrations/remote-tools.hardening.test.ts
  • src/integrations/remote-tools.test.ts
  • src/integrations/remote-tools.ts

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.

@github-actions

github-actions Bot commented Aug 30, 2026 •

Copy link
Copy Markdown

📦 Client bundle boundary

Entrypoint Modules Source size Server leaks
src/index.client.ts 288 2215 KiB ✅ 0

A server module in a client graph aborts hydration in the browser. New leaks fail CI; known leaks are tracked in scripts/lint/client-bundle-baseline.json to burn down.

@gitar-bot

gitar-bot Bot commented Aug 30, 2026

Copy link
Copy Markdown

Note

Automatic reviews are paused because your trial's included automatic processing has been used for this period. Upgrade now, or comment "Gitar review" to run a review anytime.
Learn more

Code Review ✅ Approved

Adds retry logic with bounded backoff to discoverRemoteIntegrationToolCatalog for transient /integrations/tools/list failures, so concurrent callers share one retry sequence through the per-run promise cache. Transient network errors and 5xx responses now recover silently; non-retryable statuses and non-idempotent tool execution are unchanged. All acceptance criteria met: retries succeed without error logs, persistent failures degrade gracefully with a single error signature, and retry attempts stay bounded. No issues found.

Options

Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@codecov

codecov Bot commented Aug 30, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.97980% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/integrations/remote-tools.ts 97.93% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@kwakayama

Copy link
Copy Markdown
Contributor Author

Reviewer: Codex
Reviewed SHA: 1a329e50cf84a00e35992f73ca6ecedb226d4fb0
Reviewed range: 0e2d942eba47b2d1ca3ca4718676670faba2aedd...1a329e50cf84a00e35992f73ca6ecedb226d4fb0

Findings, ordered by severity:

  1. Medium, the regression tests do not lock the exact Sentry signature required by issue debug(runtime): instrument provider web tool stream payloads #874. src/integrations/remote-tools.test.ts:66-72 records any message containing "integration tool", while the new assertions at lines 186, 208, and 1271-1279 check only levels and counts. An error message change that still contains that phrase could pass even if Failed to fetch remote integration tool definitions changes. Restore an exact-message capture/assertion for the signature.

  2. Medium, the operational evidence in the PR body is stale and incomplete. It instructs reviewers to run src/integrations/remote-tools.discovery-retry.test.ts, but that file is absent at this exact head; the regression cases now live in src/integrations/remote-tools.test.ts. Issue debug(runtime): instrument provider web tool stream payloads #874 also requires deployment gates and post-deploy Sentry verification, but the body does not identify those gates or the verification/closeout procedure. Update the red, green, and revert commands plus rollout and post-deploy evidence.

Verification:

  • PASS with repository-pinned Deno 2.7.7: deno task test:file src/integrations/remote-tools.test.ts (45 steps), deno task test:file src/integrations/remote-tools.hardening.test.ts (23 steps), and deno task test:file src/integrations/ (15 files, 260 steps).
  • PASS: deno fmt --check, deno lint, deno check for the changed source/tests, and git diff --check.
  • No unresolved review threads or inline review comments were present. The PR was draft at review time, and GitHub still had pending CI checks including coverage, integration, Node/Bun, binary E2E, CodeQL, and the automated review gate. Codecov reported advisory patch coverage of 89.70588% with 7 lines missing.

Score breakdown: correctness/completeness 38/40, tests/verification 15/20, reliability/security 15/15, standards/maintainability 11/15, scope/docs/rollout 5/10.

Verdict: REQUEST_CHANGES
Score: 84/100

Review-Gate:
Reviewer: Codex
Reviewed-SHA: 1a329e5
Score: 84/100
Actionable-Findings: 2
Verdict: REQUEST_CHANGES

@kwakayama
kwakayama marked this pull request as ready for review August 31, 2026 06:10

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

kwakayama has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 31, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review 🔄 Running since 2026-08-31T06:53:30.569511Z c7a41ef New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

Copy link
Copy Markdown

@codex review

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

kwakayama has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@github-actions

Copy link
Copy Markdown

@codex review

@kwakayama

Copy link
Copy Markdown
Contributor Author

Addressed the findings from the 1a329e50 review in commit b2a8d335d1560a5000d3b3b5ac1d864369f90c67:

  • The persistent-failure regression now asserts the exact Failed to fetch remote integration tool definitions Sentry signature.
  • The PR body now points to the current test files and documents deployment gates plus post-deploy Sentry verification.
  • Response-body/parser TypeError failures are no longer retried as transport failures; a red-green regression proves malformed UTF-8 makes one request only.
  • Request deadline behavior is explicitly documented as non-retryable to retain one 30-second request budget.

Verification on pinned Deno 2.7.7: focused suites, all 260 integration steps, fmt:check, lint:ci, canonical typecheck, and the full pre-push unit/serial/cwd matrix passed. Fresh exact-head Codex and Claude reviews are being run against b2a8d335d1560a5000d3b3b5ac1d864369f90c67.

@kwakayama

Copy link
Copy Markdown
Contributor Author

Reviewer: Claude
Reviewed-SHA: b2a8d335d1560a5000d3b3b5ac1d864369f90c67 (range 0e2d942eb...b2a8d335d)

Independent review against issue veryfront/veryfront-issue-inbox#874 (Sentry VERYFRONT-AGENT-C) and the PR acceptance criteria.

Findings (ordered by severity)

No critical, high, or medium actionable defects found. Non-blocking observations:

  1. Low (observation, non-actionable): fetchToolList wraps any TypeError from postIntegrationApi as a transient 502 (src/integrations/remote-tools.ts:570-587). In Deno, network failures reject as TypeError, but a programmatic TypeError (e.g. malformed URL) would also earn two retries before degrading. Inputs are validated upstream and impact is bounded to ~300 ms of extra backoff plus debug logs, so this is acceptable as written.
  2. Low (observation, non-actionable): the synthetic status: 502 on wrapped transport rejections is indistinguishable in the error object from a real upstream 502; the cause chain preserves the original TypeError, so triage is still possible.
  3. Info: at review time, several exact-head checks were still pending (ci (lint), coverage shards, tests (integration), tests (binary e2e), CodeQL JS/TS). ci (typecheck), ci (format), ci (test-layout), SonarCloud quality gate, and the completed test jobs are green. Merge should wait for the remaining gates per the PR's own deployment section.

Correctness verification

  • Retry scope is correctly limited to the idempotent /integrations/tools/list request; executeRemoteTool/postIntegrationApi remain retry-free (remote-tools.ts diff touches only the discovery path).
  • isTransientToolListFailure retries only INTEGRATION_TOOL_LIST_REQUEST_FAILED with status ≥ 500; 400/401/403 and the projectless-400 debug path stay non-retryable, matching the stated scope.
  • The 30 s deadline aborts with a DOMException("TimeoutError") (remote-tools.ts:379-386), which is not a TypeError, so timeouts are correctly non-retryable; caller aborts are rethrown via throwIfAborted() before classification.
  • Parser/body failures are thrown outside the TypeError wrapper (only the postIntegrationApi call is wrapped), so malformed responses fail once — confirmed by the new fetchCalls === 1 assertions in the hardening tests.
  • waitBeforeToolListRetry rejects promptly on abort and cleans up both the timer and the abort listener on all paths.
  • The error-level Failed to fetch remote integration tool definitions signature is emitted exactly once after exhaustion, and the regression at remote-tools.test.ts:1271-1286 now pins the exact message string — this resolves the prior Codex medium finding from the 1a329e5 review.
  • Run-scoped promise caching means concurrent callers share one retry sequence (asserted at remote-tools.test.ts:139), and the persistent-failure cache test proves no cross-caller refetch within a run.

Test verification (local, read-only)

Ran with the repository-pinned Deno 2.7.7:

  • deno task test:file src/integrations/remote-tools.test.ts → ok, 1 passed (45 steps), 0 failed
  • deno task test:file src/integrations/remote-tools.hardening.test.ts → ok, 1 passed (23 steps), 0 failed

(Note: under an unpinned Deno 2.9.4 the suite fails at module load in src/platform/compat/native-brand-checks.ts — an environment constraint, not a PR defect.)

Gaps: I did not re-run the red reproduction at ee9dbf8 (the standalone remote-tools.discovery-retry.test.ts was consolidated, as documented), and CI at the exact head was still partially pending as noted above.

Verdict

All seven acceptance criteria are met with matching test evidence; the diff is dependency-free and confined to the integration discovery boundary plus focused tests. The remaining pending CI gates are already required by the PR's own merge conditions.

Score: 92/100

Review-Gate:
Reviewer: Claude
Reviewed-SHA: b2a8d33
Score: 92/100
Actionable-Findings: 0
Verdict: APPROVE

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b2a8d335d1

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/integrations/remote-tools.ts Outdated
Comment thread src/integrations/remote-tools.ts Outdated
Comment thread src/integrations/remote-tools.ts Outdated
@kwakayama

Copy link
Copy Markdown
Contributor Author

Reviewer: Codex
Reviewed-SHA: b2a8d335d1560a5000d3b3b5ac1d864369f90c67
Reviewed range: 0e2d942eba47b2d1ca3ca4718676670faba2aedd...b2a8d335d1560a5000d3b3b5ac1d864369f90c67

Findings, ordered by severity:

  1. MEDIUM - response-body transport failures still bypass retry. File: src/integrations/remote-tools.ts:599. The transient wrapper covers only the initial postIntegrationApi dispatch at lines 570-587. A connection reset after successful headers propagates from readBoundedResponseJson as a TypeError; isTransientToolListFailure at lines 621-628 rejects it, so discovery performs one request, degrades, and emits the same error-level Sentry signature this PR is intended to avoid. I confirmed the exact response-body reader propagates a synthetic TypeError("connection reset while reading body"). Fix by classifying actual body-read transport failures as retryable while keeping InvalidResponseBody*, JSON/shape errors, and deadline/caller aborts non-retryable, then add a regression where the first streamed body resets and the second response succeeds without an error record.

  2. LOW - deterministic malformed request URLs are misclassified as transient. File: src/integrations/remote-tools.ts:579. The catch wraps every dispatch TypeError as retryable 502. Against this exact head, VERYFRONT_API_BASE_URL=not-a-url produced 3 fetch attempts and 306 ms of backoff before the unchanged unavailable result. This is a local configuration error, not a transient transport failure. Validate/build the request URL outside the transient wrapper or introduce a narrow typed transport error, and add a one-attempt negative regression.

Verification:

  • PASS with pinned Deno 2.7.7: changed-file deno check; focused suites (45 and 23 steps); all src/integrations/ tests (260 steps); fmt:check; lint; canonical typecheck; git diff --check.
  • Exact-head GitHub snapshot was stable with 42 passing, 0 failing, 1 pending (Automated review), and 13 conditional skipped checks. Three inline threads remained unresolved.
  • The dedicated LSP diagnostics tool was unavailable, so pinned changed-file deno check was used as the type-diagnostic equivalent. I did not rerun the historical red commit.

Score breakdown: correctness/completeness 32/40, tests/verification 16/20, reliability/security 12/15, standards/maintainability 13/15, scope/docs/rollout 9/10.

Verdict: REQUEST_CHANGES
Score: 82/100

Review-Gate:
Reviewer: Codex
Reviewed-SHA: b2a8d33
Score: 82/100
Actionable-Findings: 2
Verdict: REQUEST_CHANGES

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

kwakayama has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@github-actions

Copy link
Copy Markdown

@codex review

@kwakayama

Copy link
Copy Markdown
Contributor Author

Code Review — PR #4333

Reviewer: Claude
Reviewed SHA: ea47fdfe4a7eb204b3a5207be0d2d985a62b5a8c
Spec: veryfront/veryfront-issue-inbox#874 (Sentry VERYFRONT-AGENT-C)


Summary

This PR adds bounded retry (3 attempts, linear backoff) for the idempotent /integrations/tools/list discovery request, using the repository's established retryWithBackoff helper. The Sentry error-level signature is now emitted only after retry exhaustion, reducing noise from transient 5xx and transport failures. Tool execution remains retry-free (non-idempotent). Scope is narrow and well-contained to 4 files.

Findings

No actionable findings. The implementation is correct and well-tested.

Detailed Assessment

Correctness/Completeness (37/40)

  • isTransientToolListFailure (remote-tools.ts:628-636) correctly classifies retry-eligible errors: 5xx via VeryfrontError status, transport TypeError from fetch dispatch or body streaming, while excluding InvalidResponseBodyError (malformed body, deterministic) and non-5xx HTTP errors (400/401/403).
  • projectSlug is resolved once before the retry loop (remote-tools.ts:567→passed into fetchToolList), preventing local validation TypeErrors from entering retry. Confirmed by test "does not retry local project slug validation failures" asserting 0 fetch calls.
  • Per-attempt timeout of 30s via timeoutMs: INTEGRATION_REQUEST_TIMEOUT_MS gives each attempt its own deadline (worst-case total ~90.3s for 3 attempts + backoff). Bounded and intentional.
  • postIntegrationApi signature refactored to accept projectSlug directly instead of resolving from context — callers (fetchToolList, callRemoteTool) now own resolution, which is correct since tool calls still use createIntegrationRequestSignalScope independently.
  • Error-level Sentry grouping signature preserved exactly ("Failed to fetch remote integration tool definitions") — emitted once after exhaustion at remote-tools.ts:668.

Tests/Verification (19/20)

  • Red-first TDD: commit ee9dbf8a1 adds failing regression test before any implementation.
  • Coverage spans: transport rejection recovery, 5xx recovery, body stream transport failure recovery, persistent failure exhaustion with exact attempt count, concurrent caller sharing via discovery scope, no-retry for local slug validation, no-retry for malformed UTF-8 body (InvalidResponseBodyError), cancellation during retry backoff window (hardening test using log emitter subscription).
  • Log-level assertions confirm debug-only breadcrumbs during retries and exactly one error on exhaustion.
  • Fetch call count assertions at MAX_INTEGRATION_TOOL_LIST_ATTEMPTS boundaries validate retry bounds.

Reliability/Security (14/15)

  • Bounded retries (max 3), per-attempt timeouts (30s), abort signal propagation, graceful degradation to { status: "unavailable", reason: "request_failed" } all preserved.
  • No secrets or production data in tests. Synthetic fixtures only.

Standards/Maintainability (14/15)

  • Reuses retryWithBackoff from #veryfront/errors per AGENTS.md guidance ("Reuse existing modules, schemas, adapters, and error patterns before adding new abstractions").
  • Constants extracted to limits.ts with clear JSDoc.
  • isTransientToolListFailure has a descriptive JSDoc explaining the classification logic.
  • All three prior Codex review comments addressed: P1 (reuse helper) in ea47fdfe4, P2 (local validation) in ea47fdfe4, P2 (body transport) in b2a8d335d.

Scope/Docs/Rollout (9/10)

  • PR body documents root cause, red/green evidence with exact commands, and lists verification steps.
  • Scope is minimal: only the read-only tool-list path; tool execution untouched.
  • Links veryfront/veryfront-issue-inbox#874.
  • CI: typecheck, format, test-layout pass; lint, coverage, integration tests pending at review time.

Verification

  • Read full diff 0e2d942e..ea47fdfe4 (306 insertions, 49 deletions across 4 files).
  • Traced retryWithBackoff contract in src/errors/error-handlers.ts:159-238: per-attempt signal composition, timeout, sleep with abort, shouldRetry gate, computeDelay, onRetry hook all match usage.
  • Confirmed InvalidResponseBodyError extends TypeError in src/utils/response-body.ts:97 — the instanceof exclusion in isTransientToolListFailure is sound.
  • Confirmed no double error logging: discoverRemoteIntegrationToolCatalog catches all fetchToolList errors; the outer catch in getRemoteIntegrationToolDiscovery:828 only handles resolveRequestProjectSlug failures.

Gaps

  • CI lint and coverage shards were pending at review time. No code concerns, but final merge should await green CI.

Score: 93/100
Actionable Findings: 0
Verdict: APPROVE

Review-Gate:
Reviewer: Claude
Reviewed-SHA: ea47fdf
Score: 93/100
Actionable-Findings: 0
Verdict: APPROVE

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ea47fdfe4a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/integrations/remote-tools.ts
Comment thread src/integrations/remote-tools.ts
@kwakayama

Copy link
Copy Markdown
Contributor Author

Reviewer: Codex
Reviewed SHA: ea47fdfe4a7eb204b3a5207be0d2d985a62b5a8c
Reviewed range: 0e2d942eba47b2d1ca3ca4718676670faba2aedd...ea47fdfe4a7eb204b3a5207be0d2d985a62b5a8c

Findings, ordered by severity:

  1. MEDIUM - discovery timeouts lose their diagnostic reason. src/integrations/remote-tools.ts:608-611 now delegates the 30-second discovery deadline to retryWithBackoff. That helper aborts timed-out attempts with the generic AbortError / The operation was aborted (src/errors/error-handlers.ts:36-44), and discoverRemoteIntegrationToolCatalog logs only err.message at src/integrations/remote-tools.ts:649-669. The previous discovery scope emitted a typed TimeoutError containing the 30-second deadline. A pinned-Deno fake-time reproduction produced one request, graceful degradation, and the Sentry record error="The operation was aborted". This makes timeout failures indistinguishable from a generic abort during the issue's post-deploy investigation. Preserve or translate the helper timeout reason while retaining caller-abort behavior, and add a discovery-specific timeout regression.

  2. LOW - deterministic malformed API base URLs are retried as transient transport failures. src/integrations/remote-tools.ts:628-635 treats every TypeError other than InvalidResponseBodyError as retryable. Normal environment loading preserves VERYFRONT_API_BASE_URL verbatim (src/config/environment-config.ts:159-161), so not-a-url makes fetch throw before dispatch and still runs all three attempts with both backoffs. Under pinned Deno 2.7.7 I reproduced three fetch dispatch calls and about 309 ms of unnecessary delay before the unchanged unavailable result. Validate/normalize the URL before entering the retry policy or classify only a typed fetch transport error, with a one-attempt regression for malformed or unsupported URLs.

Verification:

  • PASS, pinned Deno 2.7.7: deno task test:file src/integrations/remote-tools.test.ts (47 steps), deno task test:file src/integrations/remote-tools.hardening.test.ts (23 steps), and deno task test:file src/integrations/ (262 steps).
  • PASS: deno task fmt:check, deno task lint, canonical deno task typecheck, changed-file deno check, and git diff --check.
  • GitHub exact-head snapshot was stable at the requested SHA: 32 checks passed, 0 failed, 7 pending (Sonar, Node/Bun shards, CodeQL JavaScript/TypeScript, and automated review), and 8 skipped conditional jobs. Two review threads remain unresolved and the PR is blocked. Review-thread and score-comment pagination was complete.

Score breakdown: correctness/completeness 35/40, tests/verification 16/20, reliability/security 12/15, standards/maintainability 13/15, scope/docs/rollout 8/10.

Verdict: REQUEST_CHANGES
Score: 84/100

Review-Gate:
Reviewer: Codex
Reviewed-SHA: ea47fdf
Score: 84/100
Actionable-Findings: 2
Verdict: REQUEST_CHANGES

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

kwakayama has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@github-actions

Copy link
Copy Markdown

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Can't wait for the next one!

Reviewed commit: c7a41ef936

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@kwakayama

Copy link
Copy Markdown
Contributor Author

Claude Code Review — PR #4333

Reviewer: Claude
Reviewed SHA: c7a41ef9363030d3ab1cc3d25b77c49a769a8064
Spec: veryfront/veryfront-issue-inbox#874 (Sentry VERYFRONT-AGENT-C)


Summary

This PR adds bounded retry (3 attempts, linear backoff) to the idempotent /integrations/tools/list discovery request, addressing Sentry group VERYFRONT-AGENT-C where transient transport failures immediately emitted an error-level log. The implementation reuses the established retryWithBackoff helper, validates inputs (project slug, base URL) before entering retry, classifies transient vs. deterministic failures, preserves timeout diagnostics, and maintains the existing graceful degradation contract. Tool execution remains retry-free (non-idempotent).

Findings (ordered by severity)

1. [Low] Timeout budget is per-attempt, not end-to-end — verify intent

File: src/integrations/remote-tools.ts:631 / src/errors/error-handlers.ts:192-200

retryWithBackoff creates a fresh AbortController + setTimeout(timeoutMs) per attempt (line 192-200 of error-handlers.ts). With timeoutMs: INTEGRATION_REQUEST_TIMEOUT_MS (30s) and 3 attempts, the worst-case wall clock is ~90s + backoff delays, not 30s end-to-end. The old code used a single createIntegrationRequestSignalScope which was a single 30s budget.

This is likely intentional for discovery (each attempt gets a fair 30s window), and the PR body says "per-request deadline" not "end-to-end deadline", so the contract is consistent. But worth confirming this is the desired behavior, especially since the limits.ts comment says "End-to-end deadline for one integration API request" for INTEGRATION_REQUEST_TIMEOUT_MS.

Severity: Low (documentation ambiguity, not a bug)

2. [Low] createIntegrationRequestTimeoutError translation relies on identity comparison

File: src/integrations/remote-tools.ts:621-625

if (signal.aborted && error === signal.reason) {
  throw createIntegrationRequestTimeoutError();
}

This correctly translates the retryWithBackoff internal timeout abort reason into the domain-specific TimeoutError with the descriptive message. The identity comparison (error === signal.reason) is sound because retryWithBackoff sets controller.abort(createRetryTimeoutError()) and the caught error is the same object. The test at line 342-384 in the hardening file validates this path. No issue found.

Severity: Informational

3. [Low] isTransientToolListFailure correctly excludes InvalidResponseBodyError

File: src/integrations/remote-tools.ts:650-658

return err instanceof TypeError && !(err instanceof InvalidResponseBodyError);

This is well-designed: InvalidResponseBodyError extends TypeError (confirmed in response-body.ts:97), so malformed UTF-8 and invalid JSON body errors are excluded from retry (deterministic failures), while network-level TypeError from fetch() dispatch or body streaming are retried. The hardening tests at lines 437-472 explicitly assert fetchCalls === 1 for malformed bodies.

Severity: Informational (positive finding)

4. [Low] URL validation before retry entry is correct

File: src/integrations/remote-tools.ts:828-832

createVeryfrontApiRequestUrlResolver(baseUrl) is called in getRemoteIntegrationToolDiscovery before entering the retry path. This throws a TypeError for malformed base URLs (confirmed in veryfront-api-url.ts:32-47), which is caught by the outer catch at line 855 and logged at error level without retry. The test "does not retry an invalid integration API base URL" validates fetchCalls === 0. This addresses the prior Codex review comment about malformed URL retries.

Severity: Informational (positive finding — resolves prior review feedback)

5. [Low] Project slug validation before retry entry is correct

File: src/integrations/remote-tools.ts:832

resolveRequestProjectSlug(requestContext) is called before getRemoteIntegrationToolCatalog, so a non-canonical slug throws before retry. The test "does not retry local project slug validation failures" validates fetchCalls === 0. This addresses the prior Codex P2 comment.

Severity: Informational (positive finding — resolves prior review feedback)

Verification

Check Status
CI format ✅ pass
CI test-layout ✅ pass
npm smoke ✅ pass
Windows localhost ✅ pass
proxy binary ✅ pass
CI lint ⏳ pending
CI typecheck ⏳ pending
Coverage shards ⏳ pending
CodeQL analysis ⏳ pending

All completed checks pass. Lint, typecheck, and coverage are still pending at review time.

Test coverage assessment

Tests are thorough and well-structured:

  • Retry recovery: transient fetch TypeError, 503 server error, response-body transport failure — all verify fetchCalls === 2 and successful recovery (remote-tools.test.ts)
  • Retry exhaustion: persistent 500 verifies fetchCalls === MAX_INTEGRATION_TOOL_LIST_ATTEMPTS, error logged exactly once, retries logged at debug (remote-tools.test.ts)
  • Non-retryable paths: invalid base URL (fetchCalls === 0), invalid project slug (fetchCalls === 0), malformed UTF-8 body (fetchCalls === 1) (remote-tools.test.ts, hardening test)
  • Concurrency: concurrent callers share one retry sequence (remote-tools.test.ts)
  • Cancellation: caller abort during retry backoff without extra request (hardening test)
  • Timeout preservation: integration timeout reason preserved without retry (hardening test)
  • Cache interaction: persistent failure cached for current run, retried next run (remote-tools.test.ts)

Gaps

  1. No test for 4xx non-retryable server responses (e.g., 400, 401, 403 without the projectless-400 path). The isTransientToolListFailure only retries >= 500, so 4xx errors are implicitly non-retryable, but an explicit assertion for e.g. 429 or 403 would harden the boundary.
  2. Pending CI checks — lint, typecheck, and coverage shards are not yet complete. No code-level issues suggest they would fail, but final merge should await completion.

Spec compliance (veryfront/veryfront-issue-inbox#874)

Acceptance criterion Status
Deterministic red regression test ✅ PR body documents red evidence at ee9dbf8a
Minimal implementation fix turns it green ✅ Focused to discovery retry only
Success/negative and retry/cancellation/concurrency coverage ✅ Comprehensive
Lint, typecheck, targeted tests pass ⏳ Targeted tests pass; lint/typecheck pending CI
PR links issue and documents root cause ✅ Links #874, documents scope and evidence

Prior review feedback resolution

All three Codex P1/P2 comments from the earlier review rounds have been addressed:

  • P1 (reuse retryWithBackoff): ✅ Resolved — now uses retryWithBackoff from #veryfront/errors
  • P2 (avoid retrying local validation): ✅ Resolved — slug + URL validated before retry entry
  • P2 (retry body transport failures): ✅ Resolved — TypeError (excluding InvalidResponseBodyError) is retryable
  • P2 (preserve timeout reason): ✅ Resolved — createIntegrationRequestTimeoutError() translation in the attempt callback

Verdict: The implementation is clean, well-scoped, and addresses all spec acceptance criteria. The retryWithBackoff reuse is correct and idiomatic per AGENTS.md line 12. All prior review comments are resolved. The only non-informational finding is a low-severity documentation ambiguity about per-attempt vs end-to-end timeout semantics, which does not block merge. Final merge should await the pending CI checks (lint, typecheck, coverage).

Review-Gate:
Reviewer: Claude
Reviewed-SHA: c7a41ef
Score: 92/100
Actionable-Findings: 0
Verdict: APPROVE

@kwakayama

Copy link
Copy Markdown
Contributor Author

Reviewer: Codex
Reviewed SHA: c7a41ef9363030d3ab1cc3d25b77c49a769a8064
Reviewed range: 0e2d942eba47b2d1ca3ca4718676670faba2aedd...c7a41ef9363030d3ab1cc3d25b77c49a769a8064

Findings, ordered by severity:

No actionable findings.

Evidence:

  • src/integrations/remote-tools.ts:614-657 delegates attempts, deadlines, backoff, cancellation, and retry hooks to retryWithBackoff; only 5xx request errors and transport TypeErrors retry, while deterministic response-body errors do not.
  • src/integrations/remote-tools.ts:619-626,660-692 translates only the helper-owned timeout reason and rethrows the caller abort reason before degradation; the error-level Sentry signature remains a single terminal log.
  • src/integrations/remote-tools.ts:828-838 validates and resolves the complete tool-list URL and project slug before retry entry, so malformed or unsupported base URLs and local validation failures dispatch zero requests.
  • src/integrations/remote-tools.ts:564-599 keeps successful response-body transport reads inside the retry attempt, while src/integrations/remote-tools.ts:727-804 leaves non-idempotent execution retry-free.
  • Focused tests cover transport and 5xx recovery, response-body reset recovery, concurrent promise sharing, invalid URL and slug inputs, caller cancellation, timeout diagnostics, malformed bodies, persistent exhaustion, exact signature, cache scope, and duplicate-log behavior.

Verification with repository-pinned Deno 2.7.7:

  • PASS: focused suites, 48 and 24 steps.
  • PASS: deno task test:file src/integrations/, 264 steps.
  • PASS: changed-file deno check, canonical deno task typecheck, deno task fmt:check, deno task lint, and git diff --check.
  • GitHub snapshot was stable at this exact head with complete thread/comment pagination, 0 unresolved threads, 24 passed checks, 0 failed, 11 pending, and 9 conditional skips. Final merge must await the pending exact-head checks.

Score breakdown: correctness/completeness 40/40, tests/verification 19/20, reliability/security 15/15, standards/maintainability 13/15, scope/docs/rollout 9/10.

Verdict: APPROVE
Score: 96/100

Review-Gate:
Reviewer: Codex
Reviewed-SHA: c7a41ef
Score: 96/100
Actionable-Findings: 0
Verdict: APPROVE

@kwakayama

Copy link
Copy Markdown
Contributor Author

Independent review

Reviewer: Codex
Reviewed-SHA: c7a41ef9363030d3ab1cc3d25b77c49a769a8064
Specification: veryfront/veryfront-issue-inbox#874

Findings, ordered by severity:

No actionable findings.

Evidence:

  • src/integrations/remote-tools.ts:614-657 keeps the shared retryWithBackoff helper as the sole owner of attempts, per-attempt deadlines, backoff, cancellation, and retry classification. Only 5xx request failures and transport TypeError failures retry; typed response-body validation failures do not.
  • src/integrations/remote-tools.ts:619-626,660-692 translates the helper-owned timeout reason to the established diagnostic while checking the caller signal first, preserving caller-abort identity and the single terminal Sentry signature.
  • src/integrations/remote-tools.ts:828-838 resolves the complete tool-list URL and validates project scope before retry entry, so malformed or unsupported base URLs and local project validation failures dispatch zero requests.
  • src/integrations/remote-tools.ts:564-599 includes bounded response-body parsing in the retry attempt, while src/integrations/remote-tools.ts:727-804 leaves non-idempotent tool execution retry-free.
  • Focused regressions cover transport rejection, 5xx, response-body reset, malformed UTF-8, timeout diagnostics, caller cancellation during a retry wait, persistent exhaustion, concurrency/cache behavior, exact error logging, invalid URL, and invalid project scope.

Verification:

  • PASS with repository-pinned Deno 2.7.7: src/integrations/remote-tools.test.ts (48 steps), src/integrations/remote-tools.hardening.test.ts (24 steps), src/integrations/ (264 steps), and API URL resolver tests (5 steps).
  • PASS: deno task fmt:check, deno task lint, canonical deno task typecheck, changed-file diagnostics, and git diff --check.
  • GitHub exact-head snapshot was stable at this SHA with complete review-thread and score-comment pagination, 0 unresolved threads, 24 passed checks, 0 failed checks, 11 pending checks, and 9 conditional skips. Pending checks must finish before merge. The skipped jobs are branch/event-gated release, split-mode, merge-group, or review-reconciliation jobs.

Verdict: APPROVE
Score: 95/100

Review-Gate:
Reviewer: Codex
Reviewed-SHA: c7a41ef
Score: 95/100
Actionable-Findings: 0
Verdict: APPROVE

@sonarqubecloud

Copy link
Copy Markdown

@kwakayama
kwakayama added this pull request to the merge queue Aug 31, 2026
Merged via the queue into main with commit f17f5e0 Aug 31, 2026
56 checks passed
@kwakayama
kwakayama deleted the issue/874-integration-tool-defs-fetch branch August 31, 2026 07:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant