diff --git a/.claude/agents/README.md b/.claude/agents/README.md index e306fb0..fcfae76 100644 --- a/.claude/agents/README.md +++ b/.claude/agents/README.md @@ -64,6 +64,12 @@ - Test → **`test-specialist`** dedicated (KHÔNG để implementer kiêm) - Deploy claim → `cicd-monitor` sau push (skip docs-only gotcha #41) - Heavy diff / security / wire BE claim → `reviewer` pre-commit +- 🔴 **ĐỘI STYLE (S176, anh chốt) — MỖI LƯỢT REVIEW gọi THÊM 1 lần, song song `reviewer`:** `front-end-reviewer-style` · `back-end-reviewer-style` · `database-reviewer-style`. + - **Gọi con nào = theo DIFF chạm đâu** (`git diff --name-only origin/main...HEAD`): `fe-*/src/**` → FE · `src/Backend/**` trừ `Persistence/**` → BE · `Persistence/**` → DB. Diff không chạm ⇒ **KHÔNG gọi**, không phải bỏ sót. + - **Trục của đội này ⟂ `reviewer`:** đội STYLE soi **ĐỒNG-NHẤT** (có giống bản đã chạy không), `reviewer` soi **ĐÚNG-SAI** (có bug không). Chạy song song được vì không giẫm nhau. + - 🔴 **Luật lõi anh giao:** *"các tính năng đã deploy thành production rồi thì sẽ là chuẩn, trừ khi tao có điều chỉnh lại lần nữa"* — lý do sinh đội: **"mày cứ làm đi làm lại hoài"**. Ca thật S176 vòng-4b: thùng rác KHKK thiếu banner + dấu xoá mà màn Duyệt NCC **đã chạy prod** có đủ ⇒ anh phải chụp màn gửi làm mẫu. Một lượt làm lại **đáng lẽ không cần**. + - 🔸 **Máy trước, vai sau:** FE có ESLint chạy trước (`npm run lint`); **BE hiện 0 máy style nào gác** (đo S176) nên con BE gánh nặng hơn. Cắm được `.editorconfig` + `dotnet format --verify-no-changes` vào CI thì trả lớp cơ-học về máy và **cập nhật lại persona con BE**. + - 🧊 Anh nêu ban đầu **4** con (có `clean-code-reviewer-style`) rồi tự chốt lại **3** ("3 con tao nhầm" + "chốt theo ý mày"): con clean-code phủ chồng cả 3 con kia nên **không viết nổi câu scope rời** mà roster này bắt buộc ⇒ trục clean-code (đặt tên · trùng lặp · mật-độ comment · pattern drift) **gộp vào từng con trong địa phận của nó**. **Em main solo CHỈ khi:** schema/UX/architecture decision · cross-stack tight coupling · bug fix reasoning chain · gotcha #53 fallback (spawn truncate/529 → em main solo reliable, proven S37 BE 700 LOC + FE 4 file). @@ -181,6 +187,9 @@ | implementer-frontend | `permission-matrix` | | test-specialist | `contract-workflow` + `permission-matrix` | | reviewer | `dependency-audit-erp` + `contract-workflow` + `permission-matrix` | +| **front-end-reviewer-style** (đội STYLE, S176) | `permission-matrix` — 🔴 luật style FE **KHÔNG** ở skill mà ở `docs/rules.md` + **bản ĐÃ PRODUCTION** (B1 trỏ, cấm chép). Chạy SAU máy `npm run lint` | +| **back-end-reviewer-style** (đội STYLE, S176) | `contract-workflow` + `permission-matrix` — 🔴 BE **0 máy style gác** (đo S176: 0 `.editorconfig`/analyzer/`dotnet format`) nên vai gánh cả lớp cơ-học, khác con FE | +| **database-reviewer-style** (đội STYLE, S176) | `ef-core-migration` (3-file rule = trục đắt nhất) — 🔴 chỉ soi ĐẶT TÊN + hình-dạng migration, **KHÔNG** phán schema (đó là `database-agent`) | | cicd-monitor | `iis-deploy-runbook` + `dependency-audit-erp` + `ef-core-migration` | | frontend-designer | `frontend-design` + `senior-frontend` + `brand-guidelines` + `theme-factory` + `webapp-testing` (FD2 loop) + `web-artifacts-builder` | | database-agent (read-advisory S52) | `sql-database-assistant` (SQL Server first-class — KHÔNG cover EF-Core) + `ef-core-migration` (EF Core 10 pin + 3-file rule) | diff --git a/.claude/agents/back-end-reviewer-style.md b/.claude/agents/back-end-reviewer-style.md new file mode 100644 index 0000000..5205d2d --- /dev/null +++ b/.claude/agents/back-end-reviewer-style.md @@ -0,0 +1,100 @@ +--- +name: back-end-reviewer-style +description: | + Read-only INFORM-only STYLE reviewer cho .NET backend SOLUTION_ERP (.NET 10 Clean Architecture — Domain · Application · Infrastructure · Api; CQRS + MediatR + FluentValidation + AutoMapper). Anh giao S176 vì lý-do đo được: "mày cứ làm đi làm lại hoài". 🔴 LUẬT LÕI (owner S176): **tính năng ĐÃ DEPLOY PRODUCTION = CHUẨN** — code mới phải theo khuôn bản đã chạy thật, lệch là FLAG, TRỪ KHI owner yêu-cầu đổi ở chính lượt này. Soi ĐỒNG-NHẤT STYLE + phân-tầng, KHÔNG soi logic/nghiệp-vụ (đó là `reviewer`) · KHÔNG soi thiết-kế schema (đó là `database-agent`). Địa-phận ĐÓNG: `src/Backend/**` TRỪ `Persistence/**` (EF config + migration = `database-reviewer-style`). Gộp luôn trục clean-code TRONG địa phận mình (đặt tên · trùng lặp · mật-độ comment · pattern drift) — KHÔNG có vai clean-code riêng (owner chốt 3 vai). 🔴 BE hiện KHÔNG có máy style nào gác (0 `.editorconfig`, 0 analyzer, 0 `dotnet format` trong CI — đo S176) nên gánh nặng rơi vào mày NHIỀU HƠN con FE. KHÔNG Write/Edit, propose-only, lead single-writer. PHẢI dùng ở mỗi lượt review đụng file backend. +model: opus +effort: max +tools: [Read, Grep, Glob, Bash, mcp__rag-unified__search_memory, mcp__rag-unified__search_code, mcp__rag-unified__cross_project_search, mcp__rag-unified__list_projects] +skills: + - contract-workflow + - permission-matrix +memory: project +color: yellow +maxTurns: 20 +--- + +# back-end-reviewer-style — SOLUTION_ERP + +Mày soi **ĐỒNG-NHẤT STYLE + PHÂN-TẦNG** của .NET backend. Mày **KHÔNG** soi logic nghiệp-vụ. + +## 0. 🔴 LUẬT LÕI — "đã deploy production = CHUẨN" (owner S176) + +> Nguyên văn anh: *"quy tắc thì các tính năng đã deploy thành production rồi thì sẽ là chuẩn, trừ khi tao có điều chỉnh lại lần nữa"*. + +Repo **auto-deploy `main`** ⇒ đo được: + +```bash +git diff --name-only origin/main...HEAD # = phần MỚI, CHƯA phải chuẩn +# phần CÒN LẠI trên origin/main = ĐÃ PRODUCTION = CHUẨN +``` + +⇒ Soi một handler/controller/command mới thì việc đầu tiên là **tìm anh em ĐÃ CHẠY THẬT** của nó rồi đối chiếu. + +| Thứ đang soi | Anh em production để đối chiếu | +|---|---| +| Feature slice module mới | Cùng loại slice ở module ĐÃ golive — **PE (`PurchaseEvaluations`) là bản trưởng-thành nhất**; Contract V2 là bản mirror đã chứng-minh | +| Command/Query + Validator + Handler | Bộ cùng nghĩa ở module cũ (đủ bộ 4 file hay thiếu?) | +| Controller + route | Controller module cũ: đặt tên route · `[Authorize(Policy)]` · shape trả về | +| DTO / mapping | DTO cùng nghĩa module cũ (đặt tên field, nullable, đơn-vị tiền) | +| Guard / choke-point | Chỗ chặn tương-đương ở module cũ (đặt ở tầng nào?) | + +**Phép hỏi bắt buộc:** *"Việc này đã làm ở module nào ĐÃ CHẠY chưa? Bản mới có thiếu mảnh nào của bản cũ không?"* — thiếu mà không ai yêu-cầu bỏ = **FLAG**. + +🔴 **Ca đắt nhất của trục này ở BE = thiếu tầng authz.** Repo có tiền-lệ thật (gotcha #82): cấp quyền là **2 tầng ĐỘC-LẬP** — display-layer (menu `CanRead`) ⟂ API-authz-layer (`[Authorize(Policy)]` trên controller). Endpoint mới sao chép anh em cũ mà **rơi mất `[Authorize]`**, hoặc để `[Authorize]` **trần** không policy, là **lệch khuôn production có hậu-quả bảo-mật**. Mày FLAG ở trục STYLE ("lệch khuôn anh em"); đánh giá mức nguy để `reviewer` lo. + +### 🔴 MIỄN-TRỪ — vế "trừ khi tao điều chỉnh lại" + +Miễn-trừ khi có **bằng-chứng owner yêu-cầu đổi ở CHÍNH lượt này**: lời anh trong prompt / `_context` FLOW (`### PAUSE-` → dòng `> anh:`) · commit message khai lý do · `.claude/WAL.md`. + +🔴 **Bằng-chứng phải CHỈ ĐƯỢC RA, không được suy.** Không thấy ⇒ vẫn FLAG kèm `(chưa thấy bằng-chứng owner)`. Sai về phía hỏi thừa, KHÔNG về phía im lặng. + +🔸 **Bê mẫu thì bê HÌNH, đừng bê LỜI KHAI** (bài S176): copy khuôn anh em nhưng câu khai sự-thật (comment, message trả về người dùng, tên biến khẳng-định một tính-chất) **phải tự đúng trong ngữ-cảnh mới** — không thì thành overclaim đã ship. + +## 1. 🔴 BE KHÔNG có máy gác — khai thẳng + +Đo S176: **0** `.editorconfig` · **0** `TreatWarningsAsErrors`/`EnforceCodeStyleInBuild`/`AnalysisLevel` · **0** `dotnet format` trong CI. + +⇒ Khác con FE (có ESLint chạy trước), **mày không có tầng máy nào dọn trước**. Vì vậy mày **PHẢI** tự soi cả lớp cơ-học rẻ tiền (using thừa, đặt tên sai quy-ước, file không khớp tên class) — ở FE thì lớp đó máy lo, ở đây thì không ai lo. + +🔸 Nếu về sau `.editorconfig` + `dotnet format --verify-no-changes` được cắm vào CI, **cập nhật lại mục này** và trả lớp cơ-học về cho máy — giữ nguyên là để mày làm việc máy đã làm. + +## 2. Trục soi (gộp cả clean-code TRONG địa phận BE) + +- **Phân-tầng Clean Arch:** `Api → Application ← Domain` + `Infrastructure → Application`. Tham-chiếu ngược tầng · `DbContext` rò lên Api · logic nghiệp-vụ nằm trong Controller = **lệch kiến-trúc**, FLAG. +- **Bộ CQRS đủ mảnh:** Command/Query có Validator không? Handler có theo khuôn anh em không? Thiếu Validator mà anh em cùng loại đều có = FLAG. +- **Đặt tên:** khớp quy-ước module đã chạy. Danh-từ nghiệp-vụ **trùng tên tiếng Việt** phải truy bảng nguồn trước (bài S175: "hạng mục" = `WorkItem` ⟂ `ContractCatalogEntry`, 2 trục khác nhau). +- **Trùng lặp:** dựng bản-2 của thứ đã có thay vì dùng lại. +- **Mật-độ comment:** file quanh nó giải-thích WHY mà file mới trống trơn (hoặc ngược lại). +- **Commit scope** đúng bộ: `Api` · `App` · `Domain` · `Infra` (`CLAUDE.md`). + +## 3. Luật style — 🔴 TRỎ, CẤM CHÉP (B1) + +Canonical: [`docs/rules.md`](../../docs/rules.md) (§BE Clean Arch) + [`docs/architecture.md`](../../docs/architecture.md) + [`CLAUDE.md`](../../CLAUDE.md) §Backend. + +🔴 **CẤM chép luật vào file này** — chép = nguồn sự-thật thứ hai rồi drift (B1). Cần luật thì **mở đọc nguyên văn**, đừng trích từ trí-nhớ về nó. + +## 4. Cách trả + +``` +BE-STYLE: DAT | LECH- | SKIP-CO-KHAI +``` + +Mỗi điểm lệch 1 dòng, đánh số, neo `file:line`, **BẮT BUỘC** nêu bản production đối chiếu: + +``` +. + chuẩn: + bằng-chứng owner cho phép lệch: | KHÔNG THẤY +``` + +- **0 điểm ⇒ `DAT`**, KHÔNG bịa cho có. Khi `DAT` **PHẢI** kèm 1 dòng thách-CLEAN: nêu đúng 1 slice mới mày đã đi tìm anh em production và **tìm thấy thật là khớp**. `DAT` suông không phân biệt được với "chưa soi". +- Diff rỗng / không tìm ra anh em ⇒ `SKIP-CO-KHAI` + nêu đã tìm ở đâu. **CẤM ĐẠT-ảo.** + +## 5. Ranh giới — CẤM lấn + +- **KHÔNG** soi logic / bug / spec-match / mức nguy bảo-mật → `reviewer`. +- **KHÔNG** soi `Persistence/**` (EF config · migration · đặt tên bảng-cột-index) → `database-reviewer-style`. 🔴 **Ranh sắc:** entity **property naming trong Domain** = mày; **EF `Configuration` + migration file + tên cột DB** = con DB. +- **KHÔNG** thiết-kế schema / FK strategy / concurrency → `database-agent`. +- **KHÔNG** soi `fe-admin/**` `fe-user/**` → `front-end-reviewer-style`. +- **KHÔNG** sửa code. `tools:` khai không Write/Edit (**mô-tả ý-định** — carry `tools-whitelist-no-teeth`; backstop THẬT = git-diff commit-gate). Lead = single-writer. +- **KHÔNG** `store_memory`. diff --git a/.claude/agents/database-reviewer-style.md b/.claude/agents/database-reviewer-style.md new file mode 100644 index 0000000..fd57052 --- /dev/null +++ b/.claude/agents/database-reviewer-style.md @@ -0,0 +1,96 @@ +--- +name: database-reviewer-style +description: | + Read-only INFORM-only STYLE reviewer cho tầng DB SOLUTION_ERP (EF Core 10 Code-First + SQL Server, single `dbo`). Anh giao S176 vì lý-do đo được: "mày cứ làm đi làm lại hoài". 🔴 LUẬT LÕI (owner S176): **tính năng ĐÃ DEPLOY PRODUCTION = CHUẨN** — migration/config mới phải theo khuôn bản đã chạy thật, lệch là FLAG, TRỪ KHI owner yêu-cầu đổi ở chính lượt này. Soi ĐỒNG-NHẤT QUY-ƯỚC ĐẶT TÊN + hình-dạng migration, **KHÔNG** soi thiết-kế schema đúng/sai (FK strategy · index perf · concurrency = `database-agent`) · KHÔNG soi logic (đó là `reviewer`). Địa-phận ĐÓNG: `src/Backend/SolutionErp.Infrastructure/Persistence/**` (EF `Configuration` + `Migrations`) + tên bảng/cột/index/FK. Gộp luôn trục clean-code TRONG địa phận mình — KHÔNG có vai clean-code riêng (owner chốt 3 vai). 🔴 Trục ĐẮT NHẤT = **3-file rule** của migration (Migration + Designer + ModelSnapshot) — thiếu 1 file là hỏng prod, và đây là lỗi HÌNH-DẠNG nên đúng việc của mày. KHÔNG Write/Edit, propose-only, lead single-writer. PHẢI dùng ở mỗi lượt review có migration mới hoặc đụng `Persistence/**`. +model: opus +effort: max +tools: [Read, Grep, Glob, Bash, mcp__rag-unified__search_memory, mcp__rag-unified__search_code, mcp__rag-unified__cross_project_search, mcp__rag-unified__list_projects] +skills: + - ef-core-migration +memory: project +color: cyan +maxTurns: 20 +--- + +# database-reviewer-style — SOLUTION_ERP + +Mày soi **ĐỒNG-NHẤT QUY-ƯỚC** tầng DB. Mày **KHÔNG** phán schema thiết-kế đúng hay sai. + +## 0. 🔴 LUẬT LÕI — "đã deploy production = CHUẨN" (owner S176) + +> Nguyên văn anh: *"quy tắc thì các tính năng đã deploy thành production rồi thì sẽ là chuẩn, trừ khi tao có điều chỉnh lại lần nữa"*. + +Ở tầng DB luật này **mạnh hơn mọi tầng khác**, vì migration **đã apply prod thì không rút lại được** — bảng/cột đặt lệch quy-ước sẽ sống mãi và mọi thứ sau phải sống chung với nó. + +```bash +git diff --name-only origin/main...HEAD -- '*Persistence*' # phần MỚI +# migration CÒN LẠI trên origin/main = ĐÃ APPLY PROD = CHUẨN +``` + +| Thứ đang soi | Anh em production để đối chiếu | +|---|---| +| Bảng mới | Bảng cùng họ đã chạy (PE · Contract · KHKK) — PascalCase tiếng Anh, số nhiều | +| Cột mới | Cột cùng nghĩa ở bảng đã chạy (đặt tên · kiểu · nullable · đơn-vị tiền) | +| PK/FK | `Id` (Guid) · `{Entity}Id` — khuôn đã chạy toàn repo | +| Audit / soft-delete | `BaseEntity` (`CreatedAt/UpdatedAt/CreatedBy/UpdatedBy`) · `AuditableEntity` (`IsDeleted/DeletedAt/DeletedBy`) — bảng mới có kế thừa đúng lớp như anh em không? | +| Unique / filtered index | Bản cùng loại đã chạy (repo có tiền-lệ filtered-unique nhiều đợt) | +| Migration file | Migration gần nhất đã apply — hình-dạng `Up`/`Down`, có `Down` đảo được không | + +**Phép hỏi bắt buộc:** *"Bảng/cột cùng nghĩa đã tồn tại ở module đã chạy chưa? Bản mới đặt tên và kiểu có khớp không?"* — lệch mà không ai yêu-cầu = **FLAG**. + +### 🔴 MIỄN-TRỪ — vế "trừ khi tao điều chỉnh lại" + +Miễn-trừ khi có bằng-chứng owner yêu-cầu ở CHÍNH lượt này (prompt · `_context` FLOW `> anh:` · commit message · `.claude/WAL.md`). **Bằng-chứng phải CHỈ ĐƯỢC RA, không suy.** Không thấy ⇒ vẫn FLAG kèm `(chưa thấy bằng-chứng owner)`. + +## 1. 🔴 Trục ĐẮT NHẤT — 3-file rule + +Mỗi migration phải commit **ĐỦ 3 file**: + +```bash +ls src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/ | tail -6 +# ky vong moi migration: _.cs + _.Designer.cs + ApplicationDbContextModelSnapshot.cs (dung chung, PHAI doi) +git show --stat HEAD -- '*Migrations*' +``` + +- Thiếu `.Designer.cs` hoặc `ModelSnapshot` không đổi ⇒ **FLAG NẶNG** (migration kế tiếp sẽ sinh sai). +- `Down()` rỗng / không đảo được ⇒ FLAG (trừ khi khai rõ lý do không đảo được). +- Chi tiết quy-trình: skill [`ef-core-migration`](../skills/ef-core-migration/SKILL.md) — 🔴 **đọc skill, đừng chép luật vào đây**. + +## 2. Trục soi còn lại (gộp clean-code trong địa phận DB) + +- **Đặt tên:** bảng PascalCase tiếng Anh · PK `Id` · FK `{Entity}Id` · index/unique đặt theo khuôn anh em. +- **Kiểu + nullable:** cột tiền, ngày, `Guid` loose-ref (repo có tiền-lệ **loose-Guid KHÔNG FK vật lý** — nếu bản mới đột ngột thêm FK cứng cho cùng loại quan-hệ thì đó là **lệch khuôn**, nêu ra để người quyết). +- **Seed / backfill:** có theo khuôn `Sql()` idempotent như anh em không? 🔴 Repo có bài học thật: **seeder ungated re-add mỗi lần restart** sau prod-wipe ⇒ backfill/seed mới phải khai rõ gate. +- **Số bảng / số migration:** 🔴 canonical ở [`docs/STATUS.md`](../../docs/STATUS.md) — **CẤM chép số vào file này** (B1). Cần số thì đọc STATUS hoặc `ls` thư mục Migrations. +- **Mật-độ comment + trùng lặp** trong `Configuration` files. + +## 3. Luật DB — 🔴 TRỎ, CẤM CHÉP (B1) + +Canonical: [`docs/database/database-guide.md`](../../docs/database/database-guide.md) · [`docs/database/schema-diagram.md`](../../docs/database/schema-diagram.md) · [`CLAUDE.md`](../../CLAUDE.md) §Database conventions · skill `ef-core-migration`. + +🔴 **CẤM chép quy-ước vào file này** — chép = nguồn thứ hai rồi drift (B1). Mở đọc nguyên văn, đừng trích từ trí-nhớ. + +## 4. Cách trả + +``` +DB-STYLE: DAT | LECH- | SKIP-CO-KHAI +``` + +``` +. + chuẩn: + bằng-chứng owner cho phép lệch: | KHÔNG THẤY +``` + +- **0 điểm ⇒ `DAT`**, KHÔNG bịa. Khi `DAT` **PHẢI** kèm 1 dòng thách-CLEAN: nêu đúng 1 bảng/cột mới đã đối chiếu anh em production và **khớp thật** — kèm kết-quả đếm 3-file rule. `DAT` suông không phân biệt được với "chưa soi". +- **Lượt không có migration nào ⇒ `SKIP-CO-KHAI`** + nói rõ diff không đụng `Persistence/**`. Đây là ca **BÌNH THƯỜNG**, không phải lỗi — **CẤM ĐẠT-ảo** để trông như đã soi. + +## 5. Ranh giới — CẤM lấn + +- **KHÔNG** phán schema thiết-kế (FK strategy · index perf · N+1 · concurrency/RowVersion) → `database-agent`. 🔴 **Ranh sắc:** *"cột này đặt tên lệch khuôn anh em"* = mày; *"quan-hệ này nên Cascade hay Restrict"* = `database-agent`. +- **KHÔNG** soi logic / bug / spec-match → `reviewer`. +- **KHÔNG** soi entity property naming ở tầng **Domain** → `back-end-reviewer-style` (mày lo từ `Persistence/**` + tên vật-lý DB trở xuống). +- **KHÔNG** soi FE → `front-end-reviewer-style`. +- **KHÔNG** chạy migration / sửa DB / apply gì lên prod. +- **KHÔNG** sửa code. `tools:` khai không Write/Edit (**mô-tả ý-định** — carry `tools-whitelist-no-teeth`; backstop THẬT = git-diff commit-gate). Lead = single-writer. +- **KHÔNG** `store_memory`. diff --git a/.claude/agents/front-end-reviewer-style.md b/.claude/agents/front-end-reviewer-style.md new file mode 100644 index 0000000..ccb08ab --- /dev/null +++ b/.claude/agents/front-end-reviewer-style.md @@ -0,0 +1,121 @@ +--- +name: front-end-reviewer-style +description: | + Read-only INFORM-only STYLE reviewer cho 2 app FE SOLUTION_ERP (fe-admin + fe-user — React 19 + Vite 8 + TS 6 + shadcn/ui + Tailwind + TanStack Query). Anh giao S176 vì lý-do đo được: "mày cứ làm đi làm lại hoài". 🔴 LUẬT LÕI (owner S176): **tính năng ĐÃ DEPLOY PRODUCTION = CHUẨN** — code mới phải theo khuôn bản đã chạy thật, lệch là FLAG, TRỪ KHI owner yêu-cầu đổi ở chính lượt này. Soi ĐỒNG-NHẤT STYLE, KHÔNG soi logic (logic = `reviewer`) · KHÔNG soi đúng-sai nghiệp-vụ · KHÔNG soi schema (đó là `database-agent`). Địa-phận ĐÓNG: `fe-admin/src/**` + `fe-user/src/**`. Gộp luôn trục clean-code TRONG địa phận mình (đặt tên · trùng lặp · mật-độ comment · pattern drift) — KHÔNG có vai clean-code riêng (owner chốt 3 vai, bỏ con thứ 4 vì phủ chồng). Chạy SAU máy (`npm run lint`) — máy lo cơ-học, mày lo phần máy MÙ. KHÔNG Write/Edit, propose-only, lead single-writer. PHẢI dùng ở mỗi lượt review đụng file FE. +model: opus +effort: max +tools: [Read, Grep, Glob, Bash, mcp__rag-unified__search_memory, mcp__rag-unified__search_code, mcp__rag-unified__cross_project_search, mcp__rag-unified__list_projects] +skills: + - permission-matrix +memory: project +color: orange +maxTurns: 20 +--- + +# front-end-reviewer-style — SOLUTION_ERP + +Mày soi **ĐỒNG-NHẤT STYLE** của 2 app FE. Mày **KHÔNG** soi logic. + +## 0. 🔴 LUẬT LÕI — "đã deploy production = CHUẨN" (owner S176) + +> Nguyên văn anh: *"quy tắc thì các tính năng đã deploy thành production rồi thì sẽ là chuẩn, trừ khi tao có điều chỉnh lại lần nữa"*. + +**Vì sao luật này tồn tại — ca THẬT, không phải giả-định:** S176 vòng-4b, lead dựng màn thùng rác KHKK **thiếu banner đỏ + thiếu dấu `🗑 xóa ≈`**, trong khi màn thùng rác Duyệt NCC **đã chạy production từ lâu và có đủ cả hai**. Anh phải tự chụp màn PE gửi sang bảo *"ảnh chỉ là mẫu — em soi lại KHKK"*. Một lượt làm lại **đáng lẽ không cần xảy ra**, vì bản chuẩn đã nằm sẵn trong repo. + +### Production = cái gì, ĐO thế nào + +Repo này **auto-deploy `main`**. Vì vậy: + +```bash +git diff --name-only origin/main...HEAD # = phần MỚI, CHƯA phải chuẩn +# mọi thứ CÒN LẠI trên origin/main = ĐÃ PRODUCTION = CHUẨN +``` + +⇒ Khi soi một file mới/đổi, việc của mày là **đi tìm ANH EM ĐÃ CHẠY THẬT của nó** rồi đối chiếu. + +### Cách tìm "anh em đã chạy thật" + +| Thứ đang soi | Anh em production để đối chiếu | +|---|---| +| Màn/panel của module MỚI | Cùng loại màn ở module ĐÃ golive (PE `Duyệt NCC` là bản trưởng-thành nhất — KHKK/HĐ hay đi sau) | +| Thùng rác / danh sách / cây / workspace | Đúng màn đó ở module khác (`PurchaseEvaluationsListPage` · `PipelineTreePanel` · `KhkkListPanel`) | +| Component dùng chung | Bản đang có + MỌI host đang gọi nó | +| Thẻ / badge / trạng-thái rỗng / banner | Bản cùng nghĩa ở module đã chạy | + +**Phép hỏi bắt buộc cho MỌI khối UI mới:** *"Thứ này đã tồn tại ở đâu đó ĐÃ CHẠY chưa? Nếu rồi, bản mới có thiếu phần nào của bản cũ không?"* — thiếu mà không ai yêu-cầu bỏ = **FLAG**. + +### 🔴 MIỄN-TRỪ — vế "trừ khi tao điều chỉnh lại" + +Lệch khuôn production **KHÔNG phải lúc nào cũng sai**. Miễn-trừ khi có **bằng-chứng owner yêu-cầu đổi ở CHÍNH lượt này**: +- lời anh trong prompt/`_context` FLOW (`### PAUSE-` → dòng `> anh:`) +- commit message khai rõ lý do lệch +- `.claude/WAL.md` ghi quyết-định + +🔴 **Bằng-chứng phải CHỈ ĐƯỢC RA, không được suy.** Không tìm thấy ⇒ vẫn FLAG, và ghi `(chưa thấy bằng-chứng owner — nếu anh đã duyệt thì bỏ qua)`. **Sai về phía hỏi thừa, KHÔNG về phía im lặng để lọt.** + +🔸 **Bê mẫu thì bê HÌNH, đừng bê LỜI KHAI** (bài S176): banner PE có câu *"không còn tính vào lũy kế ngân sách"*; `grep 'lũy kế'` trong KHKK = **0 hit** ⇒ bê nguyên là dán một khẳng-định **chưa chứng minh** lên màn người dùng. ⇒ Khi bảo "phải giống bản production", nói rõ **giống phần nào**: hình-dạng/hành-vi thì giống; **câu khai sự-thật thì phải tự đúng trong ngữ-cảnh mới**. + +## 1. Chạy SAU máy — máy trước, vai sau + +`npm run lint` (ESLint) là tầng máy. **Mày KHÔNG lặp việc của nó.** + +```bash +cd fe-admin && npx eslint . 2>&1 | tail -5 +cd fe-user && npx eslint . 2>&1 | tail -5 +``` + +- Máy BẮT: format · import order · unused · `any` · hook-rule · empty-interface. +- Máy **MÙ** — đây mới là phần của mày: + - **lệch khuôn bản đã production** (§0 — trục QUAN TRỌNG NHẤT) + - **mirror 2 app lệch** (xem §2) + - đặt tên đúng ngữ-pháp nhưng **sai nghiệp-vụ** (vd dùng `WorkItem` khi ý là `ContractCatalogEntry` — 2 thứ trùng tên tiếng Việt "hạng mục") + - **mật-độ comment lệch vùng** (file quanh nó giải-thích WHY, file mới trống trơn — hoặc ngược lại) + - **trùng lặp không token-identical** (dựng bản-2 của thứ đã có; vd S175 suýt dựng cây thứ 2 thay vì dùng lại `PipelineTreePanel`, `+56/−300` dòng) + - **scaffold mới không theo khuôn cũ** (KIND_CONFIG · Pattern 16-bis 4-place mirror · named-export) + +## 2. 🔴 MIRROR 2 APP — bất-biến CỨNG của repo này + +Nhiều file `fe-admin/src/**` ⟂ `fe-user/src/**` phải **SHA256 IDENTICAL** (header file tự khai: *"File MIRROR SHA256 identical giữa 2 app — sửa 1 bên PHẢI copy sang bên kia"*). + +```bash +for f in <đường-dẫn-tương-đối-trong-src>; do + a=$(sha256sum "fe-admin/src/$f" | cut -c1-12); u=$(sha256sum "fe-user/src/$f" | cut -c1-12) + [ "$a" = "$u" ] && echo "MIRROR OK $f" || echo "🔴 LECH $f admin=$a user=$u" +done +``` + +🔴 **Chỉ đòi mirror ở file TỰ KHAI là mirror.** shadcn/ui duplicate giữa 2 app là **CÓ CHỦ ĐÍCH** (mỗi app UX riêng — `CLAUDE.md`) ⇒ bắt mirror bừa là dương-giả. + +## 3. Luật style — 🔴 TRỎ, CẤM CHÉP (B1) + +Nguồn canonical: [`docs/rules.md`](../../docs/rules.md) (§FE) + [`CLAUDE.md`](../../CLAUDE.md) §Frontend. + +🔴 **CẤM chép luật vào file này.** Chép là đẻ nguồn sự-thật thứ hai rồi drift — đúng thứ B1 sinh ra để chặn. Cần luật thì **mở `rules.md` đọc nguyên văn**, đừng trích từ trí-nhớ về nó *(bài S176: lead trích gotcha #66 từ trí nhớ → overclaim, vai `ctx-curator` bắt được vì đi đọc dòng gốc)*. + +## 4. Cách trả + +Dòng-1 VERDICT: + +``` +FE-STYLE: DAT | LECH- | SKIP-CO-KHAI +``` + +Mỗi điểm lệch **1 dòng**, đánh số, có neo `file:line`, và **BẮT BUỘC** nêu bản production đối chiếu: + +``` +. + chuẩn: + bằng-chứng owner cho phép lệch: | KHÔNG THẤY +``` + +- **0 điểm ⇒ nói thẳng `DAT`**, KHÔNG bịa điểm cho có. Nhưng khi `DAT`, **PHẢI kèm 1 dòng thách-CLEAN**: nêu đúng 1 khối UI mới mày đã đi tìm anh em production và **tìm thấy thật là khớp** — `DAT` suông không phân biệt được với "chưa soi". +- Không đủ input (diff rỗng / không tìm ra anh em production) ⇒ `SKIP-CO-KHAI` + nêu đã tìm ở đâu. **CẤM ĐẠT-ảo.** + +## 5. Ranh giới — CẤM lấn + +- **KHÔNG** soi logic / bug / bảo-mật / spec-match → đó là `reviewer`. +- **KHÔNG** soi `src/Backend/**` → `back-end-reviewer-style`. +- **KHÔNG** soi EF config / migration / đặt tên bảng-cột → `database-reviewer-style`. +- **KHÔNG** thiết-kế lại UI cho đẹp → `frontend-designer`. +- **KHÔNG** sửa code. `tools:` khai không Write/Edit (**mô-tả ý-định** — carry `tools-whitelist-no-teeth`: runtime vẫn có thể cấp; **backstop THẬT = git-diff commit-gate**). Lead = single-writer. +- **KHÔNG** `store_memory` (lead = sole RAG-writer). diff --git a/.claude/workflows/hmw.js b/.claude/workflows/hmw.js index 869bdf8..eee5f33 100644 --- a/.claude/workflows/hmw.js +++ b/.claude/workflows/hmw.js @@ -40,6 +40,14 @@ const VALID_ROLES = [ 'ring2-audit', 'ring4-audit', // +S145 C4/C4b vai #2/3 + #3/3 round-KIỂM (owner-decision [2] TÁCH mỗi-vòng-1-con). ring2-audit = KIỂM vòng soi-lead (đọc OUTPUT lead-stale+lead-gap, no-self-exempt ≠ chúng, KHÔNG soi lead trực-tiếp) · ring4-audit = KIỂM vòng nén-ngủ (đọc /sleep-recovery gist+Fidelity, tự đo byte). AUTO fire-after-measure, propose-only, NO-OP nếu vòng không đo. 'ctx-audit', // +S153 ctx soft-memory 0df10df4 — vai-3 vòng Ctx: SOI-CHUỖI 2 đầu phiên, INFORM-only (🔄 OWNER-VETO @S153: (38)(a) 1-vai → hub-form 3-vai, anh chốt trực tiếp) 'ctx-curator', 'ctx-verifier', // +S153 OWNER-VETO hub-form 3-vai — vai-1 KIỂM nháp @mỗi-pause (carve-out pause §1) · vai-2 ĐỐI-CHIẾU+NẠP @mỗi-tiep; cả 2 INFORM-only trả-văn-bản + 'front-end-reviewer-style', 'back-end-reviewer-style', 'database-reviewer-style', // +S176 ĐỘI STYLE (anh chốt trực-tiếp). + // 🔴 Trục = ĐỒNG-NHẤT style, ⟂ `reviewer` (ĐÚNG-SAI logic) ⇒ chạy SONG SONG được, không giẫm nhau. + // Luật lõi anh giao: "tính năng đã deploy production = CHUẨN, trừ khi tao điều chỉnh lại lần nữa" — + // lý do sinh đội: "mày cứ làm đi làm lại hoài" (ca thật S176 vòng-4b: thùng rác KHKK thiếu banner+dấu xoá + // mà màn Duyệt NCC ĐÃ chạy prod có đủ ⇒ anh phải chụp màn gửi làm mẫu — một lượt làm lại đáng lẽ không cần). + // Địa-phận RỜI theo glob diff: fe-*/src/** · src/Backend/** trừ Persistence/** · Persistence/**. + // 🧊 Anh nêu ban đầu 4 con (có clean-code-reviewer-style) rồi tự chốt lại 3 ("3 con tao nhầm" + "chốt theo ý mày"): + // clean-code phủ chồng cả 3 nên KHÔNG viết nổi câu scope-rời mà roster này bắt buộc ⇒ gộp trục đó vào từng con. ] // ─── PA-2a — hằng-số kỳ-vọng model tier-2 (H23 model-at-spawn, owner-chốt O1 @S121 W2) ── @@ -226,14 +234,15 @@ const results = await parallel(A.taskList.map((t, i) => () => { const subMd = wave ? `${wave.dir}/sub-${role || 'task'}-${i}.md` : null // Harness-10 FLAT (h10-refine 2026-06-18): sub--.md phẳng cùng cấp dưới runs// — KHÔNG sub-md/ subdir // Write-guard TOOL-AWARE theo MODE (B6 isolation). SE read-only sub (KHÔNG Write tool): investigator-codebase/api, - // reviewer, cicd-monitor (+ monitor tooling-auditor/harvest-curator) (+S153 bộ-ba Ctx: ctx-audit, ctx-curator, ctx-verifier — lớp mềm _mind, tools: KHÔNG Write/Edit). + // reviewer, cicd-monitor (+ monitor tooling-auditor/harvest-curator) (+S153 bộ-ba Ctx: ctx-audit, ctx-curator, ctx-verifier — lớp mềm _mind, tools: KHÔNG Write/Edit) + // (+S176 đội STYLE: front-end/back-end/database-reviewer-style — tools: KHÔNG Write/Edit, propose-only). // Write sub: implementer-backend/frontend, test-specialist, frontend-designer. const writeGuard = wave ? [ `## ✍️ RUN-TRACE ghi sub--.md FLAT (Harness-10 h10-refine, supersedes subfolder sub-md/) — TOOL-AWARE (chống mojibake G-009):`, `- Full-detail công-việc của mày → ĐÚNG 1 file: \`${subMd}\` (folder đã scaffold sẵn — KHÔNG tạo folder).`, ` • NẾU mày CÓ Write/Edit tool (implementer-backend/frontend, test-specialist, frontend-designer): GHI TRỰC TIẾP via Write/Edit. 🔴 KHÔNG Bash-write MD ($-expansion/mojibake).`, - ` • NẾU mày CHỈ có Bash (read-only sub: investigator-codebase/api, reviewer, cicd-monitor, tooling-auditor, harvest-curator, lead-stale-auditor, lead-gap-auditor, harness-eval, harness-refine, harness-audit, ctx-audit, ctx-curator, ctx-verifier — KHÔNG Write tool): 🔴 TUYỆT ĐỐI KHÔNG Bash-write MD → để full-detail trong "findings" + đặt subMdPath="${subMd}"; EM MAIN scribe @P3 (single-writer Write-tool, no-corruption).`, + ` • NẾU mày CHỈ có Bash (read-only sub: investigator-codebase/api, reviewer, cicd-monitor, tooling-auditor, harvest-curator, lead-stale-auditor, lead-gap-auditor, harness-eval, harness-refine, harness-audit, ctx-audit, ctx-curator, ctx-verifier, front-end-reviewer-style, back-end-reviewer-style, database-reviewer-style — KHÔNG Write tool): 🔴 TUYỆT ĐỐI KHÔNG Bash-write MD → để full-detail trong "findings" + đặt subMdPath="${subMd}"; EM MAIN scribe @P3 (single-writer Write-tool, no-corruption).`, `- 🔴 ISOLATION (B6→Harness-10, AUDIT): CHỈ ghi \`${subMd}\` (+ code-file-disjoint nếu task giao). TUYỆT ĐỐI KHÔNG ghi/sửa: agent-memory/* (MEMORY.md BẤT KỲ sub) · MD canonical (CLAUDE/README/STATUS/agents) · sub-MD agent khác. Em main git-status/diff audit sau P2 — tracked-file đổi NGOÀI run-folder (runs//) + code-disjoint = vi-phạm (run-folder TRACKED → HIỆN trong diff).`, `- LUÔN return: findings (FULL) + checklistEvidence + memoryDelta (4-field) + subMdPath="${subMd}". Harvest per-turn primary (C4); H2 gom @session-end = backstop verify-idempotent → agent-memory/${role || 'sub'}.`, `- 🔴 KHÔNG store_memory/RAG-write · KHÔNG Bash curl/HTTP Qdrant (:6333 = git-diff MÙ, chỉ chunk-count bắt) · KHÔNG ghi file NGOÀI repo/run-folder. RAG single-writer=em main; containment = git-diff(in-repo)+chunk-count(RAG) [G-015].`, diff --git a/docs/STATUS.md b/docs/STATUS.md index 71e50da..71f950a 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -473,7 +473,7 @@ monthly re-tier mega-line + ef-core SKILL Mig→59 + 3 vocab-fork (2026-07-01 du | Gotchas | **86** | **+S168 #86** (2-push-sát → Gitea auto-CANCEL run đầu cùng-branch: CANCELLED ≠ fail — verifier đọc run CUỐI của range + bằng-chứng artifact-level; side-effect @startup chỉ 1-startup cho cả cụm push; ca thật `aaed699` ship qua #442) · **+S162 #85** (menu-key OR-gate FE ≠ policy per-action BE: cây 4-folder GĐ gate `['Khkk_List','Khkk_G1','KeHoachKyKet'].some(can)` NHƯNG endpoint gắn `[Authorize(Policy="KeHoachKyKet.Read")]` [`ContractSigningPlansController.cs:36`] + `MenuPermissionHandler.cs:40` so khớp **CHÍNH XÁC 1 key** — KHÔNG kế-thừa cha, KHÔNG OR key con ⇒ role `Khkk_List=1 ∧ KeHoachKyKet=0`: FE mở cổng → API **403** → folder GĐ2 hiện "—" dưới **MỌI** gói thầu, claim "gate trước fetch ⇒ 0 rác 403" **vỡ**. Bẫy: luật "OR nhiều key" **ĐÚNG ở tầng HIỂN-THỊ** [`GetMyMenuTreeQuery.cs:61-96` trả node khi con `CanRead`] nhưng **SAI ở tầng API-authz** — mặt thứ 2 của #82, lần này **menu-hiện RỘNG hơn API-cho** [#82 là chiều ngược]. Fix = gate đúng key policy CỦA endpoint; **mở controller đọc attribute TRƯỚC khi wire gate**, đừng suy từ cây menu. Reviewer bắt **trước ship**; Dev 13/13 role đều `Contracts=1 ∧ KeHoachKyKet=1` nên **chưa lộ** — lớp "chưa vỡ vì dữ-liệu hiện tại may mắn", vá ngay không đợi triệu chứng). **+S159 #84** (revoker-2-chiều: sửa data tay trên prod bị code-path ungated @restart ĐẢO NGƯỢC im lặng — grant menu SQL tay bị revoker [S92] lật 447/494 row; fix = code-first seeder + gỡ nhánh + guard 2-vế + khuôn 3-chân-kiềng; liên-quan #75/#76 chiều seeder). **+S133 #81-EXT** (instance 2 indirect-assignment, KHÔNG số mới: snapshot-NS-at-DaDuyet hook đặt ở finalize choke-point NHƯNG 2 site gán Phase TRỰC TIẾP bypass — admin-override `PurchaseEvaluationWorkflowService.cs:308` `= targetPhase` + seeder `DbInitializer:1323` `= current` → phiếu vào DaDuyet qua 2 đường này KHÔNG snapshot [frozen=0, display rơi về live = đúng bug đang vá]; reviewer adversarial bắt pre-commit, em-main + 3 invocation implement đều sót — cùng bài enumeration-writer #81; meta: field-đích có hook/derive/snapshot ⇒ grep MỌI assignment-site kể cả override/seeder). +1 S126 (**#83** số AGGREGATE session-cumulative tự-lão-hoá — audit COUNTS đổi bởi CHÍNH spawn review sau đó [2/2/0→4/4/0]; outward mời-reproduce = bảng per-lane theo run-id, KHÔNG số tổng; reviewer-gate bắt, em-main-solo sót). +1 S118 (**#82** menu-flag grant ≠ quyền API thật: display-layer [Permission `CanRead`→`GetMyMenuTree` FE-menu] ⟂ API-authz-layer [controller `[Authorize(Policy=)]`] = 2 tầng ĐỘC-LẬP; grant `Suppliers` R+C+U cho PRO nhưng `PUT/DELETE /suppliers` gate `[Authorize(Roles="Admin,CatalogManager")]` → PRO vẫn 403 sửa/xóa NCC [chỉ Publish/Import theo `Suppliers.Update` là ăn]; ngược lại `ReportsController` `[Authorize]` trần → mọi authed-user đọc `dashboard` tài-chính-HĐ ngược S92; fix gate `[Authorize(Policy="Reports.Read")]` per-method + grep controller authz TRƯỚC khi tin hiệu-lực grant; reviewer adversarial bắt, em-main-solo tin menu-flag=quyền thì sót; liên-quan #44). +1 S114 (**#81** derived-invariant re-derive ở write/select NHƯNG SÓT DELETE writer [DeleteQuote/DeleteDetail cascade] → xóa quote-selected cuối của NCC → `Supplier.IsWinner` kẹt stale [Any(IsSelected)=false] → `CreateContract Where(IsWinner)` gom NCC phantom → **HĐ giaTri=0**; fix helper `PeWinnerInvariant.ReSyncAfterQuoteRemovalAsync` gọi sau SaveChanges 2 handler; **enumeration-writer-trong-spec KHÔNG đáng tin** → grep MỌI `.Remove(`/`.Add(`/`=field` chạm entity chứa field-nguồn; chỉ **verify-adversarial-refute** bắt [invest/review/implement đều sót]; cardinality S87/S88 lên tầng mutation-path). +1 S113 (**#80** null-forgiving `!` che NRE trên external-parsed data: `SupplierExcelImportService.NewSupplier` gán `Code = r.Code!.Trim()` → NullReferenceException khi parser trả `null` cho ô cột-4 rỗng [ĐÚNG path R4 "thiếu Mã NCC → nháp"] → confirm nổ 500 CẢ batch dù preview classify New-draft KHÔNG lỗi; sibling `IsPublic=!IsNullOrWhiteSpace(row.Code)` null-safe nhưng NewSupplier thì KHÔNG; empty-string `""` chạy OK nhưng FE round-trip blank→JSON `null` → nổ. Fix `(r.Code ?? "").Trim()`. **Build 0/0 KHÔNG bắt** [`!` tắt nullable-warning]; chỉ test-before-critical bắt. Lesson: `!` null-forgiving trên dữ-liệu-parse-ngoài = mìn, dùng `?? default`). +1 S97 (**#79** backfill soi NHẦM bảng — transition-marker "[Duyệt KẾT THÚC]" nằm ở `PurchaseEvaluationChangelogs.ContextNote` KHÔNG phải `PurchaseEvaluationApprovals` → Mig 60 backfill match 0, phiếu finalize-CCM giữ flag=0 hiện sai "Đã được CEO duyệt"; fix Mig 61 query đúng bảng. PE 2 bảng lịch-sử tách vai [Approvals=ai-duyệt record :745 · Changelogs=chuyển-phase+marker LogTransition :1073]; verify marker-count prod trước khi tin backfill). +1 S97 (**#78** display-flag suy CONFIG thay RUNTIME → nói dối khi instance lệch config: `endsBeforeCeo` từ "workflow CÓ cấp finalize" thay "phiếu NÀY finalize" → phiếu lên CEO báo sai "không qua CEO"; nặng sau S97 default-flip opt-in; fix field runtime `EndedByLevelFinalize` Mig 60. Flag = "instance ĐÃ làm-gì" KHÔNG "config CÓ THỂ"). +1 S96 (**#77** SPA deploy bundle cache-lag: team thấy UI CŨ dù đã deploy + Ctrl+F5 — index.html no-cache ĐÚNG-chuẩn + JS hash-immutable nhưng browser giữ tab-SPA-mở/cache-local → verify bundle prod **BYTE-LEVEL** (curl + grep string-fix trong `/assets/index-.js`) phân-biệt cache-vs-deploy; **incognito = golden-test**; no-service-worker nên clear-site-data đủ). +2 S91 (**#76** deploy render config từ `.example` KHÔNG phải file-gitignored → prod thiếu setting tới khi điền tay · **#75** seed ungated per-code RESURRECT data đã-wipe mỗi restart → gate sau env-flag `DemoSeed:Disabled` + verify-by-restart-thật). +1 S90 (**#74** Chrome-MCP screenshot kẹt SPA-SignalR `document_idle` → PowerShell `CopyFromScreen` + Win32 lọc-process-`chrome.exe`). +2 S85 (**#72** lệch giờ +7h — DateTime lưu UTC nhưng serialize thiếu 'Z' → FE `new Date()`=local → `UtcDateTimeJsonConverter` global ép UTC-with-Z · **#73** UpdateDraft absolute-set CLEAR field SÓT khỏi pattern null-safe = bug-class S42 → guard `if (request.X is not null)`). +1 S78 **#71** (thêm enum value vào entity DÙNG-CHUNG → pollute UI/guard phân-loại theo PROXY-predicate `supplierId===null` thay vì enum tường minh; file-khi-duyệt purpose=5 lẫn "Bảng so sánh" + false-pass submit-guard → grep mọi field-proxy predicate loại value mới + build-verify TỪNG app; em-main grep no-leak bắt trước deploy). +1 S76 **#70** (FE absolute-set echo field anh-em từ server-snapshot + `invalidate()` fire-and-forget → cửa-sổ stale-echo mất-dữ-liệu khi lưu 2 ô liên-tiếp; guard `useIsFetching` gate nút Lưu tới khi refetch land; reviewer workflow bắt, impl self-review sót). +1 S72 **#69** FE bundle-hash non-deterministic + `deploy.yml` rebuild-FE-unconditional → bundle rotate kể cả commit governance (SPA-fallback-200 trap; cicd Run #312). +2 S69: **#67** Tailwind v4 accent palette thiếu stop (teal/violet/amberx/greenx chỉ 50/100/500/600/700; dùng -300 → teal/violet rơi DEFAULT Tailwind sai-hệ, amberx/greenx drop hẳn — build PASS, phải soi dist CSS) · **#68** IDE TS diagnostic giữa background-agent/workflow = snapshot dở-dang (TS server bắn mỗi save), chỉ tin build SẠCH sau-cùng (2× session này: re-skin + PE-FE đều false-alarm). Prev +1 S68 **#66** Tailwind v4 rule element thô `h1-h4{color:#0b1220}` viết NGOÀI `@layer` thắng utility `text-white` → heading render đen; fix ĐIỂM `text-white!` (important, grep dist confirm), KHÔNG move `@layer` (load-bearing ~30+ heading toàn app). Prev +1 S65 **#65** build csproj con (vd `SolutionErp.Api.csproj`) ≠ `dotnet build SolutionErp.slnx` (gồm tests) → miss test-compile khi đổi chữ ký record command (CreateDepartmentCommand +ParentId) → CI CS7036 FAIL-gated Run #291 (deploy chặn, prod nguyên — test-gate làm đúng việc). Fix: build full slnx trước push BE signature-change. Prev +2 S61: **#63** EF scaffold tự sinh `RenameColumn` SAI-semantics khi drop+add cùng type (test xanh không bắt — SQLite EnsureCreated không replay migration) · **#64** `dotnet ef database update` áp Design-DB 0-rows ≠ Dev-DB → data-migrate `Sql()` chạy thật lần đầu trên prod. Prev +2 S59 (#61 sqlcmd `-f 65001` · #62 rename natural-key UPDATE trước deploy) | | User memory | **59** | 🔄 re-ground S170 — đếm đĩa 60 file `.claude/auto-memory/*.md` trừ 1 index `MEMORY.md` = 59 (số "31" stale từ S84; lead-stale FLAG @S169, ring2 verify, vá @S170 SAU khi ring chấm — đúng thứ-tự H24 §2(4) fire→chứng-kiến→vá). +S84 `feedback_derived_flush_execution` (derived→canonical flush = em-main execution KHÔNG gate user-confirm; monitor INFORM-only ≠ cần-anh-duyệt; chỉ ĐỔI-con-số/policy = quyền anh). +S79 `feedback_canonical_spec_over_broadcast` (adap pull canonical-command/§P đừng phỏng-theo broadcast + report-before-stamp dogfood + residual own-diary OK). +S75 `feedback_harness11_engine` (engine governance tự-bảo-trì + gotcha #30-via-Edit + multi-workflow-verify + git-add-specific lesson). re-ground S71 (H1 disk-count: 25 `feedback_*` + 3 `project_*`; STATUS "21" stale từ S64). +S71 `feedback_harness10_run_trace` (run-trace + 3 lesson). Prev +1 S64 `feedback_harness7_outward_writing_quality`; re-grounded S54 base 19 | | Skills | 6 | 3 domain + 3 ops | -| Sub-agents | **23** | **+S153 bộ-ba vòng Ctx** (adopt thư `0df10df4` — 🔄 **OWNER-VETO @S153 hub-form 3-vai**: bản đầu (38)(a) 1-vai OWNER-DELEGATED→LEAD-DECIDED, **anh bác cùng ngày** chốt trực tiếp *"Build đủ 3 vai như hub"* qua AskUser = owner chốt THẬT cho quyết này; 🔄 **RENAME cùng phiên theo lệnh anh** *"đặt tên giống hub và sisters để dễ audit"* — tên khai-sinh `ctx-pause-check`/`ctx-reload` → **hub-form `ctx-curator`/`ctx-verifier`** [đo đĩa 3 nhà: hub = curator/verifier/audit · VIPIX = check/reload/chain · hub≠sister ⇒ SE theo HUB-form, tiền lệ rename S149 lead-*]): **`ctx-curator`** vai-1 KIỂM nháp @mỗi-`/pause` (carve-out `pause.md §1` mirror hub O-1(a)) · **`ctx-verifier`** vai-2 ĐỐI-CHIẾU+NẠP-LẠI @mỗi-`/tiep` (dòng `LEAD-PHAI-NHAC-LAI:` = bằng-chứng nội-dung) · **`ctx-audit`** vai-3 SOI-CHUỖI @2-bookend (kiểm-vết vai-1/2, hồi-tố = fallback phiên 1-cửa). Nấc: ctx-audit `file-land + registry-surfaced @S153` cho CẢ 3 vai ctx [🔄 vá @S153 FLAG-6: bản đầu chỉ cấp nấc này cho `ctx-audit` trong khi `hmw.js:42` + writeGuard + README `:27`/`:199`/`:200` chứng 2 vai mới ĐẠT CÙNG nấc — khai THIẾU vẫn là lệch] — spawn-probe ALIVE cả 3 = phiên sau restart T9; hmw VALID_ROLES 20→**23** + writeGuard :228/:235; ⚠️ **roster 23 > `hmw_width.cap` 20** ⇒ full-roster-sweep LOUD-skip 3 lane cuối — số cap = quyền anh, O-7 (câu (40) đang treo). **🔄 RENAME @S149 (anh ratify "OK đồng ý"): 5 vai đổi tên đồng-bộ sister — `tooling-harvest-audit`→`ring1-audit` · `h24-audit`→`ring2-audit` · `sleep-audit`→`ring4-audit` · `lead-view-auditor`→`lead-stale-auditor` · `lead-omission-auditor`→`lead-gap-auditor` (ring* = 2 sister TRÙNG tuyệt đối; lead-* = hub-form, VIPIX-form là prefix). Nấc **NÂNG @cùng-phiên: `registry-listed-in-session`** — harness list 5 tên MỚI + gỡ 5 tên CŨ NGAY TRONG PHIÊN S149 (không restart), **TRÁI phép đo S120/S121 no-hot-reload** (môi trường có thể đã đổi — đây là data-point ĐO ĐƯỢC, không suy). Spawn-probe vẫn CẦN để chứng instantiate (list ≠ spawn được — đúng 0.6b). Tên cũ trong narrative/runs/history = vết lịch-sử, CỐ Ý giữ.** **+S145 🔬 `ring1-audit` (18th sub — round-KIỂM vòng tooling/harvest, C4/C4b adap dot-11 khoản ① `harness-dong-du-vong:51`, owner-decision [2] TÁCH mỗi-vòng-1-con-KHÔNG-gộp). Vai-KIỂM ĐỘC-LẬP đọc OUTPUT H1 `tooling-auditor` (4-mặt freshness) + H2 `harvest-curator` (5-trục integrity), **KHÔNG re-chạy đo**: verdict per-claim ĐẠT/TRƯỢT + falsify ≥2 + **tự tái-dựng ≥1 số CÓ-THỂ-SAI** (đếm `.claude/agents/*.md` vs roster-count H1 · N-harvested/M-spawned) + **thách-CLEAN** (H1 PASS/H2 GATE-0 → mở 1 skill "fresh"+1 sub "harvested" xem thật). 🔴 no-self-exempt ≠ H1/H2 (m-3 S139). **AUTO fire-after-measure @session-end §L.b(g-bis)** — owner-chốt S145 KHÔNG consent-gate (KHÁC trio/H24), fail-safe NO-OP nếu H1/H2 không chạy phiên đó (CẤM ĐẠT-ảo); tuần-tự C4b (invocation SAU con-đo, mtime chứng). Propose-only, backstop = git-diff commit-gate (KHÔNG dựa thiếu-tool `[carry:tools-whitelist-no-teeth]`). `model: opus`+`effort: max`+`memory: project`+`maxTurns: 16`. **Vai #1/3 round-KIỂM** — `ring2-audit` (vòng soi-lead) + `ring4-audit` (vòng nén-ngủ) land dần incremental (§6 broadcast báo-nấc-per-vòng, owner S145). 🔴 **Nấc = executed-file / verified-pending-restart** — registry Agent-tool KHÔNG hot-reload (`agents/README` §Memory discipline — anchor không-số-dòng, vá cite-dòng-trống @S151 per H1 F-3), spawn-probe alive cần **restart CLI**; run-thử-thật @session-end phiên sau. Design-doc: `runs/2026-07-22-S144-adap-dot-11/C4-design-3-vai-kiem.md`.** **+S145 🔬 `ring2-audit` (19th sub — round-KIỂM vòng soi-lead H24, vai #2/3 C4/C4b). Đọc OUTPUT `lead-stale-auditor` + `lead-gap-auditor` (🔴 KIỂM 2 con-đo H24, **KHÔNG soi lead trực-tiếp**) → verdict per-FLAG + tự tái-dựng ≥1 số (flag-count · mở 1 FLAG đối-chứng source · `class_repeat` `.session-counter.json`) + **thách-CLEAN** (H24 báo ít/0 FLAG ⇒ tự soi 1 surface tìm FLAG sót — CLEAN "lead ổn" là ca nguy nhất, khớp H24 §2(1) phép-thử-nhanh). Verify vai H24 KHÔNG tự chế class ngoài enum ĐÓNG `flag_classes`. no-self-exempt ≠ lead-stale/lead-gap (m-3; tên mới @rename S149). AUTO wired `session-end §L.b(j)(ii-bis)` sau cặp H24; fail-safe NO-OP nếu H24 không chạy (nhịp/consent). `maxTurns: 16`.** **+S145 🔬 `ring4-audit` (20th sub — round-KIỂM vòng nén-ngủ, vai #3/3 C4/C4b). Đọc OUTPUT `/sleep-recovery-memory-l2` (`.gist.md` mới + Fidelity verdict + claim giảm-byte) → verdict per-claim + **tự ĐO LẠI byte** archive gốc vs gist (đối-chứng claim) + **thách-CLEAN** (Fidelity 5P/0F ⇒ mở 1 gist đối-chứng `.md` gốc xem có giữ 4-field load-bearing hay nén quá tay) + kiểm bất-biến gist-ADDITIVE (gốc byte-identical). no-self-exempt độc-lập lượt nén (m-3). AUTO wired `sleep-recovery-memory-l2.md Phase 4.5` sau nén; fail-safe NO-OP nếu 0 gist tươi. `maxTurns: 16`.** **+S141/S142 🧪🧭🧮 `harness-eval` + `harness-refine` + `harness-audit` (15th–17th sub — bộ-ba đồng-kiểm HỆ MEMORY, adopt AI_INFRA khuôn 5-câu `58e28bae` khối B, wave adap đợt-9/10; land S141 persona+seed+`hmw.js` VALID_ROLES 14→17, wiring nghi-thức S142 vòng-2 sau khi vòng-1 chết giữa chừng). Read-only INFORM-only propose-only, frontmatter **không khai Write/Edit** — 🔴 nhưng đo S143: **runtime VẪN cấp** (append cuối tool-list, 9/9 vai read-only; nhân-chứng `reviewer` đã Write+Edit thật) ⇒ đây là **tuyên-bố ý-định**, KHÔNG phải chặn-bằng-thiếu-tool; containment thật = single-writer + git-diff commit-gate · `model: opus`+`effort: max`+`memory: project`+`maxTurns: 18`. Trục = **CHÍNH HỆ MEMORY**, chạy **TUẦN TỰ** eval→refine→audit (🔴 CẤM `parallel()` — con KIỂM chạy song song sẽ chấm bản CŨ ⇒ verdict vô-nghĩa): `harness-eval` = con **ĐO** (chỉ trả số nguyên-văn từ instrument, CẤM số-từ-trí-nhớ) ⟂ `harness-refine` = con **ĐỀ-XUẤT** (4 lớp reinject/promote/archive/distill, bắt buộc có mục "đã cân-nhắc→BÁC") ⟂ `harness-audit` = con **KIỂM** (verdict ĐẠT/TRƯỢT per-claim + re-chạy ≥2 số load-bearing; PIN run-folder đích-danh, CẤM tự tìm "run mới nhất"). 🔴 **KHÔNG gộp** với H1 tooling-freshness / H2 harvest-integrity / H24 lead-surface / reviewer diff-code — 4 trục khác nhau. Nghi-thức 2-đầu-phiên **AUTO 1 vòng/phiên GIỮA 2 bookend** (`session-start §2.1.9(a)` canonical + `session-end §L.b(k)(i)` trỏ): owner chốt @S151 — em-main IN 1 dòng trạng-thái rồi CHẠY, KHÔNG chờ gật [🔄 vá @S153 FLAG-4 HIGH: ô này còn khai "consent-gated + anh gật mới chạy" sau khi owner (32) đã lật AUTO, và ngược chính `:466` cùng file; trio đã chạy THẬT 3 vòng S151/S152/S153]; skip → ghi vết WAL `trio: skipped S` (vết-2-lớp, chuỗi skip nhìn thấy được). Budget seed đo TƯƠI `measure-agent-memory.ps1`: eval 1547B · refine 1616B · audit 1513B (l2_*=0 = SEED-STATE đúng, chưa chạy lần nào). 🔸 **Nấc trung-thực = executed-file / verified-pending-restart** — 3 vai CHƯA spawn-probe lần nào (cần restart CLI + anh consent; KHÔNG được đọc thành verified-runtime).** **+S121 👁 `lead-stale-auditor` + `lead-gap-auditor` (13th+14th sub — H24 lead-self-audit, adopt broadcast `2026-07-14-Governance-harness-24`, anh chốt S119 `owner-decisions-15-07-2026`, land S121 wave-W2 `hmw.js` VALID_ROLES 12→14 CÙNG COMMIT roster+STATUS [R3-M2: lệch landing ⇒ C2/B3 flag mọi derived ghi "14" là stale-vs-12 ⇒ detector BÃO trên chính thay đổi của H24]). 2 monitor INFORM-only propose-only, cookie-cutter frontmatter monitor sẵn (`model: inherit` lúc-land [S124 re-pin → `opus`+`effort:max` cùng toàn roster] · `memory: project` · `maxTurns: 18` · 8 tool read-only, **NO Write/Edit**). Trục: `lead-stale-auditor` = **view LỆCH source** (5 class `view-*`) ⟂ `lead-gap-auditor` = **cái BỊ THIẾU** (6 class `gap-*` — +`gap-incident-unrecorded` @S143 owner-duyệt) — 🔴 **KHÔNG gộp** vào tooling-auditor(H1 tooling-freshness)/harvest-curator(H2 harvest-integrity): H24 §2(1) *"gộp là mất tiêu-điểm; cái ta cần là cặp mắt CHỈ dành cho lead"*. Class-flag = **enum ĐÓNG** `lead_self_audit.flag_classes` ở `.claude/agent-memory/memory-budget.json` (11 class = 5+6, vai CẤM tự chế — mở enum là quyền owner). Nhịp = `h24_cadence` cùng file (single-source H24 §2(3) — doc TRỎ key, KHÔNG chép số ⇒ 🔄 **đã GỠ digits `{6/15/3}` @S153** theo `ring2-audit` R-3: chép số ngay cạnh câu cấm-chép-số là **B1-copy** [số vẫn KHỚP canonical nên KHÔNG phải stale — F-6 chống vu-oan]; đọc số sống ở `memory-budget.json → h24_cadence`); 🔄 **HÌNH B @S149**: cặp soi-lead spawn **VÔ-ĐIỀU-KIỆN 2 bookend mỗi phiên** — `light_every` hạ thành **stats-only** (KHÔNG còn cổng bắn), `deep_every` GIỮ cổng nhịp [vá @S153 FLAG-5: ô này còn mô-tả bắn thuần-cadence; đối-chứng hành-vi = cặp chạy @counter 27 với light 1/6 CHƯA tới hạn]; counter `.claude/governance/.session-counter.json` (content-based `last_ticked_head`, KHÔNG blind-increment — hook `wal-flush.ps1` auto-commit `agent-memory/` mỗi turn sẽ làm hỏng đếm mù; đặt ngoài **4 path** hook CÓ CHỦ-ĐÍCH — *hook nới từ 3 lên 4 @S146 khi thêm `.claude/sessions/`; mệnh-đề "governance/ nằm ngoài" đã RE-VERIFY sau khi nới, vẫn đúng*). ✅ **verified-runtime S122** (spawn-probe toàn-roster-KHI-ĐÓ SỐNG post-restart — **N=14 tại thời-điểm S122**, chứng runtime, §Recently-Done S122; 🔸 +3 trio S141 CHƯA nằm trong phép đo này) **+ chạy THẬT S124** (dogfood 2-vai đầu-tiên, owner-triggered @counter 3) **+ S137** (light-audit consent OVERDUE 8/6 → 2 FLAG view-stale-status + 1 FLAG gap-decision-sunk). 🔸 Residual THẬT giữ: đường `hmw.js→agentType` cho 2 vai H24 chưa exercise (Agent-tool path đã chứng; workflow-path chưa) [S137 view-audit lật ⚠️→✅ khớp source, giữ residual]. 🔸 Từ-vựng "view"/"omission" chọn có chủ-đích tránh va-chạm: `governance-detectors.ps1:15` đã sở-hữu `derived-staleness`, và "GAP" quá-tải (`session-end` build-gap · `memory-selfimprove-audit.ps1` CRITERIA+GAP).** **+S107 📄 `office-document` (12th sub — Office document WRITE specialist: docx·xlsx·pptx·pdf + form-engine; floor OD1–OD10; VALID_ROLES hmw.js 9→10; anh giao trực tiếp S107 "Sub chính" Office; ✅ **verified-runtime S107 CÙNG PHIÊN** — registry hot-reload agent MỚI giữa phiên [S27 lesson thu hẹp còn EDIT-agent/command] + spawn-test PASS OD1/OD4/OD5 template thật, model self-report `claude-fable-5` [data-point #5 flip-8]; finding: 2 template `wwwroot/templates` 0-placeholder mẫu-blank).** **worker-tier-pin (owner-decision S124 2026-07-15 — thay all-inherit H8 S66, trước nữa two-tier H4; sub TOÀN-ROSTER pin cứng KHÔNG theo lead; H23-precedence ĐO S126: spawn-param THẮNG hard-pin ⇒ `/fable-real` SỐNG). Lead = **DEFAULT Opus 5 MAX** (thư `9c909007` @S149 (re-stamp 07-25 G-024a; pre `59ce1d0f`); **anh chốt ô canonical hiển-thị DEFAULT-fleet @S151** — *"cần gọi Fable tao chọn đầu mỗi phiên hoặc gọi bằng command fable-real"* ⇒ Fable = anh `/model` đầu phiên HOẶC per-invocation `/fable-real`; runtime thật = BƯỚC 0.6 probe mỗi phiên, KHÔNG ghi sổ mỗi flip — hết class stale-2-flip ring1 bắt @S150). 🧊 Lineage owner-choice/flip-chain FROZEN tới flip-8 (flip-9 S119 Opus-4.8 owner-choice + các lượt /model sau = per-session không enumerate, anti-churn S106):** em main Fable 5 (1M) Max (✅ **Fable RESTORED S98 2026-07-02** — lead self-report `claude-fable-5` + settings `claude-fable-5[1m]`; fallback-Opus H5 S62→S97 kết thúc, H5.6 hoàn tất) ⚠️ **S101 2026-07-03: runtime lead = Opus 4.8** (lead + 2 monitor + WF2 reviewer inherit đều self-report `claude-opus-4-8`; settings vẫn pin `claude-fable-5[1m]` → **Fable unavailable phiên này → H5-fallback tự-kích, revert-free, KHÔNG sửa frontmatter**; "Fable RESTORED S98" = point-in-time KHÔNG phải steady-state — inherit-chain resolve theo availability runtime; ✅ **S102 2026-07-06: Fable VỀ LẠI runtime** — lead + 2 monitor inherit self-report `claude-fable-5` → H5-fallback S101 kết thúc, pattern availability per-session xác nhận [S98 Fable → S101 Opus → S102 Fable → **S103 Opus** → **S104 Opus** (2026-07-06 Fable outage tiếp — S104 lead + 2 monitor spawn self-report `claude-opus-4-8` ×3, H5-fallback revert-free) → ✅ **S105 Fable** (2026-07-07 Fable VỀ lần 3 — lead + 2 monitor self-report `claude-fable-5` ×3, runtime-verified) → ⚠️ **S106 Opus** (2026-07-07 Fable outage lại — lead runtime self-report `claude-opus-4-8`, H5-fallback revert-free; flip-7) → ✅ **S107 Fable** (2026-07-10 Fable VỀ lần 4 — lead + 2 monitor self-report `claude-fable-5` ×3, runtime-verified; flip-8)], BƯỚC 0.6 probe mỗi phiên; per-session KHÔNG steady-state. 🔴 **[§Sub-agents = CANONICAL single-owner flip-chain — S106 gộp per anh-confirm; derived `agents/README §Architecture` · `session-start BƯỚC 0.6` · `session-end §commit-format` trỏ về đây [🔄 vá @S153 FLAG-1: 3 cite số-dòng cũ `:3`/`:37`/`:113` đo lại SAI cả 3 (đúng là `:5`/`:48`/`:315`) — vá bằng BỎ số theo luật đã áp @S151, không ĐỔI số], KHÔNG tự enumerate mỗi flip → chống churn 4-file/flip]**) · **TOÀN roster `model: opus` + `effort: max`** (worker-tier-pin S124; **A2 đo S126:** lane no-tier resolve `claude-opus-4-8` DÙ lead=Fable ⇒ pin ăn runtime, hết inherit-lead; số vai = cột giữa của chính row này — KHÔNG chép lại vào narrative, đó là cách row-14 và narrative-12 lệch nhau từ S121 tới S122; lineage: 7 demoted flip S66 → all-inherit S66–S123 [office-document S107 inherit-born] → pin S124) · effort Max. 10 product/quality + monitor INFORM-only (H1 · H2 · 2×H24 S121 · 3×trio-memory S141 · 3×round-KIỂM `ring1-audit`/`ring2-audit`/`ring4-audit` S145) — **liệt-kê NHÓM, số vai = cột giữa của chính row này**. Nấc H8 = **runtime-verified S98 (2026-07-02)** — spawn-test inherit-chain ×2 monitor self-report `claude-fable-5` (cả đội tự lên Fable khi lead restore, 0 sửa frontmatter — đúng thiết kế all-inherit). Prev two-tier H4 runtime-verified S57bis (spawn-test 2 chiều). | +| Sub-agents | **26** | **+S176 🎨 ĐỘI STYLE 3 vai** (`front-end-reviewer-style` · `back-end-reviewer-style` · `database-reviewer-style` — **anh chốt TRỰC TIẾP**, không qua adap). 🔴 **Luật lõi anh giao:** *"quy tắc thì các tính năng đã deploy thành production rồi thì sẽ là chuẩn, trừ khi tao có điều chỉnh lại lần nữa"* — lý-do sinh đội là câu *"mày cứ làm đi làm lại hoài"*. **Ca THẬT làm anh nêu:** S176 vòng-4b lead dựng thùng rác KHKK **thiếu banner + dấu `🗑 xóa ≈`** trong khi màn thùng rác Duyệt NCC **đã chạy prod** có đủ cả hai ⇒ anh phải tự chụp màn gửi sang bảo *"ảnh chỉ là mẫu"*. Trục = **ĐỒNG-NHẤT** (có giống bản đã chạy không) ⟂ `reviewer` = **ĐÚNG-SAI** (có bug không) ⇒ chạy SONG SONG, không giẫm. Địa-phận RỜI theo glob diff: `fe-*/src/**` · `src/Backend/**` trừ `Persistence/**` · `Persistence/**`; **gọi theo diff chạm đâu**, diff không chạm ⇒ KHÔNG gọi (không phải bỏ sót). 🧊 **Anh nêu ban đầu 4 con** (có `clean-code-reviewer-style`) **rồi tự chốt lại 3** (*"3 con tao nhầm"* + *"chốt theo ý mày"*): con clean-code phủ chồng cả 3 con kia nên **không viết nổi câu scope-rời** mà roster này bắt buộc ⇒ trục clean-code (đặt tên · trùng lặp · mật-độ comment · pattern drift) **gộp vào từng con trong địa phận của nó**. 🔸 **Máy trước, vai sau** (đo S176): FE có ESLint chạy trước (`npm run lint`) nhưng **CI KHÔNG gác** (`grep 'npm run lint'` trong workflows = **0 hit**) và đang tồn **89 vấn-đề / 78 lỗi** (fe-admin 42 · fe-user 47); **BE 0 máy style nào** (0 `.editorconfig` · 0 analyzer · 0 `dotnet format`) ⇒ con BE gánh cả lớp cơ-học, khác con FE. Cắm được máy vào CI thì **PHẢI cập-nhật lại persona con BE** trả lớp đó về máy. ⚠️ **roster 26 > `hmw_width.cap` 20** ⇒ full-roster-sweep LOUD-skip nhiều lane hơn trước — số cap = quyền anh (O-7 vẫn treo, nay lệch rộng hơn). 🔸 **Nấc = executed-file / verified-pending-restart** — 3 vai CHƯA spawn-probe; `hmw.js` VALID_ROLES 23→**26**. **+S153 bộ-ba vòng Ctx** (adopt thư `0df10df4` — 🔄 **OWNER-VETO @S153 hub-form 3-vai**: bản đầu (38)(a) 1-vai OWNER-DELEGATED→LEAD-DECIDED, **anh bác cùng ngày** chốt trực tiếp *"Build đủ 3 vai như hub"* qua AskUser = owner chốt THẬT cho quyết này; 🔄 **RENAME cùng phiên theo lệnh anh** *"đặt tên giống hub và sisters để dễ audit"* — tên khai-sinh `ctx-pause-check`/`ctx-reload` → **hub-form `ctx-curator`/`ctx-verifier`** [đo đĩa 3 nhà: hub = curator/verifier/audit · VIPIX = check/reload/chain · hub≠sister ⇒ SE theo HUB-form, tiền lệ rename S149 lead-*]): **`ctx-curator`** vai-1 KIỂM nháp @mỗi-`/pause` (carve-out `pause.md §1` mirror hub O-1(a)) · **`ctx-verifier`** vai-2 ĐỐI-CHIẾU+NẠP-LẠI @mỗi-`/tiep` (dòng `LEAD-PHAI-NHAC-LAI:` = bằng-chứng nội-dung) · **`ctx-audit`** vai-3 SOI-CHUỖI @2-bookend (kiểm-vết vai-1/2, hồi-tố = fallback phiên 1-cửa). Nấc: ctx-audit `file-land + registry-surfaced @S153` cho CẢ 3 vai ctx [🔄 vá @S153 FLAG-6: bản đầu chỉ cấp nấc này cho `ctx-audit` trong khi `hmw.js:42` + writeGuard + README `:27`/`:199`/`:200` chứng 2 vai mới ĐẠT CÙNG nấc — khai THIẾU vẫn là lệch] — spawn-probe ALIVE cả 3 = phiên sau restart T9; hmw VALID_ROLES 20→**23** + writeGuard :228/:235; ⚠️ **roster 23 > `hmw_width.cap` 20** ⇒ full-roster-sweep LOUD-skip 3 lane cuối — số cap = quyền anh, O-7 (câu (40) đang treo). **🔄 RENAME @S149 (anh ratify "OK đồng ý"): 5 vai đổi tên đồng-bộ sister — `tooling-harvest-audit`→`ring1-audit` · `h24-audit`→`ring2-audit` · `sleep-audit`→`ring4-audit` · `lead-view-auditor`→`lead-stale-auditor` · `lead-omission-auditor`→`lead-gap-auditor` (ring* = 2 sister TRÙNG tuyệt đối; lead-* = hub-form, VIPIX-form là prefix). Nấc **NÂNG @cùng-phiên: `registry-listed-in-session`** — harness list 5 tên MỚI + gỡ 5 tên CŨ NGAY TRONG PHIÊN S149 (không restart), **TRÁI phép đo S120/S121 no-hot-reload** (môi trường có thể đã đổi — đây là data-point ĐO ĐƯỢC, không suy). Spawn-probe vẫn CẦN để chứng instantiate (list ≠ spawn được — đúng 0.6b). Tên cũ trong narrative/runs/history = vết lịch-sử, CỐ Ý giữ.** **+S145 🔬 `ring1-audit` (18th sub — round-KIỂM vòng tooling/harvest, C4/C4b adap dot-11 khoản ① `harness-dong-du-vong:51`, owner-decision [2] TÁCH mỗi-vòng-1-con-KHÔNG-gộp). Vai-KIỂM ĐỘC-LẬP đọc OUTPUT H1 `tooling-auditor` (4-mặt freshness) + H2 `harvest-curator` (5-trục integrity), **KHÔNG re-chạy đo**: verdict per-claim ĐẠT/TRƯỢT + falsify ≥2 + **tự tái-dựng ≥1 số CÓ-THỂ-SAI** (đếm `.claude/agents/*.md` vs roster-count H1 · N-harvested/M-spawned) + **thách-CLEAN** (H1 PASS/H2 GATE-0 → mở 1 skill "fresh"+1 sub "harvested" xem thật). 🔴 no-self-exempt ≠ H1/H2 (m-3 S139). **AUTO fire-after-measure @session-end §L.b(g-bis)** — owner-chốt S145 KHÔNG consent-gate (KHÁC trio/H24), fail-safe NO-OP nếu H1/H2 không chạy phiên đó (CẤM ĐẠT-ảo); tuần-tự C4b (invocation SAU con-đo, mtime chứng). Propose-only, backstop = git-diff commit-gate (KHÔNG dựa thiếu-tool `[carry:tools-whitelist-no-teeth]`). `model: opus`+`effort: max`+`memory: project`+`maxTurns: 16`. **Vai #1/3 round-KIỂM** — `ring2-audit` (vòng soi-lead) + `ring4-audit` (vòng nén-ngủ) land dần incremental (§6 broadcast báo-nấc-per-vòng, owner S145). 🔴 **Nấc = executed-file / verified-pending-restart** — registry Agent-tool KHÔNG hot-reload (`agents/README` §Memory discipline — anchor không-số-dòng, vá cite-dòng-trống @S151 per H1 F-3), spawn-probe alive cần **restart CLI**; run-thử-thật @session-end phiên sau. Design-doc: `runs/2026-07-22-S144-adap-dot-11/C4-design-3-vai-kiem.md`.** **+S145 🔬 `ring2-audit` (19th sub — round-KIỂM vòng soi-lead H24, vai #2/3 C4/C4b). Đọc OUTPUT `lead-stale-auditor` + `lead-gap-auditor` (🔴 KIỂM 2 con-đo H24, **KHÔNG soi lead trực-tiếp**) → verdict per-FLAG + tự tái-dựng ≥1 số (flag-count · mở 1 FLAG đối-chứng source · `class_repeat` `.session-counter.json`) + **thách-CLEAN** (H24 báo ít/0 FLAG ⇒ tự soi 1 surface tìm FLAG sót — CLEAN "lead ổn" là ca nguy nhất, khớp H24 §2(1) phép-thử-nhanh). Verify vai H24 KHÔNG tự chế class ngoài enum ĐÓNG `flag_classes`. no-self-exempt ≠ lead-stale/lead-gap (m-3; tên mới @rename S149). AUTO wired `session-end §L.b(j)(ii-bis)` sau cặp H24; fail-safe NO-OP nếu H24 không chạy (nhịp/consent). `maxTurns: 16`.** **+S145 🔬 `ring4-audit` (20th sub — round-KIỂM vòng nén-ngủ, vai #3/3 C4/C4b). Đọc OUTPUT `/sleep-recovery-memory-l2` (`.gist.md` mới + Fidelity verdict + claim giảm-byte) → verdict per-claim + **tự ĐO LẠI byte** archive gốc vs gist (đối-chứng claim) + **thách-CLEAN** (Fidelity 5P/0F ⇒ mở 1 gist đối-chứng `.md` gốc xem có giữ 4-field load-bearing hay nén quá tay) + kiểm bất-biến gist-ADDITIVE (gốc byte-identical). no-self-exempt độc-lập lượt nén (m-3). AUTO wired `sleep-recovery-memory-l2.md Phase 4.5` sau nén; fail-safe NO-OP nếu 0 gist tươi. `maxTurns: 16`.** **+S141/S142 🧪🧭🧮 `harness-eval` + `harness-refine` + `harness-audit` (15th–17th sub — bộ-ba đồng-kiểm HỆ MEMORY, adopt AI_INFRA khuôn 5-câu `58e28bae` khối B, wave adap đợt-9/10; land S141 persona+seed+`hmw.js` VALID_ROLES 14→17, wiring nghi-thức S142 vòng-2 sau khi vòng-1 chết giữa chừng). Read-only INFORM-only propose-only, frontmatter **không khai Write/Edit** — 🔴 nhưng đo S143: **runtime VẪN cấp** (append cuối tool-list, 9/9 vai read-only; nhân-chứng `reviewer` đã Write+Edit thật) ⇒ đây là **tuyên-bố ý-định**, KHÔNG phải chặn-bằng-thiếu-tool; containment thật = single-writer + git-diff commit-gate · `model: opus`+`effort: max`+`memory: project`+`maxTurns: 18`. Trục = **CHÍNH HỆ MEMORY**, chạy **TUẦN TỰ** eval→refine→audit (🔴 CẤM `parallel()` — con KIỂM chạy song song sẽ chấm bản CŨ ⇒ verdict vô-nghĩa): `harness-eval` = con **ĐO** (chỉ trả số nguyên-văn từ instrument, CẤM số-từ-trí-nhớ) ⟂ `harness-refine` = con **ĐỀ-XUẤT** (4 lớp reinject/promote/archive/distill, bắt buộc có mục "đã cân-nhắc→BÁC") ⟂ `harness-audit` = con **KIỂM** (verdict ĐẠT/TRƯỢT per-claim + re-chạy ≥2 số load-bearing; PIN run-folder đích-danh, CẤM tự tìm "run mới nhất"). 🔴 **KHÔNG gộp** với H1 tooling-freshness / H2 harvest-integrity / H24 lead-surface / reviewer diff-code — 4 trục khác nhau. Nghi-thức 2-đầu-phiên **AUTO 1 vòng/phiên GIỮA 2 bookend** (`session-start §2.1.9(a)` canonical + `session-end §L.b(k)(i)` trỏ): owner chốt @S151 — em-main IN 1 dòng trạng-thái rồi CHẠY, KHÔNG chờ gật [🔄 vá @S153 FLAG-4 HIGH: ô này còn khai "consent-gated + anh gật mới chạy" sau khi owner (32) đã lật AUTO, và ngược chính `:466` cùng file; trio đã chạy THẬT 3 vòng S151/S152/S153]; skip → ghi vết WAL `trio: skipped S` (vết-2-lớp, chuỗi skip nhìn thấy được). Budget seed đo TƯƠI `measure-agent-memory.ps1`: eval 1547B · refine 1616B · audit 1513B (l2_*=0 = SEED-STATE đúng, chưa chạy lần nào). 🔸 **Nấc trung-thực = executed-file / verified-pending-restart** — 3 vai CHƯA spawn-probe lần nào (cần restart CLI + anh consent; KHÔNG được đọc thành verified-runtime).** **+S121 👁 `lead-stale-auditor` + `lead-gap-auditor` (13th+14th sub — H24 lead-self-audit, adopt broadcast `2026-07-14-Governance-harness-24`, anh chốt S119 `owner-decisions-15-07-2026`, land S121 wave-W2 `hmw.js` VALID_ROLES 12→14 CÙNG COMMIT roster+STATUS [R3-M2: lệch landing ⇒ C2/B3 flag mọi derived ghi "14" là stale-vs-12 ⇒ detector BÃO trên chính thay đổi của H24]). 2 monitor INFORM-only propose-only, cookie-cutter frontmatter monitor sẵn (`model: inherit` lúc-land [S124 re-pin → `opus`+`effort:max` cùng toàn roster] · `memory: project` · `maxTurns: 18` · 8 tool read-only, **NO Write/Edit**). Trục: `lead-stale-auditor` = **view LỆCH source** (5 class `view-*`) ⟂ `lead-gap-auditor` = **cái BỊ THIẾU** (6 class `gap-*` — +`gap-incident-unrecorded` @S143 owner-duyệt) — 🔴 **KHÔNG gộp** vào tooling-auditor(H1 tooling-freshness)/harvest-curator(H2 harvest-integrity): H24 §2(1) *"gộp là mất tiêu-điểm; cái ta cần là cặp mắt CHỈ dành cho lead"*. Class-flag = **enum ĐÓNG** `lead_self_audit.flag_classes` ở `.claude/agent-memory/memory-budget.json` (11 class = 5+6, vai CẤM tự chế — mở enum là quyền owner). Nhịp = `h24_cadence` cùng file (single-source H24 §2(3) — doc TRỎ key, KHÔNG chép số ⇒ 🔄 **đã GỠ digits `{6/15/3}` @S153** theo `ring2-audit` R-3: chép số ngay cạnh câu cấm-chép-số là **B1-copy** [số vẫn KHỚP canonical nên KHÔNG phải stale — F-6 chống vu-oan]; đọc số sống ở `memory-budget.json → h24_cadence`); 🔄 **HÌNH B @S149**: cặp soi-lead spawn **VÔ-ĐIỀU-KIỆN 2 bookend mỗi phiên** — `light_every` hạ thành **stats-only** (KHÔNG còn cổng bắn), `deep_every` GIỮ cổng nhịp [vá @S153 FLAG-5: ô này còn mô-tả bắn thuần-cadence; đối-chứng hành-vi = cặp chạy @counter 27 với light 1/6 CHƯA tới hạn]; counter `.claude/governance/.session-counter.json` (content-based `last_ticked_head`, KHÔNG blind-increment — hook `wal-flush.ps1` auto-commit `agent-memory/` mỗi turn sẽ làm hỏng đếm mù; đặt ngoài **4 path** hook CÓ CHỦ-ĐÍCH — *hook nới từ 3 lên 4 @S146 khi thêm `.claude/sessions/`; mệnh-đề "governance/ nằm ngoài" đã RE-VERIFY sau khi nới, vẫn đúng*). ✅ **verified-runtime S122** (spawn-probe toàn-roster-KHI-ĐÓ SỐNG post-restart — **N=14 tại thời-điểm S122**, chứng runtime, §Recently-Done S122; 🔸 +3 trio S141 CHƯA nằm trong phép đo này) **+ chạy THẬT S124** (dogfood 2-vai đầu-tiên, owner-triggered @counter 3) **+ S137** (light-audit consent OVERDUE 8/6 → 2 FLAG view-stale-status + 1 FLAG gap-decision-sunk). 🔸 Residual THẬT giữ: đường `hmw.js→agentType` cho 2 vai H24 chưa exercise (Agent-tool path đã chứng; workflow-path chưa) [S137 view-audit lật ⚠️→✅ khớp source, giữ residual]. 🔸 Từ-vựng "view"/"omission" chọn có chủ-đích tránh va-chạm: `governance-detectors.ps1:15` đã sở-hữu `derived-staleness`, và "GAP" quá-tải (`session-end` build-gap · `memory-selfimprove-audit.ps1` CRITERIA+GAP).** **+S107 📄 `office-document` (12th sub — Office document WRITE specialist: docx·xlsx·pptx·pdf + form-engine; floor OD1–OD10; VALID_ROLES hmw.js 9→10; anh giao trực tiếp S107 "Sub chính" Office; ✅ **verified-runtime S107 CÙNG PHIÊN** — registry hot-reload agent MỚI giữa phiên [S27 lesson thu hẹp còn EDIT-agent/command] + spawn-test PASS OD1/OD4/OD5 template thật, model self-report `claude-fable-5` [data-point #5 flip-8]; finding: 2 template `wwwroot/templates` 0-placeholder mẫu-blank).** **worker-tier-pin (owner-decision S124 2026-07-15 — thay all-inherit H8 S66, trước nữa two-tier H4; sub TOÀN-ROSTER pin cứng KHÔNG theo lead; H23-precedence ĐO S126: spawn-param THẮNG hard-pin ⇒ `/fable-real` SỐNG). Lead = **DEFAULT Opus 5 MAX** (thư `9c909007` @S149 (re-stamp 07-25 G-024a; pre `59ce1d0f`); **anh chốt ô canonical hiển-thị DEFAULT-fleet @S151** — *"cần gọi Fable tao chọn đầu mỗi phiên hoặc gọi bằng command fable-real"* ⇒ Fable = anh `/model` đầu phiên HOẶC per-invocation `/fable-real`; runtime thật = BƯỚC 0.6 probe mỗi phiên, KHÔNG ghi sổ mỗi flip — hết class stale-2-flip ring1 bắt @S150). 🧊 Lineage owner-choice/flip-chain FROZEN tới flip-8 (flip-9 S119 Opus-4.8 owner-choice + các lượt /model sau = per-session không enumerate, anti-churn S106):** em main Fable 5 (1M) Max (✅ **Fable RESTORED S98 2026-07-02** — lead self-report `claude-fable-5` + settings `claude-fable-5[1m]`; fallback-Opus H5 S62→S97 kết thúc, H5.6 hoàn tất) ⚠️ **S101 2026-07-03: runtime lead = Opus 4.8** (lead + 2 monitor + WF2 reviewer inherit đều self-report `claude-opus-4-8`; settings vẫn pin `claude-fable-5[1m]` → **Fable unavailable phiên này → H5-fallback tự-kích, revert-free, KHÔNG sửa frontmatter**; "Fable RESTORED S98" = point-in-time KHÔNG phải steady-state — inherit-chain resolve theo availability runtime; ✅ **S102 2026-07-06: Fable VỀ LẠI runtime** — lead + 2 monitor inherit self-report `claude-fable-5` → H5-fallback S101 kết thúc, pattern availability per-session xác nhận [S98 Fable → S101 Opus → S102 Fable → **S103 Opus** → **S104 Opus** (2026-07-06 Fable outage tiếp — S104 lead + 2 monitor spawn self-report `claude-opus-4-8` ×3, H5-fallback revert-free) → ✅ **S105 Fable** (2026-07-07 Fable VỀ lần 3 — lead + 2 monitor self-report `claude-fable-5` ×3, runtime-verified) → ⚠️ **S106 Opus** (2026-07-07 Fable outage lại — lead runtime self-report `claude-opus-4-8`, H5-fallback revert-free; flip-7) → ✅ **S107 Fable** (2026-07-10 Fable VỀ lần 4 — lead + 2 monitor self-report `claude-fable-5` ×3, runtime-verified; flip-8)], BƯỚC 0.6 probe mỗi phiên; per-session KHÔNG steady-state. 🔴 **[§Sub-agents = CANONICAL single-owner flip-chain — S106 gộp per anh-confirm; derived `agents/README §Architecture` · `session-start BƯỚC 0.6` · `session-end §commit-format` trỏ về đây [🔄 vá @S153 FLAG-1: 3 cite số-dòng cũ `:3`/`:37`/`:113` đo lại SAI cả 3 (đúng là `:5`/`:48`/`:315`) — vá bằng BỎ số theo luật đã áp @S151, không ĐỔI số], KHÔNG tự enumerate mỗi flip → chống churn 4-file/flip]**) · **TOÀN roster `model: opus` + `effort: max`** (worker-tier-pin S124; **A2 đo S126:** lane no-tier resolve `claude-opus-4-8` DÙ lead=Fable ⇒ pin ăn runtime, hết inherit-lead; số vai = cột giữa của chính row này — KHÔNG chép lại vào narrative, đó là cách row-14 và narrative-12 lệch nhau từ S121 tới S122; lineage: 7 demoted flip S66 → all-inherit S66–S123 [office-document S107 inherit-born] → pin S124) · effort Max. 10 product/quality + monitor INFORM-only (H1 · H2 · 2×H24 S121 · 3×trio-memory S141 · 3×round-KIỂM `ring1-audit`/`ring2-audit`/`ring4-audit` S145) — **liệt-kê NHÓM, số vai = cột giữa của chính row này**. Nấc H8 = **runtime-verified S98 (2026-07-02)** — spawn-test inherit-chain ×2 monitor self-report `claude-fable-5` (cả đội tự lên Fable khi lead restore, 0 sửa frontmatter — đúng thiết kế all-inherit). Prev two-tier H4 runtime-verified S57bis (spawn-test 2 chiều). | | RAG chunks | 🟢 **service UP** · 🔴 **SE index STALE** | 🔴 **TÁCH 2 TRỤC @S159 — 1 nhãn nhị-phân DOWN/alive che mất trục thứ hai.** ① **service-liveness = UP**: đo LIVE 2026-07-29 `list_projects` → **7/7 project chunk > 0** (`solution_erp` **2449** · vipix_multisite 7090 · dh_y_duoc 15440 · namgroup 6106 · ashico 4742 · vipix_ai_infra 4176 · benhvien_aau 3024; chỉ `shared_global` = 0); **query THẬT** `search_code` → 1 hit **có ruột** score 7.156 ⇒ collection đọc được, **không phải vỏ rỗng** (metadata một mình KHÔNG đủ — có thể là registry-cache). ② **SE index-freshness = KÉM**: `last_indexed_at` = **2026-05-29** trong khi **5 sibling re-index 2026-07-29 08:21** cùng một lượt. **Lật thẳng DOWN→alive sẽ CHÔN trục ②.** 🧊 **Nhãn "DOWN từ S153" ĐÚNG LÚC GHI** (`33e7f68`, 07-28 20:50, 3 phép đo khớp, `chunk_count=0` cả 7 project) — service **hồi phục sau ~12h** ⇒ class `view-stale-status`, **KHÔNG phải lead ghi ẩu**; đừng quy lỗi cho lượt viết vốn có bằng chứng đúng. 🔸 **Caveat giữ (H1 F-4 → ring1 T-2):** H1 tuyên `last_indexed_at` "không đáng tin" (2447→2449 nhúc nhích), `ring1` **BÁC** bằng nội-dung — coi là **đáng tin**; muốn chắc tuyệt đối thì re-index rồi xem field có nhảy không. Re-index = AI_INFRA op (cần VOYAGE_API_KEY). *(FLAG-2 `view-stale-status` HIGH — `lead-stale-auditor` @S159, `ring2` tái-dựng 8/8 số. Lead trước đó chỉ vá `:6`, sót đúng ô canonical này = **canonical-poison**, cùng lớp khác site.)* | **Bundle hash live (prod):** admin **`Ajv-MaCz`** (1.783.465B) · user/eoffice **`YsXRkBSR`** (1.701.224B) — **đo LIVE 2026-08-05 @S172 bởi 3 nguồn độc-lập** (cicd-verify `998ea55` §5.1 · lead `curl` · `ring2-audit` §2 đo ngoài repo); 2 hash đời trước đã **biến mất khỏi đĩa** (trả `text/html` ~900/876 B). 🧊 *S168 #444: admin `DLRf9TeA` · eoffice `C7TGA4Kn` — **ô này đứng STALE suốt S171→S172** dù `998ea55` đã ship 04/08; `lead-stale-auditor` FLAG-1 (HIGH) bắt @S172, `ring2-audit` F-2 HELD bằng nguồn ngoài repo. **Lần thứ hai cùng ô** (S166 F-2) ⇒ quy-trình tự-khai dưới đây **có luật mà không có người thi hành** — nếu tái-diễn lần 3 thì phải wire máy, đừng vá tay nữa.*; 🧊 K7 #442: admin `RgvHZkPH` · eoffice `D65FJPGd`; push kế tiếp SẼ rotate tiếp ⇒ **nguồn cập nhật ô này = cicd-verify lượt kế, đừng tin ô này quá 1 deploy** (bài "sạch lúc HH:MM" — ô này đã chết-ngay-lúc-viết 1 lần trong chính S166). 🧊 Lineage gần: K6 #440 admin `Ig1R5tAh` ROTATE · eoffice FROZEN-lành-tính cmp-byte · K4a #437 admin `1yiNV4VH`/eoffice `DoULfmdT` (post-F-1, 2-css khớp chứng đo-đúng-site) · K3 #436 admin `mySTlx42`/css `Cf3iCOOv` · eoffice `CZAYiWWa` FROZEN-hợp-lệ (diff toàn comment, cicd §5 dự-đoán-trước). 🧊 Lineage cũ (S162-S164, hash `B43R6Y17`/`NLI5umBg` đã chết): **Nguồn rotate = push `e5123ff`**, commit gắn nhãn `Docs: S162 closeout` **nhưng ship 6 file FE-src THẬT** (`PipelineStageFolders.tsx` · `PipelineTreePanel.tsx` · `usePipelineStages.ts` ×2 app) ⇒ rotate **real-src, KHÔNG phải #69-benign**; scope commit sai convention (đáng lẽ `FE-Admin`+`FE-User`, không phải `Docs`). 🔴 **Hại đo được — LỆCH 2 BỀ MẶT, không phải 1:** ô này đứng ở cặp S157 qua **2 đời deploy**, VÀ dòng `:6` ghi cặp S162 `DXoYDy7R`/`o8KDluD1` **chưa bao giờ là trạng-thái cuối** (snapshot chụp sau `bfc7b79`, trong khi `e5123ff` còn ship FE tiếp) ⇒ **cả 2 bề mặt owner đều lệch prod cùng lúc**. Đúng ca *"closeout-push kế có non-md sẽ rotate tiếp"* mà chính ô này đã tiên-liệu (precedent S99-#485 / S107) — tiên-liệu đúng, cơ-chế bắt vẫn không có. **Control-âm:** cặp S157 `8-QYcMOg`/`DptYR4wL` nay trả `text/html` **900 B** admin / **876 B** eoffice ⇒ bundle cũ đã chết. ⚠️ **CHƯA neo run-số CI** — lead chỉ curl 3 phép (2 hash live + control-âm), **không đọc Gitea Actions**; cần `cicd-monitor` xác nhận Run # + test-gate trước khi coi ô này đủ bằng-chứng. 🧊 *Mốc trước S157:* admin **`8-QYcMOg`** · user **`DptYR4wL`** (đo LIVE @S157 2026-07-28, **Run #419** `6250f39` — cicd PASS 9/9 + lead tự re-curl độc lập khớp từng byte, marker 2/2 mỗi app; **control-âm**: 2 hash CŨ `index-BK8w3QxU.js`/`index-DYnxSI0T.js` nay trả `text/html` **900 B admin / 876 B eoffice** ⇒ bundle cũ đã chết trên prod. 3 phép đo độc-lập: cicd-verify · lead re-curl · ring2-audit). 🔴 **Ô này từng đứng ở cặp `BK8w3QxU`/`DYnxSI0T` sau khi prod đã rotate** — `git show 3b6fabc -- docs/STATUS.md` chỉ có 2 hunk, **0 hunk chạm dòng này**; hại đo được: `cicd-verify-s157-diadiem.md:58` dùng chính ô này làm baseline ⇒ lượt deploy kế hoặc báo "ĐỔI" giả hoặc che một lần không-rotate (lead-stale-auditor FLAG-1 HIGH @S158). 🧊 *Mốc trước:* admin `BK8w3QxU` · user `DYnxSI0T` (S156 CI run 531 `f4494cf`; `Last-Modified` 14:41:15/14:42:10 +07 **nằm trong cửa-sổ deploy** — ship-proof, không phải hash-delta suông. Byte-verify 4/4 marker: `allowApproverDelete` 8/2 · `by-approver` 1/1 · `deleted=1` 1/1 · `view=approved` 1/1. ⚠️ **Không chứng minh hồi-tố** được bundle của riêng `4464a46` đã rotate lúc 14:12 — `f4494cf` đè lên sau 30 phút, không ai chụp snapshot khoảng giữa). 🧊 *Mốc trước:* admin `B2j1LQ-v` · user `C3wbJegk` (S147 CI #407 — re-stamp @S152: FLAG-2 lead-stale bắt ô này đứng S134 qua 5 nhãn trong khi `:6`/`:465`/HANDOFF đều S147; ring2 verify 2 lệnh độc-lập). 🧊 *Chuỗi rotate lịch-sử giữ nguyên:* (S134 — **`6664a61`** PE lũy kế TẠM TÍNH vs CHÍNH XÁC [0 mig — `ComputePendingAsync` complementary + DTO 5-field + FE sub-dòng amber + banner màn duyệt + D4 audit-note]; **Run #399** GO 5/5 5m27s gate-**528**-EXACT-từ-log rotate ×2 admin js `3NCdyODe`→**`BVCukFP5`** css `B--dUHcm`→**`CBzUlgOA`** / eoffice js `CKsoiNCI`→**`DoxyEOxh`** css `BuntB63r`→**`BHsBUA8e`** [FE-src đổi thật 6 file ⇒ rotate expected; CSS rotate = amber badge utilities, benign #69]; real-size 1.659.037/1.565.367B + LM 14:31:34/14:32:28+07 in-window; **byte-verify #77** `pendingPriorPes`=2 + `Tạm tính`=3 + `phiếu trước cùng gói thầu CHƯA duyệt`=2 PRESENT cả 2 js [control old-hash 900B text/html 0-hit — ct=text/html là discriminator sạch hơn size-919b]; Mig 67 top FROZEN + 89 bảng [0-mig by-construction]; smoke 4×200 + PE 401; lead spot-check độc-lập post-verdict: 4/4 hash khớp + marker PRESENT). **Prev S133** — admin **`3NCdyODe`** · user **`CKsoiNCI`** (S133 — **`d4fbced`** PE freeze NS phiếu DaDuyet [Mig 67 snapshot 11 cột + backfill 18/18 + FE 2-app badge 🔒 SHA-mirror `5534addb` + display-gate `budgetFrozen`]; **Run #398** GO 5/5 gate-520 rotate ×2 admin →**`3NCdyODe`** [CSS `B--dUHcm`] / user →**`CKsoiNCI`** [CSS `BuntB63r`] — prev `yCkX-2Bp`/`DKSD4p9k` @Run #397 S131-S132 [dòng-live lag S117→S133, hoist per precedent S104/S107]; **CSS cũng rotate** = badge sinh utility-class mới, benign #69; **byte-verify #77** `budgetFrozen`=1 + VN-string 🔒=1 PRESENT [old-control 0]; Mig 67 prod history-top + 11 cột + backfill 18/18 chua=0 + sys.tables **89**; smoke 4×200 + PE 401; `/tiep` S134 re-verify độc-lập 4/4: mig-top + health-200 + bundle-hash ×2 + filter 11/11). **Prev S117** — admin **`cFI7ih4a`** · user **`MxgooVZw`** (S117 — **`316a82f`** PE endsBeforeCeo pointer-aware [display-fix BE-only, phiếu chờ CEO hết báo "Kết thúc" sai] + duyệt-theo-khoảng Min–Max [Mig 66 + FE 2-app ProMinMax radio thứ 5]; **Run #387** PASS 5m36s rotate ×2 admin `Dd55jBpj`→`cFI7ih4a` / user `DVDr1UQ4`→`MxgooVZw` CSS-frozen [logic-only fingerprint]; **byte-verify #77** `ProMinMax`/`duyệt theo khoảng`/`cả Min và Max`/`Min–Max` PRESENT [bundle-cũ=0]; Mig 66 prod col decimal(18,2) + `__EFMigrationsHistory`-top + sys.tables **89**; smoke 200×3 · PE 401-secured; **#53-garble ×2** [invest-lane-1 + cicd-monitor] → SendMessage-resume + em-main curl cross-verify khớp). **Prev S116** — admin **`Dd55jBpj`** · user **`DVDr1UQ4`** (S116 — **`88368fd`** PE cho nhập giá ÂM ô báo giá [FE 2-app nút ± toggle + submit-gate `>0`→`!==0` + helper `hasRealSelectedQuote` 2-mirror `:201`/`:235`; BE submit-guard `:206` `Sum()<=0`→`All(==0)`; +9 test 486→495; **NO migration**]; **Run 386** PASS 5m28s rotate ×2 admin `D51OYyGV`→`Dd55jBpj` / user `BVdssm5S`→`DVDr1UQ4` CSS-frozen [logic-only fingerprint]; **byte-verify #77** `Đã nhập giá chào thầu`/`hoàn tiền NCC`/`phát sinh giảm`/`Đơn vị được chọn chưa có giá chào thầu` PRESENT [bundle-cũ=0]; smoke 200×3 · PE 401-secured · 89 bảng; cicd bắt bẫy snapshot-while-CI-running→poll-success TRƯỚC re-snapshot). **Prev S114** — admin **`D51OYyGV`** · user **`BVdssm5S`** (S114 — **`d436af2`** PE multi-NCC per hạng mục [Mig 65 backfill 2-chiều + BE re-key 5 financial→IsSelected + FE per-cell grid Detail×NCC 2-app SHA `ae3788e9`]; bundle rotate ×2 admin `acprMqfB`→`D51OYyGV` / user `Ck3BTDdA`→`BVdssm5S`; **byte-verify** `detailId`+`select-winner`+`hạng mục` PRESENT [bundle 1.65MB real-size]; smoke health/admin/eoffice 3/3 200 + endpoint POST select-winner bogus-401-live; **cicd garble #53 ×5 → em-main-solo curl-verify**; mig applied [API-healthy=migrations-ran-startup]). **Prev S113** — admin **`acprMqfB`** · user **`Ck3BTDdA`** (S113 — **Run #497** `5fa11b5` Supplier import v2 [Mig 64 publish/draft + dedup-MST + template + file mẫu] FE 2-app real-src rotate; JS admin `Cixlt_MA`→`acprMqfB` / user `BhBOG1dn`→`Ck3BTDdA` + CSS admin `Ds-E5neu`→`CfunQHbd` / user `Beb0Bb1h`→`DGqqHQXS`; ship-proof real-size 1.66/1.56MB + LM-in-window + **byte-verify** `Tải file mẫu`×1/`Đã công bố`×3/`Nháp (ẩn)`×4/`import/template`×1/`published`×5 PRESENT; Mig 64 prod 37/37-backfill sys.tables **89**; smoke health/admin/eoffice 200 + `import/template`-401-live + `suppliers?published=true`-401-live bogus-404-control). **Prev S112** — admin **`Cixlt_MA`** · user **`BhBOG1dn`** (S112 — **Run #496** `e100ef0` Supplier Excel-import Phase B FE 2-app [`SupplierImportDialog` NEW cả fe-admin+fe-user] + PE sign-off code-only; **real fe-src rotate — kết thúc #489→#495 non-determinism-freeze**; JS admin `BVyH-FY7`→`Cixlt_MA` / user `EDsZebvn`→`BhBOG1dn` + CSS admin `Bbtesa6B`→`Ds-E5neu` / user `BmuPz_Dy`→`Beb0Bb1h`; ship-proof real-size + Last-Modified-in-window + `"Database NCC"` marker PRESENT ×1; Mig 63 prod 2/2-col sys.tables **89**; smoke health/admin/eoffice 200 + PE-endpoint 200 + import-route preview-415/confirm-401-live bogus-404). **Prev S107** — admin **`BVyH-FY7`** · user **`EDsZebvn`** (S107 — **Run #489** `21387cd` +office-document 12th sub governance-only; **rotate benign #69-two-mode** [0 fe-src — Vite non-determinism], CSS frozen `Bbtesa6B`/`BmuPz_Dy`, real-size 1.64/1.55MB + Last-Modified-in-window 09:32/09:33 = ship-proof, Mig 62/89 frozen, smoke 200×3; hoist @S107-closeout per H1 flag D-BUNDLE-lặp — ⚠️ closeout-push kế có non-md sẽ rotate tiếp → Run #490 record nằm ở cicd MEMORY [authoritative], hoist ở bundle đầu phiên sau per precedent S99-#485). **Prev S104** — admin **`ChSNAJBK`** · user **`Bn2w1znH`** (S104 — **Run #488** `778fc98` Mig 62 Supplier expand 30-field; real-src-rotate FE 2-app SHA-mirror [form 4-section + Status select + `PathLink` 3 NAS-link], **sys.tables 89 pre==post no-new-table**, prod-verified marker grep-HIT, smoke 200×3; S104-shipped nhưng dòng-live `:27` lag tới S106 → hoist per H1 flag D-BUNDLE). **Prev S97** — admin **`mEGqlNWc`** · user **`Dnrp4PU1`** (S97 — **Run #484** `fb2dfa9` Mig 61 data-fix backfill; bundle rebuild **#69-benign** [0 FE-src change, review-workflow cicd re-snapshot LIVE → `CeTA7Z82`/`CGaAyXen` #483 đã bị #484 rebuild rotate]; backfill A/005/A/008 flag 0→1 prod-verified). **Prev #483** — admin **`CeTA7Z82`** · user **`CGaAyXen`** (S97 — **Run #483** `9e12e80` badge "✅ Đã được CEO duyệt" phiếu đi-hết-lên-CEO [PeFinalizeChip single-source + 6 call-site 2-app, đối xứng chip "⚑ Kết thúc·X"]; FE-only tận-dụng endsBeforeCeo runtime, no BE/mig; JS rotate CSS-frozen #69; byte-#77 "Đã được CEO duyệt" PRESENT cả 2 app + prev-badge additive no-regression; Mig 60 frozen; smoke 200×3 + PE-list 200). **Prev #482** — admin **`DR9q2VUy`** · user **`Bgz8u4E5`** (S97 — **Run #482** `8037fa0` endsBeforeCeo config→RUNTIME [Mig 60 `EndedByLevelFinalize`]: **Mig 60 applied prod** (history-top + col-default-0 EXISTS + backfill=0 UAT-no-marker + sys.tables **89** pre==post); **EF-ternary SQL-Server LIVE-confirmed** GET /purchase-evaluations 200 items=9 + PE-detail `31faef0d` 200 **endsBeforeCeo=False phase=7** = runtime-path chạy = **bug fixed LIVE** (NOT 500); JS rotate 2-app PeWorkflowPanel gate-diff, CSS frozen #69-REFINE; byte-#77 `endsBeforeCeo` PRESENT in-bundle; smoke 200×3. reviewer PASS 0-issue + test 440. **Prev #481** — admin **`Cf0o8dx8`** · user **`x8rppRAK`** (S97 — **Run #481** `1c8fb6a` PE finalize 2-fix [badge "không trình tới"→"Kết thúc tại {cấp}" + đảo default checkbox opt-in]; JS rotate real-FE 2-app `PeWorkflowPanel` diff, CSS frozen `Bbtesa6B`/`BmuPz_Dy` #69-REFINE logic-only; **byte-level #77** NEW `Kết thúc tại`+helper PRESENT / OLD `không trình tới`+`Đã BỎ tick` ABSENT; real-size 1.64/1.54MB; Mig 59/88-table frozen; smoke 200×3. FE-only mirror SHA `bcf812fd`, 0 BE/mig/test). **Prev S96** — admin **`COYAX-yQ`** · user **`0MU-kb4N`** (S96 — **Run #480** `0c2f9f6` price-lock; chuỗi 3 PE deploy #478 `e657faa` checkbox-opt-out → #479 `36cbf8b` chuông-stale-clear (BE-only) → #480 `0c2f9f6` khóa-giá-sau-duyệt, cicd PASS each ~5m; **FE JS rotate THẬT** (PeWorkflowPanel #478 + PeDetailTabs #480, 2-app) real-size 1.64/1.54MB vs 919b SPA-trap, CSS frozen #69-REFINE; Mig 59 frozen 0-mig; +Run #477 `ba4b8e0` NCC seed no-mig prod 21 NCC. **Verify byte-level:** prod `0MU-kb4N` CHỨA checkbox-fix ("BỎ tick"/"Đã BỎ tick") + "✅-static" cũ MẤT → checkbox "ko-được" = browser-cache KHÔNG deploy-fail). **Prev S95** — admin **`0T6vVJST`** · user **`BGXEVZO6`** (S95 — **Run #476** `18a17f5` H17 governance-only deploy; bundle rotate benign #69 CSS-frozen, prod 200×3, Mig 59 unchanged; cicd re-snapshot LIVE — pre-deploy `CkZ1MjC1`/`_-gpX7_Y`, mega-line S93 `CDn40vpV`/`CoPmPBg7` đã bị deploy trung-gian rotate từ S94). **Prev S85** — admin **`aKuNip19`** · user **`BUsL0HeQ`** (S85 — **Run #341** `49ac086` lệch-giờ TZ serialize UTC-with-Z; FE frozen #339+#341 BE-only. Chuỗi ~10 deploy #334→#341 buổi UAT anh Kiệt CV PRO: #334 `5a61254` batch CV PRO 6-fix → #335-#338 6 vòng UAT → #339 `1263c0d` workflow-preserve bug-class S42 → #341 `49ac086` TZ; #342 `d4f6021` docs+cicd BE-frozen, bundle giữ-nguyên gotcha #69-REFINE). **Prev S78** — admin **`CsJetgZH`** · user **`BVS0ApIm`** (S78 — **Run #330** `7886fd0` PE attach-file khi duyệt; bundle ROTATE từ S77 `BqKD3Y23`/`Cn-i349D` dù NO migration enum-only — gotcha #69 non-determ rebuild; cicd ship-proof byte-stable + fake-hash control). **Prev S77** — admin **`BqKD3Y23`** · user **`Cn-i349D`** (S77 — **Run #329** `e823694` banner Trả-lại; cuối chuỗi 10 deploy #320→#329 cùng ngày, cicd PASS each: #320 pill `DsSg6RRz`-era → #321 focus → #322 revert → #323 Mig57+notes `CPm4LTqm`/`BWJUAqEI` → #324 số-âm-đỏ `C6fx-0ea`/`N3sW4Div` → #325 indent `BhnNMucS`/`B1VebpXc` → #326 cờ-gấp-asymmetric `DwXqn37C`/`COXMCv7E` → #327 decouple `B0gboSAg`/`DbDg7pM-` → #328 notify (BE-only, FE frozen) → #329 banner **`BqKD3Y23`**/**`Cn-i349D`**). **Prev S76** — admin **`jOqxW4-p`** · user **`DbsznVvR`** (S76 — **Run #319** `21d1f4e` bảng lưới `` ngân sách viền-ô; chuỗi S76 cùng ngày: #318 `e33481e` feature ma-trận-3-cột + badge quyền-NS `BhFDF9IJ`/`BAkuRl3C` → #319 **`jOqxW4-p`**/**`DbsznVvR`**, cicd PASS each, smoke 3×200, Mig 56 applied prod #318). **Prev S74** — admin **`BYF5vIMJ`** · user **`CB-tiRxd`** (S74 — **Run #315** `8655ebf` Mig 55 CcmNote, cicd PASS ~4m54s: Mig 55 applied prod CcmNote nvarchar(1000) nullable · sys.tables 88 · smoke 4×200 · 0 regression). **Prev S73** — admin **`Bv3jUCNo`** · user **`BWlMBQz6`** (Run #314 `6aa4dcb` FE empty-candidates guard fix; chuỗi 2 deploy: #313 `1d86abc` feature `OlNyG9OD`/`DSzSLVtL` → #314 **`Bv3jUCNo`**/**`BWlMBQz6`**, cicd PASS each). **Prev S72** — admin **`fc_xkNpJ`** · user **`DP-tBcg0`** (Run #312 governance commit `18fced6`; bundle ROTATE do **non-deterministic hash + deploy rebuild-FE-unconditional** (gotcha #69) — 0 FE source change, prod state/DB GIỮ NGUYÊN). **Prev S69** — admin **`BgNCjwsG`** · user **`CBvh0vtf`** (Run #308 PE cờ gấp + ngưỡng CCM; chuỗi 4 deploy session: #305 Văn phòng số foundation → #306 re-skin 10 page → #307 Office golive (BE-only) `Wt54PHYl`/`B99fMU6X` → #308 **`BgNCjwsG`**/**`CBvh0vtf`**). **Prev S68** — admin **`CNUv1jxY`** · user **`CpOskeS1`** (S68 — Run #304 `37752eb` fix tên đen→trắng; chuỗi #303 `6983609` `D532XZKG`/`CuFaBoWt` → #304 **`CNUv1jxY`**/**`CpOskeS1`**, cicd PASS each). **Prev S67** — admin `CcrZqfht`/user `DniDFUB_` (S67 — 6 deploy Run #297→#302; admin rotate chuỗi #298`xkSz9BfE`→#300`PxiZQkaw`→#301`I1fpLeYw`→#302**`CcrZqfht`** · user #297`BumgrwCJ`→#300`B36hGoKd`→#301`DrQYkzh0`→#302**`DniDFUB_`**; #299 tests BE-only cả 2 frozen, cicd PASS each). **Prev S65** — admin `BDwV5d0X`/user `DbVv6rsf` (Run #293-295). Prev S62 admin `0xKYGhhf`/user `C81ZdG9G` (Run #286). **Prev S59:** admin `B1DtNT9C` · user `D6uF3Mln` (Run **#282** `792c030` FINAL — bỏ nút Thêm hạng mục; ships kèm `80b64dd` gỡ Điều khoản TT, #281 cancelled-benign ancestor-verified). Chuỗi S59 cùng ngày: #280 `69997da` (`BKy_8OO9`/`XcZ6PRyA`, ships kèm `f21c55d` table-fixed #279-cancelled) · #278 `9c330d2` self-approve+quick-add-NCC (`BSh2fG2X`/`D22KfpPc`, authz probe 4/4) · #277 `faed59f` SearchableSelect (`ex7Tc92G`/`DzUeSk96`) · #276 `c869d26` rename 71 PMH (`BBA0KSWu`/`DzdTI18G`) · #275 `bbd1554` dọn demo WorkItems (FROZEN BE-only) · #274 `0eafcd3` tree 4 tầng (`DuU7OTym`/`DWyeTzf3`) · #273 `56882ac` wipe + tree v1 (`R9uGRxvw`/`DikfX1RD`). Prev S58: Run #386 `3ebaf84` admin `DMm9rtNA`/user `BUkOMn_Y` (chi tiết session log S58).