## Summary epic r04 - begin refactor ## Change Type - [x] Cowork feature - [ ] Bug fix - [ ] Core AI contribution - [ ] Test / hardening - [ ] Performance - [ ] Documentation ## Related Work Cowork Task: Core Repo: http://34.143.229.138/gitea-admin/fsg-ai-core-assets Core AI Issue: Core Task: Related PR: ## Scope What is intentionally included? What is intentionally NOT included? ## Validation - [ ] Unit tests - [ ] Integration tests - [ ] Manual verification - [ ] Regression check Commands / evidence: ## Security Impact Permission / credential / network / customer data impact: ## Compatibility - [ ] No breaking change - [ ] Breaking change documented ## Reviewer Notes Anything Cowork reviewers should pay attention to. --------- Co-authored-by: Anh Tran Nguyen Minh <anhtnm1@fpt.com> Co-authored-by: Huong Le Thi Thien <huongltt35@fpt.com> Co-authored-by: Nam Pham Dinh Thanh <nampdt@fpt.com> Co-authored-by: Vu Dam Tuan <vudt15@fpt.com> Co-authored-by: Hiep Ha Van <hiephv3@fpt.com> Co-authored-by: Lam Hoang Van <lamhv7@fpt.com> Reviewed-on: #7 Co-authored-by: Duy Le Huu <duylh19@fpt.com>
20 KiB
BÁO CÁO KẾT QUẢ — TEAM HOA: EPIC R05, R06, R07, R08 (phần Team Hoa)
- Dự án: Cowork Local (Cowork-Local BamBOO)
- Team: 🟢 Team Hoa — Workspace, Filesystem, Scheduling & Tool Registry
- Nhánh:
feature/teamhoa/r05-r08(tạo từorigin/feature/deltateam/refactor-plan, có sẵn nền R01/R03/R04 của Team Duy; đổi tên từfeature/teamhoa/r05-r06sau khi gộp thêm R07/R08) - Thời gian thực hiện: 21/08/2026 21:40 → 27/08/2026 20:52
- Ngày báo cáo: 27/08/2026 (bản gộp, thay thế
BaoCao_TeamHoa_R05_R06.mdvàBaoCao_TeamHoa_R07_R08.md) - 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 toàn bộ 19/19 task thuộc phạm vi Team Hoa trên 4 EPIC: R05 (Tool, MCP & Connector Policy), R06 (Workspace, Filesystem & History Isolation), R07 (Scheduling & Workflow Runtime), R08 (UI/Application Separation — phần Team Hoa, T11→T14).
| Chỉ số | Kết quả |
|---|---|
| Task hoàn thành | 19/19 (R05: 5, R06: 5, R07: 5, R08: 4 — không tính R07-T06/R08-T01→T10 thuộc Team Duy/Team Nam) |
| File thay đổi | 92+ (phần lớn file mới) |
| Test cuối cùng | 377 pass / 4 fail (xem tiến trình chi tiết ở mục 4) |
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 presentation/graph/graph_renderer.py 391 dòng |
python -c "import cowork_local.app" |
OK sau mọi task |
3 lỗi thật phát hiện và sửa, 1 quyết định kiến trúc đổi so với plan gốc (xác nhận bằng thực nghiệm) — chi tiết mục 5.
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/T02 | domain/tools/{tool_descriptor,tool_registry}.py, infrastructure/filesystem/{file_tools,command_tools,fetch_tools}.py |
Tách if/elif dispatcher của core/tools.py; core/tools.py còn lại là shim strangler-fig (re-export ToolContext/ToolError, dispatch qua dict), 566 ➔ 291 dòng. |
| R05-T03 | application/conversations/tool_policy_gateway.py::ToolPolicyGateway |
Thay 2 chỗ check hardcode riêng biệt (chat_agent.py, code_agent.py) bằng 1 lookup capability chung. |
| 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::McpToolSourceManager |
Tách lifecycle connection MCP khỏi state.py::AppContext. |
🔹 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). |
| 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::ExecutionWorkspace |
Đặt tên cho quy ước .scratch đã có sẵn. |
| 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::FileWorkspaceService |
Seam cho File Explorer/AI Editor gọi execute_tool giống agent — chưa có call site thật lúc R06 xong; đã nối dây ở R08-T12 (xem mục 5). |
🔹 EPIC R07 — Scheduling & Workflow Runtime (5/5, phạm vi Team Hoa)
| Task | Sản phẩm | Ghi chú |
|---|---|---|
| R07-T01 | infrastructure/persistence/json/task_repository_impl.py::TaskRepository |
Bọc CRUD của core/tasks.py. Sửa bug thật: save_task trước đây ghi không atomic — cùng lớp bug đã sửa ở R06-T02. |
| R07-T02 | domain/tasks/schedule_calculator.py::ScheduleCalculator |
Tách "schedule math" (cron/interval/daily/weekly/monthly + holiday exclusion) thành pure Python, trước đây 0 test, giờ có 14 test. |
| R07-T03 | infrastructure/qt/qt_scheduler_clock.py::QtSchedulerClock |
Bọc QTimer sau 1 interface nhỏ, inject qua clock=. Đổi vị trí so với plan gốc — xem mục 5. |
| R07-T04 | application/scheduling/task_application_service.py::TaskApplicationService |
Gom CRUD + luật kéo-thả Kanban (move_to_status). |
| R07-T05 | application/scheduling/ai_task_planner_service.py::AiTaskPlannerService |
Seam cho plan_tasks/import_tasks. |
(R07-T06 Co4EWorkflowService là việc Team Nam — không đụng.)
🔹 EPIC R08 — UI/Application Separation (4/4, phạm vi Team Hoa: T11→T14)
presentation/ chưa tồn tại trước task này — Team Hoa tạo cấu trúc lần đầu.
| Task | God file gốc | Tách thành |
|---|---|---|
| R08-T11 | ui/schedule_task_tab.py (795 dòng) |
presentation/scheduling/{kanban_board_widget,calendar_view_widget,ai_task_creator_dialog,ai_task_import_dialog,run_history_dialog}.py + shell |
| R08-T12 | ui/folder_tab.py (1587 dòng — lớn nhất) |
presentation/folder/{workspace_file_tree,document_preview_manager,code_editor,office_document_renderer,ai_file_editor_dialog,ai_edit_model_resolver,ai_edit_pipeline}.py + shell |
| R08-T13 | ui/dashboard_tab.py (437 dòng) |
presentation/dashboard/{token_usage_card_widget,usage_chart_widget,habits_widget}.py + shell, application/monitoring/dashboard_query_service.py |
| R08-T14 | ui/structure_graph_view.py (1035 dòng) |
presentation/graph/{graph_scene_items,graph_renderer,graph_messages_view,graph_qa_widget}.py + shell |
(R08-T01→T10 thuộc Team Duy/Team Nam — không đụng.) Mỗi god-file cũ chỉ có 1-2 nơi khởi tạo thật (app.py, ui/workspace_tab.py) nên đã sửa thẳng import site và xoá hẳn file ui/*.py cũ thay vì giữ shim (khác core/tools.py ở R05, có hàng chục call site).
3. Kiến trúc sau refactor
presentation/ (MỚI ở R08 — Team Hoa tạo cấu trúc lần đầu)
scheduling/ {kanban_board_widget, calendar_view_widget,
ai_task_creator_dialog, ai_task_import_dialog,
run_history_dialog, schedule_task_tab}.py
folder/ {workspace_file_tree, document_preview_manager, code_editor,
office_document_renderer, ai_file_editor_dialog,
ai_edit_model_resolver, ai_edit_pipeline, folder_tab}.py
dashboard/ {token_usage_card_widget, usage_chart_widget,
habits_widget, dashboard_tab}.py
graph/ {graph_scene_items, graph_renderer, graph_messages_view,
graph_qa_widget, structure_graph_view}.py
shared/ web_engine_support.py (HAS_WEB_ENGINE dùng chung)
│
▼
application/ conversations/tool_policy_gateway.py ← ALLOW/CONFIRM cho mọi tool call (R05)
workspaces/{file_workspace_service, file_preview_helpers,
ai_edit_output, graph_index_service}.py (R06, R08)
scheduling/{task_application_service, ai_task_planner_service}.py (R07)
monitoring/dashboard_query_service.py (R08)
│ (100% pure Python — check_imports.py chặn import Qt)
▼
domain/ tools/{tool_descriptor,tool_registry}.py ← capability + catalogue (R05)
workspaces/workspace_session.py ← snapshot workspace bất biến (R06)
tasks/schedule_calculator.py ← due-time/cron math thuần Python (R07)
▲
infrastructure/ filesystem/{file_tools,command_tools,fetch_tools,tool_context,execution_workspace}.py (R05/R06)
mcp/mcp_source_manager.py ← lifecycle connection MCP (R05)
persistence/json/{atomic_write,workspace_repository_impl,
conversation_repository_impl,task_repository_impl}.py (R06/R07)
qt/qt_scheduler_clock.py ← QTimer đằng sau 1 interface nhỏ (R07)
Nguyên tắc di trú xuyên suốt cả 4 EPIC (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/tasks.py, core/task_scheduler.py, core/task_executors.py vẫn là engine bên dưới — tầng mới chỉ sở hữu phần từng nằm rải rác/hardcode trong widget hoặc dispatcher. pytest xanh liên tục giữa các bước, chạy full suite sau MỖI task.
Riêng ở R08: khi 1 file bị tách vượt 400 dòng dù đã theo đúng mapping gốc, đã tách thêm file phụ theo kiểu composition (class phụ nhận owner là widget chính) thay vì service riêng — ví dụ run_history_dialog.py, office_document_renderer.py, ai_edit_pipeline.py, ai_edit_model_resolver.py, graph_messages_view.py. Đây là split kỹ thuật để đạt giới hạn LOC, không phải ranh giới tầng kiến trúc.
4. Bằng chứng kiểm thử
Tiến trình test qua từng EPIC
| Mốc | Tổng pass | Ghi chú |
|---|---|---|
| Sau R05+R06 | 283 (/287, 4 fail) | +41 unit test + 2 integration test (Qt offscreen thật) |
| Sau R07 | 328 | +45 test mới (task repo, schedule calculator, Qt clock, task/AI-planner services) |
| Sau R08 | 377 | +49 test mới (4 widget split, mỗi cái có unit + integration Qt offscreen) |
4 fail cuối cùng — cùng 1 baseline có sẵn từ trước, xuyên suốt cả 4 EPIC, không phải do Team Hoa:
tests/test_config_security.py× 2 (EPIC R02/Team Nam —config.pyhardcodesandbox_pw)tests/unit/test_routing_wiring.py× 2 (môi trường máy này có Ollama/llama3.1 thật, khác giả định "fresh install" của test)
Đối chiếu Definition of Done
| # | Tiêu chí | Kết quả |
|---|---|---|
| 1 | Mọi file mới < 400 dòng | ✅ Lớn nhất: presentation/graph/graph_renderer.py 391 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 | ✅ +90 test mới sau R05/R06 lên tới 377; mọi widget Qt test bằng offscreen thật, không double |
| 5 | Không hồi quy | ✅ 377/381 pass — 4 fail cùng 1 baseline có sẵn, không đổi qua 4 EPIC |
| 6 | Ghi Start/End vào Checklist | ✅ 19 task đã tick kèm mốc thời gian thật |
| 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. Lỗi thật phát hiện & quyết định kiến trúc
🔴 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: built-in đ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) gọi thẳng extra_executor(name, args) không qua bước xác nhận nào. Đây không phải khác biệt thiết kế — không có ghi chú, không có toggle riêng.
Sửa: mọi extra_tools gắn ToolCapability mặc định bảo toàn (WRITE|EXECUTE|NETWORK), đi qua CÙNG ToolPolicyGateway với built-in tools. Thay đổi hành vi người dùng sẽ thấy: tool MCP/connector giờ hỏi xác nhận khi "confirm before running commands" bật. Test: tests/unit/test_cowork_extra_tool_policy.py.
🟠 Lỗi 2 (R06-T02, R07-T01) — Ghi file không atomic ở 3 nơi
core/projects.py::save_project, core/history.py::save_conversation/rename_conversation/set_pinned (R06), và core/tasks.py::save_task (R07) đều từng dùng path.write_text(json.dumps(...)) trần — crash giữa lúc ghi để lại file JSON hỏng, và hàm load_* tương ứng coi file hỏng như "không tồn tại" → mất project/hội thoại/task âm thầm, không báo lỗi. Cả 4 điểm ghi giờ qua infrastructure/persistence/json/atomic_write.py::write_json (temp file + os.replace). Có test giả lập crash giữa lúc ghi xác nhận file cũ không bị hỏng, cho cả 2 đợt sửa.
🟡 Lỗi 3 (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 gọi save_conversation(self.ctx.config.history_dir(), ...), đọc config._project_history_dir — field dùng chung bị ui/workspace_tab.py::_load_current ghi đè mỗi lần đổi project. Một turn chạy ngầm ở project A hoàn tất SAU khi user đã chuyển sang project B thì bị lưu nhầm vào lịch sử của B.
Sửa: thêm "home_history_dir" vào dict ctx mỗi turn, chụp giá trị tại lúc submit thay vì đọc sống lúc lưu. Test Qt offscreen thật: tests/integration/test_history_dir_race.py.
🔵 Quyết định kiến trúc (R07-T03) — platform/qt/ đè lên module chuẩn platform của Python
Plan gốc đặt tên platform/qt/qt_scheduler_clock.py. Thực nghiệm trước khi viết:
cd <repo_root> && python -c "import cowork_local; import platform; print(platform.system())"
Sau khi tạo platform/__init__.py, lệnh trên báo lỗi AttributeError: module 'platform' has no attribute 'system' — bất cứ khi nào repo root nằm trực tiếp trên sys.path (không qua __main__.py's parent-dir fixup), import platform phân giải nhầm vào package cục bộ. core/windows_sandbox_vm.py/core/appcontainer_sandbox.py đều import platform.
Sửa: chuyển sang infrastructure/qt/qt_scheduler_clock.py (không tạo package platform/ mới) — đúng layer, cùng cấp infrastructure/{filesystem,mcp,persistence,providers,telemetry}/. Verify lại: PASS.
Nối dây FileWorkspaceService (R06-T05 → R08-T12)
Báo cáo R06 để lại nợ: FileWorkspaceService (R06-T05) chưa có call site thật. Xác nhận lại bằng grep trước R08-T12: đúng 0 occurrence trong ui/folder_tab.py. Khi tách document_preview_manager.py (R08-T12), mọi điểm ghi text thuần (save, create_new_file, write_content) chuyển sang gọi FileWorkspaceService.write_file — dùng WorkspaceSession.unscoped(...) vì Folder Explorer duyệt bất kỳ thư mục nào, không giới hạn 1 project sandbox. Nhánh ghi .pptx (binary) vẫn giữ nguyên đường cũ.
Tác dụng phụ có lợi: infrastructure/filesystem/file_tools.py::write_file đã có sẵn cảnh báo cú pháp Python và tự build .xlsx thật từ text — 2 hành vi này trước đây không tồn tại trên đường ghi cũ của folder_tab.py, giờ được hưởng miễn phí.
6. Cải thiện phụ (không nằm trong yêu cầu task)
| Cải thiện | Ảnh hưởng |
|---|---|
McpServerConnection.is_alive() (R05, core/mcp_client.py) |
Cho McpToolSourceManager biết một connection cached đã chết để khởi động lại. |
domain/tasks/schedule_calculator.py có bộ test riêng (R07) |
core/tasks.py tự nhận "Qt-free, unit-testable" nhưng 0 test tồn tại cho cron/interval/holiday-exclusion trước R07-T02. Giờ 14 test. |
AiEditModelResolver.routed_provider/.routed_model (R08-T12, property public mới) |
Cần thêm để giữ tests/integration/test_routing_surfaces.py's 2 test AI-Edit routing sau khi lớp routing chuyển từ FolderTab sang AiEditModelResolver — tránh hồi quy 1 test có từ EPIC R03. |
7. Còn nợ & cần quyết định
| # | Nội dung | Người quyết |
|---|---|---|
| 1 | Xung đột quy hoạch thư mục infrastructure/persistence/json/atomic_write.py (R06) với atomic_json_file.py do Team Nam quy hoạch ở R02-T01 — chưa có xung đột file thật, cần xác nhận hợp nhất hay giữ 2 module song song. |
Team Nam |
| 2 | Xung đột quy hoạch thư mục application/monitoring/ (R08-T13, dashboard_query_service.py) — quy hoạch cho Team Nam ở R08-T07→T10, nhưng plan gốc lại đặt file Dashboard vào đúng thư mục này. Chưa có xung đột file thật (thư mục trống trước đó). |
Team Nam |
| 3 | WorkspaceRepository/ConversationRepository (R06) vẫn chưa có call site sản xuất thật — R08-T12 chỉ nối FileWorkspaceService, chưa đụng 2 repository kia. |
Chưa có EPIC nào nhận |
| 4 | AI-Edit pipeline (ai_edit_pipeline.py) và Graph Q&A ask-flow (graph_qa_widget.py::_ask) chưa có test end-to-end thật — cả 2 chạy trên AgentWorker (QThread) thật, và vốn dĩ đã không có test nào trước khi refactor (xác nhận bằng grep). |
Có thể thuộc phạm vi R10 Testing Pyramid |
| 5 | Dev tooling chưa cập nhật đường dẫn cũ: tools/check_controls_alive.py, tools/capture_screens.py, tools/build_audit_page.py, docs/screens/*.json, docs/ui-audit*.html vẫn tham chiếu ui/schedule_task_tab.py/ui/folder_tab.py/ui/dashboard_tab.py/ui/structure_graph_view.py (không còn tồn tại). Không nằm trong tests/, không ảnh hưởng CI. |
Chưa quyết định người phụ trách |
8. Phạm vi chưa kiểm thử
- R05-T04 (gate cho MCP/connector) chưa test với MCP server thật — dùng
ToolSpecgiả, chưa thửcore/mcp_client.py::McpServerConnectionchạy subprocess thật. McpToolSourceManager(R05-T05) chưa test với subprocess MCP thật — dùng_FakeConnection.presentation/folder/ai_edit_pipeline.py(toàn bộ luồng plan → edit → apply/discard quaAgentWorkerstreaming) — chỉ verify bằng import/construction, chưa gửi instruction qua worker thật.presentation/graph/graph_qa_widget.py::_ask— tương tự, chỉ test phần không cần AgentWorker thật.office_document_renderer.py's PDF/LibreOffice conversion path — chưa xác nhận kịch bản fallback trên máy không có QtPdf/LibreOffice bằng test thật.- Đã mở app thật bằng
python -c "import cowork_local.app"sau mỗi task để xác nhận không lỗi import — chưa mở app GUI thật, thao tác tay qua toàn bộ 4 màn hình đã tách để xác nhận trải nghiệm người dùng cuối.
9. Việc kế tiếp của Team Hoa
Toàn bộ 4 EPIC thuộc phạm vi Team Hoa (R05, R06, R07, R08 phần T11→T14) đã hoàn tất. Các bước còn lại không thuộc EPIC riêng của Team Hoa nữa:
| Việc | Điều kiện |
|---|---|
| Checkpoint 2 (Services & Sub-widgets, 28/08) | Cần Team Duy (R08-T01→T06) và Team Nam (R08-T07→T10) xong phần UI split của họ |
| CASAN Check 2 (Modularity/LOC, Team Hoa chủ trì, 30/08) | scripts/check_loc.py chưa tồn tại (thuộc R10-T02, Team Duy) |
| Giải quyết mục 7 #1, #2 | Khi Team Nam bắt đầu R02-T01 và R08-T07→T10 |
| R10 (Testing, Packaging & Contributor Experience) | Team Duy chủ trì, chờ 3 team hoàn tất |
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 |
69ab8e1 |
feat(R07): task repository, schedule calculator, Qt clock adapter, task/AI-planner services |
0e51356 |
feat(R08): split ScheduleTaskTab, FolderTab, DashboardTab, StructureGraphView |
| (gộp báo cáo) | docs(refactor): merge Team Hoa reports R05→R08 into one; rename branch to feature/teamhoa/r05-r08 |