diff --git a/.claude/workflows/runs/2026-07-28-S157-ke-hoach-ky-ket-hd/run.md b/.claude/workflows/runs/2026-07-28-S157-ke-hoach-ky-ket-hd/run.md index e0b07c1..06f00f9 100644 --- a/.claude/workflows/runs/2026-07-28-S157-ke-hoach-ky-ket-hd/run.md +++ b/.claude/workflows/runs/2026-07-28-S157-ke-hoach-ky-ket-hd/run.md @@ -158,9 +158,20 @@ Lead trình 2 điểm ở §1.5 và §1.4; anh chốt **cả hai**. Đây là ** propose-only; spec-file do LEAD ghi sau khi verify (H21 ① honest-note c) ``` -### Wave 2 — `/fable-clone reviewer` ensemble (đóng gói C2: ≤3 file/lane · ép khung rỗng lượt 1-2 · trần 25) +### Wave 2 — `/fable-clone reviewer` ensemble 4 lane (đóng gói C2: ≤3 file/lane · ép khung rỗng lượt 1-2 · trần 25) -*(4 lăng kính — label neo tại đây vì panel ephemeral; nội dung chốt sau khi đọc output wave 1)* +**Label C3 4-thành-phần — NEO TẠI ĐÂY** (panel ephemeral, không neo thì không tái lập/audit được): + +| Label (neo C3) | Lăng kính | File được đọc (≤3) | +|---|---|---| +| `Gate reviewer lens-wave s157` | Wave-plan có khả thi không · acceptance có **ĐO ĐƯỢC thật** không hay chỉ nghe như đo được · wave có deploy độc lập thật không | `spec-ke-hoach-ky-ket-hd-28-07-2026.md` · `runs/2026-07-27-S155-pe-delete-approver/run.md` | +| `Gate reviewer lens-fidelity s157` | Spec có trung thực với **3 nguồn gốc** không — bịa bước / rơi bước / gán sai vai / nống biên | `spec-*.md` · `src-QT-TRINH-KY-HD.txt` · `src-FO-002.01-bang-kiem-tra-HD.txt` | +| `Gate reviewer lens-schema s157` | **7 bảng** thừa/thiếu · loose-Guid vs FK · UNIQUE filtered · enum mới có đúng không · biến thể 5 bảng | `spec-*.md` · `ApprovalWorkflow.cs` · `PurchaseEvaluation.cs` | +| `Gate reviewer lens-claims s157` | Verify ĐỘC LẬP mọi claim `file:line` trong §2 — có dương-giả không | `spec-*.md` · `ProposalFeatures.cs` · `MenuKeys.cs` | + +**taskList snapshot (wave 2):** 4 lane · `role: reviewer` ×4 · `tier: 'opus'` explicit từng lane (belt-and-suspenders +theo GAP#6 — worker-tier-pin S124 đã che, nhưng lệnh này tự định nghĩa là "ensemble tier-2") · `maxTurns: 25` · +lane RETURN-only, lead = single-writer refute+synthesize @P3/P4. ### Wave 3 — lead synthesize → `spec-ke-hoach-ky-ket-hd-28-07-2026.md` + **PLAN WAVE thi công trình anh** @@ -172,10 +183,28 @@ Lead trình 2 điểm ở §1.5 và §1.4; anh chốt **cả hai**. Đây là ** - [x] S1 — lead grounding: đọc TRỌN `ContractPhase.cs` → **phát hiện A** (9 ISO ↔ 9 enum khớp 1:1) + **phát hiện B** (khoảng trống nằm giữa ISO-1 và ISO-2, thiếu ở CẢ doc lẫn code) - [x] S2 — scaffold run-folder + `run.md` (file này) -- [ ] S3 — `/fable-real investigator-codebase` deep-pass -- [ ] S4 — lead verify output S3 trên ĐĨA (byte + đối chiếu claim load-bearing) -- [ ] S5 — `/fable-clone reviewer` ensemble review -- [ ] S6 — lead refute + synthesize → spec cuối + plan wave → trình anh +- [x] S3 — `/fable-real investigator-codebase` deep-pass — **3 lượt, 2 sự-cố, PARTIAL-HONEST**: + · lượt 1 **`skeleton-ruột-rỗng`**: 202K tok / 35 tool-use, đĩa chỉ **1.661 B khung rỗng 4 mục `[PENDING]`** + (return garble `#53` = mẩu "Đọc tiếp 3 file S156"). **Ghi-đĩa-trong-lúc-làm = CẦN KHÔNG ĐỦ** — phải + verify RUỘT có chữ. Header ĐÃ hấp thụ O-1/O-2 ⇒ SendMessage giữa chừng ăn. + · lượt 2 (resume, **cắt còn 2 mục** theo khuôn cứu S156) → **§A + §B CLEAN**, đĩa 1.661→**23.085 B**, + return khớp đĩa. + · lượt 3 (resume §C/§D) — **tiến-trình CLI thoát giữa chừng**, agent stopped no-completion-record. + Đĩa **KHÔNG đổi** (23.085 B, mtime 10:55) ⇒ §C/§D **chưa từng được ghi**. Mất = phần chưa làm, **0 mất + dữ-liệu đã có**. **KHÔNG resume lần 3** (nguyên-nhân ngoại-cảnh, nhưng H21 ① đã cho phép: engine + propose-only, **spec do LEAD ghi**; §A/§B CHÍNH LÀ phần propose). +- [x] S4 — lead verify ĐỘC LẬP 4 claim load-bearing trên ĐĨA (không tin return): + · **B.1 Proposal-lite phá OR-of-N** → ✅ THẬT: `ProposalFeatures.cs` `SelectMany` flatten `:427-429` + + `ElementAtOrDefault` **1 row** `:433` + so đúng **1** `ApproverUserId` `:439`; comment code TỰ THÚ + *"Lite version: assume 1 step per workflow"*. + · **slot enum 10 trống** → ✅ THẬT: `ApprovalWorkflow.cs:53-67` dừng ở `TravelRequest = 9`. + · **`Off_DeXuat` 4 key ĐỀU trong `All`** → ✅ THẬT `MenuKeys.cs:171`; **`Pe_*` sinh bằng factory + `:150`/`:154` NẰM NGOÀI `All`** ⇒ policy per-action không tồn tại — bẫy claim đúng. + · **Mig cuối = 68** → ✅ khớp `docs/STATUS.md` CURRENT STATE. +- [x] S5 — lead ghi spec `spec-ke-hoach-ky-ket-hd-28-07-2026.md` (3 heading H21 ①) — §A/§B từ invest + (đã verify) + **§C wave-plan + §D do LEAD viết** (đúng H21: propose ⟂ spec). +- [ ] S6 — `/fable-clone reviewer` ensemble chấm spec +- [ ] S7 — lead refute + synthesize → trình anh > 🔴 **Luật của run này (vết S156):** CẤM điền sẵn kết-quả cho stage chưa chạy. Byte-count · verdict · > TOTAL chỉ được ghi SAU khi đo thật. Mọi stage chưa chạy giữ `[ ]`. diff --git a/.claude/workflows/runs/2026-07-28-S157-ke-hoach-ky-ket-hd/spec-ke-hoach-ky-ket-hd-28-07-2026.md b/.claude/workflows/runs/2026-07-28-S157-ke-hoach-ky-ket-hd/spec-ke-hoach-ky-ket-hd-28-07-2026.md new file mode 100644 index 0000000..849804a --- /dev/null +++ b/.claude/workflows/runs/2026-07-28-S157-ke-hoach-ky-ket-hd/spec-ke-hoach-ky-ket-hd-28-07-2026.md @@ -0,0 +1,340 @@ +# SPEC — "KẾ HOẠCH KÝ KẾT HỢP ĐỒNG" (khúc còn thiếu giữa Duyệt NCC và HĐ NTP/NCC) + +> **Run:** `2026-07-28-S157-ke-hoach-ky-ket-hd` · lead-written sau verify (H21 ①: engine propose-only ⟂ lead ghi spec) +> **Nguồn propose:** `sub-invest-kehoach-1.md` §A+§B (23.085 B, `/fable-real investigator-codebase` Fable) +> **Lead verify:** 4 claim load-bearing đo lại độc lập trên đĩa — xem `run.md` S4. §C+§D dưới đây **lead viết**. +> **Trạng thái:** CHỜ `/fable-clone reviewer` chấm + **5 câu owner** chưa trả lời (không chặn Wave 1-2). + +--- + +## ① TÍNH NĂNG / ĐỀ BÀI + +### 1.1 Vấn đề + +Giữa **Duyệt NCC** (module PE, kết thúc `PE.DaDuyet`) và **Hợp đồng NTP/NCC** (module Contract, bắt đầu +`DangSoanThao`) có một khúc quy trình **KHÔNG TỒN TẠI Ở CẢ HAI TẦNG**: + +- **Không có trong code** — repo 0-hit `shopdrawing|TVGS|duyệt mẫu`. +- **Không có trong quy trình ISO đang ban hành** — `QT TRINH KY HOP DONG TP-NCC.docx` bảng 2 đi THẲNG + bước 1 *"Lựa chọn NTP/NCC"* → bước 2 *"Soạn thảo hợp đồng"*, **0 bước trung gian**. +- **Chỉ có trên sơ đồ PDF** `01- Quy trinh hien tai --- 14_2026` (bản mới), các bước **b.7→12**. + +🔴 ⇒ **Spec này vừa là spec phần mềm, vừa là ĐỀ XUẤT BỔ SUNG QUY TRÌNH ISO.** Anh cần duyệt ở cả 2 tư cách. + +### 1.2 Tên + mục đích (owner đặt) + +> **anh:** *"Bước này là bước **'đề xuất giá trị ký kết hợp đồng'**"* · sau đó gọi là **"Kế hoạch ký kết Hợp đồng"**. + +Đây là phát biểu **MỤC ĐÍCH**: đầu ra của khúc này = **MỘT CON SỐ CÓ PHÊ DUYỆT** (giá trị ký kết đề xuất). +b.8-9 (duyệt mẫu / shopdrawing với TVGS) = **CĂN CỨ**; b.10-12 = **chuỗi duyệt chính con số đó**. +⇒ Vật mang trong hệ thống = **một PHIẾU**, không phải thư mục đính kèm. + +### 1.3 Bối cảnh kiến trúc — 2 phát hiện nền (lead đo, invest xác nhận từ nguồn gốc) + +**(A) 7 phase `[LEGACY]` của `ContractPhase` CHÍNH LÀ quy trình ISO 9 bước, mã-hoá cứng.** Khớp 1:1 đúng thứ tự: +`DangChon=1`↔Lựa chọn NTP/NCC · `DangSoanThao=2`↔Soạn thảo · `DangGopY=3`↔Góp ý · `DangDamPhan=4`↔Đàm phán · +`DangInKy=5`↔In HĐ · `DangKiemTraCCM=6`↔Kiểm tra CCM · `DangTrinhKy=7`↔Duyệt BOD · `DangDongDau=8`↔Đóng dấu · +`DaPhatHanh=9`↔Phát hành. Post-Mig 21/S17 thay bằng `ChoDuyet=10` + con-trỏ chạy trên workflow V2 admin-config +⇒ chuyển từ *cứng 9 bước* sang *cấu-hình N bước*. **Khung V2 sinh ra CHÍNH ĐỂ biểu diễn quy trình ISO này.** + +**(B) Khoảng trống nằm ĐÚNG giữa ISO-1 và ISO-2** (xem 1.1). + +### 1.4 🔴 OWNER ĐÃ CHỐT (2026-07-28) — ràng buộc cứng của spec + +| # | Anh nói (verbatim) | Hệ quả spec | +|---|---|---| +| **O-1** | *"Đúng chính xác, cấu trúc trình ký giống, chỉ khác nội dụng thôi."* | **TÁI DÙNG khung duyệt V2** (`ApprovalWorkflow`>Step=Phòng>Level=NV, OR-of-N, `LevelOpinions`). **0 bảng approval mới.** Dựng mới = **NỘI DUNG phiếu**. ⇒ trục tranh luận Q2 hôm qua **ĐÓNG**. | +| **O-2** | *"Cái này để tham khảo thôi, ko vấn đề j, đa số là trễ."* (SLA) | **KHÔNG SLA-engine**, không auto-approve-on-timeout, không cảnh báo chặn, **không wave nào cho SLA**. Giữ hiển thị deadline. Lỗ `AddDays(7)` hardcode = ghi nhận, ngoài scope. | +| **O-3** | *"Chỗ hợp đồng cứ từ từ nhé."* | Lỗ hổng an ninh `ContractWorkflowService.cs:48-66` + controller HĐ class-trần: **NGOÀI SCOPE**, treo có chủ đích. Wave nào chạm module Contract phải hỏi lại anh. | + +### 1.5 Quy trình chi tiết — 6 bước (b.7→12) + +Role hệ thống có thật (`AppRoles.cs:5-16`): `Procurement` (PMH) · `CostControl` (CCM) · `Director`+`AuthorizedSigner` +(BOD/NĐUQ) · `ProjectManager`/`Drafter` (BCH) · `HrAdmin` · `Admin`. +🔸 **"BCH dự án X" KHÔNG có model** (`Department.ManagerUserId` 0-row) ⇒ né bằng: **BCH = người tạo phiếu** +(`CreatedBy`); PMH/CCM/CEO = **đích danh user trong Level** của workflow V2 (đúng triết lý V2, **0 model mới**). + +| b | Việc | Ai | Đầu vào | Đầu ra | Chuyển bước | Nhánh lỗi | +|---|---|---|---|---|---|---| +| 7 | PMH chuyển thông tin cho BCH | Procurement | PE `DaDuyet` (winners `IsWinner`, giá `SUM Quote.IsSelected`, HoSoLink) | BCH biết việc | **Pull-model**: màn *"PE đã duyệt — chờ lập KH"* lọc `Phase==DaDuyet && chưa có plan` (khuôn `CreateContractFromEvaluationFeatures.cs:173`) + notify in-app. **Email ra ngoài = ngoài scope** (SMTP TODO toàn hệ thống) | PE thiếu winner → không hiện | +| 8 | Duyệt mẫu + shopdrawing với TVGS | BCH **nhập hộ** (TVGS ngoài hệ thống) | Mẫu + shopdrawing từ NTP/NCC (offline) | Danh mục **CĂN CỨ** trong phiếu: {Kind, NCC, Status, TvgsName, ngày, scan} | Vòng đời **per-item**: `ChuaNop→DaNop→TvgsDuyet\|TvgsBac`; `TvgsBac→DaNop` (nộp lại) — 🔴 **sơ đồ KHÔNG vẽ vòng lặp này, thực tế có** | Item bị bác = đổi status + note, **KHÔNG đụng state machine phiếu** | +| 9 | Tổng hợp + so sánh giá → **ĐỀ XUẤT giá trị** | BCH (Drafter phiếu) | Căn cứ b.8 + báo giá PE (**snapshot** `PeReferenceAmount` lúc tạo — nguyên tắc freeze Mig 67) | Phiếu đủ dòng giá đề xuất per-winner + file so sánh → **TRÌNH** | Guard trình: đủ dòng giá per-winner (mirror completeness-guard PE `PurchaseEvaluationWorkflowService.cs:180-212`); căn cứ chưa đủ `TvgsDuyet` → **cảnh báo mềm** (tiền lệ S62) — **O-Q1 owner quyết mềm/cứng** | Ghi chú sơ đồ *"hồ sơ sai sót → gửi mail làm rõ"* = comment + item `TvgsBac` | +| 10 | PMH kiểm tra, xác nhận giá trị | Procurement (đích danh Level) | Phiếu `ChoDuyet` Bước 1 | Duyệt → Bước 2; ý kiến vào `LevelOpinions` (UPSERT khuôn `ContractWorkflowService.cs:292-316`) | OR-of-N trong Cấp (`ApprovalWorkflow.cs:81-94`) | Trả lại → `TraLai`, hoặc 4 return-mode per-level (cờ CÓ SẴN `ApprovalWorkflowLevel.cs:116-125`) | +| 11 | CCM kiểm tra | CostControl | Bước 2 | Duyệt → Bước 3 | như trên | như trên | +| 12 | **CEO ký HOẶC CCM đóng dấu** | Director / cơ chế kết thúc sớm | Bước 3 (hoặc kết thúc tại Bước 2) | 🔴 **GIÁ TRỊ KÝ KẾT CHỐT** per-winner (`ApprovedAmount`) ghi tại **finalize choke-point** (mirror `ApplyApprovedPriceOnFinalize` PE `:973-1002` + RULE `:1008` *mọi nhánh set DaDuyet phải gọi helper*) | Terminal `DaDuyet` | Từ chối → `TuChoi`; trả lại → `TraLai` | + +**Rẽ nhánh b.12 "hoặc" — 4 đường ĐỀU CÓ SẴN trong khung V2, 0 migration:** +(i) `CeoApprovalThreshold` + CCM tích "duyệt done miễn CEO" (PE `:892-929`, fail-closed: đòi threshold + role +`CostControl` + giá < ngưỡng) · (ii) `AllowApproverFinalize` per-slot (PE `:870-890`) · (iii) +`AllowApproverSkipToFinal` per-slot, CEO vẫn ký thật (Contract `:320-352`) · (iv) 2 workflow (có/không Step CEO) +admin pin. 🔸 **Điều kiện rẽ THẬT: cả 3 nguồn IM LẶNG** → **Q3 owner**; chọn đường nào **cũng không đổi schema**. + +### 1.6 b.8-9 là **bảng con trong phiếu** — không phải phiếu con, không phải module riêng + +Lý do: (1) owner phát biểu b.8-9 = **căn cứ**, không phải đối tượng duyệt riêng; (2) PDF chỉ vẽ **MỘT** chuỗi +duyệt (10-12) cho cả khúc — phiếu con = đẻ chuỗi duyệt thứ 2 không có trên giấy; (3) vòng đời mẫu/shopdrawing +là **per-item status**, không phải state machine phiếu; (4) TVGS ngoài hệ thống ⇒ chỉ cần **ghi vết**. +Module riêng chỉ đáng khi mẫu/shopdrawing cần quản lý **độc lập với gói thầu** — chưa có yêu cầu. + +### 1.7 Mâu thuẫn / im lặng giữa 3 nguồn (bảng tra — KHÔNG tự quyết) + +| # | Điểm | ISO doc | PDF | FO-002.01 | Xử lý | +|---|---|---|---|---|---| +| 1 | Khúc b.7→12 | **KHÔNG CÓ** | CÓ (duy nhất) | không nói | Spec = đề xuất bổ sung ISO | +| 2 | SLA khúc 7→12 | im lặng | im lặng | im lặng | **O-2**: tham khảo, không hoà giải | +| 3 | SLA khúc HĐ | 07/07/07/07/03/01 ngày | 3d + 7-10d | 🔴 *"mỗi bộ phận 01 ngày, quá hạn **XEM NHƯ ĐÃ THÔNG QUA**"* | 3 nguồn 3 kiểu; auto-pass **mâu thuẫn mô hình duyệt-chặn V2**. O-2 ⇒ chỉ ghi nhận | +| 4 | Điều kiện rẽ b.12 | — | "hoặc", 0 điều kiện | không nói | **Q3 owner** | +| 5 | Nhánh trả lại 10/11/12 | không vẽ | không vẽ | không vẽ | Hệ thống có sẵn `TraLai` + 4 return-mode | +| 6 | Bypass Chủ đầu tư → GĐ | không nói | không nói | CÓ | = `Contract.BypassProcurementAndCCM` (đã có) — **KHÔNG áp phiếu KH** | +| 7 | Checklist kiểm tra per-phòng (PRO: điều khoản/thanh toán/rủi ro pháp lý · CCM: giá vs NS) | mô tả lời | không | CÓ (checkbox từng mục) | Hệ thống chỉ có `LevelOpinions.Comment` tự do ⇒ **GAP**, **O-Q2 owner** có số hoá checkbox không | +| 8 | Tên phòng/vai | PRO/CCM/BOD/NĐUQ/HRA | Procurement/CCM/CEO | PB-DA/PRO/CCM/GĐ | Khớp `AppRoles` — **0 mâu thuẫn** | +| 9 | Chuỗi ký FO-002.01 (ĐỀ XUẤT→PRO→CCM→GĐ) | ≈ ISO 5-7 | ≈ b.9→10→11→12 **CÙNG HÌNH** | CÓ | **Đối chứng GIẤY cho O-1** | + +--- + +## ② CÁCH IMPLEMENT + +### 2.1 ⚠️ BẪY SỐ MỘT — CẤM copy "Đề xuất" (Proposal) cho phần DUYỆT + +`ProposalFeatures.cs` là twin **nhỏ nhất** (849 LOC BE) nên rất cám dỗ. **Nhưng phần duyệt của nó phá OR-of-N:** +`SelectMany` flatten mọi Level xuyên Steps `:427-429` → `ElementAtOrDefault(order-1)` lấy **1 row** `:433` → +so đúng **1** `ApproverUserId` `:439`. Comment trong chính file **tự thú**: *"Lite version: assume 1 step per +workflow"*. Entity Proposal cũng **chỉ có `CurrentApprovalLevelOrder`, KHÔNG có `CurrentWorkflowStepIndex`**. + +⇒ 2 người cùng Cấp (OR-of-N hợp lệ theo `ApprovalWorkflow.cs:81-94`) bị biến thành **2 nấc TUẦN TỰ (AND)**. +Mà nhiều người cùng cấp **chính là thứ tờ FO-002.01 cần**. + +🔴 **Service PHẢI copy `ContractWorkflowService.ApproveV2Async:217-394`** (con-trỏ ĐÔI StepIndex+LevelOrder · +`GroupBy` Order `:246` · OR-of-N `:259-260` · `LevelOpinions` UPSERT `:292-316` · skipToFinal `:320-352`). +**Proposal chỉ làm khuôn cho CRUD / DTO / menu / FE.** + +> ✅ *Lead đã tự đọc `ProposalFeatures.cs:420-445` xác minh — claim ĐÚNG, không phải suy diễn.* + +### 2.2 Schema — Mig **69** `AddContractSigningPlans` · **7 CreateTable, 0 ALTER bảng cũ** + +Down = `DropTable ×7`, reversible sạch. Convention: PascalCase EN · **loose-Guid không FK vật lý** sang +PE/Supplier (convention PE, ghi ở Mig 49) · FK vật lý chỉ nội bộ module + sang bảng V2 dùng chung. + +| # | Bảng | Cột chính | Ràng buộc | +|---|---|---|---| +| 1 | `ContractSigningPlans` (header) | `MaKeHoach?` · `PurchaseEvaluationId` (loose+IX) · `ProjectId` (denorm lọc) · `Phase int` · `ApprovalWorkflowId Guid?` · `CurrentWorkflowStepIndex int?` · `CurrentApprovalLevelOrder int?` · `DrafterUserId` · `SlaDeadline?` · `HoSoLink nvarchar(1000)?` · `GhiChu` | `AuditableEntity` + `HasQueryFilter(!IsDeleted)` (khuôn `PurchaseEvaluationConfiguration.cs:84`) | +| 2 | `ContractSigningPlanLines` (giá per-winner) | `PlanId` FK Cascade · `SupplierId` loose · **`PeReferenceAmount` decimal(18,2) SNAPSHOT lúc tạo** · `ProposedAmount` · `ApprovedAmount?` (chỉ ghi tại finalize) · `Note` | UNIQUE filtered `(PlanId, SupplierId) WHERE IsDeleted=0` (gotcha #57) | +| 3 | `ContractSigningPlanDossierItems` (căn cứ b.8-9) | `PlanId` FK Cascade · `Kind int` (MauVatLieu=1/Shopdrawing=2/Khac=99) · `SupplierId?` · `Name` · `Status int` (ChuaNop=0/DaNop=1/TvgsDuyet=2/TvgsBac=3) · `TvgsName?` · `TvgsResultAt?` · `Note` | IX (PlanId) | +| 4 | `ContractSigningPlanLevelOpinions` | `PlanId` + `ApprovalWorkflowLevelId` · `Comment nvarchar(2000)` · `SignedAt` · `SignedByUserId` · `SignedByFullName` | UNIQUE (PlanId, LevelId); FK **Cascade Plan + Restrict Level** (mirror `ContractLevelOpinions` Mig 33) | +| 5 | `ContractSigningPlanAttachments` | `PlanId` FK Cascade · FileName/Path/Size/ContentType · `Purpose int` · `DossierItemId?` | khuôn `ProposalAttachment.cs` | +| 6 | `ContractSigningPlanChangelogs` | `PlanId` · Action · PhaseAtChange · UserId · Summary · ContextNote | **có từ ngày 1** — tiền lệ PE: Changelogs là nguồn transition-marker/backfill (Mig 60/61) | +| 7 | `ContractSigningPlanCodeSequences` | `Prefix` PK · `LastSeq` | khuôn `ProposalCodeSequence.cs`; format đề xuất **`KHKK/{YYYY}/{Seq:D3}`** — **owner chốt format** | + +**Biến thể tối thiểu 5 bảng** (bỏ #6 + #7) — **KHÔNG khuyến nghị**: mất audit-trail transition (Mig 60/61 chứng +minh là cần) và mất mã phiếu atomic. Nếu owner muốn cắt, cắt #7 trước (mã tay), giữ #6. + +**Enum:** +- `ApprovalWorkflowApplicableType` **+`ContractSigningPlan = 10`** — append-only. ✅ *Lead verify: `ApprovalWorkflow.cs:53-67` dừng ở `TravelRequest = 9`, **slot 10 TRỐNG THẬT**.* +- `ContractSigningPlanPhase` **MỚI**, giá trị sạch: `DangSoanThao=1 / ChoDuyet=2 / DaDuyet=3 / TraLai=98 / TuChoi=99`. + **KHÔNG copy 7/98 của PE** (sẹo lịch sử enum PE — phiếu mới không nợ data cũ); giữ 98/99 cho quen mắt FE badge. +- 🔴 **KHÔNG thêm cột `SigningPlanId` lên PE** — tra ngược bằng `Plans.Where(PurchaseEvaluationId==x)`. + Thêm cột = đẻ *"cột có mà 0 ai đọc"* (lớp lỗi đã cắn **6 lần** ở repo này). + +### 2.3 CQRS + Service + Controller + +**`Application/ContractSigningPlans/ContractSigningPlanFeatures.cs`** (mega-file, khuôn `ProposalFeatures.cs`): +- `CreateContractSigningPlanCommand(PeId, ApprovalWorkflowId, GhiChu?)` — validator: PE tồn tại + `Phase==DaDuyet` + (mirror cầu `CreateContractFromEvaluationFeatures.cs:53-54`) + **chưa có plan sống** + (`AnyAsync(p.PeId==x && p.Phase!=TuChoi)` → Conflict; cho tạo lại sau `TuChoi`) + workflow + `ApplicableType==ContractSigningPlan` (mirror Proposal `:258-265`). + Handler **auto-sinh Lines từ winners**: `pe.Suppliers.Where(IsWinner)`, `PeReferenceAmount = SUM Quote.IsSelected` + per winner (copy đúng phép tính cầu `:56-62`, `:88-90`) — **snapshot 1 lần**. +- `UpdateContractSigningPlanDraftCommand` — guard `Phase ∈ {DangSoanThao, TraLai}` + **cho re-pin + `ApprovalWorkflowId`** (mirror PE; tránh vết Contract UpdateDraft KHÔNG có đường re-pin). +- `Get` / `List` / `pendingMe` — detail Include Lines+DossierItems+Attachments+LevelOpinions + workflow-tree + (khuôn Proposal `:161-228`); 🔴 **inbox V2 precompute TỪ NGÀY 1** (mirror PE `ResolveV2InboxIdsAsync`) — + bẫy đã cắn Contract: thiếu cái này ⇒ **approver không mở được phiếu đang chờ chính mình** + (`ContractFeatures.cs:363-372` legacy-only, `grep ChoDuyet` = 0 hit). +- `UpsertDossierItemCommand` / `DeleteDossierItemCommand` — chỉ mở ở `DangSoanThao|TraLai` (căn cứ là **input** + của con số đang duyệt). Attachment mở mọi phase (triết lý PE S147: BE không phase-guard attachment). + +**`Infrastructure/Services/ContractSigningPlanWorkflowService.cs`** — copy `ApproveV2Async:217-394` (§2.1), adapt 3 chỗ: +1. Terminal → `Phase=DaDuyet` + **`ApplyApprovedValuesOnFinalize`**: mọi Line `ApprovedAmount ??= ProposedAmount` + tại **choke-point duy nhất** + RULE grep-4-site (khuôn `PurchaseEvaluationWorkflowService.cs:1008`). +2. Port 2 nhánh kết thúc sớm: `AllowApproverFinalize` `:870-890` + `finalizeByCcmDelegation`+`CeoApprovalThreshold` + `:892-929` (tổng so ngưỡng = `SUM ProposedAmount`). +3. Reject → 4 return-mode per-level (cờ sẵn `ApprovalWorkflowLevel.cs:116-125`, logic mirror PE + `ApplyReturnModeAsync:368-547`) — **tối thiểu `TraLai` toàn phần** nếu muốn gọn wave đầu. + +🔴 **Guard trình = `CreatedBy==actor ∨ DeptManager ∨ Admin`** — **CỐ Ý KHÁC** khuôn role-based +(`ContractWorkflowService.cs:70-79` đòi `Drafter|DeptManager`) để **né lớp 403-PMH**: PMH mang role `Procurement` +nên bị chặn ở b.13/b.17 — đúng lớp *"cơ-chế đúng, vai đi qua không có"*. Nếu owner muốn đồng nhất role-based thì +BCH users **phải được gán role `Drafter`**. + +🔴 **Notify:** mirror `LogTransitionAsync` báo Drafter (`ContractWorkflowService.cs:407-427`) **+ THÊM notify đích +danh approver của Cấp kế** — fix lỗ *notify-chỉ-Drafter* **ngay từ ngày 1**, đừng chép nợ. + +**`Api/Controllers/ContractSigningPlansController.cs`** — 🔴 **authz 2 TẦNG** (bài học #82 / S118): +class `[Authorize]` **+ per-action** `[Authorize(Policy = "ContractSigningPlans.{Read|Create|Update|Delete}")]`. +**KHÔNG chép kiểu `ContractsController.cs:12-13` class-trần** (đó chính là lỗ đang treo). Policy TỰ SINH khi key +vào `MenuKeys.All` (`Program.cs:82-89` foreach `All × Actions`). +Endpoints: `GET` list/inbox · `GET {id}` · `POST` · `PUT {id}` · `POST {id}/transitions` · dossier-items CRUD · +attachments · `DELETE {id}` (allow-list `DangSoanThao|TuChoi`, mirror PE `:1404`). + +### 2.4 Menu + Permission + FE 2 app (Pattern 16-bis 4-place mirror) + +- **BE `MenuKeys.cs`:** +4 const `Csp` / `Csp_List` / `Csp_Create` / `Csp_Inbox` **VÀ đưa CẢ 4 vào mảng `All`**. + ✅ *Lead verify `MenuKeys.cs:171`: `OffDeXuat`+3 con **đều trong `All`**; còn `Pe_*` sinh bằng **factory** + `:150`/`:154` **NẰM NGOÀI `All`** ⇒ per-action policy **không tồn tại**.* **Theo khuôn `Off_DeXuat`, KHÔNG theo `Pe_*`.** + ⇒ 2 row canonical `docs/STATUS.md` (Menu keys · Policies = |All|×4) phải bump cùng lúc. +- **Seed `DbInitializer.cs`:** 4 dòng menu (khuôn Off_DeXuat `:1817-1820`, label VN *"Kế hoạch ký kết HĐ"* / + *"Danh sách"* / *"Tạo mới"* / *"Inbox duyệt"*) + admin-permission (`:2383` — **grep MỌI site seed-permission**, + S155 ghi nhận có 2 site cho vài nhóm). **Dark-launch được bằng `IsVisible=0`** rồi bật sau. +- **FE ×2 app** (duplicate CÓ CHỦ ĐÍCH), mỗi app 4 chỗ: `App.tsx` routes · `Layout.tsx` staticMap · + `lib/menuKeys.ts` · `pages/` ×3 (khuôn bộ Proposal ~978 LOC/app: List 252 + Create 245 + Detail 386 + types 95). + Detail thêm: bảng **Lines** (đề xuất/chốt per-NCC) · bảng **DossierItems** (status chip + TVGS + scan) · + **panel workflow** (mirror PE Panel-3 ✓/●/○) · **banner "Đến lượt bạn"** (khuôn PE `blockedByV2Level`). +- **Designer admin nhận type 10:** fe-admin `ApprovalWorkflowsV2Page.tsx` map typeCode→int `:143` + các site BE + `ApprovalWorkflowV2AdminFeatures.cs`. ⚠️ **Invest tự khai KHÔNG verify được** path `fe-user` matrix-view + (`WorkflowMatrixViewPage.tsx` grep 0 hit) ⇒ **PHẢI grep lại lúc làm**, đừng tin line cũ. + +### 2.5 Cầu tiếp giáp b.12→13 (sửa 1 file) — ⚠️ chạm module Contract + +`CreateContractFromEvaluationFeatures.cs`: +(a) đọc plan `DaDuyet` của PE → có thì `giaTri = line.ApprovedAmount` khớp `w.SupplierId` **thay** `SUM` `:88-90`, +ghi **ContextNote lệch** *"giá KH x vs SUM-PE y"* vào changelog `:121-140` (khuôn audit-note D4 S134); +không có plan → giữ `SUM` + **cảnh báo mềm** (**Q11** owner nâng cứng sau). +(b) `+param ApprovalWorkflowId?` **pin V2 cho HĐ** — hiện pin V1-only `:68-71`,`:108` ⇒ HĐ V2-less trình xong +**KẸT CỨNG** `ConflictException` `ContractWorkflowService.cs:115-116`. +🔴 **Đây là "chỗ hợp đồng"** — anh chốt *"cứ từ từ"* ⇒ **wave này phải hỏi lại anh trước khi chạy**. + +--- + +## ③ CHECKLIST — CHIA WAVE (lead viết) + +**Nguyên tắc chia:** mỗi wave **deploy độc lập được**, không để nửa tính năng chết trên prod (bài học PE nút-xóa +S155: §F.0 chứng minh đợt-1-một-mình vô dụng ⇒ phải chia theo *"đẩy riêng thì người dùng thấy gì"*). + +| Wave | Tên | Chặn bởi owner? | Deploy riêng được? | +|---|---|---|---| +| **W1** | Schema + permission (dark-launch) | ❌ không | ✅ có (vô hình với user) | +| **W2** | CRUD phiếu nháp + căn cứ b.8-9 | ❌ không | ✅ có (bật menu, tạo được phiếu) | +| **W3** | Duyệt 3 trạm + finalize chốt giá | ⚠️ **Q3** (chọn cơ chế rẽ b.12) — nhưng chạy được với mặc định | ✅ có (phiếu chạy trọn vòng) | +| **W4** | Gate + checklist per-phòng | ✅ **O-Q1, O-Q2** | ✅ có (siết dần) | +| **W5** | Cầu sang Hợp đồng | ✅ **Q6, Q11 + anh cho phép chạm HĐ** | ✅ có | + +--- + +### WAVE 1 — Schema + permission scaffold (dark-launch) + +- **Mục tiêu:** đặt nền DB + quyền, **không đổi gì người dùng thấy**. +- **Vai:** `implementer-backend` · review `database-agent` (design-review migration trước apply). +- **File:** 7 entity + `ContractSigningPlanPhase.cs` + 7 EF Configuration + `ApplicationDbContext`/`IApplicationDbContext` + DbSets + Mig 69 (3-file rule) + `ApprovalWorkflow.cs` enum +slot 10 + `MenuKeys.cs` +4 const **vào `All`** + + `DbInitializer` menu 4 dòng `IsVisible=0` + admin-permission. +- **Acceptance ĐO ĐƯỢC:** + 1. `dotnet ef migrations list` → `69_AddContractSigningPlans` ở cuối. + 2. `sqlcmd -Q "SELECT COUNT(*) FROM sys.tables"` → **89 → 96** (+7). + 3. `dotnet test SolutionErp.slnx` → **562 PASS giữ nguyên, 0 fail** (chưa có test mới). + 4. `sqlcmd` đếm policy: `|MenuKeys.All| × 4` tăng đúng **+16**; grep `ContractSigningPlans.Read` có trong list policy. + 5. Menu **KHÔNG hiện** trên cả 2 FE (IsVisible=0) — screenshot hoặc `GET /api/menus` không chứa `Csp`. + 6. `dotnet ef migrations script 68 69` + script Down chạy sạch trên DB copy (reversible thật, không chỉ nhìn code). +- **Rủi ro:** seed-permission có **>1 site** (S155) ⇒ grep hết trước khi kết luận. · Enum slot 10 nếu ai đó chèn + trước ⇒ re-verify `ApprovalWorkflow.cs` ngay trước khi viết. + +### WAVE 2 — CRUD phiếu nháp + căn cứ b.8-9 (bật menu) + +- **Mục tiêu:** BCH **tạo được phiếu nháp**, điền dòng giá đề xuất + danh mục căn cứ, **chưa trình được**. +- **Vai:** `implementer-backend` (Features + Controller) · `implementer-frontend` (3 page × 2 app) · + `test-specialist` (test-after). +- **File:** `ContractSigningPlanFeatures.cs` (Create/Update/Get/List + DossierItem CRUD + attachment) · + `ContractSigningPlansController.cs` (**2-tầng authz**) · FE ×2 app 4 chỗ · `DbInitializer` bật `IsVisible=1`. +- **Acceptance ĐO ĐƯỢC:** + 1. `POST /api/contract-signing-plans` từ PE `DaDuyet` → **201**, response có `id`; DB có **N Lines = số winner** + của PE đó (`sqlcmd` đếm đối chiếu `PurchaseEvaluationSuppliers WHERE IsWinner=1`). + 2. `PeReferenceAmount` mỗi Line == `SUM(Quotes WHERE IsSelected)` của đúng NCC đó (`sqlcmd` 2 câu, so khớp). + 3. Tạo lần 2 trên **cùng PE** → **409 Conflict**. + 4. Tạo từ PE **chưa `DaDuyet`** → **400/409** (không phải 500). + 5. 🔴 **Authz 2 tầng:** user thiếu quyền gọi `POST` → **403** (không phải 200/401). User có quyền → 201. + *(Đây là phép chặn lỗ #82 tái phát — bắt buộc chạy, không được bỏ.)* + 6. FE 2 app: `npm run build` PASS ×2 · SHA256 3 file mirror **IDENTICAL**. + 7. Menu hiện đúng 4 mục tiếng Việt trên cả 2 app. +- **Rủi ro:** quên `HasQueryFilter(!IsDeleted)` ⇒ phiếu xóa mềm vẫn hiện. · UNIQUE filtered thiếu + `WHERE IsDeleted=0` ⇒ tạo lại sau xóa bị chặn oan (gotcha #57). + +### WAVE 3 — Duyệt 3 trạm + finalize chốt giá 🔴 **wave nặng nhất** + +- **Mục tiêu:** phiếu chạy trọn `DangSoanThao → ChoDuyet → DaDuyet`, chốt `ApprovedAmount`. +- **Vai:** `implementer-backend` (service) · `implementer-frontend` (panel + banner) · `test-specialist` + (**test-before cho OR-of-N** — đây là critical algorithm) · `reviewer` (adversarial trước commit). +- **File:** `ContractSigningPlanWorkflowService.cs` (copy `ApproveV2Async:217-394`) · `transitions` endpoint · + inbox V2 precompute · notify · FE workflow panel + banner "Đến lượt bạn". +- **Acceptance ĐO ĐƯỢC:** + 1. 🔴 **OR-of-N còn sống** *(phép chặn bẫy §2.1)*: workflow 1 Bước có **1 Cấp 2 người** → **người thứ hai + duyệt được** và phiếu **tiến sang Bước kế** (không đòi cả 2 ký). Test unit + 1 lượt UAT thật. + 2. **Con-trỏ đôi:** workflow **3 Bước** → sau mỗi lượt duyệt, `CurrentWorkflowStepIndex` **và** + `CurrentApprovalLevelOrder` đều đổi đúng (`sqlcmd` đọc 2 cột sau mỗi bước). + 3. **Finalize choke-point:** duyệt cấp cuối → **mọi** Line có `ApprovedAmount != NULL` + (`sqlcmd COUNT(*) WHERE ApprovedAmount IS NULL` = **0**). Test cả 3 đường tới `DaDuyet` + (thường · `AllowApproverFinalize` · `CeoApprovalThreshold`) — **grep 4-site RULE**. + 4. **Inbox:** approver Cấp đang chờ gọi `GET /inbox` → **thấy phiếu**; approver cấp khác → **không thấy**. + *(Phép chặn đúng lỗ Contract đang mắc.)* + 5. `LevelOpinions` UPSERT: duyệt 2 lần cùng cấp → DB **1 row** (không đúp), `SignedByUserId` đúng người thật. + 6. Trả lại → `TraLai`, sửa rồi gửi lại chạy **từ Cấp 1 Bước 1**. + 7. Notify: approver Cấp kế **nhận** thông báo (không chỉ Drafter). + 8. `dotnet test` → **562 + N mới**, 0 fail. +- **Rủi ro:** 🔴 **copy nhầm Proposal** ⇒ mất OR-of-N **âm thầm** (build sạch, test cũ vẫn xanh) — acceptance #1 + là lưới duy nhất bắt được. · Quên inbox precompute ⇒ lặp lại đúng lỗi Contract. + +### WAVE 4 — Gate trình + checklist per-phòng ✅ **CHẶN: O-Q1, O-Q2** + +- **Mục tiêu:** siết chất lượng đầu vào theo ý anh. +- **Chờ anh:** O-Q1 căn cứ b.8 chưa đủ `TvgsDuyet` → **cảnh báo mềm hay chặn cứng**? · O-Q2 checkbox + FO-002.01 (PRO: điều khoản/thanh toán/rủi ro pháp lý · CCM: giá vs NS) → **số hoá thành checklist** hay + giữ **comment tự do**? +- **Acceptance (khi có câu trả lời):** trình thiếu căn cứ → đúng hành vi anh chọn (toast cảnh báo **hoặc** 409); + checklist (nếu số hoá) lưu + hiện lại đúng trên phiếu đã duyệt. + +### WAVE 5 — Cầu sang Hợp đồng ✅ **CHẶN: Q6, Q11 + anh cho phép chạm HĐ** + +- 🔴 **Wave này chạm module Contract — anh đã chốt *"chỗ hợp đồng cứ từ từ"*. KHÔNG tự chạy.** +- **Chờ anh:** Q6 giá vào HĐ = `ApprovedAmount` của KH (mặc định thiết kế: **KH thắng** + audit lệch) hay + vẫn `SUM`-PE? · Q11 chưa có KH duyệt thì tạo HĐ = **chặn** hay **cảnh báo**? +- **Acceptance:** HĐ tạo từ PE có KH `DaDuyet` → `GiaTri == SUM(ApprovedAmount)`; changelog có ContextNote lệch; + HĐ pin được workflow V2 (không còn `ConflictException` kẹt cứng). + +--- + +### Ngoài mọi wave (ghi nhận, KHÔNG làm trong run này) + +- **SLA** — O-2: chỉ hiển thị. `AddDays(7)` hardcode vẫn sai nhưng **không ai enforce** ⇒ không đáng 1 wave. +- **Lỗ hổng an ninh HĐ** (`ContractWorkflowService.cs:48-66` Reject-trước-guard + controller class-trần) — + **vẫn SỐNG trên prod**, anh chốt *"từ từ"*. Nhắc đúng 1 dòng cho khỏi quên. +- **FO-002.01 auto-pass** (*"quá hạn xem như đã thông qua"*) — mâu thuẫn mô hình duyệt-chặn V2. O-2 ⇒ ghi nhận. + +--- + +## ④ §D — HẤP THỤ 6 việc sửa của `review-synthesis.md` §G (S156) + +| # | Việc review yêu cầu | Xử lý trong spec này | +|---|---|---| +| 1 | Claim SAI *"hardcoded policy fallback"* → thật là `ConflictException:115-116` (HĐ **kẹt cứng** `ChoDuyet`) | ✅ **Đã sửa** — §2.5(b) ghi đúng: pin V1-only ⇒ HĐ V2-less **KẸT CỨNG**, hỏng **cứng** không degrade êm. Lead tự xác minh trên đĩa. | +| 2 | Siết biên `b.14→19`, không phải `b.13→21` | ✅ Spec này **không nhắc lại biên cũ**; phạm vi khai rõ = **b.7→12**, khúc 13→21 để nguyên verdict `LAI` S156. | +| 3 | Thiếu Q **vai người TRÌNH** (PMH `Procurement` bị 403 `:70-79`) | ✅ **Đã xử ở thiết kế**, không đẩy thành câu hỏi: §2.3 guard trình = `CreatedBy ∨ DeptManager ∨ Admin` **cố ý khác** khuôn role-based, kèm lối thoát nếu owner muốn đồng nhất. | +| 4 | Thêm cảnh báo lỗ hổng reject — *việc RIÊNG* | ✅ Giữ đúng 1 dòng ở "Ngoài mọi wave"; **không nhét vào wave** (anh chốt *"từ từ"*). | +| 5 | Q5 thiếu phương án **(d) `skipToFinal`** đã wire | ✅ Có mặt — §1.5 liệt **4 đường rẽ b.12**, trong đó (iii) `AllowApproverSkipToFinal` `Contract:320-352`. | +| 6 | Viết lại Q2 theo **2 trục + 3 số đo cùng đơn vị từ twin thật** | ⚠️ **Trục cơ-chế ĐÓNG bởi O-1** (anh chốt tái dùng khung) ⇒ chỉ còn **trục NỘI DUNG**. Bảng dưới. | + +### Trục NỘI DUNG — 3 nhánh, 3 số cùng đơn vị + +> Số twin **đo thật**: `Proposal` = **4 bảng · 849 LOC BE · 1.956 LOC FE** (invest `wc`/`grep` trực tiếp). +> Số cho từng nhánh = **ngoại suy** từ twin đó — khai rõ đâu là đo, đâu là ước. + +| Nhánh | Bảng mới | LOC BE (ước) | LOC FE (ước) | Đánh giá | +|---|---|---|---|---| +| **(A) Phiếu độc lập** ⬅ *spec này chọn* | **7** (5 nếu cắt) | ~1.100–1.400 | ~2.000–2.400 | Vòng đời riêng, xóa/soft-delete riêng, workflow riêng type 10. **Đắt nhất về bảng, rẻ nhất về rủi ro.** | +| **(B) Mở rộng PE** (thêm cột + bảng con vào phiếu PE) | ~3 | ~600–800 | ~900–1.200 | Rẻ hơn, **nhưng**: PE `DaDuyet` là **terminal thành công** — mở lại vòng duyệt thứ 2 trên phiếu đã đóng ⇒ phá bất-biến PE, đụng cả `budgetFrozen` snapshot Mig 67. **Rủi ro cao nhất.** | +| **(C) Entity con của Contract** | ~4 | ~700–900 | ~1.100–1.400 | Kéo khúc 7→12 vào module HĐ — mà anh vừa chốt **"chỗ hợp đồng cứ từ từ"**; đồng thời HĐ chưa tồn tại ở b.7-12 (HĐ mới sinh ở b.13) ⇒ **sai thứ tự nghiệp vụ**. | + +**Lý do chọn (A)** — không phải vì rẻ, mà vì: nó là **PHIẾU** theo đúng phát biểu mục đích của anh (có đối tượng +được duyệt + 3 trạm + người chốt); (B) phá bất-biến của PE đang chạy ổn; (C) sai thứ tự nghiệp vụ và chạm vùng +anh vừa yêu cầu để yên. **Nếu owner ưu tiên cắt chi phí thì cắt trong (A)** (7→5 bảng), đừng đổi sang (B)/(C). + +--- + + diff --git a/.claude/workflows/runs/2026-07-28-S157-ke-hoach-ky-ket-hd/sub-review-claims-4.md b/.claude/workflows/runs/2026-07-28-S157-ke-hoach-ky-ket-hd/sub-review-claims-4.md new file mode 100644 index 0000000..ea792c1 --- /dev/null +++ b/.claude/workflows/runs/2026-07-28-S157-ke-hoach-ky-ket-hd/sub-review-claims-4.md @@ -0,0 +1,23 @@ +# LĂNG KÍNH 4/4 — VERIFY ĐỘC LẬP CLAIM file:line (reviewer, S157) + +Đích chấm: `spec-ke-hoach-ky-ket-hd-28-07-2026.md` §② — mọi claim `file:line` load-bearing được mở-file-thật đối chiếu. + +## Verdict + +(đang đo) + +## Claim ĐÚNG + +(đang đo) + +## Claim SAI + +(đang đo) + +## Claim KHÔNG KIỂM ĐƯỢC + +(đang đo) + +## Điểm CỦNG CỐ + +(đang đo) diff --git a/.claude/workflows/runs/2026-07-28-S157-ke-hoach-ky-ket-hd/sub-review-fidelity-2.md b/.claude/workflows/runs/2026-07-28-S157-ke-hoach-ky-ket-hd/sub-review-fidelity-2.md new file mode 100644 index 0000000..45b35c2 --- /dev/null +++ b/.claude/workflows/runs/2026-07-28-S157-ke-hoach-ky-ket-hd/sub-review-fidelity-2.md @@ -0,0 +1,13 @@ +# LĂNG KÍNH 2/4 — TRUNG THỰC VỚI NGUỒN GỐC + +## Verdict + +## Bịa + +## Rơi + +## Sai vai + +## Nống biên + +## Điểm CỦNG CỐ diff --git a/.claude/workflows/runs/2026-07-28-S157-ke-hoach-ky-ket-hd/sub-review-schema-3.md b/.claude/workflows/runs/2026-07-28-S157-ke-hoach-ky-ket-hd/sub-review-schema-3.md new file mode 100644 index 0000000..0de921c --- /dev/null +++ b/.claude/workflows/runs/2026-07-28-S157-ke-hoach-ky-ket-hd/sub-review-schema-3.md @@ -0,0 +1,27 @@ +# LĂNG KÍNH 3/4 — SCHEMA 7 BẢNG (reviewer, S157) + +> Khung RỖNG ghi TRƯỚC khi đọc (chống #53 return-garble). Nội dung điền dần trong lúc soi. + +## Verdict + +_(chưa chấm)_ + +## Bảng THỪA + +_(chưa soi)_ + +## Bảng THIẾU + +_(chưa soi)_ + +## Cột sai + +_(chưa soi)_ + +## Ràng buộc sai + +_(chưa soi)_ + +## Điểm CỦNG CỐ + +_(chưa soi)_ diff --git a/.claude/workflows/runs/2026-07-28-S157-ke-hoach-ky-ket-hd/sub-review-wave-1.md b/.claude/workflows/runs/2026-07-28-S157-ke-hoach-ky-ket-hd/sub-review-wave-1.md new file mode 100644 index 0000000..ff8d95a --- /dev/null +++ b/.claude/workflows/runs/2026-07-28-S157-ke-hoach-ky-ket-hd/sub-review-wave-1.md @@ -0,0 +1,17 @@ +# LĂNG KÍNH 1/4 — WAVE-PLAN + ACCEPTANCE (reviewer đối kháng) + +> Đích chấm: `spec-ke-hoach-ky-ket-hd-28-07-2026.md` §③ CHIA WAVE. +> Đối chứng: `runs/2026-07-27-S155-pe-delete-approver/run.md` (tiền lệ chia 2 đợt đã chạy THẬT). +> 4 trục tấn công: (a) acceptance đo được thật? (b) wave deploy độc lập thật? (c) thứ tự wave? (d) cờ CHẶN sai? + +## Verdict + +_(đang soi — điền cuối)_ + +## Điểm HIGH + +## Điểm MED + +## Điểm LOW + +## Điểm CỦNG CỐ