Files
solution-erp/.claude/workflows/runs/2026-08-01-S169-bookend-open/sleep-drafts/cicd-monitor--2026-05-runs.gist.draft.md
2026-08-03 10:14:49 +07:00

25 KiB
Raw Blame History

cicd-monitor/2026-05-runs — gist

distill-gen: 1 source-verbatim: .claude/agent-memory/cicd-monitor/archive/2026-05-runs.md (6 tóm-tắt + 20 Run + 8 key-pattern → 10 cụm) pointer-style: substring

TỔNG-HỢP (3-5 ý)

  1. Chuỗi per-NV lookup discrimination (Plan K→P, Mig 29/30/31): bug FirstOrDefault không lọc actor sống 2-3 ngày prod, "em main + Reviewer + Implementer all missed across 3 plan", checklist phình 6→8→9→10 điểm — với schema OR-of-N, mọi FirstOrDefault(Order==) là điểm mù (quét TOÀN kho, không chỉ handler lộ bệnh); record body Controller phải soi-gương param với Command (thiếu field ⇒ ASP.NET default im-lặng).
  2. Saga Discovery #3 chốt sau 5 lần quan-sát: Gitea xét paths-ignore trên TRỌN push-range — muốn loại một biến phải tìm ca VẮNG biến mà hiện-tượng vẫn còn (#202 then-chốt); kết-luận là "lỗi" nhưng "Anomaly is BENEFICIAL".
  3. Chuỗi audit-trail S25 (#215 FAIL → #221): phát hiện "field X không set ở N chỗ" thì vá cả N chỗ một lượt (9 site UserName); dữ-liệu cũ hỏng cứu ở FE bằng userMap từ dữ-liệu nhúng sẵn; Run #217 a734bf2 = nguồn duy nhất Discovery #5 + pattern "capture pre-call mutation state".
  4. Plan T tắt DemoSeed: cờ chống re-seed chỉ tin sau restart THẬT (Restart-WebAppPool + sqlcmd đếm 0); appsettings.Production.json bị .gitignore ⇒ default phải đặt ở appsettings.json; chính cờ này về sau nuốt seed hạ-tầng Plan B (gotcha #51 — xem gist q2).
  5. Lệnh đo sai trả về IM-LẶNG (7 phát-hiện công-cụ: /actions/tasks≠runs · updated_at trễ ~2 phút · 429 ~5 req/phút → cache token · sqlcmd 4-backslash Discovery #5 · enum cần số Discovery #4 · 503 recycle lành · route /api/auth/login + field accessToken) — mỗi phép đo cần ca đối-chứng dương. MEMORY tự phình 35→72KB qua 8 nấc cảnh-báo leo thang không tự dừng ⇒ cần cổng ngoài. 🔐 dòng 48 verbatim: password DB prod ĐÃ redact bởi lead — ROTATE REQUIRED.

Entries

Ghi-chú hợp-nhất nội-file: phần (a) tóm-tắt ∩ phần (b) đầy-đủ của verbatim chồng 4 Run (#202 · #203 · #210 · #219) — cụm dưới đây chọn bản DÀY; riêng #215 + #217 CHỈ có ở phần (a) — ĐÃ GIỮ khi hợp-nhất (KHÔNG mất Run #217).

[cao] D1 — Chuỗi per-NV lookup discrimination (Plan K → P), sinh checklist 10 điểm

  • VIỆC: wire cờ opt-in per-NV (Allow* theo từng slot Approver) xuyên Mig 29/30/31, rồi vá 5 lượt liên-tiếp khi lộ ra thiếu điểm nối.
  • KẾT-LUẬN: #194 098baa6 PARTIAL — schema + FE checkbox thứ 7 đã ship nhưng AwLevelDto (ApprovalWorkflowV2AdminFeatures.cs:23-38) thiếu hẳn param AllowApproverSkipToFinal (chỉ tồn-tại trong comment dòng 60) + ToDto:156-158 thiếu ⇒ checkbox đọc/ghi vào hư-vô. #195 0062fcb vá 15 LOC ⇒ 13 key round-trip PASS, đúc checklist 8 điểm. #201 fb3c22c (Plan N) vá PurchaseEvaluationFeatures.cs:765 thêm && l.ApproverUserId == currentUser.UserId + fallback admin ⇒ điểm 9. #202 a1c8386 (Plan O) quét ra 4 site cùng khuôn (...WorkflowService.cs:204 + :258-260, ...DetailFeatures.cs:75-76, ...Features.cs:314-315) + 3 test hồi-quy (111/111). #203 1727bd5 (Plan P) vá Controller TransitionPeBody từ 3 → 7 paramđiểm 10.
  • BÀI-HỌC: với schema OR-of-N (nhiều NV cùng một Cấp), mọi FirstOrDefault(... Order == ...)điểm mù: phải quét TOÀN kho (grep -n "FirstOrDefault.*Order.*==") chứ không chỉ sửa handler lộ bệnh. Và record body của Controller phải soi-gương số param với Command — thiếu field thì ASP.NET không báo lỗi, chỉ lặng-lẽ default.
  • BẤT-NGỜ: bug sống trên prod 2-3 ngày3 lượt refactor liên-tiếp (Mig 29/30/31) đều trượt — verbatim tự khai "em main + Reviewer + Implementer all missed across 3 plan"; đồng-thời checklist phình 6 → 8 → 9 → 10 điểm, tức mỗi lần "đã đủ" đều sai. → substring:"098baa6"

[cao] D2 — Saga Discovery #3 (paths-ignore xét trọn push-range) (trùng liên-file "Verify S22 chốt": bản dày = file này; cicd-monitor--2026-05-q3 B2 giữ bản mỏng — đóng-kín per-file, không xoá bên nào)

  • VIỆC: truy vì sao commit docs-only vẫn kích CI (ngược gotcha #41), qua 5 lần quan-sát.
  • KẾT-LUẬN: giả-thuyết đầu (Discovery #2, Run #188): ".claude/agent-memory/** không nằm trong paths-ignore nên kích CI" → BỊ BÁC ở S22-chốt vì **/*.md khớp mọi độ sâu (cc8a7d3 SKIP đúng). Chốt sau 3 lần củng-cố (#200 f4055a1, #201 fb3c22c, #202 a1c8386 — lần 3 0 file agent-memory nên loại hẳn biến này): Gitea xét paths-ignore trên TRỌN push-range, chỉ cần 1 commit trung-gian có file không-bị-ignore là cả range chạy. #210 mixed-diff nên không tính là bằng-chứng.
  • BÀI-HỌC: muốn loại một biến thì phải tìm ca VẮNG biến đó mà hiện-tượng vẫn còn (#202 là ca then-chốt) — thêm ca có-biến chỉ củng-cố giả-thuyết sai. Ghi rõ ca nào không tính là bằng-chứng (#210) cũng quan-trọng ngang ca tính.
  • BẤT-NGỜ: kết-luận cuối là "lỗi" nhưng verbatim đánh giá "Anomaly is BENEFICIAL" — CI chạy dư lại cứu được gate verify trên commit docs; và gotcha #47 tuy bị bác vẫn giữ giá-trị phòng-ngừa cho file non-.md tương-lai. → substring:"f4055a1"

[cao] D3 — Chuỗi audit-trail S25 (Plan AB → AF): 5 khuôn phục-hồi lịch-sử (trùng liên-file #215: bản dày hơn ở cicd-monitor--2026-05-q3 B1; #215+#217 trong verbatim file này chỉ có ở phần tóm-tắt (a) — Run #217 = nguồn duy nhất Discovery #5 + pattern "capture pre-call mutation state", ĐÃ GIỮ)

  • VIỆC: làm "Lịch sử duyệt" hiển-thị đúng + cứu dữ-liệu lịch-sử đã ghi thiếu.
  • KẾT-LUẬN: #215 FAIL (tie-break, xem q3) → #217 a734bf2 (AC) thêm Approval-row nhánh Reject Service:75-103 + prefix [Duyệt vượt cấp tới Cấp cuối] :472, xác-minh 6/6 approval có đủ 8 key → #218 25837b6 (AC2) FE dựng synthetic Reject row từ Changelog entityType=5 (20 entry, 8 Workflow-entry, khớp keyword "Trả về") → #219 0aaf2df (AD) bỏ badge from/to + extractNextTargetHint() parse \[Bước (\d+) — Cấp (\d+)\] → #220 9ea62be (AE) vá 9 site UserName = currentUser.FullName ?? currentUser.Email (4 Features + 5 DetailFeatures) → #221 506cada (AF) FE userMap useMemo dựng từ 5 nguồn nhúng sẵn (drafter/approvals/levelOpinions/departmentOpinions/approvalFlow.steps), 3 nấc: tin entry.userName → tra map → "Hệ thống". Cumulative 7 commit, 1 catch + 6 PASS, 0 regression.
  • BÀI-HỌC: khi phát-hiện "field X không set ở N chỗ giống nhau", vá cả N chỗ một lượt thay vì chỗ báo lỗi (AE: 1 lộ + 8 phòng-xa). Dữ-liệu cũ đã hỏng thì cứu ở FE bằng dữ-liệu đã nhúng sẵn trong bundle — không cần đổi hợp-đồng API, né luôn ràng-buộc /api/users chỉ-admin.
  • BẤT-NGỜ: bằng-chứng chốt Bug 1 là một dòng dữ-liệu cũ còn sót: entry Budget Adjust 02:31:39 entityType=1userName="" — tức prod tự lưu giữ vật-chứng tiền-sửa để đối-chứng sau deploy; và AD bỏ bớt thông-tin (badge from/to) lại làm UI đúng hơn vì phần lớn là ChoDuyet→ChoDuyet tự-lặp. → substring:"a734bf2"

[cao] D5 — Plan T tắt DemoSeed + chứng bằng restart THẬT

  • VIỆC: chặn seed demo tự mọc lại trên prod (#207 0b97840, #208 7b7b28f).
  • KẾT-LUẬN: bọc 5 method demo trong if (!demoSeedDisabled) (V1 workflow ×2, demo Contract, demo PE, sample AW V2); GIỮ 8 nhóm seed hạ-tầng (Roles/Admin/30 user UAT/master/template/catalog/backfill). T5 xoá 4 PE + 1 V2 + 2 V1 + cascade; T6 ép Restart-WebAppPool SolutionErp-Api rồi sqlcmd: PE=0, V2=0, V1=0, master còn nguyên (Users 33 · Suppliers 19 · Projects 9 · Contracts 7). Tổng R+S+T ≈ 720 dòng bị xoá, cờ tồn-tại vĩnh-viễn.
  • BÀI-HỌC: cờ chống re-seed chỉ được tin sau khi khởi-động lại thật rồi đếm lại — không tin vào việc đọc code. Và vì appsettings.Production.json bị .gitignore, giá-trị mặc-định phải đặt ở appsettings.json (thứ đi qua git) mới tới được prod.
  • BẤT-NGỜ: chính cái cờ chứng-minh-tốt này về sau nuốt luôn seed hạ-tầng của Plan B (Run #231/#232, gotcha #51) — biện-pháp đúng ở lớp này thành bẫy ở lớp khác. → substring:"Restart-WebAppPool"

[cao] D6 — 7 phát-hiện về CÔNG-CỤ ĐO (dễ mất nhất khi nén)

  • VIỆC: gom các bài học về chính phép đo, rải rác trong 6 Run.
  • KẾT-LUẬN: (1) API liệt-kê Run của Gitea là /api/v1/repos/.../actions/**tasks**, /actions/runs trả 404; đọc không cần auth. (2) updated_at trong bảng task trễ ~2 phút — phải đối-chiếu LastWriteTime của .dll/.html trên VPS. (3) đăng-nhập ~5 request/phút là 429 ⇒ cache token ra %TEMP%\*_token.txt, 2 login + 8 lệnh dùng lại (Run #188 dính 429, S22-chốt hết dính). (4) Discovery #5: sqlcmd Windows-auth qua ssh cần escape 4 dấu \\\\SQLEXPRESS — 2 dấu ra rỗng KHÔNG báo lỗi. (5) Discovery #4: ASP.NET Core 10 record + enum đòi số, không có JsonStringEnumConverter (grep Program.cs = 0 hit) ⇒ payload mẫu "Drafter" trong brief là sai, fail 400. (6) 503 giữa lúc IIS recycle (~5-15s) là bình-thường, tự khỏi. (7) route thật là /api/auth/login (không /api/v1/...), field token là accessToken (không token); /api/me + /api/menu-keys 404 vì không tồn-tại, không phải regression.
  • BÀI-HỌC: lệnh đo sai trả về IM-LẶNG (2-backslash ra rỗng; enum string ra 400; endpoint sai ra 404) — mỗi phép đo phải có ca đối-chứng dương, và phải phân-biệt "0 kết-quả" với "đo hỏng".
  • BẤT-NGỜ: brief do người ra lệnh viết lại chính là nguồn sai (payload "Drafter") — và verbatim ghi thẳng "brief example payload was misleading", không đổ cho hệ-thống. → substring:"JsonStringEnumConverter"

[cao] D7 — MEMORY phình 35→72KB: 8 lần cảnh-báo leo thang mà không ai dừng

  • VIỆC: vai tự theo-dõi kích-thước diary của chính mình sau mỗi entry.
  • KẾT-LUẬN: chuỗi tự khai: ~35KB ("approaching") → ~40 → ~43 ("over 25KB trigger, under 50KB hard") → ~50 ("at hard threshold") → ~58 ("REQUIRED ARCHIVE ... BEFORE next entry") → ~60 ("MANDATORY") → ~62 ("DEDICATED CURATION SESSION PRIORITY NEXT") → ~66→69 ("priority MAX") → ~72 ("PRIORITY MAX REINFORCED"). Chốt chỉ xảy ra khi có phiên curate riêng (chính file archive này), kéo về mục-tiêu ~25-30KB.
  • BÀI-HỌC: cảnh-báo do chính kẻ đang phình phát ra thì không tự dừng được — cần cổng ngoài (ngưỡng cứng chặn ghi) hoặc việc riêng có chủ; và ngưỡng "mềm 25KB / cứng 50KB" bị vượt gần 3 lần vẫn chỉ sinh thêm chữ in-đậm.
  • BẤT-NGỜ: mức độ khẩn-cấp trong câu chữ leo thang 8 nấc (approaching → REQUIRED → MANDATORY → PRIORITY MAX → REINFORCED) — bản thân nó là dữ-liệu: leo-thang ngôn-từ không thay cho cơ-chế. → substring:"PRIORITY MAX REINFORCED"

[cao] D10 — Tự khai tác-dụng-phụ và giới-hạn phép đo

  • VIỆC: ghi lại thứ phép kiểm-chứng làm hỏng + thứ nó KHÔNG chứng được.
  • KẾT-LUẬN: kiểm-chứng bằng POST thật đã đổi state prod: PE/2026/A/025 phase 10→98 (admin bypass qua if (isAdmin) return dòng 186), khai rõ "admin bypass là thiết-kế, không phải regression", đề bro tự chọn revert hay giữ; PE/2026/A/021 trả 409 ⇒ không đổi state (atomic) — có kiểm lại chứ không đoán. Giới-hạn tự khai: NV Test không đóng vai approver (đã quét 20 PE) nên biến-thể actor không khả-thi; #194 dùng chữ "presumed" 4 lần cho phần không đo được; #219 bỏ qua sqlcmd vì thiếu PROD_DB_PASSWORD, chấp-nhận verify gián-tiếp và nói rõ là gián-tiếp.
  • BÀI-HỌC: báo-cáo verify phải tách 3 mức: đo trực-tiếp / suy ra (inferred, presumed) / không đo được — và ghi luôn phép đo đã làm bẩn dữ-liệu.
  • BẤT-NGỜ: ca 409 lại là bằng-chứng dương mạnh nhất cho Plan O: lỗi 409 "mode mismatch" chứng-tỏ đã qua được cửa actor-check (403 cũ), tức thất-bại đúng chỗ = thành-công. → substring:"no state change (atomic)"

[vừa] D4 — Chuỗi Mig 28→31 + backfill có kiểm số

  • VIỆC: 5 lượt deploy schema per-NV/per-Level.
  • KẾT-LUẬN: Mig 28 AddAdvancedOptionsToApprovalWorkflows (#186, 6 cờ Allow* mức workflow, allowReturnToDrafter=true mặc-định + 5 false) → Mig 29 RefactorAdvancedOptionsToPerLevelAndDrafterUser (#187): Levels +5 Allow*, Users +1, ApprovalWorkflows 6; backfill 48/48 Levels=1 và 0/13 Users=1 (đúng vì nguồn false) → Plan D/C/E (#188): /api/users +allowDrafterSkipToFinal, PATCH admin 204 / nv.test 403, scope V2 chặt (nv.test 8 PE < admin 17) → Mig 30 AddAllowApproverEditBudgetToLevels (S22-chốt): currentLevelOptions 6 key, AwLevelDto 12 key → Mig 31 RefactorSkipToFinalToApproverLevel (#194): Levels +1, Users 1, endpoint zombie /allow-skip-final trả 404 đúng ý.
  • BÀI-HỌC: backfill phải kiểm cả hai chiều — "48/48 = 1" và "0/13 = 1" đều là kết-quả ĐÚNG cần xác-nhận; số 0 ở đây là bằng-chứng, không phải thiếu-sót. Dọn field cũ thì phải kiểm cả endpoint lẫn key trong DTO đã biến mất.
  • BẤT-NGỜ: cùng một tính-năng bị dời chỗ 3 lần (workflow → user → level) trong ~2 ngày; test-gate suy-ra từ "deploy chạy được" chứ không đọc trực-tiếp (test gate inferred PASS). → substring:"AddAdvancedOptionsToApprovalWorkflows"

[thấp] D8 — Đánh-bóng UX + 1 ca revert trong phiên

  • VIỆC: 4 Run FE thuần: #204 banner mx-5mb-3; #209 sidebar truncate (min-w-0 flex-1 + shrink-0 + title= tooltip); #210 widen w-60 → w-72 xl:w-80 kèm revert Plan U; #214 wrap nhãn dài.
  • KẾT-LUẬN: 4/4 PASS, bundle rotate đúng kỳ vọng mỗi lần; menu Pe_DuyetNcc_WfView Order=2 + họ Pe_DuyetNccPhuongAn Order 6-10, tổng 1-10 liền mạch không hở/không chồng (seed idempotent).
  • BÀI-HỌC: đổi FE-thuần vẫn verify được gián-tiếp bằng bundle-hash làm proxy; nhưng khi một chunk bị revert trong cùng phiên thì hash phản-ánh trạng-thái tích-luỹ, không phải diff của chunk ⇒ đừng đọc hash như chữ-ký của một thay-đổi đơn lẻ.
  • BẤT-NGỜ: phép đo bundle của Plan P bắt nhầm thay-đổi của Run #204 đang bay cùng lúc (QZIPWD-g/DaLTMGcx) — vẫn kết-luận đúng nhờ đối-chiếu diff có 0 file FE, tức dùng nguồn thứ hai để cứu phép đo bị nhiễu. → substring:"ac2c859"

[cao] D9 — "Key patterns extracted S21-S25" (giữ gần như nguyên)

  • VIỆC: phần (c) đã là bản chưng-cất 8 mục do chính vai viết.
  • KẾT-LUẬN: 8 mục = Discovery #3 · Discovery #4 · Discovery #5 · checklist 10 điểm per-NV (liệt-kê đủ 10) · gotcha #48 + cách vá · "CI test-gate bắt được lỗi mà UAT-skip-test bỏ sót" · FE merge synthetic + userMap · capture pre-call mutation state.
  • BÀI-HỌC: khi nguồn đã có lớp chưng-cất, việc nén nên giữ lớp đó gần như nguyên văn và cắt ở lớp Run bên dưới — nén lại cái đã nén là chỗ dễ mất mã-số (#48, điểm 9/10) nhất.
  • BẤT-NGỜ: checklist 10 điểm ở đây là bản duy nhất liệt-kê đủ 10 trong toàn batch A — mọi chỗ khác chỉ nhắc số "8"/"9"/"10 surface points" mà không kèm nội-dung. → substring:"Key patterns extracted"

Phụ-lục token (coverage)

  • gotcha#: #41 (path filter — dẫn ≥6 lần) · #44 (silent 403 — "fix confirmed" ở #210) · #45 (fix Trả lại, Run #186) · #47 (agent-memory — disproven, giữ vai PREVENTIVE) · #48 (NEW multi-Changelog.Add + SQLite frozen-clock tie-break)
  • Discovery#: #1 rate-limit 429 ~5 req/phút · #2 .claude/agent-memory/** không nằm trong paths-ignore (về sau bị bác) · #3 Gitea xét TRỌN push-range (củng-cố 4 lần: #200/#201/#202 + #210 "NOT applicable") · #4 ASP.NET Core 10 record+enum cần số · #5 sqlcmd cần \\\\SQLEXPRESS 4 backslash · (không đánh số) /actions/tasks/actions/runs (#186) · updated_at trễ ~2 phút (#187) · "Discovery 1" 503 giữa deploy + "Discovery 2" /api/auth/login + field accessToken (#221)
  • surprise: CRITICAL FAIL — K3 DTO mirror INCOMPLETE · "AllowApproverSkipToFinal mentioned only in comment line 60, NOT as actual record param" · "em main + Reviewer + Implementer all missed across 3 plan" · "Bug present 2 days prod" / "3-day prod bug latency" · "Spec hypothesis ... disproven for this commit" · "Anomaly is BENEFICIAL — catches verify gate even on docs-only tip" · "PROVES ACTOR VALIDATION SUCCESS ... 409 mode mismatch NOT 403" · "First attempt with string enum \"Drafter\" → HTTP 400 ... Brief example payload was misleading" · "Test 2 HTTP 409 = handler threw exception → no state change (atomic)" · "NV Test actor variant not feasible (scanned 20 PEs)" · "the Budget Adjust entry ... has userName=\"\" empty — this is the EXACT pre-fix Bug 1 evidence" · "Side effect logged: Admin transition on PE/2026/A/025 mutated phase 10→98" · "Run #190 ... CANCELLED by concurrency ... normal not a fail" · "/api/me + /api/menu-keys returned 404 (route names different) — Not regression" · "Memory size ... PRIORITY MAX REINFORCED" · "8 surface points checklist (NOT 6)" · "prophylactic codebase scan is needed"
  • guard: if (isAdmin) return (line 186, admin bypass CỐ Ý) · if (!isAdmin && currentLevel is not null) (line 264) · && l.ApproverUserId == actorId + throw if null (4 site Plan O) · fallback ?? curStep?.Levels.FirstOrDefault(...) cho admin/non-approver (Plan N) · [Authorize] class-level + GetWorkflowsForUserAsync auth filter (fix #44) · if (!demoSeedDisabled) bọc 5 seed demo (Plan T) · .Where(c => c.Summary!.Contains("Chuyển phase")) (vá #48) · HasDefaultValue(false) (điểm 2 checklist) · Migration 3-file rule (Up + Designer + Snapshot — điểm 3) · [FromBody] record param count = Command param count (điểm 10) · seed Order shift idempotent (1-10 liền mạch) · token-cache %TEMP%\*_token.txt chống 429 · test-gate CI chặn trước deploy
  • root-cause: FirstOrDefault(l => l.Order == curLevelOrder) không lọc actor ⇒ luôn lấy Levels[0] theo thứ-tự DB ⇒ cờ per-NV admin tick bị bỏ qua (4 site cùng bệnh) · Controller TransitionPeBody 3 field ⟂ Command 7 field ⇒ ASP.NET default im-lặng 3 field ⇒ F1/F2 không tới handler suốt 2 ngày · AwLevelDto thiếu param + ToDto thiếu ctor-arg ⇒ cột DB có mà API không trả · nhiều Changelog.Add() cùng SaveChangesAsync + SQLite frozen-clock ⇒ OrderByDescending(CreatedAt) tie-break bất-định · Gitea xét paths-ignore trên trọn push-range ⇒ tip docs-only vẫn chạy · thiếu JsonStringEnumConverter ⇒ enum dạng chuỗi fail 400 · \\SQLEXPRESS (2 backslash) ⇒ 0 output, im-lặng · UserName không set ở 9 site Changelog.Add() ⇒ cột người-dùng rỗng
  • file:line / định-danh (BE): ApprovalWorkflowV2AdminFeatures.cs :23-38 (AwLevelDto record), comment :60, ToDto :156-158, vùng :184-238 · PurchaseEvaluationFeatures.cs :765 (per-NV lookup), :314-315 (AdjustBudget) · PurchaseEvaluationWorkflowService.cs :204 (EnsureCanRejectV2Async), :258-260 (ApplyReturnModeAsync), :264 (guard), :186 (admin bypass), :75-103 (nhánh Reject Plan AC), :472 (prefix [Duyệt vượt cấp tới Cấp cuối]), :287-333 (F1 edge Bước 1), :268-275 (F1.Drafter giữ TraLai) · PurchaseEvaluationDetailFeatures.cs :75-76 (EnsureEditableForDetailsAsync) · Controller TransitionPeBody :267 (3 field) → :280-286 (7 param), mediator.Send :76-78, marker Plan P :71-75 · TransitionPurchaseEvaluationCommand :395 · Program.cs (0 hit JsonStringEnumConverter/AddJsonOptions) · DbInitializer.cs · MenuKeys.cs · appsettings.json / appsettings.Development.json / appsettings.Production.json (bị .gitignore)
  • file:line / định-danh (test + FE): test ApplyReturnMode_OneStep_AtStep1 line 350 · ApplyReturnMode_OneLevel_AtStep1Level1 line 308 · GetPurchaseEvaluationCurrentLevelOptionsTests.cs (2 method) · PurchaseEvaluationPerNvLookupRegressionTests.cs (3 test) · PeDetailTabs.tsx (fe-user + fe-admin, mirror §3.9) · PeWorkflowPanel.tsx · types/purchaseEvaluation.ts (WorkflowReturnMode = { OneLevel:1, OneStep:2, Assignee:3, Drafter:4 }) · Layout.tsx (w-60 → w-72 xl:w-80; min-w-0 flex-1 + shrink-0 + truncate + title=) · WorkflowMatrixViewPage.tsx · approvalWorkflowV2.ts · App.tsx · helper extractNextTargetHint(comment, decision) · regex \[Bước (\d+) — Cấp (\d+)\] · resolveActorName/resolveUserName + userMap useMemo
  • Run / sha / bundle: #186 eea86fd (id=300) · #187 c0af9e0 (301) · #188 a74e671 (302) · #189 40f64c6 · #190 8185070 CANCELLED · #191 0e70789 · #192 30d51c8 · #193 b04a11a (307) · #194 098baa6 (308) PARTIAL · #195 0062fcb (309) · #199 da30e27 · #200 f4055a1 (314) · #201 fb3c22c (315) · #202 a1c8386 (316) · #203 1727bd5 (317) · #204 108268a · #207 0b97840 · #208 7b7b28f · #209 86d8806 · #210 ac2c859 (324) · #214 ee0902a (328) · #217 a734bf2 · #218 25837b6 (332) · #219 0aaf2df (333) · #220 9ea62be (334) · #221 506cada (335) · commit lẻ: c2042ef/4dd6f9c/508b17a (Plan M) · 0326458 (N1+N2) · ae01ca5 (O1-O5) · ee776d5/c667802 (AA) · b079b27 (BE Mig 30) · cc8a7d3 (SKIP) · 3d725c4..cc8a7d3 · e23f51c..506cada · bundle-chain admin CzesdXLh→D5l49-70→Cclc8Uwu→CpI5OL8n→CRsX6cFo→D_JENTBi→QZIPWD-g→Dmk--X6w→CZdXQ2eo→CrHpBYFM→B5iZMa7g→CDVnRDe6→DR95zKWg→C8TvDy7r; user DP-gH4LW→B6N5hq3d→d064StNa→X7qb4Zl4→COJhbRxy→DaLTMGcx→Bd4gh3Tp→DCwhhey2→C3bCWZ90→CHJDH3M2→gAFN3NVx→BAj_Yaj5→BvcWrq2z
  • Mig / dữ-liệu prod: Mig 28 20260513114505_AddAdvancedOptionsToApprovalWorkflows · Mig 29 RefactorAdvancedOptionsToPerLevelAndDrafterUser (backfill 48/48 Levels · 0/13 Users) · Mig 30 20260513160703_AddAllowApproverEditBudgetToLevels · Mig 31 20260514160124_RefactorSkipToFinalToApproverLevel · test-count chuỗi 84 → 103 → 104 → 106 → 108 → 111 · Users 33 · Suppliers 19 · Projects 9 · Contracts 7 · PE-ID 3248f2f9-c6e9-43ff-a4ca-067ffecf9f36 (PE/2026/A/032) · 98736f06-... (A/025) · 59176ad5-... (A/027) · 6148ba7a-... (A/026) · f9476dad-... (A/021) · workflow bf31f120 · token len admin 468 / nv.test 477 · ~720 rows wiped
  • 🔐 secret (đã xử-lý): dòng 48 verbatim — appsettings.Production.json tại C:\inetpub\solution-erp\api\, user=vrapp, password prod ĐÃ ĐƯỢC LEAD REDACT thành nhãn pwd=[đã che 71b7740a — ROTATE REQUIRED] — gist chép NHÃN CHE này, TUYỆT ĐỐI không tra/chép giá-trị gốc từ git history; ROTATE REQUIRED vẫn treo (mật-khẩu từng nằm trong lịch-sử git)
  • meta / size-trend: ~35KB → ~40 → ~43 → ~50 → ~58 → ~60 → ~62 → ~66 → ~69 → ~72KB · ngưỡng mềm 25KB / cứng 50KB · FIFO ~8 → ~9 → ~10 → ~11 → ~12 → ~13 entry · token-cost mỗi lượt ~10k-28k · feedback_md_compact_narrative.md (§6.5) · feedback_uat_skip_verify · feedback_per_nv_permission_scope.md