docs(agent): bổ sung role fix-dispatcher và siết lại bộ tài liệu agent
- Thêm agent/roles/0_fix_dispatcher.md: phân tier/lane cho từng defect trước khi các agent khác chạy, kèm agent/commands/fix.md và hợp đồng đầu ra agent/output/dispatch_plan.md. - Cập nhật system/guardrail, response_policy, security và các checklist ui/ux/pr_readiness cho khớp luồng mới. - Mở rộng knowledge: i18n_rules, screen_map, theme_tokens, secrets_and_config; cập nhật workflow intake_to_fix và handoff_contract. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+144
-39
@@ -1,53 +1,158 @@
|
||||
# Checklist sẵn sàng tạo PR
|
||||
|
||||
Dùng bởi `fix-implementer` (bước 9) và `regression-reviewer` (bước 8).
|
||||
Bám theo `.gitea/PULL_REQUEST_TEMPLATE.md` và `docs/governance/definition-of-done.md`.
|
||||
Checklist này được sử dụng bởi:
|
||||
|
||||
## A. Cổng chất lượng
|
||||
* `fix-implementer` — kiểm tra ở bước 9.
|
||||
* `regression-reviewer` — kiểm tra ở bước 8.
|
||||
|
||||
- [ ] `python scripts/run_quality_gate.py` — xanh cả 5 cổng, **có dán output thật**.
|
||||
- [ ] Gate C: `domain/`/`application/` không import PySide6/PyQt/`ui`/`app`.
|
||||
- [ ] Gate A: không secret/plaintext mới.
|
||||
- [ ] Gate S: không file nào > 400 LOC.
|
||||
- [ ] Gate O: không module mồ côi (file mới đã được import trong cùng commit).
|
||||
- [ ] Gate A/N: pytest xanh; test vốn đỏ từ trước được ghi riêng.
|
||||
Tham chiếu:
|
||||
|
||||
## B. Kiểm chứng
|
||||
* `.gitea/PULL_REQUEST_TEMPLATE.md`
|
||||
* `docs/governance/definition-of-done.md`
|
||||
|
||||
- [ ] Test regression tồn tại và **đỏ trước / xanh sau**.
|
||||
- [ ] Test chạy được headless (`QT_QPA_PLATFORM=offscreen`).
|
||||
- [ ] Đã kiểm bằng mắt ở dark + light — hoặc ghi rõ "chưa kiểm chứng bằng mắt" kèm lý do.
|
||||
- [ ] Đã kiểm ở các ngôn ngữ liên quan.
|
||||
---
|
||||
|
||||
## C. Phạm vi & lịch sử
|
||||
## A. Kiểm tra chất lượng
|
||||
|
||||
- [ ] Một PR = một thay đổi logic. Không refactor lẫn vào.
|
||||
- [ ] Không đổi format/indent toàn file; diff đọc được.
|
||||
- [ ] Nhánh riêng, không commit thẳng `main`.
|
||||
- [ ] Commit message nêu nguyên nhân gốc + `file:line` + issue.
|
||||
- [ ] Không commit `.env`, `config.json` local, dữ liệu dưới `.cowork_local/`, `.venv`.
|
||||
* [ ] Chạy `python scripts/run_quality_gate.py`.
|
||||
Cả **5 quality gate đều phải PASS** và phải ghi lại **output thực tế**.
|
||||
|
||||
## D. Bảo mật
|
||||
* [ ] **Gate C:** Các thư mục `domain/` và `application/` không được import:
|
||||
- `PySide6`
|
||||
- `PyQt`
|
||||
- `ui`
|
||||
- `app`
|
||||
|
||||
- [ ] Không secret/PII/đường dẫn cá nhân trong code, test fixture, commit message, PR body.
|
||||
- [ ] Ảnh chụp màn hình đính kèm đã được redact.
|
||||
- [ ] Nếu chạm permission / credential / MCP write-exec / sandbox / network / TLS /
|
||||
isolation / model routing / xoá dữ liệu → đánh dấu `security-review: required` và ghi
|
||||
rõ trong PR rằng **CI xanh không đủ để merge**.
|
||||
* [ ] **Gate A:** Không tạo thêm secret hoặc thông tin nhạy cảm dạng plaintext.
|
||||
|
||||
## E. Nội dung PR
|
||||
* [ ] **Gate S:** Không có file nào vượt quá **400 dòng code (LOC)**.
|
||||
|
||||
- [ ] Summary nói **tại sao**, không chỉ **cái gì**.
|
||||
- [ ] Change Type đã tick.
|
||||
- [ ] Scope: nêu rõ cả phần **cố ý không** làm.
|
||||
- [ ] Validation: có lệnh và output thật.
|
||||
- [ ] Security Impact: đã điền, kể cả khi là "không có".
|
||||
- [ ] Compatibility: đã tick.
|
||||
- [ ] Reviewer Notes: chỉ ra chỗ cần soi kỹ nhất.
|
||||
- [ ] Tài liệu (`docs/`, ảnh `docs/screens/`) đã cập nhật nếu cần.
|
||||
* [ ] **Gate O:** Không có file/module mới bị bỏ quên.
|
||||
File Python mới phải được sử dụng/import trong cùng thay đổi.
|
||||
|
||||
## F. Ranh giới
|
||||
* [ ] **Gate A/N:** Test phải PASS.
|
||||
Nếu đã có test FAIL từ trước thì phải ghi rõ đó là **lỗi có sẵn**, không phải lỗi do bản sửa này gây ra.
|
||||
|
||||
- [ ] Agent **không** tự merge, **không** tự đóng issue.
|
||||
- [ ] Nếu là đóng góp của FSG AI Core: hiểu rằng chỉ "Done" khi PR đã merge vào Cowork Local,
|
||||
kèm đủ core issue reference, PR, evidence, reviewer phía Cowork, merge reference.
|
||||
---
|
||||
|
||||
## B. Kiểm tra bản sửa
|
||||
|
||||
* [ ] Có **regression test** cho lỗi đã sửa.
|
||||
|
||||
* [ ] Regression test phải chứng minh được:
|
||||
- **Trước khi sửa:** test FAIL.
|
||||
- **Sau khi sửa:** test PASS.
|
||||
|
||||
* [ ] Test chạy được ở chế độ headless:
|
||||
`QT_QPA_PLATFORM=offscreen`
|
||||
|
||||
* [ ] Nếu thay đổi liên quan đến UI:
|
||||
- Đã kiểm tra giao diện ở **Dark Mode**.
|
||||
- Đã kiểm tra giao diện ở **Light Mode**.
|
||||
- Nếu chưa thể kiểm tra bằng mắt, phải ghi rõ:
|
||||
**"Chưa kiểm chứng bằng mắt"** và nêu lý do.
|
||||
|
||||
* [ ] Nếu thay đổi liên quan đến ngôn ngữ:
|
||||
đã kiểm tra các ngôn ngữ bị ảnh hưởng.
|
||||
|
||||
---
|
||||
|
||||
## C. Kiểm tra phạm vi thay đổi và Git
|
||||
|
||||
* [ ] Một PR chỉ giải quyết **một thay đổi logic chính**.
|
||||
Không đưa refactor không liên quan vào cùng PR.
|
||||
|
||||
* [ ] Không tự ý format hoặc thay đổi indent của toàn bộ file.
|
||||
Diff phải rõ ràng và dễ review.
|
||||
|
||||
* [ ] Làm việc trên **branch riêng**.
|
||||
Không commit trực tiếp vào `main`.
|
||||
|
||||
* [ ] Commit message phải nêu:
|
||||
- Nguyên nhân gốc của lỗi.
|
||||
- Vị trí code liên quan (`file:line`).
|
||||
- Issue liên quan.
|
||||
|
||||
* [ ] Không commit các file/dữ liệu sau:
|
||||
- `.env`
|
||||
- `config.json` local
|
||||
- `.cowork_local/`
|
||||
- `.venv/`
|
||||
|
||||
---
|
||||
|
||||
## D. Kiểm tra bảo mật
|
||||
|
||||
* [ ] Không có các thông tin sau trong code, test fixture, commit message hoặc PR body:
|
||||
- Secret
|
||||
- PII/thông tin cá nhân
|
||||
- Đường dẫn chứa thông tin cá nhân trên máy local
|
||||
|
||||
* [ ] Nếu có ảnh chụp màn hình trong PR:
|
||||
đã che (redact) toàn bộ thông tin nhạy cảm trước khi đính kèm.
|
||||
|
||||
* [ ] Nếu thay đổi liên quan đến một trong các nội dung sau:
|
||||
|
||||
```
|
||||
- Permission/quyền truy cập
|
||||
- Credential/thông tin xác thực
|
||||
- MCP write/exec
|
||||
- Sandbox
|
||||
- Network
|
||||
- TLS
|
||||
- Isolation
|
||||
- Model routing
|
||||
- Xóa dữ liệu
|
||||
|
||||
thì phải:
|
||||
|
||||
1. Đặt `security-review: required`.
|
||||
2. Ghi rõ trong PR rằng:
|
||||
**"CI xanh không có nghĩa là có thể merge ngay."**
|
||||
3. Chờ security review theo quy trình trước khi merge.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## E. Kiểm tra nội dung PR
|
||||
|
||||
* [ ] **Summary** phải giải thích **tại sao cần sửa**, không chỉ mô tả đã sửa cái gì.
|
||||
|
||||
* [ ] Đã chọn **Change Type** phù hợp.
|
||||
|
||||
* [ ] **Scope** phải ghi rõ:
|
||||
- Những gì đã thay đổi.
|
||||
- Những gì **cố ý không thay đổi**.
|
||||
|
||||
* [ ] **Validation** phải ghi:
|
||||
- Lệnh đã chạy.
|
||||
- Kết quả thực tế/output.
|
||||
|
||||
* [ ] **Security Impact** phải được điền.
|
||||
Nếu không ảnh hưởng bảo mật, ghi rõ **"Không có"**.
|
||||
|
||||
* [ ] Đã chọn **Compatibility** phù hợp.
|
||||
|
||||
* [ ] **Reviewer Notes** phải chỉ ra những phần reviewer cần kiểm tra kỹ nhất.
|
||||
|
||||
* [ ] Đã cập nhật tài liệu nếu cần:
|
||||
- `docs/`
|
||||
- Ảnh màn hình trong `docs/screens/`
|
||||
|
||||
---
|
||||
|
||||
## F. Giới hạn quyền của Agent
|
||||
|
||||
* [ ] Agent **không được tự merge PR**.
|
||||
|
||||
* [ ] Agent **không được tự đóng issue**.
|
||||
|
||||
* [ ] Nếu đây là đóng góp từ **FSG AI Core**, cần hiểu rằng trạng thái **"Done"** chỉ được xác nhận khi PR đã thực sự được merge vào Cowork Local và có đầy đủ:
|
||||
|
||||
```
|
||||
- Core issue reference
|
||||
- PR reference
|
||||
- Evidence
|
||||
- Reviewer phía Cowork
|
||||
- Merge reference
|
||||
```
|
||||
|
||||
+190
-36
@@ -1,49 +1,203 @@
|
||||
# Checklist review bản vá UI (visual)
|
||||
# Checklist review bản vá UI (Visual)
|
||||
|
||||
Dùng bởi `ui-visual-fixer` (bước 7) và `regression-reviewer` (bước 5).
|
||||
Checklist này được sử dụng bởi:
|
||||
|
||||
## A. Đúng file
|
||||
* `ui-visual-fixer` — kiểm tra ở bước 7.
|
||||
* `regression-reviewer` — kiểm tra ở bước 5.
|
||||
|
||||
- [ ] Đã `grep` cả `ui/` và `presentation/`; file được sửa là file thực sự import vào runtime.
|
||||
- [ ] Widget này không có bản trùng tên ở thư mục còn lại.
|
||||
Mục tiêu: đảm bảo bản vá UI sửa đúng nguyên nhân, không phá theme, layout, icon hoặc vòng đời của giao diện.
|
||||
|
||||
## B. Màu & theme
|
||||
---
|
||||
|
||||
- [ ] Không hex literal (`#rrggbb`), không tên màu (`"red"`) ngoài `theme/`.
|
||||
- [ ] Không `setStyleSheet` cục bộ mới; style đi qua `objectName` + `theme/qss.py`.
|
||||
- [ ] Token mới có ở **cả** `DARK` và `LIGHT`.
|
||||
- [ ] Chữ trên nền đặc dùng `accent_solid`, không dùng `accent`.
|
||||
- [ ] Bậc bề mặt đúng ngữ nghĩa: `bg` / `surface` / `surface_raised` / `overlay` / `sunken`.
|
||||
- [ ] Contrast ≥ 4.5:1 cho body text và chữ trên nút đặc, ở cả hai theme.
|
||||
- [ ] Không thêm gradient/glow (trái ràng buộc thiết kế).
|
||||
- [ ] Nav rail vẫn tối hơn vùng nội dung.
|
||||
- [ ] Không trả bốn giá trị đã nhích lên WCAG AA về giá trị VS Code gốc.
|
||||
- [ ] Nếu chạm `_TEMPLATE`: đã liệt kê phạm vi ảnh hưởng toàn app.
|
||||
## A. Kiểm tra đúng file
|
||||
|
||||
## C. Layout & kích thước
|
||||
* [ ] Đã tìm kiếm trong **cả `ui/` và `presentation/`** để xác định file thực sự được ứng dụng sử dụng khi chạy.
|
||||
|
||||
- [ ] Không thêm `setFixedWidth` / `setFixedSize` / `setFixedHeight` mới.
|
||||
- [ ] Stretch factor / size policy được đặt tường minh.
|
||||
- [ ] `QScrollArea` có `setWidgetResizable(True)`.
|
||||
- [ ] Margin/spacing của layout lồng nhau không cộng dồn ngoài ý muốn.
|
||||
- [ ] Còn đúng ở cửa sổ nhỏ nhất **và** maximize.
|
||||
- [ ] Còn đúng ở scale 125% / 150% nếu bản vá chạm kích thước.
|
||||
* [ ] Đã kiểm tra xem widget có file/bản triển khai trùng tên ở thư mục còn lại hay không.
|
||||
|
||||
## D. Icon & vẽ tay
|
||||
* [ ] Nếu có nhiều file cùng chức năng, đã xác định rõ **file nào thực sự được import và chạy**.
|
||||
|
||||
- [ ] Icon lấy qua `ui/icons.py::icon`, không load file trực tiếp.
|
||||
- [ ] `paintEvent` đọc màu qua `current_palette()`, không đọc lại config.
|
||||
- [ ] Dùng `update()`, không `repaint()` trong vòng lặp.
|
||||
- [ ] `QPainter` có `end()`; nền được xoá đúng cách.
|
||||
---
|
||||
|
||||
## E. Vòng đời
|
||||
## B. Kiểm tra màu sắc và Theme
|
||||
|
||||
- [ ] Bản vá còn đúng khi đổi theme **trước** rồi mới mở màn dựng lười (P07).
|
||||
- [ ] `setProperty` để đổi style động có kèm `unpolish`/`polish`.
|
||||
- [ ] Không `connect()` lặp lại trong hàm được gọi nhiều lần.
|
||||
* [ ] Không thêm mã màu trực tiếp như `#rrggbb` hoặc tên màu như `"red"` bên ngoài thư mục `theme/`.
|
||||
|
||||
## F. Bằng chứng
|
||||
* [ ] Không thêm `setStyleSheet()` trực tiếp vào widget.
|
||||
Style phải được quản lý thông qua:
|
||||
|
||||
- [ ] Đã đối chiếu `docs/screens/<slug>-dark.png` và `<slug>-light.png`.
|
||||
- [ ] Ảnh trong `docs/screens/` cần cập nhật thì đã nêu.
|
||||
- [ ] Có test regression chạy headless, đỏ-trước-xanh-sau.
|
||||
```
|
||||
`objectName` → `theme/qss.py`
|
||||
```
|
||||
|
||||
* [ ] Nếu thêm token màu mới, token đó phải được khai báo cho **cả `DARK` và `LIGHT`**.
|
||||
|
||||
* [ ] Khi đặt chữ trên nền màu đặc, dùng `accent_solid`.
|
||||
Không dùng `accent` cho trường hợp này.
|
||||
|
||||
* [ ] Dùng đúng loại màu nền theo mục đích:
|
||||
|
||||
```
|
||||
- `bg` — nền chính.
|
||||
- `surface` — bề mặt thông thường.
|
||||
- `surface_raised` — bề mặt nổi.
|
||||
- `overlay` — lớp phủ.
|
||||
- `sunken` — khu vực chìm.
|
||||
```
|
||||
|
||||
* [ ] Contrast của chữ đạt tối thiểu **4.5:1** đối với:
|
||||
- Body text.
|
||||
- Chữ trên nút có nền đặc.
|
||||
- Cả Dark Mode và Light Mode.
|
||||
|
||||
* [ ] Không thêm:
|
||||
- Gradient.
|
||||
- Glow.
|
||||
|
||||
```
|
||||
Đây là các kiểu không phù hợp với design constraint hiện tại.
|
||||
```
|
||||
|
||||
* [ ] `Nav rail` vẫn **tối hơn khu vực nội dung**.
|
||||
Đây là thiết kế có chủ ý, không tự ý làm sáng lên.
|
||||
|
||||
* [ ] Không khôi phục các giá trị màu cũ theo VS Code nếu các giá trị hiện tại đã được điều chỉnh để đạt WCAG AA.
|
||||
|
||||
* [ ] Nếu thay đổi `_TEMPLATE`:
|
||||
đã đánh giá và ghi rõ **phạm vi ảnh hưởng trên toàn ứng dụng** vì `_TEMPLATE` có thể ảnh hưởng nhiều màn hình.
|
||||
|
||||
---
|
||||
|
||||
## C. Kiểm tra Layout và kích thước
|
||||
|
||||
* [ ] Không thêm mới:
|
||||
|
||||
```
|
||||
- `setFixedWidth()`
|
||||
- `setFixedHeight()`
|
||||
- `setFixedSize()`
|
||||
|
||||
để che hoặc né lỗi layout.
|
||||
```
|
||||
|
||||
* [ ] `stretch factor` và `size policy` được thiết lập rõ ràng khi cần.
|
||||
|
||||
* [ ] Nếu sử dụng `QScrollArea`, phải có:
|
||||
|
||||
```
|
||||
`setWidgetResizable(True)`
|
||||
```
|
||||
|
||||
* [ ] Kiểm tra margin và spacing của các layout lồng nhau.
|
||||
Không được để chúng cộng dồn khiến UI bị lệch hoặc quá rộng.
|
||||
|
||||
* [ ] UI vẫn hiển thị đúng ở:
|
||||
- Kích thước cửa sổ nhỏ nhất.
|
||||
- Cửa sổ maximize.
|
||||
|
||||
* [ ] Nếu bản vá liên quan đến kích thước, phải kiểm tra thêm ở:
|
||||
- Scale 125%.
|
||||
- Scale 150%.
|
||||
|
||||
---
|
||||
|
||||
## D. Kiểm tra Icon và Custom Painting
|
||||
|
||||
* [ ] Icon phải được lấy thông qua:
|
||||
|
||||
```
|
||||
`ui/icons.py::icon`
|
||||
|
||||
Không tự load file icon trực tiếp.
|
||||
```
|
||||
|
||||
* [ ] Trong `paintEvent()`, màu sắc phải lấy từ:
|
||||
|
||||
```
|
||||
`current_palette()`
|
||||
|
||||
Không đọc lại màu trực tiếp từ config.
|
||||
```
|
||||
|
||||
* [ ] Trong các vòng lặp hoặc thao tác cập nhật UI, dùng:
|
||||
|
||||
```
|
||||
`update()`
|
||||
|
||||
Không dùng `repaint()` nếu không thực sự cần thiết.
|
||||
```
|
||||
|
||||
* [ ] `QPainter` được kết thúc đúng cách bằng `end()` khi sử dụng thủ công.
|
||||
|
||||
* [ ] Nền của khu vực custom painting được xử lý/xóa đúng cách, không để lại hình ảnh hoặc pixel cũ.
|
||||
|
||||
---
|
||||
|
||||
## E. Kiểm tra vòng đời UI
|
||||
|
||||
* [ ] UI vẫn hoạt động đúng nếu người dùng:
|
||||
|
||||
```
|
||||
1. Đổi theme trước.
|
||||
2. Sau đó mới mở màn hình được tạo theo kiểu lazy.
|
||||
|
||||
Đặc biệt kiểm tra lỗi **P07**.
|
||||
```
|
||||
|
||||
* [ ] Nếu dùng `setProperty()` để thay đổi style động:
|
||||
phải gọi `unpolish()` và `polish()` khi cần để QSS được áp dụng lại.
|
||||
|
||||
* [ ] Không gọi `connect()` nhiều lần trong một hàm có thể được gọi nhiều lần.
|
||||
|
||||
* [ ] Không tạo signal/slot bị kết nối lặp, gây ra:
|
||||
- Event chạy nhiều lần.
|
||||
- UI cập nhật nhiều lần.
|
||||
- Memory leak hoặc hành vi bất thường.
|
||||
|
||||
---
|
||||
|
||||
## F. Kiểm tra bằng chứng
|
||||
|
||||
* [ ] Đã đối chiếu với screenshot trong:
|
||||
|
||||
```
|
||||
`docs/screens/<slug>-dark.png`
|
||||
|
||||
và
|
||||
|
||||
`docs/screens/<slug>-light.png`
|
||||
```
|
||||
|
||||
* [ ] Nếu bản vá làm thay đổi giao diện, đã xác định screenshot nào cần cập nhật.
|
||||
|
||||
* [ ] Nếu cần cập nhật screenshot trong `docs/screens/`, phải ghi rõ trong phạm vi thay đổi.
|
||||
|
||||
* [ ] Có regression test cho lỗi đã sửa.
|
||||
|
||||
* [ ] Regression test chạy được ở chế độ headless:
|
||||
|
||||
```
|
||||
`QT_QPA_PLATFORM=offscreen`
|
||||
```
|
||||
|
||||
* [ ] Regression test chứng minh được:
|
||||
|
||||
```
|
||||
**Trước khi sửa → FAIL**
|
||||
|
||||
**Sau khi sửa → PASS**
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Kết luận
|
||||
|
||||
Chỉ đánh giá bản vá là **PASS** khi:
|
||||
|
||||
1. Sửa đúng file thực sự chạy.
|
||||
2. Không phá theme hoặc layout hiện có.
|
||||
3. Không dùng workaround để che lỗi.
|
||||
4. Không tạo regression.
|
||||
5. Có regression test phù hợp.
|
||||
6. Có đủ bằng chứng kiểm chứng.
|
||||
7. Các vấn đề liên quan đến security hoặc product decision đã được route đúng agent/người phụ trách.
|
||||
|
||||
+198
-34
@@ -1,48 +1,212 @@
|
||||
# Checklist review bản vá UX (flow)
|
||||
# Checklist review bản vá UX (Flow)
|
||||
|
||||
Dùng bởi `ux-flow-fixer` (bước 8) và `regression-reviewer`.
|
||||
Checklist này được sử dụng bởi:
|
||||
|
||||
## A. Bốn trạng thái
|
||||
* `ux-flow-fixer` — kiểm tra ở bước 8.
|
||||
* `regression-reviewer` — kiểm tra trong quá trình review bản vá.
|
||||
|
||||
Cho mỗi view có dữ liệu bất đồng bộ:
|
||||
Mục tiêu: đảm bảo người dùng luôn biết **hệ thống đang làm gì, chuyện gì xảy ra và cần làm gì tiếp theo**, đồng thời không bị mất dữ liệu.
|
||||
|
||||
- [ ] **Rỗng** — hiện thông điệp có nghĩa, nói được bước tiếp theo (không phải màn trắng).
|
||||
- [ ] **Đang tải** — có dấu hiệu chuyển động; nút bị vô hiệu hoá để chống bấm đúp.
|
||||
- [ ] **Lỗi** — nói *cái gì hỏng* và *làm gì tiếp*; có đường thử lại; không in nguyên exception.
|
||||
- [ ] **Thành công** — có xác nhận rõ; có undo nếu hành động khó đảo ngược.
|
||||
---
|
||||
|
||||
## B. An toàn dữ liệu
|
||||
## A. Kiểm tra 4 trạng thái chính
|
||||
|
||||
- [ ] Ô nhập dài (instruction, composer, node property, AI Edit) không mất nội dung khi
|
||||
chuyển tab / đóng dialog / đổi project.
|
||||
- [ ] Có dirty-state; `closeEvent` chặn khi còn thay đổi chưa lưu.
|
||||
- [ ] Hành động phá huỷ (xoá project/task, ghi đè file) có xác nhận.
|
||||
- [ ] Xác nhận nêu rõ **cái gì** sẽ mất, không phải "Bạn có chắc không?".
|
||||
- [ ] Nút phá huỷ **không** phải default button, **không** nhận Enter.
|
||||
Đối với mỗi màn hình có dữ liệu hoặc thao tác chạy bất đồng bộ, phải kiểm tra đủ 4 trạng thái:
|
||||
|
||||
## C. Phản hồi theo thời gian
|
||||
### 1. Trạng thái Rỗng (Empty)
|
||||
|
||||
- [ ] 100ms-1s: đổi con trỏ hoặc vô hiệu hoá nút.
|
||||
- [ ] 1s-10s: chỉ báo tiến trình rõ ràng.
|
||||
- [ ] \>10s: có tiến trình, **huỷ được**, không chặn phần còn lại của UI.
|
||||
- [ ] Việc nặng chạy ở service `application/`, không ở GUI thread.
|
||||
- [ ] Bấm hai lần không chạy hai lần (kiểm `connect()` trùng — P10).
|
||||
* [ ] Khi chưa có dữ liệu, màn hình phải hiển thị thông báo có ý nghĩa.
|
||||
|
||||
## D. Khám phá được
|
||||
* [ ] Thông báo phải cho người dùng biết **cần làm gì tiếp theo**.
|
||||
|
||||
- [ ] Mọi nút icon-only có tooltip (nav rail thu gọn, toolbar Co4E, top bar).
|
||||
- [ ] Nút bị vô hiệu hoá nói được **lý do** (mẫu đúng: `app.nav.needs_project`).
|
||||
- [ ] Chức năng chính không bị chôn sau menu chuột phải mà không có lối vào khác.
|
||||
- [ ] Thứ tự control khớp thứ tự người dùng thực hiện.
|
||||
* [ ] Không để màn hình trắng khiến người dùng không biết chuyện gì đang xảy ra.
|
||||
|
||||
## E. Nhất quán
|
||||
### 2. Trạng thái Đang tải (Loading)
|
||||
|
||||
- [ ] Cùng một hành động dùng cùng một từ trên mọi màn (không chỗ "Lưu" chỗ "Cập nhật").
|
||||
- [ ] Vị trí nút chính/phụ giống các dialog khác.
|
||||
- [ ] Chuỗi mới đi qua `tr()` với đủ `en`/`ja`/`vi`.
|
||||
* [ ] Có dấu hiệu rõ ràng cho biết hệ thống đang xử lý, ví dụ loading indicator.
|
||||
|
||||
## F. Phạm vi
|
||||
* [ ] Các nút có thể gây chạy lại cùng một thao tác được vô hiệu hóa trong lúc đang xử lý.
|
||||
|
||||
- [ ] Bản vá chọn mức can thiệp thấp nhất (thêm thông tin trước, đổi luồng sau).
|
||||
- [ ] Thay đổi luồng được đánh dấu là **đề xuất** cần Cowork Team duyệt.
|
||||
- [ ] Có test regression cho signal/state, chạy headless.
|
||||
* [ ] Bấm liên tục hoặc bấm đúp không được tạo ra nhiều request/thao tác giống nhau.
|
||||
|
||||
### 3. Trạng thái Lỗi (Error)
|
||||
|
||||
* [ ] Thông báo lỗi phải cho biết:
|
||||
- **Chuyện gì đã xảy ra.**
|
||||
- **Người dùng cần làm gì tiếp theo.**
|
||||
|
||||
* [ ] Có cách để người dùng **thử lại** khi phù hợp.
|
||||
|
||||
* [ ] Không hiển thị nguyên exception, stack trace hoặc thông tin kỹ thuật khó hiểu cho người dùng.
|
||||
|
||||
### 4. Trạng thái Thành công (Success)
|
||||
|
||||
* [ ] Sau khi thao tác thành công, phải có thông báo/xác nhận rõ ràng.
|
||||
|
||||
* [ ] Với thao tác khó hoặc không thể hoàn tác, phải có cơ chế **Undo** nếu phù hợp.
|
||||
|
||||
---
|
||||
|
||||
## B. Kiểm tra an toàn dữ liệu
|
||||
|
||||
* [ ] Các ô nhập nội dung dài, ví dụ:
|
||||
- Instruction
|
||||
- Composer
|
||||
- Node property
|
||||
- AI Edit
|
||||
|
||||
```
|
||||
không được mất nội dung khi:
|
||||
|
||||
- Chuyển tab.
|
||||
- Đóng/mở dialog.
|
||||
- Đổi project.
|
||||
```
|
||||
|
||||
* [ ] Có cơ chế xác định **dirty-state** khi dữ liệu đã thay đổi nhưng chưa lưu.
|
||||
|
||||
* [ ] `closeEvent` phải cảnh báo hoặc chặn việc đóng màn hình khi vẫn còn thay đổi chưa lưu.
|
||||
|
||||
* [ ] Các thao tác có thể làm mất dữ liệu phải có bước xác nhận, ví dụ:
|
||||
- Xóa project.
|
||||
- Xóa task.
|
||||
- Ghi đè file.
|
||||
|
||||
* [ ] Nội dung xác nhận phải nói rõ **dữ liệu nào sẽ bị mất**.
|
||||
|
||||
```
|
||||
Không dùng thông báo quá chung chung như:
|
||||
|
||||
`"Bạn có chắc không?"`
|
||||
```
|
||||
|
||||
* [ ] Nút thực hiện thao tác phá hủy dữ liệu:
|
||||
- Không được đặt làm **default button**.
|
||||
- Không được thực hiện khi người dùng chỉ nhấn `Enter`.
|
||||
|
||||
---
|
||||
|
||||
## C. Kiểm tra phản hồi theo thời gian
|
||||
|
||||
Phản hồi của UI phải phù hợp với thời gian xử lý:
|
||||
|
||||
* [ ] **100ms – 1s:**
|
||||
Có thể thay đổi con trỏ hoặc vô hiệu hóa nút để người dùng biết thao tác đã được nhận.
|
||||
|
||||
* [ ] **1s – 10s:**
|
||||
Hiển thị chỉ báo tiến trình rõ ràng.
|
||||
|
||||
* [ ] **Trên 10s:**
|
||||
- Có chỉ báo tiến trình.
|
||||
- Người dùng có thể **hủy thao tác** khi phù hợp.
|
||||
- Không khóa toàn bộ UI nếu không cần thiết.
|
||||
|
||||
* [ ] Các tác vụ xử lý nặng không được chạy trực tiếp trên GUI thread.
|
||||
Phải chuyển phần xử lý nặng sang service trong `application/`.
|
||||
|
||||
* [ ] Một thao tác không được chạy hai lần khi người dùng bấm liên tục hoặc bấm đúp.
|
||||
|
||||
* [ ] Kiểm tra các `connect()` có bị đăng ký nhiều lần hay không, đặc biệt với lỗi **P10**.
|
||||
|
||||
---
|
||||
|
||||
## D. Kiểm tra khả năng khám phá chức năng
|
||||
|
||||
Người dùng phải dễ dàng biết **nút này làm gì và tìm chức năng ở đâu**.
|
||||
|
||||
* [ ] Tất cả các nút chỉ có icon (`icon-only`) đều có tooltip.
|
||||
|
||||
```
|
||||
Đặc biệt kiểm tra:
|
||||
- Nav rail khi thu gọn.
|
||||
- Toolbar Co4E.
|
||||
- Top bar.
|
||||
```
|
||||
|
||||
* [ ] Nút đang bị vô hiệu hóa phải cho người dùng biết **tại sao không thể bấm**.
|
||||
|
||||
```
|
||||
Ví dụ sử dụng key:
|
||||
|
||||
`app.nav.needs_project`
|
||||
```
|
||||
|
||||
* [ ] Chức năng chính không được chỉ nằm trong menu chuột phải nếu không có cách truy cập khác.
|
||||
|
||||
* [ ] Thứ tự các control trên màn hình phải phù hợp với **thứ tự người dùng thực hiện công việc**.
|
||||
|
||||
---
|
||||
|
||||
## E. Kiểm tra tính nhất quán
|
||||
|
||||
* [ ] Một hành động phải sử dụng **cùng một thuật ngữ** trên toàn bộ ứng dụng.
|
||||
|
||||
```
|
||||
Ví dụ:
|
||||
|
||||
Nếu dùng `"Lưu"` ở một màn hình thì không nên dùng `"Cập nhật"` ở màn hình khác cho cùng một hành động.
|
||||
```
|
||||
|
||||
* [ ] Vị trí của nút chính và nút phụ phải nhất quán với các dialog khác.
|
||||
|
||||
* [ ] Chuỗi text mới phải sử dụng `tr()`.
|
||||
|
||||
* [ ] Chuỗi mới phải có bản dịch đầy đủ cho:
|
||||
|
||||
```
|
||||
- `en`
|
||||
- `ja`
|
||||
- `vi`
|
||||
```
|
||||
|
||||
* [ ] Không hardcode text mới trực tiếp trong UI code nếu text đó cần hỗ trợ đa ngôn ngữ.
|
||||
|
||||
---
|
||||
|
||||
## F. Kiểm tra phạm vi thay đổi
|
||||
|
||||
* [ ] Bản vá sử dụng **cách can thiệp nhỏ nhất có thể**.
|
||||
|
||||
```
|
||||
Ưu tiên:
|
||||
|
||||
**Bổ sung thông tin → cải thiện feedback → điều chỉnh control → thay đổi flow**
|
||||
|
||||
Không thay đổi cả luồng khi chỉ cần bổ sung thông tin.
|
||||
```
|
||||
|
||||
* [ ] Nếu cần thay đổi flow của người dùng, thay đổi đó phải được ghi rõ là:
|
||||
|
||||
```
|
||||
**ĐỀ XUẤT**
|
||||
```
|
||||
|
||||
* [ ] Agent không tự quyết định thay đổi product/UX quan trọng.
|
||||
|
||||
* [ ] Các thay đổi flow cần được **Cowork Team xem xét và phê duyệt**.
|
||||
|
||||
* [ ] Có regression test kiểm tra:
|
||||
- Signal.
|
||||
- State.
|
||||
- Chuyển trạng thái.
|
||||
- Hành vi của user flow liên quan.
|
||||
|
||||
* [ ] Regression test chạy được ở chế độ headless:
|
||||
|
||||
```
|
||||
`QT_QPA_PLATFORM=offscreen`
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Kết luận
|
||||
|
||||
Bản vá UX chỉ nên được đánh giá là đạt khi:
|
||||
|
||||
1. Người dùng biết rõ trạng thái hiện tại của hệ thống.
|
||||
2. Không có nguy cơ mất dữ liệu ngoài ý muốn.
|
||||
3. UI phản hồi phù hợp với thời gian xử lý.
|
||||
4. Chức năng dễ tìm và dễ hiểu.
|
||||
5. Cách gọi tên và cách bố trí control nhất quán.
|
||||
6. Thay đổi flow lớn đã được đánh dấu để Cowork Team phê duyệt.
|
||||
7. Có regression test chứng minh flow vẫn hoạt động đúng.
|
||||
|
||||
Reference in New Issue
Block a user