Skip to content

feat(concurrency): port ch07 concurrency gaps from TS - #16

Merged
agentforce314 merged 1 commit into
mainfrom
claude/ch07-concurrency
May 8, 2026
Merged

feat(concurrency): port ch07 concurrency gaps from TS#16
agentforce314 merged 1 commit into
mainfrom
claude/ch07-concurrency

Conversation

@agentforce314

Copy link
Copy Markdown
Owner

Summary

Closes 7 of the gaps identified in my-docs/ch07-concurrency-gap-analysis.md between the TypeScript reference (StreamingToolExecutor.ts + toolOrchestration.ts) and the Python port (src/services/tool_execution/). All Tier-1 correctness fixes plus the listener-leak and progress-wake fixes; query-loop migration (G3-4b) and full speculative-execution wiring (G10) are deferred per the refactor plan.

Gaps closed

ID Fix Severity
G6 _execute_tool runs against a per-tool dataclasses.replace() copy of the context — concurrent tools no longer race on the executor's shared abort_controller. high
G1 _run_tools_concurrently no longer swallows exceptions; unhandled errors from run_tool_use surface as synthetic tool_use_error so the next API turn doesn't 400 on an unmatched tool_use. high
G2 partition_tool_calls + StreamingToolExecutor.add_tool share classify_concurrency_safe() — fail-closed on unknown tools, non-dict input, and per-tool classifier exceptions. medium
G4 Concurrent-batch context-modifier path accepts the ContextModifier dataclass shape (the producer's actual return value); prior dict-only reader silently dropped modifiers. medium
G5 add_tool retains the ensure_future queue task in _pending_queue_tasks; asyncio could otherwise GC the coroutine mid-flight. medium
G7 AbortSignal.add_listener(..., once=True) with self-detaching semantics; create_child_abort_controller removes its parent listener on either fire-direction. Long-lived parents no longer accumulate one dead handler per child tool. medium
G9 get_remaining_results clears the progress event before rechecking pending progress, closing the producer/consumer race where set() was wiped and the wait blocked. medium

What's deferred (and why)

  • G3-4b — migrating src/query/query.py to call orchestrator.run_tools directly. Touches the live tool-execution path; deserves its own PR with end-to-end query-loop testing.
  • G10 — wiring StreamingToolExecutor into the streaming response parser for true speculative execution. Out of scope for this chapter; touches the streaming chapter's surface.
  • G15 / G16 — UI affordance + memory-correction-hint helper. Low severity, blocked on adjacent subsystems.

Test plan

  • New: tests/test_streaming_executor_race.py — verifies per-tool abort-controller isolation under concurrent execution
  • New: tests/test_abort_controller_once.py — verifies once-fire semantics and listener-leak prevention
  • New: tests/test_orchestrator_concurrency.py — verifies fail-closed classification, exception surfacing, context-modifier ordering, and submission-order invariant
  • Existing: tests/test_streaming_executor.py (9 tests) still passes
  • Existing: tests/parity/test_concurrency_model.py (16 tests) still passes
  • Total: 41 concurrency-related tests green

Note: 25 unrelated test failures in tests/test_repl.py exist on main too — they require a configured API key, not affected by this PR.

🤖 Generated with Claude Code

Closes seven gaps identified in my-docs/ch07-concurrency-gap-analysis.md
between the TS StreamingToolExecutor + toolOrchestration.ts and the
Python port:

- G6 _execute_tool no longer mutates the executor's shared
  ToolContext.abort_controller; each tool now runs against a
  dataclasses.replace() copy with its own per-tool controller, so
  concurrent tools don't race on the abort field.
- G1 _run_tools_concurrently no longer swallows exceptions silently;
  unhandled errors from run_tool_use surface as tool_use_error so the
  next API turn doesn't 400 on an unmatched tool_use block.
- G2 partition_tool_calls + StreamingToolExecutor.add_tool now share
  classify_concurrency_safe(), which fail-closes on unknown tools,
  non-dict input, and exceptions from the per-tool classifier.
- G4 the concurrent-batch context-modifier path accepts the
  ContextModifier dataclass shape that run_tool_use actually emits;
  the prior dict-only reader silently dropped modifiers.
- G5 add_tool retains the ensure_future task in
  _pending_queue_tasks; without this asyncio could GC the scheduled
  coroutine mid-flight.
- G7 AbortSignal.add_listener supports once=True with self-detaching
  semantics; create_child_abort_controller now removes its parent
  listener on either fire-direction so long-lived parents no longer
  accumulate one dead handler per child tool.
- G9 get_remaining_results clears the progress-available event
  before the pending-progress recheck, closing the race where a
  producer's set() between check and clear was wiped and the wait
  blocked until a tool finished.

Defers G3-4b (migrating query.py to call orchestrator.run_tools), G10
(StreamingToolExecutor wiring into the streaming response parser),
G15 (set_has_interruptible_tool_in_progress UI hook), and G16
(with_memory_correction_hint helper) — see refactor plan.

Tests: 19 new cases across 3 files
(test_streaming_executor_race, test_abort_controller_once,
test_orchestrator_concurrency) covering each gap's invariant. Existing
tests/test_streaming_executor.py and tests/parity/test_concurrency_model.py
still pass — 41 concurrency tests green.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@agentforce314
agentforce314 merged commit 2501ad9 into main May 8, 2026
singlaamitesh pushed a commit to singlaamitesh/clawcodex that referenced this pull request Jul 7, 2026
…ncurrency

feat(concurrency): port ch07 concurrency gaps from TS
singlaamitesh pushed a commit to singlaamitesh/clawcodex that referenced this pull request Jul 7, 2026
… hyperlinks + frame metrics + thinking widget + per-tool permission specialization + output styles frontmatter + placeholder Resume/Doctor screens

Bundle of standalone module additions across ch13 phases 4-12 that
share no integration surface with each other and don't touch
``app.py``, ``screens/repl.py``, ``agent_bridge.py``, or
``agent_loop.py``. Each module ships with unit tests; cross-cutting
production wiring (where present) lives in a follow-up PR so this
change merges cleanly into ``main`` regardless of order.

Phase 4 vim foundation (gap agentforce314#4 wave-1+2):
- ``src/tui/vim_buffer.py`` — multi-line ``VimBuffer`` + ``Cursor`` + ``Range``
- ``src/tui/vim_text_objects.py`` — ``find_text_object`` for ``iw``/``aw``/``ip``/``ap``/quotes/braces
- ``src/tui/vim_operators.py`` — ``parse_operator_motion`` (``d2w``, ``ciw``, ``y$``); operator dispatch d/c/y/>/<
- ``src/tui/vim_visual.py`` — Visual / Visual-Line / Visual-Block selection model
- ``src/tui/vim_search.py`` — ``/`` / ``?`` regex search with ``n``/``N`` repeat
- 70 tests (``test_vim_multiline.py`` + ``test_vim_wave2.py``)

Phase 5 transcript search module (gap agentforce314#2):
- ``src/tui/widgets/transcript_search.py`` — ``TranscriptSearch`` ModalScreen + ``find_matches`` helper
- 16 tests (``test_transcript_search.py``)

Phase 6 IME declared cursor module (gap agentforce314#3):
- ``src/tui/declared_cursor.py`` — refcounted-set registry + CSI emission
- 16 tests (``test_declared_cursor.py``)

Phase 7 specialized permission dialogs (gap agentforce314#8):
- ``src/tui/screens/permission_modal.py`` ``_TOOL_RENDERERS`` dispatcher + per-tool renderers (Bash, Edit, Write, Read)
- 16 tests (``test_permission_modal_specialization.py``)

Phase 8 placeholder Resume/Doctor screens (gap agentforce314#9):
- ``src/tui/screens/resume_conversation.py`` — placeholder with empty-state until persistence wiring lands
- ``src/tui/screens/doctor.py`` — read-only diagnostics (env/hyperlinks/frame-metrics/storage)
- 6 tests (``test_resume_doctor_screens.py``)

Phase 9 output styles frontmatter (gap agentforce314#7):
- ``src/outputStyles/{loader,styles}.py`` — YAML frontmatter parsing via existing ``parse_frontmatter``; auto-discovery of ``~/.claude/outputStyles/``
- 11 tests (``test_output_styles_frontmatter.py``) + parity test (``test_output_styles_parity.py``)

Phase 10 OSC 8 hyperlinks (gap agentforce314#15):
- ``src/tui/hyperlinks.py`` — capability detection (FORCE_HYPERLINK / TERM_PROGRAM / VTE_VERSION / truecolor)
- ``src/tui/widgets/messages/tool_result.py`` wraps file paths with the negative-lookbehind regex (avoids eating sentence punctuation)
- 38 tests (``test_hyperlinks.py`` + ``test_tool_result_hyperlinks.py``)

Phase 11 frame-event observability (gap agentforce314#10):
- ``src/tui/frame_metrics.py`` — ``FrameEvent`` shape + ``register_frame_observer`` + no-op fast path when ``CLAWCODEX_DEBUG_REPAINTS`` unset
- 17 tests (``test_frame_metrics.py``)

Phase 12 assistant-thinking widget (gap agentforce314#16 sub-item):
- ``src/tui/widgets/messages/assistant_thinking.py`` — italic-dim styled distinct row; redacted variant
- ``src/tui/widgets/transcript_view.py`` adds ``append_thinking_chunk``/``append_thinking`` shells with symmetric guard for ``append_assistant_chunk`` (Critic-flagged: prevents future thinking↔assistant mis-routing once dispatch wiring lands)
- 11 tests (``test_assistant_thinking.py`` + ``test_transcript_thinking_transitions.py``)

Test surface: 521 cumulative tests pass (``tests/tui/`` + ``tests/parity/test_output_styles_parity.py``).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
peroxider pushed a commit to peroxider/clawcodex that referenced this pull request Jul 9, 2026
fix(orchestrator): P0 质量闸门 — 前提校验+诚实退出 与 回归防护

Created-by: qq_49552963
Commit-by: yeyunyao
Merged-by: chadwweng
Description: ## 这个 MR 解决什么

这个 MR 增加两道交付前质量门:

1. **前提检查**:issue 指向的文件或符号不存在时,直接说明无法继续,不再凭空创建文件。
2. **回归阻断**:代码改完后只要验证失败,就停止交付,不再创建 MR。

## 关联 issue

- [agentforce314#16 编排器遇到不存在文件时会伪造修复并创建 MR](https://gitcode.com/chadwweng/clawcodex/issues/16)
- [agentforce314#17 编排器未阻断破坏性改动,改坏测试仍交付 MR](https://gitcode.com/chadwweng/clawcodex/issues/17)

## 验证

### click agentforce314#21:文件不存在时正确停止

系统返回 `premise_not_met`,并明确 `No merge request was opened`。

![MR71 premise gate fixed validation](https://raw.gitcode.com/chadwweng/clawcodex/attachment/uploads/b3083a4f-5aa1-48e0-a702-9d73331012c2/gitcode-mr71-premise-fixed-validation.png)

### click agentforce314#22:测试失败时正确阻断

系统返回 `verification_failed`,没有把失败修改当成成功交付。

![MR71 regression guard fixed validation](https://raw.gitcode.com/chadwweng/clawcodex/attachment/uploads/05b0987c-b214-41b6-831f-e134e67b3e49/gitcode-mr71-regression-fixed-validation.png)


See merge request: chadwweng/clawcodex!71
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