diff --git a/.claude/skills/permission-matrix/SKILL.md b/.claude/skills/permission-matrix/SKILL.md index 29e743e..308e130 100644 --- a/.claude/skills/permission-matrix/SKILL.md +++ b/.claude/skills/permission-matrix/SKILL.md @@ -1,6 +1,6 @@ --- name: permission-matrix -description: Hệ thống phân quyền Role × MenuKey × CRUD. ~60 menu key (12 root + Ct_*/Wf_*/Pe_*/PeWf_*/Bg_*/Catalogs). FE PermissionGuard + usePermission. BE AuthorizationHandler + ~240 policy. Dùng khi debug access denied, gán role, menu không hiện, inheritance không work. +description: Hệ thống phân quyền Role × MenuKey × CRUD (12 root + Ct_*/Wf_*/Pe_*/PeWf_*/Catalogs — số menu-key · policy canonical → docs/STATUS.md, KHÔNG chép số ở đây). FE PermissionGuard + usePermission. BE AuthorizationHandler + policy `{menu}.{action}`. Dùng khi debug access denied, gán role, menu không hiện, inheritance không work. when-to-use: - "permission denied" - "access denied" @@ -13,14 +13,19 @@ when-to-use: # Permission Matrix Skill -> **Status (post Session 6 — 2026-04-30):** Phase 1 đợt 2 base + extended qua mọi phase. ~60 menu key total: -> - Core: Dashboard / Master+3 leaves / Forms / Reports / System+Users/Roles/Permissions (12 base) -> - Contracts root + 28 Ct_* (7 type × {Group/List/Create/Pending}) + Workflows root + 7 Wf_* -> - PurchaseEvaluations root + 6 Pe_* (2 type × 3 action) + PeWorkflows root + 2 PeWf_* -> - Budgets root + 3 Bg_* (List/Create/Pending) -> - Catalogs group + 4 leaves (Units/Materials/Services/WorkItems) +> **Status (cập-nhật 2026-07-15 — S122 W4):** base Phase 1 đợt 2 + extended qua mọi phase. +> 🔴 **Số menu-key · số policy = canonical ở [`docs/STATUS.md`](../../../docs/STATUS.md)** (row `Menu keys` / `Policies`) — **KHÔNG chép số vào file này** (B1). Policy = **DERIVED**: `|MenuKeys.All| × |Actions|` (`Api/Program.cs`) ⇒ đổi menu **BUỘC** đổi cả 2 row cùng lúc. +> 🔍 **Đếm THẬT = đọc mã, đừng tin doc:** `MenuKeys.All` ở `src/Backend/SolutionErp.Domain/Identity/MenuKeys.cs:147`. > -> **Inheritance roots (4 group, gotcha #35):** `Contracts` → Ct_*, `Workflows` → Wf_*, `PurchaseEvaluations` → Pe_*, `PeWorkflows` → PeWf_*. Khi thêm root mới có children → PHẢI extend 3 chỗ trong `GetMyMenuTreeQuery` (xem gotcha #35). Budgets KHÔNG inherit (Bg_* phải grant tay). +> **Nhóm menu (hình-dạng, KHÔNG phải con-số):** +> - Core: Dashboard / Master+3 leaves / Forms / Reports / System+Users/Roles/Permissions +> - Contracts root + `Ct_*` (7 type × {Group/List/Create/Pending}) + Workflows root + `Wf_*` +> - PurchaseEvaluations root + `Pe_*` (2 type × 3 action) + PeWorkflows root + `PeWf_*` +> - Catalogs group + 4 leaves (Units/Materials/Services/WorkItems) +> - Office/HRM/… — xem `MenuKeys.cs` (mã là nguồn) +> - 🧊 ~~Budgets root + `Bg_*`~~ — **XOÁ S61 (Mig 50)**, module Budget cũ thay bằng `PeWorkItemBudgets` (ngân-sách per-gói-thầu). Bia-mộ: `MenuKeys.cs:70` + `fe-{admin,user}/src/lib/menuKeys.ts:29`. *(Skill này liệt `Bg_*` như menu ĐANG SỐNG suốt từ S61 → S122 — stale **nặng hơn** lệch con-số vì nó mô-tả thứ **không tồn tại**; vá @S122 W4.)* +> +> **Inheritance roots (4 group, gotcha #35):** `Contracts` → `Ct_*`, `Workflows` → `Wf_*`, `PurchaseEvaluations` → `Pe_*`, `PeWorkflows` → `PeWf_*`. Thêm root mới có children → **PHẢI extend 3 chỗ** trong `GetMyMenuTreeQuery` (gotcha #35). *(🧊 câu cũ "Budgets KHÔNG inherit (Bg_* phải grant tay)" — gỡ theo module.)* ## Model @@ -78,7 +83,7 @@ Tree hierarchy qua `ParentKey` field. Seed trong `DbInitializer.SeedMenuTreeAsyn - `Application/Permissions/Queries/GetMyMenuTree/GetMyMenuTreeQuery.cs` — resolve per-user, union OR, filter tree - `Application/Permissions/PermissionFeatures.cs` — list/upsert - `Api/Authorization/MenuPermissionRequirement.cs` + `MenuPermissionHandler.cs` — policy check -- `Api/Program.cs` — register 48 policy `{menu}.{action}` trong AddAuthorization +- `Api/Program.cs` — register policy `{menu}.{action}` trong AddAuthorization (**số = `|MenuKeys.All| × |Actions|` DERIVED**; canonical → `docs/STATUS.md` row `Policies` — KHÔNG hardcode ở đây) - `Infrastructure/Persistence/DbInitializer.cs` — `SeedMenuTreeAsync` + `SeedAdminPermissionsAsync` - `Api/Controllers/MenusController.cs`, `RolesController.cs`, `PermissionsController.cs` diff --git a/.claude/workflows/runs/2026-07-15-S119-adap-6-broadcast/W4-detector-fire-15-07-2026.md b/.claude/workflows/runs/2026-07-15-S119-adap-6-broadcast/W4-detector-fire-15-07-2026.md new file mode 100644 index 0000000..172e8a6 --- /dev/null +++ b/.claude/workflows/runs/2026-07-15-S119-adap-6-broadcast/W4-detector-fire-15-07-2026.md @@ -0,0 +1,96 @@ +# W4 — detector-fire evidence (positive-control) · 2026-07-15 S122 + +> 🔴 **CAPTURE TRƯỚC KHI VÁ.** Đây là bằng-chứng **time-anchored** thay cho *"reviewer chứng-kiến"* làm neo duy-nhất (spec v3 **fix #9(c)**). +> **Luật W4 (v2 §3.3 + §2.4(4)):** `fire → chứng-kiến → RỒI MỚI vá → hết fire`. 🔴 **CẤM vá `permission-matrix` TRƯỚC** — vá trước thì detector chạy xong xanh lè và ta **mất chính bằng-chứng** rằng nó có răng. +> **Lệnh:** `powershell.exe -ExecutionPolicy Bypass -File scripts/governance-detectors.ps1` · **exit = 0** (DETECT-only, advisory, không fail build). + +--- + +## 1. 🔴 POSITIVE-CONTROL FIRE — `permission-matrix/SKILL.md` (chạy TRƯỚC mọi sửa đổi) + +``` +[DETECTOR] MED | .claude/skills/permission-matrix/SKILL.md:3 | derived-stale: writes 60 menu but canonical=54 | resolve: update to 54 OR replace with pointer '-> docs/STATUS.md' +[DETECTOR] MED | .claude/skills/permission-matrix/SKILL.md:3 | derived-stale: writes 240 policy but canonical=216 | resolve: update to 216 OR replace with pointer '-> docs/STATUS.md' +[DETECTOR] MED | .claude/skills/permission-matrix/SKILL.md:16 | derived-stale: writes 60 menu but canonical=54 | resolve: update to 54 OR replace with pointer '-> docs/STATUS.md' +[DETECTOR] LOW | .claude/skills/permission-matrix/SKILL.md:81 | derived-stale: writes 48 policy but canonical=216 | resolve: update to 216 OR replace with pointer '-> docs/STATUS.md' +[DETECTOR] LOW | .claude/skills/permission-matrix/SKILL.md:16 | title-stale: anchor says 2026-04-30 but newest governance milestone is 2026-07-15 (76d behind) +``` + +**= 5 FLAG** (2 MED + 3 LOW). +🔸 **Đính-chính con-số của LEAD:** WAL ghi *"permission-matrix **6 dòng**"* — đếm thật = **5 flag**. Dòng thứ 6 (`:70` `anchor 2026-04-30 … SKILL.md:16`) là **dòng context trong khối tóm-tắt anchor**, KHÔNG phải flag. *(Lỗi đếm-của-lead, bắt được TRƯỚC khi thành claim gửi hub — nhưng vẫn là lỗi, → adap-report.)* + +## 2. ✅ TOTAL = 50 — khớp CHÍNH XÁC dự-báo W2 + +``` +TOTAL FLAGS: 50 +NOTE: DETECT-only lowering net. Exit 0 always (never fails build). FLAGs are advisory. +``` +WAL:17 (W2) dự-báo: *"TOTAL 49→**50** (−1 cadence, +2 policy)"*. **Đo ra đúng 50.** ⇒ hợp-đồng chéo W1↔W2 khép **cả hai chiều**, đo bằng **chính script W1**. + +## 3. ✅ Chuỗi nhân-quả W1↔W2 — 3 detector đổi trạng-thái ĐÚNG như thiết-kế + +| Detector | TRƯỚC W2 | SAU W2 | Ý-nghĩa | +|---|---|---|---| +| `h24_cadence` | `missing — W2 chưa land ⇒ measuring NOTHING` | `M = light_every = 6 (read from config)` | fail-loud **tắt đúng lúc** key land — **không** giả-định default | +| `spawn-model-audit` | `expected constant missing` | `[OK] claude-opus-4-8 = owner-ratified` | PA-2a hằng-số nhận diện được | +| GAP-3 policy | `policy=MISSING` | `216` **+ BẮT NGAY** `permission-matrix:3 writes 240 but canonical=216` | 🔴 **row STATUS `Policies` land ⇒ detector LẬP TỨC có canonical để so ⇒ lộ stale ẩn 76 ngày** | + +🔴 **Dòng cuối là điểm mấu-chốt:** trước W2, nửa `policy` của GAP-3 **KHÔNG THỂ fire** vì `docs/STATUS.md` **không có row `Policies`** ⇒ detector không có gì để so ⇒ **im lặng**, và sự im-lặng đó trông y hệt "sạch". Thêm canonical ⇒ 2 flag mới hiện ra **ngay**. Bài học: **detector không có nguồn chuẩn = detector đo NOTHING, và nó không tự nói cho biết.** + +## 4. 🔴 PHÁT-HIỆN NGOÀI SCOPE W4 — vai H24 bị chính detector bắt + +``` +[DETECTOR] LOW | .claude/agents/lead-view-auditor.md:45 | title-stale: anchor says 2026-04-30 but newest governance milestone is 2026-07-15 (76d behind) +``` + +**Đây là DƯƠNG-GIẢ.** `lead-view-auditor.md:45` chứa `**Status (post Session 6 — 2026-04-30):**` làm **VÍ-DỤ minh-hoạ** cho class `view-stale-header` trong bảng flag-class của chính nó. Detector đọc ngày trong **ví-dụ** và tưởng là **anchor thật của file**. + +🔴 **Cùng CƠ-CHẾ với lỗi đã làm acceptance FIX#6 bất-khả-thi** (`lead-view-auditor.md:47` chứa *"agents/README ghi 12 sub khi roster = 14"* làm ví-dụ ⇒ sweep-pattern bắt). **Hai detector ĐỘC-LẬP, cùng một file, cùng một lý-do:** +> **Ví-dụ về stale trông y hệt stale.** File **định-nghĩa** anti-pattern tất-yếu **chứa mẫu** anti-pattern ⇒ mọi detector khớp-mẫu sẽ bắt nó. Đây **KHÔNG** phải lỗi của vai, cũng **KHÔNG** phải lỗi của detector — là **hệ-quả cấu-trúc** của việc dò bằng khớp-mẫu. + +**Vì sao KHÔNG "sửa" bằng cách đổi ví-dụ:** ví-dụ mất tính cụ-thể thì bảng flag-class mất giá-trị dạy-nghề (vai đọc "con-số lệch thực-tế" mà không có mẫu thật thì tự chế class — đúng thứ enum-ĐÓNG sinh ra để chặn). +**Xử @S122:** giữ ví-dụ, **khai vào đây + adap-report**. Đề-xuất hướng (KHÔNG tự làm — đổi hành-vi detector = quyền anh / W5): detector cần **cơ-chế miễn-trừ tường-minh** cho khối ví-dụ (vd fenced-block hoặc allowlist path), thay vì bắt lead nhớ né. + +🔸 **2 flag title-stale KHÁC là THẬT, ngoài scope W4** (không đụng): `form-engine/SKILL.md:15` (anchor 2026-04-30) · `docs/rag-setup-plan.md:4` (anchor 2026-05-12). +🔸 **`ef-core-migration/SKILL.md:167/:184` `writes 1 migration but canonical=66` = DƯƠNG-GIẢ** (cụm "1 migration" trong câu văn) — chính script tự khai: *"count-token grep is a soft net — module-local phrases … can false-positive; H18-A ratio-band demotes far-from-canonical counts to LOW = review-not-fail"*. LOW = đọc-bằng-phán-đoán, không auto-fix. + +## 5. Nấc trung-thực của chính W4 + +- **Detector = DÒ + NÊU CỜ, KHÔNG tự sửa** (tầng D6). `exit 0` **luôn luôn** — advisory, không chặn build. +- **Fire ≠ chứng minh detector tốt** — chỉ chứng nó **đọc được** và **có canonical để so**. Chất-lượng thật đo ở tỉ-lệ dương-giả (mục 4 = 1 dương-giả cấu-trúc đã lộ). +- **Bước kế:** vá `permission-matrix` → chạy lại → **5 flag đó PHẢI biến mất** (và TOTAL tụt tương-ứng). Nếu vá xong mà **vẫn fire** ⇒ vá sai chỗ. Nếu TOTAL **không tụt đúng số** ⇒ có gì đó khác đã đổi ⇒ điều-tra, **đừng làm tròn**. + +--- + +## 6. ✅ SAU KHI VÁ — chu-trình khép, số cộng KHỚP + +``` +permission-matrix : 5 FLAG → 0 FLAG +TOTAL : 50 → 45 (tụt ĐÚNG 5) +Flag MỚI xuất-hiện: 0 +``` + +**Diff đo bằng `comm` (không đếm bằng mắt):** đúng **5 dòng** biến mất — `:3`×2 · `:16`×2 · `:81`×1 — **0 dòng thêm**. ⇒ bản vá **không đụng gì ngoài phạm-vi**; TOTAL tụt **đúng bằng** số flag đóng, không phải trùng-hợp làm-tròn. + +**Cách vá = B1 (bỏ số, trỏ canonical), KHÔNG phải đổi số:** +| Dòng | Trước | Sau | +|---|---|---| +| `:3` description | `~60 menu key … ~240 policy` | bỏ số → *"số menu-key · policy canonical → `docs/STATUS.md`"* | +| `:16` Status | `(post Session 6 — 2026-04-30)` + `~60 menu key total` | anchor `2026-07-15 — S122 W4` + trỏ canonical + **chỉ ghi HÌNH-DẠNG nhóm, không ghi số** | +| `:81` | `register 48 policy` | `register policy {menu}.{action}` + khai **DERIVED** = `\|MenuKeys.All\| × \|Actions\|` | + +🔴 **Đổi số thì lần sau lại stale — bỏ số thì hết stale vĩnh-viễn.** Đây cũng đúng lời khuyên detector tự in: *"resolve: update to 54 **OR replace with pointer '-> docs/STATUS.md'**"* — chọn vế sau. + +## 7. 🔴 STALE NẶNG HƠN CON-SỐ (tìm được lúc vá, ngoài 5 flag) + +`SKILL.md:20` liệt `Budgets root + 3 Bg_* (List/Create/Pending)` + `:23` *"Budgets KHÔNG inherit (Bg_* phải grant tay)"* — **như menu ĐANG SỐNG**. +**Đo đĩa:** module Budget **XOÁ từ S61 (Mig 50)**. Hai hit `Bg_` còn lại trong mã = **comment bia-mộ**: `MenuKeys.cs:70` *"[S61 Mig 50] Module Ngân sách cũ (Budgets + Bg_List/Bg_Create/Bg_Pending)"* · `fe-{admin,user}/src/lib/menuKeys.ts:29` *"… XÓA — thay bằng bảng"*. Code **sạch**; chỉ skill còn mô-tả. +🔴 **Detector count-token KHÔNG bắt được cái này** — nó so **con-số**, mà đây là **thực-thể không tồn tại**. Skill mô-tả sai suốt **S61 → S122**. ⇒ **giới-hạn của detector đếm-số**: nó thấy *"60 ≠ 54"* nhưng không thấy *"4 trong 60 cái đó đã bị xoá 60 phiên trước"*. Vá @S122 W4 (🧊-mark, giữ bia-mộ để tra-cứu). + +## 8. Lead tự-bắt trong W4 (→ adap-report) + +1. **WAL ghi "permission-matrix 6 dòng" — thật 5.** Dòng thứ 6 (`:70`) là context trong khối tóm-tắt anchor, không phải flag. *(Bắt được TRƯỚC khi thành claim gửi hub — nhưng vẫn là lỗi đếm, lần thứ 3 trong phiên.)* +2. **Suýt claim ẩu về canonical:** lead grep thô `MenuKeys.cs` ra **64** chuỗi rồi định dùng làm số. **SAI** — grep đếm **mọi chuỗi trong file**, không phải phần-tử mảng `All`. W2 ground đúng: `|MenuKeys.All|` = **54** (`MenuKeys.cs:147`) × `|Actions|` 4 = **216**. ⇒ **Lead bỏ số của mình, dùng số W2.** Bài học: *đo bằng công-cụ sai còn tệ hơn không đo — vì nó ra một con-số trông có vẻ đo được.* + +--- +*Capture @S122 `/tiep` W4 · mục 1-5 ghi TRƯỚC mọi sửa đổi (TOTAL 50) · mục 6-8 ghi SAU (TOTAL 45) · exit 0 cả 2 lần*