wal: flush 20260813T2312
This commit is contained in:
@ -55,6 +55,9 @@ Adversarial pre-commit reviewer SOLUTION_ERP. Read-only verify + live curl prod
|
||||
## S190 (lead-seed C2 /pause) — YC-029/YC-030 ensemble review GĐ3
|
||||
- Ensemble 3-lane (B2) + 4-lane style (B4) trả cổ tức THẬT: bắt **2 BLOCKER tiền-đề** mà invest-Fable + lead verify đều lọt vì "nghe xuôi theo khuôn" — "bỏ type=3 trang tự suy" (→trang trắng) · "BE pendingMe sẵn" (0-hit, param nuốt im lặng). Luật: **claim CƠ-CHẾ khuôn ≠ claim chỗ-thiếu — phải MỞ khuôn đọc, grep chỉ bắt cái sau**. W5 anchor "KHKK có nút Xóa panel duyệt" = **CODE CHẾT S175** (nút thật ở thanh đáy detail); mirror sai module (PE ⟂ KHKK) — lời anh "tương tự KHKK" ⇒ approver-delete KHÔNG mở. be2 đo LocalDB **12/13 role CanDelete=0** chứng policy-403-trước-owner-check = khoá oan ⇒ policy dời W8. Gate-W5 reviewer chết-non #53 → lead-gate on-behalf 3 câu sắc (Roles-shape · UserInfo.roles thật · host-404-ổn). Tag `[s190, claim-co-che-phai-mo-khuon, anchor-code-chet, mirror-nham-module]`
|
||||
|
||||
## S194 (08-13) lane R4 chấm KẾ HOẠCH YC-032 (lăng-kính "cái gì THIẾU") — `PASS-WITH-FINDINGS 12 (6 nặng)`
|
||||
🔴 **THƯỚC HỎNG tự bắt:** `antiword` KHÔNG có `-m UTF-8.txt` trả text mà control-dương `'hợp đồng'`=**0** trên cả 3 file `.doc`, và `U+FFFD`=0 nên **"trông như sạch"** — census chạy trên đó ra 0-hit im lặng. Vá cờ ⇒ 56 hit. **Bài:** 0-hit trên file 516 KB mà không có control-dương = đọc thành "file rỗng". · **Chuỗi OR đọc ngược:** spec khai "chỉ 4 role thấy, người soạn KHÔNG có" — thật ra `DrafterUserId==userId` là vế OR **ĐẦU TIÊN**, 4 role là vế **nới thêm** (`ContractFeatures.cs:347-354`) ⇒ đọc vế cuối rồi tưởng là bộ lọc = đảo nghĩa cả tính năng; luôn đọc TRỌN chuỗi OR trước khi phán "hẹp". · **Acceptance 0-bit gen-2:** "upload bản cứng → sửa HĐ ⇒ 409" XANH sẵn vì phase-9 đã khoá sửa (`:167`) ⇒ hỏi "bề mặt nào CÒN mở ở trạng thái đó?" (ở đây = tập đính kèm). · **File nguồn sai ĐUÔI:** seed khai `.docx`, đĩa chỉ có `.doc` + chính mô tả seed tự thú "cần convert" ⇒ việc thiếu là 3 (convert+chép+`UPDATE IsActive=1`), 0 wave nhận. · **Số hiệu điều = auto-number Word** (`numPr`=168/`Heading`=102 ở 002.05 ⟂ `Heading`=0 ở 002.04) ⇒ mọi phép trích `<w:t>` mất trắng số điều. · 🟢 **3 nghi ngờ tôi tự bác bằng đo** (phase-9 tới được · seed workflow idempotent PER-CODE · mã HĐ sinh lúc TẠO) — khai ra để lượt sau khỏi đo lại; nghi-mà-không-đo thì thành finding bịa. Tag `[s194, thuoc-hong-antiword, chuoi-OR-doc-nguoc, acceptance-0-bit, sai-duoi-file, auto-number-vo-hinh]`
|
||||
|
||||
## 🚨 Recurring bug patterns — CHỈ phần KHÔNG có trong system-prompt
|
||||
> #44 · #43 · #42 · #41 · #39 · #17 và "Wire BE claim" đã nằm trong system-prompt (Category 1+2) — không chép lại ở đây.
|
||||
- **#70 FE absolute-set stale-echo race** — N field cùng-cột share 1 mutation + echo sibling từ server-snapshot + `invalidate()` không await ⇒ lưu 2 ô liên-tiếp đè mất. Window mở **SAU** `isPending=false`, KHÔNG lúc in-flight. Catch: đếm field-cùng-cột share mutation + check invalidate awaited.
|
||||
|
||||
@ -67,3 +67,24 @@
|
||||
Mục lục CẢ 2 file khai `PHẦN 2 — ĐIỀU KIỆN CHUNG` gồm **ĐIỀU 1→17**, thân 2 file **không chứa**. Ứng viên duy nhất = `SOL-CCM-FO-002.04.v01 Dieu kien chung hop dong tron goi.docx`.
|
||||
**Lead đo (gộp `run` như `DocxRenderer` làm, vì tách run ra chỉ thấy 1 hit — đúng bẫy repo đã tự khai):** bản repo và bản gốc FORM **byte-identical 192.608 B**; `62 w:p · 0 bảng · 40 đoạn có chữ`; đúng **5 ĐIỀU**: 1 Am hiểu · 2 Nội dung công việc · 3 Tiến độ-chất lượng · **4 = "Xử lý khi có tranh chấp" (mục lục gọi là ĐIỀU 16)** · **5 = "Sửa đổi, bổ sung" (mục lục gọi là ĐIỀU 17)**.
|
||||
⇒ 🔴 **THIẾU 12 điều: 4 Đơn giá · 5 Tiện ích công trường · 6 Phát sinh · 7 Nghiệm thu · 8 TẠM ỨNG & THANH TOÁN · 9 Quyền-nghĩa vụ · 10 Phạt · 11 Chấm dứt · 12 BẢO HÀNH · 13 Bất khả kháng · 14 Thông tin-thông báo · 15 Quy tắc ứng xử.** Bảng tóm tắt PHẦN 1 lại trỏ sang ĐIỀU 2/3/8/10/12 của PHẦN 2 ⇒ **trỏ vào chỗ trống**. Đây là câu **CHẶN vế (2)**: "toàn bộ cái hợp đồng" hiện **không tồn tại đủ** trong tài liệu đã cấp.
|
||||
|
||||
## Vòng 2 — `/fable-clone review` (`wf_71a58432-f90`) · 4 lane `reviewer` tier-2
|
||||
|
||||
| lane | verdict | artifact | ghi chú |
|
||||
|---|---|---|---|
|
||||
| r1-taxonomy | `PASS-WITH-FINDINGS — 10 finding (3 nặng)` | 22.356 B | A1 GIỮ (2 trụ đỡ HELD) · site thật **17** không phải 13 · W1 bỏ sót trọn tầng chi-tiết per-loại |
|
||||
| r2-formshape | `PASS-WITH-FINDINGS — 9 finding (6 nặng)` | 26.903 B | **W2⟂W3 tranh cột `DraftData`** · `DraftData` 0 validator/0 RowVersion · **mảnh thứ 4: template rỗng sau tách** |
|
||||
| r3-prefill | `PASS-WITH-FINDINGS — 9 finding (4 nặng)` | 25.058 B | **chuỗi VAT của spec SAI** · **HĐ tự mâu thuẫn tiền, Dev 2/2 lệch** · **5/8 ô master in trắng** |
|
||||
| r4-plan | `PASS-WITH-FINDINGS — 12 finding (6 nặng)` | 17.931 B | chạy **LẦN 2** (lần 1 chết chỉ ghi khung 758 B) · bác 2 tiền-đề của spec (S-1/S-2) · 3 việc 0-wave |
|
||||
|
||||
**Tổng 40 finding / 19 nặng.** Vá ghi ở `spec-…§⑤` (APPEND, không sửa đè §①-④ — giữ vết spec-trước ⟂ spec-sau).
|
||||
|
||||
### Sự-cố tầng RETURN vòng 2 (vết thứ 3 cùng phiên)
|
||||
`r4` lượt 1 mất return **và** chết trước khi đổ ruột ⇒ **file 758 B chỉ có khung**. Đây là ca đầu tiên trong phiên mà **kỷ-luật khung-rỗng KHÔNG cứu được nội dung** — nó cứu được *file*, không cứu được *việc*. Phục hồi = **phóng lại ĐÃ-CẮT-GỌT** (agent lẻ, pin sẵn 3 verdict anh em để khỏi lặp) ⇒ lần 2 ra 12 finding.
|
||||
🔸 Lượt 2 cũng mất return (`"Updating my diary before returning"`) nhưng **artifact đầy đủ trên đĩa** ⇒ lead thu hoạch bằng đọc đĩa. **3/3 lần garble trong phiên này đều phục hồi trọn** — 2 lần qua đĩa, 1 lần qua resume.
|
||||
|
||||
### Lead tự bắt lỗi của chính mình (ghi làm vết, không giấu)
|
||||
Spec §①-④ có **4 câu SAI** (bảng `§⑤.a`), trong đó 2 câu **đã lỡ nói với owner** trước khi review chạy xong:
|
||||
1. *"upload bản cứng xong vẫn sửa được HĐ"* — sai, phase 9 đã khoá sửa từ trước.
|
||||
2. *"W1 và W2 chạy song song được"* — sai, không cặp wave nào song song thật.
|
||||
Gốc chung của S-1/S-2: lane invest **đọc bộ lọc của một MÀN HÌNH** (`HardCopiesPage` lọc phase 9 + 4 role) rồi suy ngược ra luật BE — mà **BE rộng hơn FE** (`DrafterUserId == userId` là vế OR đầu). ⇒ **Bài: đọc bộ lọc của một màn hình không bằng đọc câu `Where` của truy vấn.**
|
||||
|
||||
@ -145,3 +145,76 @@ Nặng thêm: **bảng tóm tắt PHẦN 1 trỏ sang ĐIỀU 2/3/8/10/12 của
|
||||
4. **`soffice` trên prod**: lane-2 báo `Test-Path` = True; **máy dev không có** ở đường mặc định ⇒ nhánh IN server-side sẽ chết câm ở local. `Pdf:SofficePath` **0-hit trong mọi `*.json`** = ghost-wire.
|
||||
5. **`antiword` cắt UTF-8 ở biên wrap dòng** (f02 3 dòng, f03 5 dòng; `U+FFFD` = 0 nên **trông như sạch**, chỉ decode mới lộ) ⇒ 🔴 **CẤM chép thẳng text antiword vào code**; câu chữ chuẩn phải lấy qua Word/LibreOffice.
|
||||
6. **Số ô THẬT** có thể > 24 — bảng §②.4 là **cận dưới**, chưa gộp ô nằm trong 61/113 dòng bảng.
|
||||
|
||||
---
|
||||
|
||||
## ⑤ VÁ SAU REVIEW — `/fable-clone review` 4 lane, **40 finding (19 nặng)**
|
||||
|
||||
> Ghi APPEND, **KHÔNG sửa đè** §①-④ ở trên: giữ bản gốc làm vết để đối chiếu spec-trước ⟂ spec-sau.
|
||||
> Nguồn: `sub-reviewer-1..4.md`. 🔴 **CẤM giao thợ bản §①-④ chưa đọc mục này.**
|
||||
|
||||
### ⑤.a — 4 chỗ spec NÓI SAI (lead tự đo lại, xác nhận)
|
||||
|
||||
| # | Spec §①-④ nói | Sự thật đo được | Neo |
|
||||
|---|---|---|---|
|
||||
| S-1 | *"người soạn HĐ không nằm trong 4 role thấy trang bản cứng"* | **SAI** — `DrafterUserId == userId` là **vế OR ĐẦU TIÊN**; 4 role là vế **NỚI THÊM**. ⇒ W5 "nới cửa GĐ4" là việc trên tiền đề sai | `ContractFeatures.cs:347` |
|
||||
| S-2 | *"upload bản cứng xong vẫn sửa được HĐ"* | **SAI** — bản cứng chỉ tồn tại ở phase 9, mà sửa HĐ **đã khoá từ khi rời `DangSoanThao`** ⇒ acceptance W5 "upload → sửa ⇒ 409" **PASS sẵn, 0-bit** | `ContractFeatures.cs:167-168` |
|
||||
| S-3 | *"W1 và W2 chạy song song được"* | **SAI** — acceptance W2 không có đường chạy khi chưa có 2 loại (W1); cộng vế tranh `DraftData` ⇒ **không cặp wave nào song song thật** | R4-F06 · R2-F4 |
|
||||
| S-4 | *"`GiaTri` ← `ThanhTien` = KL × `ChuaVat` ⇒ chưa VAT"* | **SAI** — `ChuaVat` là **cột chết** (UI gửi 0; **12/34 dòng Dev = 0**); `ThanhTien` là số **gõ tay, không khai VAT** ⇒ hỏi owner theo khuôn cũ = rủi ro **cộng VAT 2 lần** | `PeDetailTabs.tsx:2959-2962` |
|
||||
|
||||
🔸 S-1/S-2 cùng một gốc: lane invest đọc **trang FE** (`HardCopiesPage` lọc phase 9 + 4 role) rồi suy ngược ra luật BE, mà **BE rộng hơn FE**. Bài: *đọc bộ lọc của một màn hình không bằng đọc câu `Where` của truy vấn.*
|
||||
|
||||
### ⑤.b — 5 lỗ CHẶN mới (không lỗ nào có wave)
|
||||
|
||||
| # | Lỗ | Neo |
|
||||
|---|---|---|
|
||||
| B-1 🔴 | **W2 ⟂ W3 tranh CÙNG cột `DraftData`** — W2 ghi thân HĐ, W3 ghi `pe.PaymentTerms` ⇒ parse nhầm + đè chết. Phải chốt **bao-ngoài + `schemaVersion` TRƯỚC W2** (Dev đang 0 row = thời điểm rẻ nhất) | R2-F4 · `PurchaseEvaluation.cs:29` |
|
||||
| B-2 🔴 | **HĐ tự mâu thuẫn về tiền**: bảng giá Σ`PeReferenceAmount` ⟂ ô Giá trị Σ`ApprovedAmount`; Dev **2/2 lệch 100tr & 50tr** | R3-F7 · `…FromSigningPlanFeatures.cs:202,223,226-229` |
|
||||
| B-3 🔴 | **5/8 ô master IN TRẮNG**: TK 83% · người đại diện 83% · chủ đầu tư 93% · địa điểm 93% · MST 25% rỗng. Đổi C4 sang `pe.DiaDiem` (**0% rỗng**) | R3-F5b |
|
||||
| B-4 🔴 | **Upload/xoá đính kèm: 0 policy · 0 phase-guard · 0 ownership** (chỉ `[Authorize]` trần ở class) ⇒ phương án khoá-mềm của W5 để **bất kỳ ai khoá/mở khoá HĐ người khác** | R4-F02 · `ContractsController.cs:105,:130` |
|
||||
| B-5 🔴 | **Template rỗng sau khi tách loại**: 2 row seed cứng `ContractType.HopDongThauPhu`; FE+BE lọc template theo type ⇒ tách xong **dropdown Mẫu RỖNG**. Seeder skip-theo-FormCode **không tự sửa** | R2-F7 · `DbInitializer.cs:3833,3861` · `FormFeatures.cs:33` |
|
||||
|
||||
### ⑤.c — 3 việc BẮT BUỘC mà 0 wave nào nhận
|
||||
|
||||
1. **Đưa 2 file mẫu vào hệ**: bản trên đĩa là **`.doc`**, seed khai **`.docx`**, `wwwroot/templates` **không có** ⇒ **convert + chép + `UPDATE IsActive=1`** = 3 việc (R4-F04).
|
||||
2. **Số hoá 12 điều thiếu** của PHẦN 2 — không wave nào làm, cũng **không có nhánh dự phòng** nếu owner không cấp file (R4-F05).
|
||||
3. **Sản xuất bản chữ CHUẨN** — §④.5 cấm dùng text `antiword` (cắt UTF-8 ở biên wrap), nhưng **không wave nào sinh ra bản chữ sạch** để dựng TSX (R4-F05).
|
||||
|
||||
### ⑤.d — Acceptance phải viết lại (2/5 của W1 hỏng, W2 hụt 6 lớp ca, W5 0-bit)
|
||||
|
||||
- **W1-A4 PASS VACUOUS**: cùng một hàm ném 409 cho **cả** ca-pin-chéo **lẫn** ca-quên-khai ⇒ xanh ở 2 thế giới. Phải có **ca DƯƠNG** (pin đúng mã loại mới ⇒ 200) + phân biệt **đúng CÂU lỗi**, không chỉ đúng mã HTTP.
|
||||
- **W1-A5 SAI BY-CONSTRUCTION**: `AwV2DesignerMenuKeyTests` neo cứng `HaveCount(7)`/`HaveCount(17)` ⇒ **không tồn tại đường "thêm 2 loại mà 697 giữ nguyên"**. Viết *"697+k, khai k, cho phép sửa ĐÚNG method nào, cấm xoá/skip"*.
|
||||
- **W2** hụt 6 lớp ca (ô lặp · bảng N dòng · số/tiền dẫn xuất · ngày 3 mảnh · **xoá về rỗng** · bằng chữ) + **thiếu ca ÂM** (sửa ô header cũ ⇒ `DraftData` KHÔNG được về null). Bỏ tiêu chí "phân biệt bằng mắt" (không đo được).
|
||||
- **W5-A1 0-bit** (S-2 ở trên) — thay bằng ca thật: **xoá file bản cứng ⇒ KHÔNG mở khoá lại**.
|
||||
- **W3** thay ngưỡng "≥14 ô" (đặt trên tập chỉ có **13** phần tử) bằng **bảng điểm danh 13 dòng** + cấm bảng-giá-rỗng qua bài.
|
||||
|
||||
### ⑤.e — 3 nhánh CÂM nữa (ngoài `WorkflowPolicy.cs:155` đã biết)
|
||||
|
||||
Lý do sót: spec chỉ grep `case ContractType` ⇒ **bỏ trọn switch-EXPRESSION (`=>`)**. C# có 2 cú pháp switch; grep 1 là đo nửa.
|
||||
- 🔴 `ContractCodeGenerator.cs:28` `_ => "HĐ"` ⇒ **mã HĐ sai chuẩn RG-001, KHÔNG hồi tố được** (`ContractCodeSequences` đã tăng).
|
||||
- `ContractDetailsFeatures.cs:41-113` switch **không default** ⇒ bundle RỖNG trả **HTTP 200** (câm).
|
||||
- `ContractDetailsTab.tsx:583-597` switch không default ⇒ **payload rụng sạch field**.
|
||||
|
||||
### ⑤.f — Đồ thị phụ thuộc ĐÚNG (thay câu "W1 ∥ W2" đã bị bác)
|
||||
|
||||
```
|
||||
B-1 chốt lược đồ DraftData ─┐
|
||||
├─→ W2 (khung) ──┐
|
||||
W1 (tách 2 loại) ────────────┘ ├─→ W4 (in) ─→ W6
|
||||
└─→ W3 (pre-fill)┘
|
||||
⑤.c-1 (đưa mẫu vào hệ) ──→ B-5 (vá template type) ──→ W2
|
||||
⑤.c-2/3 (12 điều + bản chữ chuẩn) ──→ W2 [CHẶN: chờ owner câu (97)]
|
||||
B-4 (authz đính kèm) ──→ W5
|
||||
```
|
||||
⇒ **W1 là wave DUY NHẤT khởi động được ngay**; mọi wave khác có tiền đề.
|
||||
|
||||
### ⑤.g — Bộ câu hỏi owner ĐÃ SỬA
|
||||
|
||||
| Cũ | Phán của review | Mới |
|
||||
|---|---|---|
|
||||
| (97) 12 điều thiếu | ✅ giữ — R4 đo lại độc lập, **xác nhận** | giữ nguyên |
|
||||
| (98) in bằng gì | ❌ **THỪA** — W4 đã chốt Print CSS **trước khi** hỏi | **RÚT** |
|
||||
| (99) khoá 0-mig/1-mig | ❌ **SAI KHUÔN** — hỏi owner chọn cách cài đặt | → *"bản cứng đã upload có được **xoá/thay** không, và **ai** được làm?"* |
|
||||
| (100) VAT | ❌ **SAI KHUÔN** (S-4) | → *"giá nhập ở Duyệt NCC là **đã gồm** hay **chưa gồm** VAT?"* |
|
||||
| (101) đánh số menu | ✅ giữ | giữ nguyên |
|
||||
| — | 🔴 **QUÊN HỎI** | **(102)** *"**ai** được bấm upload bản cứng?"* — quyết cả authz (B-4) lẫn cửa GĐ4 |
|
||||
|
||||
@ -14,28 +14,165 @@
|
||||
|
||||
## 1. Verify 12 điều thiếu
|
||||
|
||||
(đang đo)
|
||||
**Phương pháp:** `python zipfile` đọc `word/document.xml`, gộp mọi `<w:t>` trong từng `<w:p>` (script `scratchpad/dieu_scan.py` · `hunt12.py`); `.doc` dùng `antiword -m UTF-8.txt -w 0`.
|
||||
|
||||
🔴 **THƯỚC HỎNG lần 1 — phải khai:** `antiword` KHÔNG có `-m UTF-8.txt` trả text mà **control-dương `'hợp đồng'` = 0 hit** trên cả 3 file `.doc` ⇒ output không phải UTF-8, mọi số đọc từ đó là RÁC. Thêm `-m UTF-8.txt` ⇒ control lên **56 hit** ở `002.02`. (Bài: 0-hit trên file 516 KB mà không có control-dương thì đọc thành "file không chứa gì".)
|
||||
|
||||
### 1.1 — Claim §②.3 ĐỨNG (verify độc lập, 2 nơi)
|
||||
|
||||
| Đo | Kết quả |
|
||||
|---|---|
|
||||
| `002.04` ở `wwwroot/templates` vs `FORM/` | **sha256[:16] = `d83fd7ad832b0ba3` cả hai** · 192.608 B ⇒ byte-identical, khớp spec |
|
||||
| `002.04` thân | `w:p=62` · nonempty=40 · `<w:tbl>`=0 · **đúng 5 heading** `ĐIỀU 1..5` |
|
||||
| 5 điều đó | 1 AM HIỂU TÀI LIỆU · 2 NỘI DUNG CÔNG VIỆC · 3 TIẾN ĐỘ-CHẤT LƯỢNG · **4 XỬ LÝ KHI CÓ TRANH CHẤP** · **5 SỬA ĐỔI, BỔ SUNG HỢP ĐỒNG** |
|
||||
| Mục lục `002.02` (`L0042-L0058`) | **ĐIỀU 1→17**, trong đó `16 = XỬ LÝ KHI CÓ TRANH CHẤP`, `17 = SỬA ĐỔI, BỔ SUNG` |
|
||||
| Mục lục `002.03` (`L0041-L0057`) | **giống hệt 1→17** |
|
||||
|
||||
⇒ Số học khớp: 17 − 5 = **12 điều thiếu**, và ánh xạ đổi-số (16→4, 17→5) đúng như spec mô tả. **Claim §②.3 HELD.**
|
||||
|
||||
### 1.2 — Truy 12 điều ở file khác: KHÔNG CÓ (0-hit có control-dương)
|
||||
|
||||
Quét **7 file** `FORM/` + **4 file** `wwwroot/templates/`, khớp **nguyên văn tiêu đề** 12 điều:
|
||||
|
||||
| File | 12 tiêu đề có mặt | Control-dương (3 tiêu đề thân `002.04`) |
|
||||
|---|---|---|
|
||||
| `002.02` `.doc` | 12/12 — **nhưng chỉ trong khối MỤC LỤC `L0042-58`** | c1·c2·c3 ✓ |
|
||||
| `002.03` `.doc` | 12/12 — **cũng chỉ mục lục** | c1·c2·c3 ✓ |
|
||||
| `002.04` | **2/12** (`BẢO HÀNH`·`BẤT KHẢ KHÁNG`) — khớp thân-văn rời, KHÔNG phải heading | c1·c2·c3 ✓ |
|
||||
| `002.05` Giao khoán | 3/12 (`HỦY BỎ HĐ`·`BẢO HÀNH`·`BẤT KHẢ KHÁNG`) — HĐ **loại khác**, thân riêng | c2·c3 ✓ |
|
||||
| `002.06` Mua bán `.doc` | 1/12 | c3 ✓ |
|
||||
| `002.01`, `RG-001` | **0/12** | **0 control** ⇒ thước còn sống, file thật sự không liên quan |
|
||||
|
||||
Phép kiểm quyết định: `'TẠM ỨNG VÀ THANH TOÁN'` trong `002.02` = **đúng 1 lần** (dòng mục lục `L0049`) ⇒ **không có thân ĐIỀU 8**. Đối chứng: `'ĐỊNH NGHĨA VÀ DIỄN GIẢI'` = **3 lần** — `L0035` mục lục + `L0130` **heading thân** + 1 lần trong câu ⇒ điều CÓ thân thì đếm ≥2.
|
||||
|
||||
⇒ **12 điều KHÔNG nằm ở bất kỳ file nào trong `FORM/` hay `wwwroot/templates/`.** §②.3 là CHẶN thật, không phải "chưa tìm kỹ".
|
||||
|
||||
### 1.3 — 🔴 PHÁT HIỆN MỚI spec chưa có: số hiệu ĐIỀU là AUTO-NUMBER, không nằm trong text
|
||||
|
||||
- `L0130` thân `002.02` in ra **`ĐỊNH NGHĨA VÀ DIỄN GIẢI` KHÔNG kèm chữ "ĐIỀU 2"** ⇒ số điều ở **thân** 2 file `.doc` do Word tự sinh (list numbering), **không có trong luồng text**.
|
||||
- `002.05`: `<w:numPr>` = **168** · `pStyle="Heading*"` = **102**, mà `'ĐIỀU <n>'` literal chỉ **1 hit** ⇒ cùng bệnh.
|
||||
- Đối chứng: `002.04` có `<w:numPr>`=33 · `pStyle Heading`=**0**, và `ĐIỀU 1..5` **là chữ thật** ⇒ hai file cùng họ dùng **hai cơ chế đánh số khác nhau**.
|
||||
|
||||
**Hệ quả cho kế hoạch:** W2 phải chép tay mục lục + tiêu đề điều, nhưng §④.5 đã cấm chép text `antiword`; đồng thời **mọi phép trích `<w:t>` sẽ mất trắng số hiệu**. ⇒ Không có nguồn chữ nào đáng tin trên đĩa, mà **không wave nào nhận việc sản xuất bản chữ chuẩn** (xem F-03).
|
||||
|
||||
⚠️ Vết cần Word xác nhận (không tự phán): dòng mục lục **ĐIỀU 14** ở CẢ 2 file đọc ra `"THÔNG TIN VÀ THÔNG BÁOXỬ LÝ KHI CÓ TRANH CHẤP"` — dính 2 tiêu đề, không dấu ngăn. Có thể là lỗi copy-paste trong form gốc, cũng có thể là `antiword` gộp 2 đoạn cùng ô. Ai chép mục lục vào HTML phải mở Word đọc lại ô này.
|
||||
|
||||
## 2. Wave thiếu/chồng
|
||||
|
||||
(chưa)
|
||||
### 2.1 — 🔴 Nạp 2 file template: KHÔNG wave nào nhận, và nó KHÔNG chỉ là "thả file" (F-04)
|
||||
|
||||
`DbInitializer.cs:3833-3838` khai tên file cho `002.02` / `002.03` **đuôi `.docx`**, mô tả tự ghi *"(cần convert .doc → .docx qua Word COM)"*. Đĩa thật:
|
||||
|
||||
```
|
||||
FORM/ SOL-CCM-FO-002.02.v01 ... .doc 516.608 B (ls -la)
|
||||
FORM/ SOL-CCM-FO-002.03.v01 ... .doc 591.360 B
|
||||
wwwroot/templates/ → 002.02/002.03 KHÔNG có mặt (4 file: 002.01, 002.04, 002.05, 002.07, RG-001)
|
||||
```
|
||||
|
||||
⇒ việc thật là **3 việc**, spec chỉ nhắc 1 nửa của việc thứ 3, và **0 wave nhận cả ba**:
|
||||
1. **CONVERT `.doc → .docx`** — chất lượng chưa ai chạy thử (chính §④.3 khai); nếu convert hỏng bảng thì mọi thứ dưới nó hỏng theo.
|
||||
2. Chép 2 file vào `wwwroot/templates/`.
|
||||
3. `UPDATE ContractTemplates SET IsActive=1` — vì `DbInitializer.cs:3861 if (existingCodes.Contains(formCode)) continue` chặn mọi lượt boot sau.
|
||||
|
||||
*(Khác ca R2 đã bắt: R2 nói 2 row seed cứng `ContractType.HopDongThauPhu`; đây là ca **file nguồn sai đuôi + không tồn tại**.)*
|
||||
|
||||
### 2.2 — Wave số-hoá 12 ĐIỀU: không tồn tại (F-05)
|
||||
|
||||
W0 chỉ "hỏi owner". Không có nhánh dự phòng cho đáp án *"12 điều nằm ở file anh chưa gửi"* hoặc *"chưa có, tự soạn"*. Khối lượng ước theo số đo: `002.04` dùng **40 đoạn không rỗng cho 5 điều**; 12 điều còn lại (gồm ĐIỀU 8 Tạm ứng-thanh toán và ĐIỀU 10 Phạt — 2 điều dài nhất) ⇒ khối chữ **≳2-3×** cái đang có. W2 chỉ dành cho nó **1 gạch đầu dòng** ("điều khoản chung") trong danh sách 8 mục.
|
||||
|
||||
### 2.3 — Wave "sản xuất bản chữ chuẩn": không tồn tại (F-05b, nối §1.3)
|
||||
|
||||
§④.5 **CẤM** chép text `antiword` vào code; `<w:t>` thì mất trắng số hiệu điều (auto-number). Vậy chữ đưa vào HTML lấy từ đâu, ai kiểm? Không wave nào nhận, không acceptance nào đo. Đây là đầu vào của W2 — thiếu nó thì W2 không có gì để gõ.
|
||||
|
||||
## 3. Phụ thuộc
|
||||
|
||||
(chưa)
|
||||
Vẽ lại từ chính nội dung checklist (không theo câu tuyên bố ở W0):
|
||||
|
||||
```
|
||||
W0 (owner) ──┬──► W3 (prefill) ──► W2 (document-view) ──► W4 (in) ──► W6
|
||||
└──► W5 (khoá) ──────────────────────────────────────► W6
|
||||
W1 (tách loại) ─────────────────► W2 (acceptance) ─────────────────────► W6
|
||||
[wave THIẾU: convert+nạp template] ──► W4 (nếu đường in dùng docx)
|
||||
```
|
||||
|
||||
- **Spec sai ở câu "W1/W2 chạy song song được".** W2 phải "gỡ `draftData: null` 4/4 site" và ghi `DraftData`; W3 cũng ghi `DraftData` (vá regression `pe.PaymentTerms`) — R2/R3 đã bắt vế tranh-cột. Vế **tôi bổ sung**: acceptance của W2 (*"nhập → Lưu → F5 → đủ ô còn nguyên"*) **không có đường chạy** nếu 2 loại HĐ mới chưa có route/menu — người kiểm không biết mở màn nào. ⇒ W1 là tiền-đề **của acceptance** W2, dù không phải tiền-đề của việc gõ component.
|
||||
- ⇒ **không cặp wave nào song song thật.** Chuỗi đúng: `W1 → W3 → W2 → W4 → W6`, nhánh `W5` rẽ sau W0.
|
||||
|
||||
## 4. Khoá chết vế 4
|
||||
|
||||
(chưa)
|
||||
### 4.1 — 🔴 Tiền đề của spec SAI: người soạn CÓ thấy trang bản cứng (F-01)
|
||||
|
||||
`ContractFeatures.cs:347-354` là chuỗi **OR 4 vế**, vế **ĐẦU TIÊN** là `x.c.DrafterUserId == userId`:
|
||||
|
||||
```csharp
|
||||
q = q.Where(x => x.c.DrafterUserId == userId // ← người soạn, KHÔNG lệ thuộc phase
|
||||
|| eligiblePhases.Contains(x.c.Phase)
|
||||
|| (x.c.Phase != DangSoanThao && myWorkflowIds.Contains(x.c.ApprovalWorkflowId))
|
||||
|| (isHardCopyActor && x.c.Phase == ContractPhase.DaPhatHanh)); // ← 4 role, vế THỨ TƯ
|
||||
```
|
||||
|
||||
`HardCopyActorRoles` (`:427-428`, 4 role) là vế **nới thêm** cho người *không* có chân nào, **không phải bộ lọc thu hẹp**. Thêm nữa: `fe-user/src/App.tsx` có **`PermissionGuard` = 0 lần** (`grep -c`), route `/hard-copies` (`:69`) trần ⇒ mọi user đăng nhập mở được trang.
|
||||
|
||||
⇒ Gạch đầu dòng W5 *"Nới cửa GĐ4 nếu owner muốn người soạn thấy"* là **việc trên tiền đề sai** — cửa đã mở sẵn cho drafter.
|
||||
|
||||
### 4.2 — 🔴 Khoá chết THẬT nằm chỗ khác: upload không có cửa nào (F-02)
|
||||
|
||||
| Bề mặt | Đo | Kết quả |
|
||||
|---|---|---|
|
||||
| `POST /contracts/{id}/attachments` | `ContractsController.cs:105-121` | **0 `[Authorize(Policy=…)]`** — chỉ `[Authorize]` trần ở class `:13` |
|
||||
| Handler | `ContractAttachmentFeatures.cs:62-84` | **0 phase-guard · 0 kiểm người soạn/vai · 0 kiểm `Purpose`** — chỉ `FirstOrDefault` HĐ rồi ghi |
|
||||
| `DELETE …/attachments/{attId}` | `ContractsController.cs:130` + `:143 .Remove(att)` | **0 policy · HARD delete** |
|
||||
|
||||
⇒ nếu W5 chọn **(0-mig) khoá theo `HasSealedCopy`** thì **bất kỳ user đăng nhập nào** biết `contractId` cũng: (a) upload `purpose=SealedCopy` ⇒ **khoá HĐ của người khác**; (b) `DELETE` ⇒ **mở khoá lại**. Owner nói "khoá" = mốc 1 chiều; máy hiện cho 2 chiều **và cho người lạ cầm công tắc**. Vế 4 bắt buộc kèm authz — spec **0 dòng** về chuyện này.
|
||||
|
||||
### 4.3 — 🔴 Acceptance W5 là 0-bit (F-03)
|
||||
|
||||
W5 đo: *"upload bản cứng → sửa HĐ ⇒ 409"*. Nhưng `ContractFeatures.cs:167-168` đã ném 409 cho **mọi** phase ≠ `DangSoanThao`, còn trang bản cứng chỉ nạp phase **9** (`HardCopiesPage.tsx:81`) ⇒ **409 xảy ra dù W5 không viết dòng nào**. Phép đo XANH cả khi tính năng vắng mặt. Thước đúng phải nhắm bề mặt **còn mở** ở phase 9 = tập đính kèm (xoá `SealedCopy` sau khi seal) — đúng cái §②.5 tự nhận là "cờ 2 chiều".
|
||||
|
||||
### 4.4 — Ba nghi ngờ tôi ĐÃ ĐO và bác bỏ (khai để lượt sau khỏi đo lại)
|
||||
|
||||
| Nghi | Đo | Kết luận |
|
||||
|---|---|---|
|
||||
| HĐ không bao giờ tới phase 9 | `ContractWorkflowService.cs:576 FinalizeToDaPhatHanhAsync` + `:241` + `ContractPhase.cs` comment | **Tới được** — cấp cuối duyệt ⇒ `DaPhatHanh` (terminal) |
|
||||
| 2 quy trình `HD-*` mới sẽ không seed trên prod đã có data | `DbInitializer.cs:789 if (await db.ApprovalWorkflows.AnyAsync(w => w.Code == code)) continue` + `:801-802 IsActive/IsUserSelectable = true` | **Idempotent PER-CODE ⇒ +2 member là seed thêm 2**, không chặn |
|
||||
| In sớm thì chưa có số HĐ | `ContractFeatures.cs:122` · `CreateContractFromSigningPlanFeatures.cs:243` | **Mã sinh ngay lúc TẠO** ở cả 2 đường ⇒ không chặn |
|
||||
|
||||
## 5. Rủi ro đánh giá thấp
|
||||
|
||||
(chưa)
|
||||
- **④.2 "prod có HĐ Thầu phụ sống không" — xếp nhầm hạng: đây là TIỀN-ĐỀ của W1, không phải "chưa đo" chung chung** (F-07). Row `Type=1` đang sống sẽ **không thuộc** 2 menu mới sau khi tách ⇒ phải quyết "giữ nguyên hay backfill" TRƯỚC khi seed menu, không phải sau. Rẻ: 1 câu `sqlcmd`, **không cần owner**.
|
||||
- **④ BỎ SÓT hẳn: số hiệu ĐIỀU là auto-number, vô hình với mọi phép trích chữ** (F-08) — chứng ở §1.3. Ảnh hưởng cả W2 (gõ tay) lẫn nhánh `DocxRenderer` (§②.5).
|
||||
- **④ BỎ SÓT: authz đính kèm** (F-02) — không mục nào trong 6 mục nói tới, mà nó là bản lề của vế 4.
|
||||
- **④.5 (antiword cắt UTF-8) xếp ĐÚNG hạng nhưng thiếu một vế:** lượt này đo được thêm — **thiếu `-m UTF-8.txt` thì output KHÔNG phải UTF-8 và `U+FFFD` = 0** ⇒ "trông như sạch" theo đúng nghĩa đen, mọi census chạy trên nó ra 0-hit im lặng.
|
||||
|
||||
## 6. Câu hỏi owner
|
||||
|
||||
(chưa)
|
||||
| Câu | Chấm | Lý do (đo được) |
|
||||
|---|---|---|
|
||||
| (97) 12 điều thiếu | **ĐÚNG, giữ** | §1 xác nhận 0-hit toàn bộ 11 file có control-dương ⇒ chỉ owner mới có nguồn |
|
||||
| (98) in bằng gì | **THỪA / tự mâu thuẫn** (F-09) | §③ W4 **đã chốt sẵn** "Print CSS `@media print`", §②.5 đã đo `soffice` ghost-wire ⇒ lead tự quyết rồi mới hỏi. Nếu vẫn hỏi thì hỏi cái owner mới trả lời được: *"in giấy A4 ký tay, hay xuất PDF gửi NCC ký số?"* |
|
||||
| (99) 0-mig hay 1-mig | **SAI KHUÔN** (F-10) | Hỏi owner chọn *cách cài đặt* — owner không có dữ kiện để chọn. Khuôn đúng: *"sau khi upload bản cứng, có được XOÁ / THAY file đó không, và ai được làm?"* — trả lời đó mới suy ra 0-mig hay 1-mig |
|
||||
| (100) VAT | (R3 đã chấm — không lặp) | |
|
||||
| (101) đánh số menu | Được | |
|
||||
| **THIẾU** | 🔴 **QUÊN HỎI: "ai được bấm upload bản cứng?"** (F-11) | Owner mô tả *"upload → khóa"* nhưng chưa ai hỏi **actor**. F-02 chứng đây không phải chi tiết nhỏ: hiện tại là **bất kỳ ai đăng nhập**. Câu này quyết cả authz (W5) lẫn việc có nới cửa GĐ4 hay không — mà lại **không nằm trong 5 câu đã gửi** |
|
||||
|
||||
## 7. VERDICT + finding
|
||||
|
||||
(chưa)
|
||||
**`R4-PLAN: PASS-WITH-FINDINGS — 12 finding (6 nặng)`**
|
||||
Kế hoạch đúng trục và §②.3 đứng vững khi đo lại độc lập; nhưng **vế 4 dựa trên một tiền đề sai**, acceptance của nó **0-bit**, và **3 việc bắt buộc không wave nào nhận**.
|
||||
|
||||
| # | Mức | Neo | Nội dung |
|
||||
|---|---|---|---|
|
||||
| F-01 | NẶNG | `ContractFeatures.cs:347` · `fe-user/src/App.tsx:69` (`PermissionGuard`=0) | Spec §②.5 nói người soạn không thấy trang bản cứng — SAI: `DrafterUserId == userId` là vế OR **đầu tiên**; 4 role là vế **nới thêm**. W5 "nới cửa GĐ4" = việc trên tiền đề sai |
|
||||
| F-02 | NẶNG | `ContractsController.cs:105`, `:130` · `ContractAttachmentFeatures.cs:62-84`, `:143` | Upload/xoá đính kèm **0 policy · 0 phase-guard · 0 ownership**. Phương án 0-mig của W5 ⇒ bất kỳ ai cũng khoá/mở khoá HĐ người khác |
|
||||
| F-03 | NẶNG | spec `③ W5` vs `ContractFeatures.cs:167-168` | Acceptance "upload → sửa HĐ ⇒ 409" **PASS sẵn** trước khi viết code (phase 9 đã khoá sửa) ⇒ 0-bit |
|
||||
| F-04 | NẶNG | `DbInitializer.cs:3833-3838`, `:3861` · `ls FORM/` | 2 template trên đĩa là **`.doc`**, seed khai **`.docx`**, `wwwroot/templates` **không có** ⇒ cần CONVERT + chép + `UPDATE IsActive=1`: **3 việc, 0 wave** |
|
||||
| F-05 | NẶNG | spec `③ W0/W2` | 12 điều thiếu (đã xác nhận §1) **không có wave số-hoá**, cũng không có nhánh dự phòng; và **không wave nào sản xuất bản chữ chuẩn** dù §④.5 cấm dùng text antiword |
|
||||
| F-06 | NẶNG | spec `③ W0` | "W1/W2 song song" sai: acceptance W2 không có đường chạy khi chưa có 2 loại (W1); cộng vế tranh `DraftData` R2/R3 ⇒ **không cặp wave nào song song thật** |
|
||||
| F-07 | vừa | spec `④.2` | "Prod có HĐ Thầu phụ sống không" là **tiền-đề của W1** (row `Type=1` không thuộc 2 menu mới), đo bằng 1 query — không phải rủi ro "chưa đo" |
|
||||
| F-08 | vừa | `002.02 L0130` · `002.05 numPr=168/Heading=102` vs `002.04 Heading=0` | §④ bỏ sót: số hiệu ĐIỀU ở thân là **auto-number Word**, vô hình với mọi phép trích `<w:t>`/antiword |
|
||||
| F-09 | vừa | spec `③ W4` vs câu (98) | Câu owner (98) **thừa** — W4 đã chốt Print CSS trước khi hỏi |
|
||||
| F-10 | vừa | câu (99) | **Sai khuôn** — hỏi owner chọn cách cài đặt; khuôn đúng hỏi *"có được xoá/thay file bản cứng không, ai được làm"* |
|
||||
| F-11 | vừa | F-02 | **Quên hỏi**: *"ai được bấm upload bản cứng?"* — không có trong 5 câu, mà nó quyết authz + cửa GĐ4 |
|
||||
| F-12 | nhẹ | spec giao việc | Xung đột đường dẫn: khối TASK ghi `sub-reviewer-4.md`, footer RUN-TRACE ghi `sub-reviewer-3.md` |
|
||||
|
||||
**Thước hỏng đã tự bắt:** `antiword` thiếu `-m UTF-8.txt` ⇒ control-dương `'hợp đồng'` = **0** trên cả 3 file `.doc` (sau khi vá: 56). Mọi số `.doc` ở §1 đều đo lại sau khi vá thước.
|
||||
|
||||
Reference in New Issue
Block a user