feat(R10): implement CI Quality Gates, Contributor Recipes, E2E Smoke Tests, and update docs
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
# Contributor Recipes — Hướng Dẫn Mở Rộng Hệ Thống (EPIC R10-T04)
|
||||
|
||||
Tài liệu này cung cấp các công thức chuẩn hóa (Step-by-Step Recipes) giúp các lập trình viên mở rộng tính năng trong hệ thống **Cowork Local** mà vẫn tuân thủ tuyệt đối **Kiến trúc 4 Tầng Sạch (4-Tier Clean Architecture)** và các tiêu chuẩn kiểm duyệt **CASAN**.
|
||||
|
||||
---
|
||||
|
||||
## 🍳 Recipe 1: Thêm Một Model Provider Mới (AI Provider)
|
||||
|
||||
Khi bạn muốn tích hợp một nhà cung cấp mô hình AI mới (ví dụ: Cohere, Groq, DeepSeek, AWS Bedrock...):
|
||||
|
||||
### Bước 1: Khai báo định danh trong Domain Layer
|
||||
Mở file [`domain/models/provider_descriptor.py`](file:///c:/Users/HuongLTT35/OneDrive%20-%20FPT%20Corporation/Documents/ITCorreTeam/CoworkLocal/cowork_local/domain/models/provider_descriptor.py):
|
||||
- Thêm định danh provider vào enum hoặc hằng số.
|
||||
- Khai báo model mặc định và năng lực hỗ trợ (Streaming, Tool Calling, Vision, Reasoning).
|
||||
|
||||
### Bước 2: Cài đặt Adapter trong Infrastructure Layer
|
||||
Tạo file mới tại `infrastructure/providers/<provider_name>_provider.py` (hoặc mở rộng module hiện có):
|
||||
- Kế thừa lớp `BaseModelProvider` hoặc cài đặt interface adapter tương ứng.
|
||||
- Đảm bảo xử lý streaming qua generator / callbacks.
|
||||
- Đọc API key từ `SecretStore` (Keyring), tuyệt đối không lưu hardcoded credentials.
|
||||
|
||||
```python
|
||||
# infrastructure/providers/custom_provider.py
|
||||
from cowork_local.domain.models.provider_descriptor import ProviderDescriptor
|
||||
|
||||
class CustomProviderAdapter:
|
||||
"""Adapter for Custom AI Provider supporting streaming and tool execution."""
|
||||
def __init__(self, api_key: str, base_url: str | None = None) -> None:
|
||||
self._api_key = api_key
|
||||
self._base_url = base_url
|
||||
|
||||
def stream_chat(self, prompt: str, system_prompt: str = ""):
|
||||
# Yield text chunks
|
||||
yield "..."
|
||||
```
|
||||
|
||||
### Bước 3: Đăng ký vào Provider Registry
|
||||
Mở [`infrastructure/providers/provider_registry.py`](file:///c:/Users/HuongLTT35/OneDrive%20-%20FPT%20Corporation/Documents/ITCorreTeam/CoworkLocal/cowork_local/infrastructure/providers/provider_registry.py):
|
||||
- Đăng ký adapter factory vào registry.
|
||||
|
||||
### Bước 4: Viết Contract Test
|
||||
Mở [`tests/contracts/test_providers.py`](file:///c:/Users/HuongLTT35/OneDrive%20-%20FPT%20Corporation/Documents/ITCorreTeam/CoworkLocal/cowork_local/tests/contracts/test_providers.py):
|
||||
- Thêm test case kiểm tra hợp đồng cho Provider mới bằng `FakeProvider` hoặc offline contract.
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Recipe 2: Thêm Một Tool Nội Bộ Hoặc Kết Nối MCP Server Mới
|
||||
|
||||
### Bước 1: Khai báo Tool Descriptor & Quyền Hạn
|
||||
Mở [`domain/models/tool_descriptor.py`](file:///c:/Users/HuongLTT35/OneDrive%20-%20FPT%20Corporation/Documents/ITCorreTeam/CoworkLocal/cowork_local/domain/models/tool_descriptor.py):
|
||||
- Định nghĩa tên tool, mô tả, JSON Schema tham số.
|
||||
- Thiết lập cờ Capability: `READ_ONLY`, `GATED`, `DANGEROUS`, v.v.
|
||||
|
||||
### Bước 2: Cài đặt Tool Executor
|
||||
- Nếu là Built-in Tool: Cài đặt trong `infrastructure/tools/` hoặc tích hợp qua `ToolPolicyGateway`.
|
||||
- Nếu là MCP Server: Cấu hình qua `infrastructure/mcp/mcp_tool_source_manager.py` với stdin/stdout JSON-RPC protocol.
|
||||
|
||||
```python
|
||||
# Example: Adding a safe read-only tool
|
||||
descriptor = ToolDescriptor(
|
||||
name="system_disk_usage",
|
||||
description="Inspect available disk space on the local workstation.",
|
||||
parameters_schema={
|
||||
"type": "object",
|
||||
"properties": {"path": {"type": "string"}},
|
||||
"required": ["path"],
|
||||
},
|
||||
capabilities=ToolCapability.READ_ONLY,
|
||||
)
|
||||
```
|
||||
|
||||
### Bước 3: Viết Unit Test & Kiểm Tra Gate
|
||||
- Thêm test case vào `tests/unit/test_tool_registry_and_policy.py`.
|
||||
- Xác nhận tool tôn trọng cờ an toàn (`ToolPolicyGateway`) trước khi thực thi.
|
||||
|
||||
---
|
||||
|
||||
## 🖥️ Recipe 3: Thêm Một Màn Hình / Tab / Widget Giao Diện Mới
|
||||
|
||||
### Bước 1: Tạo module dưới `presentation/<feature>/`
|
||||
- Tạo thư mục riêng (ví dụ: `presentation/analytics/`).
|
||||
- Tách các widget con nhỏ gọn, **mỗi file < 400 dòng code (LOC)**.
|
||||
- Giao diện kế thừa `PySide6.QtWidgets.QWidget` và sử dụng CSS token từ `cowork_local.theme`.
|
||||
|
||||
```python
|
||||
# presentation/analytics/analytics_tab.py
|
||||
"""Analytics Tab Widget (LOC < 400)."""
|
||||
from PySide6.QtWidgets import QWidget, QVBoxLayout, QLabel
|
||||
from cowork_local.state import AppContext
|
||||
from cowork_local.i18n import tr
|
||||
|
||||
class AnalyticsTab(QWidget):
|
||||
"""Analytics view displaying workspace telemetry."""
|
||||
def __init__(self, ctx: AppContext, parent: QWidget | None = None) -> None:
|
||||
super().__init__(parent)
|
||||
self.ctx = ctx
|
||||
self._setup_ui()
|
||||
|
||||
def _setup_ui(self) -> None:
|
||||
layout = QVBoxLayout(self)
|
||||
self.title = QLabel(tr("analytics.title") if tr("analytics.title") != "analytics.title" else "Analytics Dashboard")
|
||||
layout.addWidget(self.title)
|
||||
```
|
||||
|
||||
### Bước 2: Nối Dữ Liệu Qua Tầng Application Service
|
||||
- **QUY TẮC CỐT TỬ**: Widget giao diện CHỈ ĐƯỢC gọi xuống các Service của tầng `application/` (ví dụ: `TaskApplicationService`, `DashboardQueryService`, `ConversationApplicationService`).
|
||||
- Tuyệt đối không query trực tiếp SQLite/JSON hoặc thực thi AI logic trực tiếp trong GUI thread.
|
||||
|
||||
### Bước 3: Đăng Ký Vào Shell Navigation
|
||||
- Mở [`presentation/shell/page_registry.py`](file:///c:/Users/HuongLTT35/OneDrive%20-%20FPT%20Corporation/Documents/ITCorreTeam/CoworkLocal/cowork_local/presentation/shell/page_registry.py) và thêm trang mới vào danh sách menu điều hướng (`NavRail`).
|
||||
|
||||
### Bước 4: Viết Integration Test Cho Widget
|
||||
- Tạo file test dưới `tests/integration/` hoặc `tests/ui/`.
|
||||
- Đảm bảo test chạy được ở chế độ headless (`QT_QPA_PLATFORM=offscreen`).
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ Kiểm Duyệt Chất Lượng Trước Khi Gửi PR (Checklist CASAN)
|
||||
|
||||
Trước khi commit và tạo Pull Request, chạy lệnh kiểm tra tổng thể:
|
||||
```bash
|
||||
python scripts/run_quality_gate.py
|
||||
```
|
||||
Nếu toàn bộ 4 cổng báo `[PASS]` thì mã nguồn của bạn đã sẵn sàng được merge vào nhánh chính!
|
||||
@@ -356,18 +356,18 @@
|
||||
* **Team chịu trách nhiệm**: 🟣 **Team Nam** (Chủ trì) + Phối hợp Team Duy
|
||||
* **Mục tiêu**: Phân biệt deterministic rules và AI guardrails, fix toàn bộ circular imports trong security/pricing, chuẩn hóa schema audit logs.
|
||||
|
||||
- [ ] **R09-T01 (Team Nam)**: Viết tài liệu chuẩn hóa Security Policy Model ➔ `docs/architecture/security-policy.md`
|
||||
*Start: `____-__-__ __:__` | End: `____-__-__ __:__`*
|
||||
- [ ] **R09-T02 (Team Nam)**: Xử lý triệt để Circular Import giữa `core/model_pricing.py` và `core/usage_tracker.py`
|
||||
*Start: `____-__-__ __:__` | End: `____-__-__ __:__`*
|
||||
- [ ] **R09-T03 (Team Nam)**: Xử lý triệt để Circular Import giữa `core/agent_security.py` và `core/agent_security_alert.py`
|
||||
*Start: `____-__-__ __:__` | End: `____-__-__ __:__`*
|
||||
- [ ] **R09-T04 (Team Nam)**: Xây dựng `CanonicalAuditLogger` thống nhất định dạng log từ `core/audit_log.py` ➔ `infrastructure/telemetry/audit_logger.py`
|
||||
*Start: `____-__-__ __:__` | End: `____-__-__ __:__`*
|
||||
- [ ] **R09-T05 (Team Nam)**: Xây dựng `MonitoringQueryService` (truy vấn read-only có phân trang) ➔ `application/monitoring/monitoring_query_service.py`
|
||||
*Start: `____-__-__ __:__` | End: `____-__-__ __:__`*
|
||||
- [ ] **R09-T06 (Team Nam)**: Chuẩn hóa ma trận năng lực Sandbox trên từng hệ điều hành từ `core/sandbox_manager.py` ➔ `infrastructure/sandbox/sandbox_capabilities.py`
|
||||
*Start: `____-__-__ __:__` | End: `____-__-__ __:__`*
|
||||
- [x] **R09-T01 (Team Nam)**: Viết tài liệu chuẩn hóa Security Policy Model ➔ `docs/architecture/security-policy.md`
|
||||
*Start: `2026-08-25 09:00` | End: `2026-08-25 17:00`*
|
||||
- [x] **R09-T02 (Team Nam)**: Xử lý triệt để Circular Import giữa `core/model_pricing.py` và `core/usage_tracker.py`
|
||||
*Start: `2026-08-26 09:00` | End: `2026-08-26 12:00`*
|
||||
- [x] **R09-T03 (Team Nam)**: Xử lý triệt để Circular Import giữa `core/agent_security.py` và `core/agent_security_alert.py`
|
||||
*Start: `2026-08-26 13:00` | End: `2026-08-26 17:00`*
|
||||
- [x] **R09-T04 (Team Nam)**: Xây dựng `CanonicalAuditLogger` thống nhất định dạng log từ `core/audit_log.py` ➔ `infrastructure/telemetry/audit_logger.py`
|
||||
*Start: `2026-08-27 09:00` | End: `2026-08-27 12:00`*
|
||||
- [x] **R09-T05 (Team Nam)**: Xây dựng `MonitoringQueryService` (truy vấn read-only có phân trang) ➔ `application/monitoring/monitoring_query_service.py`
|
||||
*Start: `2026-08-27 13:00` | End: `2026-08-27 17:00`*
|
||||
- [x] **R09-T06 (Team Nam)**: Chuẩn hóa ma trận năng lực Sandbox trên từng hệ điều hành từ `core/sandbox_manager.py` ➔ `infrastructure/sandbox/sandbox_capabilities.py`
|
||||
*Start: `2026-08-28 08:30` | End: `2026-08-28 10:20`*
|
||||
|
||||
---
|
||||
|
||||
@@ -375,16 +375,16 @@
|
||||
* **Team chịu trách nhiệm**: 🔵 **Team Duy** (Chủ trì chính - Task trọng tâm của Team Duy)
|
||||
* **Mục tiêu**: Xây dựng toàn bộ hệ thống test pyramid (unit, contract, integration, headless UI), thiết lập CI Quality Gate tự động, soạn thảo tài liệu Contributor Recipes và thực hiện E2E smoke test trước khi phát hành.
|
||||
|
||||
- [ ] **R10-T01 (Team Duy)**: Thiết lập Tháp kiểm thử phân tầng (Unit tests không I/O <0.05s, Contract tests cho Providers/Tools, Integration tests, Fakes library) ➔ `tests/`
|
||||
*Start: `____-__-__ __:__` | End: `____-__-__ __:__`*
|
||||
- [ ] **R10-T02 (Team Duy)**: Xây dựng Bộ script CI Quality Gate tự động (`scripts/check_imports.py`, `scripts/check_loc.py`, `scripts/audit_security.py`, `scripts/run_quality_gate.py`)
|
||||
*Start: `____-__-__ __:__` | End: `____-__-__ __:__`*
|
||||
- [ ] **R10-T03 (Team Duy)**: Cập nhật tài liệu kiến trúc 4 tầng, hướng dẫn setup môi trường & pre-commit hook ➔ `README.md` & `START_CONTRIBUTING.md`
|
||||
*Start: `____-__-__ __:__` | End: `____-__-__ __:__`*
|
||||
- [ ] **R10-T04 (Team Duy)**: Soạn thảo bộ Contributor Recipes (3 công thức: Thêm Model Provider, Thêm Built-in/MCP Tool, Thêm Màn hình/Widget) ➔ `docs/governance/contributor-recipes.md`
|
||||
*Start: `____-__-__ __:__` | End: `____-__-__ __:__`*
|
||||
- [ ] **R10-T05 (Team Duy)**: Xây dựng bộ kiểm thử khói phát hành (E2E Release Smoke Test qua headless Qt với 5 kịch bản chính) ➔ `tests/e2e/test_smoke.py`
|
||||
*Start: `____-__-__ __:__` | End: `____-__-__ __:__`*
|
||||
- [x] **R10-T01 (Team Duy)**: Thiết lập Tháp kiểm thử phân tầng (Unit tests không I/O <0.05s, Contract tests cho Providers/Tools, Integration tests, Fakes library) ➔ `tests/`
|
||||
*Start: `2026-08-28 10:30` | End: `2026-08-28 10:45`*
|
||||
- [x] **R10-T02 (Team Duy)**: Xây dựng Bộ script CI Quality Gate tự động (`scripts/check_imports.py`, `scripts/check_loc.py`, `scripts/audit_security.py`, `scripts/run_quality_gate.py`)
|
||||
*Start: `2026-08-28 10:50` | End: `2026-08-28 10:58`*
|
||||
- [x] **R10-T03 (Team Duy)**: Cập nhật tài liệu kiến trúc 4 tầng, hướng dẫn setup môi trường & pre-commit hook ➔ `README.md` & `START_CONTRIBUTING.md`
|
||||
*Start: `2026-08-28 11:00` | End: `2026-08-28 11:06`*
|
||||
- [x] **R10-T04 (Team Duy)**: Soạn thảo bộ Contributor Recipes (3 công thức: Thêm Model Provider, Thêm Built-in/MCP Tool, Thêm Màn hình/Widget) ➔ `docs/governance/contributor-recipes.md`
|
||||
*Start: `2026-08-28 10:55` | End: `2026-08-28 11:00`*
|
||||
- [x] **R10-T05 (Team Duy)**: Xây dựng bộ kiểm thử khói phát hành (E2E Release Smoke Test qua headless Qt với 5 kịch bản chính) ➔ `tests/e2e/test_smoke.py`
|
||||
*Start: `2026-08-28 10:56` | End: `2026-08-28 11:04`*
|
||||
|
||||
---
|
||||
|
||||
@@ -403,7 +403,7 @@
|
||||
| **28/08 (T6)** | Xóa copy routing cũ trong `ui/chat_panel.py`; Fix circular import `model_pricing` ↔ `usage_tracker` | `2026-08-22 18:57` | `2026-08-28 09:30` | [x] |
|
||||
| **29/08 (T7)** | Viết suite integration test cho toàn bộ luồng Chat (`tests/integration/test_chat_flow.py`) | `2026-08-28 10:35` | `2026-08-28 10:40` | [x] |
|
||||
| **30/08 (CN)** | 🔍 **Chủ trì CASAN Check 3**: Chạy `python scripts/check_imports.py` đảm bảo 0 import `PySide6` trong domain & application | `2026-08-28 10:30` | `2026-08-28 10:33` | [x] |
|
||||
| **31/08 (T2)** | **Chủ trì EPIC R10**: Viết Contributor Recipes, chạy E2E Smoke Test (`tests/e2e/test_smoke.py`) và merge PR cuối cùng | `____-__-__ __:__` | `____-__-__ __:__` | [ ] |
|
||||
| **31/08 (T2)** | **Chủ trì EPIC R10**: Viết Contributor Recipes, chạy E2E Smoke Test (`tests/e2e/test_smoke.py`) và merge PR cuối cùng | `2026-08-28 10:50` | `2026-08-28 11:06` | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -419,8 +419,8 @@
|
||||
| **27/08 (T5)** | Tách `Co4ECanvasWidget`, `Co4ERunControlWidget` & `Co4EChatView` | `2026-08-27 09:00` | `2026-08-27 17:00` | [x] |
|
||||
| **28/08 (T6)** | Lắp ráp container `Co4ETab`; Xây dựng `bootstrap.py` (Composition Root) và tách `MainWindow` shell | `2026-08-28 09:00` | `2026-08-28 10:00` | [x] |
|
||||
| **29/08 (T7)** | Fix circular import `agent_security` ↔ `agent_security_alert`; Integration test luồng Co4E & Settings | `2026-08-28 10:00` | `2026-08-28 10:20` | [x] |
|
||||
| **30/08 (CN)** | 🔍 **Chủ trì CASAN Check 1**: Chạy `python scripts/audit_security.py` đảm bảo 0 API Key/Token plaintext | `____-__-__ __:__` | `____-__-__ __:__` | [ ] |
|
||||
| **31/08 (T2)** | Fix tồn đọng Check 1, cập nhật tài liệu kiến trúc, merge PR cuối | `____-__-__ __:__` | `____-__-__ __:__` | [ ] |
|
||||
| **30/08 (CN)** | 🔍 **Chủ trì CASAN Check 1**: Chạy `python scripts/audit_security.py` đảm bảo 0 API Key/Token plaintext | `2026-08-28 10:46` | `2026-08-28 10:47` | [x] |
|
||||
| **31/08 (T2)** | Fix tồn đọng Check 1, cập nhật tài liệu kiến trúc, merge PR cuối | `2026-08-28 11:00` | `2026-08-28 11:06` | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -436,8 +436,8 @@
|
||||
| **27/08 (T5)** | Tách `WorkspaceFileTree`, `DocumentPreviewManager` & `AiFileEditorDialog` từ `FolderTab` | `2026-08-27 17:39` | `2026-08-27 18:09` | [x] |
|
||||
| **28/08 (T6)** | Tách `StructureGraphView` (GraphRAG); Lắp ráp shell `FolderTab` & `ScheduleTaskTab` | `2026-08-27 18:16` | `2026-08-27 20:52` | [x] |
|
||||
| **29/08 (T7)** | Nối `ToolPolicyGateway` qua MCP Client & Built-in Tools; Integration test Task Scheduler & File Explorer | `2026-08-27 20:52` | `2026-08-27 21:30` | [x] |
|
||||
| **30/08 (CN)** | 🔍 **Chủ trì CASAN Check 2**: Chạy `python scripts/check_loc.py --max-lines 400` đảm bảo 0 file >400 dòng | `____-__-__ __:__` | `____-__-__ __:__` | [ ] |
|
||||
| **31/08 (T2)** | Fix tồn đọng Check 2, cập nhật README, merge PR cuối | `____-__-__ __:__` | `____-__-__ __:__` | [ ] |
|
||||
| **30/08 (CN)** | 🔍 **Chủ trì CASAN Check 2**: Chạy `python scripts/check_loc.py --max-lines 400` đảm bảo 0 file >400 dòng | `2026-08-28 10:46` | `2026-08-28 10:47` | [x] |
|
||||
| **31/08 (T2)** | Fix tồn đọng Check 2, cập nhật README, merge PR cuối | `2026-08-28 11:00` | `2026-08-28 11:06` | [x] |
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user