chore: enforce no-floating-promises in core/task/#253
Conversation
Next slice of the no-floating-promises ratchet — widens the rule's scope in eslint.config.mjs to core/task/**. core/task/ had 25 un-awaited promises. Task.ts (19): all genuine fire-and-forget, marked void — task-loop kickoffs in the constructor and start(), UI posts in synchronous/streaming/setTimeout paths, the explicitly-background checkpoint init, and 9 presentAssistantMessage calls (a self-locking streaming presenter designed for fire-and-forget). void preserves current behavior, and the file already uses void this.updateClineMessage(...) elsewhere. Task.spec.ts (6): un-awaited task.submitUserMessage(...) calls in async tests, now awaited.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Pull request overview
Expands the @typescript-eslint/no-floating-promises “ratchet” to cover src/core/task/**, and updates Task implementation + tests to satisfy the rule without changing intended async behavior.
Changes:
- Extend
eslint.config.mjsto enforceno-floating-promisesincore/task/**/*.ts. - Mark intended fire-and-forget async calls in
Task.tswithvoid. - Await
submitUserMessage(...)in async tests to ensure actions under test complete before assertions.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/eslint.config.mjs |
Adds core/task/**/*.ts to the scoped no-floating-promises enforcement block. |
src/core/task/Task.ts |
Replaces floating promises with explicit void for intentional background operations / UI updates. |
src/core/task/__tests__/Task.spec.ts |
Awaits task.submitUserMessage(...) calls in async tests to satisfy lint and improve test determinism. |
Comments suppressed due to low confidence (3)
src/core/task/Task.ts:575
startTask()/resumeTaskFromHistory()can throw/reject on unexpected failures (they rethrow in their catch blocks). Kicking them off withvoidin the constructor means those failures become unhandled promise rejections. Consider adding an explicit.catch(...)handler (while keeping the background execution) to log or route errors throughprovider.log.
if (startTask) {
this._started = true
if (task || images) {
void this.startTask(task, images)
} else if (historyItem) {
void this.resumeTaskFromHistory()
} else {
src/core/task/Task.ts:1788
startTask()is async and may reject; starting it withvoidhere risks an unhandled rejection if initialization fails. Consider handling the background promise with.catch(...)(and optionally logging via the provider) so unexpected failures don’t surface as process-level unhandled rejections.
const { task, images } = this.metadata
if (task || images) {
void this.startTask(task ?? undefined, images ?? undefined)
}
src/core/task/Task.ts:2772
presentAssistantMessage()is async and can reject (e.g., it throws whenabortis set). Calling it fire-and-forget viavoidmeans a rejection would be unhandled. Consider adding a.catch(...)handler (while keeping it non-blocking) to avoid process-level unhandled rejections during cancellation/aborts.
// Add to content and present
this.assistantMessageContent.push(partialToolUse)
this.userMessageContentReady = false
void presentAssistantMessage(this)
} else if (event.type === "tool_call_delta") {
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| this.emit(RooCodeEventName.TaskUserMessage, this.taskId) | ||
| this.emit(RooCodeEventName.QueuedMessagesUpdated, this.taskId, this.messageQueueService.messages) | ||
| this.providerRef.deref()?.postStateToWebviewWithoutTaskHistory() | ||
| void this.providerRef.deref()?.postStateToWebviewWithoutTaskHistory() |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Summary
Next slice of the
no-floating-promisesratchet (follows #251). Widens the rule's scope ineslint.config.mjsto includecore/task/**.core/task/had 25 un-awaited promises.Task.ts— 19, all markedvoidEvery one is genuine fire-and-forget, so
void(which preserves current behavior) is the correct fix:presentAssistantMessage(this)— its doc comment describes a self-locking streaming presenter designed to be called concurrently/fire-and-forget; awaiting it would be wrong.startTask/resumeTaskFromHistoryin the constructor and the synchronousstart(); awaiting isn't possible there and the task is meant to run in the background.updateClineMessage— UI message posts in the hot streaming path.awaitwould change streaming behavior;voidpreserves it, and the file already usesvoid this.updateClineMessage(...)at line 1396.setTimeoutcallback.getCheckpointService(this)— the existing comment states it kicks the work off "in the background".Task.spec.ts— 6, awaitedUn-awaited
task.submitUserMessage(...)calls inasynctests — tests should await the action under test; these are nowawaited.Test plan
eslint core/task/clean.eslint . --ext=ts --max-warnings=0(the repo lint command) passes.tsc --noEmitclean.core/task/test suite passes (17 files, 175 tests).