wal: flush 20260731T1552

This commit is contained in:
pqhuy1987
2026-07-31 15:52:08 +07:00
parent 50e6d8c382
commit b33cb687f0
7 changed files with 161 additions and 36 deletions

View File

@ -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

View File

@ -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`#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ở |

View File

@ -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`#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)

View File

@ -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)

View File

@ -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