wal: flush 20260801T1211
This commit is contained in:
@ -24,9 +24,13 @@ chain:
|
||||
control-âm 3-key-bịa MISS · C1 N=15 tái-lập trên PROD SỐNG · C2 0-diff control-dương · C4 3-tầng
|
||||
+ curl prod 200 ×2 · tsc/build tái-lập trùng số lane) · MAJOR-1 3-số-đếm-comment FIXED trước commit
|
||||
("28 leaf" đã sai lúc land — luật #84) + MINOR-3 câu-xếp-nhánh · FLAG-2 lane ĐÓNG bằng /api/menus/me
|
||||
prod (10 key AwV2*, 0 AwV2_Contract) · FLAG-3 GIỮ nhánh-chết (đảo 2 lần) · cicd-K6 ĐANG nền
|
||||
(sub-cicd-verify-k6.md). 🔴 CLARIFY-2 CHỜ ANH: Hrm/Off/Personal isVisible=false bên eOffice +
|
||||
K6 ẩn bên admin ⇒ 3 module MẤT LỐI VÀO MENU Ở CẢ 2 APP (route/quyền còn nguyên — display-only)
|
||||
prod (10 key AwV2*, 0 AwV2_Contract) · FLAG-3 GIỮ nhánh-chết (đảo 2 lần) · cicd-K6 PASS 5/5
|
||||
return-CLEAN (run #440 6m29s · gate 622 delta-0 · mig 71 · admin ROTATE Ig1R5tAh +168B · eoffice
|
||||
FROZEN-lành-tính [CI-log cùng-hash + cmp byte-identical] · marker 12-key +1 differential + marker-ÂM
|
||||
route /purchase-evaluations 68→68 · /api/menus vẫn trả 12-key-ẩn = display-only đo bằng số · bài mới:
|
||||
Vite-8-minify-backtick làm regex-marker FAIL-OAN ×2 → luật grep -F, vai đã ghi MEMORY —
|
||||
sub-cicd-verify-k6.md). ✅ CLARIFY-2 CHỐT @S167 (anh: "Admin (Site) để nguyên"): giữ nguyên cả 2 —
|
||||
Hrm/Off/Personal tạm 0 lối menu 2 app (route/quyền còn nguyên; bật lại = màn Menu eOffice, 0 code)
|
||||
[!] K7 — 2 lane ĐANG nền @S167 (spec-cum3 ② vá 1/3/4/6/16): BE = grant Contracts.Create
|
||||
[câu III default Drafter+Procurement+Admin, comment chờ-ratify] + hội-2-khóa endpoint + guard 2-cờ +
|
||||
Line.ContractId-ghi-luôn + DTO expose + nạp Code/SLA/pin (sub-implbe-k7.md) ⟂ FE = nút gate hội-2-khóa
|
||||
@ -34,8 +38,11 @@ chain:
|
||||
Cửa chết → ĐỌC 2 FILE ĐÓ trước, đừng spawn lại. CÒN SAU LANE: test-lane 8+2-authz+idempotency →
|
||||
gate-K7 → commit → cicd
|
||||
[ ] K8-prep form-14-người fail-closed + gói SOLUTION-PE-{n} + QĐ9 đo-2-nhánh + ZZTEST + rollback-theo-ID
|
||||
[ ] closeout-nợ: diary vai (C2) · tally #53 CHỐT derive-from-body (S166 nháp 8 lượt + cicd-K5K4C chết-return
|
||||
artifact-trọn = 9-cận-dưới / 0 mất công) · nhãn D MIND · STATUS row S163-S167 · spec-cụm1 vá-3 đính-chính
|
||||
[ ] closeout-nợ: 🆕 slot(57) anh chốt "AUTO" @S167 ⇒ em chọn SPLIT _INDEX (không nâng cap) + wire
|
||||
auto-rotate khi chạm ngưỡng — LÀM SAU khi cicd-K6 trả (tránh đua file archive cicd-monitor) ·
|
||||
🆕 G-1 RATIFIED @S167 (anh: "Đồng ý. Cứ theo khuôn PE") ⇒ grep đóng mọi comment/ghi-chú "chờ ratify"
|
||||
SAU khi lane FE-K7 trả (tránh đua fe-*) · diary vai (C2, gồm ctx-verifier S167 lead-seed) · tally #53
|
||||
CHỐT derive-from-body (S166 nháp 8 lượt + cicd-K5K4C + gate-K6-garble = 10-cận-dưới / 0 mất công) · nhãn D MIND · STATUS row S163-S167 · spec-cụm1 vá-3 đính-chính
|
||||
PUT-Conflict · lỗi-tự-bắt 607/609 · H24-DEEP 11-FLAG-SÀN resolve (sub-lead-stale-deep-S166.md: F-1
|
||||
STATUS-canonical-5-row + canonical-poison :465-472 · F-2 bundle :479 · F-7 slot(57) _INDEX 101,23% ·
|
||||
F-8 slot(59) ring2-BÁC-một-phần số-mới-120.633B-vẫn-dưới-sàn · F-4 CLAUDE-mig-70/71 · F-5/6/9/10/11;
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
# 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** (#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]`
|
||||
- **S167 (08-01) `1c6d77a` K6 admin thu-gọn menu `ADMIN_HIDDEN_KEYS` 12-key (FE-admin 1 file, 0-mig) — PASS 5/5** (#440 6m29s · gate **622=45+577** delta 0 · Mig GIỮ 71 · tables 97 · menu 200/perm 2221 đứng im). 🔴 **BÀI TO NHẤT: 0-hit marker SAI 2 LẦN LIÊN TIẾP do CƠ-CHẾ REGEX, cả 2 lần trông y hệt "code không ship" ⇒ suýt FAIL-OAN.** (a) **rolldown/Vite 8 minify string → BACKTICK** (`new Set([\`PurchaseEvaluations\`,…])`), regex neo `["']` không bao giờ khớp; (b) vá xong truyền pattern-chứa-backtick qua biến trong `"…"` → shell nuốt, `grep -cE` gãy (`Unmatched (`) trả **0 gần như im lặng** trong vòng lặp. ⇒ **LUẬT: marker chuỗi trên bundle dùng `grep -F` fixed-string + LUÔN trích byte thật `.{90}X.{90}` TRƯỚC khi kết luận vắng-mặt.** 🔑 **KỸ THUẬT MỚI — COUNT-DELTA**: literal đã có từ K4a ⇒ *có/không* = 0 bit, nhưng *số lần xuất hiện* PRE→POST đo được: **12/12 key đều +1**; ngoại lệ duy nhất `Workflows` **+2** giải-thích-được (substring ⊂ `PeWorkflows`) ⇒ thành kiểm-tra-nhất-quán MIỄN PHÍ. 🔑 **LOG CI IN THẲNG `dist/assets/index-<hash>.js <size>`** = nhân chứng THỨ 4 độc lập curl, khớp LM prod **tới từng giây** ⇒ rẻ nhất để phân biệt **FROZEN-lành-tính** (eoffice: LM 11:14→**12:00:41** đổi + log in CÙNG hash `DFOCQ2ks` = rebuild deterministic) với bundle-kẹt (LM cũ). 🔑 **2-TẦNG #82 đo bằng SỐ**: `/api/menus` trả **12/12 key bị ẩn** ⇒ ẩn client KHÔNG thu quyền. Marker-ÂM display-only: route `/purchase-evaluations` **68→68** nguyên vẹn. Evidence `runs/2026-07-31-S164-4gd-khkk-fanout/sub-cicd-verify-k6.md`. Tag `[run-440, 0-hit-gia-2-lan, grep-F, count-delta, frozen-lanh-tinh, log-CI-nhan-chung-4]`
|
||||
|
||||
- _(**08-01 `e202393` S166 K5+K4c FE-only — PASS 5/5**: #439 6m16s, gate **622=45+577** (+1 = đúng 1 Fact mới). 🔑 marker differential 4/4 element-wise (K5 vào CẢ 2 app · K4c CHỈ eoffice) + Δsize cùng chiều = nhân chứng ⟂ grep. 🔴 **recipe đếm mig trong MEMORY có FALSE-NEGATIVE**: `grep -v Snapshot` ăn nhầm `AddPeApprovedBudgetSnapshot` (Mig 67) → phải neo đuôi `Designer\.cs$|ModelSnapshot\.cs$`; ưu tiên **set-diff repo↔prod 2 chiều** thay khớp-tổng. ⚠️ **bar "FE route 200" KHÔNG CÓ RĂNG** với SPA-fallback (POS ≡ NEG byte-identical 876B). verbatim → `runs/…/sub-cicd-verify-k5-k4c.md`)_
|
||||
|
||||
- _(**08-01 `88e7ecf` S166 K4a+K4b — PASS 7/7**: #438, gate 621=45+576, menu 151→**200**, perm 1584→**2221**. 🔑 **PHỦ 2 RUN 1 LƯỢT ⇒ delta khép kín**. 🔑 **MỐC PRE ĐỀ BÀI SAI, bắt bằng ĐO** (lead giao 143⇒192, đo LIVE ra 151 → truy phạm-vi thay vì hô drift) — **mốc PRE lão hoá sau MỖI deploy**. 🔑 phân rã 49+588 khớp cơ-chế 2-site skip-existing (588=49×12 role). ⚠️ **PERM row ≠ policy**. 🔑 A5 restart ×2 thật + **soi ĐIỀU KIỆN log trong source TRƯỚC khi đọc im-lặng** (`if (added>0)`). 🔴 **marker lead gợi ý BỊ BÁC BẰNG ĐO** (`Kết cấu` có sẵn ⇒ 1/1 = 0 bit). verbatim → `runs/…/sub-cicd-verify-k4ab.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`)_
|
||||
|
||||
@ -50,7 +54,7 @@ cicd-lane trả nhầm slice transcript thay verdict — **tao TÁI PHẠM dai n
|
||||
- **Stage 0 RAG infra:** `Get-Service Qdrant` Running + `http://localhost:6333/healthz`. Collection `proj_solution_erp`.
|
||||
- **Stage 1 Push+filter:** `git log -1 --format='%H %s'` + `git log origin/main..HEAD` empty + diff vs paths-ignore (docs-only → SKIPPED-DOCS return).
|
||||
- **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 **622 PASS** (45 Domain + 577 Infra; run #440 `1c6d77a` 08-01; climb 622/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.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.
|
||||
- enum cần input SỐ (no `JsonStringEnumConverter`). INFRASTRUCTURE seed ungated / DEMO seed gated → #51.
|
||||
@ -68,10 +72,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.
|
||||
- **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):** 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.
|
||||
- **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:** 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.
|
||||
- **Tests baseline:** **622 PASS** (45 Domain + 577 Infra · 0 fail/skip; run #440 `1c6d77a` 08-01 đo LIVE ở web-UI log). ⚠️ **Flaky đã khai** `DeleteContractSigningPlan_SoftDeletes` (1/12 theo commit-msg K4c) — #439+#440 đều XANH; nếu đỏ run sau, đối chiếu 2 mốc này trước khi hô hồi-quy. 🔑 **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-#440). Path `src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/`. 🔴 **Đếm phải NEO ĐUÔI**: `grep -vE 'Designer\.cs$|ModelSnapshot\.cs$'` — recipe cũ `grep -v Snapshot` **ăn nhầm** `AddPeApprovedBudgetSnapshot` (Mig 67 có chữ "Snapshot" trong TÊN) ⇒ đếm hụt 1 im lặng. Mạnh hơn khớp-tổng = **set-diff repo↔prod 2 chiều**. 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:** baseline #440 (08-01) admin `Ig1R5tAh`/`DqXm5Hpn` · eoffice `DFOCQ2ks`/`DlN3yZ5L`. 🔑 **Ship-proof rẻ nhất = so LM prod với dòng `dist/assets/index-<hash>.js <size>` IN TRONG LOG CI** (khớp tới từng giây ⇒ tách FROZEN-lành-tính khỏi bundle-kẹt). ⚠️ marker chuỗi: **rolldown/Vite 8 minify ra BACKTICK** không phải `"`/`'` → dùng `grep -F`. ⚠️ **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, re-verify đứng im ở #440 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).
|
||||
- **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.)*
|
||||
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
CICD-K6: (đang đo — chưa chốt)
|
||||
CICD-K6: PASS — 5/5
|
||||
|
||||
# cicd-monitor — verify deploy K6 (S167, 2026-08-01)
|
||||
|
||||
Push range `e202393..1c6d77a` (2 commit: `d82c5a8` wal-flush + `1c6d77a` K6). ĐÚNG 1 file code `fe-admin/src/components/Layout.tsx`. 0-mig · fe-user 0-diff · BE 0-diff.
|
||||
Push range `e202393..1c6d77a` (2 commit: `d82c5a8` wal-flush + `1c6d77a` K6). ĐÚNG 1 file code `fe-admin/src/components/Layout.tsx`. 0-mig · fe-user 0-diff · BE 0-diff (đo: `git diff --name-only e202393..1c6d77a -- fe-user/ src/Backend/ tests/` = **0 file**).
|
||||
Run **#440** (task id 553) — **success**, 11:54:38 → 12:01:07 (**6m29s**, trong dải quen 5m3x–6m3x).
|
||||
Artifact ghi TỪNG MỤC ngay khi có bằng chứng (luật #53: ghi-đĩa-TRONG-lúc-làm; return chỉ là tóm tắt).
|
||||
|
||||
---
|
||||
@ -16,29 +17,129 @@ Cửa sổ vàng: bắt được #440 ở `running` lúc **11:55:43** ⇒ bundle
|
||||
| admin | `index-C2FhHCNT.js` | `index-DqXm5Hpn.css` (86.495) | **1.758.685** | 01 Aug 04:13:05 GMT = **11:13:05 +07** | application/javascript |
|
||||
| eoffice | `index-DFOCQ2ks.js` | `index-DlN3yZ5L.css` (92.233) | **1.676.752** | 01 Aug 04:14:05 GMT = **11:14:05 +07** | application/javascript |
|
||||
|
||||
🔑 **PRE tự chứng là bundle #439 bằng LM, không chỉ bằng hash**: LM 11:13:05 / 11:14:05 nằm TRỌN trong cửa-sổ deploy #439 (11:08:17 → 11:14:33). Trùng baseline đề bài — nhưng trùng đó là KẾT QUẢ ĐO, không phải giả-định đầu vào.
|
||||
Content-Type `application/javascript` + ~1,7MB ⇒ không dính bẫy SPA-fallback (`text/html` ~900B). File tải về khớp byte-for-byte `Content-Length` (1.758.685 / 1.676.752).
|
||||
🔑 **PRE tự chứng là bundle #439 bằng LM, không chỉ bằng hash**: LM 11:13:05 / 11:14:05 nằm TRỌN trong cửa-sổ deploy #439 (11:08:17 → 11:14:33). Trùng baseline đề bài — nhưng trùng đó là KẾT QUẢ ĐO, không phải giả-định đầu vào (bài "mốc PRE lão hoá sau MỖI deploy").
|
||||
Content-Type `application/javascript` + ~1,7MB ⇒ không dính bẫy SPA-fallback (`text/html` ~900B). File tải về khớp byte-for-byte `Content-Length`.
|
||||
|
||||
## M0-bis — Chọn byte-marker CÓ RĂNG (verify 0-hit PRE TRƯỚC khi phong)
|
||||
|
||||
Đề bài cảnh báo đúng: K6 = 12 key literal **đã tồn tại từ K4a** + comment sẽ bị Vite strip ⇒ marker ngây thơ = 0 bit. Đo PRE xác nhận:
|
||||
Đề bài cảnh báo đúng: K6 = 12 key literal **đã có từ K4a** + comment sẽ bị Vite strip ⇒ marker ngây thơ = 0 bit. Đo PRE xác nhận từng cái:
|
||||
|
||||
❌ **LOẠI marker "key đơn lẻ"** — PRE admin đã có sẵn: `PurchaseEvaluations` **4** · `KeHoachKyKet` **1** · `HopDongCung` **1** · `PeWorkflows` **3** ⇒ POST có hit cũng **0 bit**.
|
||||
❌ **LOẠI marker tên biến** `ADMIN_HIDDEN_KEYS` — biến module-scope, minifier đổi tên (bài K3 `earlyFinalizeLocked`).
|
||||
❌ **LOẠI marker comment** — toàn bộ khối lineage 2-đảo là `//` ⇒ Vite strip.
|
||||
❌ **LOẠI "key đơn lẻ"** — PRE admin đã có sẵn: `PurchaseEvaluations` **4** · `KeHoachKyKet` **1** · `HopDongCung` **1** · `PeWorkflows` **3** ⇒ POST có hit cũng **0 bit**.
|
||||
❌ **LOẠI tên biến `ADMIN_HIDDEN_KEYS`** — minifier đổi tên; đo POST: source-name **0 hit**, thực tế thành `var IW=new Set([...])`. (Bài K3 `earlyFinalizeLocked` tái xác nhận.)
|
||||
❌ **LOẠI comment lineage** — `NƠI-CONFIG` / `thắng mốc trước`: PRE **0** POST **0** ⇒ Vite strip đúng như dự đoán.
|
||||
|
||||
✅ **CHỌN marker HỢP-CHẤT: TRẬT TỰ KỀ NHAU của literal trong `new Set([...])`.** Từng literal thì cũ, nhưng *thứ tự kề nhau* chỉ sinh ra khi Set mới được minify. Đo PRE admin (regex chấp cả `"` lẫn `'`):
|
||||
✅ **CHỌN marker HỢP-CHẤT: TRẬT TỰ KỀ NHAU của 12 literal trong `new Set([...])`.** Từng literal thì cũ, nhưng *thứ tự kề nhau* chỉ sinh ra khi Set mới được minify. Lineage source: `git grep ADMIN_HIDDEN_KEYS` @`e202393` = **0 hit** (exit 1) · @`1c6d77a` = **2 hit** ⇒ marker chưa tồn tại ở mốc PRE.
|
||||
✅ **Marker ÂM (K6 là DISPLAY-ONLY, không gỡ route):** `/purchase-evaluations`, `/khkk/list`, `/suppliers` PHẢI CÒN trong bundle admin POST.
|
||||
✅ **Differential:** eoffice kỳ vọng FROZEN (fe-user 0-diff) ⇒ marker phải **0** ở eoffice cả PRE lẫn POST.
|
||||
|
||||
| # | Marker (adjacency) | PRE admin |
|
||||
|---|---|---|
|
||||
| A | `PurchaseEvaluations","KeHoachKyKet","Contracts` (3-key) | **0** |
|
||||
| B | `PurchaseEvaluations","KeHoachKyKet` | **0** |
|
||||
| C | `HopDongCung","Forms` | **0** |
|
||||
| D | `Workflows","PeWorkflows` | **0** |
|
||||
---
|
||||
|
||||
- **control-DƯƠNG cùng file/cùng pipeline grep:** `/purchase-evaluations` = **68**, `/khkk/list` = **13**, `/suppliers` = **16** ⇒ 0-hit ở A-D là ĐO THẬT, không phải grep chết.
|
||||
- **control-ÂM:** chuỗi bịa `ZZZ_NOT_EXIST_MARKER` = **0**.
|
||||
## Mục 1 — Run Gitea cho `1c6d77a` ✅ PASS
|
||||
|
||||
✅ **Marker ÂM (K6 là DISPLAY-ONLY, không phải gỡ route):** 3 chuỗi route trên PHẢI CÒN trong bundle admin POST. Nếu chúng tụt về 0 thì K6 đã cắt nhầm sang lớp route/quyền.
|
||||
✅ **Differential:** eoffice = FROZEN kỳ vọng (fe-user 0-diff) ⇒ A-D phải **0** ở eoffice cả PRE lẫn POST.
|
||||
| | |
|
||||
|---|---|
|
||||
| head_sha | `1c6d77a6ccd95b2e287a3602f23780558c9d1ecc` (khớp HEAD; `git log origin/main..HEAD` **rỗng**) |
|
||||
| run | **#440** / task id **553** / `status=success` |
|
||||
| thời gian | created 11:54:38 → updated 12:01:07 = **6m29s** |
|
||||
| tail log | `Deploy done. App pool started.` → `API /health/live -> 200` → `🏁 Job succeeded` |
|
||||
|
||||
`paths-ignore` (7-glob): 8/10 file trong range là `.claude/**` (bị bỏ qua), **nhưng** `fe-admin/**` KHÔNG ⇒ CI đúng ra phải chạy, và nó đã chạy.
|
||||
|
||||
## Mục 2 — Gate CI **622 = 45 D + 577 I** ✅ PASS (tách-phần, khớp từng vế)
|
||||
|
||||
Đo ở web-UI log `…/actions/runs/440/jobs/0/logs` (18.697 B):
|
||||
|
||||
| Project | dòng log | Failed | Passed | Skipped |
|
||||
|---|---|---|---|---|
|
||||
| `SolutionErp.Domain.Tests.dll` | L29 | **0** | **45** | 0 |
|
||||
| `SolutionErp.Infrastructure.Tests.dll` | L47 | **0** | **577** | 0 |
|
||||
| **Tổng** | | **0** | **622** | 0 |
|
||||
|
||||
- Khớp **dự đoán TRƯỚC khi đo**: giữ nguyên 622, delta **0** — đúng: K6 chỉ thêm `Set` + comment, 0 file test trong range.
|
||||
- **control ÂM:** `grep -c 'Failed!'` = **0**; pattern lỗi = **1 hit duy nhất** là dòng runner vô hại `Failed to setup Pty Unsupported` (L12, có ở mọi run).
|
||||
- **Flaky đã khai** `DeleteContractSigningPlan_SoftDeletes`: run này **KHÔNG đỏ** (Failed 0). Mốc #439 xanh, #440 xanh ⇒ 2 run liên tiếp sạch, chưa cần re-run.
|
||||
|
||||
## Mục 3 — Mig GIỮ **71** + `sys.tables` **97** ✅ PASS (2 nguồn)
|
||||
|
||||
- **Nguồn 1 — git:** `git diff e202393..1c6d77a -- '*Migrations*'` = **rỗng** ⇒ 0-mig phía code. Đếm file repo (filter neo-đuôi `Designer\.cs$|ModelSnapshot\.cs$` — bản vá từ K5) = **71**.
|
||||
- **Nguồn 2 — sqlcmd prod (đo SAU `status=success`, tức sau recycle):**
|
||||
`MIGTOP=20260731085624_AddKhkkApprovalGroupCatalogAndFinalizeRuntime` (= Mig 71, GIỮ) · `MIGCOUNT=71` · `SYSTABLES=97` (is_ms_shipped=0).
|
||||
- Anchor DB không nhích (đúng cho FE-only): `MENUITEMS=200` · `PERMS=2221` — y hệt mốc #438.
|
||||
|
||||
## Mục 4 — Bundle: admin ROTATE / eoffice FROZEN-hợp-lệ ✅ PASS
|
||||
|
||||
| App | asset | PRE | POST | size | LM POST (+07) | trong cửa-sổ deploy? |
|
||||
|---|---|---|---|---|---|---|
|
||||
| admin | js | `C2FhHCNT` | **`Ig1R5tAh`** ROTATE | 1.758.685 → **1.758.853** (**+168**) | 11:59:40 | ✅ (11:54:38–12:01:07) |
|
||||
| admin | css | `DqXm5Hpn` | `DqXm5Hpn` FROZEN | 86.495 (giữ) | 11:59:40 | ✅ |
|
||||
| eoffice | js | `DFOCQ2ks` | `DFOCQ2ks` FROZEN | 1.676.752 (giữ) | **12:00:41** (PRE là 11:14:05) | ✅ |
|
||||
| eoffice | css | `DlN3yZ5L` | `DlN3yZ5L` FROZEN | 92.233 (giữ) | 12:00:41 | ✅ |
|
||||
|
||||
**Admin rotate = ĐẠT điều kiện FAIL-nếu-không** của đề bài. Δ **+168 B** — dương, nhỏ, đúng cỡ 1 `new Set` 12 key backtick (~170 ký tự); comment bị strip nên không cộng thêm.
|
||||
|
||||
🔑 **eoffice FROZEN là LÀNH TÍNH — chứng bằng 3 nhân chứng độc lập, KHÔNG phải "đoán vì fe-user 0-diff":**
|
||||
1. **LM ĐÃ ĐỔI** 11:14:05 → **12:00:41**, nằm trong cửa-sổ #440 ⇒ file **có bị ghi lại** (deploy.yml copy `dist\*` vô điều kiện = #69 mode-A), không phải bundle cũ kẹt lại. *Đây là discriminator quyết định: bundle-cũ-kẹt sẽ giữ LM 11:14:05.*
|
||||
2. **Log CI in thẳng asset phát ra** — L128 `dist/assets/index-DFOCQ2ks.js 1,676.75 kB` lúc `05:00:41.56` GMT ⇒ runner **ĐÃ BUILD LẠI** fe-user trong CHÍNH run này và Vite phát ra **cùng hash**. LM trên prod = mốc build này **khớp tới từng giây**. Frozen = build-determinism, không phải skip.
|
||||
3. `cmp` PRE vs POST eoffice = **byte-identical** ⇒ nhất quán với source 0-diff.
|
||||
(Đối chiếu admin: L93 `dist/assets/index-Ig1R5tAh.js 1,758.85 kB` @ `04:59:40.39` — khớp cả hash lẫn size lẫn LM prod. Log-CI là nhân chứng **thứ 4**, độc lập với curl prod.)
|
||||
|
||||
🔑 **Control-ÂM tầng-2 (chứng ghi-đè thật):** GET hash CŨ admin `index-C2FhHCNT.js` → **200 `text/html` 900B** ⇒ rơi về SPA-fallback ⇒ bundle cũ đã biến mất khỏi đĩa. (200 ở đây KHÔNG phải "còn sống" — discriminator là Content-Type.)
|
||||
|
||||
### 4-bis — Byte-marker: 🔴 SUÝT FAIL-OAN 2 LẦN vì CƠ-CHẾ REGEX, không phải vì code
|
||||
|
||||
Marker hợp-chất đo lần đầu ra **PRE 0 / POST 0** — đọc vội thì thành "K6 không ship" ⇒ FAIL. Không phong vội, đi **soi byte thật** (`grep -oE '.{90}HopDongCung.{90}'`) thì thấy dạng minify THẬT:
|
||||
|
||||
```
|
||||
var IW=new Set([`PurchaseEvaluations`,`KeHoachKyKet`,`Contracts`,`HopDongCung`,`Forms`,
|
||||
`Reports`,`Master`,`Hrm`,`Off`,`Personal`,`Workflows`,`PeWorkflows`]);
|
||||
```
|
||||
|
||||
- **Sai lần 1:** regex của tao chỉ chấp `["']` — rolldown/Vite 8 phát **backtick** (template literal). 0-hit là do regex không thể khớp, KHÔNG phải do vắng mặt.
|
||||
- **Sai lần 2:** vá xong, truyền pattern qua biến trong `"..."` → backtick bị shell nuốt, `grep -cE` gãy (`grep: Unmatched (`) và trả **0** gần như im lặng trong vòng lặp. Lại một 0-hit giả.
|
||||
- **Chốt bằng `grep -F` fixed-string** (không còn regex để sai):
|
||||
|
||||
| Marker | PRE admin | POST admin | POST eoffice | kỳ vọng | |
|
||||
|---|---|---|---|---|---|
|
||||
| Trọn chuỗi 12-key `new Set([...])` | **0** | **1** | **0** | 0→1, eoffice 0 | ✅ |
|
||||
| Adjacency 3-key `` `PurchaseEvaluations`,`KeHoachKyKet`,`Contracts` `` | **0** | **1** | 0 | 0→1 | ✅ |
|
||||
| control-ÂM đảo trật tự `` `KeHoachKyKet`,`PurchaseEvaluations` `` | 0 | **0** | 0 | 0 | ✅ |
|
||||
| control-ÂM chuỗi bịa `ZZZ_NOT_EXIST_MARKER` | 0 | **0** | 0 | 0 | ✅ |
|
||||
|
||||
🔑 **COUNT-DELTA — nhân chứng thứ 2, độc lập với adjacency.** Vì literal đã tồn tại từ K4a nên *sự có mặt* = 0 bit, nhưng *số lần xuất hiện tăng* thì đo được. PRE→POST admin, cả **12/12** key đều **+1**:
|
||||
|
||||
`PurchaseEvaluations` 4→5 · `KeHoachKyKet` 1→2 · `Contracts` 7→8 · `HopDongCung` 1→2 · `Forms` 3→4 · `Reports` 3→4 · `Master` 4→5 · `Hrm` 28→29 · `Off` 400→401 · `Personal` 2→3 · `PeWorkflows` 3→4 · **`Workflows` 9→11 (+2)**.
|
||||
|
||||
Con **+2** duy nhất KHÔNG phải bất thường mà là **kiểm-tra-nhất-quán tự thân**: `Workflows` là substring của `PeWorkflows`, nên Set thêm 1 `Workflows` + 1 `PeWorkflows` ⇒ đếm substring ra +2. Nếu K6 chỉ ship một phần thì hình dạng 12×(+1) với đúng 1 ngoại lệ giải-thích-được này sẽ không dựng lại được.
|
||||
|
||||
🔑 **Marker ÂM (display-only, KHÔNG phải gỡ route) — ĐẠT:** `/purchase-evaluations` **68→68** · `/khkk/list` **13→13** · `/suppliers` **16→16**. Route còn nguyên trong bundle admin ⇒ K6 cắt đúng lớp *hiển thị*, không lỡ tay cắt lớp routing.
|
||||
|
||||
## Mục 5 — Smoke + phép K6-đặc-thù ✅ PASS
|
||||
|
||||
Bearer admin: `POST /api/auth/login` → `accessToken` len **468** ✅
|
||||
|
||||
| Endpoint | code |
|
||||
|---|---|
|
||||
| `GET /api/contracts` | **200** |
|
||||
| `GET /api/purchase-evaluations` | **200** |
|
||||
| `GET /api/menus` | **200** |
|
||||
| `GET /api/contract-signing-plans` | **200** |
|
||||
| control-ÂM `GET /api/khong-ton-tai-zzz` (có bearer) | **404** ✅ |
|
||||
| control-ÂM `GET /api/contracts` KHÔNG token | **401** ✅ |
|
||||
|
||||
🔑 **Phép K6-đặc-thù — 2 tầng độc lập (gotcha #82) đo được bằng số:** `GET /api/menus` bearer admin trả **200 item** (khớp `MENUITEMS=200` sqlcmd ⇒ không mất row nào). Trong đó **10/10 root bị K6 ẩn VẪN được BE trả về**: `Contracts`, `Forms`, `HopDongCung`, `Hrm`, `KeHoachKyKet`, `Master`, `Off`, `Personal`, `PurchaseEvaluations`, `Reports` — cộng cả 2 con của `System` là `Workflows` + `PeWorkflows` ⇒ **12/12**.
|
||||
⇒ Đúng như comment K6 tự khai: **ẩn ở client, KHÔNG thu quyền**. Deep-link + API vẫn 200. Muốn CHẶN thật phải làm ở BE authz.
|
||||
|
||||
---
|
||||
|
||||
## Verdict
|
||||
|
||||
**PASS 5/5.** Run #440 `1c6d77a` success 6m29s · gate **622 = 45 D + 577 I** (Failed 0, delta 0 đúng dự đoán) · Mig GIỮ **71** + `sys.tables` **97**, MenuItems/Perms đứng im 200/2221 · admin ROTATE `Ig1R5tAh` (+168 B) & eoffice FROZEN-**lành-tính** (LM đổi + log CI in cùng hash + byte-identical) · marker 12-key `Set` PRE 0→POST 1 + count-delta 12/12 + marker-ÂM route 68/13/16 nguyên vẹn · smoke 4/4 + 2 control-âm + menu-tree 12/12 key vẫn được BE phục vụ.
|
||||
|
||||
**Treo / khuyến nghị (không chặn):**
|
||||
1. 🔴 **Bài học to nhất run này: 0-hit marker bị SAI 2 lần liên tiếp do cơ-chế regex, cả 2 lần đều trông y hệt "code không ship".** (a) rolldown/Vite 8 minify string → **backtick**, regex neo `["']` không bao giờ khớp; (b) truyền pattern chứa backtick qua biến trong `"..."` làm grep gãy và trả 0 gần như im lặng. ⇒ Từ nay: marker chuỗi trên bundle **dùng `grep -F` fixed-string**, và **luôn trích byte thật quanh literal (`.{90}X.{90}`) trước khi kết luận vắng mặt**.
|
||||
2. **Kỹ thuật mới dùng được lại — COUNT-DELTA:** khi literal đã tồn tại từ đợt trước (marker "có/không" = 0 bit), đo **số lần xuất hiện PRE→POST**. Ngoại lệ substring (`Workflows` ⊂ `PeWorkflows` ⇒ +2) lại thành kiểm-tra-nhất-quán miễn phí.
|
||||
3. **Log CI in thẳng `dist/assets/index-<hash>.js <size>`** — nhân chứng thứ 4 cho ship-proof, độc lập hoàn toàn với curl prod, và là cách rẻ nhất phân biệt FROZEN-lành-tính với bundle-kẹt (so LM prod với mốc build in trong log).
|
||||
|
||||
**Bundle baseline MỚI (post-#440):** admin `Ig1R5tAh`/`DqXm5Hpn` · eoffice `DFOCQ2ks`/`DlN3yZ5L`. *(Snapshot-per-run — lão hoá ngay ở deploy kế.)*
|
||||
|
||||
END cicd-k6 — VERDICT=PASS 5/5
|
||||
|
||||
@ -0,0 +1,60 @@
|
||||
IMPLBE-K7: IN-PROGRESS
|
||||
|
||||
# sub-implbe-k7 — Lane BE K7 (chuỗi SP-002 B4, S167 · 2026-08-01)
|
||||
|
||||
> Rào: chỉ `src/Backend/**` · KHÔNG FE · KHÔNG tests/ · KHÔNG commit. Trọng tài = `dotnet build` SẠCH.
|
||||
> Artifact ghi-từng-mục chống-#53: mỗi vá xong ghi NGAY {file:line, 1 câu}.
|
||||
|
||||
## §0 — Tiền đề đo TRƯỚC khi gõ (KHÔNG đoán)
|
||||
|
||||
**git status @spawn** (sạch phía `src/`): dirty chỉ `.claude/WAL.md · .claude/agent-memory/reviewer/MEMORY.md · .claude/auto-memory/* · .claude/governance/.session-counter.json · .claude/workflows/runs/.../sub-reviewer-gate-k6.md · docs/STATUS.md` = **của lead, bỏ qua**. `src/Backend/**` = 0 file dirty. Branch `main`, HEAD `1c6d77a` (K6).
|
||||
|
||||
### 🔴 BÁC TIỀN ĐỀ ĐỀ BÀI — "Bridge KHKK→HĐ ĐÃ TỒN TẠI (W6-S161)" là **SAI** (4 phép đo)
|
||||
|
||||
| # | Phép đo | Kết quả |
|
||||
|---|---|---|
|
||||
| 1 | `grep -rn "CreateContractFromSigningPlan\|create-contract" src/` | **1 hit duy nhất** = `PurchaseEvaluationsController.cs:339` (bridge **PE→HĐ** cũ). 0 hit KHKK. |
|
||||
| 2 | `ls src/Backend/SolutionErp.Application/ContractSigningPlans/` | `ContractSigningPlanFeatures.cs` + `Services/` — **0 file bridge** |
|
||||
| 3 | `grep "HttpPost" ContractSigningPlansController.cs` | 4 POST: `""` · `{id}/dossier-items` · `{id}/attachments` · `{id}/transitions` — **0 endpoint tạo HĐ** |
|
||||
| 4 | write-site `ContractSigningPlanLine.ContractId` | **0 assignment toàn codebase** (khớp đo độc lập của invest K7.0(b) @S164 **và** K5 FLAG-1 "`lines[].contractId` không tồn tại ở bất cứ đâu") |
|
||||
|
||||
**Cái ĐÃ tồn tại @W6-S161 = "đường ống HĐ V2"** (List/Detail V2-aware `ContractFeatures.cs:306-318` · Inbox V2 `:532-543` · guard trình `ContractWorkflowService.cs:73-92`) — invest K7.0 đã BÁC đúng chỗ này. Lead **nhập nhằng "đường ống đã vá" ⇒ "bridge đã tồn tại"**.
|
||||
|
||||
### Hệ quả + quyết định lane (khai rõ, không im lặng)
|
||||
|
||||
4/5 vá được giao (**vá-1-endpoint · vá-3 · vá-4a · vá-6**) **VẬT LÝ KHÔNG THỰC HIỆN ĐƯỢC** nếu không có bridge — chúng đều là mệnh đề *về thân bridge*. Đặc biệt **vá-6** ("nạp Project.Code + Supplier.Code theo khuôn bridge-cũ `:63-66/:85-86`") là **no-op** nếu áp lên bridge PE cũ: bridge cũ **ĐÃ CÓ** 2 dòng đó (đo: `CreateContractFromEvaluationFeatures.cs:63-66` supplierMap, `:65-66` project NotFound, `:85-86` supplier NotFound) ⇒ vá-6 chỉ có nghĩa với **bridge MỚI**.
|
||||
|
||||
⇒ **Quyết: DỰNG bridge KHKK→HĐ (BE-only) theo spec đã có sẵn `sub-invest-fable-b2-cum3.md §K7.1` (K7.6 V1+V2) + nhúng thẳng 5 vá vào lúc dựng.** Chệch-khuôn-so-brief KHAI RÕ tại đây, trỏ đề xuất gốc (invest K7.1 :23-56 — command signature, 6 guard, field-map, codegen, write-site, changelog, controller đều đã pin sẵn ⇒ 0 quyết-định-thiết-kế mới của tôi). **KHÔNG làm:** FE (K7.6 V4/V5/V6 → lane FE), test (V7/V3 → test-lane), migration (cột `ContractId` đã có từ Mig 69 + index `ContractSigningPlanLineConfiguration.cs:26`).
|
||||
|
||||
### Tiền đề phụ thuộc (cụm-1 đã land?) — ĐO
|
||||
|
||||
- K2 Mig 71: `ContractSigningPlanLine.TenHangMuc` (`:27`) + `CatalogEntryId` (`:23`) ✅ có trên đĩa.
|
||||
- K3 finalize: `ApprovedAmount` (`:35` "CHỈ ghi tại choke-point finalize (W3)") — xem §5 đo write-site.
|
||||
|
||||
---
|
||||
|
||||
## §1 — [vá-1a] Grant `Contracts.Create` (seeder, code-first)
|
||||
|
||||
(đang làm — ghi khi xong)
|
||||
|
||||
## §2 — [vá-1b] Endpoint bridge = HỘI 2 khóa
|
||||
|
||||
(đang làm)
|
||||
|
||||
## §3 — [vá-3] Guard workflow `IsActive ∧ IsUserSelectable`
|
||||
|
||||
(đang làm)
|
||||
|
||||
## §4 — [vá-4a] `Line.ContractId` write-site + DTO expose
|
||||
|
||||
(đang làm)
|
||||
|
||||
## §5 — [vá-6] Project.Code/Supplier.Code + SlaDeadline + WorkflowDefinitionId
|
||||
|
||||
(đang làm)
|
||||
|
||||
## §6 — Build + verify
|
||||
|
||||
(đang làm)
|
||||
|
||||
END implbe-k7 — VERDICT=IN-PROGRESS
|
||||
@ -0,0 +1,60 @@
|
||||
IMPLFE-K7: IN-PROGRESS
|
||||
|
||||
# Lane FE K7 — bridge KHKK→HĐ (vá-1 FE · vá-16 · vá-4b) — S167 2026-08-01
|
||||
|
||||
## §0 ĐO TRƯỚC KHI GÕ (đĩa, không đoán)
|
||||
|
||||
**git status TRƯỚC-Write** (`git status --short`): 8 file dirty + 1 untracked, **TẤT CẢ thuộc `.claude/*` + `docs/STATUS.md`** (của lead) — `src/Backend`, `fe-admin`, `fe-user` **SẠCH 0 dòng** (`git status --short -- src/Backend fe-admin fe-user` → rỗng). HEAD = `1c6d77a [CLAUDE] FE-Admin: K6 …`.
|
||||
|
||||
**Đo hiện trạng nút bridge trên màn KHKK (2 app):**
|
||||
```
|
||||
grep -rn -i -E "vào HĐ|create-contract|bridge|contractId" fe-user/src/pages/khkk fe-admin/src/pages/khkk
|
||||
→ 0 HIT ⇒ CHƯA CÓ nút bridge nào (W6-S161 KHÔNG dựng phần này)
|
||||
```
|
||||
Tiền lệ DUY NHẤT đang sống = bridge PE: `POST /purchase-evaluations/{id}/create-contract`
|
||||
(`PurchaseEvaluationsController.cs:339-343`, body `CreateContractFromEvaluationBody:379`), FE gọi ở
|
||||
`components/pe/PeDetailTabs.tsx:2186`.
|
||||
|
||||
**Baseline SHA256 (trước sửa) — 3 cặp mirror ĐỀU identical:**
|
||||
| file | SHA256 (cả 2 app) |
|
||||
|---|---|
|
||||
| `src/hooks/usePipelineStages.ts` | `edff9931…94a19ac` |
|
||||
| `src/types/khkk.ts` | `7a99b865…6f22b6` |
|
||||
| `src/pages/khkk/KhkkDetailPage.tsx` | `1863153b…95f16be` |
|
||||
|
||||
**Chữ ký hook quyền THẬT** (`src/hooks/usePermission.ts:14-26`, 2 app identical):
|
||||
`usePermission()` trả `{ can(menuKey, action: CrudAction = 'Read') }` — **KHÔNG** phải
|
||||
`usePermission('X','Y')` như đề bài viết. Dùng `const { can } = usePermission()` rồi
|
||||
`can('KeHoachKyKet','Read') && can('Contracts','Create')`.
|
||||
|
||||
---
|
||||
|
||||
## §1 [vá-4b] Cây GĐ3/GĐ4 nối qua TẬP — `usePipelineStages.ts` (fe-user XONG, `npx tsc -b` EXIT 0)
|
||||
|
||||
**Trạng thái đo được TRƯỚC:** K5 (S166) đã đổi HÌNH DẠNG sẵn (`Set contractIdsOfPackage` + ghi chú
|
||||
"chỗ cắm nguồn thứ hai") nhưng **chỉ có 1 nguồn** `pe.contractId`. Lượt này CẮM nguồn 2.
|
||||
|
||||
- `types/khkk.ts`: +`KhkkListItemDto.lineContractIds?: string[] | null` (OPTIONAL — xem KHỚP-DTO)
|
||||
+`khkkPlanContractIds(plan)` gom 2 nguồn (`lineContractIds` ∪ `lines[].contractId`) — **1 hàm duy
|
||||
nhất** để BE đổi tên thì sửa 1 chỗ, không vá rải.
|
||||
- `usePipelineStages.ts`: `contractIdsOfPackage` = `pe.contractId` (legacy) **∪** `khkkPlanContractIds(k)`
|
||||
cho mọi phiếu KHKK của gói, dedupe bằng `Set` (2 nguồn CỐ Ý chồng nhau ở HĐ đầu).
|
||||
`fallbackName` tra CẢ 2 nguồn (HĐ đến từ KHKK thì `peItems` không trỏ tới nó → trước đây rơi về `null`).
|
||||
- **Sai-thầm-lặng → thấy được:** BE chưa trả field ⇒ nguồn 2 im, mà "im vì chưa nối" trông y hệt "im vì
|
||||
gói chưa có HĐ". `console.warn` DEV-only **1 lần/phiên** (cờ module `warnedMissingLineContractIds`)
|
||||
khi có phiếu `DaDuyet` mà **mọi** phiếu `lineContractIds == null`. BE trả `[]` (gói chưa có HĐ) thì IM
|
||||
— phân biệt được vắng-field ⟂ rỗng-thật.
|
||||
|
||||
### 🔴 KHỚP-DTO (lead đối chiếu khi lane BE trả)
|
||||
|
||||
| Vế | Đo được trên đĩa lúc 2026-08-01 | FE làm gì |
|
||||
|---|---|---|
|
||||
| `KhkkLineDto.contractId` | ✅ **ĐÃ CÓ 2 phía**: FE `types/khkk.ts:201` · BE `ContractSigningPlanLineDto:74 Guid? ContractId` | không phải thêm gì |
|
||||
| Line-contract-id trên **LIST** payload | ❌ **KHÔNG CÓ**: `ContractSigningPlanListItemDto:34-64` (17 field) không mang contract-id nào; `KhkkListItemDto` cũng không có `lines[]` | FE thêm **`lineContractIds?: string[]`** (optional) |
|
||||
| Endpoint bridge | ❌ chưa tồn tại lúc đo (`0 hit` grep `create-contract` phía KHKK) | FE gọi `POST /contract-signing-plans/{id}/create-contract` theo `sub-invest-fable-b2-cum3.md §K7.1:23,54` |
|
||||
|
||||
⚠️ **Tên `lineContractIds` là FE chọn** (BE-lane K7 sở hữu tên thật). Nếu BE đặt khác ⇒ sửa **đúng 1 hàm**
|
||||
`khkkPlanContractIds()` trong `types/khkk.ts` ×2 app. Nếu BE **không** expose trên list ⇒ cây vẫn chạy
|
||||
bằng nguồn legacy + có chuông DEV (không vỡ, không im).
|
||||
|
||||
END implfe-k7 — VERDICT=IN-PROGRESS
|
||||
Reference in New Issue
Block a user