15 KiB
PLAN CHA — DRY-RUN TOÀN TRÌNH ĐẾN HỢP ĐỒNG CỨNG (S160, 2026-07-29)
Lệnh anh (verbatim):
run.md§"LỆNH ANH". Tóm: dry-run luôn đến Hợp đồng cứng · spec KHKK cũ ĐÚNG (ratified) · ra Plan cha + mỗi wave 1 spec-chi-tiết-cách-làm + checklist ·/fable-clonereview ·/fable-realreview chốt · hmw Opus-5-MAX chạy từng wave theo Plan cha + kiểm. Nguồn: spec cũ RATIFIEDruns/2026-07-28-S157-ke-hoach-ky-ket-hd/spec-ke-hoach-ky-ket-hd-28-07-2026.md(+NOTE W1 @S160) · lane-1sub-investigator-codebase-1.md(25 mục F-01→F-24) · lane-2sub-investigator-codebase-2.md(8 mục §0-§7) · đo tươi @S160 (lead): prod Contracts 0 active + 0 deleted (admin-bypass verifyContractFeatures.cs:298/:440). Trạng thái: CHỜ/fable-clone reviewer+/fable-real reviewerchốt cuối → rồi hmw thi công.
1. RANH + NHÃN (chốt @S159, spec cũ :27 — CẤM tự chế lại)
- GĐ1 Duyệt NCC = PE (live) · GĐ2 Kế hoạch ký kết HĐ = b.7→12 (b.7 email BỎ — Q8 ĐÓNG) · GĐ3 Duyệt hợp đồng = b.13→18 (tận dụng Contract V2) · GĐ4 Hợp đồng cứng = b.19→21, ký cứng UPLOAD-ONLY.
- Khúc b.13→21 = span verdict-LAI (toàn bộ tái dùng Contract), KHÔNG phải span GĐ3 (lane-1 F-09 bắt drift này trong chính run.md).
- Nhãn GĐ2 2 đời: "Đề xuất ký kết hợp đồng" (sáng @S159, còn trong spec cũ
:27) → superseded "Kế hoạch ký kết HĐ" (chốt cuối @S159, prodKhkk_G1seeder). Mọi label VN mới = "Kế hoạch ký kết HĐ"; class/bảng EnglishContractSigningPlan*GIỮ. - Nuance lane-1 F-11: bản cứng vật lý SINH ở b.17-18 (∈GĐ3). 🔄 @S161 anh rút GĐ4 còn 1 MỐC hệ-thống (default #8): b.19/20/21 làm trên giấy, hệ thống chỉ ghi nhận upload scan bộ cứng = xong; khối readonly GĐ3 chỉ còn LevelOpinions (duyệt điện tử), KHÔNG attachment ký nháy.
2. BỨC TRANH WAVE (hợp nhất 2 lane — đánh số GIỮ W1-W5 của spec ratified, nối W6-W8)
| Wave | Tên | GĐ | Mig? | Độ lớn | Spec file | Trạng thái |
|---|---|---|---|---|---|---|
| W1 | Schema + permission + Designer type-10 | GĐ2 | ✅ Mig 69 (7 bảng — mig DUY NHẤT toàn plan) | BE nặng | spec-wave-w1-*.md |
sẵn chạy |
| W2 | CRUD phiếu nháp + căn cứ b.8-9 + FE 3 page ×2 app | GĐ2 | ❌ | FE nặng | spec-wave-w2-*.md |
sẵn chạy (sau W1) |
| W3 | Duyệt 3 trạm + finalize chốt giá 🔴 nặng nhất | GĐ2 | ❌ | BE nặng | spec-wave-w3-*.md |
sẵn chạy (sau W2) |
| Gate + checklist per-phòng FO-002.01 | GĐ2 | ❌ (1 mig nếu số hoá checkbox) | — | KHÔNG có spec — DEFERRED | ⛔ chặn O-Q1/O-Q2; dry-run chạy default (comment tự do + cảnh báo mềm, 0 dev — lane-1 F-18) | |
| W5 | Cầu KHKK→HĐ + pin V2 (sửa bridge cũ 1 file) | GĐ2→3 | ❌ | 2 BE + 2 FE | spec-wave-w5-*.md |
anh ĐÃ MỞ gate chạm HĐ @S160 |
| W6 | Đường ống HĐ V2 (view + inbox + vai-trình) | GĐ3 | ❌ | 2-3 BE | spec-wave-w6-*.md |
sẵn chạy (độc lập W1-W3) |
| W7 | Bản cứng Hdc_* 7 leaf trang thật |
GĐ4 | ❌ (option A derive; B = 1 mig nếu anh muốn field tường minh) | FE nặng + 1-2 BE | spec-wave-w7-*.md |
sẵn chạy (build song song; verify cần HĐ phase-9 từ W5/W6) |
| cross | — | — | spec-wave-w8-*.md (= biên bản quyết định) |
⛔ GỠ KHỎI DRY-RUN — anh chốt O-A @S160 (để mở, không siết; đúng ❹ "hiển thị hết để góp ý" + O-3 "hợp đồng cứ từ từ"). Rủi ro (Reject-trước-guard + attachment IDOR + Create ăn số) = chấp nhận có ý thức, khoanh bằng ZZTEST + backup DB (§4) | ||
| Hồ sơ hoàn tất + thống kê lưu trữ | GĐ4 | ❌ | — | KHÔNG có spec — DEFERRED | không chặn vòng chạy (lane-1 F-19) |
Thứ tự thi công (đồ thị phụ thuộc):
W1 → W2 → W3 ──┐ (GĐ2: phiếu KHKK sống trọn vòng)
├→ W5 → [E2E] (cầu: HĐ sinh từ KHKK, pin V2, hết kẹt ConflictException)
W6 ───────────┘ (đường ống HĐ V2: mở view+inbox ChoDuyet (GĐ3 approver) VÀ view
DaPhatHanh phase-9 (GĐ4 actor bản-cứng) — PHẢI trước W5/W7 VẬN HÀNH;
build song song W1-W3 được vì 0 đụng file KHKK)
W7 (build song song từ đầu; 🔴 VERIFY PHỤ THUỘC W6-view — không có W6 mở phase-9 thì trang
Bảng cứng RỖNG + 403 cho HRA/BOD/CCM non-admin: F-C2/F-01)
W8: GỠ (O-A — anh chốt để mở @S160)
Dry-run E2E đạt khi: W1+W2+W3+W6+W5+W7 xong — 1 phiếu PE DaDuyet (dự án ZZTEST) → phiếu KHKK → 3 trạm duyệt → HĐ per-winner pin V2 → 3 trạm HĐ → DaPhatHanh → trang Bảng cứng upload scan ký/dấu/lưu. Toàn bộ 1 migration duy nhất (Mig 69, W1) — GĐ3+GĐ4 = 0 mig (lane-2 §6).
🔴 CẠNH ĐỒ THỊ THÊM (review F-C2/F-01/F-06): W7-verify đòi W6-đã-mở-view-phase-9; b.14-16 (soạn/góp ý/đàm phán) = trạng thái Nháp HĐ + comment + update-draft trong W5/W6 (KHÔNG wave riêng — default #13). Cần dựng 1 ApprovalWorkflow type=3 THẬT 3-trạm qua Designer (seed chỉ 1 trạm — §4 bước mới).
3. BASELINE ĐO TƯƠI @S160 (căn mọi acceptance vào đây, KHÔNG chép số cũ)
- Prod: Contracts = 0 active + 0 deleted (curl admin 6 phase + tổng + /deleted; admin bypass
ContractFeatures.cs:298) — sổ S156 "7 HĐ V1 thật" đã STALE. ⇒ lỗ Reject chưa có nạn nhân thật; anh chốt O-A (để mở) ⇒ W8 GỠ, rủi ro khoanh bằng ZZTEST + backup. - Menu 142 key (root 12) ·
Khkk_*= 7 key (G1 + 6 leaf, CanRead 13/13, leaf →/coming-soon) ·Hdc_*= 7 leaf per-ContractType (KHÔNG phải 6-leaf — lane-2 §3 sửa grounding) ·Ct_*21 key mới. - Test 562 (45D+517I) — acceptance neo TÍNH-CHẤT: "PASS 0 fail + số-sau ≥ số-trước-đo-ngay-trước-wave + N + tên test tồn tại", CẤM literal "562+N" (lane-2 §7).
- Mig cuối = 68 · 89 bảng · mig kế = 69 (chỉ W1).
- Bundle: admin
D0sXA0fe/DWDbm5As· userY6dW_5CM/6YIAufJR. MenuKeys.KeHoachKyKetconst CÓ (MenuKeys.cs:41+ comment dặn tái dùng) nhưng NGOÀIAll⇒ 0 policyKeHoachKyKet.*runtime (đo @S160).Contracts∈ All:168⇒ 4 policyContracts.*sống sẵn.
4. CHIẾN LƯỢC DATA DRY-RUN (lane-2 §4 + đo mới)
- Dự án test
ZZTEST+ 1-2 NCC test tạo qua UI (0 code): mã HĐ dry-run ăn sequence per-prefix riêng (ContractCodeGenerator.cs:33,:40-53) — không đụng chuỗi số dự án thật; NGOÀI seed-list ⇒ xóa mềm không bị re-seed (#75/#76). CẤM đặt Code trùng seed-list (twin FLOCK-01). 🔴 CẤM tạo HĐ loạiHopDongMuaBan(anh bỏ "MB" @S161 — default #7). - PE side có data THẬT — phiếu KHKK dry-run tạo từ PE test mới (mỗi vòng thử cần PE mới vì idempotency
pe.ContractId:59-60,:145). 2-bis. 🔴 DỰNG WORKFLOW type=3 3-TRẠM (review F-02 — bắt buộc E2E): seed prod chỉ cóQT-HD-V2-001= 1 trạm CCM (DbInitializer.cs:239-265, approver binh.le). E2E hứa 3 trạm (PRO→CCM→CEO) + b.19 BOD ⇒ admin phải tự dựng 1 ApprovalWorkflowApplicableType=33 Bước qua Designer (0 code — Designer type-3 đã có) TRƯỚC khi chạy W5. Tương tự KHKK type=10 cần 1 workflow 3-trạm (W1 Designer). Ghi là bước setup thủ công, không phải code wave. - KHÔNG cờ IsDryRun (blast cardinality mọi list/inbox — bài S87/S88).
- Hố rollback khai trước: HĐ ≥ phase 5 KHÔNG xóa được qua API (
ContractFeatures.cs:632-633so sánh SỐ) → HĐ test DaPhatHanh nằm lại (lọc bằng ZZTEST) hoặc admin-override về DangSoanThao (ContractWorkflowService.cs:191-198) rồi xóa. Phiếu KHKK/PE DaDuyet cũng không xóa (allow-list). Sequence không rollback (gap số chấp nhận — comment repo:113-115). - Notification bắn cho user thật (in-app, không tắt per-record) — dặn team trước đợt dry-run.
- Backup DB trước đợt (
scripts/backup-sql.ps1). - 🔴 HỐ AUTHZ CHẤP NHẬN CÓ Ý THỨC (O-A anh chốt @S160 — review F-S2/F-O1) — disclose để không ai tưởng đã kín: vì để mở, trong dry-run mọi user đăng nhập: (i) TraLai/TuChoi bất kỳ HĐ bất kỳ phase (Reject-trước-guard
ContractWorkflowService.cs:49-66); (ii) upload/xóa/tải attachment (kể cả scan đã ký) của HĐ bất kỳ — 3 handlerContractAttachmentFeatures.cs:56-1520 IDOR + 0 phase-guard; (iii) Create HĐ ăn sequence thật nếu chọn dự án thật. ⇒ CHỈ chạy dry-run trên ZZTEST + backup trước; đây là rủi ro anh nhận, KHÔNG phải lỗ chưa biết. (Khác KHKK GĐ2: module MỚI vẫn build authz 2-tầng đúng #82 — "để mở" chỉ áp module Contract sẵn có.)
5. BẢNG DEFAULT 13 QUYẾT ĐỊNH (dry-run chạy bằng default ĐẢO ĐƯỢC — anh veto lúc nào cũng kịp; #11 ĐÃ RESOLVED O-A)
| # | Câu treo | DEFAULT dry-run | Đảo thế nào | Nguồn |
|---|---|---|---|---|
| 1 | Q3 điều kiện rẽ b.12 | KHÔNG rẽ sớm — đường THƯỜNG 3 trạm, CEO ký thật (2 đường sớm anh từng phán "PHÁ VỠ" S155) | bật cờ workflow (0 code) | spec cũ :67-71, W3 :362-365 |
| 2 | Q6 giá vào HĐ | ApprovedAmount KH thắng + ContextNote lệch vào changelog |
đổi 1 nhánh if trong bridge | spec cũ §2.5 + lane-2 §2(b) |
| 3 | Q11 tạo HĐ khi chưa có KH | cảnh báo mềm — 2 đường song song (PE→HĐ tắt vẫn sống) | nâng 409 chặn cứng | lane-2 §2(d) |
| 4 | O-Q1 căn cứ thiếu TvgsDuyet | cảnh báo mềm khi trình | 409 | spec cũ W4 |
| 5 | O-Q2 checkbox per-phòng | comment tự do LevelOpinions (W4 DEFERRED, 0 dev) | số hoá checkbox (wave riêng) | lane-1 F-18 |
| 6 | Mã phiếu KHKK | ✅ RESOLVED — anh OK @S161: KHKK/{YYYY}/{Seq:D3} qua WorkflowAppCodeSequence (RG-001 v02 CÂM về phiếu); phép đo format thêm @W1 §③-B + W2 §③-B (review A6: quyết owner-visible phải có acceptance) |
đổi format 1 chỗ CodeGen | lane-1 F-07 + spec cũ :128 + owner @S161 |
| 7 | Viết tắt "MB" (Mua bán) — codegen tự chế, KHÔNG có trong RG-001 v02 | ✅ RESOLVED — anh chốt @S161 (verbatim): "Bỏ cái MB đi" ⇒ (i) dry-run CẤM tạo HĐ HopDongMuaBan — không mã MB nào được sinh (guard W5 §③-C); (ii) ContractCodeGenerator.cs:25 "MB" = nợ đã tuyên án, format thay thế (PO-format theo comment :18 hay khác) làm CHUNG đợt sửa Contract service — O-3 "cứ từ từ" còn hiệu lực, KHÔNG đổi codegen giữa dry-run |
đổi 1 case switch khi anh chốt format thay | lane-1 F-06/F-21 + owner @S161 |
| 8 | GĐ4 vật mang | ✅ RESOLVED — anh chốt @S161 (verbatim): "trình ký sẽ hiển thị full form doc có sẵn trong mục FORM -> điền đầy đủ vào luôn -> Duyệt qua (Nếu có) -> Upload file cứng lên -> Là xem như xong" ⇒ A-RÚT-GỌN 1-MỐC: 0-mig + 0 enum-extend — badge duy nhất "Đã lưu bản cứng" = has(SealedCopy SẴN); ScannedSigned — F-C6/gotcha #71). Option B RETIRED |
nâng field 4-mốc nếu vận hành thật đòi — backfill từ attachment sẵn | lane-2 §3 + owner @S161 |
| 9 | Số bộ gốc / nơi lưu / ngày giao | note TÙY CHỌN khi upload SealedCopy (hạ từ "bắt buộc" theo 1-mốc @S161 — "upload = xong", không chặn thao tác) | option B field tường minh | lane-1 F-13/F-14 (nguồn câm) |
| 10 | Thời điểm gán mã HĐ (QT đòi b.17, máy gen ở create + terminal) | GIỮ máy hiện trạng (create-time ContractFeatures.cs:113-118 — có mã TRƯỚC khi in, sớm hơn cả QT đòi) + khai lệch trình tự so QT |
dời site gen | lane-1 F-16 + lane-2 §1-b.13 |
| 11 | Authz Contract | ✅ RESOLVED — anh chốt O-A @S160: ĐỂ MỞ, không siết. W8 GỠ. Rủi ro disclose §4.7 | (anh đổi ý lúc nào cũng kịp — bật lại W8 = 1-2 file) | owner @S160 |
| 12 | Grant quyền KHKK (W1) | 🔴 UPGRADE-if-exists (SeedProcurementMasterAccessAsync:2367-2374 khuôn), chạy SAU revoke — KHÔNG insert (row KeHoachKyKet/Khkk_* seed CanRead-only sẵn ⇒ insert = skip-existing no-op ⇒ 403 giết dry-run, review F-S1/F-O3/F-O4). Cấp CanCreate/CanUpdate cho 13/13 role (đúng O-A "hiển thị hết") + Delete cho Drafter/Admin |
siết nhóm hẹp hơn nếu muốn | review 4-lane hội tụ |
| 13 | b.14-16 (soạn/góp ý/đàm phán HĐ) | Nháp HĐ + comment thread + update-draft trong W5/W6 — KHÔNG wave riêng | thêm Bước workflow admin-config (0 code) nếu muốn trạm thật | lane-1 F-19 + review F-06 |
6. GATE 5-ANCHOR (bắt buộc TRƯỚC MỖI WAVE — HANDOFF, reviewer đề @S157, gotcha-proof)
Trước thi công wave: mở 5 anchor của wave đó (danh sách trong từng spec-wave §③) — ≥1 trượt ⇒ chấm lại TOÀN BỘ anchor wave đó rồi mới code. Anchor per-wave lấy từ lane-2 §7 (đã re-verify 07-29) + spec cũ.
7. KHUÔN ACCEPTANCE + CICD (mọi wave)
- Test:
dotnet test SolutionErp.slnxPASS 0 fail · baseline = số test đo NGAY TRƯỚC wave bằng chính lệnh đó (KHÔNG literal "562" — nó tự lão-hoá; review F-B8) · số-sau ≥ baseline + N · tên test pin cụ thể theo KHUÔN REPO (anh chốt @S160: English-predicate + danh-từ-VN-không-dấu, vdApproveV2_LastLevel_TransitionsToDaPhatHanh— KHÔNG vị-ngữ-thuần-Việt; census repo 0/460 tên kiểu cũ, review F-B7). - FE:
npm run build×2 app PASS; mirror SHA khi cookie-cutter. - Deploy: cicd 3-chân-kiềng — CI run PASS · bundle byte-marker (#77, control ÂM phải ĐỘC QUYỀN — bài đợt-5) · data prod verify bằng curl API (SSH-SQL client chết S134/S148; sqlcmd chỉ để local/Dev).
- Menu/permission: CHỈ qua seeder (gotcha #84) — mọi thay đổi grant đi cùng commit wave.
8. PIPELINE CÒN LẠI (đúng lệnh anh)
- ✅ 2
/fable-real invest(xong, verified) → 2 ✅ Plan cha + specs (file này + 7 spec-wave) → 3 ⏳/fable-clone reviewer(ensemble N-lane chấm bộ plan+spec) → 4 ⏳/fable-real reviewerchốt cuối → 5 ⏳ hmw Opus-5-MAX: mỗi wave = 1 đợt hmw, args con-trỏ vào ĐÚNG spec-wave file (B6 args-mỏng), worker đọc spec + thi công + tự chạy checklist §③; lead + cicd-monitor verify 3-chân-kiềng sau mỗi wave; gate 5-anchor chạy TRƯỚC mỗi đợt.
- Điều KHÔNG tự làm khi chưa có gật riêng: O-C (vá
ContractWorkflowService.cs:49Reject-guard — chạm service HĐ, O-3 còn hiệu lực cho phần SỬA service) · số hoá W4 · option B GĐ4.