Skip to content

feat: implement task pause/resume functionality (closes #383)#455

Closed
pkonal23 wants to merge 9 commits intoGetBindu:mainfrom
pkonal23:feat/pause-resume-task-383
Closed

feat: implement task pause/resume functionality (closes #383)#455
pkonal23 wants to merge 9 commits intoGetBindu:mainfrom
pkonal23:feat/pause-resume-task-383

Conversation

@pkonal23
Copy link
Copy Markdown
Contributor

@pkonal23 pkonal23 commented Apr 16, 2026

Summary

Implements the Task Pause/Resume feature that was marked as incomplete in issue #383. This PR adds proper state management for pausing and resuming long-running tasks.

What Changed

1. Error Types (types.py)

  • Added TaskNotPausableError (-32007)
  • Added TaskNotResumableError (-32008)

2. Request/Response Types (types.py)

  • Added PauseTaskRequest/PauseTaskResponse
  • Added ResumeTaskRequest/ResumeTaskResponse
  • Added to A2ARequest/A2AResponse discriminated unions

3. Settings (settings.py)

  • Added tasks/pause and tasks/resume to method_handlers
  • Added "suspended" and "resumed" to non_terminal_states

4. TaskManager (task_manager.py)

  • Added pause_task() and resume_task() router methods

5. TaskHandlers (task_handlers.py)

  • Implemented pause_task() with state validation (only "working" state allowed)
  • Implemented resume_task() with state validation (only "suspended" state allowed)

6. Worker Handlers (workers/base.py)

  • Implemented _handle_pause() - updates state to "suspended"
  • Implemented _handle_resume() - updates state to "resumed" and re-queues task

Testing

Created unit tests in tests/unit/server/handlers/test_task_handlers.py covering:

  • pause_task: success, not working state, not found
  • resume_task: success, not suspended state, not found

All tests pass: ✅

Validation Rules

Operation Valid State Invalid States
pause working submitted, completed, failed, canceled, suspended, resumed, input-required, auth-required
resume suspended working, submitted, completed, failed, canceled, resumed, input-required, auth-required

API Usage

// Pause a task
{"method": "tasks/pause", "params": {"taskId": "uuid"}}

// Resume a task  
{"method": "tasks/resume", "params": {"taskId": "uuid"}}

Files Modified

  • bindu/common/protocol/types.py
  • bindu/settings.py
  • bindu/server/task_manager.py
  • bindu/server/handlers/task_handlers.py
  • bindu/server/workers/base.py
  • tests/unit/server/handlers/test_task_handlers.py

Related Issues


Generated with OpenClaude

Summary by CodeRabbit

  • New Features

    • Task pause/resume endpoints: pause running tasks and resume suspended tasks with validation and explicit error responses.
  • Documentation

    • Added project context, contribution opportunities, pause/resume specification and debugging log, test contribution plan, and minor metadata cleanup.
  • Examples

    • Added a slow-echo agent demonstrating delayed handler behavior.
  • Tests

    • Added unit tests and an executable pause/resume test script covering lifecycle scenarios.

pkonal23 and others added 3 commits April 17, 2026 00:02
## Summary

Implements the Task Pause/Resume feature that was marked as incomplete
in PR GetBindu#357. The implementation adds proper state management for pausing
and resuming long-running tasks.

## What Changed

### 1. Error Types (types.py)
- Added TaskNotPausableError (-32007)
- Added TaskNotResumableError (-32008)

### 2. Request/Response Types (types.py)
- Added PauseTaskRequest/PauseTaskResponse
- Added ResumeTaskRequest/ResumeTaskResponse
- CRITICAL: Added these to A2ARequest/A2AResponse discriminated unions

### 3. Settings (settings.py)
- Added tasks/pause and tasks/resume to method_handlers
- Added "suspended" and "resumed" to non_terminal_states

### 4. TaskManager (task_manager.py)
- Added pause_task() and resume_task() router methods

### 5. TaskHandlers (task_handlers.py)
- Implemented pause_task() with state validation (only "working" state)
- Implemented resume_task() with state validation (only "suspended" state)

### 6. Worker Handlers (workers/base.py)
- Implemented _handle_pause() - updates state to "suspended"
- Implemented _handle_resume() - updates state to "resumed" and re-queues task

## Testing

Created test script (test_pause_resume.py) and slow echo agent
(examples/beginner/slow_echo_agent.py) for testing.

### Critical Finding for Testing
The agent handler MUST use asyncio.sleep() instead of time.sleep():
- time.sleep() BLOCKS the event loop, preventing pause/resume
- asyncio.sleep() YIELDS control, allowing pause/resume to work

All 4 test cases pass:
✅ Pause working task → suspended
✅ Pause completed task → TaskNotPausableError
✅ Resume suspended task → resumed (re-queued)
✅ Resume working task → TaskNotResumableError

## Validation Rules

- Pause: only allowed in "working" state
- Resume: only allowed in "suspended" state

## API Usage

// Pause a task
{"method": "tasks/pause", "params": {"taskId": "uuid"}}

// Resume a task
{"method": "tasks/resume", "params": {"taskId": "uuid"}}

## Files Modified

- bindu/common/protocol/types.py
- bindu/settings.py
- bindu/server/task_manager.py
- bindu/server/handlers/task_handlers.py
- bindu/server/workers/base.py

## Related Issues

- Closes GetBindu#383 (the original bug report about unimplemented pause/resume)
- Related to GetBindu#356 (feature request) and GetBindu#357 (attempted implementation)

Co-Authored-By: Claude Opus 4.6 <noreply@openclaude.dev>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 16, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Task pause/resume functionality was added across protocol types, server handlers, task manager, worker execution, settings, docs, examples, and tests to implement JSON‑RPC tasks/pause and tasks/resume flows, new lifecycle error types, worker pause/resume behavior, and corresponding unit/integration tests addressing Issue #383.

Changes

Cohort / File(s) Summary
Protocol Types
bindu/common/protocol/types.py
Added TaskNotPausableError and TaskNotResumableError; added tasks/pause/tasks/resume request/response types; extended A2ARequest/A2AResponse unions.
Server Handlers
bindu/server/handlers/task_handlers.py
Added pause_task and resume_task RPC handlers with state validation and typed error responses; adjusted cancel_task to handle deleted-between cases.
Task Manager
bindu/server/task_manager.py
Added TaskManager.pause_task and resume_task delegating to handlers; removed caller_did optional param from task_feedback signature (method signature/body changed).
Worker Implementation
bindu/server/workers/base.py
Implemented _handle_pause and _handle_resume: normalize IDs, load tasks, update state/metadata (suspended/resumed), persist changes, and re-queue on resume via scheduler.
Settings & Config
bindu/settings.py
Registered "tasks/pause" and "tasks/resume" in AgentSettings.method_handlers; added suspended and resumed to non_terminal_states.
Tests & Examples
tests/unit/server/handlers/test_task_handlers.py, tests/unit/server/workers/test_base_worker.py, test_pause_resume.py, examples/beginner/slow_echo_agent.py
Added unit tests for handler and worker pause/resume paths, an integration-style pause/resume test script, and a slow agent example to exercise long-running tasks.
Docs & Contribution
PAUSE_RESUME_IMPLEMENTATION.md, PAUSE_RESUME_DEBUGGING.md, CONTRIBUTION_OPPORTUNITIES.md, PROJECT_CONTEXT.md, TEST_CONTRIBUTION_PLAN.md, CLAUDE.md
Added specification, debugging notes, contribution guidance, project/context docs, and testing roadmap documenting pause/resume design, implementation notes, and contribution/testing guidance.
Misc Tests
tests/unit/utils/...
Expanded miscellaneous unit tests (capabilities, logging, server runner, tracing) to increase coverage alongside feature work.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Server as Server (TaskHandlers)
    participant Scheduler
    participant Worker
    participant Storage

    Note over Client,Storage: Task Pause Flow
    Client->>Server: tasks/pause (task_id)
    Server->>Storage: load_task(task_id)
    Storage-->>Server: Task (state=working)
    alt State == "working"
        Server->>Scheduler: pause_task(task_id)
        Scheduler->>Worker: _handle_pause(task_id)
        Worker->>Storage: update_task(task_id, state="suspended", metadata+=paused_at, pause_checkpoint)
        Storage-->>Worker: ✓
        Scheduler-->>Server: ✓
        Server-->>Client: PauseTaskResponse {Task}
    else
        Server-->>Client: Error: TaskNotPausableError
    end

    Note over Client,Storage: Task Resume Flow
    Client->>Server: tasks/resume (task_id)
    Server->>Storage: load_task(task_id)
    Storage-->>Server: Task (state=suspended)
    alt State == "suspended"
        Server->>Scheduler: resume_task(task_id)
        Scheduler->>Worker: _handle_resume(task_id)
        Worker->>Storage: load_task(task_id)
        Storage-->>Worker: Task
        Worker->>Storage: update_task(task_id, state="resumed", metadata+=resumed_at)
        Worker->>Scheduler: run_task(TaskSendParams(task_id, context_id, message?))
        Scheduler-->>Server: ✓
        Server-->>Client: ResumeTaskResponse {Task}
    else
        Server-->>Client: Error: TaskNotResumableError
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Fix/task ownership idor #460 — Modifies task handler/manager code paths overlapping with these pause/resume additions; may conflict with method signatures and ownership checks.

Poem

🐰 I twitched my nose and took a nap,

I paused the tasks without a gap,
Then nudged them gently—off they run,
From suspended hops to resumed fun,
Hooray! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is incomplete. It lacks critical sections from the template: Environment/OS/Python version, Steps to Test, Expected/Actual Behavior, Evidence, Human Verification, Compatibility/Migration, Failure Recovery, and Risks/Mitigations. Change Type checkboxes are unchecked. Complete all required template sections: add testing environment details, specific test steps with expected outcomes, verification evidence (test results/logs), human verification details, backward compatibility assessment, and failure recovery procedures.
Out of Scope Changes check ⚠️ Warning The PR includes substantial scope beyond the core pause/resume implementation: multiple documentation files (CLAUDE.md update, CONTRIBUTION_OPPORTUNITIES.md, PROJECT_CONTEXT.md, TEST_CONTRIBUTION_PLAN.md, PAUSE_RESUME_*.md), an example file (slow_echo_agent.py), and integration test script (test_pause_resume.py). While these provide context and testing infrastructure, they extend beyond the #383 objectives. Clarify scope boundaries: confirm whether documentation and example files are required for this PR or should be separated. Focus this PR on core implementation and unit tests per issue #383; move supplementary documentation to follow-up PRs if appropriate.
Linked Issues check ❓ Inconclusive The PR substantially addresses issue #383 objectives: implements _handle_pause() and _handle_resume() in workers/base.py with state management and scheduler re-queueing; adds comprehensive error types, request/response types, and method handlers; includes unit tests for handlers (though worker tests are present). However, documentation updates (README.md or docs/SCHEDULER.md) mentioned in issue objectives are not evidenced in the changeset. Verify that documentation updates (README.md or docs/SCHEDULER.md describing pause/resume behavior, API usage, and storage/scheduler requirements) have been completed or confirm they are out of scope for this PR.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and concisely describes the main feature being implemented (task pause/resume functionality) and references the related issue (#383).
Docstring Coverage ✅ Passed Docstring coverage is 95.65% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 17

🧹 Nitpick comments (1)
tests/unit/server/handlers/test_task_handlers.py (1)

152-170: Consider parametrizing invalid-state tests to cover the full validation matrix.

Current tests verify one invalid state per operation. A parametrized set for all disallowed states would better protect the “pause only from working / resume only from suspended” contract.

Also applies to: 209-227

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/server/handlers/test_task_handlers.py` around lines 152 - 170,
Replace the single-case invalid-state unit tests with parametrized tests using
pytest.mark.parametrize to cover all disallowed task states; for example, update
test_pause_task_not_working_state in the test module to iterate over a list of
non-"working" states (e.g., "completed", "suspended", "cancelled", "pending")
and assert the handler.pause_task returns an error and scheduler.pause_task is
not called for each state, and similarly parametrize the resume test (the tests
around lines 209-227) to iterate over all non-"suspended" states to assert
handler.resume_task returns an error and scheduler.resume_task is not called;
keep using AsyncMock mock_storage.load_task to return the parametrized mock_task
and preserve the existing error_response_creator behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@bindu/common/protocol/types.py`:
- Around line 1375-1388: TaskNotPausableError and TaskNotResumableError
introduce JSON-RPC code collisions (they reuse -32007 and -32008); locate the
JSONRPCError definitions for TaskNotPausableError and TaskNotResumableError and
change their numeric Literal codes to unique, unused JSON-RPC error codes (pick
consecutive unused values, e.g., -32027 and -32028 or follow your module's
unused range), ensuring no other JSONRPCError in this module uses those codes;
also update any documentation or mappings that reference the old codes to the
new ones so each semantic error maps to a single unique code.

In `@bindu/server/handlers/task_handlers.py`:
- Around line 130-135: Replace the post-operation "assert task is not None"
checks with explicit error handling: after calling
self.scheduler.pause_task(...) and getting task = await
self.storage.load_task(task_id) (and the similar block around lines 165-170),
test if task is None and raise/return a typed JSON-RPC error (or the project's
standard RPCError/JsonRpcError) with a clear message like "task not found or
expired" and an appropriate error code, instead of allowing an AssertionError to
bubble up; update both occurrences (the pause flow using
self.scheduler.pause_task and the other corresponding flow) to follow this
pattern.

In `@bindu/server/workers/base.py`:
- Around line 285-295: The current logic only calls self.scheduler.run_task(...)
when message is truthy, leaving tasks stuck in "resumed" if history is empty;
change the flow so you always call await
self.scheduler.run_task(TaskSendParams(...)) for the resumed task (using
task_id, task["context_id"], and the message variable even if None/empty) so the
task is re-queued regardless of history, and adjust logging to still warn if
message is empty but log that the task was re-queued (use scheduler.run_task,
TaskSendParams, task_id, task["context_id"], and message to locate and update
the code).

In `@CLAUDE.md`:
- Around line 79-83: The Background section is stale: update the CLAUDE.md text
that claims pause/resume is not implemented to reflect the PR’s actual
implementation (remove or replace the `raise NotImplementedError` snippet and
the assertion that pause/resume is unimplemented). Reference the implemented
handler `_handle_pause` in `workers/base.py` (and `_handle_resume` if present)
and describe the current behavior (briefly summarize how pause/resume works now)
so the docs match the code changes.
- Around line 55-59: The fenced code block that begins with "1. [Step] → verify:
[check]" should specify a language (e.g., change the opening fence to ```text)
and the tables that start with the header row "| # | Test Case | Expected |"
must be separated from the preceding headings/paragraphs by a blank line (add a
blank line before each table and, where suggested, a blank line after the "###
Pause Operation" / "### Resume Operation" headings) so markdownlint stops
flagging missing language and table separation; locate the fenced block and the
two tables by searching for the exact list lines and the table header line to
apply these fixes.

In `@CONTRIBUTION_OPPORTUNITIES.md`:
- Line 175: Replace the mixed-language artifact "Good first issue标签" with the
intended English text (e.g., "Good first issue") in
CONTRIBUTION_OPPORTUNITIES.md and remove the stray raw filenames that appear
after the timestamp on the line referenced (line containing the trailing
filenames) so only the timestamp or intended metadata remains; search for the
exact token "Good first issue标签" and the trailing filename text to locate the
strings and edit them to clean user-facing text.
- Around line 68-73: The CONTRIBUTION_OPPORTUNITIES.md section listing "TODO:
Implement task pause functionality" and "TODO: Implement task resume
functionality" for bindu/server/workers/base.py is now stale because the task
pause/resume handlers (e.g., the implemented methods in
bindu.server.workers.base such as pause_task and resume_task or the handler
methods responsible for pause/resume) are present in the code; update the
document by removing those two TODO entries or replacing them with an accurate
description of the current status (e.g., link to the implemented
pause_task/resume_task methods or note remaining enhancements), ensuring the
file no longer claims pause/resume are unimplemented.

In `@PAUSE_RESUME_DEBUGGING.md`:
- Line 28: The documentation contains a typo: update the union registration note
that currently says "Added PauseTaskRequest and ResumeTaskResponse to the
A2ARequest Union" so it correctly reads "Added PauseTaskRequest and
ResumeTaskRequest to the A2ARequest Union"; ensure the referenced type names
(ResumeTaskResponse -> ResumeTaskRequest) match the actual union in types.py
(A2ARequest) and any other occurrences of ResumeTaskResponse in this note are
corrected to ResumeTaskRequest.
- Around line 149-163: Update the "Current Test Status (3/4 passing)" section so
it isn't presented as an active/in-progress state: either add a timestamp and
"historical" label (e.g., "Status as of YYYY-MM-DD — historical") or replace it
with the final outcome and cleaned up next steps; edit the "Current Test Status
(3/4 passing)" header and the lines enumerating tests (items 1–4 and the Next
Steps list) so they clearly indicate whether this is historical context or the
resolved/final status.

In `@PAUSE_RESUME_IMPLEMENTATION.md`:
- Around line 163-189: The fenced flow-diagram block starting with "Client" is
missing a language hint and triggers markdownlint MD040; update the opening
fence from ``` to include a language (e.g., ```text or ```mermaid if you convert
to mermaid syntax) so the block is typed; locate the fenced block in
PAUSE_RESUME_IMPLEMENTATION.md (the diagram around the Client -> tasks/pause/...
sequence) and add the chosen language identifier to the opening triple-backtick.

In `@PROJECT_CONTEXT.md`:
- Line 30: Some fenced code blocks use plain ``` without language tags (the
triple-backtick fences) causing MD040; update each offending fence to include an
appropriate language identifier (e.g., ```js, ```json, ```bash) or use ```text
if the language is unknown so the markdown linter passes, ensuring every opening
``` is followed by a language token and matching closing ``` remains unchanged.
- Around line 196-200: The A2A protocol method list is missing the newly shipped
RPCs; update the A2A section to include `tasks/pause` and `tasks/resume`
alongside the existing `tasks/get`, `tasks/cancel`, and `tasks/pushNotify/set`.
Edit the methods list to add `tasks/pause` and `tasks/resume` with a one-line
description each (e.g., "Pause a running task" and "Resume a paused task") and
ensure ordering/formatting matches the surrounding entries so the protocol docs
stay in sync with the implementation.

In `@TEST_CONTRIBUTION_PLAN.md`:
- Around line 85-87: Replace the machine-specific absolute path and command
shown ("cd /Users/konalsmac/MEGA/Bindu" and "uv run pytest tests/unit/ -v
--tb=short") with repository-relative instructions: instruct contributors to
change to the project root (e.g., "cd <repo-root>" or simply run from repo root)
and run pytest with a relative path (e.g., "pytest tests/unit/ -v --tb=short")
or use the documented project test runner; update the TEST_CONTRIBUTION_PLAN.md
entry to remove the /Users/... path and provide the portable command and any
required setup steps.
- Around line 61-67: The example test snippet fails because it references names
that aren't imported; update the snippet so it imports the referenced symbols
(e.g., add from bindu import applications, AgentManifest or adjust imports to
match the library API) and ensure the test assertions refer to those imported
names (e.g., TestBinduApplicationModule.test_module_imports should assert
hasattr(applications, "BinduApplication") only after importing applications).
Also include any missing imports for AgentManifest where it’s used in examples
so the snippet runs as-is.

In `@test_pause_resume.py`:
- Around line 1-3: The requests.post call to BASE_URL (where you send
json=payload with headers={"Content-Type": "application/json"}) lacks a timeout
and can hang; update that call (the POST invocation) to include a timeout
argument (e.g. timeout=30) so the request fails fast on unresponsive servers and
handle the resulting timeout exception as appropriate.
- Around line 19-21: The requests.post call that sends payload to BASE_URL
should include a timeout to avoid hanging; update the call to
requests.post(BASE_URL, json=payload, headers={"Content-Type":
"application/json"}, timeout=10) (or another appropriate seconds value) so the
HTTP client will fail fast if the server is unresponsive, and adjust surrounding
test error handling to expect a requests.exceptions.Timeout/RequestException if
you want to assert timeout behavior.

---

Nitpick comments:
In `@tests/unit/server/handlers/test_task_handlers.py`:
- Around line 152-170: Replace the single-case invalid-state unit tests with
parametrized tests using pytest.mark.parametrize to cover all disallowed task
states; for example, update test_pause_task_not_working_state in the test module
to iterate over a list of non-"working" states (e.g., "completed", "suspended",
"cancelled", "pending") and assert the handler.pause_task returns an error and
scheduler.pause_task is not called for each state, and similarly parametrize the
resume test (the tests around lines 209-227) to iterate over all non-"suspended"
states to assert handler.resume_task returns an error and scheduler.resume_task
is not called; keep using AsyncMock mock_storage.load_task to return the
parametrized mock_task and preserve the existing error_response_creator
behavior.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: f1696cd8-235e-46a8-8b24-02cb2e38673d

📥 Commits

Reviewing files that changed from the base of the PR and between 301624d and 4f0faa5.

📒 Files selected for processing (14)
  • CLAUDE.md
  • CONTRIBUTION_OPPORTUNITIES.md
  • PAUSE_RESUME_DEBUGGING.md
  • PAUSE_RESUME_IMPLEMENTATION.md
  • PROJECT_CONTEXT.md
  • TEST_CONTRIBUTION_PLAN.md
  • bindu/common/protocol/types.py
  • bindu/server/handlers/task_handlers.py
  • bindu/server/task_manager.py
  • bindu/server/workers/base.py
  • bindu/settings.py
  • examples/beginner/slow_echo_agent.py
  • test_pause_resume.py
  • tests/unit/server/handlers/test_task_handlers.py

Comment thread bindu/common/protocol/types.py
Comment thread bindu/server/handlers/task_handlers.py
Comment thread bindu/server/workers/base.py
Comment thread bindu/server/workers/base.py Outdated
Comment on lines +285 to +295
if message:
await self.scheduler.run_task(
TaskSendParams(
task_id=task_id,
context_id=task["context_id"],
message=message,
)
)
logger.info(f"Task {task_id} resumed and re-queued")
else:
logger.warning(f"Task {task_id} has no message to resume with")
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Always re-queue on resume, even if no history message exists.

Line 285 gates scheduler.run_task(...) on message. If history is empty, task is left in "resumed" and never progresses.

Proposed fix
-        # Re-queue task for execution
-        if message:
-            await self.scheduler.run_task(
-                TaskSendParams(
-                    task_id=task_id,
-                    context_id=task["context_id"],
-                    message=message,
-                )
-            )
-            logger.info(f"Task {task_id} resumed and re-queued")
-        else:
-            logger.warning(f"Task {task_id} has no message to resume with")
+        # Re-queue task for execution (message is optional in TaskSendParams)
+        run_params: TaskSendParams = TaskSendParams(
+            task_id=task_id,
+            context_id=task["context_id"],
+        )
+        if message is not None:
+            run_params["message"] = message
+
+        await self.scheduler.run_task(run_params)
+        logger.info(f"Task {task_id} resumed and re-queued")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if message:
await self.scheduler.run_task(
TaskSendParams(
task_id=task_id,
context_id=task["context_id"],
message=message,
)
)
logger.info(f"Task {task_id} resumed and re-queued")
else:
logger.warning(f"Task {task_id} has no message to resume with")
# Re-queue task for execution
await self.scheduler.run_task(
TaskSendParams(
task_id=task_id,
context_id=task["context_id"],
message=message,
)
)
logger.info(f"Task {task_id} resumed and re-queued")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bindu/server/workers/base.py` around lines 285 - 295, The current logic only
calls self.scheduler.run_task(...) when message is truthy, leaving tasks stuck
in "resumed" if history is empty; change the flow so you always call await
self.scheduler.run_task(TaskSendParams(...)) for the resumed task (using
task_id, task["context_id"], and the message variable even if None/empty) so the
task is re-queued regardless of history, and adjust logging to still warn if
message is empty but log that the task was re-queued (use scheduler.run_task,
TaskSendParams, task_id, task["context_id"], and message to locate and update
the code).

Comment thread CLAUDE.md
Comment thread PROJECT_CONTEXT.md
Comment on lines +196 to +200
- `message/send` - Send messages to agent
- `tasks/get` - Get task status
- `tasks/cancel` - Cancel task
- `tasks/pushNotify/set` - Set up webhooks

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Document the new task RPC methods in the A2A section.

This method list still omits tasks/pause and tasks/resume, so the protocol docs are out of sync with the feature shipped in this PR.

Suggested doc update
 ### A2A Protocol
 
 The Agent-to-Agent protocol defines:
 - `message/send` - Send messages to agent
 - `tasks/get` - Get task status
 - `tasks/cancel` - Cancel task
+- `tasks/pause` - Pause a running task (valid from `working`)
+- `tasks/resume` - Resume a suspended task (valid from `suspended`)
 - `tasks/pushNotify/set` - Set up webhooks
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- `message/send` - Send messages to agent
- `tasks/get` - Get task status
- `tasks/cancel` - Cancel task
- `tasks/pushNotify/set` - Set up webhooks
- `message/send` - Send messages to agent
- `tasks/get` - Get task status
- `tasks/cancel` - Cancel task
- `tasks/pause` - Pause a running task (valid from `working`)
- `tasks/resume` - Resume a suspended task (valid from `suspended`)
- `tasks/pushNotify/set` - Set up webhooks
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@PROJECT_CONTEXT.md` around lines 196 - 200, The A2A protocol method list is
missing the newly shipped RPCs; update the A2A section to include `tasks/pause`
and `tasks/resume` alongside the existing `tasks/get`, `tasks/cancel`, and
`tasks/pushNotify/set`. Edit the methods list to add `tasks/pause` and
`tasks/resume` with a one-line description each (e.g., "Pause a running task"
and "Resume a paused task") and ensure ordering/formatting matches the
surrounding entries so the protocol docs stay in sync with the implementation.

Comment thread TEST_CONTRIBUTION_PLAN.md
Comment on lines +61 to +67
class TestBinduApplicationModule:
"""Test module-level functionality."""

def test_module_imports(self):
"""Test that module imports correctly."""
assert hasattr(applications, "BinduApplication")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix the example snippet so it runs as written.

The snippet references names that aren’t imported in the example (applications, AgentManifest), which makes it invalid when copied.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TEST_CONTRIBUTION_PLAN.md` around lines 61 - 67, The example test snippet
fails because it references names that aren't imported; update the snippet so it
imports the referenced symbols (e.g., add from bindu import applications,
AgentManifest or adjust imports to match the library API) and ensure the test
assertions refer to those imported names (e.g.,
TestBinduApplicationModule.test_module_imports should assert
hasattr(applications, "BinduApplication") only after importing applications).
Also include any missing imports for AgentManifest where it’s used in examples
so the snippet runs as-is.

Comment thread TEST_CONTRIBUTION_PLAN.md
Comment on lines +85 to +87
cd /Users/konalsmac/MEGA/Bindu
uv run pytest tests/unit/ -v --tb=short
```
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use repository-relative commands instead of a local absolute path.

The /Users/... path is machine-specific and won’t work for other contributors.

Suggested doc update
-cd /Users/konalsmac/MEGA/Bindu
+cd Bindu
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
cd /Users/konalsmac/MEGA/Bindu
uv run pytest tests/unit/ -v --tb=short
```
cd Bindu
uv run pytest tests/unit/ -v --tb=short
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TEST_CONTRIBUTION_PLAN.md` around lines 85 - 87, Replace the machine-specific
absolute path and command shown ("cd /Users/konalsmac/MEGA/Bindu" and "uv run
pytest tests/unit/ -v --tb=short") with repository-relative instructions:
instruct contributors to change to the project root (e.g., "cd <repo-root>" or
simply run from repo root) and run pytest with a relative path (e.g., "pytest
tests/unit/ -v --tb=short") or use the documented project test runner; update
the TEST_CONTRIBUTION_PLAN.md entry to remove the /Users/... path and provide
the portable command and any required setup steps.

Comment thread test_pause_resume.py
Comment on lines +1 to +3
#!/usr/bin/env python3
"""Test script for pause/resume functionality."""

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether pytest discovery is likely to include this file.
fd -H 'pytest.ini|pyproject.toml|setup.cfg|tox.ini'
echo "---- pytest discovery config ----"
rg -n '(^\s*\[tool.pytest.ini_options\])|(^\s*python_files\s*=)|(^\s*testpaths\s*=)|(^\s*addopts\s*=)' pyproject.toml pytest.ini setup.cfg tox.ini 2>/dev/null
echo "---- candidate test files at repo root ----"
fd -d 1 '^test_.*\.py$'

Repository: GetBindu/Bindu

Length of output: 275


🏁 Script executed:

cat test_pause_resume.py

Repository: GetBindu/Bindu

Length of output: 9569


Add timeout parameter to requests.post() call to prevent indefinite hangs.

The requests.post() call at lines 19-22 lacks a timeout parameter. This can cause the script to hang indefinitely if the server becomes unresponsive. Add a timeout:

response = requests.post(
    BASE_URL, json=payload, headers={"Content-Type": "application/json"}, timeout=30
)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test_pause_resume.py` around lines 1 - 3, The requests.post call to BASE_URL
(where you send json=payload with headers={"Content-Type": "application/json"})
lacks a timeout and can hang; update that call (the POST invocation) to include
a timeout argument (e.g. timeout=30) so the request fails fast on unresponsive
servers and handle the resulting timeout exception as appropriate.

Comment thread test_pause_resume.py
Comment on lines +19 to +21
response = requests.post(
BASE_URL, json=payload, headers={"Content-Type": "application/json"}
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

fd -type f -name "test_pause_resume.py"

Repository: GetBindu/Bindu

Length of output: 228


🏁 Script executed:

fd test_pause_resume.py

Repository: GetBindu/Bindu

Length of output: 78


🏁 Script executed:

cat -n test_pause_resume.py

Repository: GetBindu/Bindu

Length of output: 11788


🏁 Script executed:

rg "requests\." --type py -A 2 -B 2 | head -60

Repository: GetBindu/Bindu

Length of output: 2828


🏁 Script executed:

rg "raise_for_status" --type py

Repository: GetBindu/Bindu

Length of output: 127


🏁 Script executed:

rg "raise_for_status" --type py -B 5 -A 5

Repository: GetBindu/Bindu

Length of output: 827


Add a timeout to requests.post to prevent indefinite blocking.

Without a timeout, this can block indefinitely if the server is unavailable or becomes unresponsive. Note that raise_for_status() is not appropriate here since error handling is done via JSON-RPC pattern (checking the "error" field in the response body), not HTTP status codes.

Suggested fix
-    response = requests.post(
-        BASE_URL, json=payload, headers={"Content-Type": "application/json"}
-    )
+    response = requests.post(
+        BASE_URL,
+        json=payload,
+        headers={"Content-Type": "application/json"},
+        timeout=10,
+    )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
response = requests.post(
BASE_URL, json=payload, headers={"Content-Type": "application/json"}
)
response = requests.post(
BASE_URL,
json=payload,
headers={"Content-Type": "application/json"},
timeout=10,
)
🧰 Tools
🪛 Ruff (0.15.10)

[error] 19-19: Probable use of requests call without timeout

(S113)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test_pause_resume.py` around lines 19 - 21, The requests.post call that sends
payload to BASE_URL should include a timeout to avoid hanging; update the call
to requests.post(BASE_URL, json=payload, headers={"Content-Type":
"application/json"}, timeout=10) (or another appropriate seconds value) so the
HTTP client will fail fast if the server is unresponsive, and adjust surrounding
test error handling to expect a requests.exceptions.Timeout/RequestException if
you want to assert timeout behavior.

This commit addresses the review feedback from CodeRabbit and increases
test coverage to meet the project's threshold.

## CodeRabbit Fixes

### 1. Error Code Collisions (Critical)
- Moved TaskNotPausableError from -32007 to -32014
- Moved TaskNotResumableError from -32008 to -32015
- These codes were colliding with AuthenticatedExtendedCardNotConfiguredError
  and TaskImmutableError respectively

### 2. Assert Replacement (Major)
- Replaced assert statements with proper error handling in:
  - cancel_task: Returns TaskNotFoundError if task deleted between operations
  - pause_task: Returns TaskNotFoundError if task deleted between operations
  - resume_task: Returns TaskNotFoundError if task deleted between operations
- Previously, AssertionError would return HTTP 500 instead of typed JSON-RPC error

### 3. Metadata Persistence (Major)
- _handle_pause: Now saves paused_at timestamp and pause_checkpoint
- _handle_resume: Now saves resumed_at timestamp
- Metadata is preserved and merged with existing task metadata

### 4. Resume Always Re-queues (Major)
- Removed conditional check that skipped re-queue when message was None
- Resume now always calls scheduler.run_task to ensure task progresses

## Test Coverage Improvements

Added new tests to increase coverage from 60.38% to 61.03%:

- tests/unit/server/workers/test_base_worker.py (6 tests)
  * test_pause_task_success
  * test_pause_task_not_found
  * test_resume_task_success
  * test_resume_task_not_found
  * test_resume_task_no_history
  * test_resume_task_with_metadata

- tests/unit/server/handlers/test_task_handlers.py (4 tests)
  * test_cancel_task_success
  * test_cancel_task_deleted_between
  * test_pause_task_deleted_between
  * test_resume_task_deleted_between

- tests/unit/utils/test_capabilities.py (2 tests)
  * test_add_extension_with_none_capabilities
  * test_add_extension_with_non_dict_capabilities

- tests/unit/utils/test_logging.py (2 tests)
  * test_set_log_level
  * test_pre_configured_logger

- tests/unit/utils/test_tracing.py (4 tests)
  * test_get_trace_context_with_exception
  * test_get_trace_context_with_invalid_span
  * test_get_trace_context_with_valid_span
  * test_get_trace_context_with_none_span

- tests/unit/utils/test_server_runner.py (4 tests)
  * test_setup_signal_handlers_in_main_thread
  * test_setup_signal_handlers_not_in_main_thread
  * test_run_server
  * test_run_server_with_display_info

## Files Modified
- bindu/common/protocol/types.py (error codes)
- bindu/server/handlers/task_handlers.py (assert fixes)
- bindu/server/workers/base.py (metadata + re-queue)

## Test Results
- 804 tests passing
- Coverage: 61.03% (up from 60.38%)

Co-Authored-By: Claude Opus 4.6 <noreply@openclaude.dev>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (5)
tests/unit/utils/test_server_runner.py (2)

1-9: Scope check: this test file is unrelated to the pause/resume feature.

PR #455 targets task pause/resume (issue #383), but this module tests bindu.utils.server_runner signal handling and uvicorn startup. If it was added only to lift overall coverage numbers, consider splitting it into a separate PR to keep this one focused and easier to review/revert. If it is intentionally bundled (e.g., touched indirectly by the feature), ignore this note.

Based on learnings: "Every changed line should trace directly to the user's request. Don't refactor things that aren't broken."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/utils/test_server_runner.py` around lines 1 - 9, The added test
file tests signal handling and uvicorn startup in bindu.utils.server_runner
(class TestServerRunner) but is unrelated to the pause/resume feature for PR
`#455` (issue `#383`); either remove this test from the current PR or move it into a
separate PR/branch so every changed line maps to the feature—if the file was
intentionally modified because of an indirect dependency, leave a clear comment
in the commit/PR explaining that linkage (referencing bindu.utils.server_runner
and TestServerRunner) so reviewers know why it belongs here.

37-69: Assertions are weak — consider verifying uvicorn.run args.

test_run_server and test_run_server_with_display_info only assert that uvicorn.run / logger.info were called, not how. A small tightening would catch regressions in argument wiring (host/port/app) without extra setup:

♻️ Proposed tweak
-                run_server(mock_app, "localhost", 8000, display_info=False)
-
-                # Verify uvicorn.run was called
-                mock_uvicorn.run.assert_called_once()
+                run_server(mock_app, "localhost", 8000, display_info=False)
+
+                mock_uvicorn.run.assert_called_once_with(
+                    mock_app, host="localhost", port=8000
+                )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/utils/test_server_runner.py` around lines 37 - 69, Update the two
tests to assert that uvicorn.run is invoked with the expected app, host and port
rather than only called: in test_run_server, after run_server(mock_app,
"localhost", 8000, ...), assert
mock_uvicorn.run.assert_called_once_with(mock_app, host="localhost", port=8000,
reload=False) (or match the actual keyword names used in run_server); in
test_run_server_with_display_info also assert uvicorn.run was called with the
same app/host/port and additionally assert logger.info was called with the
startup message substring you expect (e.g., contains the host/port) so the test
verifies both argument wiring and the display_info logging behavior.
tests/unit/utils/test_capabilities.py (1)

77-92: Test only covers a defensive branch that can't be reached in practice.

The inline comment on Line 84 ("this case won't happen in practice since TypedDict IS a dict") acknowledges the test exists solely to exercise the isinstance(capabilities, dict) guard. That's fine for coverage, but consider either removing the self-deprecating comment or tightening the test to assert the FakeCapabilities input is discarded (e.g., that no attributes from it leak into the result). As written, the test narrative reads as "we know this is pointless" which is a smell.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/utils/test_capabilities.py` around lines 77 - 92, The test's
inline comment downplays the case and doesn't assert that a non-dict input
doesn't leak attributes; update test_add_extension_with_non_dict_capabilities to
either remove the self-deprecating comment or (preferred) tighten the assertion:
create FakeCapabilities with a sentinel attribute, call
add_extension_to_capabilities(FakeCapabilities(), extension) and assert the
returned dict only contains "extensions" (and the extension) and does not
contain the sentinel attribute or any other attributes from FakeCapabilities so
it's clear the isinstance(capabilities, dict) guard discards non-dict inputs;
reference add_extension_to_capabilities and the FakeCapabilities class in the
test.
bindu/server/handlers/task_handlers.py (1)

137-143: Consider a more specific error message for the post-scheduler race.

If the task disappears between scheduler.pause_task()/resume_task() and the reload, returning a generic "Task not found" masks the fact that the pause/resume was actually dispatched. A message like "Task was deleted during pause" / "Task was deleted during resume" would give clients a clearer signal to distinguish this race from the up-front not-found case at Lines 116-119 / 155-158. Non-blocking.

Also applies to: 176-182

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bindu/server/handlers/task_handlers.py` around lines 137 - 143, Update the
error message returned when the task is missing after the scheduler operation to
indicate the post-scheduler race: in the PauseTask handler (use symbol
PauseTaskResponse and the block checking "if task is None" after
scheduler.pause_task()) change the generic "Task not found" to a message like
"Task was deleted during pause"; do the analogous change in the ResumeTask
handler (the resume block returning ResumeTaskResponse when task is None) to
"Task was deleted during resume" and also update the similar check around lines
176-182 referenced in the review so all post-scheduler missing-task responses
clearly indicate the task was deleted during the dispatched action.
tests/unit/server/workers/test_base_worker.py (1)

12-19: Bypassing __init__ via __new__ is brittle.

ManifestWorker.__new__(ManifestWorker) skips the real constructor and only the attributes the test explicitly sets exist on the instance. If ManifestWorker.__init__ ever starts initializing additional state that _handle_pause/_handle_resume touch (e.g., locks, tracers, task registries), these tests will silently start passing against an unrealistic object. Consider either using the real constructor with mocked dependencies or asserting only on the mocks used. Non-blocking for this PR.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/server/workers/test_base_worker.py` around lines 12 - 19, The test
fixture uses ManifestWorker.__new__ to bypass ManifestWorker.__init__, which
creates a brittle test; change the mock_worker fixture to construct a real
ManifestWorker via its constructor (call ManifestWorker(...) or
ManifestWorker(...) with mocked dependencies) or otherwise call its __init__ on
the instance, and inject AsyncMock instances for storage and scheduler rather
than manually setting attributes; ensure the fixture still configures
_task_handlers as needed and that tests invoking _handle_pause/_handle_resume
operate against a fully-initialized ManifestWorker instance so future init-added
state (locks, tracers, registries) exists.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@bindu/common/protocol/types.py`:
- Around line 1375-1390: The file's error-range comments conflict: the new "Task
errors (-32014 to -32019)" header overlaps the existing "Authentication errors
(-32009 to -32015)" header; update the authentication range comment to
"Authentication errors (-32009 to -32013)" so it no longer claims -32014/-32015
used by TaskNotPausableError and TaskNotResumableError, ensuring the banner
above the authentication-related JSONRPCError definitions matches the actual
assigned codes.

---

Nitpick comments:
In `@bindu/server/handlers/task_handlers.py`:
- Around line 137-143: Update the error message returned when the task is
missing after the scheduler operation to indicate the post-scheduler race: in
the PauseTask handler (use symbol PauseTaskResponse and the block checking "if
task is None" after scheduler.pause_task()) change the generic "Task not found"
to a message like "Task was deleted during pause"; do the analogous change in
the ResumeTask handler (the resume block returning ResumeTaskResponse when task
is None) to "Task was deleted during resume" and also update the similar check
around lines 176-182 referenced in the review so all post-scheduler missing-task
responses clearly indicate the task was deleted during the dispatched action.

In `@tests/unit/server/workers/test_base_worker.py`:
- Around line 12-19: The test fixture uses ManifestWorker.__new__ to bypass
ManifestWorker.__init__, which creates a brittle test; change the mock_worker
fixture to construct a real ManifestWorker via its constructor (call
ManifestWorker(...) or ManifestWorker(...) with mocked dependencies) or
otherwise call its __init__ on the instance, and inject AsyncMock instances for
storage and scheduler rather than manually setting attributes; ensure the
fixture still configures _task_handlers as needed and that tests invoking
_handle_pause/_handle_resume operate against a fully-initialized ManifestWorker
instance so future init-added state (locks, tracers, registries) exists.

In `@tests/unit/utils/test_capabilities.py`:
- Around line 77-92: The test's inline comment downplays the case and doesn't
assert that a non-dict input doesn't leak attributes; update
test_add_extension_with_non_dict_capabilities to either remove the
self-deprecating comment or (preferred) tighten the assertion: create
FakeCapabilities with a sentinel attribute, call
add_extension_to_capabilities(FakeCapabilities(), extension) and assert the
returned dict only contains "extensions" (and the extension) and does not
contain the sentinel attribute or any other attributes from FakeCapabilities so
it's clear the isinstance(capabilities, dict) guard discards non-dict inputs;
reference add_extension_to_capabilities and the FakeCapabilities class in the
test.

In `@tests/unit/utils/test_server_runner.py`:
- Around line 1-9: The added test file tests signal handling and uvicorn startup
in bindu.utils.server_runner (class TestServerRunner) but is unrelated to the
pause/resume feature for PR `#455` (issue `#383`); either remove this test from the
current PR or move it into a separate PR/branch so every changed line maps to
the feature—if the file was intentionally modified because of an indirect
dependency, leave a clear comment in the commit/PR explaining that linkage
(referencing bindu.utils.server_runner and TestServerRunner) so reviewers know
why it belongs here.
- Around line 37-69: Update the two tests to assert that uvicorn.run is invoked
with the expected app, host and port rather than only called: in
test_run_server, after run_server(mock_app, "localhost", 8000, ...), assert
mock_uvicorn.run.assert_called_once_with(mock_app, host="localhost", port=8000,
reload=False) (or match the actual keyword names used in run_server); in
test_run_server_with_display_info also assert uvicorn.run was called with the
same app/host/port and additionally assert logger.info was called with the
startup message substring you expect (e.g., contains the host/port) so the test
verifies both argument wiring and the display_info logging behavior.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 89f3f826-6f70-40f4-b766-31519f53a6db

📥 Commits

Reviewing files that changed from the base of the PR and between 4f0faa5 and 424731b.

📒 Files selected for processing (9)
  • bindu/common/protocol/types.py
  • bindu/server/handlers/task_handlers.py
  • bindu/server/workers/base.py
  • tests/unit/server/handlers/test_task_handlers.py
  • tests/unit/server/workers/test_base_worker.py
  • tests/unit/utils/test_capabilities.py
  • tests/unit/utils/test_logging.py
  • tests/unit/utils/test_server_runner.py
  • tests/unit/utils/test_tracing.py
✅ Files skipped from review due to trivial changes (3)
  • tests/unit/utils/test_logging.py
  • bindu/server/workers/base.py
  • tests/unit/server/handlers/test_task_handlers.py

Comment on lines +1375 to +1390
# Task errors (-32014 to -32019)
# Bindu-specific task management extensions
TaskNotPausableError = JSONRPCError[
Literal[-32014],
Literal[
"This task cannot be paused in its current state. Tasks can only be paused while in 'working' state. "
"See task lifecycle: /docs/tasks"
],
]
TaskNotResumableError = JSONRPCError[
Literal[-32015],
Literal[
"This task cannot be resumed in its current state. Tasks can only be resumed while in 'suspended' state. "
"See task lifecycle: /docs/tasks"
],
]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Error-code range comments now overlap.

The new comment declares "Task errors (-32014 to -32019)" for TaskNotPausableError/TaskNotResumableError, but Line 1435 below still declares "Authentication errors (-32009 to -32015)", which claims -32014/-32015 for auth. The actual codes don't collide (prior collision at -32007/-32008 was correctly fixed to -32014/-32015), but the two range banners are now inconsistent and will mislead anyone adding future codes. Tighten one of the ranges — e.g., narrow the auth banner to -32009 to -32013.

Proposed fix
-# Authentication errors (-32009 to -32015)
+# Authentication errors (-32009 to -32013)
 # Bindu-specific authentication extensions
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bindu/common/protocol/types.py` around lines 1375 - 1390, The file's
error-range comments conflict: the new "Task errors (-32014 to -32019)" header
overlaps the existing "Authentication errors (-32009 to -32015)" header; update
the authentication range comment to "Authentication errors (-32009 to -32013)"
so it no longer claims -32014/-32015 used by TaskNotPausableError and
TaskNotResumableError, ensuring the banner above the authentication-related
JSONRPCError definitions matches the actual assigned codes.

Accept main branch's full CLAUDE.md to resolve merge conflict.

Co-Authored-By: Claude Opus 4.6 <noreply@openclaude.dev>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
CLAUDE.md (1)

59-81: ⚠️ Potential issue | 🟡 Minor

Add language specifiers to fenced code blocks.

Two fenced code blocks are missing language specifiers, which causes markdownlint warnings:

  • Line 59: ASCII architecture diagram
  • Line 123: File structure tree
📝 Proposed fix for markdownlint violations
 ## Architecture Overview
 
-```
+```text
 ┌─────────────────────────────────────────────────────────────┐
 │                      Bindu Core (Python)                     │
 ## File Structure
 
-```
+```text
 bindu/
 ├── bindu/                      # Python core

Based on learnings, documentation should follow project conventions and pass linting.

Also applies to: 123-161

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CLAUDE.md` around lines 59 - 81, Add the missing language specifier "text" to
the fenced code blocks in CLAUDE.md: update the ASCII architecture diagram block
(the large box diagram labeled "Bindu Core (Python)" / "Language SDKs (Thin
Wrappers)") and the file structure tree block (the bindu/ tree listing) so each
opening ``` becomes ```text to satisfy markdownlint and project conventions.
🧹 Nitpick comments (1)
CLAUDE.md (1)

1-365: Consider separating comprehensive documentation from feature implementation.

This PR implements the pause/resume feature (closes #383) but also adds a comprehensive 365-line AI assistant documentation file. These appear to be two separate concerns that could be split into:

  1. Feature PR: pause/resume implementation with feature-specific docs
  2. Documentation PR: CLAUDE.md infrastructure for AI-assisted development

Based on learnings, PRs should stay focused on one feature or fix per PR. However, if CLAUDE.md was specifically requested as part of the pause/resume work or serves as the primary documentation for this feature, this concern can be disregarded.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CLAUDE.md` around lines 1 - 365, This PR mixes the pause/resume feature
(closes `#383`) with a large unrelated documentation addition (CLAUDE.md); split
them by removing CLAUDE.md from this branch/commit and creating a separate PR
that contains only CLAUDE.md and its docs changes, leaving this PR focused on
the pause/resume implementation and its feature-specific docs; update the
current PR description to reference only the pause/resume work and ensure
commits/changes mention "pause/resume" and `#383`, while the new PR’s description
notes it is purely documentation (CLAUDE.md) so reviewers can review each
concern independently.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CLAUDE.md`:
- Around line 228-234: Update the ManifestWorker documentation to describe the
new pause/resume capability: add brief entries for the two new task states
`suspended` (task paused) and `resumed` (task resumed), document the new API
operations `tasks/pause` and `tasks/resume`, and describe the key state
transitions (e.g., `working` → `suspended` via pause, `suspended` → `resumed`
via resume). Place these additions in the ManifestWorker section near the state
machine description and reference the ManifestWorker state names (`suspended`,
`resumed`) and the endpoint names (`tasks/pause`, `tasks/resume`) so readers can
map doc text to the implemented lifecycle behavior.

---

Duplicate comments:
In `@CLAUDE.md`:
- Around line 59-81: Add the missing language specifier "text" to the fenced
code blocks in CLAUDE.md: update the ASCII architecture diagram block (the large
box diagram labeled "Bindu Core (Python)" / "Language SDKs (Thin Wrappers)") and
the file structure tree block (the bindu/ tree listing) so each opening ```
becomes ```text to satisfy markdownlint and project conventions.

---

Nitpick comments:
In `@CLAUDE.md`:
- Around line 1-365: This PR mixes the pause/resume feature (closes `#383`) with a
large unrelated documentation addition (CLAUDE.md); split them by removing
CLAUDE.md from this branch/commit and creating a separate PR that contains only
CLAUDE.md and its docs changes, leaving this PR focused on the pause/resume
implementation and its feature-specific docs; update the current PR description
to reference only the pause/resume work and ensure commits/changes mention
"pause/resume" and `#383`, while the new PR’s description notes it is purely
documentation (CLAUDE.md) so reviewers can review each concern independently.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 145fb460-96ac-4c67-88bc-215b0e3dd72c

📥 Commits

Reviewing files that changed from the base of the PR and between 424731b and ea64b0f.

📒 Files selected for processing (1)
  • CLAUDE.md

Comment thread CLAUDE.md
Comment on lines +228 to +234
### Why ManifestWorker?

- **Separation of concerns**: Task lifecycle separate from handler logic
- **State machine**: Handles all task states (pending, running, completed, failed, input-required, etc.)
- **Uniform interface**: `manifest.run(messages)` works for Python and gRPC handlers identically
- **Error handling**: Catches exceptions, creates artifacts, updates storage

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Document the new pause/resume capability.

The ManifestWorker section describes the task state machine but doesn't mention the pause/resume functionality added in this PR. Since this file is being modified in the same PR that implements pause/resume, it should document the new suspended and resumed states and the corresponding task lifecycle operations.

Consider adding a brief mention of:

  • New states: suspended (task paused), resumed (task resumed)
  • New operations: tasks/pause and tasks/resume endpoints
  • State transitions: workingsuspended (via pause), suspendedresumed (via resume)

Based on learnings, documentation should be updated in the same PR as code changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CLAUDE.md` around lines 228 - 234, Update the ManifestWorker documentation to
describe the new pause/resume capability: add brief entries for the two new task
states `suspended` (task paused) and `resumed` (task resumed), document the new
API operations `tasks/pause` and `tasks/resume`, and describe the key state
transitions (e.g., `working` → `suspended` via pause, `suspended` → `resumed`
via resume). Place these additions in the ManifestWorker section near the state
machine description and reference the ManifestWorker state names (`suspended`,
`resumed`) and the endpoint names (`tasks/pause`, `tasks/resume`) so readers can
map doc text to the implemented lifecycle behavior.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@bindu/server/task_manager.py`:
- Around line 282-283: There is a stray duplicate declaration of the async
function task_feedback (async def task_feedback(self, request:
TaskFeedbackRequest) -> TaskFeedbackResponse) immediately before the real
implementation; remove the earlier duplicate declaration so only the implemented
async def task_feedback remains, keeping the signature that uses
TaskFeedbackRequest and returns TaskFeedbackResponse and ensure the file
compiles and tests/linters pass.
- Around line 274-280: Update pause_task and resume_task to accept a caller_did
parameter and enforce ownership like other endpoints: in task_manager.py change
async def pause_task(self, request: PauseTaskRequest) -> PauseTaskResponse and
resume_task signatures to accept caller_did and forward it to
self._task_handlers.pause_task(...) and resume_task(...). In task_handlers.py
add caller_did to the pause_task and resume_task method signatures, then perform
the same ownership check used in cancel_task (use get_task_owner(task_id) and
compare to caller_did, returning an error/permission denial when they differ)
before performing the pause/resume operation so the handlers both accept
caller_did and validate ownership.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: b7cfcd73-85f9-42f2-82c5-d61d6b8f3f0f

📥 Commits

Reviewing files that changed from the base of the PR and between ea64b0f and e9f0e46.

📒 Files selected for processing (5)
  • CLAUDE.md
  • bindu/common/protocol/types.py
  • bindu/server/handlers/task_handlers.py
  • bindu/server/task_manager.py
  • tests/unit/server/handlers/test_task_handlers.py
✅ Files skipped from review due to trivial changes (1)
  • CLAUDE.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/unit/server/handlers/test_task_handlers.py
  • bindu/server/handlers/task_handlers.py

Comment on lines +274 to +280
async def pause_task(self, request: PauseTaskRequest) -> PauseTaskResponse:
"""Pause a running task."""
return await self._task_handlers.pause_task(request)

async def resume_task(self, request: ResumeTaskRequest) -> ResumeTaskResponse:
"""Resume a paused task."""
return await self._task_handlers.resume_task(request)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Inspect pause/resume signatures and routing to confirm caller_did propagation expectations.

rg -n -C4 --type=py 'async def (pause_task|resume_task)\b|tasks/(pause|resume)|caller_did=caller_did'

Repository: GetBindu/Bindu

Length of output: 18611


🏁 Script executed:

#!/bin/bash
# Examine pause_task and resume_task implementations in task_handlers.py
# to understand if they need caller_did for authorization

# Get the full implementations
sed -n '133,170p' bindu/server/handlers/task_handlers.py
sed -n '172,210p' bindu/server/handlers/task_handlers.py

Repository: GetBindu/Bindu

Length of output: 3011


🏁 Script executed:

#!/bin/bash
# Get cancel_task implementation to see if caller_did is used for authorization
sed -n '110,132p' bindu/server/handlers/task_handlers.py

Repository: GetBindu/Bindu

Length of output: 948


🏁 Script executed:

#!/bin/bash
# Get full cancel_task implementation
sed -n '95,132p' bindu/server/handlers/task_handlers.py

Repository: GetBindu/Bindu

Length of output: 1561


🏁 Script executed:

#!/bin/bash
# Verify that get_task_owner pattern is consistent
# and check if there are other handlers that also lack ownership check

rg -n 'get_task_owner' bindu/server/handlers/task_handlers.py

Repository: GetBindu/Bindu

Length of output: 245


🏁 Script executed:

#!/bin/bash
# Check what methods perform ownership checks
rg -B 5 'get_task_owner' bindu/server/handlers/task_handlers.py | grep -E 'async def|get_task_owner'

Repository: GetBindu/Bindu

Length of output: 234


🏁 Script executed:

#!/bin/bash
# Get broader context around get_task_owner calls
rg -B 20 'get_task_owner' bindu/server/handlers/task_handlers.py | grep -E '(async def|get_task_owner)' | head -20

Repository: GetBindu/Bindu

Length of output: 314


🏁 Script executed:

#!/bin/bash
# Verify get_task method signature and implementation
sed -n '66,94p' bindu/server/handlers/task_handlers.py

Repository: GetBindu/Bindu

Length of output: 1136


🏁 Script executed:

#!/bin/bash
# Check task_feedback to confirm pattern
sed -n '233,265p' bindu/server/handlers/task_handlers.py

Repository: GetBindu/Bindu

Length of output: 1254


Add caller_did parameter and ownership check to pause/resume handlers.

The pause_task and resume_task methods in both task_manager.py and task_handlers.py are missing caller_did propagation. This causes two issues:

  1. Runtime failure: a2a_protocol.py (line 203) calls all handlers with caller_did=caller_did, but these endpoints don't accept that parameter, resulting in TypeError.
  2. Security bypass: Unlike get_task, cancel_task, and task_feedback, these methods lack ownership validation. Without the ownership check, callers can pause/resume tasks they don't own, bypassing cross-tenant access control.

Both files require updates:

task_manager.py (lines 274–280):

-    async def pause_task(self, request: PauseTaskRequest) -> PauseTaskResponse:
+    async def pause_task(
+        self,
+        request: PauseTaskRequest,
+        caller_did: str | None = None,
+    ) -> PauseTaskResponse:
         """Pause a running task."""
-        return await self._task_handlers.pause_task(request)
+        return await self._task_handlers.pause_task(request, caller_did=caller_did)
 
-    async def resume_task(self, request: ResumeTaskRequest) -> ResumeTaskResponse:
+    async def resume_task(
+        self,
+        request: ResumeTaskRequest,
+        caller_did: str | None = None,
+    ) -> ResumeTaskResponse:
         """Resume a paused task."""
-        return await self._task_handlers.resume_task(request)
+        return await self._task_handlers.resume_task(request, caller_did=caller_did)

task_handlers.py (lines 135, 174): Add the same caller_did parameter and add ownership validation matching the pattern in cancel_task (check get_task_owner and compare against caller_did).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bindu/server/task_manager.py` around lines 274 - 280, Update pause_task and
resume_task to accept a caller_did parameter and enforce ownership like other
endpoints: in task_manager.py change async def pause_task(self, request:
PauseTaskRequest) -> PauseTaskResponse and resume_task signatures to accept
caller_did and forward it to self._task_handlers.pause_task(...) and
resume_task(...). In task_handlers.py add caller_did to the pause_task and
resume_task method signatures, then perform the same ownership check used in
cancel_task (use get_task_owner(task_id) and compare to caller_did, returning an
error/permission denial when they differ) before performing the pause/resume
operation so the handlers both accept caller_did and validate ownership.

Comment thread bindu/server/task_manager.py Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (2)
bindu/server/task_manager.py (2)

282-284: ⚠️ Potential issue | 🔴 Critical

task_feedback is broken: indentation SyntaxError and removed caller_did breaks ownership auth.

Two defects introduced here:

  1. Syntax error (CI blocker). The docstring and return statements are dedented to 4 spaces, so Python parses the method body as empty. Ruff already flags this: Expected an indented block after function definition. Module import will fail, breaking the entire server.

  2. Auth regression. TaskHandlers.task_feedback (see bindu/server/handlers/task_handlers.py:235-276) still accepts caller_did and enforces owner != caller_did → TaskNotFoundError. By removing caller_did from this wrapper, every call now passes caller_did=None implicitly — so unless the task's owner_did is also None, all feedback submissions will be rejected. Also, a2a_protocol.py:203 invokes this handler with caller_did=caller_did, which will raise TypeError before the body even runs.

🔧 Proposed fix
-    async def task_feedback(self, request: TaskFeedbackRequest) -> TaskFeedbackResponse:
-    """Submit feedback for a completed task."""
-    return await self._task_handlers.task_feedback(request)
+    async def task_feedback(
+        self,
+        request: TaskFeedbackRequest,
+        caller_did: str | None = None,
+    ) -> TaskFeedbackResponse:
+        """Submit feedback for a completed task."""
+        return await self._task_handlers.task_feedback(
+            request, caller_did=caller_did
+        )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bindu/server/task_manager.py` around lines 282 - 284, The task_feedback
wrapper has two problems: its docstring/return are mis-indented causing a
SyntaxError, and it no longer accepts/passes caller_did causing auth failures;
fix by properly indenting the method body and signature to include caller_did
(e.g., async def task_feedback(self, request: TaskFeedbackRequest, caller_did:
str | None) -> TaskFeedbackResponse:) and call through to
self._task_handlers.task_feedback(request, caller_did), ensuring the docstring
is inside the function body and the wrapper forwards caller_did to
TaskHandlers.task_feedback.

274-280: ⚠️ Potential issue | 🔴 Critical

Add caller_did parameter to pause_task/resume_task — causes runtime TypeError and bypasses ownership checks.

bindu/server/endpoints/a2a_protocol.py:203 dispatches every handler via await handler(a2a_request, caller_did=caller_did). These two methods don't accept that keyword, so any tasks/pause or tasks/resume call will raise TypeError: pause_task() got an unexpected keyword argument 'caller_did'.

Additionally, unlike get_task, cancel_task, and task_feedback, neither TaskManager.pause_task/resume_task nor TaskHandlers.pause_task/resume_task (see bindu/server/handlers/task_handlers.py:135-212) perform an ownership check against get_task_owner(task_id). This permits cross-tenant pause/resume of tasks the caller does not own.

🔧 Proposed fix (task_manager.py)
-    async def pause_task(self, request: PauseTaskRequest) -> PauseTaskResponse:
-        """Pause a running task."""
-        return await self._task_handlers.pause_task(request)
-
-    async def resume_task(self, request: ResumeTaskRequest) -> ResumeTaskResponse:
-        """Resume a paused task."""
-        return await self._task_handlers.resume_task(request)
+    async def pause_task(
+        self,
+        request: PauseTaskRequest,
+        caller_did: str | None = None,
+    ) -> PauseTaskResponse:
+        """Pause a running task."""
+        return await self._task_handlers.pause_task(request, caller_did=caller_did)
+
+    async def resume_task(
+        self,
+        request: ResumeTaskRequest,
+        caller_did: str | None = None,
+    ) -> ResumeTaskResponse:
+        """Resume a paused task."""
+        return await self._task_handlers.resume_task(request, caller_did=caller_did)

TaskHandlers.pause_task/resume_task must be updated in the same pass to accept caller_did and enforce the same owner != caller_did → error check used by cancel_task/task_feedback.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bindu/server/task_manager.py` around lines 274 - 280, The
pause_task/resume_task methods in TaskManager and their counterparts in
TaskHandlers must accept the caller_did keyword and enforce ownership checks:
update TaskManager.pause_task and TaskManager.resume_task signatures to accept
caller_did and forward it to TaskHandlers; update TaskHandlers.pause_task and
TaskHandlers.resume_task to accept caller_did, call get_task_owner(task_id) and
return the same ownership-error behavior used by cancel_task and task_feedback
when owner != caller_did, otherwise proceed with pausing/resuming; this makes
them compatible with the dispatcher that invokes handler(a2a_request,
caller_did=caller_did) and prevents cross-tenant pause/resume.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@bindu/server/task_manager.py`:
- Around line 282-284: The task_feedback wrapper has two problems: its
docstring/return are mis-indented causing a SyntaxError, and it no longer
accepts/passes caller_did causing auth failures; fix by properly indenting the
method body and signature to include caller_did (e.g., async def
task_feedback(self, request: TaskFeedbackRequest, caller_did: str | None) ->
TaskFeedbackResponse:) and call through to
self._task_handlers.task_feedback(request, caller_did), ensuring the docstring
is inside the function body and the wrapper forwards caller_did to
TaskHandlers.task_feedback.
- Around line 274-280: The pause_task/resume_task methods in TaskManager and
their counterparts in TaskHandlers must accept the caller_did keyword and
enforce ownership checks: update TaskManager.pause_task and
TaskManager.resume_task signatures to accept caller_did and forward it to
TaskHandlers; update TaskHandlers.pause_task and TaskHandlers.resume_task to
accept caller_did, call get_task_owner(task_id) and return the same
ownership-error behavior used by cancel_task and task_feedback when owner !=
caller_did, otherwise proceed with pausing/resuming; this makes them compatible
with the dispatcher that invokes handler(a2a_request, caller_did=caller_did) and
prevents cross-tenant pause/resume.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7f9ff454-94d7-432f-88ab-2d2d5347b1d4

📥 Commits

Reviewing files that changed from the base of the PR and between e9f0e46 and fd7ee3d.

📒 Files selected for processing (1)
  • bindu/server/task_manager.py

pkonal23 and others added 2 commits April 20, 2026 13:35
- Fix indentation in task_feedback method in TaskManager
- Add caller_did parameter for cross-tenant ownership checks
- Fix test_cancel_task_success to include required mocks

Co-Authored-By: Claude Opus 4.6 <noreply@openclaude.dev>
- Replace deprecated utcnow() with now(timezone.utc)
- Fix TaskSendParams message type with type ignore comment
- Fix base64.b64decode type by casting to str
- Fix FakeCapabilities test to inherit from dict

Co-Authored-By: Claude Opus 4.6 <noreply@openclaude.dev>
@pkonal23 pkonal23 closed this Apr 20, 2026
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.

[Bug]: Unimplemented Task Pause/Resume Functionality in Base Worker

1 participant