Commit Graph
5 Commits
Author SHA1 Message Date
duylh19andClaude Sonnet 5 4f0010a971 merge: hoà cập nhật mới từ origin/feature/delta-team/epic-R04 (R08 Chat UI Hub + R10)
Đồng nghiệp đã push thêm 10 commit lên nhánh trong lúc đang xử lý merge
trước đó (R08-T01..T06 chat_panel.py split, R08 folder/dashboard/graph/
scheduling hoàn thiện, R10 CI Quality Gates + Contributor Recipes + E2E
smoke test). Resolve conflict:

- application/monitoring/__init__.py, domain/tasks/__init__.py,
  infrastructure/persistence/json/__init__.py: chỉ khác docstring — giữ bản
  HEAD (đầy đủ ngữ cảnh EPIC hơn), hợp nhất __all__ khi cần
  (MonitoringQueryService).
- tests/fakes/__init__.py: hợp nhất __getattr__ để lazy-load cả
  FakeToolExecutor lẫn ToolInvocation (bản HEAD thiếu ToolInvocation), bỏ
  entry "FakeClock" bị lặp trong __all__.
- tests/integration/test_routing_surfaces.py (deleted by them): khôi phục
  lại bản đã sửa ở lần merge trước — verify lại: API routing
  (RoutingApplicationService.resolve/_apply_routing/_apply_co4e_routing)
  không đổi sau khi chat_panel.py chuyển sang presentation/chat/*, 9/9 test
  vẫn pass trên code đã merge.

Ghi chú (không sửa, ngoài phạm vi merge): tests/fakes/__init__.py trên nhánh
remote export "ToolInvocation" từ fake_tool_executor.py nhưng class này đã
bị xoá nhầm từ commit chung 10739f1 (breakdown folder tree epic R01) — hiện
là dead code, không ai import, nhưng sẽ raise ImportError nếu có test nào
sau này thử dùng.

Đã chạy pytest tests/: 793 passed (không phát sinh fail mới so với lần
merge trước — 8 fail còn lại đều do môi trường sandbox: thiếu package
keyring, và tên thư mục checkout "cowork-local" thay vì "cowork_local"
khiến vài test spawn-subprocess không import được package).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-28 11:57:14 +09:00
duylh19andClaude Sonnet 5 c7784defc3 merge: hoàn tất merge origin/feature/teamhoa/r05-r06 vào feature/delta-team/epic-R04
Resolve 3 file conflict:
- docs/refactor/Refactoring_Checklist.md: giữ nội dung incoming (phía HEAD
  trống ở đoạn conflict).
- tests/integration/test_routing_surfaces.py: khôi phục từ incoming (bị mất
  ở merge trước đó), điều chỉnh lại cho khớp API hiện tại của
  RoutingApplicationService (resolve()/RouteEvaluation/mode_resolver thay vì
  route_turn()/mode_reader cũ), bỏ 2 test pin một lớp RoutingDecision không
  còn tồn tại trên nhánh này.
- ui/folder_tab.py: chấp nhận xoá (deleted by them) — đã được thay thế hoàn
  toàn bởi presentation/folder/* (R08-T12), không còn nơi nào import module
  cũ.

Sửa thêm 2 chỗ lệch API bị auto-merge không báo conflict (phát hiện khi chạy
lại test):
- presentation/folder/ai_edit_model_resolver.py + ai_file_editor_dialog.py:
  AiEditModelResolver.apply_routing() gọi route_turn() đã bị xoá khỏi
  RoutingApplicationService — chuyển sang build_routing_application_service()
  .resolve(RoutingRequest(...)) giống chat_panel.py/co4e_chat.py; sửa luôn
  chữ ký _confirm_routing_switch nhận thêm timeout cho khớp contract confirm
  mới.
- config.py: import JsonConfigRepository ở đầu file gây circular import với
  core/tasks.py (cần CONFIG_DIR) qua chuỗi mới
  infrastructure/persistence/json/task_repository_impl.py (R07). Dời import
  xuống ngay trước chỗ dùng đầu tiên.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-28 11:40:44 +09:00
duylh19andClaude Opus 5 3665135c38 feat(R04): run every Cowork turn through ConversationApplicationService
R04-T03 — the turn lifecycle, extracted from `core/chat_agent.py::run_cowork`
into `application/conversations/`. The 260-line body mixed the lifecycle (step
budget, cancel checks, guard -> preview -> gate -> execute ordering, sandbox
tidy-up) with the machinery doing each step, and reaching any of it meant
standing up a Qt widget and a worker thread. It is now a plain object driven
through two Protocols and six callables (`turn_runtime.py`), with the concrete
`core/*` wiring confined to `core_runtime_adapter.py` — the same shape R03 used
for routing. Faithful port, not an improvement pass: where the original had a
quirk (the step-ceiling note only merges into the answer when the last message
is the assistant's) the quirk is preserved and commented.

R04-T04 — `ui/cowork_tab.py::build_job` no longer calls run_cowork. It captures
the widget's state at submit time, builds the request via the new
`cowork_turn_request.py` and executes it. `execute(..., messages=...)` hands the
widget's own list over because `_reattach_running_turn` replays from it WHILE
the worker appends and `_finalize_turn` slices it afterwards — a private list
would break both silently.

R04-T05 — `core/task_executors.py`'s cowork branch shares the same engine. All
five unattended-run behaviours stay put (plan reminder, history_ready, History
autosave per assistant message, timeout notice, plan_incomplete_reason), and
`_unattended_prompt` now expresses the load-bearing prefix order in one
readable call instead of three successive rebindings.

Verification: 74 new tests (364 passed, 1 skipped overall; check_imports PASS).
The two that matter most:
- `test_conversation_service_parity.py` runs the same scripted turn through
  run_cowork AND the service and compares the event stream, the resulting
  conversation and the advertised tool list across 7 scenarios;
- `test_task_executor_turn.py` was written BEFORE the migration and passed 8/8
  against the old code, then unchanged against the new.

Known: `ui/cowork_tab.py` (416 -> 455) and `core/task_executors.py` (476 -> 524)
stay above the 400-LOC limit. Both were already over it before this change;
bringing them under needs the R08 / R07 decompositions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 13:14:15 +09:00
duylh19andClaude Opus 5 19e6b4deb2 feat(R04): add the immutable turn snapshot and typed agent event stream
R04-T01 — `domain/agents/conversation_execution_request.py`: a frozen
snapshot of everything one chat turn needs. Turn inputs previously lived in a
closure plus a 15-key ctx dict inside `ui/chat_panel.py::_start_turn`, and the
worker thread kept reading the widget back while it ran, so every later click
was visible to work already in flight. The request also owns the prompt
composition rules (instruction prefix separator, session notes, model-switch
review note) that were inline in that closure.

R04-T02 — `domain/agents/agent_event.py`: 13 frozen event types replacing the
untyped `{"type": ...}` dicts, whose only specification was the 130-line
if/elif chain in `_on_event`. Each event serialises back to the exact legacy
dict, so the presentation layer is untouched; `agent_event_codec.py` parses the
other way and is a temporary shim, isolated so R08 can delete it in one move.
`assistant_done` is deliberately NOT the end of a turn (it fires once per
provider call), so it maps to AssistantMessageCompletedEvent while the new
TurnCompletedEvent reports the turn itself.

R04-T03 (part) — `domain/agents/agent_result.py`: one named outcome for a
finished turn, replacing the message list / 3-tuple / reconstructed-from-side-
effects trio the three callers each read differently.

Verification: 66 tests. Beyond the unit tests,
`tests/integration/test_agent_event_bridge.py` runs the REAL `run_cowork` loop
offline and asserts every dict it emits is recognised and round-trips
byte-for-byte — a guard against an event type nobody modelled or a key whose
meaning silently drifted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 13:13:56 +09:00
duylh19andClaude Opus 5 176e6aef79 fix(ci): guard the MCP SDK import so pytest can collect the suite
`tests/test_project_context_mcp_template.py` imported `mcp` at module scope,
but the SDK is a runtime dependency (requirements.txt) and is deliberately
absent from requirements-test.txt — the only thing CI installs. Collection
therefore aborted for the ENTIRE suite before a single test ran.

The guard now sits inside the one test that touches the SDK, so the other
cases in the file (pure-Python contract checks) keep running on CI instead
of being skipped along with it.

Unrelated to the R04 refactor; kept as its own commit so it can be cherry-
picked to main on its own.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 13:13:42 +09:00