Files
cowork-local/docs/refactor/BaoCao_TeamHoa_R05_R06.md
T
vudt15andClaude Sonnet 5 8ab29800db docs(refactor): add the Team Hoa completion report for R05/R06
Mirrors docs/refactor/BaoCao_TeamDuy_R01_R03_R04.md's structure: per-EPIC
results, test evidence, the two real bugs found and fixed, secondary
improvements, open items needing another team's sign-off, untested scope,
and what's next.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-22 21:30:52 +09:00

16 KiB
Raw Blame History

BÁO CÁO KẾT QUẢ — TEAM HOA: EPIC R05, R06

  • Dự án: Cowork Local (Cowork-Local BamBOO)
  • Team: 🟢 Team Hoa — Workspace, Filesystem, Scheduling & Tool Registry
  • Nhánh: feature/teamhoa/r05-r06 (tạo từ origin/feature/deltateam/refactor-plan, chưa push lên remote — xem mục 7)
  • Thời gian thực hiện: 21/08/2026, 21:40 ➔ 22:57
  • Ngày báo cáo: 22/08/2026
  • Tài liệu gốc: Feature_Architecture_Proposal.md, Refactoring_Checklist.md, plan.md

1. Tóm tắt điều hành

Hoàn tất 10/10 task của 2 EPIC được giao: R05 (Tool, MCP & Connector Policy) và R06 (Workspace, Filesystem & History Isolation). Đã commit 2 commit trên branch cục bộ; chưa push lên Gitea — remote từ chối với lỗi quyền ghi (xem mục 7 #1).

Chỉ số Kết quả
Task hoàn thành 10/10 (R05: 5, R06: 5)
Commit 2 (ae4fe72, cf542b7)
File thay đổi 41 (27 file mới, 14 file sửa — 1 file (docs/refactor/Refactoring_Checklist.md) sửa ở cả 2 commit)
Dòng code +3.054 / −459
Test 283 pass / 12,5s (283/287 — 4 fail có sẵn từ trước, không do R05/R06)
Test suite nhanh (unit + contract + characterization + routing) 256 pass / 4,5s
CASAN Check 3 (scripts/check_imports.py) PASS — 0 Qt import trong domain/, application/
File production > 400 dòng (file mới) 0 — lớn nhất domain/tools/tool_registry.py 125 dòng

2 lỗi thật được phát hiện và sửa trong quá trình làm (chi tiết mục 5): một lỗ hổng bảo mật (MCP/connector tool không qua permission gate) và một race condition (turn chạy ngầm lưu nhầm lịch sử vào project khác).


2. Kết quả theo từng EPIC

🔹 EPIC R05 — Tool, MCP & Connector Policy (5/5)

Task Sản phẩm Ghi chú
R05-T01 domain/tools/tool_descriptor.py, tool_registry.py ToolCapability (Flag: READ/WRITE/EXECUTE/NETWORK, kết hợp được) + ToolDescriptor + ToolRegistry
R05-T02 infrastructure/filesystem/{file_tools,command_tools,fetch_tools,tool_context}.py Tách if/elif dispatcher của core/tools.py; core/tools.py còn 291 dòng (từ 566), là shim strangler-fig
R05-T03 application/conversations/tool_policy_gateway.py ToolPolicyGateway.allow(name, gate, payload) — thay 2 chỗ check hardcode riêng biệt (chat_agent.py, code_agent.py) bằng 1 lookup capability
R05-T04 Sửa core/chat_agent.py, core/mcp_client.py Thay đổi hành vi có chủ đích — xem mục 5, Lỗi 1
R05-T05 infrastructure/mcp/mcp_source_manager.py Tách lifecycle connection MCP khỏi state.py::AppContext

Vấn đề gốc đã giải quyết — cùng một việc "tool này có cần xác nhận trước khi chạy không" tồn tại 3 cách trả lời khác nhau:

core/chat_agent.py::run_cowork    name in ("run_command", "install_package")
core/code_agent.py::run_code      name in (WRITE_TOOLS | MS365_WRITE_TOOLS)
core/mcp_client.py / ext_connectors.py    (không hỏi gì cả)

Cách thứ 3 là một lỗ hổng thật, không phải khác biệt thiết kế — xem mục 5.

🔹 EPIC R06 — Workspace, Filesystem & History Isolation (5/5)

Task Sản phẩm Ghi chú
R06-T01 domain/workspaces/workspace_session.py WorkspaceSession — snapshot bất biến (project_id/workspace_root/sandbox_dir/allowed_paths) + is_allowed(path), cùng khuôn với ConversationExecutionRequest (R04-T01)
R06-T02 infrastructure/persistence/json/{atomic_write,workspace_repository_impl,conversation_repository_impl}.py Sửa bug thật — xem mục 5, Lỗi 2
R06-T03 infrastructure/filesystem/execution_workspace.py Đặt tên cho quy ước .scratch đã có, không đổi vị trí file
R06-T04 Sửa ui/chat_panel.py Sửa race condition thật — xem mục 5, Lỗi 3
R06-T05 application/workspaces/file_workspace_service.py File Explorer/AI Editor gọi core/tools.py::execute_tool giống agent, không viết lại logic

3. Kiến trúc sau refactor

presentation/  (chưa đổi ở đợt này — ui/chat_panel.py chỉ thêm 1 field "home_history_dir")
      │
      ▼
application/   conversations/tool_policy_gateway.py        ← ALLOW/CONFIRM cho mọi tool call
               workspaces/file_workspace_service.py         ← file ops cho File Explorer/AI Editor
      │        (100% pure Python — check_imports.py chặn import Qt)
      ▼
domain/        tools/{tool_descriptor,tool_registry}.py     ← capability + catalogue
               workspaces/workspace_session.py              ← snapshot workspace bất biến
      ▲
infrastructure/ filesystem/{file_tools,command_tools,fetch_tools,tool_context,execution_workspace}.py
                mcp/mcp_source_manager.py                   ← lifecycle connection MCP
                persistence/json/{atomic_write,*_repository_impl}.py

Nguyên tắc di trú (ADR-001 mục 4, tiếp nối cách Team Duy làm ở R04): không viết lại engine. core/tools.py::execute_tool, core/chat_agent.py::run_cowork, core/code_agent.py::run_code vẫn là engine bên dưới — tầng mới chỉ sở hữu phần phân loại rủi ro (R05) và phần định danh workspace (R06) mà trước đây nằm rải rác/hardcode. pytest xanh liên tục giữa các bước.


4. Bằng chứng kiểm thử

Phân bố test (bao gồm test mới của Team Hoa)

Suite Số test Ghi chú
tests/unit/ 137 +41 test mới (R05: 26, R06: 15 — không tính test_history_dir_race.py, ở integration/)
tests/contracts/ 29 có sẵn từ R03, không đổi
tests/characterization/ 13 có sẵn từ R01, vẫn xanh — xác nhận run_cowork không hồi quy sau khi sửa gate
tests/routing/ 79 có sẵn từ trước, không đụng
Cộng 4 suite nhanh 256 (4 fail routing-env, không do R05/R06) 4,5s
tests/integration/ 27 +2 test mới: test_history_dir_race.py — Qt offscreen thật, không phải test double
Tổng 287 (283 pass) 12,5s

Đối chiếu Definition of Done (theo DeltaTeam_prompt.md / mẫu Team Duy)

# Tiêu chí Kết quả
1 Mọi file mới < 400 dòng ✅ Lớn nhất: domain/tools/tool_registry.py 125 dòng
2 0 import Qt trong domain/, application/ ✅ check_imports.py PASS
3 Comment tiếng Anh giải thích lý do ở mọi khối sửa/mới ✅
4 Có unit/contract/integration test, verify bằng chạy thật ✅ 41 test mới + 2 test Qt offscreen thật cho race condition
5 Không hồi quy ✅ 283/287 pass — 4 fail là lỗi có sẵn từ trước R05/R06 (2 EPIC R02, 2 do môi trường máy có Ollama thật)
6 Ghi Start/End vào Checklist ✅ 10 task đã tick kèm mốc thời gian
7 Cổng CASAN (run_quality_gate.py, R10-T02) ⚠️ Chưa viết (thuộc R10, chưa tới lượt) — Check 3 đã PASS

5. Hai lỗi thật phát hiện và sửa trong quá trình làm

🔴 Lỗi 1 (R05-T04) — Tool MCP/Connector chạy hoàn toàn không qua permission gate

core/chat_agent.py::run_cowork có 2 nhánh dispatch tool call: nhánh built-in (read_file, run_command, ...) đi qua gate xác nhận khi Settings bật "confirm before running commands"; nhánh extra_tools (mọi tool từ MCP server hoặc Connector — core/mcp_client.py, core/ext_connectors.py) gọi thẳng:

if name in extra_names and extra_executor is not None:
    ...
    result = extra_executor(name, args)   # KHÔNG có bước xác nhận nào

Nghĩa là một MCP server (kể cả server tự cấu hình, hoặc MS365 write-tool như send_mail) chạy auto-run tuyệt đối, bất kể người dùng đã bật "confirm before running commands" trong Settings hay chưa. Đây không phải khác biệt thiết kế có chủ đích — không có ghi chú, không có toggle riêng cho việc này.

Sửa: mọi extra_tools được gắn ToolCapability mặc định bảo toàn (WRITE|EXECUTE|NETWORK — vì MCP không có chuẩn khai báo rủi ro), đăng ký vào registry của turn, và đi qua CÙNG ToolPolicyGateway với built-in tools.

Đây là thay đổi hành vi người dùng sẽ thấy: khi "confirm before running commands" đang bật, tool MCP/connector từ giờ sẽ hỏi xác nhận — giống run_command. Verify bằng test tests/unit/test_cowork_extra_tool_policy.py (3 test: rejected trước khi executor chạy, approved thì chạy, gate=None vẫn auto-run như cũ).

🟠 Lỗi 2 (R06-T04) — Turn chạy ngầm lưu nhầm lịch sử vào project khác

ui/chat_panel.py::_persist_session (lưu hội thoại của một turn chạy ngầm, không phải conversation đang xem) gọi:

save_conversation(self.ctx.config.history_dir(), ...)

history_dir() đọc config._project_history_dir — một field dùng chung trên AppContext.config, được ui/workspace_tab.py::_load_current ghi đè mỗi lần người dùng đổi project trong màn Workspace. Nếu một turn ở project A còn đang chạy (ví dụ Scheduled Task, hoặc user gõ câu hỏi rồi chuyển sang xem project B ngay) và người dùng đổi sang project B trước khi turn đó lưu xong, hội thoại của project A bị ghi nhầm vào thư mục lịch sử của project B.

Sửa: thêm "home_history_dir" vào dict ctx mà mỗi turn đã có sẵn (cùng quy ước với home_id/home_messages/home_title — dict này được author code gốc thiết kế đúng cho mục đích này, chỉ thiếu 1 field), chụp giá trị tại lúc submit thay vì đọc sống lúc lưu.

Kèm 1 phát hiện phụ: _save_snapshot (dùng cho conversation ĐANG XEM) đã có logic đúng từ trước để không ghi đè project_id của một turn nền bằng project hiện tại — chỉ riêng thư mục lưu là bị bỏ sót, không phải toàn bộ cơ chế bị thiếu.

Verify bằng test Qt offscreen thật (không phải double): tests/integration/test_history_dir_race.py — dựng ChatPanel thật, giả lập đổi project giữa lúc turn chạy, xác nhận file được lưu đúng thư mục project A.


6. Cải thiện phụ (không nằm trong yêu cầu task)

Cải thiện Ảnh hưởng
core/projects.py::save_project, core/history.py::save_conversation/rename_conversation/set_pinned chuyển sang ghi atomic (infrastructure/persistence/json/atomic_write.py) Trước đây path.write_text(json.dumps(...)) không atomic — crash/kill giữa lúc ghi để lại file JSON hỏng, và load_project/load_conversation coi file hỏng như "không tồn tại" ➔ mất project hoặc hội thoại âm thầm, không báo lỗi. Có test giả lập crash giữa lúc ghi xác nhận file cũ không bị hỏng (tests/unit/test_atomic_write_and_repositories.py)
McpServerConnection.is_alive() (mới, core/mcp_client.py) Nhỏ, cộng thêm — cho McpToolSourceManager biết một connection cached đã chết (subprocess crash) để khởi động lại, thay vì cache giữ một connection chết vô thời hạn

7. Còn nợ & cần quyết định

# Nội dung Người quyết
1 Branch chưa lên được Gitea — git push bị từ chối: User permission denied for writing (pre-receive hook). Cần cấp quyền push cho tài khoản git đang dùng trên máy này, hoặc push bằng tài khoản khác có quyền. Admin Gitea
2 Xung đột file với EPIC R02 (Team Nam): R02-T01 giao infrastructure/persistence/json/atomic_json_file.py. R06-T02 cần atomic write ngay nên tạo atomic_write.py (tên khác, cùng thư mục) — không đụng file của Team Nam, nhưng 2 module cùng mục đích sẽ tồn tại song song cho tới khi hợp nhất. Team Nam (khi bắt đầu R02-T01)
3 WorkspaceRepository/ConversationRepository/FileWorkspaceService chưa có call site thật — giống tình trạng ProviderRegistry của Team Duy ở R03 (mục 7 #1 trong báo cáo Team Duy). Mọi nơi trong production vẫn gọi trực tiếp core/projects.py/core/history.py/core/tools.py::execute_tool. Team Hoa (nối dây ở EPIC sau)
4 R06-T04 không sửa đúng y nguyên ui/workspace_tab.py::_load_current như mô tả gốc trong plan.md — bug thật nằm ở điểm ĐỌC (ui/chat_panel.py::_persist_session), không phải điểm GHI (_load_current chỉ set field, tự nó không đọc lại). Đã sửa đúng điểm đọc, có test thật xác nhận. Việc đổi _load_current sang "đồng bộ bằng session id" như plan gốc gợi ý cần tách sâu hơn WorkspaceTab/ChatPanel, thuộc phạm vi R08 (UI/Application Separation). Team Duy (R08)
5 2 test đỏ có sẵn từ trước, không do R05/R06: tests/test_config_security.py × 2 (EPIC R02/Team Nam, đã ghi nhận từ báo cáo Team Duy) và tests/unit/test_routing_wiring.py × 2 (môi trường máy này có Ollama/llama3.1 thật + config routing cục bộ khác giả định "fresh install" của test — nghi là do máy chạy test có cấu hình routing/Ollama khác máy Team Duy dùng, cần Team Duy xác nhận lại trên máy sạch). Team Nam (#1), Team Duy (#2)

8. Phạm vi chưa kiểm thử

Nêu rõ để tránh hiểu nhầm mức độ bảo đảm:

  • R05-T04 (gate cho MCP/connector) chưa test với MCP server thật — toàn bộ test dùng ToolSpec giả (_EXTRA_SPEC trong test_cowork_extra_tool_policy.py), chưa có tình huống thật với core/mcp_client.py::McpServerConnection chạy subprocess thật.
  • McpToolSourceManager (R05-T05) chưa test với subprocess MCP thật — test dùng _FakeConnection, không spawn tiến trình. Đã smoke-test AppContext.build_mcp_tools() thật (không có server nào cấu hình → chỉ trả về ms365 local tools) nhưng chưa thử ensure/restart trên một server thật.
  • ui/folder_tab.py, ui/file_edit_dialog.py chưa được nối vào FileWorkspaceService (R06-T05) — dịch vụ tồn tại và có test unit đầy đủ, nhưng chưa xác nhận bằng cách chạy UI thật (đã mở app kiểm tra sau R05, nhưng không lặp lại cho R06's file explorer flow cụ thể).
  • Đã mở app thật 1 lần sau khi sửa ui/chat_panel.py (R06-T04) để xác nhận không crash lúc khởi động — chưa thử tay thao tác "đổi project giữa lúc chat đang trả lời" trên UI thật (chỉ verify bằng test offscreen).

9. Việc kế tiếp của Team Hoa

EPIC Nội dung Điều kiện
R07 (Scheduling & Workflow Runtime) Tách TaskRepository/ScheduleCalculator khỏi QTimer (core/task_scheduler.py), xây TaskApplicationService Phối hợp 🟣 Team Nam (Co4E Workflows)
R08 (T01 ➔ ...) Phần Team Hoa trong tách UI (ui/workspace_tab.py, ui/folder_tab.py, ui/schedule_task_tab.py, ui/dashboard_tab.py, Graph) Chờ R07
Nối WorkspaceRepository/ConversationRepository/FileWorkspaceService vào call site thật Xem mục 7 #3 Có thể làm sớm hơn R07/R08 nếu được yêu cầu

10. Lịch sử commit

Commit Nội dung
ae4fe72 feat(R05): tool capability registry, unified policy gateway, MCP lifecycle manager
cf542b7 feat(R06): workspace session snapshot, atomic persistence, history-dir race fix