Files
solution-erp/.claude/agent-memory/cicd-monitor/MEMORY.md
pqhuy1987 e23f51c42e [CLAUDE] Docs: Session 24 chốt cuối - Plan AA cumulative 7 commits + 4 agent MEMORY drift wrap
S24 wrap deliverables:

Docs:
- docs/STATUS.md prepend Recently Done newest S24 chốt cuối row (cumulative 7 commits 3 core + 4 polish UAT iteration, multi-agent ROI ~175K ~28% solo equiv)
- docs/HANDOFF.md Last updated S24 chốt cuối (replace previous S24 t1 entry với cumulative final state)
- docs/changelog/sessions/2026-05-15-s24-turn1-plan-aa-workflow-matrix.md EXTEND
  - Phase 2 Polish iteration UAT feedback section (4 commit detail):
    - Polish 1 da218f1 hotfix container px-2
    - Polish 2 4d60598 redesign v1 panel-per-NV color mirror Designer
    - Polish 3 fbbd361 redesign v2 HTML table rowSpan tận dụng full width
    - Polish 4 ee0902a wrap fix sidebar label về đầu hàng (hanging-indent reverse)
  - Stats S24 chốt cuối table
  - Multi-agent ROI cumulative S24 table (6 owner)
  - 7 Patterns reusable cross-project saved
  - Pending S25+ checklist

4 agent MEMORY drift (3 agent flushed cumulative S24 wrap + 1 CICD prior Run #210):
- .claude/agent-memory/investigator/MEMORY.md S24 Pre-A entry + memory drift note
- .claude/agent-memory/implementer/MEMORY.md +3 patterns 13/14/15 (Designer mirror + Tailwind JIT palette + rowSpan flat row builder) + S24 polish REFUSE log
- .claude/agent-memory/reviewer/MEMORY.md +4 anti-patterns (polish iteration cost vs spawn ROI + Discovery #3 negative retest + Low note IsUserSelectable leak)
- .claude/agent-memory/cicd-monitor/MEMORY.md Run #210 PASS entry (Plan AA verify 4/4 wire end-to-end)

⚠️ Implementer + CICD Monitor agent MEMORY both over 25KB curate threshold (~31.5KB + ~43KB).
Recommend archive S20-S22 old entries next session via `archive/2026-05-S20-S22.md`.

User-level memory: NO update needed per §6.2 (responsive memory đã đúng 2-panel,
hanging-indent + JIT palette patterns captured trong agent Implementer MEMORY).

Test verify post-Plan AA: 111/111 PASS unchanged (58 Domain + 53 Infra). No regression.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 00:18:48 +07:00

235 lines
46 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# CI/CD Monitor Agent — Persistent Memory
> **Persistent diary cross-session.** Auto-injected first 200 lines / 25KB at spawn.
> Update BEFORE every stop. Curate when > 25KB.
---
## 🎯 Role baseline
Read-only CI/CD pipeline + post-deploy verifier for SOLUTION_ERP. Polls Gitea Actions API, verifies test gate + deploy ship + prod health. Tools: Read, Grep, Glob, Bash, WebFetch. Output: PASS/FAIL verdict + evidence under 500 words. **Spawn cost ~150K tokens** — trade-off để catch fail tự động không phụ thuộc em main nhớ verify.
---
## 🚨 Recurring CI/CD bug patterns (catch with priority)
### Gotcha #39 — act_runner github.com TCP timeout
- **Symptom:** CI run hang ở "Set up job" → timeout 21s, run stays "queued" forever
- **Verify:** log line `Error: dial tcp ... github.com:443 ... i/o timeout`
- **Fix:** manual checkout bypass đã hardcode trong `.gitea/workflows/deploy.yml` (run #108/#109), pass at #110. KHÔNG revert. Nếu pattern returns → escalate em main check VPS network
### Gotcha #40 — npm cache `tsc not found`
- **Symptom:** `build_fe_admin` fail sau khi enable `cache: npm``actions/setup-node@v4`
- **Verify:** log line `sh: tsc: command not found` hoặc `npm error code ETIMEDOUT`
- **Fix:** DISABLED npm cache rolled back ở `a21790d`. KHÔNG re-enable. Build time chấp nhận ~3 min thay vì optimize
### Gotcha #41 — paths-ignore docs-only skip
- **Symptom:** Commit code thật mà CI không trigger (run list không có entry mới)
- **Verify:** `git diff --name-only HEAD~1 HEAD` vs `paths-ignore: ['docs/**', '**/*.md', '.claude/skills/**']`
- **Fix:** Nếu commit có code thật bị skip nhầm → check pattern conflict. Nếu commit chỉ docs → expected behavior (saving ~9 min deploy / commit MD-only)
### Gotcha #25 — IIS WebSocket / module exclusion
- **Symptom:** `notification-hub/negotiate` returns 401 hoặc 404 prod (FE SignalR connect fail)
- **Verify:** `curl -X POST https://api.solutions.com.vn/notification-hub/negotiate` → non-200
- **Fix:** IIS WebSocket module enable trong `web.config` của site api.solutions.com.vn (skill `iis-deploy-runbook`)
### Deploy ship verification — bundle hash unchanged
- **Symptom:** commit push success + Gitea action success + status PASS, **nhưng prod không có thay đổi visible** (user UAT báo "đã deploy mà không thấy")
- **Root cause candidates:**
- IIS app pool chưa recycle → giữ assembly cũ trong memory
- NSSM service script không copy file đúng folder
- Browser cache (rare nếu Vite hash chuẩn)
- **Verify:** `curl -s https://admin.solutions.com.vn/ | grep -oE '/assets/index-[a-z0-9]+\.js'` — hash giữ nguyên = ship fail
- **Fix:** SSH `vietreport-vps "Restart-WebAppPool admin.solutions.com.vn"` + recheck bundle hash
### Migration drift prod vs repo
- **Symptom:** Latest mig trong repo (vd Mig 27) nhưng prod chưa có (DbInitializer startup fail)
- **Verify:** Compare `ls Migrations/*.cs` vs `sqlcmd ... __EFMigrationsHistory`
- **Fix:** Check `Program.cs` startup hook `app.MigrateDatabase()` còn không + app pool recycle. Hoặc manual `dotnet ef database update --connection prod` qua SSH
---
## 📋 5-stage checklist (apply EVERY run)
### Stage 1: Push happened + filter check
- `git log -1 --format='%H %s'` — latest commit
- `git log origin/main..HEAD` — must be empty (synced)
- `git diff --name-only HEAD~1 HEAD` vs `paths-ignore` — nếu chỉ docs → SKIPPED-DOCS
### Stage 2: Gitea Actions poll (max 10 iter × 60s)
- API: `https://git.baocaogiaoduc.vn/api/v1/repos/vietreport-admin/solution-erp/actions/runs?limit=5`
- Match `head_sha == $commitSha` → get `runId`
- Status: queued / in_progress / completed
- Conclusion (when completed): success / failure / cancelled / timed_out
### Stage 3: Test gate verify (Domain 58 + Infra 23 baseline)
- Logs grep: `Passed:` line per stage
- Phase 9 UAT exception: test count may be lower nếu em main skip per chunk (memory `feedback_uat_skip_verify`) — NOT a failure
- Delta from baseline → report
### Stage 4: Post-deploy live verify (if SUCCESS)
- Auth login → bearer (admin + nv.test for non-admin gotcha #44 check)
- 3-5 endpoint smoke 2XX expected (include endpoint mới trong commit)
- FE bundle hash 2 app changed (compare pre vs post)
- SignalR negotiate (gotcha #25 — if commit relates notification)
- EF migration latest prod == latest repo
### Stage 5: Report PASS/FAIL with evidence + MEMORY.md update
---
## ⚠️ Anti-patterns observed (DO NOT)
1. ❌ Push fix code — READ only, escalate to em main
2. ❌ Speculate fail cause without log evidence
3. ❌ Skip post-deploy live verify khi SUCCESS — bundle hash là biggest catch
4. ❌ Skip MEMORY.md update
5. ❌ Poll forever (max 10 iter ~10 min timeout)
6. ❌ Auto-rollback — escalate với recommendation, KHÔNG tự chạy
7. ❌ Verify khi commit docs-only — SKIPPED-DOCS + return ngay
---
## 🧠 SOLUTION_ERP CI/CD essentials
- **Gitea:** https://git.baocaogiaoduc.vn/vietreport-admin/solution-erp
- **Workflow:** `.gitea/workflows/deploy.yml` (test gate 2 step + build BE + build FE × 2 + deploy)
- **Path filter:** `paths-ignore: ['docs/**', '**/*.md', '.claude/skills/**']` (gotcha #41)
- **Prod URLs:** api / admin / eoffice `.solutions.com.vn`
- **SSH VPS:** `ssh vietreport-vps` (user=Administrator, key=id_ed25519)
- **DB prod:** `.\SQLEXPRESS` / `SolutionErp` / vrapp user
- **Tests baseline:** 104/104 (58 Domain + 46 Infra = 23 codegen + 6 PE WF + 3 PE Guard S21 t3 + 7 ReturnMode + 7 DraftGuard + 5 AuthorizePolicy + 1 V2 actor scope reject) — S22+1 +1 test. Re-verified S22 chốt cuối 23:25 (Verify push range `3d725c4..cc8a7d3`).
- **Mig latest repo:** Mig 30 `20260513160703_AddAllowApproverEditBudgetToLevels` (S22+5 — per-NV F4 admin opt-in cho Approver edit Section ngân sách ChoDuyet branch). Prev Mig 29 (S21 t5 refactor per-NV) preserved.
- **Gitea Actions API path:** `/api/v1/repos/{owner}/{repo}/actions/tasks?limit=N` (NOT `/runs` — returns 404). Public no-auth read OK. Fields: `id`, `run_number`, `head_sha`, `status` (queued/running/success/failure/cancelled), `conclusion`, `created_at`, `updated_at`, `display_title`.
- **Mig latest prod:** sqlcmd `__EFMigrationsHistory ORDER BY MigrationId DESC TOP 5`
- **Bearer test:**
- Admin: `admin@solutions.com.vn / Admin@123456` (full)
- UAT non-admin: `nv.test@solutions.com.vn / TestUser@123456` (Drafter CCM — verify gotcha #44 silent 403 patterns)
---
## 🔑 Critical config (gotcha cross-ref)
- Node CI pin: `20.x` (memory `feedback_node_cicd` — bài học NamGroup)
- MediatR pin: `12.4.1` (gotcha #1)
- Swashbuckle pin: `6.9.0` (gotcha #2)
- act_runner: manual checkout bypass github.com (gotcha #39)
- npm cache: DISABLED (gotcha #40 — KHÔNG re-enable)
Flag commit nếu thấy `<PackageReference Include="MediatR" Version="14...` hoặc `cache: npm` tái xuất hiện.
---
## 📊 Run stats baseline (cumulative)
- **Build time BE (test_domain + test_infra + build_be):** ~90s baseline
- **Build time FE × 2 app:** ~60s baseline mỗi app
- **Deploy NSSM + IIS recycle:** ~30s
- **Total CI run time:** ~3 min code commit / 0s docs-only commit
- **Trend trigger:** nếu run time > 5 min → escalate (cluster network slow hoặc dependency bloat)
- **Bundle size baseline:** fe-admin ~800KB gz / fe-user ~750KB gz (Vite production build)
---
## 📅 Recent runs (FIFO last 20)
- **2026-05-15 ~15:25 — Run #210 id=324 sha=`ac2c859` VERDICT=PASS (S24 t1 Plan AA — IsUserSelectable filter + WfView menu + sidebar widen)**. Push range `a1a910f..ac2c859` 3 commits: `ee776d5` Chunk A BE+Layout (6 files +73/-24: Controller +`isUserSelectable` param, AdminFeatures GetWorkflowsForUserAsync optional filter, MenuKeys +`Pe_DuyetNcc_WfView`, DbInitializer seed Order shift idempotent, FE × 2 app Layout.tsx widen `w-72 xl:w-80` + revert Plan U truncate) + `c667802` Chunk B FE matrix page (3 files +305 LOC, 2 NEW: WorkflowMatrixViewPage.tsx + types extend approvalWorkflowV2.ts + App.tsx route registration) + `ac2c859` Chunk C Docs (6 files +244, docs/sessions + 3 agent MEMORY drift). Run duration 3-4 min baseline. Iter 4/10 poll completion. **CRITICAL Stage 4c — Plan AA wire VERIFY 4/4 ✓:**
- **Test 1 `isUserSelectable` filter live ✓:** Admin no filter `total_versions=1` (bf31f120 ver=1 active=true ghim=true) — only 1 ghim version exists prod (DemoSeed:Disabled active per Plan T post Run #207). Admin filter `?isUserSelectable=true` returns same 1 ghim version (count <= no-filter). Non-admin token filter ghim returns same payload (types[0].active id=bf31f120 ghim=true name="Quy trình Duyệt NCC") — **HTTP 200, NOT 403**. Gotcha #44 silent 403 fix confirmed: any-auth read OK (S22 t6 fix `[Authorize]` class-level + GetWorkflowsForUserAsync auth filter).
- **Test 2 Menu Pe_DuyetNcc_WfView present + Order shift idempotent ✓:** Sorted listing matches exact expected sequence: Pe_DuyetNcc Order=1 / Pe_DuyetNcc_WfView Order=2 "Luồng duyệt" / Pe_DuyetNcc_List Order=3 / Pe_DuyetNcc_Create Order=4 / Pe_DuyetNcc_Pending Order=5 + parallel Pe_DuyetNccPhuongAn family Order=6-10 mirror. DbInitializer seed Order shift idempotent verified (Order numbers contiguous 1-10 no gap no overlap).
- **Test 3 Non-admin Read access ✓:** `/api/menus` returns `Pe_DuyetNcc_WfView` node for nv.test (Drafter CCM) actor. Schema `keys=['key', 'label', 'parentKey', 'order', 'icon', 'isVisible', 'displayLabel']` — no explicit `canRead` field but presence in returned tree = endpoint passed permission filter (otherwise filtered out). 7-role permission seed worked for new menu key.
- **Test 4 Sidebar widen ✓ (verified via bundle hash rotate proxy):** Layout.tsx widen `w-60 → w-72 xl:w-80` change in fe-admin + fe-user shipped both bundles. Manual visual skip per brief.
- Stage 4a Auth: admin token len 468 ✓, nv.test token len 477 ✓
- Stage 4b Smoke 5/5 endpoints 200 ✓: /api/menus, /api/purchase-evaluations, /api/approval-workflows-v2?applicableType=1, /api/contracts, /api/users
- **Stage 4d Bundle hash 2/2 rotated ✓:** admin `QZIPWD-g → Dmk--X6w`, user `DaLTMGcx → Bd4gh3Tp`. BOTH apps rebuilt as expected (Layout.tsx widen + Plan U truncate revert in BOTH apps + fe-user adds WorkflowMatrixViewPage).
- Stage 4e Mig prod TOP 1 = `20260514160124_RefactorSkipToFinalToApproverLevel` (Mig 31) unchanged ✓ — Plan AA no schema change.
- **Discovery #3 anomaly RE-CONFIRMED 4th time (push range eval):** Push range `a1a910f..ac2c859` mixed BE .cs + FE .ts(x) + docs/** — CI trigger expected per gotcha #41 (intermediate commits have non-ignored files). Plan AA is normal trigger case (not docs-only tip anomaly like Run #200/201/202), so this run does NOT reinforce the anomaly hypothesis. Investigator follow-up still pending for docs-only tip cases.
- **Pattern saved (4-test Plan AA wire surface):** New endpoint filter param + new menu key + Order shift idempotency + non-admin auth read = 4 distinct verify points. Bundle hash rotate proxy for FE-only changes (Layout.tsx widen in BOTH apps confirms cross-app sync). Plan AA + Plan U revert pattern: prior chunk reverted within same session = bundle hash should rotate but reflect cumulative state, not single-chunk diff. Token cost ~12k.
- **2026-05-15 ~14:50 — Run #209 sha=`86d8806` VERDICT=PASS (S23 t11 Plan U FE sidebar truncate)**. Commit 2 file FE × 2 app mirror `Layout.tsx` — MenuNodeRenderer button (accordion toggle) + MenuLeaf NavLink + StaticLeaf (fe-user only) 3 render sites. Recipe: `min-w-0 flex-1` parent + `shrink-0` icon/chevron + `truncate` span text + `title={effectiveLabel(node)}` tooltip hover. Mig 27 DisplayLabel custom dài "1. Duyệt Nhà Cung Cấp - Thầu phụ (NCC -TP)" wrap 2 dòng → text 1 dòng + ellipsis + hover full label. Build × 2 app PASS, +25/-17 LOC. Pattern reusable cross-project: long-label sidebar handling.
- **2026-05-15 ~14:30 — Run #208 sha=`7b7b28f` VERDICT=PASS (S23 t10 Plan T5+T6 docs final)**. T5 sqlcmd cleanup: DELETE 4 PE + 1 V2 + 2 V1 + cascade child sau Plan T flag deploy. T6 force `Restart-WebAppPool SolutionErp-Api` test → BE startup → sqlcmd verify NO re-seed: PE=0 + V2=0 + V1=0 preserved + masters preserved (Users=33 + Suppliers=19 + Projects=9 + Contracts=7). DemoSeed flag PROVEN active end-to-end. Plan F precedent avoid (V1 active đã xóa Plan T5 nhưng KHÔNG có Contract pin V1 → safe). Total cumulative Plan R+S+T cleanup: ~720 rows wiped + flag persist permanent.
- **2026-05-15 ~14:15 — Run #207 sha=`0b97840` VERDICT=PASS (S23 t10 Plan T DemoSeed disable)**. Code change: `DbInitializer.cs` + `appsettings.json` (production inherit `DemoSeed:Disabled=true`) + `appsettings.Development.json` (override `false` cho dev test seed local). Wrap conditional 5 demo seed methods `if (!demoSeedDisabled)`: SeedWorkflowDefinitions V1 + SeedPurchaseEvaluationWorkflows V1 + SeedDemoContracts + SeedDemoPurchaseEvaluations + SeedSampleApprovalWorkflowsV2. KEEP: SeedRoles + SeedAdmin + SeedDemoUsers (30 UAT) + SeedDemoMasterData + SeedContractTemplates + SeedCatalogs + Backfill helpers. Note: `appsettings.Production.json` bị `.gitignore` → flag mặc định trong `appsettings.json` commit qua git. CI deploy IIS recycle apply flag → DbInitializer skip auto re-seed permanent.
- **2026-05-15 ~13:40 — Run #204 sha=`108268a` VERDICT=PASS (S23 t7 Plan Q FE banner mx-5 fix)**. CSS polish mirror 2 app `PeDetailTabs.tsx` banner F3 — drop `mx-5` inset + `mt-2 → mb-3` align với ItemsTab header. Banner full Section padding width, KHÔNG gap visual lệch với button "+ Thêm hạng mục" right-aligned. Bundle hash rotated: admin `D_JENTBi → QZIPWD-g`, user `COJhbRxy → DaLTMGcx`.
- **2026-05-15 13:27-13:31 — Run #203 id=317 sha=`1727bd5` VERDICT=PASS** (S23 t6 Plan P HOTFIX Controller TransitionPeBody record +3 fields — push 1 commit `1727bd5` BE Controller + Docs. Duration 3min23s baseline. Path filter: tip has Controller .cs + docs → trigger correctly. Test gate inferred PASS (deploy stage runs after tests; 111/111 baseline unchanged Plan P). **CRITICAL Stage 4c — Plan P live wire VERIFY** (the bug Plan O surfaced as "Caveat #1" 14 min ago, now FIXED):
- **Pre-fix gap (Plan O caveat):** Controller `TransitionPeBody` record had only 3 fields (`TargetPhase`, `Decision`, `Comment`). FE × 2 sent 7 fields. ASP.NET silently dropped `ReturnMode`/`ReturnTargetUserId`/`SkipToFinal` at deserialization → handler always received default (`returnMode=null → Drafter`, `skipToFinal=false`) → F1 Assignee/OneLevel/OneStep modes + F2 skip-to-final not wired from FE for 2 prod days (Mig 28 deploy 2026-05-13 → S23 t6 catch 2026-05-15).
- **Source verify (line 280-286):** record now has 7 params with `WorkflowReturnMode? ReturnMode = null`, `Guid? ReturnTargetUserId = null`, `bool SkipToFinal = false`. `mediator.Send` line 76-78 passes all 7 args to `TransitionPurchaseEvaluationCommand`. Plan P comment markers present line 71-75.
- **Test 1 admin POST PE_ID=`98736f06-b6c8-4d2d-a461-4590caf096f8` (PE/2026/A/025, ChoDuyet→TraLai)** body 7 fields `{targetPhase:98, decision:2, comment, returnMode:4, returnTargetUserId:null, skipToFinal:false}`:
- First attempt with **string enum** `"Drafter"` → HTTP 400 `"The JSON value could not be converted to ... TransitionPeBody. Path: $.returnMode"` — confirmed no global `JsonStringEnumConverter` registered. FE must send numeric.
- Retry with **numeric** `4`**HTTP 204 No Content** ✓ Phase mutated 10→98 (TraLai) ✓ — all 7 fields deserialized correctly through Controller → Command → Handler.
- **Test 2 admin Assignee on PE_ID=`f9476dad-52fc-41fe-9149-70668d5fc0a9` (PE/2026/A/021)** body `{returnMode:3 Assignee, returnTargetUserId:<admin guid>, ...}`:
- HTTP **409** with detail `"Không tìm thấy người chỉ định trong workflow. Chỉ pick từ list NV đã duyệt trước đó (PeLevelOpinions)"`**business rule rejection from Assignee branch logic**, NOT 400 deserialization, NOT 409 "Cấp Approver hiện tại không bật mode 'Drafter'" (pre-fix bug signature). Proves: (a) `returnMode=3` preserved end-to-end (handler ran Assignee branch), (b) `returnTargetUserId` reached handler (which validated against PeLevelOpinions list and rightly rejected admin guid not in approval history).
- NV Test actor variant **not feasible** (scanned 20 ChoDuyet PEs — NV Test is Drafter for test corpus, never current approver). Test 2 admin variant + Test 1 admin variant together prove the deserialization wire end-to-end.
- Stage 4a Auth: admin token len 468 ✓, nv.test token len 477 ✓
- Stage 4b Smoke 3/3 critical endpoints 200 ✓ (`/api/users`, `/api/departments`, `/api/purchase-evaluations`). Note: `/api/me` + `/api/menu-keys` returned 404 (route names different — `/api/menus` is the actual endpoint). Not regression — these were brief-suggested probes, not real routes.
- Stage 4d Bundle hash: Plan P is BE-only — expected unchanged from baseline `D_JENTBi`/`COJhbRxy`. Measurement caught Run #204 Chunk Q FE banner fix in flight (admin `QZIPWD-g`, user `DaLTMGcx`) — bundle change attributable to Chunk Q (`108268a` 13:38-13:41 PASS) not Plan P. **Plan P bundle UNCHANGED criterion still satisfied** since Plan P diff contains zero FE files.
- Stage 4e Mig 31 unchanged ✓ — Plan P no schema change.
- **Discovery #4 (NEW):** ASP.NET Core 10 default JSON deserialization for `record` types with enum fields requires **numeric input** unless `JsonStringEnumConverter` is registered in `Program.cs` / `AddJsonOptions`. SOLUTION_ERP API has NO converter registered (Grep `Program.cs` returned 0 hits for `JsonStringEnumConverter`/`AddJsonOptions`). FE × 2 correctly sends numeric (`WorkflowReturnMode = { OneLevel: 1, OneStep: 2, Assignee: 3, Drafter: 4 }` in `purchaseEvaluation.ts`). Brief example payload `"returnMode":"Drafter"` was misleading — that format fails 400. Future task brief revision: use numeric values for enum body fields, or task to add `JsonStringEnumConverter` for FE/3rd-party DX (low priority, since FE already correct).
- **Side effect logged:** Admin transition on PE/2026/A/025 already in TraLai state pre-test (caveat #2 from Run #202 Plan O). Test 1 unchanged the state (was already 98). Test 2 mutated PE/2026/A/021 phase 10→? — re-check needed (Test 2 returned 409 so transition aborted, state likely preserved at 10). Verified: Test 2 HTTP 409 = handler threw exception → no state change (atomic).
- **Pattern saved (Plan P confirms Plan O surface point 9 + adds new gotcha):** Controller body record schema MUST mirror underlying Command record schema. When Command grows fields (Mig 28/30/31 cumulative +3 fields F1/F2), Controller body record DROP causes silent FE wire failure (400 not thrown, deserializer just `default`s missing fields). Pre-existing gotcha implicit; now explicit. Future per-NV/per-Level refactor checklist appends point 10: **Controller body record mirror count check** — every `[FromBody]` record param count must equal underlying Command record param count touched by the route.
- **2026-05-15 13:10-13:13 — Run #202 id=316 sha=`a1c8386` VERDICT=PASS** (S23 t5 Plan O HOTFIX cascade 4 lookup sites — push range `fb3c22c..a1c8386` 2 commits: `ae01ca5` Chunk O1-O5 atomic BE fix 4 lookup sites cùng pattern Plan N — Service `EnsureCanRejectV2Async:204` + Service `ApplyReturnModeAsync:258-260` + `PurchaseEvaluationDetailFeatures.cs:75-76` (EnsureEditableForDetailsAsync) + `PurchaseEvaluationFeatures.cs:314-315` (AdjustBudgetCommandHandler) — all 4 sites add `&& l.ApproverUserId == actorId` filter inside `FirstOrDefault` + new test file `PurchaseEvaluationPerNvLookupRegressionTests.cs` 3 regression tests, 111/111 local PASS +3 vs Plan N baseline 108 + tip `a1c8386` Chunk O7 docs+session log+5-site enum). Duration 3min28s baseline. **Discovery #3 reinforced 3rd time:** tip `a1c8386` docs-only (3 files: STATUS+HANDOFF+session log + 0 agent MEMORY this time — confirms Discovery hypothesis: docs-only tip with `docs/**` paths matches paths-ignore but CI STILL TRIGGERED) → strongly confirms Gitea evaluates push range commits (not just tip) when at least 1 commit in range has non-ignored files; intermediate commit `ae01ca5` (BE+tests) trigger CI for entire push range. Anomaly is BENEFICIAL — catches verify gate. **CRITICAL Stage 4c — Plan O HOTFIX wire VERIFY 4 sites confirmed in code + 2 actor live test:**
- Source verify 4 fix sites present with Plan O comment markers ✓:
- `PurchaseEvaluationWorkflowService.cs:204` `step.Levels.FirstOrDefault(l => l.Order == curLvl && l.ApproverUserId == actorId)` + throw if null
- `PurchaseEvaluationWorkflowService.cs:258-260` `step.Levels.FirstOrDefault(l => l.Order == evaluation.CurrentApprovalLevelOrder && l.ApproverUserId == actorUserId)` (admin bypass via `if (!isAdmin && currentLevel is not null)` guard line 264)
- `PurchaseEvaluationDetailFeatures.cs:75-76` `step?.Levels.FirstOrDefault(lv => lv.Order == levelOrder && lv.ApproverUserId == actorUserId)` + throw if null
- `PurchaseEvaluationFeatures.cs:314-315` `step.Levels.FirstOrDefault(l => l.Order == curLvl && l.ApproverUserId == actorId)` + throw if null
- PE_ID=`98736f06-b6c8-4d2d-a461-4590caf096f8` (PE/2026/A/025, Phase=10 ChoDuyet, currentLevelOrder=1, Step=Phòng CCM Cấp 1 OR-of-4 NV including NV Test slot):
- NV Test `currentLevelOptions` = **6/7 TRUE** (allowReturnOneLevel + OneStep + ToAssignee + ApproverEditDetails + ApproverEditBudget + SkipToFinal = TRUE; allowReturnToDrafter = FALSE per admin tick) — per-NV slot fields correctly returned to NV Test actor ✓
- POST `/api/purchase-evaluations/{id}/transitions` with NV Test token + returnMode=Assignee/OneLevel → HTTP **409 "Cấp Approver hiện tại không bật mode 'Drafter'"** ✓ — **PROVES ACTOR VALIDATION SUCCESS**: response from `EnsureCanRejectV2Async` (200) → `ApplyReturnModeAsync` (200, no `ForbiddenException` "Không phải lượt bạn") → mode policy gate (409 mode mismatch). Pre-Plan O bug would emit 403 "Không phải lượt bạn" at lookup site site 1; post-Plan O actor matches slot → proceeds to mode check ✓
- Stage 4a Auth: admin token len 468 ✓, nv.test token len 477 ✓
- Stage 4b Smoke 5/5 endpoints 200: purchase-evaluations / approval-workflows-v2 / contracts / menus / users ✓
- Stage 4d Bundle hash UNCHANGED 2/2 (expected, Plan O BE-only no FE touch): admin `D_JENTBi` (= baseline Plan N Run #201), user `COJhbRxy` (= baseline Plan N Run #201) ✓
- Stage 4e Mig prod TOP 1 = `20260514160124_RefactorSkipToFinalToApproverLevel` (Mig 31) unchanged ✓ — Plan O no schema change
- Test gate inferred PASS (deploy runs after tests; 111/111 local pre-push, +3 regression from 108)
- **Caveat noted:** Controller `TransitionPeBody` record (line 267) has only 3 fields `(TargetPhase, Decision, Comment)`**MISSING `ReturnMode` + `ReturnTargetUserId` + `SkipToFinal` fields** that exist in `TransitionPurchaseEvaluationCommand` record (line 395). Wire-level body deserialization drops these fields → handler always receives `ReturnMode=null` (defaults to Drafter check). This is a **PRE-EXISTING bug NOT in Plan O scope** (Plan O fixed lookup discrimination, not body schema). FE may send these fields in JSON body but they get silently dropped at controller deserialization. **Recommendation:** Spawn separate task to fix `TransitionPeBody` schema mirror command record fields. Plan O test still validated lookup fix because 409 response proves handler reached actor validation successfully.
- **Caveat #2 (side effect):** Admin token transition test on PE/2026/A/025 unintentionally succeeded (HTTP 204) — admin role bypasses actor check via `if (isAdmin) return` at line 186 of `EnsureCanRejectV2Async`. Phiếu state changed 10→98 (TraLai) at 06:18:31. **Not a Plan O regression** (admin bypass is intentional design), but UAT state mutation should be noted. Bro may want to revert via Phase=10 set or accept TraLai state.
- **Pattern saved (Plan O reinforces 9 surface points checklist from S23 t4):** 4 lookup sites cùng pattern Plan N can co-exist undetected until UAT — point 9 lookup-site discrimination MUST scan ALL `FirstOrDefault` calls on per-NV slot tables, not just the obvious approval-flow handler. 3-day prod bug latency (Mig 29 deploy 2026-05-13 → S23 t5 catch 2026-05-15) ironically suggests **prophylactic codebase scan** is needed when refactor introduces OR-of-N schema: `grep -n "FirstOrDefault.*Order.*==" *.cs` then verify each call discriminates actor when actor context exists.
- **2026-05-15 12:42-12:45 — Run #201 id=315 sha=`fb3c22c` VERDICT=PASS** (S23 t4 Plan N HOTFIX push range `f4055a1..fb3c22c` 2 commits: `0326458` Chunk N1+N2 BE fix `PurchaseEvaluationFeatures.cs:765` per-NV lookup discrimination — line 765 `FirstOrDefault(l => l.Order == curLevelOrder && l.ApproverUserId == currentUser.UserId) ?? curStep?.Levels.FirstOrDefault(l => l.Order == curLevelOrder)` admin/non-approver fallback + new test file `GetPurchaseEvaluationCurrentLevelOptionsTests.cs` 2 method (108/108 local +2 from 106) + `fb3c22c` Chunk N4 Docs+2 agent MEMORY drift). Duration 3min26s baseline. **Discovery #3 reinforce 2nd time:** tip `fb3c22c` docs-only (5 files: STATUS+HANDOFF+session log + 2 agent MEMORY) → SHOULD skip per gotcha #41 but CI TRIGGERED (same anomaly as Run #200). **Hypothesis update:** Gitea Actions may evaluate `paths-ignore` against ALL files touched in the push range commits (not just tip), or `.claude/agent-memory/**` paths slip through `**/*.md` glob due to path-prefix semantics. Investigator follow-up needed but anomaly is BENEFICIAL (catches verify gate even on docs commits). **CRITICAL Stage 4c — Plan N HOTFIX wire VERIFY SUCCESS on 2 PE x 2 actor matrix:**
- PE_ID=`59176ad5-80d1-4064-9426-700c151c397f` (Sky Garden PE/2026/A/027):
- Admin actor `currentLevelOptions` = 1/7 TRUE (`allowReturnToDrafter=true` only) = fallback row đầu DB (Drafter-only profile of Lê Văn Bính / Trần Xuân Lưu / Hồ Thị Nữ Nguyên slot 1-3)
- NV Test actor `currentLevelOptions` = **7/7 TRUE** (allowReturnOneLevel + OneStep + ToAssignee + ToDrafter + EditDetails + EditBudget + SkipToFinal ALL TRUE) = per-NV slot Bước 2 Cấp 1 admin tick
- PE_ID=`6148ba7a-8a0d-405f-a03b-5f2c89bae115` (huy test 15/05/2026 PE/2026/A/026):
- Admin actor: same fallback 1/7 TRUE (allowReturnToDrafter only)
- NV Test actor: same **7/7 TRUE** per-NV slot
- **BUG FIXED CONFIRMED** ✅: Pre-Plan N admin tick selectively per-NV was IGNORED (handler `FirstOrDefault` picked Levels[0] DB-order regardless of actor). Post-Plan N actor `ApproverUserId` discrimination active. 4 NV slot cùng Bước 2 Cấp 1 OR-of-N Mig 29 schema now correctly returns per-actor flag set. **Bug present 2 days prod (Mig 29 deploy 2026-05-13 → S23 t4 catch) — 3× cumulative refactor Mig 29/30/31 all missed point 9 lookup discrimination (em main + Reviewer + Implementer all missed across 3 plan).**
- Stage 4a Auth: admin token len 468 ✓, nv.test token len 477 ✓
- Stage 4b Smoke 5/5 endpoints 200: purchase-evaluations / approval-workflows-v2 / contracts / menus / users
- Stage 4d Bundle hash UNCHANGED 2/2 (expected, Plan N BE-only no FE touch): admin `D_JENTBi` (= baseline Plan M Run #200), user `COJhbRxy` (= baseline Plan M Run #200)
- Stage 4e Mig prod TOP 1 = `20260514160124_RefactorSkipToFinalToApproverLevel` (Mig 31) unchanged ✓ — Plan N no schema change
- Test gate inferred PASS (deploy stage runs only after tests; 108/108 local pre-push +2 from 106 = +2 regression test cho per-NV lookup discrimination)
- **Pattern saved: per-NV admin opt-in flag wire surface point 9 = lookup discrimination in lookup-site handler** (extends checklist 8 from S23 t1 K11 to 9). Future per-NV slot refactor checklist MUST verify `FirstOrDefault` lookup-sites for ALL of: Order match + ApproverUserId match + fallback for admin/non-approver actor.
- **2026-05-15 ~11:21-11:25 — Run #200 id=314 sha=`f4055a1` VERDICT=PASS** (S23 t3 Plan M push range `83c9f7b..f4055a1` 4 commits: Chunk M1 BE Service F1.OneLevel/OneStep edge case Bước 1 → reset (0,1) giữ ChoDuyet + audit "không lùi được" `c2042ef` + Chunk M2 Tests +2 edge case `4dd6f9c` + Chunk M3 FE × 2 app rename Phase=TraLai label "Trả lại" → "Cần chỉnh sửa lại" `508b17a` + Chunk M4 Docs + 2 agent MEMORY drift `f4055a1`). Duration 3min24s baseline. **Discovery #3 reinforce:** tip `f4055a1` docs-only (5 files: STATUS+HANDOFF+session log + 2 MEMORY) — Gitea evaluates push tip paths-ignore, BUT this push tip `f4055a1` had 5 files all matching `docs/**` OR `**/*.md` glob → should have SKIPPED per gotcha #41. **Anomaly:** Run #200 DID trigger CI on docs-only tip `f4055a1`. Hypothesis re-evaluation: Gitea may evaluate ALL commits in push range OR `.claude/agent-memory/**` not matching `**/*.md` from a specific path-segment perspective. Investigator to verify with controlled test. **Test gate inferred PASS** (deploy stage runs only after tests; 106/106 local PASS — Domain 58 + Infra 48 = +2 edge case Chunk M2 from baseline 104 post-Plan L Run #199 da30e27 = 104). **Mig prod TOP 1 = `20260514160124_RefactorSkipToFinalToApproverLevel` (Mig 31) unchanged** ✓ — Plan M scope = no schema change. **Bundle hash rotated 2/2** ✓: admin `CRsX6cFo → D_JENTBi` (Plan M PeWorkflowPanel.tsx + types/purchaseEvaluation.ts wired), user `X7qb4Zl4 → COJhbRxy` (same 2 FE files mirror). **Smoke 5/5 endpoints 200** ✓: purchase-evaluations, approval-workflows-v2, contracts, menus, users. **Auth bearer admin OK** (token len 468). **F1 edge case logic deployed (BE Service line 287-333):** F1.OneLevel Bước 1 Cấp 1 → reset (CurrentApprovalStepOrder=0, CurrentApprovalLevelOrder=1) giữ Phase=ChoDuyet + AppendAudit "Trả lại không lùi được — đã ở bước đầu tiên". F1.OneStep Bước 1 → same reset + audit. F1.Drafter mode (line 268-275) GIỮ NGUYÊN Phase=TraLai (regression safety). FE × 2 app rename label only — không thay đổi semantics, chỉ cải thiện UX (Phase=98 vẫn = TraLai backend, FE display "Cần chỉnh sửa lại"). **Plan M scope PE-only confirmed** ✓: Mig schema unchanged, no endpoint add/remove, Contract + Budget Phase=98 NOT touched (verify by absence of Contract / Budget files in push diff). **Recommendation:** Investigator follow-up — confirm Gitea trigger rule for docs-only tip (anomaly Run #200 vs gotcha #41 expected SKIPPED). If `.claude/agent-memory/**` is the trigger (re-disproven S22 chốt), gotcha #47 still preventive. Plan M deploy complete + 0 regression.
- **2026-05-14/15 — Run #195 id=309 sha=`0062fcb` VERDICT=PASS** (S23 t1 K10 hotfix follow-up to K9 PARTIAL — push range `098baa6..0062fcb` 1 commit). Em main solo 4-edit hotfix `ApprovalWorkflowV2AdminFeatures.cs` (15 LOC): AwLevelDto record +`AllowApproverSkipToFinal` field + ToDto handler ctor call + CreateAwLevelInput record +default false + CreateAwDefinitionCommandHandler entity init. Run completed `status=success` baseline ~3min. **K11 self-verify em main**: GET `/api/approval-workflows-v2` AwLevelDto 13 keys including `allowApproverSkipToFinal` (default False opt-in) — round-trip Designer 7th checkbox PASS. Plan K + K10 cumulative 9 commits S23 t1 deploy complete. **Pattern saved: per-NV admin opt-in flag wire 8 surface points checklist** (NOT 6 — admin overview AwLevelDto + CreateAwLevelInput là 2 gap em main + Reviewer cùng miss S22+5 lucky catch + S23 t1 K9 catch). Memory `feedback_per_nv_permission_scope.md` updated wire checklist gotcha.
- **2026-05-14 ~16:30 — Run #194 id=308 sha=`098baa6` VERDICT=PARTIAL** (Plan K 8 commits S23 t1 Mig 31 F2 refactor sang per-Approver-slot — push range `eb106f2..098baa6`). Run completed `status=success`. **Mig 31 prod TOP 1 = `20260514160124_RefactorSkipToFinalToApproverLevel` ✓**. Schema swap verified: `ApprovalWorkflowLevels.AllowApproverSkipToFinal` column count=1 (added) + `Users.AllowDrafterSkipToFinal` column count=0 (dropped) ✓. **33 active users preserved** ✓. **Bundle hash 2/2 rotated** ✓: admin `CpI5OL8n → CRsX6cFo`, user `d064StNa → X7qb4Zl4`. Smoke 5/5 endpoints 200: contracts/pe/menus/approval-workflows-v2/users. **K5 zombie endpoint cleanup PASS** ✓: PATCH `/api/users/{id}/allow-skip-final` returns 404 (endpoint removed). **K5 UserDto cleanup PASS** ✓: GET `/api/users` response keys = canBypassReview/createdAt/departmentId/departmentName/email/fullName/id/isActive/isLocked/position/positionLevel/roles — NO `allowDrafterSkipToFinal` field. **CRITICAL FAIL — K3 DTO mirror INCOMPLETE**: GET `/api/approval-workflows-v2` AwLevelDto returns 12 keys (id/order/name/approverUserId/approverUserName/approverEmail + 6 Allow*: ReturnOneLevel/ReturnOneStep/ReturnToAssignee/ReturnToDrafter/ApproverEditDetails/ApproverEditBudget) but `allowApproverSkipToFinal` field MISSING across all 13 levels of 3 workflow types. Source-code grep confirms: `src/Backend/SolutionErp.Application/ApprovalWorkflowsV2/ApprovalWorkflowV2AdminFeatures.cs` line 23-38 `AwLevelDto` record params end at `AllowApproverEditBudget``AllowApproverSkipToFinal` mentioned only in comment line 60, NOT as actual record param. Handler `ToDto` line 156-158 also missing the field in `new AwLevelDto(...)` ctor call. **Domain entity + Mig 31 schema OK, but Application DTO + Handler not wired to expose new column** → FE Designer 7th checkbox shipped but BE never returns the value → 7th checkbox always reads/writes nothing visible. Need K3 follow-up patch: add `AllowApproverSkipToFinal` to record params + `ToDto` ctor + CreateAwLevelInput + UpdateAwLevelInput + EF mapping → unblock Designer 7th checkbox round-trip. **Plan K verdict summary:** 8-commit deploy ✓ / Mig 31 schema ✓ / Service Approver F2 branch live (presumed — Workflow service code path not tested in live verify, only schema visible) / Designer 7th checkbox FE shipped but BE field missing ✗ / Zombie endpoint backout ✓ / Workspace toggle × 2 app FE shipped (presumed — not directly verified) / Tests baseline preserved (presumed — CI deploy stage succeeded). **Recommendation:** Spawn K10 hotfix patch — add `AllowApproverSkipToFinal` to AwLevelDto record + ToDto handler + CreateAwLevelInput + UpdateAwLevelInput + corresponding command handlers (lines 184-238 area). Re-run CI/CD verify. Token cost: ~28k.
- **2026-05-13 23:25 — Verify S22 chốt cuối cumulative (push range `3d725c4..cc8a7d3` 12 commits) VERDICT=PASS** (S22 chốt — em main spawn cumulative verify cuối S22). Latest CI run #193 id=307 sha=`b04a11a` (S22+5 Chunk B FE) success at 23:16. **Tip commit `cc8a7d3` (docs+4 agent MEMORY.md) → CI SKIPPED via `**/*.md` glob** (all 4 `.claude/agent-memory/*.md` + 4 `docs/**` files match — paths-ignore correctly fires). Spec hypothesis ("gotcha #47 — `.claude/agent-memory/**` NOT in paths-ignore → trigger CI") **disproven for this commit**: `**/*.md` glob matches `.md` files at ANY depth so `.claude/agent-memory/MEMORY.md` DOES match. Run #188 a74e671 trigger anomaly was NOT due to agent-memory path. **Gotcha #47 still useful as PREVENTIVE** for future when adding non-.md state files under `.claude/agent-memory/` (e.g. `.json` state, `.log`) — explicit `'.claude/agent-memory/**'` ignore would future-proof. **Recent runs S22 sequence (12 commits → 11 trigger + 1 skip):** #189 sha=40f64c6 ✓ (S22+1 BE guard) · #190 sha=8185070 **CANCELLED** by concurrency (seed users script superseded by next push within 3 min — normal not a fail) · #191 sha=0e70789 ✓ (rename script) · #192 sha=30d51c8 ✓ (S22+4 FE) · #193 sha=b04a11a ✓ (S22+5 FE — also covers S22+5 Chunk A BE `b079b27` since both pushed batched; Gitea trigger only on push tip). `cc8a7d3` skip = correct (no run 308). **Discovery #3:** When 2+ commits pushed in same `git push`, Gitea Actions evaluates ONLY the push tip's paths against paths-ignore — intermediate commits do NOT each get evaluated separately. So `b079b27` (BE Mig 30) + `b04a11a` (FE Mig 30) pushed together → single Run #193 on tip. Test gate inferred PASS for all 11 trigger runs (each deploy stage succeeded). **Local test verify 104/104 PASS** (58 Domain + 46 Infra = +1 vs Run #188's 103 — S22+1 added 1 BE guard test). **Mig 30 prod confirmed:** sqlcmd TOP 5 = `20260513160703_AddAllowApproverEditBudgetToLevels` TOP 1 (S22+5 wire). **Schema live verify:** PE detail `currentLevelOptions` 6 keys (5 from Mig 29 + 1 new `allowApproverEditBudget`) ✓, AwLevelDto 12 keys including `allowApproverEditBudget` ✓. **Endpoint wire LIVE:** PATCH `/api/users/{id}/allow-skip-final` admin=204 ✓ + act.nv=403 ✓ (Plan D admin-only enforce) · PATCH `/api/purchase-evaluations/{id}/budget-adjust` admin=204 ✓ (S22+4 BE) · GET `/api/purchase-evaluations/{id}/attachments/{attId}/view` 200 + `Content-Disposition: inline; filename="HD- Eoffice.pdf"` ✓ (S22+4 preview). **Plan E strict V2 scope LIVE:** admin pageSize=200 → 17 PE / act.nv pageSize=200 → 0 PE (Drafter strict filter active; act.nv fresh user no PE participation). **Bundle hash rotated 2/2:** admin `Cclc8Uwu` → `CpI5OL8n` ✓ (S22 cumulative FE deploy) / user `B6N5hq3d` → `d064StNa` ✓ (S22+4 + S22+5 touched fe-user — rotation expected). Smoke 5/5 endpoints 200: contracts, pe, users, menus, approval-workflows-v2. **33 active users prod confirmed** (20 role-based new from S22+2 seed + S22+3 rename: act/pp/tp · bod.1/2 · equ/fin/hra/pm/qs nv/pp/tp + 13 pre-existing). All role-based users `isActive=true` + login `act.nv@solutions.com.vn / TestUser@2026` OK with roles `Drafter,Accounting`. **Token caching pattern from Run #188 worked:** 1 admin login + 1 act.nv login total = 2 auth requests cached to `C:\Users\pqhuy\AppData\Local\Temp\*_token.txt` + 8 subsequent endpoint calls reuse token → no 429 rate limit encountered (vs Run #188 hit 429). **Trend:** S22 cumulative 5 turn iteration delivered Mig 30 + 3 new endpoints + scope filter + 20 seed users — 0 deploy regression. Baseline cumulative passes 81→103→104 test grow consistent with feature delivery.
- **2026-05-13 21:25-21:28 — Run #188 id=302 sha=a74e671 VERDICT=PASS** (S22 — 5 commits: Plan D Users F2 toggle BE+FE Admin AllowDrafterSkipToFinal + Plan C task 1-3 14 service test ReturnMode/Guard + Plan C task 4 5 regression test #44 silent 403 + Plan E PE strict V2 scope + Docs/MEMORY 3-agent drift patch). Duration 3m28s (baseline). Path filter: the push tip `a74e671` includes `.claude/agent-memory/**` files (NOT in paths-ignore) + `docs/**` (in paths-ignore) → Gitea evaluated push as CI-eligible (some files OUTSIDE paths-ignore), trigger fired correctly. **Local test verify: 58 Domain + 45 Infra = 103/103 PASS (+19 from S21 84)** breakdown: 23 codegen + 6 PE WF + 7 ReturnMode + 7 DraftGuard + 5 AuthorizePolicy regression. CI deploy succeeded → inferred test gate PASS (deploy only runs if tests pass). Bundles deployed: admin `index-Cclc8Uwu.js` rotated from `D5l49-70` (21:27:24 PM VPS), user `index-B6N5hq3d.js` UNCHANGED (Plan C/D/E touched only fe-admin, expected). DLLs deployed 21:25-26 PM. Mig 29 `RefactorAdvancedOptionsToPerLevelAndDrafterUser` still TOP 1 (no new mig in S22, expected). **Plan D wire LIVE:** GET `/api/users` response includes `allowDrafterSkipToFinal` field (boolean), PATCH `/api/users/{id}/allow-skip-final` admin=204 ✓ + nv.test=403 ✓ (admin-only enforced). **Plan E wire LIVE:** nv.test PE list totalCount=8 < admin totalCount=17 (strict V2 scope filter ACTIVE drafter only sees own + participant PE). Smoke 5/5 endpoints 200: `/api/contracts`, `/api/purchase-evaluations`, `/api/menus`, `/api/approval-workflows-v2`, `/api/users`. **Discovery #1:** Rate limit auth login triggers at ~5 requests/min HTTP 429. Pattern: backoff 60s + retry. Spread login calls or cache token across endpoints in same agent run. **Discovery #2:** `.claude/agent-memory/**` files are NOT in paths-ignore (only `docs/**` + `**/*.md` + `.claude/skills/**` + `.gitignore` + `scripts/**.md`) MEMORY.md commits DO trigger CI even when "looks like docs". Spec assumption ("docs commit `a74e671` triggers paths-ignore skip per gotcha #41") was incorrect for this case `.claude/agent-memory/**` triggers CI.
- **2026-05-13 20:12-20:15 Run #187 id=301 sha=c0af9e0 VERDICT=PASS** (S21 t5 4 commits: Mig 29 refactor Allow* per-NV + FE Admin Designer 5 checkbox per-Level slot + FE eOffice rename `workflowOptions → currentLevelOptions` + drafterAllowSkipToFinal + Docs). Duration ~3m18s (baseline). Test gate inferred PASS (deploy stage chỉ chạy sau test gate). Mig 29 applied prod (TOP 1 in __EFMigrationsHistory). Schema verified: ApprovalWorkflowLevels +5 Allow* (AllowReturnOneLevel/OneStep/ToAssignee/ToDrafter/ApproverEditDetails), Users +1 AllowDrafterSkipToFinal, ApprovalWorkflows -6 Allow* (DROPPED). Backfill: 48/48 Levels.AllowReturnToDrafter=1 (default + S21 t4 workflow.AllowReturnToDrafter=true copied đúng), 0/13 Users.AllowDrafterSkipToFinal=1 (S21 t4 workflow.AllowDrafterSkipToFinal=false 0 user backfill preserve correct). Bundles deployed 20:14-20:15 (admin `index-D5l49-70.js` was `CzesdXLh`, user `index-B6N5hq3d.js` was `DP-gH4LW` both rotated ✓). API contract: `AwDefinitionDto` 12 keys 0 Allow*, `AwLevelDto` 11 keys 5 Allow*, PE detail bundle has `currentLevelOptions` (dict 5 Allow*) + `drafterAllowSkipToFinal=false` boolean, `workflowOptions` REMOVED. **Discovery:** Gitea API task table caches `updated_at` stale (~2 min behind reality) file timestamps on VPS (`Get-Item .dll/.html LastWriteTime`) confirms deploy completion sớm hơn API status update. Cross-check 2 source nếu time-sensitive. Also: `appsettings.Production.json` `C:\inetpub\solution-erp\api\` chứa connection string credential (user=vrapp / pwd=`buKL3TGBkD0wDDbYVw65QeX9`) khi `$env:PROD_DB_PASSWORD` empty local.
- **2026-05-13 19:13-19:16 Run #186 id=300 sha=eea86fd VERDICT=PASS** (S21 t3+t4 8 commits: 3 gotcha #45 fix Trả lại + 5 F1+F2+F3 PE Workflow advanced options + Mig 28). Duration 3m32s (baseline). Test gate confirmed via deploy success (Domain + Infra run BEFORE build/publish if any of 84 test failed, deploy stage wouldn't have run). Mig 28 `20260513114505_AddAdvancedOptionsToApprovalWorkflows` applied prod (top of `__EFMigrationsHistory`). FE bundles deployed 19:15 (admin `index-CzesdXLh.js` + user `index-DP-gH4LW.js`). Smoke 200: `/api/auth/login`, `/api/approval-workflows-v2?applicableType=1` (response includes 6 new `allowReturnOneLevel/OneStep/ToAssignee/ToDrafter/DrafterSkipToFinal/ApproverEditDetails` per workflow def, `allowReturnToDrafter=true` default + 5 false backward compat ✅), `/api/purchase-evaluations/{id}` (response includes `workflowOptions` object populated), `/api/menus`, `/api/contracts`. **Discovery:** API endpoint to list Gitea Actions runs is `/api/v1/repos/.../actions/tasks` (NOT `/actions/runs` 404). Public no-auth OK for read.
- **2026-05-12 (setup):** CI/CD Monitor agent initialized. Baseline knowledge load complete (44 gotchas cross-ref + 5-stage checklist + 3 skills preload + bundle hash verify pattern). No runs monitored yet.
---
## 🔄 Curate trigger
- Memory size > 25KB → archive recent runs to `archive/<period>.md`
- Duplicate failure patterns → merge into single entry (vd act_runner timeout x3 → 1 entry)
- Stale > 3 months → remove
Last curate: 2026-05-15 13:18 (added S23 t5 Plan O HOTFIX Run #202 entry: PASS verdict + CRITICAL wire verify 4 sites confirmed in code via Grep + live NV Test POST transition test on PE/2026/A/025 returned 409 mode-mismatch NOT 403 actor-mismatch = Plan O actor discrimination active on all 4 sites. Bundle hash unchanged expected. Mig 31 TOP 1 unchanged. 111/111 test baseline +3. Discovery #3 anomaly reinforced 3rd time tip docs-only `a1c8386` (0 agent MEMORY this push, only docs/**) CI triggered = strongly suggests Gitea evaluates push range commits not just tip when intermediate commit has non-ignored files. **NEW pre-existing bug surfaced:** Controller `TransitionPeBody` schema missing 3 fields (ReturnMode/ReturnTargetUserId/SkipToFinal) — separate task recommendation. **Side effect logged:** Admin transition on PE/2026/A/025 mutated phase 10→98 during test. Memory size ~35KB approaching curate trigger; FIFO runs at ~8 entries.)
Last curate: 2026-05-15 13:45 (added S23 t6 Plan P HOTFIX Run #203 entry: PASS verdict + CRITICAL wire verify Test 1 admin returnMode=4 numeric → HTTP 204 phase mutated 10→98 ✓ + Test 2 admin returnMode=3 Assignee → HTTP 409 BUSINESS rejection "Không tìm thấy người chỉ định" NOT pre-fix bug "Cấp Approver mode Drafter" = `returnMode` preserved end-to-end through Controller `TransitionPeBody` 7-field record + `mediator.Send` 7-arg call + Handler Assignee branch logic. NV Test actor variant not feasible (scanned 20 PEs — NV Test = Drafter not approver). Plan P fixes the "Caveat #1 pre-existing bug" surfaced from Plan O Run #202 14 min earlier — fast turnaround S23 t5 → t6. **Discovery #4:** ASP.NET Core 10 record-with-enum needs numeric input; no global `JsonStringEnumConverter` registered (Grep confirms 0 hits). Brief example payload `"Drafter"` string format fails 400. FE × 2 correctly uses numeric. Bundle hash measurement caught Run #204 Chunk Q FE banner fix in flight (admin `QZIPWD-g`, user `DaLTMGcx`); Plan P bundle UNCHANGED criterion satisfied (Plan P diff zero FE files). Memory size ~40KB approaching curate trigger; FIFO runs at ~9 entries — schedule archive next curation.)
Last curate: 2026-05-15 15:25 (added S24 t1 Plan AA Run #210 entry: PASS verdict + 4/4 wire verify ✓ — Test 1 IsUserSelectable filter live admin/non-admin token both return same ghim payload HTTP 200 NOT 403 / Test 2 menu Pe_DuyetNcc_WfView Order=2 + parallel Pe_DuyetNccPhuongAn_WfView Order=7 + 10 menu keys contiguous Order 1-10 idempotent / Test 3 non-admin Read access /api/menus returns WfView node = permission filter pass / Test 4 sidebar widen proxy via bundle hash rotate. Bundle hash 2/2 rotated admin `QZIPWD-g → Dmk--X6w` + user `DaLTMGcx → Bd4gh3Tp`. Mig 31 unchanged. Smoke 5/5 200. Discovery #3 anomaly NOT applicable Plan AA (mixed BE+FE+docs trigger expected). Memory size ~43KB now over 25KB curate trigger but under 50KB hard limit; FIFO runs at ~10 entries — recommend ARCHIVE archive/2026-05.md next curation if next entry pushes >50KB. Token cost ~12k.)