Skip to content

refactor(task): replace dispatch lock booleans with DispatchState - #2198

Open
edelauna wants to merge 1 commit into
mainfrom
issue/374
Open

edelauna wants to merge 1 commit into
mainfrom
issue/374

Conversation

@edelauna

@edelauna edelauna commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Related GitHub Issue

Closes: #374

Refs: #325, #359

Description

This PR replaces the two dispatch booleans on Task with a state field. It does not change behavior.

A boolean cannot show whether a serial or a parallel dispatch holds the lock. The parallel path in Story 4.2b needs that information. This PR adds the state machine first, so the serial path stays correct when 4.2b lands.

Changes:

  • Task.dispatchState (IDLE, SERIAL, PARALLEL) replaces presentAssistantMessageLocked.
  • Task.pendingWork ({ mode } | null) replaces presentAssistantMessageHasPendingUpdates.
  • src/core/task/dispatchState.ts holds the enum and transitionDispatch. The helper throws on any transition except IDLE to SERIAL, IDLE to PARALLEL, SERIAL to IDLE, and PARALLEL to IDLE.
  • presentAssistantMessage queues pendingWork when the state is not IDLE. The existing drain loop in the try block reads pendingWork.
  • PARALLEL is defined but never set. It is reserved for Story 4.2b.

Points for reviewers:

  • The finally block and the per-request reset in Task.ts assign IDLE directly. They do not call transitionDispatch. A call from a reset would throw IDLE to IDLE and hide the original error.
  • The drain stays inside the try block, as before. It does not move to finally.
  • transitionDispatch is only called for IDLE to SERIAL. A release that only the owning pass can do belongs in Story 4.2b.
  • Nothing drains work queued during PARALLEL yet. Story 4.2b must add that.
  • [BUG] Chat window state duplicated (messages/blocks render twice), not limited to one mode #325 stays open. This PR removes one possible cause of the duplicate render. It does not confirm the fix.
  • The issue says existing tests must pass without changes. The old fields no longer exist, so the specs only change their mocks and assertions to the new field names. No assertion is weaker or removed.

Test Procedure

  1. Run pnpm --dir src run check-types.
  2. Run pnpm --dir src exec vitest run core/assistant-message core/task/__tests__/Task.spec.ts core/task/__tests__/ToolExecutionContext.spec.ts. All 357 tests pass.
  3. Run pnpm tool-turn:model-check. It passes.

The new spec presentAssistantMessage.spec.ts covers:

  • A call while SERIAL or PARALLEL queues pendingWork and does not run.
  • One drain after the first call completes.
  • N rapid re-entrant calls run at most two times.
  • The state returns to IDLE when the presenter throws.
  • Every valid and invalid transition of transitionDispatch.

Pre-Submission Checklist

  • Issue Linked: This PR is linked to an approved GitHub Issue (see "Related GitHub Issue" above).
  • Scope: My changes are focused on the linked issue (one major feature/fix per PR).
  • Self-Review: I reviewed my own code with care.
  • Writing Style: The PR title and description use Simplified Technical English (ASD-STE100): short sentences, plain words, active voice, one idea per sentence, no contractions.
  • Testing: New and/or updated tests have been added to cover my changes (if applicable).
  • Visual Snapshot (UI changes only): Not applicable. This PR has no UI change.
  • Documentation Impact: I checked if my changes need documentation updates (see "Documentation Updates" section below).
  • Contribution Guidelines: I read and agree to the Contributor Guidelines.

Documentation Updates

  • No documentation updates are required.

The lifecycle model doc and the model script comment now name the new fields.

Additional Notes

Follow-up for Story 4.2b: release the state only from the pass that owns it, and drain work queued during PARALLEL.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between 5cc5a37 and 529861c.

Files selected for processing: 14
  • docs/architecture/task-lifecycle-model.md
  • scripts/check-tool-turn-continuation.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage-auto-deny.spec.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage-custom-tool.spec.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage-images.spec.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage-tool-repetition.spec.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage-tool-usage-attribution.spec.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage-unknown-tool.spec.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage.spec.ts
  • src/core/assistant-message/presentAssistantMessage.ts
  • src/core/task/Task.ts
  • src/core/task/__tests__/Task.spec.ts
  • src/core/task/__tests__/ToolExecutionContext.spec.ts
  • src/core/task/dispatchState.ts

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 6 remain after this review.

Recent review details
Context from checks skipped due to timeout: 1
  • GitHub Check: mutation-diff
Additional context used
Path-based instructions: 5
Check persistence and lifecycle invariants: awaited atomic writes, rollback or explicit partial-failure behavior, cross-window state consistency, stale listeners/watchers, cancellation, idempotency, and safe restart/resume without lost or d...

CodeRabbit configuration file

Files:

  • src/core/task/dispatchState.ts
  • src/core/task/__tests__/ToolExecutionContext.spec.ts
  • src/core/task/__tests__/Task.spec.ts
  • src/core/task/Task.ts
Require regression coverage at the lowest valid harness with behavior-focused assertions, including relevant negative, error, false/unset, and boundary cases.

CodeRabbit configuration file

Files:

  • src/core/assistant-message/__tests__/presentAssistantMessage-tool-usage-attribution.spec.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage-unknown-tool.spec.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage-auto-deny.spec.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage-images.spec.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage-tool-repetition.spec.ts
  • src/core/task/__tests__/ToolExecutionContext.spec.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage.spec.ts
  • src/core/task/__tests__/Task.spec.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage-custom-tool.spec.ts
Check strict typing and exhaustive behavior across normal, boundary, error, cancellation, retry, and compatibility paths.

CodeRabbit configuration file

Files:

  • scripts/check-tool-turn-continuation.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage-tool-usage-attribution.spec.ts
  • src/core/assistant-message/presentAssistantMessage.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage-unknown-tool.spec.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage-auto-deny.spec.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage-images.spec.ts
  • src/core/task/dispatchState.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage-tool-repetition.spec.ts
  • src/core/task/__tests__/ToolExecutionContext.spec.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage.spec.ts
  • src/core/task/__tests__/Task.spec.ts
  • src/core/task/Task.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage-custom-tool.spec.ts
Verify extension/webview contracts, cancellation and error propagation, VS Code lifecycle correctness, and behavior under retries and partial failure.

CodeRabbit configuration file

Files:

  • src/core/assistant-message/__tests__/presentAssistantMessage-tool-usage-attribution.spec.ts
  • src/core/assistant-message/presentAssistantMessage.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage-unknown-tool.spec.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage-auto-deny.spec.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage-images.spec.ts
  • src/core/task/dispatchState.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage-tool-repetition.spec.ts
  • src/core/task/__tests__/ToolExecutionContext.spec.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage.spec.ts
  • src/core/task/__tests__/Task.spec.ts
  • src/core/task/Task.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage-custom-tool.spec.ts
Act as an adversarial second-opinion reviewer.

CodeRabbit configuration file

Files:

  • scripts/check-tool-turn-continuation.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage-tool-usage-attribution.spec.ts
  • src/core/assistant-message/presentAssistantMessage.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage-unknown-tool.spec.ts
  • docs/architecture/task-lifecycle-model.md
  • src/core/assistant-message/__tests__/presentAssistantMessage-auto-deny.spec.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage-images.spec.ts
  • src/core/task/dispatchState.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage-tool-repetition.spec.ts
  • src/core/task/__tests__/ToolExecutionContext.spec.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage.spec.ts
  • src/core/task/__tests__/Task.spec.ts
  • src/core/task/Task.ts
  • src/core/assistant-message/__tests__/presentAssistantMessage-custom-tool.spec.ts
Additional comments: 14
src/core/task/dispatchState.ts: 1

1-4: LGTM!

Also applies to: 7-7, 9-12, 15-19

src/core/task/Task.ts: 1

112-112: LGTM!

Also applies to: 520-521, 613-613, 4139-4140

src/core/task/__tests__/Task.spec.ts: 1

4-4: LGTM!

Also applies to: 650-650, 652-654, 700-700, 921-921, 988-988, 994-994, 1062-1062

docs/architecture/task-lifecycle-model.md: 1

105-105: LGTM!

scripts/check-tool-turn-continuation.ts: 1

23-23: LGTM!

Also applies to: 25-25

src/core/assistant-message/presentAssistantMessage.ts: 1

2-2: LGTM!

Also applies to: 106-107, 111-111, 119-119, 123-123, 134-134

src/core/assistant-message/__tests__/presentAssistantMessage.spec.ts: 1

1-108: LGTM!

src/core/assistant-message/__tests__/presentAssistantMessage-custom-tool.spec.ts: 1

3-3: LGTM!

Also applies to: 71-72, 228-228, 232-232, 238-238, 249-249, 256-257, 283-283, 295-295, 298-298, 304-306, 308-311, 315-315, 318-318, 330-331, 340-340, 343-344, 354-355, 371-371, 374-374, 385-385, 390-390, 408-408

src/core/assistant-message/__tests__/presentAssistantMessage-auto-deny.spec.ts: 1

3-3: LGTM!

Also applies to: 73-74, 106-107

src/core/assistant-message/__tests__/presentAssistantMessage-images.spec.ts: 1

3-3: LGTM!

Also applies to: 44-45

src/core/assistant-message/__tests__/presentAssistantMessage-tool-repetition.spec.ts: 1

3-3: LGTM!

Also applies to: 157-158, 221-222

src/core/assistant-message/__tests__/presentAssistantMessage-tool-usage-attribution.spec.ts: 1

3-3: LGTM!

Also applies to: 64-65, 104-105

src/core/assistant-message/__tests__/presentAssistantMessage-unknown-tool.spec.ts: 1

3-3: LGTM!

Also applies to: 46-47, 247-247

src/core/task/__tests__/ToolExecutionContext.spec.ts: 1

3-3: LGTM!

Also applies to: 48-49, 162-162

📝 Summary

Summary by CodeRabbit

  • Bug Fixes
    • Improved coordination of assistant message presentation and tool execution, preventing overlapping presentations and ensuring queued updates are handled after the active presentation completes.
    • Task readiness now reflects whether dispatch is idle, including after completion, aborts, and errors.

The task and assistant-message presenter replace two presenter-lock flags with dispatchState and nullable pendingWork. The presenter queues serial work during active dispatch, drains pending updates, and returns to IDLE. Tests and lifecycle mappings now use the dispatch state.

Changes: Task dispatch state and lifecycle

Layer / File(s) Summary
Dispatch state and Task lifecycle
src/core/task/dispatchState.ts, src/core/task/Task.ts, src/core/task/__tests__/Task.spec.ts, docs/architecture/task-lifecycle-model.md, scripts/check-tool-turn-continuation.ts
Adds IDLE, SERIAL, and PARALLEL states, a pending-work type, and transition validation. Task uses dispatch state and pending work for readiness and request resets. Lifecycle tests and mappings use the new fields.

Assistant-message presentation

Layer / File(s) Summary
Presenter dispatch and re-entrancy
src/core/assistant-message/presentAssistantMessage.ts, src/core/assistant-message/__tests__/presentAssistantMessage.spec.ts, src/core/assistant-message/__tests__/presentAssistantMessage-custom-tool.spec.ts
The presenter queues serial work while dispatch is active, drains pending work, and restores IDLE in finally. Tests cover re-entrant calls, state transitions, queued updates, aborts, and failures.
Task mocks and supporting tests
src/core/assistant-message/__tests__/presentAssistantMessage-auto-deny.spec.ts, src/core/assistant-message/__tests__/presentAssistantMessage-images.spec.ts, src/core/assistant-message/__tests__/presentAssistantMessage-tool-repetition.spec.ts, src/core/assistant-message/__tests__/presentAssistantMessage-tool-usage-attribution.spec.ts, src/core/assistant-message/__tests__/presentAssistantMessage-unknown-tool.spec.ts, src/core/task/__tests__/ToolExecutionContext.spec.ts
Test fixtures replace the former presenter-lock flags with dispatchState and pendingWork. Related assertions and resets use the new state.

Priority: Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Refactor

Merge Risk: Minimal · up to 52986

The dispatch-state refactor has no established merge-blocking issue; it is ready for normal merge checks.

Pre-merge checks: Passed 8
Check name Status Explanation
Description check Passed The description includes the linked issues, implementation details, reviewer notes, test procedure, checklist, and documentation impact. It omits the Get in Touch section, but the required technical c…
Title check Passed The title is concise, specific, and accurately describes the main refactor from dispatch lock booleans to DispatchState.
Linked Issues check Passed #374 requirements are implemented. Task now uses dispatchState and nullable pendingWork; the former boolean fields are removed. DispatchState.PARALLEL is defined but not assigned by the serial…
Out of Scope Changes check Passed The changes stay within #374. The production changes implement the dispatch state model and serial re-entrancy flow. The test changes cover that flow and update affected task fixtures. The architectur…
Regression Evidence Passed PASS. The changed dispatch behavior has focused unit coverage. presentAssistantMessage.spec.ts covers SERIAL and PARALLEL queueing, one pending drain, rapid re-entry, presenter failure cleanup, and …
Security Boundaries Passed No changed path matches the security failure conditions. The runtime changes only replace the internal presenter lock with DispatchState and pendingWork; the only queued value is the fixed `{ mode…
Persistence Integrity Passed No changed persistence path exists. The PR changes only in-memory Task.dispatchState/pendingWork, the presenter control flow, tests, and documentation. The diff adds no storage or database writes,…
Lifecycle Resource Cleanup Passed No changed lifecycle path meets the failure condition. presentAssistantMessage sets SERIAL, clears pendingWork at each pass, drains at most one pending continuation per pass, stops draining when…
Full details: Description check

Explanation

The description includes the linked issues, implementation details, reviewer notes, test procedure, checklist, and documentation impact. It omits the Get in Touch section, but the required technical content is otherwise complete.

Full details: Linked Issues check

Explanation

#374 requirements are implemented. Task now uses dispatchState and nullable pendingWork; the former boolean fields are removed. DispatchState.PARALLEL is defined but not assigned by the serial presenter. transitionDispatch accepts only the four required transitions and throws for other transitions. presentAssistantMessage queues serial work while dispatch is active, drains one pending pass, and restores IDLE in finally. The added and updated tests cover re-entrancy, bounded draining, error reset, and transition validation. The test updates only rename the removed state fields and preserve the existing behavior checks.

Full details: Out of Scope Changes check

Explanation

The changes stay within #374. The production changes implement the dispatch state model and serial re-entrancy flow. The test changes cover that flow and update affected task fixtures. The architecture document and model-check comments document the renamed state fields. No unrelated behavior or feature change is shown.

Full details: Regression Evidence

Explanation

PASS. The changed dispatch behavior has focused unit coverage. presentAssistantMessage.spec.ts covers SERIAL and PARALLEL queueing, one pending drain, rapid re-entry, presenter failure cleanup, and every valid and invalid transitionDispatch transition. presentAssistantMessage-custom-tool.spec.ts covers handoff draining, abort stopping, consecutive blocks, partial updates, later-block errors, and dispatch errors. Task.spec.ts covers IDLE, SERIAL, and PARALLEL readiness states plus real presenter completion, failure, and abort paths. The documentation and comment updates introduce no executable behavior, and the PR has no durable UI change requiring a Playwright snapshot.

Full details: Security Boundaries

Explanation

No changed path matches the security failure conditions. The runtime changes only replace the internal presenter lock with DispatchState and pendingWork; the only queued value is the fixed { mode: "serial" }, and state transitions use internal enum values. Tool validation and approval checks remain in place, with no changed secret or PII handling and no new execution of unvalidated input.

Full details: Persistence Integrity

Explanation

No changed persistence path exists. The PR changes only in-memory Task.dispatchState/pendingWork, the presenter control flow, tests, and documentation. The diff adds no storage or database writes, serialization, rollback, or persistence defaults. Therefore no persistence loss or corruption condition is introduced.

Full details: Lifecycle Resource Cleanup

Explanation

No changed lifecycle path meets the failure condition. presentAssistantMessage sets SERIAL, clears pendingWork at each pass, drains at most one pending continuation per pass, stops draining when abort is true, and always restores IDLE in finally (src/core/assistant-message/presentAssistantMessage.ts:106-124, 127-135). The request reset also clears both fields before a new turn (src/core/task/Task.ts:4118-4142). The refactor preserves the prior boolean guard and cleanup behavior; PARALLEL is defined but no production path sets it.

Finishing Touches
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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 Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Review status

Thanks for contributing. This comment tracks the review sequence and the next action.

Current step: Awaiting fresh human maintainer or CODEOWNER approval.

Automated review is complete for the latest commit but does not replace human approval.

Review-state labels are managed by this workflow; do not edit them manually. community-approved is managed the same way — do not add or remove it manually. It signals a fresh community code approval for the current head as an advisory priority only; maintainer review is still required.

@codecov

codecov Bot commented Oct 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions github-actions Bot added coderabbit-review-active Required CI passed; CodeRabbit review is active awaiting-coderabbit Waiting for CodeRabbit to approve the latest commit labels Oct 26, 2026
@github-actions github-actions Bot added awaiting-maintainer CodeRabbit approved; waiting for a human maintainer and removed coderabbit-review-active Required CI passed; CodeRabbit review is active awaiting-coderabbit Waiting for CodeRabbit to approve the latest commit labels Oct 26, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting-maintainer CodeRabbit approved; waiting for a human maintainer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Story 4.2a] DispatchState machine refactor

1 participant