Skip to content

Implement Phase 4 timeout functionality with full builder chain support#5

Merged
adelrodriguez merged 1 commit intomainfrom
02-22-implement_phase_4_timeout_functionality_with_full_builder_chain_support
Feb 26, 2026
Merged

Implement Phase 4 timeout functionality with full builder chain support#5
adelrodriguez merged 1 commit intomainfrom
02-22-implement_phase_4_timeout_functionality_with_full_builder_chain_support

Conversation

@adelrodriguez
Copy link
Copy Markdown
Owner

@adelrodriguez adelrodriguez commented Feb 23, 2026

This pull request implements timeout functionality for the hardtry library, completing Phase 4 of the development plan.

Key Changes:

  • Timeout Controller: Added a new TimeoutController interface and createTimeoutController function to manage timeout logic, including deadline checking and racing promises against timeouts
  • Runner Integration: Enhanced the execution runner to integrate timeout checks at all critical points - before attempts, during try/catch execution, and during retry delays
  • Error Handling: Updated error resolution to properly handle TimeoutError instances as control errors that bypass normal catch mapping
  • Builder Chain Support: Extended the builder API to support timeout in combination with retry, signal, and wrap operations
  • Comprehensive Testing: Added test coverage for timeout behavior in isolation, during retries, in catch handlers, and as part of full builder chains

The implementation uses Promise.race() to enforce timeouts on async operations and tracks elapsed time for synchronous timeout checks. All timeout violations return TimeoutError instances that are treated as control errors and propagate through the execution chain without transformation.

Greptile Summary

Implements Phase 4 timeout functionality with comprehensive integration into the execution flow.

Key additions:

  • TimeoutController interface manages deadline tracking using Date.now() and races promises against timeouts with proper cleanup
  • Timeout checks integrated at 7 critical points: before attempts, after sync execution, during async execution (via Promise.race), before retry delays, during retry delays, and in both sync/async catch handlers
  • TimeoutError treated as control error that bypasses catch mapping and propagates unchanged
  • Builder API extended to support chaining timeout with retry, signal, and wrap operations

Implementation quality:

  • Clean abstraction with the timeout controller pattern
  • Thorough test coverage including isolation tests and full builder chain integration tests
  • Proper timer cleanup in finally blocks to prevent memory leaks
  • Consistent error handling where original errors are preserved as causes when timeout occurs during error handling

Confidence Score: 5/5

  • This PR is safe to merge with no issues found
  • The implementation is thorough and correct with timeout checks at all critical execution points, proper promise cleanup, comprehensive test coverage, and consistent error handling throughout the codebase
  • No files require special attention

Important Files Changed

Filename Overview
src/lib/timeout.ts New file implementing timeout controller with deadline tracking and promise racing - clean implementation with proper cleanup
src/lib/runner.ts Integrated timeout checks at all critical execution points (before attempts, during try/catch, during delays) - thorough and correct
src/tests/index.test.ts Added comprehensive integration tests for full builder chains with timeout - covers sync/async, error mapping, and API equivalence
src/lib/tests/runner.test.ts Added unit tests for timeout in try execution, retry backoff, and catch handlers - good coverage of timeout scenarios

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    Start[Start Execution] --> CreateController[Create TimeoutController]
    CreateController --> CheckTimeout1{Check Timeout<br/>Before Attempt?}
    CheckTimeout1 -->|Expired| ReturnTimeout1[Return TimeoutError]
    CheckTimeout1 -->|OK| ExecuteTry[Execute Try Function]
    
    ExecuteTry --> IsAsync{Is Promise?}
    IsAsync -->|Yes| RacePromise[Race Promise<br/>vs Timeout]
    RacePromise --> RaceResult{Result?}
    RaceResult -->|Timeout| ReturnTimeout2[Return TimeoutError]
    RaceResult -->|Success| ReturnValue[Return Value]
    RaceResult -->|Error| HandleError[Handle Error]
    
    IsAsync -->|No| CheckTimeout2{Check Timeout<br/>After Sync?}
    CheckTimeout2 -->|Expired| ReturnTimeout3[Return TimeoutError]
    CheckTimeout2 -->|OK| ReturnValue
    
    HandleError --> ShouldRetry{Should<br/>Retry?}
    ShouldRetry -->|No| ExecuteCatch[Execute Catch Handler]
    ShouldRetry -->|Yes| CheckTimeout3{Check Timeout<br/>Before Delay?}
    
    CheckTimeout3 -->|Expired| ReturnTimeout4[Return TimeoutError]
    CheckTimeout3 -->|OK| RaceDelay[Race Sleep Delay<br/>vs Timeout]
    RaceDelay --> DelayResult{Result?}
    DelayResult -->|Timeout| ReturnTimeout5[Return TimeoutError]
    DelayResult -->|Complete| CheckTimeout1
    
    ExecuteCatch --> CatchAsync{Is Promise?}
    CatchAsync -->|Yes| RaceCatch[Race Catch<br/>vs Timeout]
    RaceCatch --> CatchResult{Result?}
    CatchResult -->|Timeout| ReturnTimeout6[Return TimeoutError]
    CatchResult -->|Success| ReturnMapped[Return Mapped Value]
    
    CatchAsync -->|No| CheckTimeout4{Check Timeout<br/>After Catch?}
    CheckTimeout4 -->|Expired| ReturnTimeout7[Return TimeoutError]
    CheckTimeout4 -->|OK| ReturnMapped
Loading

Last reviewed commit: d793553

@adelrodriguez adelrodriguez marked this pull request as ready for review February 23, 2026 03:28
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 23, 2026

Warning

Rate limit exceeded

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

⌛ How to resolve this issue?

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

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

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

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

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between d793553 and e215d1d.

📒 Files selected for processing (6)
  • .context/plan.md
  • src/__tests__/index.test.ts
  • src/lib/__tests__/runner.test.ts
  • src/lib/builder.ts
  • src/lib/runner.ts
  • src/lib/timeout.ts

Walkthrough

Adds timeout functionality to a try-retry library by introducing a TimeoutController interface for managing timeout state and promise racing, integrating timeout checks throughout the async runner execution paths, exporting TimeoutError and timeout utilities in the public API, and adding comprehensive test coverage for timeout scenarios across sync/async contexts. Updates type signatures to reflect timeout outcomes and marks the timeout phase as complete in the project plan.

Changes

Cohort / File(s) Summary
Timeout Core Implementation
src/lib/timeout.ts
New TimeoutController interface with checkDidTimeout and raceWithTimeout methods; createTimeoutController factory function computes remaining time from timeoutPolicy.ms and manages timeout state.
Runner Integration
src/lib/runner.ts
Widened type signatures for finalizeError and resolveAttemptError to include TimeoutError in Promise return types; added timeout checks before and after async catch/attempt resolutions and promise race operations.
Test Coverage
src/__tests__/index.test.ts, src/lib/__tests__/runner.test.ts
Comprehensive test suites validating TimeoutError exports, full builder chain with timeout/retry/signal/wrap combinations, and async executeRunAsync timeout handling during try execution, retry backoff, and catch execution.
Code Formatting
src/lib/builder.ts
Removed blank lines between function overload signatures and their implementations in TryBuilder.run and TryBuilder.runAsync.
Project Documentation
.context/plan.md
Marked Phase 4 timeout phase as completed.

Possibly related PRs

  • feat: add execution runner #1: Introduces the initial timeout controller and timeout-aware runner branches that are further integrated in the async execution paths and exported public API in this PR.
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: implementing Phase 4 timeout functionality with full builder chain support, which aligns with the primary objective and all file changes.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, covering timeout controller implementation, runner integration, error handling, builder chain support, and testing—all reflected in the file changes.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 02-22-implement_phase_4_timeout_functionality_with_full_builder_chain_support

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

❤️ Share

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

Copy link
Copy Markdown
Owner Author

adelrodriguez commented Feb 23, 2026

@adelrodriguez
Copy link
Copy Markdown
Owner Author

@coderabbitai review
@greptileai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 26, 2026

@adelrodriguez: I'll review the changes in this PR.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Feb 26, 2026

Greptile Summary

This PR implements Phase 4 timeout functionality with comprehensive integration across the execution pipeline. The implementation correctly enforces timeouts at all critical points - before attempts, during try/catch execution, and during retry delays.

Key implementation highlights:

  • Created TimeoutController interface with checkDidTimeout() for synchronous timeout checks using Date.now() and raceWithTimeout() for racing promises against timeout deadlines
  • Integrated timeout checks throughout the runner at all critical points: before each attempt, after sync execution, during async promise resolution, during retry backoff delays, and during catch handler execution
  • Properly treats TimeoutError as a control error that bypasses catch mapping and propagates unchanged
  • Extended builder chain to support timeout in combination with retry, signal, and wrap operations
  • Added comprehensive test coverage for timeout behavior in isolation, during retries, in catch handlers, and as part of full builder chains

Minor findings:

  • One instance of unreachable dead code in timeout.ts (the timeoutMs === undefined branch in createTimeoutError)
  • Test coverage could include a test for synchronous long-running functions with timeout (currently all timeout tests use async functions with setTimeout)

The implementation is well-architected and handles both synchronous and asynchronous timeout enforcement correctly.

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The implementation is solid with comprehensive timeout integration at all critical execution points. The code correctly handles both sync and async paths, properly treats TimeoutError as a control error, and includes thorough test coverage. The only finding is minor unreachable code that doesn't affect functionality.
  • No files require special attention

Important Files Changed

Filename Overview
src/lib/timeout.ts New timeout controller implementation with clean API for racing promises and checking elapsed time
src/lib/runner.ts Comprehensive timeout integration at all critical execution points (before attempts, during execution, during retries)
src/tests/index.test.ts Added comprehensive full builder chain tests combining retry, timeout, signal, and wrap functionality
src/lib/tests/runner.test.ts Added timeout-specific tests covering try execution, retry backoff, and catch handler timeouts

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    Start([executeRunCore]) --> CreateTimeout[Create TimeoutController]
    CreateTimeout --> BeforeAttempt{Check timeout<br/>before attempt}
    BeforeAttempt -->|Exceeded| ReturnTimeout1[Return TimeoutError]
    BeforeAttempt -->|OK| TryExec[Execute try function]
    
    TryExec --> IsPromise{Returns<br/>Promise?}
    IsPromise -->|Yes| RaceAsync[Race promise<br/>with timeout]
    IsPromise -->|No| CheckSync{Check timeout<br/>after sync result}
    
    RaceAsync --> AsyncResult{Timeout<br/>won race?}
    AsyncResult -->|Yes| ReturnTimeout2[Return TimeoutError]
    AsyncResult -->|No| Success1[Return result]
    
    CheckSync -->|Exceeded| ReturnTimeout3[Return TimeoutError]
    CheckSync -->|OK| Success2[Return result]
    
    TryExec --> TryError[Try throws error]
    TryError --> CheckTimeout1{Check timeout<br/>after error}
    CheckTimeout1 -->|Exceeded| ReturnTimeout4[Return TimeoutError]
    CheckTimeout1 -->|OK| CheckRetry{Should<br/>retry?}
    
    CheckRetry -->|No| HasCatch{Has catch<br/>handler?}
    CheckRetry -->|Yes| CheckTimeoutDelay{Check timeout<br/>before delay}
    
    CheckTimeoutDelay -->|Exceeded| ReturnTimeout5[Return TimeoutError]
    CheckTimeoutDelay -->|OK| RaceDelay[Race sleep<br/>with timeout]
    RaceDelay --> DelayResult{Timeout<br/>won race?}
    DelayResult -->|Yes| ReturnTimeout6[Return TimeoutError]
    DelayResult -->|No| BeforeAttempt
    
    HasCatch -->|No| ReturnUnhandled[Return UnhandledException]
    HasCatch -->|Yes| ExecCatch[Execute catch handler]
    ExecCatch --> IsCatchPromise{Catch returns<br/>Promise?}
    IsCatchPromise -->|Yes| RaceCatch[Race catch promise<br/>with timeout]
    IsCatchPromise -->|No| CheckCatchSync{Check timeout<br/>after catch}
    
    RaceCatch --> CatchResult{Timeout<br/>won race?}
    CatchResult -->|Yes| ReturnTimeout7[Return TimeoutError]
    CatchResult -->|No| ReturnMapped1[Return mapped error]
    
    CheckCatchSync -->|Exceeded| ReturnTimeout8[Return TimeoutError]
    CheckCatchSync -->|OK| ReturnMapped2[Return mapped error]
Loading

Last reviewed commit: e215d1d

Copy link
Copy Markdown
Owner Author

adelrodriguez commented Feb 26, 2026

Merge activity

  • Feb 26, 10:44 AM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Feb 26, 10:46 AM UTC: Graphite couldn't merge this PR because it had merge conflicts.

@adelrodriguez adelrodriguez changed the base branch from graphite-base/5 to main February 26, 2026 10:44
@adelrodriguez adelrodriguez force-pushed the 02-22-implement_phase_4_timeout_functionality_with_full_builder_chain_support branch from d793553 to e215d1d Compare February 26, 2026 10:45
@adelrodriguez adelrodriguez merged commit 75ef562 into main Feb 26, 2026
8 checks passed
Copy link
Copy Markdown

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

6 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines +17 to +22
const createTimeoutError = (cause?: unknown): TimeoutError => {
if (timeoutMs === undefined) {
return new TimeoutError({ cause })
}

return new TimeoutError({ cause: cause ?? createTimeoutCause(timeoutMs) })
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Dead code - the if (timeoutMs === undefined) branch is unreachable. Both checkDidTimeout and raceWithTimeout only call createTimeoutError when timeoutMs !== undefined

Suggested change
const createTimeoutError = (cause?: unknown): TimeoutError => {
if (timeoutMs === undefined) {
return new TimeoutError({ cause })
}
return new TimeoutError({ cause: cause ?? createTimeoutCause(timeoutMs) })
const createTimeoutError = (cause?: unknown): TimeoutError => {
return new TimeoutError({ cause: cause ?? createTimeoutCause(timeoutMs) })
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/timeout.ts
Line: 17-22

Comment:
Dead code - the `if (timeoutMs === undefined)` branch is unreachable. Both `checkDidTimeout` and `raceWithTimeout` only call `createTimeoutError` when `timeoutMs !== undefined`

```suggestion
  const createTimeoutError = (cause?: unknown): TimeoutError => {
    return new TimeoutError({ cause: cause ?? createTimeoutCause(timeoutMs) })
  }
```

How can I resolve this? If you propose a fix, please make it concise.

@adelrodriguez adelrodriguez deleted the 02-22-implement_phase_4_timeout_functionality_with_full_builder_chain_support branch February 26, 2026 11:20
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