From 4163f42fd234e9223a93a97cd56d1bfd1e9fe8ae Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Sat, 1 Aug 2026 08:55:22 +0700 Subject: [PATCH] wal: flush 20260801T0855 --- .../sub-reviewer-f1-checkbox.md | 70 ++++++++++++++++++- .../sub-lead-stale-deep-S166.md | 30 ++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-reviewer-f1-checkbox.md b/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-reviewer-f1-checkbox.md index 0af68ec..72fe8ef 100644 --- a/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-reviewer-f1-checkbox.md +++ b/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-reviewer-f1-checkbox.md @@ -26,4 +26,72 @@ không có nhánh "sửa 1 app quên app kia". ✅ (cùng bảng, cùng row mà `ResolveActingLevel` sẽ chấm lúc duyệt) ⇒ không có nguồn thứ hai. - 0 đổi logic BE: `git diff` trên `src` chỉ 2 hunk, cả 2 nằm trong DTO + projection. ✅ -(các mục dưới điền dần) +## §2 (đề bài 1) `actingLevel` FE có mirror ĐÚNG `ResolveActingLevel` không + +BE (`ContractSigningPlanWorkflowService.cs:463-468`): +`own = pendingLevelGroup.FirstOrDefault(l => l.ApproverUserId == actorId)` → `if (own is not null) return own;` +→ `if (isAdmin) return pendingLevelGroup.First();` → còn lại **ném Forbidden**. +FE (`KhkkWorkflowPanel.tsx:135-137`): +`currentLevels.find(l => l.approverUserId === user?.id) ?? (isAdmin ? currentLevels[0] : undefined)`. + +| Ca | BE chấm level nào | FE chấm level nào | Khớp? | +|---|---|---|---| +| Actor ∈ Cấp, cấp CÓ cờ | own (có cờ) | own (có cờ) → hiện ô-tích | ✅ | +| Actor ∈ Cấp OR-of-N, **cờ ở NGƯỜI KHÁC cùng Cấp** | own (KHÔNG cờ) ⇒ không finalize | own (KHÔNG cờ) ⇒ ẩn ô-tích, gửi `true` = no-op | ✅ (ca đề bài hỏi kỹ — không rò) | +| Admin **có** slot riêng trong Cấp | own | own (vì `find` chạy TRƯỚC nhánh isAdmin) | ✅ | +| Admin **không** có slot | `pendingLevelGroup.First()` | `currentLevels[0]` | ⚠️ xem §6 (thứ tự 2 truy vấn) | +| Không phải approver, không Admin | **Forbidden 403** | `undefined` ⇒ ẩn ô-tích | ✅ (và nút Duyệt đã `disabled` bởi `blockedByLevel` `:130`) | +| `currentLevels` **RỖNG** (phase ≠ ChoDuyet, hoặc con-trỏ null, hoặc idx ngoài biên) | không tới `ApproveV2Async` (409 `:250-251`) | `find`→undefined, `currentLevels[0]`→`undefined` ⇒ eligible=false | ✅ **không nổ** (index-out-of-range trên mảng rỗng trong JS = `undefined`, không throw) | +| `user` chưa nạp (`user?.id` undefined) | — | `find` không khớp (`approverUserId` là `string` non-null) ⇒ undefined; `isAdmin`=false ⇒ nút Duyệt disabled | ✅ | + +Vế **so-khớp GUID dạng chuỗi**: `user.id` sinh từ `res.data.user` (`AuthContext.tsx:53-56`, DTO login +serialize `Guid` → chữ thường có gạch), `approverUserId` cũng là `Guid` serialize cùng kiểu ⇒ `===` +hợp lệ; và đây là **cùng phép so đã sống từ W3** (`:127` `actorIsCurrentApprover`), không phải phép mới. + +⇒ **Mirror ĐÚNG 6/7 ca**; ca thứ 7 (Admin duyệt-thay trên Cấp **trộn cờ**) là finding §6/G-4. + +## §3 (đề bài 2) Body — rò field ở action khác? khớp hợp-đồng `bool?`? + +`KhkkWorkflowPanel.tsx:151-157`: +`{ action, comment, ...(a === Approve ? { applyLevelFinalize: eligible ? state : true } : {}) }` + +| Action | Key có trong JSON? | Giá trị | BE nhận | Ảnh hưởng | +|---|---|---|---|---| +| `submit` | **KHÔNG** | — | `ApplyLevelFinalize = null` → `?? true` (`Controller:213`) | `SubmitAsync` **không nhận tham số** (`Service:122`) ⇒ vô hại | +| `return` / `reject` | **KHÔNG** | — | như trên | `ReturnOrRejectAsync` **không nhận tham số** (`:130-136`) ⇒ vô hại | +| `approve`, cấp thường | CÓ | `true` | `true` | `actingLevel.AllowApproverFinalize && true` = **false** ⇒ no-op (§4) | +| `approve`, cấp có cờ, giữ tick | CÓ | `true` | `true` | finalize (đúng ý người bấm) | +| `approve`, cấp có cờ, bỏ tick | CÓ | `false` | `false` | rơi xuống advance thường ⇒ trình tiếp (đúng mục tiêu F-1) | + +⇒ **0 rò field** sang action khác; và ngay cả khi rò cũng không có chỗ đọc ở BE (3 nhánh kia không có tham số). + +**absent vs null vs true** — hợp-đồng `ContractSigningPlanTransitionBody(string Action, string? Comment = null, bool? ApplyLevelFinalize = null)`: +key vắng ⇒ System.Text.Json để `bool?` = `null` (bằng ĐÚNG giá trị mặc định khai báo, nên **không phụ thuộc** +vào chuyện STJ có tôn trọng default-parameter-value của positional record hay không — cả hai đường đều ra `null`) +⇒ `?? true`. FE **không bao giờ** gửi `null` tường minh (spread bỏ hẳn key, không set `undefined`). +⇒ 3 trạng thái absent/null/true hội tụ về CÙNG một hành vi. ✅ Khớp hợp-đồng. + +## §4 (đề bài 3) Regression cấp thường + rò ô-tích ở phase khác + +**Cấp thường (không cờ), trước ⟂ sau vá:** +- trước: body `{action, comment}` ⇒ BE `?? true` ⇒ `applyLevelFinalize=true` +- sau: body `{action, comment, applyLevelFinalize: true}` ⇒ BE `true` +⇒ **cùng một giá trị đi vào cùng một biểu thức** `if (actingLevel.AllowApproverFinalize && applyLevelFinalize)` +(`Service:276`), mà vế trái = `false` ⇒ nhánh không vào ở CẢ HAI thế giới. Hành vi **y hệt**, không phải +"giống về mặt cảm tính". ✅ +(Điểm yếu còn lại là của thiết kế BE, không phải của diff: no-op này chỉ đúng vì BE **AND** với cờ cấp — +FE gửi `true` cho người không có quyền finalize là dữ liệu thừa, không phải quyền thừa.) + +**Rò ô-tích ở phase khác — 3 lớp chặn ĐỘC LẬP, hỏng 1 lớp vẫn không rò:** +1. `currentLevels` chỉ khác `[]` khi `isWaiting` (`:118-121`) ⇒ DaDuyet/TraLai/TuChoi/Nháp ⇒ `actingLevel=undefined` ⇒ `approverFinalizeEligible=false`. +2. Nút "Duyệt" (đường DUY NHẤT đặt `action=Approve`) chỉ render trong `{isWaiting && (…)}` (`:312-335`). +3. Chính JSX ô-tích đòi `action === Approve` (`:443`). +⇒ **0 rò**. Kiểm chéo: `KhkkTransitionAction.Approve` được gán ở đúng 1 site (`:323`), grep 2 app cho thấy +không có site thứ hai. ✅ + +**Reset trạng thái:** `setApplyLevelFinalize(true)` đặt ở `onClick` mở dialog (`:322`) ⇒ bỏ tick rồi **Huỷ** +rồi mở lại vẫn về mặc định. (PE chỉ reset trong `onSuccess` `:288` ⇒ PE **giữ** tick sau khi Huỷ — chỗ này +KHKK **chặt hơn** khuôn.) ✅ + + + diff --git a/.claude/workflows/runs/2026-08-01-S166-h24-deep-midsession/sub-lead-stale-deep-S166.md b/.claude/workflows/runs/2026-08-01-S166-h24-deep-midsession/sub-lead-stale-deep-S166.md index 67ee1dd..b0deebb 100644 --- a/.claude/workflows/runs/2026-08-01-S166-h24-deep-midsession/sub-lead-stale-deep-S166.md +++ b/.claude/workflows/runs/2026-08-01-S166-h24-deep-midsession/sub-lead-stale-deep-S166.md @@ -42,3 +42,33 @@ Wave K1→K3 (`50e6d8c`/`0779f2d`/`15349e8`, S164-S165) cập-nhật **dòng :6* **resolve:** re-ground 5 ô `:465/:466/:470/:471/:472` về 71 / 97 / 256 / 64 / 614 (bao gồm sub-số `45D+569I`), rồi chạy lại detector — kỳ vọng FLAG `SKILL.md:93` biến mất và TOTAL đổi. --- + +## FLAG-2 — `view-stale-count` — HIGH + +**view:** `docs/STATUS.md:479` — chuỗi THẬT: `**Bundle hash live (prod):** admin **\`B43R6Y17\`** · user **\`NLI5umBg\`** … **Đo LIVE bằng curl 2026-07-31 @S164**` +**source:** `.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-cicd-verify-k3.md:86,88` — POST-deploy đo LIVE 31/07 18:26-18:27 +07: admin `index-mySTlx42.js` **ROTATE** · eoffice `index-CZAYiWWa.js` **FROZEN** + +Lệch: view nói `B43R6Y17`/`NLI5umBg`, source nói `mySTlx42`/`CZAYiWWa`. **0/2 khớp.** + +🔴 Nặng gấp đôi vì hai lẽ: +1. `docs/STATUS.md:6` cố ý **KHÔNG chép** số bundle mà trỏ *"canonical ở `:479`"* (đúng B1) ⇒ ai tuân B1 sẽ đáp thẳng vào ô sai. Đây là **con trỏ tốt tới nội-dung hỏng** — nguy hơn số rời rạc vì nó được cả hệ tin. +2. Chính vai cicd **đã nói ra** rồi mà chưa ai lật sổ: `sub-cicd-verify-k3.md:93-94` viết nguyên văn *"Đính chính mốc PRE trong đề bài: spec giao việc ghi baseline `admin B43R6Y17 · user NLI5umBg (@S164)` — KHÔNG khớp gì đo được"*. Phát-hiện đã tồn tại **trên đĩa**; khoảng cách nằm ở chỗ nó chưa đi từ run-folder về sổ owner-facing (đúng lớp bài S162: *nơi lead làm việc ≠ nơi anh đọc*). + +Ghi chú danh-pháp (đừng lẫn khi vá): `:479` gọi app thứ hai là **user**, cicd gọi **eoffice** — cùng site, khác tên. Nếu vá thì thống nhất một tên, kẻo sinh vocab-fork mới. + +**resolve:** `:479` = admin `mySTlx42` · user/eoffice `CZAYiWWa` (FROZEN, không phải ship-fail — cicd §5 dự-đoán TRƯỚC khi đo), kèm mốc 2026-07-31 18:26 +07 sau run #436. Hết flag khi hash ở `:479` khớp lần curl mới nhất. + +--- + +## FLAG-3 — `view-stale-count` — LOW + +**view:** `docs/STATUS.md:6` — chuỗi THẬT: `counter **39**` +**source:** `.claude/governance/.session-counter.json` → `counter = 40` (`last_ticked_head` = `24ea71e2…` = HEAD hiện tại, session S166; detector `H24-5` xác nhận `[OK] OK-reachable`) + +Lệch 39 vs 40 (−1). SEV LOW **có điều kiện**: đây là stale **biết-trước** giữa phiên (S166 vừa tick, STATUS được re-stamp ở closeout). Ghi FLAG để tally by-class không mất mẫu, KHÔNG phải để đòi vá ngay. + +🔴 Nhưng đừng đọc thành vô hại: `counter` là **đầu vào của chính nhịp H24** (`h24_cadence.deep_every`). Một sổ owner-facing trình sai counter làm owner không thấy được món nợ `deep` 15/15 mà chính lượt này đang trả. Nếu closeout quên bump, LOW này lặng lẽ sống tiếp. + +**resolve:** re-stamp `counter 40` ở closeout S166; hoặc bỏ hẳn con số khỏi `:6` và trỏ khoá `.session-counter.json` (kiểu B1 mà `:6` đã dùng đúng cho bundle). + +---