diff --git a/.claude/WAL.md b/.claude/WAL.md index 102a264..7ea66aa 100644 --- a/.claude/WAL.md +++ b/.claude/WAL.md @@ -13,32 +13,27 @@ chain: BACKLOG 3 mục giai-đoạn-sau (amendment · authz-nhóm · bung-gộp) [x] B3 fable-review chốt: GO-WITH-SPEC-FIXES (GO 9/10 · 8 mâu-thuẫn · 0 vá-đè-vá/43 · build-order K1→K2→K3→K4a→K4b→K5→K4c→K6→K7→K8) → 8 fix lead ÁP XONG → SPEC ĐÓNG BĂNG 4 file -[x] wf: B4 K1-BE p1 wf_2569b79b-12f — chết-giữa ~40% NHƯNG disk-truth: SCHEMA-LAYER XONG ĐÚNG SPEC - (entity 10-cột ✓ unique-filtered ✓ Mig70 3-file ✓ 0-RenameColumn ✓ DbContext ✓ — lead sanity 3/3). - Thiếu: seeder+MenuKeys+CQRS+build. Files uncommitted trên cây (git status 4 mới + 3 sửa) -[x] wf: B4 K1-BE p2 wf_2e08ba77-f53 — K1-BE TRỌN (return rỗng, disk-truth ĐỦ): seeder 86/86 (lead đếm máy) - + MenuKeys/All + CQRS ContractCatalogFeatures + controller (path api/catalogs/contract-catalog, authz - mirror) + build 0W/0E + TEST 590/590 (lead chạy ×2 — lần-1 flaky 2-fail isolation, lần-2 SẠCH; chạy - riêng class 11/11). Lane khai 2 quyết hợp lý (TenVi sạch · controller file-riêng path-spec). UNCOMMITTED -[x] wf: B4 K1-FE ✅ (6 file/3-per-app đối xứng · KIND_CONFIG kind-5 · 4-chỗ-16-bis · SHA-pair d71a9e3b ×2 · - build ×2 PASS — lead RE-BUILD trọng-tài #68 lần-3: diagnostics IDE = stale, build thật XANH; lane khai - 2 khoảng-hở UX [3 filter BE chưa UI · tab hiện cả fe-user] chờ lead/anh, KHÔNG tự quyết — đúng luật). - Bonus finding: `columns` KIND_CONFIG = config CHẾT từ S29, kind-5 là lần đầu được nối render -[x] wf: B4 K1-test ✅ wf_1fb9f309-a27 — 7 test (T1b thêm chống-hồi-sinh #75) · SUITE 597/0 (45D+552I, +7) · - probe VI-PHÂN chứng răng · flaky-DocxOOM tự-khỏi-có-chẩn-đoán · 🔴 lane BẮT tiền-đề lead SAI: p1 ĐÃ - viết file 25KB (mtime>sub-md) — git-status-trước-Write cứu 7 test khỏi bị đè. File Application/ (khai lý). - K1 = BE+FE+TEST TRỌN, UNCOMMITTED. -[ ] NEXT (🔴 MANDATE OWNER @pause: "sang đợt tiếp sau -> chạy 1 mạch ko ngừng luôn nhé"): reviewer-gate K1 - → commit-push K1 → K2 → K3 → K4a→K4b→K5→K4c→K6 → K7 → K8 dry-run — CHẠY LIỀN KHÔNG DỪNG CHỜ. - OG-6 ĐỔI NHÃN: soát-SAU qua Admin CRUD (bảng admin-editable, anh sửa UI được) — HẾT chặn push. - Mỗi wave vẫn: build+test+reviewer-gate rồi lăn tiếp; chỉ DỪNG nếu FAIL cứng không tự xử được. -[x] (dòng cũ K1-FE/test đã xong ở trên; vế "PUSH chờ OG-6" SUPERSEDED bởi MANDATE — OG-6 = soát-SAU) +[x] B4 K1 TRỌN 3 lane (BE p1 wf_2569b79b chết-40%-schema-xong + p2 wf_2e08ba77 seeder86+CQRS+controller · + FE SHA-pair d71a9e3b ×2 · test wf_1fb9f309 7-test; bài giữ: git-status-trước-Write · #68 build-trọng-tài + · TenVi sạch · 2 khoảng-hở UX FE khai [3 filter BE chưa UI · tab cả fe-user]) — chi tiết sub-*-k1.md +[x] gate K1 PASS-WITH-FLAGS 9f (sub-reviewer-gate-k1.md; reviewer #53 ×1 → resume-sạch, artifact cứu B4-d). + Xử @S165: F4 IncludeInactive-mặc-định-loại + T5b · F7 5-kind · F2 STATUS-delta · F9 Mig70-áp-LocalDB. + Khai: F1 OG-6-soát-SAU · F3 authz-mirror-#82 · F5 PermGuard-nợ-khuôn-cũ · F6 IX_-NIT. Suite 598/0 +[x] commit 50e6d8c 20-file PUSH e5123ff..50e6d8c — cicd-monitor verify chạy nền (sub-cicd-verify-k1.md) +[!] wf: K2-BE wf_583be7ad-1a2 — implbe solo (Mig71 AddKhkkApprovalGroupCatalogAndFinalizeRuntime: + +ApprovalGroup-backfill-1 +EndedByLevelFinalize +Line.CatalogEntryId/TenHangMuc + unique-3-cột + + rào-2-site (PeId,ApprovalGroup) + submit/phase-guard + Σ×3 + 18-consumer). Gate 5-anchor 5/5 ✓. + FE/test đợi BE land (khuôn K1) +[ ] MANDATE còn lại (🔴 owner @PAUSE-2 "chạy 1 mạch ko ngừng"): K2(FE+test sau BE)→gate→push → K3 → + K4a→K4b→K5→K4c→K6 → K7 → K8-prep (form 14 người fail-closed = điểm dừng hợp lệ duy nhất). + Mỗi wave: build+test+reviewer-gate rồi lăn; DỪNG chỉ khi FAIL cứng. OG-6 = soát-SAU (HẾT chặn push). [ ] K8 dry-run: gói test SOLUTION-PE-{n} + B0-form 14 người thật (fail-closed) + đo freeze 2-nhánh -[ ] closeout: seed diary vai (C2 A∖B=∅) · tally garble (+sự-cố hôm nay) · nhãn D · STATUS row S163/S164 +[ ] closeout: seed diary vai (C2 A∖B=∅) · tally garble (+reviewer-gate ×1 @S165 resume-sạch, đếm lại cuối + phiên) · nhãn D MIND-3 + nén MIND-1-kèm-nơi-đi-6-ý (trần 84,8%) · STATUS row S163/S164/S165 -next: MANDATE 1-mạch (PAUSE-2): reviewer-gate K1 → commit-push K1 → K2 → K3 → K4a→K4b→K5→K4c→K6 - → K7 → K8 dry-run. Mỗi wave build+test+reviewer-gate rồi lăn tiếp; DỪNG chỉ khi FAIL cứng. - (next: cũ "đợi wf_2569b79b + chờ OG-6 rồi push" STALE @S165 — K1 đã TRỌN disk-truth, OG-6 = soát-SAU.) +next: đợi wf_583be7ad-1a2 (K2-BE) → VERIFY ĐĨA (sub-implbe-k2.md + git status + build/test THẬT) → + phóng K2-FE + K2-test song song → gate K2 → push → lăn K3 theo build-order. + Chết giữa chừng: relaunch-cắt-gọt tiep.md §4 (git status + sub-file cho biết đã tới đâu). verify: ls .claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/ | wc -l # 29 @S165 sau gate-K1 (tăng theo lane — đếm tươi, đừng neo; ctx-verifier K8 vá 28→29) diff --git a/.claude/agent-memory/reviewer/MEMORY.md b/.claude/agent-memory/reviewer/MEMORY.md index 07d82d1..6493b3d 100644 --- a/.claude/agent-memory/reviewer/MEMORY.md +++ b/.claude/agent-memory/reviewer/MEMORY.md @@ -1,5 +1,7 @@ # Reviewer Agent — Persistent Memory +- **S164 (07-31) gate K1 danh mục SP-002 (`ContractCatalogEntries`, Mig 70) — PASS-WITH-FLAGS 9 finding/0 blocker kỹ-thuật:** build 0W/0E + **597 test** (45D+552I) tươi. Lane khai ĐÚNG 100% ở chỗ đo được: seed **86/86** parse-máy khớp transcribe từng dòng (chỉ 8 mismatch = đúng tập lane đã khai), phân bố 5/10/5/23/14/15/13/1, SHA-pair FE `d71a9e3b…` ×2 khớp claim. **Bài thu:** ① 🔴 giá-trị lớn nhất KHÔNG nằm ở mã mà ở **GATE QUY-TRÌNH**: spec đặt PRE-gate owner (`OG-6`) + kế hoạch **tách 2 commit** (code trước, seed sau khi gật) — code đúng hết mà commit gộp là **vượt quyền owner**; seeder per-Code idempotent KHÔNG có nhánh update ⇒ gật muộn = phải UPDATE tay. ② **2 quyết-định hợp-lệ ghép lại đẻ ghost-wire**: lead bắt key vào `MenuKeys.All` (để có menu row) + spec bắt controller mirror khuôn `Roles="Admin"` ⇒ 4 policy `ContractCatalog.*` sinh ra mà **0 endpoint tiêu thụ**; ma trận CRUD chỉ điều-khiển HIỂN-THỊ, tick CanCreate vẫn 403 / bỏ tick CanRead vẫn GET được (lớp #82) — **phải đo `grep policy` chứ đừng suy từ "key đã vào All"**. ③ **default-lấn-ngữ-nghĩa**: spec `IncludeInactive?` (mặc-định-LOẠI) → code `IsActive?` (mặc-định-GỒM) ⇒ cột "ẩn khỏi picker" chỉ ẩn nếu MỌI consumer nhớ truyền tham số. ④ drift DELTA đo được (All=56, policy=224) mà `STATUS.md:6` chưa đổi ⇒ acceptance "khai cùng commit" chưa đóng. Tag `[s164, gate-quy-trinh-owner, ghost-policy-2-quyet-dinh-hop-le, default-lan-ngu-nghia, seed-doc-may]` **Evidence:** `.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-reviewer-gate-k1.md`. ⚠️ Sổ này **18.8KB > 17.1KB** — nợ curate lượt sau. + - **S162 (07-30) cây 4-folder GĐ (FE-only 2 app) — PASS-WITH-FLAGS 8 flag/0 blocker:** 🔴 class **`im-lặng-về-độ-chính-xác-của-con-số`** — 3 ca cùng lớp 1 diff, tsc+build ×2 xanh: hook VỨT `total` khi `pageSize:200` ⇒ không biết mình bị cắt, badge tụt không dấu vết · `pe.ContractId = contracts[0].Id` ⇒ phiếu liên-danh N HĐ chỉ nối 1 mà badge in như thật (**tái phát cardinality S87/S88 — field làm KHOÁ-NỐI phải đi đọc WRITE-SITE của nó**) · fallback `?? '(chưa cấp mã)'` trộn 2 nguyên nhân ⇒ **khẳng định SAI**, không phải degrade. · **#82 ngược chiều:** FE gate OR nhiều menu-key nhưng `MenuPermissionHandler.cs:40` khớp CHÍNH XÁC 1 key ⇒ OR làm gate LỎNG HƠN policy, tự chuốc 403; **gate query = ĐÚNG key policy của endpoint**. 2 endpoint cùng diff kết luận NGƯỢC nhau (`/contracts` `[Authorize]` trần ⇒ KHÔNG 403) ⇒ đo TỪNG cái. · **Nhãn ✓ không có phép đo:** rubric 'responsive ✓' mà harness grep 'grid-cols-[19rem'=0 hit ⇒ 2 trang lưới chưa từng render; **đọc `^import` của harness = cách rẻ nhất biết ảnh CHỨNG được gì**. Tag `[s162, im-lang-do-chinh-xac, total-bi-vut, contracts0-cardinality, or-key-long-hon-policy, nhan-tick-khong-phep-do]` **Evidence:** `.claude/workflows/runs/2026-07-30-S162-cay-4-folder-gd/sub-reviewer-1.md` · lead áp 5/8 flag (F-1/3/4/6/7) + F-8 sửa memory designer TRƯỚC ship `bfc7b79`; F-2 (liên-danh N-HĐ) + F-5 (layout 19rem chưa render thật) = giới-hạn khai thẳng lên HANDOFF slot (56). - **[→ archive/2026-07.md @S162-curate] S161-W2 (07-29) KHKK CRUD — 2 lane viết SONG SONG:** hợp-đồng FE↔BE VỠ 6 điểm mà tsc+build+574-test đều XANH (route lệch tên · field lệch · 2 lỗi CHE NHAU); lời-khai-lane = GIẢ ĐỊNH ⇒ so ROUTE+FIELD trên ĐĨA; list-bịt-detail-mở ⇒ rào chỉ còn UX; INNER-join global-filter = picker vanish. diff --git a/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/run.md b/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/run.md index d6d5a12..205ce3e 100644 --- a/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/run.md +++ b/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/run.md @@ -18,8 +18,9 @@ | 2.c3 | Cụm-3 K7+K8 — `WF invest-fable b2c3 s164` | /fable-real invest | ✅ SẠCH 22.9KB (K7 8v/8test BÁC tiền-đề 3-lỗ-S156-đã-vá · K8 17 bước · HỐ-1-KHKK mới) | | 2.c3-r | Review cụm-3 — `WF reviewer c3-{evi,cov,seq} s164` | /fable-clone (opus ×3) | ✅ 3/3 sạch · L1 7/7-ĐÚNG PWF 4M · L2 FAIL 5H (K8 chưa-chạy-được-như-viết) · L3 FAIL 4H (authz hội-2-khóa · Contracts.Create 0-vai) → **spec-cum3-final 16 vá** | | 3 | Chốt spec cuối — `WF review-fable b3-chot s164` | /fable-real review | ✅ **GO-WITH-SPEC-FIXES**: GO 9/10 (K8 by-design) · 8 mâu-thuẫn (3 nặng A-1/A-4/A-5) · 0 vá-đè-vá/43 · build-order K1→K2→K3→K4a→K4b→K5→K4c→K6→K7→K8 · 8 fix lead ÁP XONG | -| 4 | Fan-out build bám spec+checklist | Opus 5 MAX (hmw) | ☐ | -| 5 | Dry-run E2E toàn trình | theo spec wave cuối | ☐ | +| 4.K1 | Build K1 danh mục HĐ (BE+FE+test) | wf_2569b79b p1 · wf_2e08ba77 p2 · implfe · wf_1fb9f309 | ✅ @S165: 3 lane trọn · gate `sub-reviewer-gate-k1.md` **PASS-WITH-FLAGS 9f** · F4/F7 vá + T5b · Mig 70 áp LocalDB | +| 4.K2-K8 | Build K2..K8 theo build-order B3 | hmw (Opus 5 MAX) | ☐ K2 kế tiếp | +| 5 | Dry-run E2E toàn trình (K8) | theo spec-cum3 | ☐ chặn bởi B0-form 14 người | ## Stages @@ -28,5 +29,5 @@ - [x] B1b — wf_8eb13087-5f9 3/3 lane sạch (l1 FAIL 5H · l2 6/6 chịu-lực ĐÚNG +l2-F1 rào-1-PE · l3 FAIL 6H + bác 6 nghi) → `b1b-review-synthesis.md` 14 vá + OG-6/OG-7 → spec v2 (hội tụ 2-lane: admin-hide tập-key tường minh; ApprovalGroup K2-chủ + backfill=1; 8 designer có phép đo; nguồn K1 = 7 ảnh [file đĩa 6-trang không bảng]) - [x] B2 — 3 cụm TRỌN: c1 (33KB + 12 vá) · c2 (5/5 + 15 vá; l3 chết-ruột phủ-bù) · c3 (22.9KB 7/7-đúng + 16 vá) = 43 vá tổng, 3 spec final - [x] B3 — GO-WITH-SPEC-FIXES (sub-review-fable-b3.md 6 section; 3 anchor re-grep đĩa khớp; 8 fix áp vào 3 spec) — SPEC ĐÓNG BĂNG cho B4 -- [ ] B4 — fan-out build +- [ ] B4 — fan-out build (K1/8 ✅ @S165: gate PASS-WITH-FLAGS 9f, disposition trong sub-reviewer-gate-k1.md; K2..K8 ☐) - [ ] B5 — dry-run E2E diff --git a/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/spec-4gd-khkk-tong-quat-31-07-2026.md b/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/spec-4gd-khkk-tong-quat-31-07-2026.md index 9f1ba28..a5131c2 100644 --- a/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/spec-4gd-khkk-tong-quat-31-07-2026.md +++ b/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/spec-4gd-khkk-tong-quat-31-07-2026.md @@ -44,7 +44,7 @@ Chuỗi 4 GĐ per-gói-thầu theo SOL-CCM-SP-002: GĐ1 Duyệt NCC (PE = CHA, | OG-2 | Roster 8 workflow | ✅ **CHỐT @S164** (*"Đúng đội hình cũ"*) — map đội hình `QT-DN-V2-001`, **verify bản LIVE (không archived) trước seed** | K3 | | OG-7 | 1 PE → N phiếu | ✅ **CHỐT @S164** (*"PE cho nhiều, nhưng… rào hiển thị 1-1 lại — User cần thì bung ra"*) ⇒ **BE mở N-phiếu/PE (đổi rào 2 site :342/:1201 → (PeId, ApprovalGroup)) + UI default hướng-1**: PE có phiếu rồi → hiện phiếu + nút phụ "Tạo thêm cho nhóm khác" (thay 409 chặn hẳn) | K2 | | OG-3 | NĐUQ | ✅ **CHỐT @S164** (*"Đồng ý. Vẫn như trước - các quyền kiểu chuyển CEO các thứ y chang vậy luôn, thì ko cần quan tâm mức duyệt, vì a Chương sẽ quyết định là cái đó cần CEO ko hay là anh ấy đc duyệt."*) ⇒ NĐUQ = ghi chú tham khảo; **cơ chế lên-CEO = TÁI DÙNG level-finalize sẵn có của V2** (người trạm CCM — Phan Văn Chương — tự chọn "kết thúc tại cấp" hay trình tiếp, y PE đang chạy: `EndedByLevelFinalize` runtime + checkbox opt-in S97) — 🔄 **SỬA @B2c1 (mâu thuẫn spec↔code, evidence `fe-admin/src/pages/system/ApprovalWorkflowsV2Page.tsx:151-152` `KHKK_EARLY_FINALIZE_NOTE` "backend chưa xử lý đường duyệt sớm"):** cơ chế = tái dùng KHUÔN level-finalize NHƯNG BE KHKK **CHƯA port nhánh** ⇒ K3 có **3 việc code**: (a) port nhánh finalize vào `ContractSigningPlanWorkflowService` (b) cột runtime `EndedByLevelFinalize` né #78 (forward-provision vào Mig 71 ⇒ K3 0-mig) (c) gỡ khóa 2 ô Designer FE. Seed `AllowApproverFinalize=true` cấp CCM-Chương CHỈ SAU khi (a) land — kẻo config-lie #78 | — | -| OG-6 | Soát bảng transcribe 86 dòng (gõ tay từ ảnh; 4 ô ⚠️ cuối file) HOẶC anh gửi file 15-trang | ⏳ đã giải thích, chờ anh soát | K1 (seed) | +| OG-6 | Soát bảng transcribe 86 dòng (gõ tay từ ảnh; 4 ô ⚠️ cuối file) HOẶC anh gửi file 15-trang | 🔄 **ĐỔI NHÃN soát-SAU @PAUSE-2 (mandate chạy-1-mạch)** — bảng admin-editable qua Admin CRUD; seeder skip-if-exists nên sửa-tay CRUD **sống qua restart** (update-branch sẽ đè mất sửa của anh — cố ý KHÔNG có); 4 ô ⚠️ chờ anh soát SAU trên UI | — (HẾT chặn K1) | | OG-8 | Authz per-nhóm | ✅ **CHỐT @S164** (*"Đúng chính xác. Cho hiển thị, phân quyền tính sau."*) — đợt này 8 nhóm = hiển thị + luồng duyệt; XEM/TẠO chung bộ khóa module; authz-per-nhóm = wave sau khi anh gọi | — | | OG-5 | Extract mơ hồ → hỏi khi gặp | mở | K1 | | OG-9 | Bật lại duyệt-sớm KHKK = ĐẢO phán quyết "PHÁ VỠ" @S155 | ✅ **CHỐT @S164** (*"đúng, chỗ budget chốt chặn là a Chương, anh ấy sẽ quyết cái j cần quyền CEO và cái j anh ấy đc duyệt"*) — K3 việc (a)-(e) HẾT CHẶN: port nhánh finalize + cột runtime + gỡ khóa FE + sweep ép-false ≥4 site + sửa comment/panel/HANDOFF khai supersede-S155 | K3 mở | diff --git a/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-cicd-verify-k1.md b/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-cicd-verify-k1.md new file mode 100644 index 0000000..2e6a4be --- /dev/null +++ b/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-cicd-verify-k1.md @@ -0,0 +1,47 @@ +CICD-K1: IN-PROGRESS — 0/6 mục (đang đo) + +# cicd-monitor — verify deploy K1 SP-002 (`50e6d8c`) + +> Artifact chống #53: ghi TỪNG mục NGAY khi đo xong. Kết bằng dòng `END … VERDICT=`. +> Spawn: 2026-07-31 · commit `50e6d8c38283a9dd45da2001bd2fc6596595558f` + +## Stage 1 — Push + path-filter (PRE-điều-kiện) + +- `git log -1` → `50e6d8c38283a9dd45da2001bd2fc6596595558f|[CLAUDE] Contract: K1 SP-002 danh muc loai HD - ContractCatalogEntries (Mig 70) + seed 86 + CQRS + FE kind-5 x2 app + 8 test|2026-07-31 15:45:50 +0700` +- `git log origin/main..HEAD` → **RỖNG** (đã push hết) +- `git diff --name-only 50e6d8c^ 50e6d8c` → **20 file**; breakdown: + - BE 11 (Api 1 · Application 2 · Domain 2 · Infrastructure 6 gồm 2 file Mig + snapshot) + - FE 6 (fe-admin 3 · fe-user 3) + - Test 1 (`tests/…/Application/ContractCatalogTests.cs`) + - Docs 2 (`docs/HANDOFF.md`, `docs/STATUS.md`) +- vs `paths-ignore` 7-glob: **18/20 file KHÔNG bị ignore** ⇒ CI **PHẢI chạy** (không phải docs-only). ✅ + +## Stage 1b — DỰ ĐOÁN TRƯỚC KHI ĐO (chống xác-nhận-ngược) + +Đọc `DbInitializer.SeedContractCatalogAsync` (dòng 3163-3325) **trước** khi chạm prod, parse mảng `entries`: + +- Lần parse 1 (regex line-anchored) → **78** ⇒ SAI (tuple xuống dòng). KHÔNG dùng số này. +- Lần parse 2 (regex multiline) → **86**; cross-check độc-lập `raw code-literal` = **86**, distinct = **86**, dup = **[]**. +- Dự đoán `GROUP BY ApprovalGroup`: **1:5 · 2:10 · 3:5 · 4:23 · 5:14 · 6:15 · 7:13 · 8:1** (tổng 86) +- Dự đoán `GROUP BY GroupCode`: A1:5 A2:1 A3:9 A4:5 B1:23 B2:14 B3:15 C1:6 C2:3 C3:4 CEO:1 +- Seeder **idempotent** (`existingCodes.Contains(code) continue` + `IgnoreQueryFilters` né #75/#76) ⇒ lần deploy đầu `added=86`, restart sau `added=0`. +- Serilog line dự kiến: `Seeded {Count} contract catalog entries (S164 SP-002)` (chỉ ghi khi `added > 0`). + +⇒ Dự đoán KHỚP kỳ vọng đề bài. Giờ mới đo prod. + +## Bundle PRE-deploy (đo LIVE lúc run đang chạy, 15:47) + +| App | js | css | +|---|---|---| +| admin | `index-B43R6Y17.js` | `index-BZ5F-g1w.css` | +| eoffice | `index-NLI5umBg.js` | `index-yNZDmVNz.css` | + +Khớp nguyên văn hash STATUS/đề-bài (`B43R6Y17` / `NLI5umBg`) ⇒ mốc "TRƯỚC" hợp lệ. + +## Mục 1 — Gitea run + +- Run **id=547 · run_number=#434** · head_sha `50e6d8c3` · name `build-deploy` +- created `2026-07-31T15:46:06+07:00` (push 15:45:50 → runner pick sau 16s) +- (đang poll…) + +(các mục 2-6 đổ dần bên dưới) diff --git a/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-implbe-k2.md b/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-implbe-k2.md new file mode 100644 index 0000000..94734ec --- /dev/null +++ b/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-implbe-k2.md @@ -0,0 +1,14 @@ +K2-BE: IN-PROGRESS — build 0W/0E? chưa đo · test chưa chạy + +# sub-implbe-k2 — K2 BACKEND (S164, wave 2/8, SP-002 phiếu mang hạng-mục + nhóm) + +> Luật đan-xen (bài K1): làm mục nào GHI NGAY mục đó. Dòng-1 file = verdict, cập nhật cuối. +> Nguồn: spec-cum1 (12 vá) + spec-tổng-quát (QĐ-1/2/9 + OG-1/7) + nền `sub-invest-fable-b2-cum1.md` §K2. + +## §0 Tiền đề — git status TRƯỚC mọi Write (bài K1 sự-cố suýt-đè) + +- `git status --porcelain` @start: `sub-implbe-k2.md` **KHÔNG tồn tại** (run-folder có k1/implfe/test/reviewer/cicd, không có k2) ⇒ Write mới AN TOÀN. +- Baseline nhận từ lead: Mig 70 land (`50e6d8c`), suite 598/0 (45D+553I), MenuKeys.All=56, policies=224. +- Nhánh: `main`. + +(các mục a-h ghi tiếp bên dưới khi xong từng mục) diff --git a/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-reviewer-gate-k1.md b/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-reviewer-gate-k1.md index d1f20f6..214d46a 100644 --- a/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-reviewer-gate-k1.md +++ b/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-reviewer-gate-k1.md @@ -1,4 +1,4 @@ -REVIEWER-GATE-K1: IN-PROGRESS — 0 finding (đang chạy) +REVIEWER-GATE-K1: PASS-WITH-FLAGS — 9 finding # Reviewer gate K1 — ContractCatalogEntries (SP-002 danh mục loại HĐ) @@ -9,9 +9,9 @@ Artifact ghi LIÊN TỤC chống #53 — mỗi finding đổ ngay khi có. - [x] B1 Đọc spec cụm-1 + transcribe + 3 lane return - [x] B2 dotnet build tươi — **Build succeeded · 0 Warning(s) · 0 Error(s) · Time Elapsed 00:00:44.23** (6 project) - [x] B3 dotnet test — **45 Domain + 552 Infra = 597 PASS / 0 FAIL / 0 SKIP** (đúng kỳ vọng, 0 flaky, không phải chạy isolation) -- [ ] B4 Đối chiếu code-vs-spec -- [ ] B5 Ranh CẤM -- [ ] B6 Xác nhận 2 khoảng-hở UX +- [x] B4 Đối chiếu code-vs-spec (a→h) +- [x] B5 Ranh CẤM +- [x] B6 Xác nhận 2 khoảng-hở UX ## Log @@ -28,10 +28,9 @@ Time Elapsed 00:00:44.23 `ContractCatalogEntry.cs:15-24` = Code · TenVi · TenEn? · GroupCode · ApprovalGroup · SignerRole · InitialsChain? · NduqNote? · IsActive · SortOrder = **10 cột** + 7 audit từ `AuditableEntity`. Khớp bảng spec `sub-invest-fable-b2-cum1.md:13-26`. ### B4-b Config + Mig — ĐẠT (1 nit tên index) -`ContractCatalogEntryConfiguration.cs:27` unique filtered `[IsDeleted] = 0` ✔ (#57) · `:28` index ApprovalGroup ✔ · `:29` HasQueryFilter ✔. -Mig `20260731072441_AddContractCatalogEntries.cs`: 1 CreateTable + 2 CreateIndex, Down = DropTable. **0 RenameColumn** ✔. -NIT: spec `:28` đặt tên `UX_ContractCatalogEntries_Code`, code để EF-default `IX_ContractCatalogEntries_Code` (`Mig:48`). Không ảnh hưởng chức năng; khuôn repo (Mig 47) cần đối chiếu. - +`ContractCatalogEntryConfiguration.cs:27` unique filtered `[IsDeleted] = 0` (#57) · `:28` index ApprovalGroup · `:29` HasQueryFilter. +Mig `20260731072441_AddContractCatalogEntries.cs`: 1 CreateTable + 2 CreateIndex, Down = DropTable. **0 RenameColumn**. +NIT: spec `:28` đặt tên `UX_ContractCatalogEntries_Code`, code để EF-default `IX_ContractCatalogEntries_Code` (`Mig:48`). Không ảnh hưởng chức năng. ### B3 test (nguyên văn) ``` @@ -56,3 +55,70 @@ Script parse trực tiếp `DbInitializer.cs` vùng `SeedContractCatalogAsync` ( ### B4-d Gate seeder — ĐẠT `DbInitializer.cs:129-131` call-site NẰM NGOÀI mọi `if (!demoSeedDisabled)` ⇒ UNGATED (đúng spec K1.b, gotcha #51). `IgnoreQueryFilters()` có mặt khi dò existing (né #75/#76 resurrect). + +### B4-e MenuKeys + drift DELTA — ĐẠT (1 finding kéo theo ở B4-f) +Đo máy trên `MenuKeys.cs` (parse vùng mảng `All`, strip comment, tách dấu phẩy): +- `All count = 56` · `has ContractCatalog = True` · `Actions = [Read, Create, Update, Delete]` = 4 → **policies derived = 224**. +- Diff `MenuKeys.cs:28-31` const + `:173` VÀO `All` ⇒ đúng vá-1 spec-cum1 `:8` (ngoài All = 0 permission row = sidebar chết). +- Grant Admin: loop `SeedAdminPermissionsAsync` `DbInitializer.cs:2064-2066` nguồn = `MenuKeys.All` ⇒ tự động, 0 code thêm. Menu row `DbInitializer.cs:1773-1776` leaf order 5 dưới `Catalogs`, đi nhánh INSERT (key mới) nên không dính bẫy gotcha #11. +- 🔴 **Drift DELTA CHƯA vào doc:** `docs/STATUS.md:6` hiện vẫn `Mig 69 · 96 bảng · 590 test PASS · Policies 220 · Menu keys 55`. Acceptance spec-cum1 ③ đòi "drift DELTA khai cùng commit" ⇒ **F-2**. + +### B4-f CQRS + Controller authz — ĐẠT-theo-khuôn, nhưng khoảng-hở phải khai +Soi TỪNG action (không nhận `[Authorize]` trần làm bằng chứng): + +| Action | Attribute thật | File:line | +|---|---|---| +| class | `[Authorize]` trần | `ContractCatalogController.cs:22` | +| GET list | (thừa hưởng class) | `:25` | +| GET id | (thừa hưởng class) | `:34` | +| POST | `[Authorize(Roles = "Admin")]` | `:39` | +| PUT | `[Authorize(Roles = "Admin")]` | `:47` | +| DELETE | `[Authorize(Roles = "Admin")]` | `:56` | + +Khuôn đối chứng `CatalogsController.cs`: `:13` route · `:14` `[Authorize]` trần · 12 action ghi đều `[Authorize(Roles="Admin")]` (`:23,31,40,53,61,70,83,91,100,113,121,130`) ⇒ **mirror khuôn CHÍNH XÁC**, đúng QUYẾT spec K1.c `:47`. **KHÔNG có OR key con** (gotcha #85 N-A: controller này không dùng policy nào). +🔴 Đo tiếp: `grep -rn "ContractCatalog\." src/Backend --include=*.cs | grep -i policy` → **0 endpoint** dùng 4 policy vừa sinh. Hệ quả đo được (lớp gotcha #82): ô CRUD `ContractCatalog` trên ma trận phân quyền chỉ điều khiển **hiển thị menu**, KHÔNG điều khiển API — tick `CanCreate` cho CatalogManager vẫn 403 (`Roles="Admin"`), bỏ tick `CanRead` vẫn GET được (class `[Authorize]` trần). ⇒ **F-3**. + +Validator (`ContractCatalogFeatures.cs:110-124` / `:168-183`): MaxLength lấy ĐÚNG từ EF config (20/500/500/10/100/300/500) — đối chiếu `ContractCatalogEntryConfiguration.cs:18-24` khớp 7/7. `ApprovalGroup` `InclusiveBetween(1,8)` cả Create lẫn Update. Dup-Code chạy trên active-set (`:131`, `:193`) ⇒ khớp index filtered. +🔸 `ListContractCatalogQuery` (`:42-46`) mặc định **KHÔNG lọc IsActive** ⇒ trả CẢ dòng đã tắt; spec K1.c `:50` ghi tham số `IncludeInactive?` (ngữ nghĩa mặc-định-loại). ⇒ **F-4**. + +### B4-g FE 2 app — ĐẠT (đối xứng đo bằng hash) +- `CatalogsPage.tsx` SHA-256 **d71a9e3bb17b835b85b4f3467905af2ab414f52e3ec0c58e79d55ef57673985a** ×2 app IDENTICAL → **khớp claim lane FE `sub-implfe-k1.md:49`**. +- `menuKeys.ts` + `Layout.tsx` DIVERGE cả-file (đúng như lane khai) ⇒ đo hash **phần THÊM VÀO**: `menuKeys.ts` admin=`ab97ed56b743f262…` user=`ab97ed56b743f262…` **MATCH** · `Layout.tsx` admin=`4ed4ea6b6f059c0e…` user=`4ed4ea6b6f059c0e…` **MATCH**. +- Pattern 16-bis 4 chỗ: (1) page OK · (2) route `/master/catalogs/:kind` CÓ SẴN `fe-user/src/App.tsx:63` + `fe-admin/src/App.tsx:69` ⇒ đúng "không cần route mới" · (3) `menuKeys.ts` ×2 · (4) `Layout.tsx` staticMap ×2 (thiếu chỗ 4 = sidebar drop im lặng, gotcha #50). +- KIND_CONFIG kind-5: 10 field khớp ĐÚNG 10 field `CreateContractCatalogEntryCommand` · PUT gửi `{ id: form.id, ...body }` (`CatalogsPage.tsx:164`) ⇒ qua được chốt `id != body.Id → 400` (`Controller:50`) · `buildBody` ép `numeric` → 0 (BE `int` non-nullable) · `openCreate` mồi select N1 (né `InclusiveBetween`). +- 🔸 Page **0 hit** `PermissionGuard`/`usePermission` ⇒ nút Thêm/Sửa/Xóa hiện cho mọi user vào được trang (khuôn 4 kind cũ y vậy) ⇒ **F-5**. +- 🔸 `CatalogsPage.tsx:218` ×2 app: comment `{/* Sub-tabs cho 4 kind */}` còn "4" trong khi `KINDS` đã 5 ⇒ **F-7** (nit). + +### B4-h Test — ĐẠT (7 [Fact], có T1b thật) +Đếm máy `[Fact]` = **7**: T1 `Seed_RunTwice_Keeps86Rows_NoDuplicateCode` · **T1b `Seed_AfterSoftDeleteOfOneCode_DoesNotResurrectIt`** · T2 phân-bố 8 nhóm + section + denorm-đồng-nhất · T3 unique filtered 2 CHIỀU · T4 validator biên 0/9/-1/99 ⟂ 1/4/8 · T5 vòng đời handler thật (soft-delete + tái dùng mã) · T6 menu-key ∈ All + MenuItem row + grant Admin. +T1b có RĂNG thật (không phải nhãn): fixture RIÊNG wire `AuditingInterceptor` (`:83`) ⇒ `Remove()` là xoá MỀM thật; có sanity chống-vacuous `raw.IsDeleted.Should().BeTrue()` (`:179`); assert 85 active **VÀ** 86 tổng `IgnoreQueryFilters` (`:185-190`) ⇒ gỡ `IgnoreQueryFilters()` khỏi seeder thì tổng thành 87 và test ĐỎ. Đúng phép đo cho #75/#76. +T6 còn cài invariant TOÀN BỘ `All`: `MenuKeys.All.Except(menuKeysInDb).Should().BeEmpty()` (`:414`) — bắt lỗi key ∈ All mà thiếu MenuItem row (FK nổ lúc boot). + +### B5 Ranh CẤM — SẠCH +- `git status --porcelain | grep -cE "ContractSigningPlan"` → **0**. +- `git diff --stat` cho `Infrastructure/Services/ContractSigningPlanWorkflowService.cs` + `Application/Contracts/ContractSigningPlanFeatures.cs` → **rỗng** (0 thay đổi) ⇒ `WorkflowService.cs:342` KHÔNG bị đụng · vùng `Features.cs:342-347` (việc của K2+) KHÔNG bị đụng — đúng vá-2 spec-cum1 `:9`. +- Scope toàn diff: **17 file** (11 M + 6 untracked) = 6 FE + 10 BE + 1 test. 0 file ngoài phạm vi K1. +- 3-file rule Mig (gotcha #17): `20260731072441_AddContractCatalogEntries.cs` + `.Designer.cs` (cả 2 untracked, có trên đĩa) + `ApplicationDbContextModelSnapshot.cs` (modified, +83 dòng, có `ContractCatalogEntry`) = **đủ 3, cùng commit-set**; `grep -c RenameColumn` = **0**. + +### B6 Hai khoảng-hở UX lane FE khai — XÁC NHẬN ĐÚNG THỰC TRẠNG (không quyết thay lead/owner) +1. **3 filter BE chưa có UI** — ĐÚNG. BE nhận 4 tham số (`groupCode`/`approvalGroup`/`isActive`/`q` — `Controller:27-30`), FE chỉ gửi `q` (`CatalogsPage.tsx:158`) ⇒ 3/4 filter chưa có đường bấm từ UI. +2. **Tab hiện cả fe-user** — ĐÚNG. `KINDS` render vô điều kiện 5 sub-tab, route `:kind` mở ở CẢ 2 app (`fe-user/src/App.tsx:63`), GET là `[Authorize]` trần ⇒ user thường vào `/master/catalogs/units` sẽ thấy tab "Danh mục Hợp đồng" và đọc được 86 dòng, dù leaf menu chỉ Admin được grant. Cùng lớp với F-3. + +## FINDING (9 — 0 blocker kỹ-thuật; F-1 chờ lead/owner disposition trước khi commit seed) +1. **[HIGH · gate quy-trình]** `DbInitializer.cs:3147-3324` — 86 dòng seed đang nằm CÙNG change-set với code, trong khi spec đặt PRE-gate `[OG-6]` (owner soát transcribe) **TRƯỚC khi land seed** và kế hoạch tách 2 commit (`sub-invest-fable-b2-cum1.md:7` + `:240-241`: "Commit K1-seed (SAU [OG-6] anh gật)"). Nặng thêm vì seeder per-Code idempotent **không có nhánh update**: owner sửa ô sau khi seed đã chạy thì row cũ KHÔNG tự cập nhật (lane tự khai `sub-implbe-k1.md:51`). Tiêu chí đóng: hoặc owner gật OG-6 trước commit, hoặc tách seed sang commit riêng giữ chờ gật. +2. **[MEDIUM]** `docs/STATUS.md:6` — drift DELTA chưa vào doc: Mig 69→**70** · 96→**97** bảng · 590→**597** test · Policies 220→**224** · Menu keys 55→**56**. Acceptance spec-cum1 ③ đòi khai **cùng commit**. +3. **[MEDIUM · lớp #82]** `ContractCatalogController.cs:22,39,47,56` — 4 policy `ContractCatalog.{Read,Create,Update,Delete}` sinh ra do key vào `All` nhưng **0 endpoint tiêu thụ**; ô CRUD trên ma trận phân quyền chỉ điều khiển hiển-thị-menu, không điều khiển API (tick CanCreate vẫn 403 · bỏ tick CanRead vẫn GET được). KHÔNG phải lệch spec (K1.c QUYẾT mirror khuôn `CatalogsController.cs:14`) nhưng phải khai thành giới-hạn, đừng để lượt sau đọc ma trận thành "đã khoá API". +4. **[LOW]** `ContractCatalogFeatures.cs:42-46,59-60` — List mặc định trả CẢ dòng `IsActive=false`; entity khai mục đích cột này là "ẩn khỏi picker" (`ContractCatalogEntry.cs:23`) và spec K1.c `:50` đặt tham số `IncludeInactive?` (mặc-định-loại). Ngữ nghĩa hiện tại ngược ⇒ consumer K2 (picker) phải nhớ truyền `isActive=true`, quên là dòng đã tắt hiện lại. +5. **[LOW]** `fe-{admin,user}/src/pages/master/CatalogsPage.tsx` — 0 hit `PermissionGuard`/`usePermission`: nút Thêm/Sửa/Xóa render cho mọi user vào được trang, bấm mới nhận 403. Pre-existing của khuôn 4 kind cũ, không phải hồi quy do K1. +6. **[NIT]** `20260731072441_AddContractCatalogEntries.cs:48` — tên index `IX_ContractCatalogEntries_Code` (EF default) trong khi spec `sub-invest-fable-b2-cum1.md:28` đặt `UX_ContractCatalogEntries_Code`. Chức năng không đổi (unique + filter đúng). +7. **[NIT]** `fe-{admin,user}/src/pages/master/CatalogsPage.tsx:218` — comment `{/* Sub-tabs cho 4 kind */}` còn "4" trong khi `KINDS` đã 5 kind. +8. **[INFO · lệch đã khai]** `DbInitializer.cs` B2-07..B2-14 — 8 dòng bỏ hậu tố `(ảnh: …)` so với transcribe; đo máy xác nhận **đây là 8 mismatch DUY NHẤT** trên 86 dòng, đúng tập lane đã khai (`sub-implbe-k1.md:16`). Bản chất là ghi-chú người chép, nhưng vẫn thuộc phạm vi OG-6 owner soát. +9. **[LOW]** Acceptance K1.f `:70-71` (runtime) chưa đo: Mig 70 chưa áp Dev-DB (lane khai `sub-implbe-k1.md:48-49`), chưa có log "Seeded 86…" + `sqlcmd COUNT(*)=86` + `GROUP BY ApprovalGroup`. T1/T1b thay được phần logic-idempotent trên SQLite nhưng KHÔNG thay được "SQL Server nhận đúng filtered index + boot thật" ⇒ đừng tick 2 ô đó cho tới khi chạy API. + +## Bằng chứng số (nguyên văn) +- build: `Build succeeded. · 0 Warning(s) · 0 Error(s) · Time Elapsed 00:00:44.23` +- test: `Failed: 0, Passed: 45` (Domain) + `Failed: 0, Passed: 552` (Infrastructure) = **597 PASS / 0 FAIL / 0 SKIP** +- seed: 86 row · 86 mã duy nhất · 5/10/5/23/14/15/13/1 · 0 mismatch ngoài 8 dòng đã khai +- MenuKeys.All = 56 · policies derived = 224 · [Fact] = 7 · RenameColumn = 0 · file KHKK bị chạm = 0 + +END sub-reviewer-gate-k1 — 9 finding · build+test XANH tươi · ranh CẤM sạch