wal: flush 20260812T1727

This commit is contained in:
pqhuy1987
2026-08-12 17:27:21 +07:00
parent a4f81d9913
commit db9cf99fdf
7 changed files with 215 additions and 8 deletions

View File

@ -8,11 +8,13 @@ goal: YC-029 "SOLTUON-12-08-2026" — 5 điều chỉnh GĐ3 sau review prod c
chain:
[x] /tiep S190 trọn (sàn-3 ⑤ cân sổ · verify site+marker+A/059 Phase=10 · tick 64 · se=0/all=0
· ctx-verifier 13 cờ → vá run.md S189 + di-trú 2 carry vào HANDOFF + WAL số mục)
[!] B1 /fable-real investigator-codebase — invest 5 điều chỉnh + đối chiếu khuôn KHKK/NCC
→ propose wave+checklist. #53 ×2 (return rỗng) nhưng artifact cứu: lượt-1 Thứ-1+Thứ-3
(root-cause A+B) · B1b Thứ-2+Thứ-4 (máy per-loại OK, lẫn = 3 lỗ UI; thiếu nút Xóa màn
duyệt). wf: B1c wf_09951efa-970 chốt Thứ-5+Đọc-ý+PROPOSE · sub-invest-b1-fable.md
[ ] B2 /fable-clone reviewer — review spec/wave B1 (≥3 lane opus, đóng-gói C2)
[x] B1 invest TRỌN 7/7 mục qua 3 chặng (#53 ×2, artifact cứu 100%): root-cause bug menu
A+B · máy per-loại OK (lẫn = 3 lỗ UI) · Thứ-5 đã có ~95% (thiếu 1 nhánh resolvePath
fe-admin #50) · 5 wave. LEAD VERIFY 4/4 claim + SPEC GHI:
runs/2026-08-12-S190-soltuon-dieu-chinh-hd/spec-soltuon-dieu-chinh-hd-12-08-2026.md
[!] B2 /fable-clone reviewer — 3 lane opus (lens correctness · khuôn+scope · acceptance).
wf: wf_d9a14945-190 · artifact sub-review-b2-lens{1,2,3}.md · xong thì lead synthesize
→ sửa spec → B3
[ ] B3 /fable-real ×2 style: front-end-reviewer-style + back-end-reviewer-style → spec-list
style đồng nhất khuôn KHKK/DuyetNCC
[ ] B4 /fable-clone ×2 style FE/BE — review lại 2 spec style

View File

@ -50,3 +50,21 @@ HEAD lúc mở run: ae1dcf4a · HMW-mode ON · lead Opus 5 MAX (default)
- **B1c chặng chót** `wf_09951efa-970`: 3 mục cuối (Thứ-5 admin Designer · Đọc-ý · PROPOSE
spec/wave/checklist — gợi ý W1 bug-menu → W2 cây-trái → W3 slice-WfView → W4 admin-7-leaf
→ W5 nút-Xóa-BE-nhỏ, lane được quyền chỉnh).
- **B1c KẾT QUẢ** (19 tool-use · 157K · ~11' · return SẠCH): VERDICT READY 3/3. Phát hiện
đắt: **Thứ-5 ĐÃ CÓ ~95% @S188** (7 menu-row `AwV2_Hd*` seed by-construction Order 11..17 +
Designer family-slice + route đủ) — thiếu ĐÚNG 1 nhánh `Hd*` trong resolvePath fe-admin
`Layout.tsx:175-189` ⇒ 7 leaf DROP IM LẶNG #50 (mà comment ngay đó tự cảnh báo #50 — ca
"vá tệp A quên tệp B" tái diễn). Đọc-ý 4 ĐÚNG / Đ3 CẦN-CHỈNH (không phải #34/#50/#85
2 lỗ trên-thuốc-#34). Wave chốt: W1 GỘP danh-tính + slice (2 nửa 1 thay đổi) → W2 cây-trái
→ W3 pendingMe → W4 admin-5-dòng (song song) → W5 Xóa V2-aware (khuôn BE = PE
PeSoftDeleteFeatures.cs:110-114, KHÔNG phải KHKK).
- **LEAD VERIFY B1** (@S190): 4/4 claim load-bearing grep độc lập KHỚP (seed 2296/2306 ·
awV2Match 0-Hd + control KhkkN · Layout:151-152 nguyên văn · delete-guard so số — đính
chính path `Application/Contracts/ContractFeatures.cs`; + lead phát hiện thêm: đường xóa
= `db.Contracts.Remove` ⇒ W5 phải xác minh soft-vs-hard trước khi nới guard). **SPEC ghi:**
`spec-soltuon-dieu-chinh-hd-12-08-2026.md` (3-mục H21).
- **B2 phóng** `wf_d9a14945-190` — /fable-clone reviewer ensemble 3 lane opus:
label `WF reviewer b2-lens1-correct s190` (correctness W1/W2 + regression) ·
`WF reviewer b2-lens2-khuon s190` (fidelity khuôn + scope + đọc-ý cần hỏi lại anh) ·
`WF reviewer b2-lens3-accept s190` (acceptance chạy-được + Goodhart + W5 khuôn PE).
Artifact: sub-review-b2-lens{1,2,3}.md.

View File

@ -0,0 +1,103 @@
# SPEC — SOLTUON-12-08-2026 (YC-029: 5 điều chỉnh GĐ3 đồng-khuôn KHKK/Duyệt NCC)
> H21 spec-file 3-mục. Engine propose = `sub-invest-b1-fable.md` (Fable-5, 3 chặng B1/B1b/B1c).
> Lead verify @S190: 4/4 claim load-bearing spot-check KHỚP (seed `AwV2_Hd*` by-construction ·
> fe-admin `Layout.tsx:175-189` 0 nhánh `Hd` + control `KhkkN:188` · fe-user `Layout.tsx:151-152`
> đúng nguyên văn · delete-guard so số tại `src/Backend/SolutionErp.Application/Contracts/ContractFeatures.cs:896-897`
> — lane ghi thiếu segment path `Contracts/`, đã đính chính). Trạng thái: CHỜ B2 review (fable-clone).
## ① Tính-năng / đề-bài
Đồng-khuôn GĐ3 "Duyệt Hợp đồng" với KHKK + Duyệt NCC prod (luật owner S176: đã-deploy = CHUẨN),
theo 5 điều chỉnh anh chốt @S190 sau khi review prod (YC-029, ảnh kết tinh `anh-mo-ta.md`):
- **Đ1** cây toàn trình DỜI sang cột TRÁI trang `my-contracts` làm trục điều hướng (khuôn KHKK:
cây trái 400px, flat-list = fallback trong CÙNG panel, panel-3 dashed/WorkflowHistory).
- **Đ2+Đ3** menu 7 loại HĐ mang đúng danh-tính trục ContractType — hết click-nhảy-lẫn (bug A:
`?type=3` ApplicableType đá accordion; bug B: `phase=9` transient 2-leaf-cùng-sáng), leaf
"Luồng duyệt" slice đúng họ `HD-*` của loại. Máy per-loại ĐÃ XONG @S188 — toàn bộ là UI.
- **Đ4** bộ thao-tác theo hành-vi KHKK: Đang-duyệt ở lại cùng trang (`pendingMe=1`), Đã-duyệt
danh-tính chuẩn (`view=approved`), nút XÓA ở màn duyệt theo cờ per-Cấp `AllowApproverDelete`.
- **Đ5** admin Designer hiện 7 leaf per-loại (menu-row + policy + Designer + route ĐÃ seed @S188
— thiếu đúng 1 nhánh `resolvePath` fe-admin ⇒ drop im lặng #50).
Tổng: **FE-only 4 wave + 1 wave BE nhỏ (0 migration)**. Đợt này PROPOSE + IMPLEMENT + gate
per-wave; **DEPLOY CHỜ** (CI runner workspace còn hỏng P0 — vá-gốc = CỬA NGOÀI-GIỜ; push gom).
## ② Cách implement (per điều chỉnh — file:line đã verify)
- **Đ1 (W2):** `fe-user/src/pages/contracts/MyContractsPage.tsx:153` grid `[320px_1fr_360px]`
`[400px_1fr_360px]`; panel-1 = `PipelineTreePanel currentStage={3} hideHeader hideSearch` +
flat-list fallback server-side trong CÙNG panel (khuôn `KhkkListPage.tsx:171` +
`KhkkListPanel.tsx:21-24,366-371`); panel-3 = dashed khi chưa chọn / `WorkflowHistoryPanel`
khi chọn (QĐ-2 `KhkkListPage.tsx:252-256` — CẤM cây ở panel-3). Bịt gap:
`usePipelineStages.ts:377` leaf GĐ3 `navigate('/contracts/…')` → thêm opt-in
`stage3Options.onOpenContract` (analog `onOpenKhkk` `:185,:244`) + wire `PipelineTreePanel.tsx:111`
→ chọn vào panel-2 qua `?id=`. Host CHỈ fe-user (fe-admin không có MyContractsPage) nhưng
`usePipelineStages` + `PipelineTreePanel` = cặp MIRROR ×2 app → sửa cả 2 bản SHA-identical.
- **Đ2+Đ3 (W1):** `fe-user/src/components/Layout.tsx:151` WfView → `/contracts/workflow-matrix?ct=N`
(BỎ `type=3` khỏi URL — trang tự suy applicableType=3 theo path, khuôn alias-path khkk sẵn) ·
`:152` thêm `&view=approved` (khuôn PE `:180-185`, giữ `phase=9` làm lọc thật) · sửa comment
stale `:141-142` · accordion `:470-485` ưu tiên `ct` + loại trừ pathname `/contracts/workflow-matrix`
khỏi nhánh else (phòng thủ 2 lớp) · `fe-user/src/pages/pe/WorkflowMatrixViewPage.tsx` thêm
nhánh đọc `ct` → lọc `code === 'HD-<token>'` y khuôn nhánh `group` `:84-98` (map ContractType→mã:
const cục bộ 7-entry — fe-user KHÔNG mirror lib fe-admin, `contractWorkflowFamily.ts:16-18`
tự khai fe-admin-ONLY). fe-admin KHÔNG đụng (Ct_* bị ẩn `:248` + cố ý không có trang matrix).
- **Đ4-Đang-duyệt (W3):** `fe-user/Layout.tsx:150` `Ct_*_Pending``/my-contracts?type=N&pendingMe=1`
(khuôn PE `:176`; BE param `pendingMe` sẵn) + `MyContractsPage` đọc `pendingMe` gửi server;
GIỮ `/inbox` làm hộp thư tổng. (`[carry:w7b-inbox-param]` liên quan — KHÔNG gộp vào đợt này.)
- **Đ5 (W4):** `fe-admin/src/components/Layout.tsx` import `findContractWfByTypeCode` từ
`@/lib/contractWorkflowFamily` + thêm sau `:188`:
`if (findContractWfByTypeCode(code)) return '/system/approval-workflows-v2/' + code`.
KHÔNG dùng `?ct=`/`?family=` — khuôn họ này là path-param `:typeCode` y hệt `KhkkN{n}`.
KHÔNG sửa DbInitializer (label đã khớp khuôn).
- **Đ4-nút-Xóa (W5, BE nhỏ 0-mig):** mirror khuôn PE prod `PeSoftDeleteFeatures.cs:110-114`
(3 điều-kiện: ChoDuyet · ĐÚNG-LƯỢT actor · `matchingLevel.AllowApproverDelete`). Việc: nới
delete-guard `Application/Contracts/ContractFeatures.cs:896-897` (đang so SỐ
`Phase >= DangInKy` — chặn nhầm V2 ChoDuyet=10/TraLai=98/TuChoi=99, finding S160) thành
V2-aware + nhánh xóa-bởi-approver đọc cờ (cột `ApprovalWorkflowLevels.AllowApproverDelete`
Mig 68 DÙNG CHUNG V2 — Designer round-trip sẵn cho `HD-*`:
`ApprovalWorkflowV2AdminFeatures.cs:475,883,1288`) + DTO expose cờ (khuôn
`PurchaseEvaluationDtos.cs:138`) + FE `ContractWorkflowPanel.tsx` thêm mục Xóa mirror
`KhkkWorkflowPanel.tsx:12-14,118,233` (panel SHA-mirror ×2 app).
🔴 Lead thêm khi verify: đường xóa hiện tại là `db.Contracts.Remove(entity)` — wave PHẢI
XÁC MINH interceptor soft-delete (AuditableEntity `IsDeleted`) đứng sau Remove hay không
TRƯỚC khi nới guard; màn "Đã xóa" (`/contracts/deleted`) đang đọc soft-delete ⇒ nếu Remove
là hard-delete thật thì nhánh xóa-bởi-approver phải đi đường soft-delete như PE, KHÔNG
tái dùng Remove.
## ③ Checklist — 5 wave + acceptance ĐO ĐƯỢC
Thứ tự: **W1 → W2 → W3** (W3 sau W1 vì cùng vùng `Layout.tsx:148-152`); **W4 song song** bất kỳ
lúc; **W5 cuối** (BE + test-before guard). Mỗi wave: implement → build → reviewer-gate → tick.
- **W1 — Danh-tính menu + slice per-loại (fe-user):**
✅(i) tái-hiện A: mở nhóm 01 → click "Luồng duyệt" → accordion GIỮ nhóm 01, leaf sáng đúng;
✅(ii) tái-hiện B: `?type=1&phase=9&view=approved` → CHỈ leaf "Đã duyệt" sáng;
✅(iii) WfView `ct=1` chỉ hiện họ `HD-TP` (0 dòng QT-HD-V2-001/loại khác);
✅(iv) `grep -rn "workflow-matrix?type=3" fe-user/src` = 0-hit + control dương
`grep "workflow-matrix?ct=" fe-user/src` ≥1;
✅(v) `npm run build` fe-user PASS.
- **W2 — Cây toàn trình sang trái (fe-user + cặp mirror ×2 app):**
✅(i) grep `grid-cols-\[400px` MyContractsPage ≥1;
✅(ii) click leaf HĐ trên cây → panel-2 mở detail, URL Ở LẠI `/my-contracts`;
✅(iii) gõ search → flatMode;
✅(iv) cặp `usePipelineStages.ts`/`PipelineTreePanel.tsx` 2 app SHA256 khớp từng cặp;
✅(v) build 2 app PASS.
- **W3 — Đang-duyệt về cùng trang (fe-user):**
✅(i) grep `pendingMe=1` nhánh Ct_ Layout ≥1; ✅(ii) leaf "Đang duyệt" ở lại `/my-contracts`,
list khớp inbox cũ cùng user; ✅(iii) build PASS.
- **W4 — Admin Designer 7 leaf (fe-admin ≈5 dòng):**
✅(i) sidebar "Quy trình duyệt (Mới)" đủ 17 leaf (2 NCC + 8 KHKK + 7 HĐ Order 11..17);
✅(ii) click "QT Duyệt HĐ — Thầu phụ" → panel khoá mã `HD-TP`, history CHỈ HD-TP;
✅(iii) build fe-admin PASS.
- **W5 — Nút Xóa màn duyệt V2-aware (BE nhỏ + FE, 0-mig, TEST-BEFORE guard):**
✅(i) `dotnet test SolutionErp.slnx` PASS + test mới: TraLai/TuChoi Drafter xóa được ·
ChoDuyet CHỈ approver-đúng-lượt-có-cờ · cờ tắt → 403/409 · V1 `Phase>=DangInKy` VẪN chặn
(regression S160) · xác minh soft-vs-hard-delete (lead-note ②);
✅(ii) grep `AllowApproverDelete` Contract-delete-path ≥1 (trước: 0-hit);
✅(iii) panel 2 app SHA-identical + nút Xóa chỉ render khi DTO cờ bật; ✅(iv) build 2 app PASS.
**Ngoài wave — CHỜ ANH (không tự làm):** ① gỡ-ghim/thay `QT-HD-V2-001` bằng 7 bản `HD-*` nếu
muốn "tuyệt-đối mỗi loại 1 quy trình" (van xả `ContractWorkflowGuards.cs:141-143` là CỐ Ý —
việc Designer/DATA 0 code); ② port 3 món KHKK cố-ý-không-port (skipToFinal · duyệt-thay-CEO ·
banner-kết-thúc-sớm) = cần nở `ContractDetail` DTO.

View File

@ -4,7 +4,13 @@
## Đọc-ý 5 điều chỉnh (xác nhận/bác từng cái)
(pending)
| # | Điều chỉnh | Đọc-ý lead (anh-mo-ta.md) | Verdict | Căn cứ (từ ground-truth 4 mục) | Tin |
|---|---|---|---|---|---|
| 1 | Dời cây toàn trình | DỜI panel cây PHẢI→TRÁI, bố cục giống KHKK; "danh-sách đi đâu" = invest đề xuất | **ĐÚNG** | Khuôn KHKK: cây = panel TRÁI 400px làm trục điều hướng, danh-sách KHÔNG cần cột riêng — flat-list là chế-độ FALLBACK trong CÙNG panel khi search/lọc (`KhkkListPage.tsx:171` + `KhkkListPanel.tsx:21-24,366-371`), panel-3 = dashed/WorkflowHistory (QĐ-2 `:252-256`) | CAO |
| 2 | Mỗi loại HĐ 1 quy trình riêng | "đúng 7 workflow HD-* đã seed @S188 — UI/menu/trang từng loại lọc ĐÚNG, không lẫn" | **ĐÚNG** (kèm đính-chính phạm-vi) | Máy ĐÃ per-loại TRỌN: 7 mã `HD-*` (`ContractApprovalWorkflowFamily.cs:33-42`) + 4 cửa guard 409 + PA-A tự pin — dữ-liệu KHÔNG lẫn; việc còn lại CHỈ là 3 lỗ UI: WfView không đọc `ct` (`WorkflowMatrixViewPage.tsx:57,67`) + 2 bug menu A/B + comment stale `Layout.tsx:141-142` | CAO |
| 3 | Menu lẫn lộn, click nhảy | BUG điều hướng — "nghi lớp gotcha #34 / #50 / #85, lane truy bằng code" | **CẦN-CHỈNH** (đúng bệnh, sai lớp nghi) | KHÔNG phải #34/#50/#85 — 2 lỗ TRÊN thuốc-của-#34: (A) leaf WfView cả 7 loại mang `?type=3` (ApplicableType) mà accordion auto-expand đọc `type` như ContractType → mutex đóng nhóm đang mở (`Layout.tsx:151``:470-485`, chuỗi tái-hiện được); (B) `phase=9` transient làm danh-tính → 2 leaf cùng sáng (`:152``:350` — đúng ca PE đã chữa `view=approved` `:180-185`) | CAO |
| 4 | Thao tác/duyệt/xóa y khuôn KHKK | Bộ hành-vi submenu + nút trong màn duyệt theo Y KHUÔN KHKK prod (owner S176) | **ĐÚNG** (1 ranh chờ anh) | Bảng lệch 6-leaf đã đo: 3 🔴 (Luồng-duyệt slice · Danh-sách = Thứ-1 · Đã-duyệt `view=approved`) + 1 ⚠️ (Đang-duyệt → `pendingMe=1` thay trang Inbox rời) + 2 ✅; màn duyệt THIẾU nút Xóa (cột `AllowApproverDelete` Mig 68 DÙNG CHUNG V2, sẵn cho `HD-*`) — delta BE nhỏ 0-mig; **ranh:** 3 món cố-ý-không-port (skipToFinal/duyệt-thay-CEO/banner-kết-thúc-sớm, chặn bởi `ContractDetail` DTO 0 field) — mặc-định GIỮ khai-lệch, port = quyết của anh | CAO (ranh 3-món: VỪA) |
| 5 | Admin Designer per-loại HĐ | Sidebar admin cần mục RIÊNG từng loại y khuôn KHKK N1-8; "lane xác nhận đã có chưa hay đang gộp" | **ĐÚNG — và ĐÃ CÓ ~95%** | Menu-row + 28 policy + Designer family-slice + route ĐÃ land S188 (`DbInitializer.cs:2303-2310` · `ApprovalWorkflowsV2Page.tsx:314-396` · `App.tsx:80`); thiếu ĐÚNG nhánh `Hd*` trong `resolvePath` fe-admin (`Layout.tsx:175-189`, 0-hit `Hd` + control dương `KhkkN:188`) ⇒ leaf drop im lặng #50 — fix ≈ 5 dòng | CAO |
## Thứ-1 layout cây toàn trình
@ -87,8 +93,43 @@
## Thứ-5 admin Designer per-loại
(pending)
**VERDICT: ~95% ĐÃ CÓ từ S188 — BE seed 7 menu-row + policy + Designer slice + route đều ĐỦ; thiếu ĐÚNG 1 nhánh `resolvePath` fe-admin ⇒ 7 leaf DROP IM LẶNG khỏi sidebar (đúng gotcha #50). Fix ≈ 5 dòng fe-admin, 0 BE, 0 mig, 0 seed mới.**
1. **Sidebar fe-admin = DB-menu-driven, KHÔNG phải menu tĩnh** (bác giả-thuyết cũ): render từ `AuthContext.menu``filterForAdmin` lọc ĐỆ QUY theo `ADMIN_HIDDEN_KEYS` (`Layout.tsx:227-240` — ẩn Contracts/PE/KeHoachKyKet/HopDongCung/Workflows/PeWorkflows…; **`ApprovalWorkflowsV2` KHÔNG nằm trong tập ẩn**) + prefix `Ct_` (`:248`). Leaf sống PHẢI qua `resolvePath` — trả `null` = drop im lặng (`:251-260`).
2. **Menu-row GĐ3 ĐÃ SEED:** `DbInitializer.cs:2303-2310` sinh 7 leaf key `AwV2_Hd{TP,GK,NCC,DV,MB,NTNCC,NTDV}`, label `"QT Duyệt HĐ — {shortName}"`, parent `ApprovalWorkflowsV2`, Order 11..17 (tiếp ngay sau KHKK 3..10) — key sinh by-construction từ `MenuKeys.ApprovalWorkflowContractLeaf` (`MenuKeys.cs:151`) trên `ContractApprovalWorkflowFamily.Members` (invariant T6 `:2296-2300`: key vào `All` ⇒ 28 policy `AwV2_Hd*.{R,C,U,D}` + admin auto-grant). ⇒ leaf hiện có của mục "Quy trình duyệt (Mới)": `AwV2_DuyetNcc` · `AwV2_DuyetNccPhuongAn` · `AwV2_KhkkN1..8` render được; **7 leaf HĐ tồn tại trong DB nhưng KHÔNG hiện** — khớp ảnh-3.
3. **Lỗ duy nhất — `resolvePath` fe-admin (`Layout.tsx:175-189`)**: block `awV2Match` chỉ có 2 nhánh `DuyetNcc|DuyetNccPhuongAn|Contract` (`:178`) + `/^KhkkN[1-8]$/` (`:188`) — **0 nhánh `Hd*`** (grep `Hd|KhkkN` toàn file: chỉ `Hdc_*` GĐ4 `:49-55` + KhkkN `:188` = control dương cùng block). Trớ trêu: `lib/contractWorkflowFamily.ts:10-11` TỰ KHAI resolvePath là "chỗ đọc thứ 2 … thiếu là leaf DROP IM LẶNG — gotcha #50", lib dựng sẵn cho đúng việc này mà Layout **chưa import** (grep import: duy nhất `ApprovalWorkflowsV2Page.tsx:39`). Đây là ca "vá tệp A quên tệp B" kinh điển (S180): W6 nối trang Designer, quên nối sidebar.
4. **Designer page slice ĐÃ TRỌN (W6 S187, "type-3 family-slice"):** route path-param `/system/approval-workflows-v2/:typeCode` (`App.tsx:80`) → `findContractWfByTypeCode(typeCode)` (`ApprovalWorkflowsV2Page.tsx:314`) → `selectedTypeInt=3` (`:316-320`) → cắt `history.filter(d => d.code === familyCode)`, `active` suy TỪ lát cắt — cố ý KHÔNG đọc `ActiveOthers` (F-3 phương án ii, `:337-370`) → `lockedCode` khoá ô Mã (`:377`) → `panelLabel = ContractTypeLabel[n]` (`:385-389`) + panelDescription tự giải thích vì sao khoá (`:393-396`). Cùng MỘT đường với `KhkkN{n}` (`:298-308`, `:334-336` "Hai HỌ per-Code dùng CHUNG một đường"). 9-checkbox Sửa-tại-chỗ (ảnh-3, gồm cờ Xóa đỏ) round-trip sẵn cho MỌI workflow V2 kể cả `HD-*` (`ApprovalWorkflowV2AdminFeatures.cs:475,883,1288`).
5. **Việc CỤ THỂ ra 7 leaf "Duyệt HĐ — <loại>":** CHỈ sửa fe-admin `components/Layout.tsx` — import `findContractWfByTypeCode` từ `@/lib/contractWorkflowFamily` + thêm sau `:188`: `if (findContractWfByTypeCode(code)) return '/system/approval-workflows-v2/' + code`. **KHÔNG dùng `?ct=`/`?family=`** — khuôn họ này là path-param `:typeCode` (`HdTP``HdNTNCC`) y hệt `KhkkN{n}`. KHÔNG sửa DbInitializer (label "QT Duyệt HĐ — Thầu phụ…" đã khớp khuôn "Duyệt KHKK — Nhóm n"; muốn đổi label thì BẮT BUỘC đường `labelBackfill` `:2301-2302` — không cần). Ghi chú phụ: leaf `AwV2_Contract` cũ ("cả loại Hợp đồng", `:178`) không thấy trong ảnh-3 — chưa đo row DB, không thuộc scope 7-leaf, uncertain vô hại.
## PROPOSE spec 3-mục + wave + checklist
(pending)
### ① Đề-bài (1 đoạn)
Đồng-khuôn GĐ3 "Duyệt Hợp đồng" với KHKK + Duyệt NCC prod (luật owner S176: đã-deploy = CHUẨN): (Đ1) cây toàn trình đứng TRÁI làm trục điều hướng thay danh-sách; (Đ2+Đ3) menu 7 loại HĐ mang danh-tính đúng trục ContractType — hết nhảy lẫn, hết 2-leaf-cùng-sáng, leaf "Luồng duyệt" chỉ hiện đúng quy trình `HD-*` của loại đó (máy per-loại ĐÃ xong @S188 — toàn bộ việc là UI); (Đ4) bộ thao-tác theo hành-vi KHKK (Đang-duyệt ở lại cùng trang, Đã-duyệt danh-tính chuẩn, nút Xóa ở màn duyệt theo cờ per-Cấp); (Đ5) admin Designer hiện 7 leaf per-loại đã seed sẵn. Tổng: **FE-only + 1 delta BE nhỏ (W5, 0-mig)**.
### ② Cách implement per-điều-chỉnh (file:line)
- **Đ1 (cây trái):** `fe-user/pages/contracts/MyContractsPage.tsx:153` grid → `[400px_1fr_360px]`; panel-1 = `PipelineTreePanel currentStage={3} hideHeader hideSearch` + flat-list fallback server-side trong CÙNG panel (khuôn `KhkkListPage.tsx:171` + `KhkkListPanel.tsx:21-24,366-371`); panel-3 = hộp dashed khi chưa chọn / `WorkflowHistoryPanel` khi chọn (QĐ-2 `KhkkListPage.tsx:252-256` — CẤM cây ở panel-3). Gap phải bịt: `usePipelineStages.ts:377` leaf GĐ3 đang `navigate('/contracts/...')` → thêm opt-in `stage3Options.onOpenContract` (analog `onOpenKhkk` `:185,:244`) + wire `PipelineTreePanel.tsx:111` để chọn vào panel-2 qua `?id=`. **Host chỉ fe-user** (glob `fe-admin/src/pages/contracts/`: 4 file, KHÔNG có MyContractsPage); `usePipelineStages` + `PipelineTreePanel` = cặp MIRROR ×2 app → sửa cả 2 bản giữ SHA-identical.
- **Đ2+Đ3 (danh-tính menu + slice):** `fe-user/components/Layout.tsx:151` WfView → `/contracts/workflow-matrix?ct=N` (BỎ `type=3` khỏi URL — trang tự suy applicableType=3 theo path, khuôn alias-path khkk sẵn) · `:152` thêm `&view=approved` (khuôn PE `:180-185`, giữ `phase=9` làm lọc thật) · sửa comment stale `:141-142` ("chung 1 bộ" → 7 bộ per-loại S188) · accordion `:470-485` ưu tiên `ct` khi có + loại trừ pathname `/contracts/workflow-matrix` khỏi nhánh else (phòng-thủ 2 lớp) · `WorkflowMatrixViewPage.tsx` thêm nhánh đọc `ct` → lọc `code === HD-<token>` y khuôn nhánh `group` `:84-98` (map ContractType→mã: const cục-bộ 7-entry khuôn `khkkGroupWorkflowCode` — fe-user KHÔNG mirror lib fe-admin, `contractWorkflowFamily.ts:16-18` tự khai fe-admin-ONLY). fe-admin KHÔNG đụng: `Ct_*` bị ẩn (`Layout.tsx:248`) + cố ý không có WorkflowMatrixViewPage (`:185-187`).
- **Đ4 (thao tác):** Đang-duyệt: `fe-user/Layout.tsx` `Ct_*_Pending``/my-contracts?type=N&pendingMe=1` (khuôn PE `:176`; BE param `pendingMe` sẵn) + `MyContractsPage` đọc `pendingMe` gửi server; GIỮ `/inbox` làm hộp thư tổng. Đã-duyệt/Đã-xóa: đã nằm Đ2+Đ3 (`view=approved`; `deleted=1` giữ nguyên ✅). Nút Xóa màn duyệt → W5. 3 món cố-ý-không-port: GIỮ khai-lệch (ngoài scope, chờ anh).
- **Đ5 (admin 7 leaf):** `fe-admin/components/Layout.tsx` import `findContractWfByTypeCode` từ `@/lib/contractWorkflowFamily` + thêm sau `:188`: `if (findContractWfByTypeCode(code)) return '/system/approval-workflows-v2/' + code`. Hết. (Menu-row/policy/Designer/route đã sẵn — xem mục Thứ-5.)
- **W5 BE nhỏ (nút Xóa, 0-mig):** mirror khuôn PE prod — `PeSoftDeleteFeatures.cs:110-114` (3 điều-kiện: ChoDuyet · ĐÚNG-LƯỢT actor · `matchingLevel.AllowApproverDelete`) + contract-surface `PurchaseEvaluationsController.cs:169`. Việc: nới delete-guard `ContractFeatures.cs:896-897` (đang so SỐ `Phase >= DangInKy` — chặn nhầm V2 ChoDuyet=10/TraLai=98/TuChoi=99, finding S160) thành V2-aware + nhánh xóa-bởi-approver đọc cờ (cột `ApprovalWorkflowLevels.AllowApproverDelete` Mig 68 DÙNG CHUNG V2 — Designer đã round-trip cho `HD-*` sẵn: `ApprovalWorkflowV2AdminFeatures.cs:475,883,1288`) + DTO expose cờ (khuôn `PurchaseEvaluationDtos.cs:138`) + FE `ContractWorkflowPanel.tsx` thêm mục Xóa mirror `KhkkWorkflowPanel.tsx:12-14,118,233` (panel SHA-mirror ×2 app).
### ③ WAVE (5, thứ tự phụ thuộc) + CHECKLIST acceptance đo được
Chỉnh so đề-xuất lead: **gộp slice-WfView vào W1** (route `:151` đổi sang `?ct=` và trang đọc `ct` là 2 nửa của MỘT thay đổi — tách wave thì trạng-thái trung-gian leaf trỏ `ct` mà trang chưa đọc); W3 thu về pendingMe; W4 độc lập chạy song song được bất kỳ lúc.
- **W1 — Danh-tính menu + slice per-loại (fe-user, FE nhỏ — chữa bug anh đau nhất):** Layout `:151/:152/:141-142/:470-485` + WorkflowMatrixViewPage nhánh `ct`.
✅ (i) tái-hiện A: mở nhóm "01. HĐ Thầu phụ" → click "Luồng duyệt" → accordion GIỮ nhóm 01, leaf sáng đúng (trước: đóng 01, mở 03); (ii) tái-hiện B: đứng `?type=1&phase=9&view=approved` → CHỈ leaf "Đã duyệt" sáng (trước: 2 leaf); (iii) WfView `ct=1` chỉ hiện họ `HD-TP` (0 dòng QT-HD-V2-001/6 loại kia); (iv) `grep -rn "workflow-matrix?type=3" fe-user/src` = 0-hit (control dương: grep `workflow-matrix?ct=` ≥1-hit `Layout.tsx`); (v) `npm run build` fe-user PASS.
- **W2 — Cây toàn trình sang trái (FE lớn):** MyContractsPage 3-panel đảo + `stage3Options.onOpenContract` + flatMode.
✅ (i) grep `grid-cols-\[400px` MyContractsPage.tsx ≥1-hit; (ii) hành-vi: click leaf HĐ trên cây → panel-2 mở detail, URL Ở LẠI `/my-contracts` (trước: navigate rời `/contracts/{id}`); (iii) gõ search → flatMode list; (iv) cặp mirror `usePipelineStages.ts`/`PipelineTreePanel.tsx` 2 app: `git diff --stat` chạm cả 2 bản + so SHA256 từng cặp khớp; (v) `npm run build` 2 app PASS.
- **W3 — Đang-duyệt về cùng trang (FE nhỏ, sau W1 — cùng vùng Layout `:148-152` tránh conflict):** `Ct_*_Pending``pendingMe=1`.
✅ (i) grep `pendingMe=1` fe-user Layout.tsx nhánh Ct_ ≥1-hit; (ii) hành-vi: leaf "Đang duyệt" ở lại `/my-contracts`, list = phiếu đến-lượt-mình (đối chiếu inbox cũ cùng user); (iii) build fe-user PASS.
- **W4 — Admin Designer 7 leaf (fe-admin ≈5 dòng, song song được):** resolvePath nhánh `Hd*`.
✅ (i) đăng nhập admin → "Quy trình duyệt (Mới)" hiện đủ 17 leaf (2 NCC + 8 KHKK + 7 "QT Duyệt HĐ — …" Order 11..17); (ii) click "QT Duyệt HĐ — Thầu phụ" → panel title "…: HĐ Thầu phụ", ô Mã khoá `HD-TP`, history CHỈ version HD-TP; (iii) `npm run build` fe-admin PASS.
- **W5 — Nút Xóa màn duyệt V2-aware (BE nhỏ + FE, 0-mig, test-before cho guard):** ContractFeatures guard + delete-by-approver + DTO + panel ×2 app.
✅ (i) `dotnet test SolutionErp.slnx` PASS + test mới: TraLai(98)/TuChoi(99) Drafter xóa được · ChoDuyet(10) CHỈ approver-đúng-lượt-có-cờ xóa được · cờ tắt → 403/409 · V1 `Phase>=DangInKy` VẪN chặn (regression S160); (ii) grep `AllowApproverDelete` trong `ContractFeatures.cs`/Contract-delete-path ≥1-hit (trước: 0-hit toàn Contract-side); (iii) panel 2 app SHA-identical + nút Xóa chỉ render khi DTO cờ bật; (iv) build 2 app PASS.
**Ngoài wave (chờ anh / data-only):** ① gỡ-ghim hoặc thay `QT-HD-V2-001` (bản type-3 đặt tay duy nhất trên Dev) bằng 7 bản `HD-*` nếu anh muốn "tuyệt-đối mỗi loại 1 quy trình" — việc Designer/DATA, 0 code (van xả `ContractWorkflowGuards.cs:141-143` là CỐ Ý); ② 3 món cố-ý-không-port (skipToFinal / duyệt-thay-CEO / banner-kết-thúc-sớm) cần nở `ContractDetail` DTO nếu anh muốn ngang KHKK tuyệt đối.
== HẾT — 3/3 mục B1c chốt ==

View File

@ -0,0 +1,13 @@
# LENS-1 CORRECTNESS — review đối-kháng spec SOLTUON 12-08-2026 (YC-029)
## Verdict
_(đang đo — điền cuối lượt)_
## Findings
_(đánh số F-1.. — append ngay khi chốt)_
## Đề nghị sửa spec
_(điền cuối lượt)_

View File

@ -0,0 +1,15 @@
# sub-review-b2-lens2 — LENS-2 FIDELITY-KHUÔN + SCOPE
> Reviewer lane-2/3 (ensemble /fable-clone). Lăng-kính: độ trung-thành với lời anh (YC-029 + 3 ảnh) + khuôn S176 "đã-deploy = CHUẨN" + scope-risk + mirror 2 app + wave order.
## Verdict
_(đang đo — điền cuối lượt)_
## Findings (F-1..)
_(append từng finding ngay khi đo xong)_
## Đề nghị sửa spec
_(điền cuối lượt)_

View File

@ -0,0 +1,15 @@
# LENS-3 ACCEPTANCE + VẬN HÀNH — review đối-kháng spec SOLTUON 12-08-2026 (YC-029)
Reviewer lane-3/3 (ensemble /fable-clone). Lăng-kính: **acceptance ô-nào-gãy-khi-chạy-thật** + **vận hành W5 delete-path**.
## Verdict
_(điền sau khi đo)_
## Findings (F-1..)
_(append từng finding ngay khi đo xong)_
## Đề nghị sửa spec
_(điền cuối)_