Điều 31+ — Gương Soi Độc Lập (Independent Verification Mirror) v1.1
Điều 31+ — Gương Soi Độc Lập (Independent Verification Mirror)
Version: v1.1 draft | Ngày: 2026-03-25 | Tác giả: Anh Huyên (ý tưởng + phương pháp luận) + Claude (phân tích kỹ thuật) TD: TD-359 | Ưu tiên: 🔴 Sau khi Điều 31 Phase B-C hoàn thành
I. VẤN ĐỀ
Hiện tại hệ thống có 2 luật kiểm tra: Điều 26 (Luật Đếm) và Điều 31 (Toàn Vẹn). Cả hai đều so sánh PG ↔ Nuxt API endpoint.
Điểm mù:
- Nuxt API → Màn hình: Nếu Vue code lỗi, hiển thị sai → không ai biết
- Nếu Điều 26 VÀ Điều 31 cùng lúc sai giống nhau → không phát hiện được
- Toàn bộ dữ liệu đi qua Directus (trung gian) → nếu Directus lỗi, cả 2 luật cùng sai
Yêu cầu Anh Huyên: "Kiểm soát 100% đúng nghĩa. Sai là biết luôn. Kể cả 2 luật cùng không chạy, user vẫn được biết. Tôi không thể yên tâm khi hệ thống sai hay đúng mà tôi không tự biết."
II. MÔ HÌNH — GƯƠNG SOI ĐỘC LẬP
Nguyên lý
Kênh kiểm chứng PHẢI hoàn toàn độc lập với kênh chính:
- Không dùng chung code
- Không dùng chung đường dẫn dữ liệu
- Không phụ thuộc dịch vụ bên thứ 3 (GitHub, v.v.) cho chức năng cốt lõi
Dòng chảy dữ liệu hiện tại (kênh chính)
PG → Directus → Nuxt API endpoint → Nuxt Vue UI → Màn hình
Con người bấm "Refresh" trên Registries → Nuxt gọi API → API gọi Directus → Directus đọc PG → số hiển thị.
Dòng chảy Gương Soi (kênh độc lập — KHÔNG dùng Directus)
PG ──(trực tiếp)──→ Nuxt endpoint riêng → Ô gương soi trên Registries
↑
PG ──(trigger)──→ Playwright trên VPS → Đọc số thật trên màn hình → So sánh
III. BA BƯỚC THỰC HIỆN
Bước 1: PG cung cấp số (đã có gần hết)
- 17 PG TRIGGER đếm realtime đã có (S116)
verify_counts()đã có- Cần: 1 PG FUNCTION trả 3 số tổng hợp:
- Tổng thực thể (entities across all managed collections)
- Tổng collection (managed collections count)
- Tổng loài (species count)
- Khi số thay đổi → PG NOTIFY (pub/sub có sẵn của PostgreSQL)
Bước 2: Ô gương soi trên Registries — Nuxt đọc thẳng PG
- Nuxt endpoint riêng kết nối TRỰC TIẾP PG (pg driver, KHÔNG qua Directus)
- Nhúng 1 ô nhỏ trên trang chủ Registries hiển thị 3 con số
- Cập nhật tự động: PG NOTIFY → Nuxt lắng nghe (WebSocket/SSE hoặc polling ngắn 30s)
- Code phải ĐƠN GIẢN NHẤT CÓ THỂ — ít code = ít lỗi = đáng tin
Tại sao không qua Directus? Vì đây là kênh kiểm chứng. Nếu Directus lỗi → kênh chính sai → gương soi vẫn đúng vì đọc thẳng PG. "Người kiểm tra không dùng chung dụng cụ với người bị kiểm tra."
Bước 3: Playwright trên VPS đọc số thật trên màn hình
Playwright chạy trên VPS (Docker container riêng hoặc cùng container) — KHÔNG phụ thuộc GitHub Actions. Lý do:
- Chủ động 100%, không phụ thuộc chính sách bên thứ 3
- Khởi động nhanh hơn (vài giây vs 30-60 giây của GitHub Actions)
- Không tốn quota, không giới hạn số lần chạy
- VPS đã có Docker, thêm Playwright image đơn giản
Cơ chế:
- PG số thay đổi → NOTIFY → đợi vài giây (để Nuxt render xong)
- Playwright trên VPS mở trình duyệt headless → vào Registries → đọc số hiển thị thật trên màn hình
- So sánh: Số PG = Số trên màn hình?
- KHỚP → ô gương soi hiện màu xanh
- LỆCH → ô gương soi hiện màu đỏ + cảnh báo + khởi động quy trình điều tra tự động
Dual-Trigger (Điều S131):
- Định kỳ: cron trên VPS (mỗi giờ hoặc tuỳ cấu hình)
- On-demand: gọi API bất kỳ lúc nào để verify ngay
GitHub Actions Playwright (đã có): Vẫn giữ làm lớp kiểm tra bổ sung khi CI/CD chạy. Nhưng không phải nguồn chính — VPS Playwright mới là nguồn chính.
IV. HIỂN THỊ TRÊN REGISTRIES
┌─────────────────────────────────────┐
│ 🪞 GƯƠNG SOI (PG trực tiếp) │
│ │
│ Tổng thực thể: 9,847 ✅ │
│ Tổng collection: 18 ✅ │
│ Tổng loài: 21 ✅ │
│ │
│ Playwright verify: 2 phút trước ✅ │
│ (hoặc: ❌ LỆCH — xem chi tiết) │
└─────────────────────────────────────┘
- Số xanh = PG trực tiếp KHỚP với Playwright đọc trên màn hình
- Số đỏ = LỆCH → tự cảnh báo, khởi động điều tra
- Số trong ô này LUÔN là số PG trực tiếp — nếu kênh chính sai, ô này vẫn hiện số đúng
V. TẠI SAO ĐÁNG TIN
| Rủi ro | Kênh chính (qua Directus) | Gương soi (PG trực tiếp + Playwright VPS) |
|---|---|---|
| Directus lỗi | ❌ Sai | ✅ Vẫn đúng (không dùng Directus) |
| Nuxt API code lỗi | ❌ Sai | ✅ Vẫn đúng (endpoint riêng, code đơn giản) |
| Nuxt Vue hiển thị lỗi | ❌ Không biết | ✅ Playwright đọc màn hình phát hiện |
| Điều 26 + 31 cùng sai | ❌ Không biết | ✅ Gương soi độc lập, phát hiện |
| GitHub thay đổi chính sách | ❌ Mất verify | ✅ Playwright trên VPS, không phụ thuộc |
| PG sai | ❌ Cả hai cùng sai | ❌ Cả hai cùng sai (PG sai = cực hiếm) |
VI. KẾ HOẠCH THỰC HIỆN
| Phase | Việc | Assembly First |
|---|---|---|
| 31+ A | PG function trả 3 số tổng hợp + NOTIFY | PG FUNCTION — gần hết đã có |
| 31+ B | Nuxt endpoint đọc thẳng PG + ô gương soi nhúng trên Registries | pg driver + 1 component đơn giản |
| 31+ C | Playwright Docker trên VPS + test case đọc số màn hình + so sánh PG | Docker image + script đơn giản |
| 31+ D | Cơ chế cảnh báo: lệch → đỏ + auto-alert + khởi động điều tra | PG NOTIFY + webhook/log |
Nguyên tắc tối thượng: Code gương soi phải ĐƠN GIẢN. Gương soi phức tạp = gương soi cũng lỗi = vô nghĩa.
VII. PHÂN BIỆT VAI TRÒ
| Thành phần | Vai trò | Chạy ở đâu |
|---|---|---|
| PG triggers + verify_counts() | Nguồn sự thật, đếm realtime | VPS (Docker PostgreSQL) |
| Điều 26 (Luật Đếm) | Kiểm tra PG ↔ Nuxt API | VPS (cron/on-demand) |
| Điều 31 (Toàn Vẹn) | Phát hiện issues hệ thống | VPS (runner cron/on-demand) |
| Điều 31+ Gương Soi | Kiểm chứng độc lập: PG thẳng → Nuxt → Playwright đọc màn hình | VPS (Playwright Docker) |
| GitHub Actions Playwright | Lớp bổ sung khi CI/CD | GitHub (giữ nhưng không phải nguồn chính) |
Tất cả trên VPS = tự chủ 100%, không phụ thuộc bên ngoài.