wal: flush 20260801T1038
This commit is contained in:
@ -1,6 +1,6 @@
|
|||||||
# CI/CD Monitor Agent — Persistent Memory
|
# CI/CD Monitor Agent — Persistent Memory
|
||||||
|
|
||||||
- **S166 (08-01) `88e7ecf` K4a+K4b sidebar 8-nhóm × 6-leaf + nối `?group=` (0-mig) — PASS 7/7** (run **#438** 6m23s · gate **621=45D+576I** · Mig GIỮ 71 · menu 151→**200** · perm 1584→**2221**). 🔑 **PHỦ 2 RUN 1 LƯỢT ⇒ delta KHÉP KÍN**: #437 F-1 = 614 (Infra +0) ⇒ trọn **+7** quy được về K4. 🔑 **MỐC PRE ĐỀ BÀI SAI, bắt bằng ĐO**: lead giao `143⇒192`, đo LIVE lúc run còn `running` ra **151** → truy phạm-vi thay vì hô drift (K3 đẻ 8 row `AwV2_KhkkN1..N8`) ⇒ đúng là **200**. **Mốc PRE lão hoá sau MỖI deploy.** 🔑 **TAM-GIÁC 3 NGUỒN** + **phân rã 49+588=637 khớp cơ-chế 2-site skip-existing** (588=49×12 role còn-lại) — KHÔNG suy được từ tổng ⇒ bit thật. ⚠️ **PERM row ≠ policy** (1 row/cặp 4 cờ; "49×4=196" là POLICY, cấm cộng). 🔑 **A5 restart ×2 THẬT** (PID 7020→220→6636): 8 chỉ số ĐỨNG IM + **soi ĐIỀU KIỆN log trong source TRƯỚC khi đọc im-lặng** (`if (added>0)`) ⇒ vắng dòng ⟺ 0 added; control #84 `Hrm*/Off*` giữ **29/377**. 🔴 **MARKER LEAD GỢI Ý BỊ BÁC BẰNG ĐO** (`Kết cấu…` có sẵn từ `7a903cf` ⇒ đo **1/1** = 0 bit) → thay bằng POS `/khkk/list?group=1` 4/4 element-wise · NEG `/khkk/list?view=deleted` 0/0 · 🔑 **DIFFERENTIAL admin 0 / eoffice 2** (bất-đối-xứng CỐ Ý ship + tự chứng comment-strip). 🔑 `?group=abc` no-token=**401 (authz chặn TRƯỚC bind)** → bearer re-probe **400 bind-error** ⇒ gỡ giới-hạn phép đo. ⚠️ Treo: bảng **0 dòng** ⇒ chứng ống+bind KHÔNG chứng lọc-đúng-nhóm; `?group=999`→200. Evidence `runs/2026-07-31-S164-4gd-khkk-fanout/sub-cicd-verify-k4ab.md`. Tag `[run-438, pass-7-7, phu-2-run, moc-PRE-sai, marker-0-bit, differential-marker]`
|
- **S166 (08-01) `88e7ecf` K4a+K4b sidebar 8-nhóm × 6-leaf + nối `?group=` (0-mig) — PASS 7/7** (#438 6m23s · gate **621=45+576** · Mig GIỮ 71 · menu 151→**200** · perm 1584→**2221**). 🔑 **PHỦ 2 RUN 1 LƯỢT ⇒ delta KHÉP KÍN**: #437 F-1 = 614 (Infra +0) ⇒ trọn **+7** quy được về K4. 🔑 **MỐC PRE ĐỀ BÀI SAI, bắt bằng ĐO**: lead giao `143⇒192`, đo LIVE lúc còn `running` ra **151** → truy phạm-vi thay vì hô drift (K3 đẻ 8 row `AwV2_KhkkN1..N8`) ⇒ đúng là **200**. **Mốc PRE lão hoá sau MỖI deploy.** 🔑 **phân rã 49+588=637 khớp cơ-chế 2-site skip-existing** (588=49×12 role còn-lại) — KHÔNG suy được từ tổng ⇒ bit thật (tam-giác source ⟂ sqlcmd ⟂ Serilog). ⚠️ **PERM row ≠ policy** (1 row/cặp 4 cờ; "49×4=196" là POLICY). 🔑 **A5 restart ×2 THẬT** (PID 7020→220→6636): 8 chỉ số ĐỨNG IM + **soi ĐIỀU KIỆN log trong source TRƯỚC khi đọc im-lặng** (`if (added>0)`) ⇒ vắng dòng ⟺ 0 added; control #84 `Hrm*/Off*` giữ **29/377**. 🔴 **MARKER LEAD GỢI Ý BỊ BÁC BẰNG ĐO** (`Kết cấu…` có sẵn từ `7a903cf` ⇒ đo **1/1** = 0 bit) → thay bằng POS `/khkk/list?group=1` 4/4 element-wise · NEG `/khkk/list?view=deleted` 0/0 · 🔑 **DIFFERENTIAL admin 0 / eoffice 2** (bất-đối-xứng CỐ Ý ship + tự chứng comment-strip). 🔑 `?group=abc` no-token=**401 (authz chặn TRƯỚC bind)** → bearer re-probe **400 bind-error**. ⚠️ Treo: bảng **0 dòng** ⇒ chứng ống+bind KHÔNG chứng lọc-đúng-nhóm; `?group=999`→200. Evidence `runs/2026-07-31-S164-4gd-khkk-fanout/sub-cicd-verify-k4ab.md`. Tag `[run-438, phu-2-run, moc-PRE-sai, marker-0-bit, differential-marker]`
|
||||||
|
|
||||||
- _(**07-31 `15349e8` S164 K3 8 workflow KHKK-N1-8 (0-mig) — PASS 6/6**: #436, gate 614=45+569. 🔑 **dự-đoán-TRƯỚC-khi-đo** (seeder → 8wf×3step×11level + finalize 2+8=10 → khớp từng đồng + shape element-wise từng email + control ÂM OUTSIDE=2/OUTOFSET=0). 🔑 **FROZEN = hệ-quả BẮT BUỘC** (diff toàn `//` → Vite strip); ❌ marker `earlyFinalizeLocked` LOẠI vì **tên biến local bị minifier đổi = 0 bit**. ⚠️ tao chết #53 ×2. verbatim → `archive/2026-07.md`)_
|
- _(**07-31 `15349e8` S164 K3 8 workflow KHKK-N1-8 (0-mig) — PASS 6/6**: #436, gate 614=45+569. 🔑 **dự-đoán-TRƯỚC-khi-đo** (seeder → 8wf×3step×11level + finalize 2+8=10 → khớp từng đồng + shape element-wise từng email + control ÂM OUTSIDE=2/OUTOFSET=0). 🔑 **FROZEN = hệ-quả BẮT BUỘC** (diff toàn `//` → Vite strip); ❌ marker `earlyFinalizeLocked` LOẠI vì **tên biến local bị minifier đổi = 0 bit**. ⚠️ tao chết #53 ×2. verbatim → `archive/2026-07.md`)_
|
||||||
|
|
||||||
@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
## 🎯 Role baseline
|
## 🎯 Role baseline
|
||||||
|
|
||||||
Read-only CI/CD + post-deploy verifier SOLUTION_ERP. Polls Gitea Actions API, verifies test gate + deploy ship + prod health. Tools: Read, Grep, Glob, Bash, WebFetch + 5 RAG MCP. Output: PASS/FAIL + evidence <500 words. Skills: `iis-deploy-runbook` + `dependency-audit-erp` + `ef-core-migration`. Spawn ~150K — trade-off catch fail tự động.
|
Read-only CI/CD + post-deploy verifier SOLUTION_ERP. Poll Gitea Actions API, verify test-gate + deploy-ship + prod health. Output PASS/FAIL + evidence <500 words. Skills: `iis-deploy-runbook` · `dependency-audit-erp` · `ef-core-migration`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ cicd-lane trả nhầm slice transcript thay verdict — **tao TÁI PHẠM dai n
|
|||||||
|
|
||||||
- **#39 act_runner github.com TCP timeout** — run hang "Set up job" 21s. Log `dial tcp github.com:443 i/o timeout`. Fix: manual checkout bypass hardcoded `.gitea/workflows/deploy.yml`. KHÔNG revert.
|
- **#39 act_runner github.com TCP timeout** — run hang "Set up job" 21s. Log `dial tcp github.com:443 i/o timeout`. Fix: manual checkout bypass hardcoded `.gitea/workflows/deploy.yml`. KHÔNG revert.
|
||||||
- **#40 npm cache `tsc not found`** — `build_fe_admin` fail post `cache: npm`. DISABLED rolled back `a21790d`. KHÔNG re-enable.
|
- **#40 npm cache `tsc not found`** — `build_fe_admin` fail post `cache: npm`. DISABLED rolled back `a21790d`. KHÔNG re-enable.
|
||||||
- **#41 paths-ignore docs-only skip** — code commit không trigger CI? Check `git diff --name-only HEAD~1 HEAD` vs `paths-ignore` **7-entry từ D10 S111 `8aa3869` (#495): `['docs/**','**/*.md','.claude/skills/**','.claude/agent-memory/**','.claude/workflows/runs/**','.gitignore','scripts/**.md']`**. Gitea evaluates push *range* — ≥1 commit có non-ignored file → toàn range build. ⚠️ **AS-6 BASELINE SHIFT: từ #495, memory-push chứa `memory-budget.json`/agent-memory-file/run-trace HẾT trigger CI (pattern #490/#492 DEAD).**
|
- **#41 paths-ignore docs-only skip** — code commit không trigger CI? Check `git diff --name-only HEAD~1 HEAD` vs `paths-ignore` **7-entry từ D10 S111 `8aa3869` (#495): `['docs/**','**/*.md','.claude/skills/**','.claude/agent-memory/**','.claude/workflows/runs/**','.gitignore','scripts/**.md']`**. Gitea evaluates push *range* — ≥1 commit có non-ignored file → toàn range build. ⚠️ **AS-6: từ #495 memory-push/run-trace HẾT trigger CI.**
|
||||||
- **#25 IIS WebSocket** — `notification-hub/negotiate` 401/404 prod. Fix: WebSocket module enable `web.config` site api (skill `iis-deploy-runbook`).
|
- **#25 IIS WebSocket** — `notification-hub/negotiate` 401/404 prod. Fix: WebSocket module enable `web.config` site api (skill `iis-deploy-runbook`).
|
||||||
- **#48 SQLite tie-break** — `OrderByDescending(CreatedAt).First()` pick wrong khi 2+ `.Add()` cùng frozen-clock. Fix: discriminator filter BEFORE OrderBy.
|
- **#48 SQLite tie-break** — `OrderByDescending(CreatedAt).First()` pick wrong khi 2+ `.Add()` cùng frozen-clock. Fix: discriminator filter BEFORE OrderBy.
|
||||||
- **Bundle hash unchanged = ship FAIL** — push+action success nhưng prod không đổi. Verify via INDEX.HTML ref (`curl -s https://admin.solutions.com.vn/ | grep -oE '/assets/index-[A-Za-z0-9_-]+\.(js|css)'`), NOT by GETting a hash-named asset directly. ⚠️ verify MUST sau status=success (Run #242 false-positive: check khi "running" → stale hash).
|
- **Bundle hash unchanged = ship FAIL** — push+action success nhưng prod không đổi. Verify via INDEX.HTML ref (`curl -s https://admin.solutions.com.vn/ | grep -oE '/assets/index-[A-Za-z0-9_-]+\.(js|css)'`), NOT by GETting a hash-named asset directly. ⚠️ verify MUST sau status=success (Run #242 false-positive: check khi "running" → stale hash).
|
||||||
@ -52,8 +52,8 @@ cicd-lane trả nhầm slice transcript thay verdict — **tao TÁI PHẠM dai n
|
|||||||
- **Stage 2 Gitea poll** (max 10 iter × 60s): API `.../actions/tasks?limit=5` (NOT `/runs` 404). Match `head_sha` (NOT run_number — cancelled runs shift numbering). ⚠️ `updated_at` stale ~2min (gotcha #46) → cross-check VPS mtime. Foreground-sleep BLOCKED Windows → Monitor-style busy-wait-curl-spin ~30s/iter.
|
- **Stage 2 Gitea poll** (max 10 iter × 60s): API `.../actions/tasks?limit=5` (NOT `/runs` 404). Match `head_sha` (NOT run_number — cancelled runs shift numbering). ⚠️ `updated_at` stale ~2min (gotcha #46) → cross-check VPS mtime. Foreground-sleep BLOCKED Windows → Monitor-style busy-wait-curl-spin ~30s/iter.
|
||||||
- **Stage 3 Test gate:** baseline **621 PASS** (45 Domain + 576 Infra; run #438 `88e7ecf` 08-01; climb 621/614/609/598/590/566/562/551/532/528/520/509/458/440). CI chạy cả 2 proj TRƯỚC build/deploy ⇒ `status:success` = gate passed (`tasks` KHÔNG populate `conclusion` — tin `status`). Local grep undercount (Theory/InlineData). Phase 9 UAT skip OK.
|
- **Stage 3 Test gate:** baseline **621 PASS** (45 Domain + 576 Infra; run #438 `88e7ecf` 08-01; climb 621/614/609/598/590/566/562/551/532/528/520/509/458/440). CI chạy cả 2 proj TRƯỚC build/deploy ⇒ `status:success` = gate passed (`tasks` KHÔNG populate `conclusion` — tin `status`). Local grep undercount (Theory/InlineData). Phase 9 UAT skip OK.
|
||||||
- **Stage 4 Post-deploy** (if SUCCESS): auth login bearer (admin + nv.test gotcha #44; token=`accessToken` route `/api/auth/login`) → 3-5 endpoint smoke 2XX (incl new — NEW-endpoint bodyless-POST returns **411 IIS Length-Required pre-auth NOT 404**; re-probe WITH body → 401 confirms wired) → FE bundle hash 2 app (per #69 = real-size-vs-fake not hash-delta) → SignalR negotiate (gotcha #25) → EF mig prod==repo.
|
- **Stage 4 Post-deploy** (if SUCCESS): auth login bearer (admin + nv.test gotcha #44; token=`accessToken` route `/api/auth/login`) → 3-5 endpoint smoke 2XX (incl new — NEW-endpoint bodyless-POST returns **411 IIS Length-Required pre-auth NOT 404**; re-probe WITH body → 401 confirms wired) → FE bundle hash 2 app (per #69 = real-size-vs-fake not hash-delta) → SignalR negotiate (gotcha #25) → EF mig prod==repo.
|
||||||
- **Stage 4.6 (S29 CRITICAL):** sqlcmd seed sample verify post-deploy (NOT chỉ schema). Seed=runtime-row-insert (no history/tables advance) → verify via DB-query (e.g. Permissions count), NOT bundle-frozen-alone.
|
- **Stage 4.6 (S29):** sqlcmd seed verify post-deploy (NOT chỉ schema) — seed = runtime-row-insert (history/tables KHÔNG nhích) ⇒ verify bằng DB-query, không bằng bundle-frozen.
|
||||||
- Discovery: record enum cần input SỐ (SOL không có `JsonStringEnumConverter`). sqlcmd ssh Windows-auth `\\\\SQLEXPRESS` 4-backslash. INFRASTRUCTURE seed ungated / DEMO seed gated → gotcha #51.
|
- enum cần input SỐ (no `JsonStringEnumConverter`). INFRASTRUCTURE seed ungated / DEMO seed gated → #51.
|
||||||
- **Stage 5 Report** PASS/FAIL + evidence + MEMORY update.
|
- **Stage 5 Report** PASS/FAIL + evidence + MEMORY update.
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -67,10 +67,10 @@ cicd-lane trả nhầm slice transcript thay verdict — **tao TÁI PHẠM dai n
|
|||||||
|
|
||||||
- **Gitea:** `git.baocaogiaoduc.vn/vietreport-admin/solution-erp` · workflow `.gitea/workflows/deploy.yml` · paths-ignore 7-entry (D10 S111 #495): `['docs/**','**/*.md','.claude/skills/**','.claude/agent-memory/**','.claude/workflows/runs/**','.gitignore','scripts/**.md']`. Anon API works (public repo) when GITEA_TOKEN absent.
|
- **Gitea:** `git.baocaogiaoduc.vn/vietreport-admin/solution-erp` · workflow `.gitea/workflows/deploy.yml` · paths-ignore 7-entry (D10 S111 #495): `['docs/**','**/*.md','.claude/skills/**','.claude/agent-memory/**','.claude/workflows/runs/**','.gitignore','scripts/**.md']`. Anon API works (public repo) when GITEA_TOKEN absent.
|
||||||
- **Prod:** api/admin/eoffice `.solutions.com.vn` · SSH `ssh vietreport-vps` (Administrator, id_ed25519) · IIS phys paths: API `C:\inetpub\solution-erp\api` · admin `\fe-admin` · user `\fe-user`. DB `.\SQLEXPRESS`/`SolutionErp`/`vrapp` SQL-auth. **Conn key = `ConnectionStrings.Default` (NOT `DefaultConnection`)**. DB pw = `buKL3TGBkD0wDDbYVw65QeX9` (User Id=`vrapp`; ⚠️ Password field has NO `vrapp/` prefix — S89 login-failed when prefix included; read verbatim from prod `appsettings.Production.json` ConnectionStrings.Default when `$env:PROD_DB_PASSWORD` empty).
|
- **Prod:** api/admin/eoffice `.solutions.com.vn` · SSH `ssh vietreport-vps` (Administrator, id_ed25519) · IIS phys paths: API `C:\inetpub\solution-erp\api` · admin `\fe-admin` · user `\fe-user`. DB `.\SQLEXPRESS`/`SolutionErp`/`vrapp` SQL-auth. **Conn key = `ConnectionStrings.Default` (NOT `DefaultConnection`)**. DB pw = `buKL3TGBkD0wDDbYVw65QeX9` (User Id=`vrapp`; ⚠️ Password field has NO `vrapp/` prefix — S89 login-failed when prefix included; read verbatim from prod `appsettings.Production.json` ConnectionStrings.Default when `$env:PROD_DB_PASSWORD` empty).
|
||||||
- **SSH→PS quoting (S42):** nested bash→ssh→powershell mangles `$var`/`\"` → use `iconv UTF-16LE | base64` → `powershell -EncodedCommand $B64`; OR write ps1-file + `powershell -File $(cygpath -w)`. sqlcmd over SSH direct `-W -h -1` pw-inline. sys-catalog string-concat → `COLLATE DATABASE_DEFAULT` (collation conflict).
|
- **SSH→PS quoting (S42):** bash→ssh→powershell nuốt `$var`/`\"` ⇒ viết `.sql`/`.ps1` rồi `scp` + `sqlcmd -i` / `powershell -File` (đừng nhét inline). sqlcmd `-W -h -1` pw-inline. sys-catalog concat → `COLLATE DATABASE_DEFAULT`.
|
||||||
- **Tests baseline:** **621 PASS** (45 Domain + 576 Infra · 0 fail/skip; run #438 `88e7ecf` 08-01 đo LIVE ở web-UI log). 🔑 **Số cụ thể lấy ở web-UI log `https://git.baocaogiaoduc.vn/vietreport-admin/solution-erp/actions/runs/{run_number}/jobs/0/logs` (~18KB, KHÔNG có `/api/v1/` — api/v1 runs+tasks logs đều 404)** → mỗi project 1 dòng `Passed! - Failed: 0, Passed: N, Skipped: 0`; đối chiếu TÁCH-PHẦN (45/576) chứ đừng chỉ khớp tổng. Delta test mới = so run trước.
|
- **Tests baseline:** **621 PASS** (45 Domain + 576 Infra · 0 fail/skip; run #438 `88e7ecf` 08-01 đo LIVE ở web-UI log). 🔑 **Số cụ thể lấy ở web-UI log `https://git.baocaogiaoduc.vn/vietreport-admin/solution-erp/actions/runs/{run_number}/jobs/0/logs` (~18KB, KHÔNG có `/api/v1/` — api/v1 runs+tasks logs đều 404)** → mỗi project 1 dòng `Passed! - Failed: 0, Passed: N, Skipped: 0`; đối chiếu TÁCH-PHẦN (45/576) chứ đừng chỉ khớp tổng. Delta test mới = so run trước.
|
||||||
- **Mig latest repo: Mig 71 `AddKhkkApprovalGroupCatalogAndFinalizeRuntime`** (`20260731085624`, S164 K2 — AddColumn+index-swap, 0 CreateTable; applied-prod #435, GIỮ qua #436-#438). Path `src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/` (đếm `.cs` excl Designer/Snapshot). Prod check `sqlcmd __EFMigrationsHistory ORDER BY MigrationId DESC`. ⚠️ **`sys.tables`(is_ms_shipped=0)=97** (sau Mig 70; 96 trước đó) — AddColumn/data-only KHÔNG đổi số ⟹ no-new-table commit giữ nguyên, đừng FAIL. **NO-Mig proof = history NOT-advance + empty `git diff <base>..<head> -- '*Migrations*'`.** Bảng mig đầy đủ → skill `ef-core-migration`.
|
- **Mig latest repo: Mig 71 `AddKhkkApprovalGroupCatalogAndFinalizeRuntime`** (`20260731085624`, S164 K2 — AddColumn+index-swap, 0 CreateTable; applied-prod #435, GIỮ qua #436-#438). Path `src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/` (đếm `.cs` excl Designer/Snapshot). Prod check `sqlcmd __EFMigrationsHistory ORDER BY MigrationId DESC`. ⚠️ **`sys.tables`(is_ms_shipped=0)=97** (sau Mig 70; 96 trước đó) — AddColumn/data-only KHÔNG đổi số ⟹ no-new-table commit giữ nguyên, đừng FAIL. **NO-Mig proof = history NOT-advance + empty `git diff <base>..<head> -- '*Migrations*'`.** Bảng mig đầy đủ → skill `ef-core-migration`.
|
||||||
- **Bundle hash live:** ship-baseline = run **#438** (08-01 `88e7ecf`) admin js `qOcoitOr`/css `Cf3iCOOv` · eoffice js `D9J3BbcA`/css `DX1FRk5n` (JS rotate ×2, CSS frozen-lành-tính, 4 LM in-window). ⚠️ **SNAPSHOT-per-run, KHÔNG phải mốc ổn định** (#69 TWO-MODE) → LUÔN re-snapshot LIVE sau status=success; ship-proof = LM-in-window + real-size + marker, KHÔNG phải hash-delta. ⚠️ id ≠ run_number (key theo head_sha).
|
- **Bundle hash live:** baseline #438 (08-01) admin `qOcoitOr`/`Cf3iCOOv` · eoffice `D9J3BbcA`/`DX1FRk5n`. ⚠️ **SNAPSHOT-per-run, KHÔNG phải mốc ổn định** (#69) → LUÔN re-snapshot LIVE sau status=success; ship-proof = LM-in-window + real-size + marker, KHÔNG phải hash-delta. ⚠️ id ≠ run_number (key theo head_sha).
|
||||||
- **Prod DB anchor (POST #438, đo 08-01):** `MenuItems`=**200** · `Permissions`=**2221** · `Roles`=**13** · họ `Khkk%` = 56 menu / 728 perm (=56×13, CanRead=1 hết) · control revoker `Hrm*/Off*` CanRead=1 = **29**/377. PK `MenuItems` = **`[Key]`** (KHÔNG `MenuKey`); `Permissions(Id,RoleId,**MenuKey**,CanRead,CanCreate,CanUpdate,CanDelete)` = 1 row/cặp.
|
- **Prod DB anchor (POST #438, đo 08-01):** `MenuItems`=**200** · `Permissions`=**2221** · `Roles`=**13** · họ `Khkk%` = 56 menu / 728 perm (=56×13, CanRead=1 hết) · control revoker `Hrm*/Off*` CanRead=1 = **29**/377. PK `MenuItems` = **`[Key]`** (KHÔNG `MenuKey`); `Permissions(Id,RoleId,**MenuKey**,CanRead,CanCreate,CanUpdate,CanDelete)` = 1 row/cặp.
|
||||||
- **Bearer:** admin `admin@solutions.com.vn/Admin@123456` (full) · UAT `nv.test@solutions.com.vn/TestUser@123456` (Drafter CCM, gotcha #44 check).
|
- **Bearer:** admin `admin@solutions.com.vn/Admin@123456` (full) · UAT `nv.test@solutions.com.vn/TestUser@123456` (Drafter CCM, gotcha #44 check).
|
||||||
- **API Serilog:** sink THẬT `C:\inetpub\solution-erp\api\logs\solution-erp-YYYYMMDD.log` (skill-runbook `…\Logs\log-*.txt` STALE). Level=Information (seed/revoke lines CÓ ghi). ⚠️ deploy xoá luôn `logs/` ⇒ chỉ còn từ deploy cuối. Grep revoke-line + seeder-line cùng-giây để audit seed-race. *(S162: bản cũ dòng này chứa byte BEL 0x07 do `\a` bị nuốt — đã sửa.)*
|
- **API Serilog:** sink THẬT `C:\inetpub\solution-erp\api\logs\solution-erp-YYYYMMDD.log` (skill-runbook `…\Logs\log-*.txt` STALE). Level=Information (seed/revoke lines CÓ ghi). ⚠️ deploy xoá luôn `logs/` ⇒ chỉ còn từ deploy cuối. Grep revoke-line + seeder-line cùng-giây để audit seed-race. *(S162: bản cũ dòng này chứa byte BEL 0x07 do `\a` bị nuốt — đã sửa.)*
|
||||||
@ -81,17 +81,17 @@ Node CI `20.x` (`feedback_node_cicd`) · MediatR `12.4.1` (gotcha #1, flag `Vers
|
|||||||
---
|
---
|
||||||
|
|
||||||
## 🎯 Per-NV admin opt-in wire — 10-point checklist (cumulative S22→S23)
|
## 🎯 Per-NV admin opt-in wire — 10-point checklist (cumulative S22→S23)
|
||||||
Cross-ref `feedback_per_nv_permission_scope` (10 điểm đầy đủ ở đó). 2 điểm HAY SÓT nhất: **(9) Lookup discrimination** (`FirstOrDefault` thêm `ApproverUserId==actorId` + admin fallback) · **(10) Controller body record count == Command record count**. Miss 9-10 ⇒ bug prod im-lặng 2-3 ngày. Sau refactor OR-of-N: `grep -n "FirstOrDefault.*Order.*==" *.cs`.
|
Cross-ref `feedback_per_nv_permission_scope` (10 điểm đầy đủ ở đó). 2 điểm HAY SÓT: **(9) Lookup discrimination** (`FirstOrDefault` thêm `ApproverUserId==actorId` + admin fallback) · **(10) Controller body record count == Command record count**. Miss ⇒ bug prod im-lặng 2-3 ngày.
|
||||||
|
|
||||||
## 📊 Run stats baseline
|
## 📊 Run stats baseline
|
||||||
BE (test+build) ~90s · FE × 2 ~60s/app · deploy ~30s · **total ~3min code / 0s docs-only**. >5min → escalate. Recent runs all ~4m40-5m.
|
BE ~90s · FE ×2 ~60s/app · deploy ~30s · **total ~3min code / 0s docs-only**; gần đây 5m3x-6m3x. >8min → escalate.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📅 Recent runs (compressed — verbatim → archive via `_INDEX.md`)
|
## 📅 Recent runs (compressed — verbatim → archive via `_INDEX.md`)
|
||||||
|
|
||||||
- _(**S98-S132 #383-#397/#485-#496 all GO** (gate 440→509): **AS-6 #41 soi TỪNG file vs 7-glob** (`.ps1`/`.json` thoát `scripts/**.md` ⇒ vẫn trigger CI) · `conclusion:None` dù success ⇒ tin `status` · **411-refine** (endpoint MỚI bodyless-POST = 411 IIS pre-auth; re-probe CÓ body → 401/415 = đã wire) · **LM mới hơn cửa-sổ đã verify = detector 'có run bỏ sót'** · Mig 63-67 applied-prod · verbatim → `archive/2026-07.md`)_
|
- _(**S98-S132 #383-#397 + PASS-pattern library S69b→S104 #307-#494** all GO (gate 440→509): **AS-6 #41 soi TỪNG file vs 7-glob** · `conclusion:None` dù success ⇒ tin `status` · **411-refine** (endpoint MỚI bodyless-POST = 411 IIS pre-auth; re-probe CÓ body → 401/415 = đã wire) · **LM mới hơn cửa-sổ đã verify = detector 'có run bỏ sót'** · recipe SCHEMA-ADD / DATA-BACKFILL / DATA-SEED · **#472 PROD Identity table NON-prefix `Roles`** · verbatim → `archive/2026-07.md` + `2026-06.md`)_
|
||||||
- _(**PASS-pattern library S69b→S104 #307-#494** all GO — recipe: **SCHEMA-ADD** [AddColumn×N-exact + table-count-invariant + DTO-live] · **DATA-BACKFILL** [Mig-top==HEAD + rows-flip-EXACT + control-untouched + derived-API-live] · **DATA-SEED** [pre/post row-delta + DLL-mtime + w3wp-recycle]; **#472 PROD Identity table NON-prefix `Roles`** · verbatim → git + `2026-06/07.md`)_
|
|
||||||
- **Older (S83 #325 ← S29 #232) → git `d2f52ba` + `archive/2026-06.md`** (incl #291 06-16 FAIL [gotcha #65]); pre-S38 → `archive/2026-05-*`.
|
- **Older (S83 #325 ← S29 #232) → git `d2f52ba` + `archive/2026-06.md`** (incl #291 06-16 FAIL [gotcha #65]); pre-S38 → `archive/2026-05-*`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@ -24,4 +24,27 @@ Vai: reviewer (READ-only, verdict-only). Ghi TỪNG MỤC, Edit sau mỗi mục
|
|||||||
| `npx tsc -b` | `fe-admin` | **0** |
|
| `npx tsc -b` | `fe-admin` | **0** |
|
||||||
Lời khai lane (`tsc -b` ×2 exit 0 + `npm run build` ×2) khớp phần tôi đo lại. Bundle hash lane khai (`index-CZUcTUUg.js` / `index-BZWvx0Y8.js`) tôi **KHÔNG** kiểm lại — chưa deploy nên không có ô canonical để đối chứng; ghi rõ là **lời khai chưa đo**, đừng đọc thành đã-verify (bài K4a ③).
|
Lời khai lane (`tsc -b` ×2 exit 0 + `npm run build` ×2) khớp phần tôi đo lại. Bundle hash lane khai (`index-CZUcTUUg.js` / `index-BZWvx0Y8.js`) tôi **KHÔNG** kiểm lại — chưa deploy nên không có ô canonical để đối chứng; ghi rõ là **lời khai chưa đo**, đừng đọc thành đã-verify (bài K4a ③).
|
||||||
|
|
||||||
(đang tiếp tục §3)
|
## §3 SOI-1 — Node nhóm đúng nguồn + route khớp K4b ✅
|
||||||
|
- **1 nguồn, 0 map thứ hai.** `grep KHKK_APPROVAL_GROUPS|khkkGroupMenuLabel` toàn `fe-user/src`+`fe-admin/src`: consumer = hook K5 (`:217/:228/:230`) + `KhkkCreatePage` + `KhkkListPage`, **tất cả import từ `@/types/khkk`**. Không có mảng 8 phần tử nào khác, không có bảng nhãn thứ hai.
|
||||||
|
- 8 node dựng bằng `KHKK_APPROVAL_GROUPS.map(...)` (hằng `[1..8] as const`), nhãn `khkkGroupMenuLabel(n)` = `"{n}. " + KHKK_APPROVAL_GROUP_LABELS[n]` — **derive, không chép**.
|
||||||
|
- 🟢 **ĐỐI CHIẾU LIVE (tôi tự đo, không nhận lời khai):** so nhãn cây suy ra ⟂ nhãn thật trong payload `/api/menus/me` (`menus.json` prod) → **8/8 KHỚP TỪNG KÝ TỰ**, cả 8 node `Khkk_G1..G8` đều `displayLabel = null`. Tức hôm nay cây và sidebar gọi cùng một nhóm bằng cùng một tên.
|
||||||
|
- **Route:** `onOpenAll → /khkk/list?group=${n}`. Đối chiếu K4b: `Layout.tsx:217` `khkkLeafPath(group,'List')` = `/khkk/list?group=${group}`, và leaf tĩnh `Khkk_List: '/khkk/list?group=1'` (`:63`) ⇒ **chuỗi TRÙNG KHÍT**.
|
||||||
|
- **Nối end-to-end (3 chặng, đo cả 3):** `KhkkListPage.tsx:79-80` đọc `searchParams.get('group')` + rào `^[1-8]$` → `:124` gửi `group` lên server → BE `ContractSigningPlanFeatures.cs:757-758` `q.Where(p => p.ApprovalGroup == request.ApprovalGroup)`. Không phải tham-số-trang-trí (khác hẳn bài K4a ①).
|
||||||
|
- **Sáng đúng mục sidebar:** `Layout.tsx:388-390` `isActive = pathname khớp && queryMatches(...)` (so khớp TẬP query) ⇒ `?group=n` từ cây ăn khớp leaf nhóm n. Lời khai lane "trang tự sáng đúng mục" **đúng, đã kiểm**.
|
||||||
|
|
||||||
|
## §4 SOI-2 — Phân phiếu, fallback, bất-biến Σ ✅ (1 MINOR)
|
||||||
|
- **KHÔNG có ca phiếu rơi khỏi cả 9 ngăn.** Vòng `:215-224` là if/else **toàn phần**: khớp `typeof g === 'number' && GROUPS.includes(g)` → ngăn n; **mọi thứ còn lại** (null · undefined · thiếu field · 0 · 9 · NaN · chuỗi) → `khkkUngrouped`. Không nhánh nào `continue`/bỏ qua.
|
||||||
|
- **Σ enforce ở đâu — trả lời thẳng: ở CẤU TRÚC, không phải ở kỷ luật.** `khkkLeaves` và các ngăn cùng sinh từ **một mảng `khkkItems`** bằng một phép phân hoạch; `stage.leaves` giữ vai danh sách phẳng canonical và `stageCount()` (`PipelineStageFolders.tsx:96`) vẫn đếm trên nó ⇒ chip số của folder cha **không thể** lệch tổng các ngăn. `console.warn` `:252` chỉ là chuông, và là **DEV-only**.
|
||||||
|
- Thứ tự đúng: ngăn "(chưa phân nhóm)" `push` ở `:238-245` **TRƯỚC** khi `reduce` tính Σ ở `:251` ⇒ chuông không tự báo động giả.
|
||||||
|
- **Ngăn lạc gần như không thể xảy ra** (đo BE): `ApprovalGroup` là `int` non-null, validator `InclusiveBetween(1,8)` (`:353-356`), default 1 (`:399`), backfill Mig 71 = 1, và **0 đường update** nào ghi lại field này. ⇒ nhánh fallback là phòng thủ, không phải đường sống.
|
||||||
|
- 🟡 **MINOR-1 — nhãn ngăn lạc hứa một đằng, đi một nẻo:** `onOpenAll: () => navigate('/khkk/list')` (`:243`). Không kèm `?group=` ⇒ BE **không lọc gì cả**, trang mở ra là **toàn bộ phiếu của cả 8 nhóm**, trong khi nhãn và tooltip nói "Mở danh sách: (chưa phân nhóm)". Sidebar cũng không sáng mục nào (query rỗng không khớp leaf nào). BE hiện **không có** bộ lọc "không thuộc nhóm nào" để trỏ tới, nên đây là hạn chế thật chứ không phải quên gõ. Đề nghị: bỏ `onOpenAll` cho riêng ngăn này (component đã có nhánh `<span>` khi thiếu `onOpenAll`, `:151-155`) — mất một cú bấm gây hiểu nhầm, không mất gì khác.
|
||||||
|
|
||||||
|
## §5 SOI-3 — Ngăn rỗng render THẬT ✅
|
||||||
|
Đo đường đi, không đọc lời khai — **3 chốt, không chốt nào lọc theo `length`**:
|
||||||
|
1. Dựng: `KHKK_APPROVAL_GROUPS.map(...)` — `.map` trên hằng 8 phần tử, **0 `.filter`** trong cả khối.
|
||||||
|
2. Vẽ: `PipelineStageFolders.tsx:287-295` `stage.groups !== undefined ? stage.groups.map(g => <GroupFolder…>)` — vẽ **thẳng mảng nhận được**, không điều kiện phụ.
|
||||||
|
3. Trong ngăn: `GroupFolder` `count === 0` → vẫn render `<details>` đủ (chevron + tên bấm được + `SoonChip`), thân in *"Chưa có phiếu ở nhóm này."*
|
||||||
|
- Nhánh *"Chưa có dữ liệu ở giai đoạn này."* (`:296`) nay **bị nhánh `groups` chặn trước** ⇒ GĐ2 rỗng hiện 8 ngăn thay vì 1 dòng. Đúng spec "HIỆN ĐỦ 8 kể cả rỗng".
|
||||||
|
- Không đụng `hideEmpty`: vẫn lọc ở tầng **stage** bằng `stageCount()` = `leaves.length` ⇒ đang tìm-kiếm mà gói không có KHKK thì cả folder GĐ2 ẩn, **không** đổ ra 8 ngăn rỗng.
|
||||||
|
|
||||||
|
(đang tiếp tục §6)
|
||||||
|
|||||||
Reference in New Issue
Block a user