github →

New request

#2833: Kanban worker marks task done after approval-gated command fails instead of moving to blocked

We'll provision a sandbox, run an agent against the issue, and open a draft PR. You can pull the branch and iterate from there.

Issue
bugupstream-changeinvestigationtasks

Kanban worker marks task done after approval-gated command fails instead of moving to blocked

Bug Description

A Hermes Kanban worker running under a secondary profile was assigned a task that required deleting a file. The worker attempted or needed to run a destructive command, but the command required approval because approvals.mode was set to manual.

Because the Kanban worker was spawned as a detached/non-interactive process, there was no visible approval prompt and no apparent way for the user to approve the command.

Instead of moving the task to the blocked lane or failing the task, the Kanban task was marked done / completed even though the requested filesystem change did not happen.

This creates a false-positive completion state: the board says the worker completed the task, but the side effect never occurred.

Steps to Reproduce

  1. Configure two Hermes profiles, for example:

    • default profile
    • secondary worker profile
  2. Ensure both profiles use manual command approvals:

    approvals:
      mode: manual
    
  3. Start Hermes gateway/WebUI with Kanban dispatch enabled.

  4. Create a Kanban task assigned to the secondary worker profile that creates an empty Markdown file in a workspace:

    hermes kanban create 'Create empty Markdown file in workspace' \
      --assignee worker_profile \
      --workspace dir:/home/user_name/workspace/test-workspace \
      --body 'Create an empty Markdown (.md) file in the workspace directory /home/user_name/workspace/test-workspace. The file should contain no content. Choose a sensible filename if none is specified, and report the exact path created.' \
      --created-by default \
      --priority 10 \
      --json
    
  5. Wait for the worker to complete. In the observed case, the worker created:

    /home/user_name/workspace/test-workspace/empty.md
    
  6. Create a second Kanban task assigned to the same worker profile asking it to delete that exact file:

    hermes kanban create 'Delete empty Markdown file from workspace' \
      --assignee worker_profile \
      --workspace dir:/home/user_name/workspace/test-workspace \
      --body 'Delete the file /home/user_name/workspace/test-workspace/empty.md that you previously created. Only delete this exact file. After deleting it, report whether the file no longer exists.' \
      --created-by default \
      --priority 10 \
      --json
    
  7. Dispatch the task if needed:

    hermes kanban dispatch --max 1 --json
    
  8. Check the task status:

    hermes kanban show <task_id>
    hermes kanban runs <task_id>
    
  9. Check whether the file still exists:

    test -e /home/user_name/workspace/test-workspace/empty.md && echo "still exists" || echo "missing"
    

Expected Behavior

If a detached Kanban worker cannot execute a command because approval is required or unavailable, the task should not be marked done.

Expected behavior should be one of the following:

  1. Move the task to blocked with a clear reason, for example:

    Blocked: approval required to run `rm -f /home/user_name/workspace/test-workspace/empty.md`
    
  2. Fail the run/task with an explicit error.

  3. Route the approval request back to the originating user/WebUI/gateway session and wait for approval.

The most natural behavior for the existing Kanban model seems to be:

approval required → task moves to blocked

Actual Behavior

The Kanban task was marked done / completed, but the file still existed.

Observed state:

Task: completed/done
Run: completed
File: /home/user_name/workspace/test-workspace/empty.md still existed
File size: 0 bytes

The worker log indicated the delete command failed or was not successfully executed, but the task still ended in a completed state.

The user did not receive or approve any approval prompt for the detached worker.

Additional Context

The Kanban dispatcher appears to spawn workers as detached subprocesses. The relevant code path appears to be in:

hermes_cli/kanban_db.py

Specifically, _default_spawn launches something equivalent to:

hermes -p <profile> chat -q "work kanban task <task.id>"

The worker subprocess is spawned with:

stdin=subprocess.DEVNULL
stdout=<worker log>
stderr=subprocess.STDOUT
start_new_session=True

Because stdin is /dev/null and output is written to a log, the worker does not have an interactive approval channel.

The Kanban system appears to depend on the worker explicitly calling kanban_complete or kanban_block. A terminal command approval failure does not appear to automatically transition the task to blocked.

This means a non-interactive worker can encounter an approval-gated operation and fail to perform the requested action, but still allow the agent/task flow to complete successfully.

Environment

  • OS: Linux

  • Hermes mode: WebUI + gateway

  • Profiles involved:

    • default profile
    • secondary worker profile
  • Kanban dispatch:

    kanban:
      dispatch_in_gateway: true
    
  • Approval config for both profiles:

    approvals:
      mode: manual
      timeout: 60
      cron_mode: deny
    
  • Security config observed:

    security:
      tirith_enabled: true
      tirith_fail_open: true
    

Impact

This is risky because it can make Kanban task state unreliable:

  • The board can show done when the requested operation did not happen.
  • Users may trust completed tasks without independently verifying side effects.
  • Approval-gated commands in detached workers can silently become false-positive completions.
  • The blocked lane is bypassed even though this is exactly the type of situation it should represent.

Suggested Fix

Possible fixes:

  1. Automatically convert unresolved approval-required terminal/tool errors in Kanban workers into kanban_block.

  2. Add a Kanban worker-level guard: if a tool call returns an approval-required, denied, or unavailable error, the worker must not call kanban_complete.

  3. Route worker approval requests through the gateway/WebUI so the originating user can approve or deny them.

  4. Add dispatcher/runtime validation: if a worker exits cleanly but does not provide a valid completion result, or if the completion summary is empty after a tool error, mark the task failed or blocked instead of done.

  5. Include task metadata/event history showing approval-required failures so they are visible from hermes kanban show or hermes kanban log.

Minimal Desired Behavior

For detached Kanban workers, this should happen:

Worker needs approval for destructive command
→ no interactive approval channel is available
→ task transitions to blocked
→ blocked reason includes exact command/tool and reason
→ task is not marked done
Assessmentadvisory
bug●● medium75% confidence

Kanban worker incorrectly transitions tasks to 'done' when non-interactive approval fails rather than moving them to a 'blocked' state.

Likely files
  • api/tasks.py
  • api/approvals.py
  • api/models.py
  • api/agent_sessions.py
Create the request

This opens a fresh agent run and a draft PR for issue #2833.

Cancel