Skip to content

fix(claude): resolve queue-operation prompt completion deadlock#259

Open
nixecn wants to merge 1 commit into
SeemSeam:mainfrom
nixecn:fix/claude-queue-operation-anchor-leak
Open

fix(claude): resolve queue-operation prompt completion deadlock#259
nixecn wants to merge 1 commit into
SeemSeam:mainfrom
nixecn:fix/claude-queue-operation-anchor-leak

Conversation

@nixecn

@nixecn nixecn commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Problem

When CCB delivers a prompt to Claude via tmux paste-buffer while the Claude
REPL is busy
(cooking / tool_use), Claude CLI records it as

type=queue-operation, operation=enqueue, content=<prompt>

and does not synthesize a type=user message. The completion pipeline was
blind to queue-operation records: structured_event() dropped them entirely, so

  • handle_user_event() never saw the CCB_REQ_ID anchor;
  • anchor_seen stayed False;
  • every subsequent assistant event (including the final end_turn, and the
    last_assistant_uuid needed for turn_duration matching) was gated out;
  • the attempt hung in running forever, backing up the mailbox queue.

Field impact: repeated pm (central dispatcher) stalls, because pm is the
agent that most often receives async replies while busy. A 183-job sample
of the pm session log found 14 queue-op prompt deliveries; 9 of them
failed to complete automatically (7 incomplete, 2 cancelled by human recovery).

Full diagnosis: docs/claude-queue-operation-completion-deadlock-diagnosis.md
(added by this PR, 183 lines).

Fix

Three coordinated changes:

  1. Synthesize a user event from queue-operation records
    (lib/provider_backends/claude/comm_runtime/parsing_runtime/structured.py)

    structured_event() now emits a role=user event for
    queue-operation/enqueue entries carrying the full prompt text, so the
    CCB_REQ_ID anchor is observed and anchor_seen flips.

  2. Emit zero-text assistant events for tool_use / thinking-only entries
    (same file)

    Previously dropped because extract_message yielded no visible text.
    These carry the assistant uuid so pre-anchor bookkeeping can track
    last_assistant_uuid and the later turn_duration system event's
    parent_uuid matches.

  3. Pre-anchor uuid bookkeeping
    (lib/provider_backends/claude/execution_runtime/polling.py)

    _process_event() now runs a bookkeeping pass on every assistant event
    (uuid tracking only) before the anchor_seen gate. Full
    chunk/reply/turn-boundary handling still requires anchor_seen.

Total: +335 / -2 across 3 files (docs + 2 code files).

Verification

  • All 14 historical queue-op stuck jobs (sampled from the pm session)
    now reach TURN_BOUNDARY under the patched detector.
  • The idle-REPL (user/text_string) path is unchanged — regression on
    normal prompts passes.
  • Test suites:
    $ python3.11 -m pytest test/ -q -k "claude and (parsing or event_reading or exec_polling or assistant_events or queue_operation)"
    10 passed
    $ python3.11 -m pytest test/ -q -k "structured or polling"
    55 passed
    

When a prompt is delivered via tmux paste-buffer while the Claude REPL is
busy (cooking/tool_use), Claude CLI records it as a
'type=queue-operation, operation=enqueue, content=<prompt>' entry
(optionally followed later by an attachment/queued_command), and does NOT
synthesize a 'type=user' message. The completion pipeline was blind to
queue-operation records: structured_event() dropped them entirely, so
handle_user_event() never saw the CCB_REQ_ID anchor, anchor_seen stayed
False, assistant events (including the final end_turn and the
last_assistant_uuid needed for turn_duration matching) were gated out,
and the attempt hung in 'running' forever, backing up the mailbox queue.

This manifested in the field as repeated pm (central dispatcher) stalls
because pm is the agent that most often receives async replies while
busy. A 183-job sample of the pm session log found 14 queue-op prompt
deliveries; 9 of them failed to complete automatically (7 incomplete, 2
cancelled by human recovery).

Fix:
1. structured_event() now synthesizes a role=user event from
   queue-operation/enqueue records carrying the full prompt text, so the
   anchor (CCB_REQ_ID) is observed and anchor_seen flips.
2. structured_event() also emits zero-text assistant events for
   assistant entries whose content is pure tool_use/thinking blocks
   (previously dropped because extract_message yields no visible text).
   These carry the assistant uuid so that pre-anchor bookkeeping can
   track last_assistant_uuid and the later turn_duration system event's
   parent_uuid matches.
3. _process_event() runs a pre-anchor bookkeeping pass on every
   assistant event (uuid tracking only) before the anchor_seen gate;
   full chunk/reply/turn-boundary handling still requires anchor_seen.

A detailed diagnosis is in
docs/claude-queue-operation-completion-deadlock-diagnosis.md.

Verified:
- All 14 historical queue-op stuck jobs (sampled from the pm session)
  now reach TURN_BOUNDARY under the patched detector.
- idle-REPL (user/text_string) path is unchanged; regression on normal
  prompts passes.
- Existing claude parsing/event_reading/exec_polling/assistant_events
  tests still pass (19/19).
@nixecn

nixecn commented Jul 18, 2026

Copy link
Copy Markdown
Contributor Author

这个PR修改的内容,已经经过一段时间使用的验证了。 所以可以放心合并。

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant