[CLAUDE] Docs+Memory: Session 29 FINAL wrap — Plan CA + Plan B Contract V2 cumulative
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m31s

20 commits S29 push 4 CI Runs PASS (#229+#230 Plan CA, #231+#232 Plan B).
2 big plans END-TO-END deployed prod.

Changes (docs + memory + scripts — CI skip per paths-ignore):

docs/:
- STATUS.md: S29 FINAL wrap header với cumulative summary 20 commits +
  multi-agent ROI ~565K + 8 patterns NEW + state stats (33 mig, 60 tables,
  51 gotcha, 14 AppRoles, 34 active users, 4× bundle rotate)
- HANDOFF.md: S29 FINAL wrap header với end-to-end V2 capability + pending S30+
  follow-up (anh restart CLI MCP RAG hot-reload, UAT verify V2, test bundle
  Plan B, curate dedicated session)
- gotchas.md: +gotcha #51 INFRASTRUCTURE vs DEMO seed phân biệt (Plan B
  Hotfix CICD lesson) với decision tree + seed classification table
- changelog/sessions/2026-05-22-s29-plan-ca-plan-b-contract-v2-wire.md:
  Session log đầy đủ 20 commits + 4× Smart Friend pattern proven + 8
  patterns NEW + file-touched list + NEW capability end-to-end test plan

.claude/agent-memory/:
- 4 MEMORY.md flush S29 wrap entry FIFO each agent perspective:
  - Investigator (25.2 KB just over threshold) — Plan CA + Plan B pre-flight
    2 spawn + 3 patterns NEW (terrain map, V1+V2 coexist, reference templates)
  - Implementer (35.4 KB over hard threshold, defer curate S30) — 5 spawn
    cookie-cutter + E3 stopped + Pattern 12-bis NEW (cross-module entity mirror)
  - Reviewer (23.0 KB compacted) — 4 spawn 2 MAJOR catches + Cat 3 security
    cross-module validation foundation reinforced
  - CICD Monitor (24.9 KB) — 4 Runs verify + CRITICAL DemoSeed gate catch +
    Stage 4.6 sqlcmd seed verify foundation + Discovery #6 gotcha #51 cross-ref
- implementer/pattern_master_page_mirror.md (NEW Plan CA Chunk B Pattern 16-bis)

scripts/:
- plan-ca-{verify-menu,verify-perms,run-perms}.{sql,ps1} (5 verify scripts)
- plan-b-{verify-prod,run-verify}.{sql,ps1} (2 verify scripts)

Smart Friend pattern proven 4× cumulative S22 #44 + S25 #48 + S29 Reviewer
#ApplicableType + S29 CICD #DemoSeed.

Pending S30+:
- Anh restart CLI hot-reload MCP RAG cho 4 sub-agents (commit b51fc94)
- Anh UAT verify V2 contract end-to-end (Drafter → CCM approve → DaPhatHanh)
- Test bundle Plan B (regression ApproveV2Async + ApplicableType validation)
- Curate dedicated session 4 MEMORY (Implementer 35.4 KB priority)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-05-22 13:29:49 +07:00
parent 38f1c4d2d9
commit e199603420
22 changed files with 2216 additions and 153 deletions

View File

@ -29,7 +29,7 @@ Read-only CI/CD pipeline + post-deploy verifier for SOLUTION_ERP. Polls Gitea Ac
- **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)
- **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). **Discovery #3 cumulative 4× S23 t3-t6:** Gitea evaluates push range commits (not just tip) when at least 1 commit has non-ignored files — anomaly BENEFICIAL catches verify gate.
### Gotcha #25 — IIS WebSocket / module exclusion
@ -37,6 +37,13 @@ Read-only CI/CD pipeline + post-deploy verifier for SOLUTION_ERP. Polls Gitea Ac
- **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`)
### Gotcha #48 — Multi-Changelog.Add SQLite tie-break (S25 lesson)
- **Symptom:** Tests using `OrderByDescending(CreatedAt).First()` pick wrong audit row khi same SaveChangesAsync transaction có 2+ Changelog.Add() entries cùng CreatedAt (SQLite frozen-clock).
- **Verify:** Test fail message `Expected changelog.ContextNote not to be <null>` post BE refactor adding new audit Add() alongside existing LogTransitionAsync chain.
- **Fix:** Discriminator filter: `.Where(c => c.Summary!.Contains("Chuyển phase"))` OR `.Where(c => c.EntityType == X)` BEFORE OrderByDescending. Pattern verified Plan AB Chunk A2 (Run #216 PASS).
- **Side benefit:** CI test gate catches BEFORE prod deploy → bro UAT spared broken audit. UAT mode skip-test pattern STILL RISKY khi refactor > 100 LOC touches existing test paths.
### 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")
@ -57,28 +64,38 @@ Read-only CI/CD pipeline + post-deploy verifier for SOLUTION_ERP. Polls Gitea Ac
## 📋 5-stage checklist (apply EVERY run)
### Stage 0: Local RAG infrastructure status (S27 NEW — post NSSM Service mode)
- `Get-Service Qdrant` → must show Status=Running, StartType=Automatic (NSSM Windows Service)
- Nếu Stopped/Paused → `Start-Service Qdrant` (Admin) hoặc `& "D:\.claude-rag\scripts\fix-service-start.ps1"` elevated
- `Invoke-RestMethod http://localhost:6333/healthz -TimeoutSec 3` verify HTTP
### 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`
- API: `https://git.baocaogiaoduc.vn/api/v1/repos/vietreport-admin/solution-erp/actions/tasks?limit=5` (NOT `/runs` — 404)
- Match `head_sha == $commitSha` → get `runId`
- Status: queued / in_progress / completed
- Conclusion (when completed): success / failure / cancelled / timed_out
- **Discovery S21 t5:** Gitea API task table caches `updated_at` stale (~2 min behind reality) — cross-check VPS file mtime nếu time-sensitive.
### Stage 3: Test gate verify (Domain 58 + Infra 23 baseline)
### Stage 3: Test gate verify (Domain 58 + Infra 53 baseline = 111 PASS)
- 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)
- Auth login → bearer (admin + nv.test for non-admin gotcha #44 check). Token field = `accessToken` (NOT `token`). Route = `/api/auth/login` (NOT `/api/v1/auth/login` 404)
- 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
- **NEW Stage 4.6 (S29 Plan B CRITICAL):** sqlcmd seed sample data verify post-deploy — KHÔNG chỉ check schema/Mig table. Phải verify seed data populated khi feature cần sample (V2 workflow Drafter dropdown, etc.). Pattern: `sqlcmd ... -Q "SELECT Code FROM ApprovalWorkflows WHERE Code LIKE 'QT-%-V2-%'"` → 0 rows post-deploy = seed GATE BLOCKED → escalate gotcha #51 INFRASTRUCTURE vs DEMO seed分类.
- **Discovery #4 (S23 t6 Plan P):** ASP.NET Core 10 record types với enum fields cần **numeric input** unless `JsonStringEnumConverter` registered. SOLUTION_ERP API has NO converter — FE × 2 correctly sends numeric (`WorkflowReturnMode = { OneLevel: 1, OneStep: 2, Assignee: 3, Drafter: 4 }`).
- **Discovery #5 (S25 t3 Plan AC):** sqlcmd Windows-auth via ssh requires `\\\\SQLEXPRESS` 4-backslash escape; `\\SQLEXPRESS` produces 0 output silently.
- **Discovery #6 NEW (S29 Plan B CICD):** INFRASTRUCTURE seed (Roles/Depts/Catalogs/MenuTree/AdminPerms/Templates/**SampleWorkflowsV2**) MUST always run — NOT inside `if (!demoSeedDisabled)`. DEMO seed (DemoUsers/DemoContracts/DemoPE) OK gated. Anti-pattern: Implementer mirror PE V2 seed pattern (which IS gated) for Contract V2 → V2 path BLOCKED prod khi `DemoSeed:Disabled=true`. Fix: hoist `SeedSampleContractWorkflowV2Async` out of gate (Run #232 PASS).
### Stage 5: Report PASS/FAIL with evidence + MEMORY.md update
@ -92,7 +109,7 @@ Read-only CI/CD pipeline + post-deploy verifier for SOLUTION_ERP. Polls Gitea Ac
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
7. ❌ Verify khi commit docs-only — SKIPPED-DOCS + return ngay (per Discovery #3 anomaly note)
---
@ -100,13 +117,12 @@ Read-only CI/CD pipeline + post-deploy verifier for SOLUTION_ERP. Polls Gitea Ac
- **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)
- **Path filter:** `paths-ignore: ['docs/**', '**/*.md', '.claude/skills/**']` (gotcha #41 + Discovery #3 anomaly)
- **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`.
- **DB prod:** `.\SQLEXPRESS` / `SolutionErp` / vrapp user. Connection string fallback `C:\inetpub\solution-erp\api\appsettings.Production.json` khi `$env:PROD_DB_PASSWORD` empty local (S21 t5 discovery).
- **Tests baseline:** **111/111 PASS** (58 Domain + 53 Infra — gồm 23 codegen + 7 PE 2-stage + 7 PE N-stage + 6 PE WF + 5 AuthorizePolicy + 4 TraLai + 2 Plan M edge + 1 V2 actor scope reject). S25 unchanged post Plan AB Chunk A2 fix gotcha #48.
- **Mig latest repo:** **Mig 31 `20260514160124_RefactorSkipToFinalToApproverLevel`** (S23 t1 Plan K — F2 swap Users → ApprovalWorkflowLevels per-Approver-slot). Prev Mig 30 (S22+5 F4) + Mig 29 (S21 t5 per-NV refactor) preserved.
- **Mig latest prod:** sqlcmd `__EFMigrationsHistory ORDER BY MigrationId DESC TOP 5`
- **Bearer test:**
- Admin: `admin@solutions.com.vn / Admin@123456` (full)
@ -126,6 +142,25 @@ Flag commit nếu thấy `<PackageReference Include="MediatR" Version="14...` ho
---
## 🎯 Per-NV admin opt-in wire surface — 10-point checklist (cumulative S22+5 → S23 t6)
Cross-ref memory `feedback_per_nv_permission_scope.md`. Future per-NV/per-Level refactor MUST verify:
1. Domain entity field
2. EF config `HasDefaultValue(false)`
3. Migration 3-file rule (Up + Designer + Snapshot)
4. Service handler read field
5. Domain DTO + Application DTO mirror
6. Designer FE checkbox inline (admin)
7. Admin overview AwLevelDto record + ToDto ctor
8. CreateAwLevelInput record + Update mutation handler
9. **Lookup discrimination** in handler (`FirstOrDefault` ADD `ApproverUserId == actorId` filter + admin fallback)
10. **Controller body record** mirror count check (`[FromBody]` record param count = Command record param count)
Bug latency observed when miss points 9-10: 2-3 days prod silent (Mig 28-29 deploy → S23 t4-t6 catch). Prophylactic codebase scan recommended: `grep -n "FirstOrDefault.*Order.*==" *.cs` after OR-of-N schema refactor.
---
## 📊 Run stats baseline (cumulative)
- **Build time BE (test_domain + test_infra + build_be):** ~90s baseline
@ -137,124 +172,41 @@ Flag commit nếu thấy `<PackageReference Include="MediatR" Version="14...` ho
---
## 📅 Recent runs (FIFO last 20)
## 📅 Recent runs (FIFO — slim post-curate 2026-05-22)
- **2026-05-21 (S26 Run #222 PASS Plan AG + 5 subsequent runs em main solo verify):** Run #222 sha=`0bf6c7e` Plan AG Phase 1 PE List tree view 2-level — Stage 4d bundle hash 2/2 ROTATED ✓ (admin `C8TvDy7r → CWHIdoFo`, user `BvcWrq2z → Bg2FNeIz`), Stage 4a admin token 468 + nv.test token 477, Stage 4b smoke 5/5 endpoints 200, Stage 4c PE List API shape preserved (all 9 fields present: maPhieu/tenGoiThau/type/phase/projectId/projectName/selectedSupplierName/contractId/createdAt). Test gate 111 unchanged. Mig 31 prod unchanged. Token ~12k. **Subsequent Plan AG2-AG6 (5 commits `c5429c0..d99069a`) em main solo verify** — CICD KHÔNG spawn per UAT iteration mode `feedback_uat_skip_verify` + cost ~150K spawn × 5 = ~750K wasteful for trivial UI polish. Em main verify mỗi push: git push success + Gitea auto-trigger build → wait ~3-4min deploy → bundle hash visual check (KHÔNG full smoke). Plan AG4 BE+FE cross-stack (DTO + 3 projection JOIN Users+Departments + FE PE card render): dotnet test 111/111 PASS local verify trước push. **Pattern saved: CICD Monitor spawn 1 lần đầu tiên Phase wire (Plan AG initial) — verify bundle rotate baseline + endpoint shape. Subsequent polish chunks (CSS/UX/copy)** trong cùng Plan: em main self-verify bundle visual + smoke 1 endpoint, KHÔNG re-spawn. ROI tốt cho 1 dev solo iteration scenario. **Cumulative S26 6 runs** #222 (AG) + #223 (AG2) + #224 (AG3) + #225 (AG4) + #226 (AG5) + #227 (AG6) verified PASS by em main bundle check + visual confirm bro UAT. 0 prod regression. **Memory size BEFORE update ~72KB, AFTER ~74KB — CRITICAL CURATE next session priority MAX URGENT** (archive Run #186-#221 verbose entries to `archive/2026-05-runs.md` + keep Run #222-#227 + summary cumulative).
- **2026-05-22 (S29 wrap — Run #229-#232 verify PASS + Plan B CICD CRITICAL DemoSeed gate catch — gotcha #51 NEW INFRASTRUCTURE vs DEMO):** S29 cumulative 2 big plans + 4 Run. **Plan CA (admin → eoffice move 7 commits):** Run #229 sha=68bcedd PASS 3m32s (5 chunks A+B+C+D+D2 batch — bundle hash rotate ×2 app + login catalog.manager 200 + JWT claims valid) + Run #230 sha=e55d96b PASS 3m24s (Hotfix 1 resolvePath staticMap — fe-user bundle rotate only, no BE change). **Plan B Contract V2 wire (mirror PE Mig 22-26 11 commits):** Run #231 sha=3e92584 PASS 10 chunks + Hotfix Reviewer — bundle rotate fe-admin `leEMWFLU→BBADl46y` + fe-user `Dgn1iU9E→DA_VI3zO` + Mig 32+33 prod apply confirmed. **Run #232 sha=38f1c4d PASS Hotfix CICD `SeedSampleContractWorkflowV2` out of DemoSeed gate** — CRITICAL CATCH agentId a2ea2e3a5dbe271b5 ~90K: nested inside `if (!demoSeedDisabled)` DbInitializer.cs:105-111, prod `DemoSeed:Disabled=true` (Plan T S23 t10) → seed SKIP → QT-HD-V2-001 KHÔNG tồn tại prod → Drafter Workspace dropdown V2 EMPTY → V2 contract path BLOCKED end-to-end UAT. Smoking gun log: "DemoSeed:Disabled=true → skip workflow + contracts + PE + sample V2 seed (Plan T S23 t10 + Plan B Chunk A2 Contract V2)". CICD Monitor agent fail 529 transient × 2 (Anthropic API overload) Plan CA verify → em main fallback manual smoke Bash curl + sqlcmd direct. **Patterns proven NEW:** Discovery #6 INFRASTRUCTURE vs DEMO seed phân biệt → gotcha #51 sẽ docs. **Smart Friend ROI 4× cumulative:** S22 #44 + S25 #48 + S29 Plan B Reviewer ApplicableType + S29 Plan B CICD DemoSeed gate. **Anti-patterns observed:** (a) Implementer A2 mirror PE V2 seed pattern (gated) cho Contract V2 — Plan B should treat differently vì V2 path BLOCKED if seed skip. (b) Em main miss noticing seed inside DemoSeed gate khi review Chunk A2. 0 prod regression observed S29 cumulative.
- **2026-05-19 13:05-13:08 — Run #221 id=335 sha=`506cada` VERDICT=PASS (S25 t7 Plan AF — FE userMap fallback resolve historical entries pre-Plan AE).** Push range cumulative S25 remote = `e23f51c..506cada` (7 commits Plan AB+AC+AC2+AD+AE+AF since S24). Tip commit Plan AF: 2 FE-only files mirror `fe-user/src/components/pe/PeDetailTabs.tsx` (+74 LOC, ApprovalsTab+HistoryTab userMap useMemo + resolveActorName/resolveUserName helpers) + `fe-admin/src/components/pe/PeDetailTabs.tsx` (+70 LOC mirror §3.9 identical logic). Duration ~3m23s (~baseline). Pre-push em main local: npm build × fe-user PASS 0 TS err (9.12s), npm build × fe-admin PASS 0 TS err (8.91s), BE unchanged from 9ea62be. Status poll 5 iter (13:06→13:08) status `running → success`. **CRITICAL Stage 4c Plan AF wire VERIFY ✓:** Endpoint `GET /api/purchase-evaluations/3248f2f9-c6e9-43ff-a4ca-067ffecf9f36` returns PeDetailBundle with ALL 5 userMap data sources present: `drafterUserId=ce7eb96a` + `drafterName="Nguyễn Văn Duy"` ✓, `approvals` (6 entries) ✓, `levelOpinions` (5 entries) ✓, `departmentOpinions` (0 entries — field present, no rows OK) ✓, `approvalFlow.steps` (3 steps with nested levels.approvers) ✓. FE userMap useMemo will build composite lookup từ embedded domain data, NO extra API contract change. Stage 4a Auth admin: HTTP 200 token len 468 ✓ (password `Admin@123456`, accessToken field). Stage 4b Smoke 5/5 endpoints 200 ✓ (`/api/purchase-evaluations`, `/api/contracts`, `/api/menus`, `/api/users`, `/api/purchase-evaluations/{id}`). **Stage 4d Bundle hash 2/2 ROTATED ✓ as expected (FE touch):** admin `DR95zKWg → C8TvDy7r` ✓, user `BAj_Yaj5 → BvcWrq2z` ✓. Stage 4e Health ready 200 + Health live 200 ✓. Mig prod TOP 1 = `20260514160124_RefactorSkipToFinalToApproverLevel` (Mig 31) unchanged ✓ (Plan AF no schema change). **Discovery 1 — 503 mid-deploy expected:** During IIS publish step API returned 503 Service Unavailable ~13:08:07 (app pool recycle window ~5-15s before deploy stage complete), self-recovered post-deploy 200. Discovery 2 — Auth route NOT `/api/v1/auth/login` (404) but `/api/auth/login` (CLAUDE.md route). Token field NOT `token` but `accessToken`. **Pattern saved Plan AF — FE userMap fallback synthetic recovery for audit trail:** When historical entries with empty `userName` field exist pre-data-fix deploy (Plan AE forward-only fix BE side), apply **FE-only fallback lookup builder pattern** using embedded domain bundle data sources (drafter + approvals + workflow approvers + opinions). Build composite Map<userId, fullName> via useMemo at top of Tab component, expose resolveXxxName(entry) helper: 1) trust entry.userName if non-empty, 2) lookup userMap by entry.userId, 3) fallback "Hệ thống". Reusable cho Contract changelog audit + Budget changelog audit historical recovery without extra API contract change (avoid /api/users admin-only permission constraint for non-admin viewers). Mirror 2 FE app §3.9 identical logic. Token cost ~10k. **Memory size BEFORE update ~69KB, AFTER ~72KB — DEDICATED CURATION SESSION REQUIRED NEXT (archive Run #186-#210 + S22-S24 verbose entries to `archive/2026-05.md` priority MAX REINFORCED).**
- **2026-05-22 12:54-12:58 — Run #231 (id=345) Plan B Contract V2 wire kick-off VERDICT=PARTIAL (PASS deploy + PARTIAL seed):** Push range `6eec8d7..3e92584` 11 commits (1 docs MCP RAG tools sub-agent + 2 Mig BE A1+A2+C + 1 Service B+B2 ApproveV2Async ~150 LOC + 1 DTO E1+E2 + 6 FE × 2 app Workspace D + Section 5 E3 + 1 Hotfix Reviewer ApplicableType=Contract guard). CI status=success duration **3m30s** (12:54:24→12:57:54). **Bundle hash 2/2 ROTATED:** admin `leEMWFLU→BBADl46y` + user `Dgn1iU9E→DA_VI3zO` (FE shipped OK). **Mig 32 + Mig 33 BOTH applied prod:** sqlcmd TOP 2 = `20260522052240_AddContractLevelOpinions` + `20260522051059_AddApprovalWorkflowToContract`. **Contract V2 cols verified:** `ApprovalWorkflowId` + `CurrentApprovalLevelOrder` exist. **ContractLevelOpinions table exists, 0 rows** (no Contract V2 created yet — expected). **API health 200 + 5/5 smoke 200** (contracts/PE/menus/AW-v2 filter / AW-v2 all). **V1 backward compat OK:** 7 contracts V1 preserved untouched.
- **2026-05-19 12:33-12:36 — Run #220 id=334 sha=`9ea62be` VERDICT=PASS (S25 t6 Plan AE — Changelog UserName 9 sites populate via ICurrentUser.FullName fallback Email).** Push range cumulative S25 remote = `e23f51c..9ea62be` (6 commits Plan AB+AC+AC2+AD+AE since S24). Tip commit Plan AE: 2 BE-only files `PurchaseEvaluationFeatures.cs` (+6 LOC, 4 sites: Create×2/UpdateDraft/AdjustBudget) + `PurchaseEvaluationDetailFeatures.cs` (+7 LOC, 5 sites: 1 inside if-block 16-space indent) — uniform pattern `UserName = currentUser.FullName ?? currentUser.Email`. Duration ~3m25s (~baseline). Pre-push em main local: `dotnet test SolutionErp.slnx` PASS 111/111 (~5s). Status poll 4 iter (12:33→12:36) status `running → success`. **CRITICAL Stage 4c Plan AE wire VERIFY ✓:** Endpoint `/api/purchase-evaluations/3248f2f9-c6e9-43ff-a4ca-067ffecf9f36/changelogs` returns 20 entries len 6822 bytes. Response shape has `userName` field accessible ✓. **Pre/Post split observed live:** Recent LogTransition entries (`entityType=5`) at 02:31-02:36 have populated `userName` ("Nguyễn Văn Trường", "Bùi Lê Thủy Trà", "Phan Văn Chương") because LogTransitionAsync already sets UserName independently. **The Budget Adjust entry at 02:31:39 `entityType=1` summary="Điều chỉnh ngân sách: tên..." has `userName=""` empty** — this is the EXACT pre-fix Bug 1 evidence: entry logged by old AdjustPurchaseEvaluationBudgetCommandHandler code BEFORE Plan AE deploy (existing stale entry from Run #216 prod time pre-Plan-AE). Post-Plan AE deploy: new Budget Adjust action will populate userName correctly (FullName fallback Email). Bro test verification: trigger 1 Budget Adjust on any PE → next entry will show userName ✓. Stage 4a Auth admin: HTTP 200 token len 468 ✓. Stage 4b Smoke 5/5 endpoints 200 ✓ (`/api/purchase-evaluations`, `/api/contracts`, `/api/menus`, `/api/users`, `/api/purchase-evaluations/.../changelogs`). **Stage 4d Bundle hash 2/2 UNCHANGED ✓ as expected (BE-only commit):** admin `DR95zKWg` (= Run #219 baseline), user `BAj_Yaj5` (= Run #219 baseline). Stage 4e Health ready 200 "Healthy" + Health live 200 ✓. Mig prod TOP 1 = `20260514160124_RefactorSkipToFinalToApproverLevel` (Mig 31) unchanged ✓ (Plan AE no schema change). **Pattern saved Plan AE — Changelog audit log UserName populate batch:** When detecting systemic gap "Changelog field X not set in N similar Add() sites" (Bug ngày 19/5 bro report Budget Adjust empty user column), do **preventive batch fix across ALL Changelog.Add() sites in same domain** (PE = 9 sites: 4 Features + 5 DetailFeatures), KHÔNG chỉ fix 1 site phát hiện. Pattern reusable for Contract changelog + Budget changelog future (similar entity types with audit trail). Source pattern `UserName = currentUser.FullName ?? currentUser.Email` — assumes ICurrentUser is injected scoped DI (already standard). Side benefit: 8 preventive sites future-proof against same bug pattern. Token cost ~14k. **Memory size BEFORE update ~66KB, AFTER ~69KB — STRONG CURATE REQUIRED next session (defer dedicated curate priority MAX).**
**CRITICAL DISCOVERY #6 (NEW gotcha to add) — DemoSeed feature flag gates Plan B Chunk A2 sample V2 workflow seed:** API log evidence `2026-05-22 12:57:44 [INF] DemoSeed:Disabled=true → skip workflow + contracts + PE + sample V2 seed (Plan T S23 t10 + Plan B Chunk A2 Contract V2)`. Result: **QT-HD-V2-001 NOT seeded in prod** (0 rows ApplicableType=3 — only ApplicableType=1 has 2 workflows QT-DN-V2-001 v1+v2 seeded historically pre-flag). V2 endpoint returns `{"active":null,"history":[]}` cho ApplicableType=3 → FE Workspace Contract Create dropdown EMPTY → user CAN'T pick V2 workflow → V2 wire E2E NOT testable UAT mode. **Fix options (escalate em main):** (a) admin manually create QT-HD-V2-001 via FE Admin Designer V2 UI post-deploy (preferred — production-safe) OR (b) carve seed out of DemoSeed gate (Plan T S23 t10 design opposite — DemoSeed flag intentional UAT clean-state). Decision NOT for CICD Monitor — escalating.
- **2026-05-19 10:21-10:25 — Run #216 id=330 sha=`8c05947` VERDICT=PASS (S25 t2 Plan AB Chunk A2 — fix Plan M tests filter LogTransition entry post Plan AB).** Push range cumulative Plan AB remote = `e23f51c..8c05947` (2 commits Plan AB total). Tip commit Chunk A2: 1 test file `tests/SolutionErp.Infrastructure.Tests/Services/PurchaseEvaluationWorkflowServiceReturnModeTests.cs` +7/-2 LOC — 2 Plan M edge case tests (OneLevel + OneStep AtStep1Level1) add `.Where(c => c.Summary!.Contains("Chuyển phase"))` filter trước `OrderByDescending(CreatedAt).First()` để pick LogTransition entry (cha ContextNote) thay vì Plan AB new Changelog entry (Summary="Đã xử lý Trả lại — ..." no ContextNote, SAME CreatedAt SQLite frozen clock tie-break). Plan AB Chunk A code `cdfd542` KHÔNG bị revert — Bug 1+Bug 2 fix giữ nguyên. Duration ~3m17s (baseline 3-4 min — full pipeline tests + builds + deploy + smoke). Test gate PASS: test_domain 58/58 ✓ + test_infra 53/53 ✓ (2 Plan M tests now PASS — verified live). Build BE + FE × 2 PASS. Deploy NSSM + IIS recycle PASS. **CRITICAL Stage 4c Plan AB cumulative live wire VERIFY 2/2 ✓ (cdfd542 BE + Chunk A2 test fix DEPLOYED via Run #216):**
- Bug 1 Budget Adjust entry LIVE on PE c6e9 (`3248f2f9-c6e9-43ff-a4ca-067ffecf9f36` — task brief id `c8e9` typo, actual `c6e9`): entityType=1 (Header), summary="Điều chỉnh ngân sách: tên 'Ngân sách mới update thử ccm' → 'Ngân sách mới', số tiền 5,200,000,000đ → 5,300,000,000đ [Approver Bước 2/Cấp 2]", userId=Phan Văn Chương ✓
- Bug 2 Return Mode entries LIVE on PE c6e9: multiple entityType=5 LogTransition entries với contextNote chứa "Trả về..." text: "Trả về Bước 1 Cấp 2", "Trả về Bước 2 Cấp 2", "Trà xem lại phần so sánh [Trả về Người chỉ định — Bước 1 (Phòng 1) Cấp 2]" (user comment + auto context append pattern Plan AB Chunk A) ✓
- Stage 4a Auth admin: HTTP 200 token len 468 ✓
- Stage 4b Smoke 5/5 endpoints 200 ✓ (`/api/purchase-evaluations`, `/api/contracts`, `/api/menus`, `/api/users`, `/api/purchase-evaluations/{id}/changelogs`)
- **Stage 4d Bundle hash 2/2 ROTATED ✓:** admin `CZdXQ2eo → CrHpBYFM` ✓, user `DCwhhey2 → C3bCWZ90` ✓ — Plan AB cumulative shipped 2 FE PeDetailTabs.tsx changes from Chunk A `cdfd542` (test file commit doesn't change FE bundle; rotate proxy reflects cumulative Plan AB delta from Run #214 baseline since Run #215 deploy never ran).
- Stage 4e Health live HTTP 200 + Health ready "Healthy" → Mig 31 RefactorSkipToFinalToApproverLevel applied prod unchanged ✓ (Plan AB no schema change as expected).
- **Pattern saved gotcha #48 RESOLVED (Run #215 lesson):** When introducing 2nd Changelog.Add() in same transaction as existing LogTransitionAsync chain, tests using `.OrderByDescending(CreatedAt).First()` with SQLite frozen-clock get NON-DETERMINISTIC pick. Fix verified working in Chunk A2: `.Where(c => c.Summary!.Contains("Chuyển phase"))` discriminator filter restores deterministic test pick. Pattern reusable for future multi-Changelog.Add in same SaveChangesAsync transaction.
- **Side benefit Run #215#216 catch loop:** CI test gate caught Plan AB tie-break bug BEFORE prod deploy (Run #215 fail = bro UAT spared broken Plan M edge cases). Chunk A2 test-only fix shipped surgically without re-touching BE service code. Demonstrates: test-after pattern UAT mode CAN tolerate Plan M edge case bug for 1 chunk if next chunk lands within minutes (10:13 fail → 10:21 fix = 8 min turnaround). Token cost ~12k.
**0 new tests added Plan B (~150 LOC ApproveV2Async NO test cover)** — test gate 111 unchanged baseline (UAT skip-test per `feedback_uat_skip_verify`, but Plan B ApproveV2Async is exact gotcha #48 high-risk pattern: Service refactor > 100 LOC touching changelog/audit paths. Recommend Plan B+1 test addition next chunk before scaling).
- **2026-05-19 10:13-10:15 — Run #215 id=329 sha=`cdfd542` VERDICT=FAIL (S25 t1 Plan AB Chunk A — Changelog visibility fix Bug 1 Budget Adjust + Bug 2 Return Mode). Push range `e23f51c..cdfd542` 1 commit 3 files (1 BE `PurchaseEvaluationWorkflowService.cs` +207/-95 LOC refactor `ApplyReturnModeAsync` + 2 FE `PeDetailTabs.tsx` mirror filter extend). Duration 1m06s (much shorter than ~3-4 min baseline = early test gate fail, deploy stage never reached). **CRITICALTest gate FAIL at test_infra:** 51/53 passed, 2 FAIL same root cause:
- `PurchaseEvaluationWorkflowServiceReturnModeTests.ApplyReturnMode_OneStep_AtStep1_ResetsToBuoc1Cap1_KeepsChoDuyet` (line 350)
- `PurchaseEvaluationWorkflowServiceReturnModeTests.ApplyReturnMode_OneLevel_AtStep1Level1_ResetsToBuoc1Cap1_KeepsChoDuyet` (line 308)
- **Error message:** `Expected changelog.ContextNote not to be <null>`
- **Root cause:** Plan AB Chunk A `ApplyReturnModeAsync` adds NEW Changelog entry at end (line 403-412) for Bug 2 visibility — `EntityType=Workflow + Action=Update + Summary` (NO ContextNote field). After refactor, BOTH ApplyReturnModeAsync (new entry, no ContextNote) AND LogTransitionAsync (line 100, existing entry with ContextNote=comment) are added in same `SaveChangesAsync` transaction. Test fetches `.OrderByDescending(c => c.CreatedAt).FirstAsync()` — with SQLite + frozen test clock both entries get SAME CreatedAt, OrderByDescending tie-break returns Plan AB's Workflow entry (without ContextNote) instead of Transition entry. Plan M edge case tests broken.
- Test gate stages: test_domain PASS 58/58 ✓ / test_infra FAIL 51/53 (-2 from baseline 53) / build_be NOT RUN / build_fe_admin NOT RUN / build_fe_user NOT RUN / deploy NOT RUN — exit status 1
- **Deploy NOT shipped:** Bundle hashes unchanged from Run #214 Plan AA baseline (admin `CZdXQ2eo`, user `DCwhhey2`). Mig prod TOP 1 = `Mig 31 RefactorSkipToFinalToApproverLevel` unchanged ✓. Smoke 5/5 endpoints 200 OK (pre-Plan-AB code stable). PE 3248f2f9... changelogs endpoint 200 returns `[]` empty array (PE may not exist or no log entries — endpoint shape OK).
- **Plan AB Bug 1+Bug 2 fix NOT live** — bro UAT screenshot pre-deploy stale, Plan AB Changelog visibility refactor NOT shipped to prod.
- **Recommendation em main:** Plan AB Chunk B HOTFIX — choose ONE of 2 approaches:
1. **Add `ContextNote` to new Plan AB Changelog entry** (line 403-412): set `ContextNote = summary` (the OneLevel/OneStep edge case "không lùi được" string). Plan M tests pass because either entry now has ContextNote. Caveat: ContextNote may duplicate Summary content.
2. **Skip Plan AB Changelog entry when `comment` would propagate via LogTransitionAsync ContextNote**: only add NEW Changelog for cases NOT covered by Transition entry (vd terminal mode that doesn't trigger LogTransitionAsync). Refactor more conservative, but Plan AB scope says "single uniform log".
3. **Alternative — fix tests to fetch specifically EntityType=Transition Changelog**: `Where(c => c.EntityType == PurchaseEvaluationEntityType.Transition)` then OrderByDescending → pick the Transition entry deterministically. Tests still verify ContextNote via LogTransitionAsync chain.
- **Pattern saved gotcha #48:** When introducing 2nd Changelog.Add() in same transaction as existing LogTransitionAsync chain, tests using `OrderByDescending(CreatedAt).First()` with SQLite frozen-clock get NON-DETERMINISTIC pick → favor `.Where(c => c.EntityType == X)` filter or use deterministic order proxy (Id is Guid not sequential — must use insertion-order via explicit ordering column or filter by discriminator). 4 Mode of Plan AB exposes the assumption.
- **Side benefit:** CI test gate caught this BEFORE prod deploy — bro UAT spared from broken Changelog audit (pre-existing Plan M edge case audit trail would have been broken in prod if test gate skipped per UAT mode). UAT mode skip test-after pattern still RISKY when refactor touches existing code paths with tests.
- Token cost ~10k.
**SlaExpiryJob ERR cluster (5 entries 12:58:16):** pre-existing V1 SLA jobs auto-approve fail on legacy contracts `ConflictException Transition X→Y không hỗ trợ` (DangInKy→DangKiemTraCCM / DangSoanThao→DangGopY / DangGopY→DangDamPhan / DangTrinhKy→DongDau). **UNRELATED to Plan B** (V1 phase enum transitions, no V2 involvement). Pre-existing prod noise — escalate em main for separate investigation if cleanup desired.
- **2026-05-15 ~17:35 — Run #214 id=328 sha=`ee0902a` VERDICT=PASS (S24 t1 Plan AA wrap fix sidebar)**. Last successful deploy before Plan AB. Sidebar label dài wrap về đầu hàng + text smaller. Bundle hashes post-Run-#214 = admin `CZdXQ2eo` + user `DCwhhey2` (baseline for Plan AB delta comparison — KHÔNG rotated since Plan AB failed). Duration ~3m25s.
**0 regression observed prod.** Plan B BE schema + FE wire shipped successfully; seed gap is feature-flag design choice not deploy failure.
- **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-22 (S28 wrap — Layer A governance apply, NO Run S28):** Zero git push remote cả S28 (all local docs + memory + RAG store chunks) → 0 deploy event, bundle hash 2/2 unchanged Run #227 baseline, Mig 31 prod unchanged, 111 test baseline unchanged. **Timeline:** t1 startup 9-step + smoke test pass (registry hot-reload OK post-S27 model:inherit fix + 3,462 RAG chunks indexed) → t2-t4 RAG ROI verdict + over-reach mistake + scope-down → t5 Layer A governance apply. **Layer A 3 rule cụ thể:** (1) **4-category default tags mandatory** cho mọi chunk forward — CICD scope: `pattern`, `gotcha`, `session-wrap`, `cicd` + tag bổ sung `phase-9` + optional `commit:<sha7>` cross-ref + `severity:p0..p3`. (2) **source_path convention** for retrieval: `solution_erp/audit/cicd-<run-id>-<date>` cho per-Run audit chunks; `solution_erp/session/cicd-wrap-<date>` cho session wrap. (3) **Weekly Friday eval ritual** starting **2026-05-29** Friday 5 metric: query/session count + hit rate (rerank ≥0.7) + store noise % + RAG vs MD ratio + Voyage embed cost/week. 10 golden query draft sẵn cross-stack scenarios: Plan B Contract V2 wire kick-off + gotcha #48 SQLite tie-break + per-NV 10-surface checklist + cookie-cutter mirror PE→Contract + controller body record param count + FE merge synthetic Policy V2 + EF backfill idempotent + Smart Friend agent eval + DemoSeed feature flag + Run #215+#216 fail-fix pair pattern. **ABANDONED rule cũ:** "mọi tương tác mandatory RAG" → wastes ~30K query overhead khi single Run verify Bash poll đủ context. **Foundation kept:** 10-surface-point per-NV checklist (S22+5 → S23 t6) vẫn promoted MEMORY foundation cho future per-NV refactor verify. **Forward S28+:** chờ catch first CICD Run đầu Plan B Contract V2 wire khi bro push commit BE/FE/Mig (Mig 32+ expected).
- **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-22 (S27 wrap-up em main proxy - hot-reload pitfall):** NO Run triggered S27 (zero git push remote - all changes local docs + scripts + memory curate). 0 deploy event to monitor. **Meta-discovery em main S27:** Sub-agent registry KHÔNG load trong session active vì 4 file `.claude/agents/*.md` dùng `model: claude-opus-4-7` (200K) + non-standard `effort: max` field → CLI silent reject per VIPIX pitfall #2. Em main solo cả S27 KHÔNG có lựa chọn delegate. Fix applied: 4 file → `model: inherit` + remove `effort: max`. **Pending:** Anh restart Claude Code CLI để hot-reload (pitfall #1 - edit file disk KHÔNG hot-reload session đang chạy). Sau restart, S28+ next push sẽ có CICD Monitor spawn trở lại normal cycle. Pattern reinforced: SESSION START PROTOCOL MUST spawn test agent trước khi assume registry loaded.
- **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-22 (Curate session em main):** Archived Run #186 → Run #221 verbose entries (14 runs S21 t3 → S25 Plan AF) → `archive/2026-05-runs.md`. KEEP: Run #215+#216 (gotcha #48 fail+fix pair lesson critical) + Run #222-#227 S26 summary + setup baseline. Memory size before: ~72KB → after: ~25-28KB target. Cumulative 8 patterns extracted vào archive header + 10-surface-point per-NV checklist promoted to foundation section above.
- **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-21 (S26 Run #222-#227 cumulative — Plan AG series PE List tree view UI iteration):** Hybrid verify pattern: CICD Monitor spawn 1× cho Phase wire initial Run #222 sha=`0bf6c7e` Plan AG (~12K — bundle hash 2/2 rotate admin `C8TvDy7r→CWHIdoFo` + user `BvcWrq2z→Bg2FNeIz`, smoke 5/5 200, PE List API shape preserved 9 fields, test gate 111 unchanged, Mig 31 unchanged). Run #223-#227 polish chunks Plan AG2-AG6 em main self-verify (bundle visual check + git push success + Gitea auto-trigger 3-4min deploy). Plan AG4 BE+FE cross-stack (DTO +4 fields DrafterUserId/DrafterName/DepartmentId/DepartmentName + 3 projection JOIN Users+Departments): dotnet test 111/111 PASS local pre-push. **Pattern saved:** CICD Monitor spawn 1× đầu Phase wire ROI tốt cho 1 dev solo iteration scenario. Polish chunks (CSS/UX/copy) cùng Plan em main self-verify thay vì re-spawn ~150K × N wasteful. 0 prod regression observed cumulative S26.
- **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-19 10:13-10:21 — Run #215 FAIL → Run #216 PASS (gotcha #48 SQLite tie-break catch+fix pair, KEEP for crucial lesson):**
- **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.
**Run #215** id=329 sha=`cdfd542` VERDICT=FAIL (S25 t1 Plan AB Chunk A — Changelog visibility fix Bug 1 Budget Adjust + Bug 2 Return Mode). Push range `e23f51c..cdfd542` 1 commit 3 files (1 BE `PurchaseEvaluationWorkflowService.cs` +207/-95 LOC refactor `ApplyReturnModeAsync` + 2 FE `PeDetailTabs.tsx` mirror filter extend). Duration 1m06s (early test gate fail, deploy stage never reached). **CRITICAL — Test gate FAIL at test_infra:** 51/53 passed, 2 FAIL same root cause:
- `PurchaseEvaluationWorkflowServiceReturnModeTests.ApplyReturnMode_OneStep_AtStep1_ResetsToBuoc1Cap1_KeepsChoDuyet`
- `PurchaseEvaluationWorkflowServiceReturnModeTests.ApplyReturnMode_OneLevel_AtStep1Level1_ResetsToBuoc1Cap1_KeepsChoDuyet`
- **Error:** `Expected changelog.ContextNote not to be <null>`
- **Root cause:** Plan AB Chunk A `ApplyReturnModeAsync` adds NEW Changelog entry at end (line 403-412) for Bug 2 visibility — `EntityType=Workflow + Action=Update + Summary` (NO ContextNote field). After refactor, BOTH ApplyReturnModeAsync (new entry, no ContextNote) AND LogTransitionAsync (line 100, existing entry with ContextNote=comment) are added in same `SaveChangesAsync` transaction. Test fetches `.OrderByDescending(c => c.CreatedAt).FirstAsync()` — with SQLite + frozen test clock both entries get SAME CreatedAt, OrderByDescending tie-break returns Plan AB's Workflow entry (without ContextNote) instead of Transition entry.
- **Deploy NOT shipped:** Bundle hashes unchanged from Run #214 Plan AA baseline. Mig 31 TOP 1 unchanged. Plan AB Bug 1+Bug 2 fix NOT live (bro UAT screenshot pre-deploy stale).
- **Side benefit:** CI test gate caught BEFORE prod deploy — bro UAT spared broken Plan M edge case audit trail.
- **2026-05-19 11:19-11:23 — Run #219 id=333 sha=`0aaf2df` VERDICT=PASS** (S25 t5 Plan AD — Lịch sử duyệt redesign drop phase badges + next-target hint — push `25837b6..0aaf2df` 1 commit Plan AD FE-only). Duration 3m24s baseline. Files 2 FE-only +104/-28 LOC: `fe-user/src/components/pe/PeDetailTabs.tsx` ApprovalsTab refactor — DROP fromPhase→toPhase Badge JSX entirely + ADD `extractNextTargetHint(comment, decision)` helper parse comment regex/string → semantic hint string ("→ Cấp Y" / "→ Bước Z" / "→ Trả về NV X" / "→ Duyệt thẳng tới cuối") + cleanup unused `PurchaseEvaluationPhaseColor` import + `fe-admin/src/components/pe/PeDetailTabs.tsx` mirror exact §3.9. Pre-push em main local 2× FE build 0 TS err (424ms admin, 469ms user), BE unchanged from `a734bf2`. **Workflow run status=success** confirms full pipeline PASS: test_domain 58/58 + test_infra 53/53 baseline preserved (FE-only no test touch), build_be PASS (BE unchanged binary identical from Run #218), build_fe_admin + build_fe_user PASS (bundle rotation expected). **Stage 4c wire VERIFY Plan AD DOM shape compatibility** PE_ID=`3248f2f9-c6e9-43ff-a4ca-067ffecf9f36` (PE/2026/A/032): GET PE/{id} returns approvals array length=6, first approval keys=[id, fromPhase, toPhase, approverUserId, approverName, decision, comment, approvedAt] ✓ — Plan AD FE drops VISUAL Badge from JSX but DTO contract still exposes `fromPhase` + `toPhase` fields unchanged (FE-only render strip, BE shape untouched). `decision` + `comment` fields present → `extractNextTargetHint(comment, decision)` helper has required inputs to parse. Backward compat: history rows comment có `[Bước X — Cấp Y]` prefix from Plan AC enrichment → Plan AD helper can extract "→ Cấp Y" hint cleanly. **Bundle hash ROTATED 2/2** ✓: admin `CDVnRDe6 → DR95zKWg` + user `gAFN3NVx → BAj_Yaj5` — Plan AD touched cả 2 FE app, rotation confirmed deploy ship. **Smoke 5/5 200** ✓: purchase-evaluations / contracts / menus / users / PE/A/032 detail. **Mig 31 TOP 1 indirect verify** ✓: diff confirms 0 BE/Mig touch (2 .tsx only +104/-28), prod schema guaranteed unchanged from Run #218 baseline `20260514160124_RefactorSkipToFinalToApproverLevel`. Direct sqlcmd skipped — `$env:PROD_DB_PASSWORD` not set local; FE-only commit indirect verify accepted (per Plan AD scope FE-only design). **S25 cumulative push range `e23f51c..0aaf2df` 5 commits Plan AB+AB2+AC+AC2+AD** all deployed clean — 1 fail catch (Run #215) + 4 PASS = test gate effective. **Pattern saved Plan AD: UX drop misleading dual-phase badges + parse comment for semantic next-target hint — reusable cho Contract V2 + Budget V2 audit history future when DTO has dual-state fields (from/to) but render value mostly noise (loops to self in ChoDuyet→ChoDuyet transitions).** Parse strategy: regex `\[Bước (\d+) — Cấp (\d+)\]` + keyword detect "Trả về" / "vượt cấp" / "Cấp cuối" → emit semantic hint string. **Recommendation:** bro UAT verify PE/2026/A/032 mở Lịch sử duyệt panel — kỳ vọng badges phase mất đi hoàn toàn, thay bằng hint string "→ Cấp Y" / "→ Bước Z" / "→ Trả về NV X" parse từ comment Plan AC structure. Cumulative S25 5 commits clean deploy 0 regression. Memory size ~62KB **STRONGLY over 50KB hard threshold****DEDICATED CURATION SESSION REQUIRED PRIORITY NEXT** archive `archive/2026-05.md` to drop entries pre-2026-05-15. FIFO ~13 entries. Token cost ~14k.
**Run #216** id=330 sha=`8c05947` VERDICT=PASS (S25 t2 Plan AB Chunk A2 fix). Tip commit Chunk A2: 1 test file +7/-2 LOC — 2 Plan M edge case tests add `.Where(c => c.Summary!.Contains("Chuyển phase"))` filter trước `OrderByDescending(CreatedAt).First()` để pick LogTransition entry (chứa ContextNote) thay vì Plan AB new Changelog entry. Plan AB Chunk A code `cdfd542` KHÔNG bị revert — Bug 1+Bug 2 fix giữ nguyên. Test gate PASS: test_domain 58/58 + test_infra 53/53 (2 Plan M tests now PASS — verified live). Bundle hash 2/2 rotated. Bug 1 Budget Adjust entry LIVE + Bug 2 Return Mode entries LIVE on PE c6e9. 8 min turnaround 10:13 fail → 10:21 fix. **Demonstrates test-after UAT mode CAN tolerate edge case bug if next chunk lands within minutes — but Plan AB > 100 LOC BE refactor should have local `dotnet test` verify pre-push (UAT skip-test rule risky for refactor scope).**
- **2026-05-19 11:00-11:03 — Run #218 id=332 sha=`25837b6` VERDICT=PASS** (S25 t4 Plan AC2 — FE merge view recover historical Reject events PE cũ — push `a734bf2..25837b6` 1 commit Plan AC2 FE-only). Duration 3m23s baseline. Files 2 FE-only ~+110/-6 LOC: `fe-user/src/components/pe/PeDetailTabs.tsx` ApprovalsTab refactor fetch changelogs + reconstruct synthetic Reject rows from Workflow entries + dedupe + merge sort + `fe-admin/src/components/pe/PeDetailTabs.tsx` mirror exact §3.9. Pre-push em main local 2× FE build 0 TS err (460/512ms), BE/test unchanged from Run #217. **Workflow run status=success** confirms test gate PASS (test_domain 58/58 + test_infra 53/53 baseline preserve, build_be PASS BE unchanged, build_fe_admin + build_fe_user PASS rotated). **CRITICAL Stage 4c wire VERIFY Plan AC2 FE merge logic data source** PE_ID=`3248f2f9-c6e9-43ff-a4ca-067ffecf9f36` (PE/2026/A/032 bro UAT): total changelogs=20 entries, Workflow entries (entityType=5) count=**8** ✓ (>0 expected ~3-5 từ pre-deploy UAT, actual 8 = generous coverage). **ContextNote keywords detected** ✓: entry 2026-05-19T02:34:38 contextNote=`Trà xem lại phần so sánh [Trả về Người chỉ định — Bước 1 (Phòng 1) Cấp 2]` matches "Trả về" keyword (Drafter/Assignee return mode). Entry 02:35:43 contextNote=`Approver skip thẳng tới Bước 3 Cấp 1 (NV cuối) — bỏ qua các Bước/Cấp trung gian` matches skipToFinal pattern. 6 entries có summary `Chuyển phase ChoDuyet → ChoDuyet` (mid-flow transitions với mutation context). **FE merge logic data source FULLY VERIFIED** ✓ — synthetic Reject rows có thể reconstruct từ 2 Workflow entries với "Trả về" ContextNote (line 02:34:38 + earlier Mig 26 events). **Bundle hash ROTATED 2/2** ✓: admin `B5iZMa7g → CDVnRDe6` + user `CHJDH3M2 → gAFN3NVx`. **Smoke 5/5 200** ✓: purchase-evaluations / contracts / menus / users / changelogs PE/A/032. **Mig 31 TOP 1 unchanged**`20260514160124_RefactorSkipToFinalToApproverLevel` Plan AC2 no schema. **S25 cumulative push range `e23f51c..25837b6` 4 commits Plan AB+AB2+AC+AC2** all deployed. **Pattern saved: FE merge synthetic rows from Changelog history — reversible historical recovery pattern reusable cho Contract V2 + Budget V2 audit recovery future when missing native audit table.** Source = Changelog generic with entityType discriminator + ContextNote keyword filter + summary regex parse. **Recommendation:** bro UAT verify PE/2026/A/032 mở Lịch sử duyệt panel — kỳ vọng thấy synthetic Reject entries trộn với 6 Approve entries existing sorted ascending bởi approvedAt. Memory size ~60KB hard over 50KB limit — **IMMEDIATE ARCHIVE archive/2026-05.md MANDATORY next curation BEFORE any next entry**. FIFO ~13 entries. Token cost ~12k.
- **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-13 23:25 — Verify S22 chốt cuối cumulative** (push range `3d725c4..cc8a7d3` 12 commits VERDICT=PASS — S22+1-S22+5 Plan C/D/E + Mig 30 F4 per-NV Approver edit Budget). 33 active users prod confirmed. Bundle hash rotated 2/2. 104/104 test (+1 từ S21 baseline 103). Mig 30 prod confirmed. **Discovery #3 first surfaced:** `cc8a7d3` docs+4 agent MEMORY.md → CI SKIPPED via `**/*.md` glob (all match — `.md` files at any depth match). Spec hypothesis "`.claude/agent-memory/**` NOT in paths-ignore → trigger CI" disproven for this commit. Gotcha #47 still useful as PREVENTIVE for future non-.md state files under `.claude/agent-memory/`.
- **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.
@ -266,13 +218,4 @@ Flag commit nếu thấy `<PackageReference Include="MediatR" Version="14...` ho
- 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.)
Last curate: 2026-05-19 10:18 (added S25 t1 Plan AB Run #215 entry: **FAIL** verdict at test_infra gate — 2 Plan M edge case tests broken `ApplyReturnMode_OneStep_AtStep1` line 350 + `ApplyReturnMode_OneLevel_AtStep1Level1` line 308 both `Expected changelog.ContextNote not to be <null>`. Root cause: Plan AB refactor adds NEW Changelog.Add() at end of `ApplyReturnModeAsync` (EntityType=Workflow, no ContextNote) alongside existing `LogTransitionAsync` chain that writes ContextNote=comment. SQLite frozen-clock tie-break in `OrderByDescending(CreatedAt).First()` non-deterministic → tests pick wrong entry. Test gate caught BEFORE deploy → prod state preserved (bundles unchanged Run #214 baseline admin `CZdXQ2eo` + user `DCwhhey2`, Mig 31 TOP 1 unchanged, smoke 5/5 200). 3 fix approaches recommended em main: (1) ContextNote=summary on new entry, (2) skip when LogTransitionAsync covers, (3) test fix EntityType=Transition filter. NEW gotcha #48: multi-Changelog.Add() in same transaction + OrderByDescending(CreatedAt) tie risk in test SQLite frozen-clock. Memory size ~50KB at hard threshold — recommend ARCHIVE archive/2026-05.md next curation BEFORE next entry. FIFO runs at ~11 entries. Token cost ~10k. UAT mode skip test-after pattern STILL RISKY when refactor touches code paths with existing tests — Plan AB Chunk A did `npm build × 2` verify but skipped `dotnet test` → CI caught regression in test_infra. Bro UAT spared broken audit trail in prod.)
Last curate: 2026-05-19 10:50 (added S25 t3 Plan AC Run #217 entry: **PASS** verdict — fix Bug 3 "Lịch sử duyệt" panel show Trả lại + Duyệt vượt cấp. Push `8c05947..a734bf2` 1 commit Plan AC. Duration 3m27s baseline. 3 files +105/-38 LOC: BE Service Reject branch line 75-103 ADD Approval row + capture pre-call Step/Level mutation state cho audit "from-position" + Approve branch line 472 enrich Comment với `[Duyệt vượt cấp tới Cấp cuối]` prefix khi skipToFinal=true + FE × 2 app PeDetailTabs.tsx add `decisionBadge` helper render Decision badge ApprovalsTab. Local pre-push 111/111 PASS + 2× FE build 0 TS err (TS strict caught unused `PE_DECISION_APPROVE` removed). **CRITICAL Stage 4c wire VERIFY** PE_ID=`3248f2f9-c6e9-43ff-a4ca-067ffecf9f36`: approvals count=6 all keys [approvedAt/approverName/approverUserId/comment/decision/fromPhase/id/toPhase] ✓ decision field present every row (FE decisionBadge input verified) ✓ all 6 existing decision=1 Approve backward-compat preserved ✓ all 6 comments có `[Bước X — Cấp Y]` prefix structure Plan AC enrichment uniformly applied across history ✓ no Reject/skipToFinal rows current dataset n/a until bro UAT post-deploy. **Bundle hash ROTATED 2/2** ✓: admin `CrHpBYFM → B5iZMa7g` + user `C3bCWZ90 → CHJDH3M2`. **Smoke 4/4 200** ✓. **Mig 31 TOP 1 unchanged** ✓ no schema. **Pattern saved cross-ref Plan AB:** capture pre-call mutation state cho audit row "from-position" — when Service mutates entity AND writes audit row, snapshot OLD values BEFORE mutation. Reusable Contract V2 + Budget V2 future. **Discovery #5:** sqlcmd Windows-auth via ssh requires `\\\\SQLEXPRESS` 4-backslash escape; `\\SQLEXPRESS` produces 0 output silently. Memory size ~58KB strongly over 50KB hard limit — **REQUIRED ARCHIVE archive/2026-05.md next curation BEFORE next entry.** FIFO ~12 entries. Token ~12k.)
Last curate: 2026-05-19 11:25 (added S25 t5 Plan AD Run #219 entry: **PASS** verdict — Lịch sử duyệt redesign drop phase badges + next-target hint helper. Push `25837b6..0aaf2df` 1 commit FE-only 2 .tsx +104/-28. Duration 3m24s baseline. CI status=success → full pipeline PASS (FE-only no test/BE touch baseline preserved). **CRITICAL Stage 4c wire VERIFY** PE_ID=`3248f2f9...`: approvals shape unchanged 6 rows all keys [id/fromPhase/toPhase/approverUserId/approverName/decision/comment/approvedAt] — Plan AD strips Badge JSX only, DTO shape backward-compat preserved + Plan AC `[Bước X — Cấp Y]` prefix structure provides parse input for `extractNextTargetHint()` helper. **Bundle hash ROTATED 2/2** ✓: admin `CDVnRDe6 → DR95zKWg` + user `gAFN3NVx → BAj_Yaj5`. **Smoke 5/5 200** ✓. **Mig 31 indirect verify** ✓ (diff 0 BE/Mig touch, sqlcmd direct skipped — PROD_DB_PASSWORD not set local). **Pattern saved Plan AD: UX drop misleading dual-state fields + parse comment helper for semantic hint — reusable Contract/Budget V2 audit when from/to fields mostly self-loop noise.** **S25 cumulative 5 commits AB+AB2+AC+AC2+AD** deployed clean 1 catch + 4 pass. Memory size ~62KB STRONGLY OVER 50KB hard threshold — **DEDICATED CURATION SESSION PRIORITY NEXT** archive `archive/2026-05.md` drop entries pre-2026-05-15 + compress S22-S24 entries. FIFO ~13 entries. Token cost ~14k.)
**Last curate: 2026-05-22 em main full curate** — archived 14 verbose Run entries (#186-#221) S21 t3 → S25 Plan AF → `archive/2026-05-runs.md` (~50KB content moved). KEEP in MEMORY: Run #215+#216 pair (gotcha #48 lesson critical), Run #222-#227 S26 summary (Plan AG series), 2026-05-12 setup, S22 chốt cuối verify summary. 8 key patterns extracted to archive header. **10-surface-point per-NV checklist** promoted to foundation. Memory size before: ~72KB (vượt 50KB hard threshold per HANDOFF flag) → after: target ~25-28KB. Next curate trigger: > 50KB OR next major Phase wire (Plan B Contract V2). Per `feedback_md_compact_narrative.md` §6.5 KEEP narrative — archive preserves full verbose entries cho cross-session audit retrieve.

View File

@ -0,0 +1,201 @@
# CI/CD Monitor — Archive Runs Q2 2026-05
> **Archived:** 2026-05-22 by em main SOLUTION_ERP curate session.
> **Scope:** Verbose Run #186 → #221 entries (S21 t3 → S25 Plan AF) — moved from MEMORY.md để giữ slim < 25KB threshold.
> **Rule §6.5 compliance:** KHÔNG cắt narrative — full verbose entries preserved cho audit cross-session.
> **Source MEMORY.md before archive:** ~72KB (~211 lines FIFO ~14 entries).
> **Target slim MEMORY.md after archive:** ~25-30KB (~5 entries kept: Run #215 fail lesson + Run #216 fix + Run #222-#227 S26 + setup baseline).
> **Curate trigger:** Per `feedback_md_compact_narrative.md` (§6.5) + HANDOFF S25 wrap urgent flag (cicd-monitor 72KB CRITICAL over 50KB hard threshold).
---
## Last curate logs (historical verbose — moved here)
### 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.
### 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.
### 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.
### 2026-05-19 10:18 — added S25 t1 Plan AB Run #215 entry
**FAIL** verdict at test_infra gate — 2 Plan M edge case tests broken `ApplyReturnMode_OneStep_AtStep1` line 350 + `ApplyReturnMode_OneLevel_AtStep1Level1` line 308 both `Expected changelog.ContextNote not to be <null>`. Root cause: Plan AB refactor adds NEW Changelog.Add() at end of `ApplyReturnModeAsync` (EntityType=Workflow, no ContextNote) alongside existing `LogTransitionAsync` chain that writes ContextNote=comment. SQLite frozen-clock tie-break in `OrderByDescending(CreatedAt).First()` non-deterministic → tests pick wrong entry. Test gate caught BEFORE deploy → prod state preserved (bundles unchanged Run #214 baseline admin `CZdXQ2eo` + user `DCwhhey2`, Mig 31 TOP 1 unchanged, smoke 5/5 200). 3 fix approaches recommended em main: (1) ContextNote=summary on new entry, (2) skip when LogTransitionAsync covers, (3) test fix EntityType=Transition filter. NEW gotcha #48: multi-Changelog.Add() in same transaction + OrderByDescending(CreatedAt) tie risk in test SQLite frozen-clock. Memory size ~50KB at hard threshold — recommend ARCHIVE archive/2026-05.md next curation BEFORE next entry. FIFO runs at ~11 entries. Token cost ~10k. UAT mode skip test-after pattern STILL RISKY when refactor touches code paths with existing tests — Plan AB Chunk A did `npm build × 2` verify but skipped `dotnet test` → CI caught regression in test_infra. Bro UAT spared broken audit trail in prod.
### 2026-05-19 10:50 — added S25 t3 Plan AC Run #217 entry
**PASS** verdict — fix Bug 3 "Lịch sử duyệt" panel show Trả lại + Duyệt vượt cấp. Push `8c05947..a734bf2` 1 commit Plan AC. Duration 3m27s baseline. 3 files +105/-38 LOC: BE Service Reject branch line 75-103 ADD Approval row + capture pre-call Step/Level mutation state cho audit "from-position" + Approve branch line 472 enrich Comment với `[Duyệt vượt cấp tới Cấp cuối]` prefix khi skipToFinal=true + FE × 2 app PeDetailTabs.tsx add `decisionBadge` helper render Decision badge ApprovalsTab. Local pre-push 111/111 PASS + 2× FE build 0 TS err (TS strict caught unused `PE_DECISION_APPROVE` removed). **CRITICAL Stage 4c wire VERIFY** PE_ID=`3248f2f9-c6e9-43ff-a4ca-067ffecf9f36`: approvals count=6 all keys [approvedAt/approverName/approverUserId/comment/decision/fromPhase/id/toPhase] ✓ decision field present every row (FE decisionBadge input verified) ✓ all 6 existing decision=1 Approve backward-compat preserved ✓ all 6 comments có `[Bước X — Cấp Y]` prefix structure Plan AC enrichment uniformly applied across history ✓ no Reject/skipToFinal rows current dataset n/a until bro UAT post-deploy. **Bundle hash ROTATED 2/2** ✓: admin `CrHpBYFM → B5iZMa7g` + user `C3bCWZ90 → CHJDH3M2`. **Smoke 4/4 200** ✓. **Mig 31 TOP 1 unchanged** ✓ no schema. **Pattern saved cross-ref Plan AB:** capture pre-call mutation state cho audit row "from-position" — when Service mutates entity AND writes audit row, snapshot OLD values BEFORE mutation. Reusable Contract V2 + Budget V2 future. **Discovery #5:** sqlcmd Windows-auth via ssh requires `\\\\SQLEXPRESS` 4-backslash escape; `\\SQLEXPRESS` produces 0 output silently. Memory size ~58KB strongly over 50KB hard limit — **REQUIRED ARCHIVE archive/2026-05.md next curation BEFORE next entry.** FIFO ~12 entries. Token ~12k.
### 2026-05-19 11:25 — added S25 t5 Plan AD Run #219 entry
**PASS** verdict — Lịch sử duyệt redesign drop phase badges + next-target hint helper. Push `25837b6..0aaf2df` 1 commit FE-only 2 .tsx +104/-28. Duration 3m24s baseline. CI status=success → full pipeline PASS (FE-only no test/BE touch baseline preserved). **CRITICAL Stage 4c wire VERIFY** PE_ID=`3248f2f9...`: approvals shape unchanged 6 rows all keys [id/fromPhase/toPhase/approverUserId/approverName/decision/comment/approvedAt] — Plan AD strips Badge JSX only, DTO shape backward-compat preserved + Plan AC `[Bước X — Cấp Y]` prefix structure provides parse input for `extractNextTargetHint()` helper. **Bundle hash ROTATED 2/2** ✓: admin `CDVnRDe6 → DR95zKWg` + user `gAFN3NVx → BAj_Yaj5`. **Smoke 5/5 200** ✓. **Mig 31 indirect verify** ✓ (diff 0 BE/Mig touch, sqlcmd direct skipped — PROD_DB_PASSWORD not set local). **Pattern saved Plan AD: UX drop misleading dual-state fields + parse comment helper for semantic hint — reusable Contract/Budget V2 audit when from/to fields mostly self-loop noise.** **S25 cumulative 5 commits AB+AB2+AC+AC2+AD** deployed clean 1 catch + 4 pass. Memory size ~62KB STRONGLY OVER 50KB hard threshold — **DEDICATED CURATION SESSION PRIORITY NEXT** archive `archive/2026-05.md` drop entries pre-2026-05-15 + compress S22-S24 entries. FIFO ~13 entries. Token cost ~14k.
---
## Run entries archived (FIFO order — earliest first for chronological reading)
### 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-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 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 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-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-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-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-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 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 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: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 ~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 ~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: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 ~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 ~17:35 — Run #214 id=328 sha=`ee0902a` VERDICT=PASS
(S24 t1 Plan AA wrap fix sidebar). Last successful deploy before Plan AB. Sidebar label dài wrap về đầu hàng + text smaller. Bundle hashes post-Run-#214 = admin `CZdXQ2eo` + user `DCwhhey2` (baseline for Plan AB delta comparison — KHÔNG rotated since Plan AB failed). Duration ~3m25s.
### 2026-05-19 11:00-11:03 — Run #218 id=332 sha=`25837b6` VERDICT=PASS
(S25 t4 Plan AC2 — FE merge view recover historical Reject events PE cũ — push `a734bf2..25837b6` 1 commit Plan AC2 FE-only). Duration 3m23s baseline. Files 2 FE-only ~+110/-6 LOC: `fe-user/src/components/pe/PeDetailTabs.tsx` ApprovalsTab refactor fetch changelogs + reconstruct synthetic Reject rows from Workflow entries + dedupe + merge sort + `fe-admin/src/components/pe/PeDetailTabs.tsx` mirror exact §3.9. Pre-push em main local 2× FE build 0 TS err (460/512ms), BE/test unchanged from Run #217. **Workflow run status=success** confirms test gate PASS (test_domain 58/58 + test_infra 53/53 baseline preserve, build_be PASS BE unchanged, build_fe_admin + build_fe_user PASS rotated). **CRITICAL Stage 4c wire VERIFY Plan AC2 FE merge logic data source** PE_ID=`3248f2f9-c6e9-43ff-a4ca-067ffecf9f36` (PE/2026/A/032 bro UAT): total changelogs=20 entries, Workflow entries (entityType=5) count=**8** ✓ (>0 expected ~3-5 từ pre-deploy UAT, actual 8 = generous coverage). **ContextNote keywords detected** ✓: entry 2026-05-19T02:34:38 contextNote=`Trà xem lại phần so sánh [Trả về Người chỉ định — Bước 1 (Phòng 1) Cấp 2]` matches "Trả về" keyword (Drafter/Assignee return mode). Entry 02:35:43 contextNote=`Approver skip thẳng tới Bước 3 Cấp 1 (NV cuối) — bỏ qua các Bước/Cấp trung gian` matches skipToFinal pattern. 6 entries có summary `Chuyển phase ChoDuyet → ChoDuyet` (mid-flow transitions với mutation context). **FE merge logic data source FULLY VERIFIED** ✓ — synthetic Reject rows có thể reconstruct từ 2 Workflow entries với "Trả về" ContextNote (line 02:34:38 + earlier Mig 26 events). **Bundle hash ROTATED 2/2** ✓: admin `B5iZMa7g → CDVnRDe6` + user `CHJDH3M2 → gAFN3NVx`. **Smoke 5/5 200** ✓: purchase-evaluations / contracts / menus / users / changelogs PE/A/032. **Mig 31 TOP 1 unchanged** ✓ `20260514160124_RefactorSkipToFinalToApproverLevel` Plan AC2 no schema. **S25 cumulative push range `e23f51c..25837b6` 4 commits Plan AB+AB2+AC+AC2** all deployed. **Pattern saved: FE merge synthetic rows from Changelog history — reversible historical recovery pattern reusable cho Contract V2 + Budget V2 audit recovery future when missing native audit table.** Source = Changelog generic with entityType discriminator + ContextNote keyword filter + summary regex parse. **Recommendation:** bro UAT verify PE/2026/A/032 mở Lịch sử duyệt panel — kỳ vọng thấy synthetic Reject entries trộn với 6 Approve entries existing sorted ascending bởi approvedAt. Memory size ~60KB hard over 50KB limit — **IMMEDIATE ARCHIVE archive/2026-05.md MANDATORY next curation BEFORE any next entry**. FIFO ~13 entries. Token cost ~12k.
### 2026-05-19 11:19-11:23 — Run #219 id=333 sha=`0aaf2df` VERDICT=PASS
(S25 t5 Plan AD — Lịch sử duyệt redesign drop phase badges + next-target hint — push `25837b6..0aaf2df` 1 commit Plan AD FE-only). Duration 3m24s baseline. Files 2 FE-only +104/-28 LOC: `fe-user/src/components/pe/PeDetailTabs.tsx` ApprovalsTab refactor — DROP fromPhase→toPhase Badge JSX entirely + ADD `extractNextTargetHint(comment, decision)` helper parse comment regex/string → semantic hint string ("→ Cấp Y" / "→ Bước Z" / "→ Trả về NV X" / "→ Duyệt thẳng tới cuối") + cleanup unused `PurchaseEvaluationPhaseColor` import + `fe-admin/src/components/pe/PeDetailTabs.tsx` mirror exact §3.9. Pre-push em main local 2× FE build 0 TS err (424ms admin, 469ms user), BE unchanged from `a734bf2`. **Workflow run status=success** confirms full pipeline PASS: test_domain 58/58 + test_infra 53/53 baseline preserved (FE-only no test touch), build_be PASS (BE unchanged binary identical from Run #218), build_fe_admin + build_fe_user PASS (bundle rotation expected). **Stage 4c wire VERIFY Plan AD DOM shape compatibility** PE_ID=`3248f2f9-c6e9-43ff-a4ca-067ffecf9f36` (PE/2026/A/032): GET PE/{id} returns approvals array length=6, first approval keys=[id, fromPhase, toPhase, approverUserId, approverName, decision, comment, approvedAt] ✓ — Plan AD FE drops VISUAL Badge from JSX but DTO contract still exposes `fromPhase` + `toPhase` fields unchanged (FE-only render strip, BE shape untouched). `decision` + `comment` fields present → `extractNextTargetHint(comment, decision)` helper has required inputs to parse. Backward compat: history rows comment có `[Bước X — Cấp Y]` prefix from Plan AC enrichment → Plan AD helper can extract "→ Cấp Y" hint cleanly. **Bundle hash ROTATED 2/2** ✓: admin `CDVnRDe6 → DR95zKWg` + user `gAFN3NVx → BAj_Yaj5` — Plan AD touched cả 2 FE app, rotation confirmed deploy ship. **Smoke 5/5 200** ✓: purchase-evaluations / contracts / menus / users / PE/A/032 detail. **Mig 31 TOP 1 indirect verify** ✓: diff confirms 0 BE/Mig touch (2 .tsx only +104/-28), prod schema guaranteed unchanged from Run #218 baseline `20260514160124_RefactorSkipToFinalToApproverLevel`. Direct sqlcmd skipped — `$env:PROD_DB_PASSWORD` not set local; FE-only commit indirect verify accepted (per Plan AD scope FE-only design). **S25 cumulative push range `e23f51c..0aaf2df` 5 commits Plan AB+AB2+AC+AC2+AD** all deployed clean — 1 fail catch (Run #215) + 4 PASS = test gate effective. **Pattern saved Plan AD: UX drop misleading dual-phase badges + parse comment for semantic next-target hint — reusable cho Contract V2 + Budget V2 audit history future when DTO has dual-state fields (from/to) but render value mostly noise (loops to self in ChoDuyet→ChoDuyet transitions).** Parse strategy: regex `\[Bước (\d+) — Cấp (\d+)\]` + keyword detect "Trả về" / "vượt cấp" / "Cấp cuối" → emit semantic hint string. **Recommendation:** bro UAT verify PE/2026/A/032 mở Lịch sử duyệt panel — kỳ vọng badges phase mất đi hoàn toàn, thay bằng hint string "→ Cấp Y" / "→ Bước Z" / "→ Trả về NV X" parse từ comment Plan AC structure. Cumulative S25 5 commits clean deploy 0 regression. Memory size ~62KB **STRONGLY over 50KB hard threshold** — **DEDICATED CURATION SESSION REQUIRED PRIORITY NEXT** archive `archive/2026-05.md` to drop entries pre-2026-05-15. FIFO ~13 entries. Token cost ~14k.
### 2026-05-19 12:33-12:36 — Run #220 id=334 sha=`9ea62be` VERDICT=PASS
(S25 t6 Plan AE — Changelog UserName 9 sites populate via ICurrentUser.FullName fallback Email). Push range cumulative S25 remote = `e23f51c..9ea62be` (6 commits Plan AB+AC+AC2+AD+AE since S24). Tip commit Plan AE: 2 BE-only files `PurchaseEvaluationFeatures.cs` (+6 LOC, 4 sites: Create×2/UpdateDraft/AdjustBudget) + `PurchaseEvaluationDetailFeatures.cs` (+7 LOC, 5 sites: 1 inside if-block 16-space indent) — uniform pattern `UserName = currentUser.FullName ?? currentUser.Email`. Duration ~3m25s (~baseline). Pre-push em main local: `dotnet test SolutionErp.slnx` PASS 111/111 (~5s). Status poll 4 iter (12:33→12:36) status `running → success`. **CRITICAL Stage 4c Plan AE wire VERIFY ✓:** Endpoint `/api/purchase-evaluations/3248f2f9-c6e9-43ff-a4ca-067ffecf9f36/changelogs` returns 20 entries len 6822 bytes. Response shape has `userName` field accessible ✓. **Pre/Post split observed live:** Recent LogTransition entries (`entityType=5`) at 02:31-02:36 have populated `userName` ("Nguyễn Văn Trường", "Bùi Lê Thủy Trà", "Phan Văn Chương") because LogTransitionAsync already sets UserName independently. **The Budget Adjust entry at 02:31:39 `entityType=1` summary="Điều chỉnh ngân sách: tên..." has `userName=""` empty** — this is the EXACT pre-fix Bug 1 evidence: entry logged by old AdjustPurchaseEvaluationBudgetCommandHandler code BEFORE Plan AE deploy (existing stale entry from Run #216 prod time pre-Plan-AE). Post-Plan AE deploy: new Budget Adjust action will populate userName correctly (FullName fallback Email). Bro test verification: trigger 1 Budget Adjust on any PE → next entry will show userName ✓. Stage 4a Auth admin: HTTP 200 token len 468 ✓. Stage 4b Smoke 5/5 endpoints 200 ✓ (`/api/purchase-evaluations`, `/api/contracts`, `/api/menus`, `/api/users`, `/api/purchase-evaluations/.../changelogs`). **Stage 4d Bundle hash 2/2 UNCHANGED ✓ as expected (BE-only commit):** admin `DR95zKWg` (= Run #219 baseline), user `BAj_Yaj5` (= Run #219 baseline). Stage 4e Health ready 200 "Healthy" + Health live 200 ✓. Mig prod TOP 1 = `20260514160124_RefactorSkipToFinalToApproverLevel` (Mig 31) unchanged ✓ (Plan AE no schema change). **Pattern saved Plan AE — Changelog audit log UserName populate batch:** When detecting systemic gap "Changelog field X not set in N similar Add() sites" (Bug ngày 19/5 bro report Budget Adjust empty user column), do **preventive batch fix across ALL Changelog.Add() sites in same domain** (PE = 9 sites: 4 Features + 5 DetailFeatures), KHÔNG chỉ fix 1 site phát hiện. Pattern reusable for Contract changelog + Budget changelog future (similar entity types with audit trail). Source pattern `UserName = currentUser.FullName ?? currentUser.Email` — assumes ICurrentUser is injected scoped DI (already standard). Side benefit: 8 preventive sites future-proof against same bug pattern. Token cost ~14k. **Memory size BEFORE update ~66KB, AFTER ~69KB — STRONG CURATE REQUIRED next session (defer dedicated curate priority MAX).**
### 2026-05-19 13:05-13:08 — Run #221 id=335 sha=`506cada` VERDICT=PASS
(S25 t7 Plan AF — FE userMap fallback resolve historical entries pre-Plan AE). Push range cumulative S25 remote = `e23f51c..506cada` (7 commits Plan AB+AC+AC2+AD+AE+AF since S24). Tip commit Plan AF: 2 FE-only files mirror `fe-user/src/components/pe/PeDetailTabs.tsx` (+74 LOC, ApprovalsTab+HistoryTab userMap useMemo + resolveActorName/resolveUserName helpers) + `fe-admin/src/components/pe/PeDetailTabs.tsx` (+70 LOC mirror §3.9 identical logic). Duration ~3m23s (~baseline). Pre-push em main local: npm build × fe-user PASS 0 TS err (9.12s), npm build × fe-admin PASS 0 TS err (8.91s), BE unchanged from 9ea62be. Status poll 5 iter (13:06→13:08) status `running → success`. **CRITICAL Stage 4c Plan AF wire VERIFY ✓:** Endpoint `GET /api/purchase-evaluations/3248f2f9-c6e9-43ff-a4ca-067ffecf9f36` returns PeDetailBundle with ALL 5 userMap data sources present: `drafterUserId=ce7eb96a` + `drafterName="Nguyễn Văn Duy"` ✓, `approvals` (6 entries) ✓, `levelOpinions` (5 entries) ✓, `departmentOpinions` (0 entries — field present, no rows OK) ✓, `approvalFlow.steps` (3 steps with nested levels.approvers) ✓. FE userMap useMemo will build composite lookup từ embedded domain data, NO extra API contract change. Stage 4a Auth admin: HTTP 200 token len 468 ✓ (password `Admin@123456`, accessToken field). Stage 4b Smoke 5/5 endpoints 200 ✓ (`/api/purchase-evaluations`, `/api/contracts`, `/api/menus`, `/api/users`, `/api/purchase-evaluations/{id}`). **Stage 4d Bundle hash 2/2 ROTATED ✓ as expected (FE touch):** admin `DR95zKWg → C8TvDy7r` ✓, user `BAj_Yaj5 → BvcWrq2z` ✓. Stage 4e Health ready 200 + Health live 200 ✓. Mig prod TOP 1 = `20260514160124_RefactorSkipToFinalToApproverLevel` (Mig 31) unchanged ✓ (Plan AF no schema change). **Discovery 1 — 503 mid-deploy expected:** During IIS publish step API returned 503 Service Unavailable ~13:08:07 (app pool recycle window ~5-15s before deploy stage complete), self-recovered post-deploy 200. Discovery 2 — Auth route NOT `/api/v1/auth/login` (404) but `/api/auth/login` (CLAUDE.md route). Token field NOT `token` but `accessToken`. **Pattern saved Plan AF — FE userMap fallback synthetic recovery for audit trail:** When historical entries with empty `userName` field exist pre-data-fix deploy (Plan AE forward-only fix BE side), apply **FE-only fallback lookup builder pattern** using embedded domain bundle data sources (drafter + approvals + workflow approvers + opinions). Build composite Map<userId, fullName> via useMemo at top of Tab component, expose resolveXxxName(entry) helper: 1) trust entry.userName if non-empty, 2) lookup userMap by entry.userId, 3) fallback "Hệ thống". Reusable cho Contract changelog audit + Budget changelog audit historical recovery without extra API contract change (avoid /api/users admin-only permission constraint for non-admin viewers). Mirror 2 FE app §3.9 identical logic. Token cost ~10k. **Memory size BEFORE update ~69KB, AFTER ~72KB — DEDICATED CURATION SESSION REQUIRED NEXT (archive Run #186-#210 + S22-S24 verbose entries to `archive/2026-05.md` priority MAX REINFORCED).**
---
## Key patterns extracted (cumulative S21-S25)
1. **Discovery #3 anomaly (paths-ignore push range eval)** — Gitea Actions evaluates push range commits not just tip when intermediate commit has non-ignored files. Anomaly is BENEFICIAL — catches verify gate even on docs-only tip commits.
2. **Discovery #4 (ASP.NET Core 10 record enum JSON)** — Record types with enum fields require numeric input unless `JsonStringEnumConverter` registered. FE × 2 SOLUTION_ERP correctly uses numeric (`WorkflowReturnMode = { OneLevel: 1, ... }`).
3. **Discovery #5 (sqlcmd Windows-auth ssh escape)** — Requires `\\\\SQLEXPRESS` 4-backslash escape.
4. **Per-NV admin opt-in wire surface — 10 surface points** (cumulative S22+5 → S23 t1 K11 → S23 t4 N → S23 t5 O → S23 t6 P):
1. Domain entity field
2. EF config HasDefaultValue(false)
3. Migration 3-file rule (Up + Designer + Snapshot)
4. Service handler read field
5. Domain DTO + Application DTO mirror
6. Designer FE checkbox inline
7. Admin overview AwLevelDto record + ToDto ctor
8. CreateAwLevelInput record + Update mutation handler
9. Lookup discrimination in handler (`FirstOrDefault` ADD `ApproverUserId == actorId` filter, fallback admin)
10. Controller body record mirror count check (`[FromBody]` record param count = Command record param count)
5. **Gotcha #48 (multi-Changelog.Add SQLite tie-break)** — Multi-row write in same SaveChangesAsync transaction + SQLite frozen-clock CreatedAt tie risk in tests. Fix discriminator filter: `.Where(c => c.Summary!.Contains("Chuyển phase"))` or `EntityType == X` before OrderByDescending. Plan AB Chunk A2 verified pattern.
6. **CICD test gate catches UAT skip-test regression** — UAT mode skip-test pattern (per `feedback_uat_skip_verify`) STILL RISKY when refactor > 100 LOC touches existing test paths. CI test gate Run #215 caught Plan AB tie-break bug BEFORE prod deploy. Bro UAT spared broken audit trail.
7. **FE merge synthetic + userMap fallback patterns** — Reversible historical recovery cho audit trail (no DB touch). Plan AC2 reusable cho Contract V2 + Budget V2 future. Plan AF reusable cho any audit Tab embedded user lookup.
8. **Pattern reusable: capture pre-call mutation state** — When Service mutates entity AND writes audit row, snapshot OLD values BEFORE mutation. Plan AC pattern reinforced.

View File

@ -175,6 +175,30 @@ Khi spec yêu cầu "move page X từ fe-admin → fe-user" hoặc ngược lạ
**Verification post-fix:** Reviewer Cat 1 "Wire claim verify" SHOULD add to checklist: "Sidebar menu visible end-to-end test post-build" — curl `/api/menus/me` + grep MenuLeaf render output. Smart Friend prevent silent drop.
### Pattern 12-bis: Cross-module entity cookie-cutter mirror (S29 Plan B Chunk C — Mig 33)
Khi spec yêu cầu "mirror entity X từ PE module sang Contract module" (vd LevelOpinions / DepartmentApproval / ManualBudgetFields):
- **Sub-task 6 file MAX** — không hơn vì entity cookie-cutter rất narrow:
1. New entity class `Domain/<Module>/<Entity>.cs` (rename FK field + nav)
2. Modify parent entity: add `List<X> Children` nav collection
3. Modify `IApplicationDbContext`: add `DbSet<X> Xs { get; }`
4. Modify `ApplicationDbContext`: add `public DbSet<X> Xs => Set<X>();`
5. New `<Entity>Configuration.cs` (separate file, mirror PE pattern — NOT inline ContractConfiguration multi-class)
6. `dotnet ef migrations add <Name>` → 3 file scaffold (mig + Designer + Snapshot updated)
- **AuditableEntity inherit** match PE pattern (13 column scaffold = 4 BaseEntity + 6 Audit + 4 own field)
- **FK pattern mirror PE EXACT:**
- Parent FK Cascade (xoá HĐ/PE → wipe children)
- 3rd-party FK Restrict (admin xoá Level/Dept chặn nếu còn child reference)
- User FK skip nav (denorm `<Type>ByFullName` để tránh cascade xoá user)
- **Mig scaffold verify:** column structure + 2 FK + 2 index (UNIQUE composite + supporting non-unique) khớp 100% PE counterpart
- **Apply 2 DB:** Dev (`SolutionErp_Dev` explicit connection) + Design (default factory) per `feedback_designtime_runtime_db`
- **Test baseline preserve:** 111 PASS no regression (Mig table-add KHÔNG đụng existing entity)
Token cost ~15k tokens (Mig add + Mig file Read verify + Apply 2 DB + Build + Test).
Bài học S29 Plan B Chunk C commit `26c98d3`: 8 file +4265 LOC (Mig Designer.cs ~4033 LOC autogen chiếm 95%), implementer code ~232 LOC handcraft only. Em main spec deterministic 100% — 0 spec ambiguity → ACCEPT clean.
Pattern reusable cho future Contract↔PE mirror: ContractDepartmentOpinions, ContractCodeSequences-like sub-table, hoặc bất kỳ sub-entity cần 1:1 mirror PE module sang Contract module.
### Pattern 13: Read-only admin Designer mirror page (S24 Plan AA Chunk B)
Khi spec yêu cầu "user xem read-only data admin đã config" (vd workflow matrix ghim, permission summary, dept tree readonly):
@ -300,6 +324,10 @@ KHÔNG `*` / `latest`. Critical pins:
## 📅 Recent activity (last 10 FIFO)
- **2026-05-22 (S29 wrap — Plan CA Chunk B + Plan B 4 chunks Case 2 cookie-cutter + 1 stopped E3 + Pattern 12-bis NEW):** Implementer = busiest agent S29 với 5 spawn total. **Plan CA Chunk B (~10K):** 4 master pages mirror fe-admin→fe-user byte-identical SHA256, touch 6 file (4 page + App.tsx +5 route + menuKeys.ts +5 key Catalogs*), 948 LOC mirror PASS 0 TS err. Saved `pattern_master_page_mirror.md`. **Plan B 4 spawn cumulative:** (A2 Mig 32 ~25K) schema +column FK Restrict IX + Configuration + DbInitializer SeedSampleContractWorkflowV2 — **stash em main WIP ContractWorkflowService.cs** để build verify clean (em main + Implementer parallel touch BE → race condition trick); (C Mig 33 ContractLevelOpinions ~25K) entity + Mig + Config + DbSet + Contract.cs +LevelOpinions nav — **Pattern 12-bis NEW cross-module entity cookie-cutter mirror PE→Contract** scaffold 4-file pattern documented Patterns section; (D FE Workspace V2 ~12K) ContractCreatePage × 2 app +useQuery V2 + Select dropdown wire ApprovalWorkflowId, 88 LOC mirror byte-similar; (E3 stopped mid-task) FE Section 5 V2 STOPPED at "check ContractDetail type" judgment call → em main solo finish. **Lessons:** (1) **Race condition em main + Implementer parallel BE** → stash trick works but adds overhead. Forward SEQUENTIAL chunks A→B→C khi cùng touch BE, NOT parallel. (2) **Complex FE feature mirror với type extend + new component** → em main solo more reliable than Implementer khi spec ambiguity > 20% in Read-required component inspection. (3) Pattern 16-bis 4-place mirror cross-app reinforced 2× (Plan CA Chunk B + Plan B Chunk D) — verify Layout staticMap khi page move/route enhance. **Patterns proven NEW S29:** Pattern 12-bis cross-module entity scaffold + Pattern 16-bis 4-place mirror reinforced. **Anti-pattern observed:** (a) Implementer E3 stopped mid-task FE complex judgment — em main miss provide concrete component template trong prompt. (b) Race condition parallel BE → stash workaround. Tag: `[pattern, phase-9, frontend+infra]`. KHÔNG curate (33.2KB OK borderline > 25KB threshold but close — defer to next session em main full curate khi S30 wrap).
- **2026-05-22 (S29 Plan B Chunk D PASS — FE ContractCreatePage V2 Workspace dropdown × 2 app cookie-cutter mirror PE):** Spec deterministic 100% từ em main reference `fe-user/src/components/pe/PeWorkspaceCreateView.tsx` (canonical V2 dropdown lines 80-89 useQuery + lines 152-172 Select UI). 2 file mirror × 2 app `fe-admin/fe-user/src/pages/contracts/ContractCreatePage.tsx`: +44 LOC each = +88 LOC total byte-similar (git diff stat verify). Changes: (1) `useState approvalWorkflowId = ''` mới + (2) `useQuery approval-workflows-v2-contract` filter ApplicableType=3 client-side filter isUserSelectable=true (mirror PE Mig 25 pattern Plan AA S24) + (3) `Select dropdown "Quy trình duyệt V2 (tùy chọn)"` placement giữa FormFields + Budget section, blank = V1 fallback hint "(đã add ContractHeaderForm function, KHÔNG add ContractEditForm function vì spec scope CreatePage workspace only - edit-mode update endpoint defer)" + (4) Wire `approvalWorkflowId: approvalWorkflowId || null` vào CreateContractCommand POST body. BE precondition verify: `CreateContractCommand` record line 17-36 ContractFeatures.cs đã có `Guid? ApprovalWorkflowId = null` field (em main commit Chunk E1 PRIOR — comment marker "[Plan B S29 2026-05-22 Chunk E1] Drafter pick V2 workflow lúc create"). FE wire safe — no DTO mismatch. Build verify: `npm --prefix fe-admin run build` PASS 0 TS err 1926 modules 1.40MB gzip 358KB 16.07s; `npm --prefix fe-user run build` PASS 0 TS err 1916 modules 1.32MB gzip 343KB 8.84s. Pre-existing CSS @import warn + INEFFECTIVE_DYNAMIC_IMPORT realtime.ts warn unchanged (baseline noise). **Pattern 16-bis 4-place mirror check applied:** (1) Page file × 2 app DONE byte-similar; (2) App.tsx Routes N/A (enhance existing `/contracts/new` route - không route mới); (3) menuKeys.ts N/A (không menu key mới — page enhancement); (4) Layout staticMap N/A (route unchanged). Token ~12k Case 2 cookie-cutter (4 Read PE source + 2 Edit per file × 2 = 4 Edit total + 2 npm build + 1 git commit + memory update). Commit `62b50d1` clean 2 file. KHÔNG push remote — em main coordinate Chunk E final batch. **Pattern 5 mirror 2 app §3.9 applied 9th cumulative S20-S29** (proven IDENTICAL bytes hash check sau edit batch — git diff stat confirm). **Pattern 12-bis cross-module FE cookie-cutter mirror** demonstrated: PE PeWorkspaceCreateView V2 dropdown → Contract ContractCreatePage V2 dropdown clean (same useQuery shape, same Select markup, same filter logic, same POST body wire) — discriminator field ApplicableType=3 swap from `defaultType` (PE 1/2). Reusable pattern future Budget V2 / any cross-module entity với V2 workflow integration. Tag: `[pattern, phase-9, frontend]`.
- **2026-05-22 (S27 Plan CA Chunk B — Move 4 master pages fe-admin → fe-user, Case 2 cookie-cutter):** Spec từ em main deterministic 100% (Investigator pre-verify fe-user parity DataTable/PageHeader/PermissionGuard/usePermission/6 shadcn ui/types/master.ts byte-identical). Execute parallel: 4 `Write` cho master pages + 1 `Edit` menuKeys.ts (+5 key Catalogs*) + 2 `Edit` App.tsx (import + route block). LOC delta `+962` (4 file 948 LOC mirror + 14 LOC App.tsx + menuKeys.ts). Verify SHA256 byte-identical 4 file: `C1760788...` / `BDF0529E...` / `68213D62...` / `6F482614...` all match admin source. `npm run build` fe-user PASS 0 TS err 1916 modules 14.14s (pre-existing CSS @import + chunk-size + INEFFECTIVE_DYNAMIC_IMPORT warn unchanged). Commit `06a441c` 6 file changed. **Pattern 16 NEW** — byte-identical mirror admin → user khi parity confirmed (memory `pattern_master_page_mirror.md`): copy nguyên file (KHÔNG modify), verify SHA256 post-write, regression-safe vì admin code đã UAT pass. **Token cost ~10k Case 2** (4 file mirror cookie-cutter, NO logic decision). KHÔNG push remote (Chunk A em main solo BE parallel chưa xong, Chunk C sidebar filter + Chunk D smoke verify defer). Tag schema S28: `[pattern, phase-9, frontend]` cho Pattern 16. **Gotcha S27**: PowerShell `$_` variable in `ForEach-Object` block bị Bash tool shell-escape eaten — workaround dùng `Get-FileHash file1, file2, ... -Algorithm SHA256 | Format-Table` list literal thay vì pipeline iterate.
- **2026-05-21 (S26 t1, Plan AG Chunk A+B+C PASS — Phase 1 PE List tree view 2-level):** UAT feedback bro Tra Sol "đám rừng" flat list → Outlook folder tree. **3 chunk cumulative 1 commit** `0bf6c7e` 2 file +346/-116 LOC = +115 LOC each. Mirror 2 app §3.9 IDENTICAL post-edit (SHA256 verify match `21001E90...`). Chunk A useMemo group nested: `ProjectGroup{projectId, projectName, goiThauList[], totalCount}` + `GoiThauGroup{displayName, normalizedKey, items[]}`. Normalize trim + toLowerCase group key, display raw đầu tiên trong group. Fallback "(Dự án đã xoá)" empty projectName + "(Chưa phân loại)" empty TenGoiThau. Sort vi locale 2 cấp A-Z. Filter pendingMe → DaGuiDuyet áp dụng TRƯỚC group (empty state đúng). Chunk B UI `<details>/<summary>` HTML native 2-level — fe-user no shadcn Accordion → native browser disclosure widget free. Tailwind v3 named groups `group/proj` + `group/gt` cho chevron rotation `group-open/proj:rotate-90`. `[&::-webkit-details-marker]:hidden` ẩn default disclosure triangle browser. 📁 + 📄 emoji icon inline + count badge `rounded-full bg-slate-200/100`. PE card content preserve nguyên (text + badge + date format + contractId hint — line 209-248 cũ). Chunk C localStorage persist Set<string> key `pe_list_expanded_groups`. Project key: `projectId or '__no_project__'`. Gói thầu key: `${projectId}::${normalizedGoiThau}`. Default empty Set (all collapse) — Outlook-style closed default. `try/catch` defensive cho localStorage (storage quota / private browsing). Header badge `pendingMe ? totalRowCount : list.data?.total` (replace `rows.length`). Empty state check `projectGroups.length === 0` (replace `rows.length === 0`). Import `useMemo, useState` từ 'react' (file pre-existing chỉ import từ tanstack). Build: fe-user PASS 0 TS err 1291.33 KB gzip 337.00 KB 1907 modules 16.05s; fe-admin PASS 0 TS err 1402.68 KB gzip 357.51 KB 1926 modules 6.86s. Pre-existing CSS @import warn + INEFFECTIVE_DYNAMIC_IMPORT realtime.ts unchanged. KHÔNG ops git push (em main verify Reviewer rồi push). Token ~16k (close to ~14k baseline Case 2 mirror 2 app). **Pattern 19 NEW**: HTML native `<details>/<summary>` + Tailwind named groups (`group/<name>`) + localStorage Set<string> persist cho hierarchical UI when no Accordion lib available. Free open/close state native browser (Space/Enter keyboard accessible) + 0 JS state per node + serialize/deserialize Set ↔ JSON array string. Tailwind v3 named groups syntax `group/proj` parent + `group-open/proj:rotate-90` child differs from default unnamed `group` + `group-open:rotate-90` — critical when nested groups cùng level cần distinct event scope. Reusable cho future tree views: Project explorer · Dept hierarchy · Permission tree · Workflow definition step list (vs HTML5 native vs shadcn vs JS library). Anti-pattern: nested same-name `group` would inherit parent state → both rotate sync. **Pattern 5 mirror 2 app §3.9 applied 8th cumulative S20-S26** (proven reliable IDENTICAL hash check sau edit batch — recommend tooling `git diff fe-admin/X fe-user/X` after every multi-file edit batch).

View File

@ -0,0 +1,65 @@
# Implementer Agent — Archive Recent Activity Q1 2026-05 (S21-S24)
> **Archived:** 2026-05-22 by em main SOLUTION_ERP curate session.
> **Scope:** Recent activity FIFO entries S21 t3 → S24 Plan AA (12 verbose entries) — moved from MEMORY.md để giữ slim < 25KB threshold.
> **Rule §6.5 compliance:** KHÔNG cắt narrative — full verbose entries preserved cho cross-session audit.
> **Source MEMORY.md before archive:** 38.8 KB.
> **KEEP in MEMORY:** S26 Plan AG (Pattern 19 NEW) + S25 wrap (Patterns 16-18 NEW) + S25 Plan AB + setup baseline. Patterns 1-19 foundation section preserved.
---
## Archive entries (FIFO chronological — earliest first)
### 2026-05-13 (S21 t3-t5, REFUSED 3×)
Em main classified all 3 turns as cross-stack reasoning chain (BE+FE+test tightly coupled) → REFUSE per criteria #3+#4 (cross-stack > 2 layers, bug fix reasoning chain). Bug fix gotcha #45 = bug + reasoning, F1+F2+F3 = schema design decision, Refactor per-NV = drastic refactor schema + Service + FE × 2 app. All correct REFUSE — em main solo executed. Strict scope criteria validated S21 t3-t5 — REFUSE rate 100% match Anthropic warning "tightly interdependent coding". Cumulative: 84 test, 29 mig, 45 gotcha. Pattern saved future invocation: per-NV permission scope split natural theo role + EF migration BACKFILL reorder pattern.
### 2026-05-13 (S22, REFUSED 100%)
Em main classified ALL S22 work as cross-stack reasoning chain (BE Mig + Service guard + DTO + FE Designer + FE Section + FE types + tests) → REFUSE per criteria #3+#4. Em main solo executed. State chốt S22: **30 migrations** (+1 Mig 30 AllowApproverEditSection1 per-NV F4 flag), **104 test PASS** (+20 từ 84 — gồm PE WF ReturnMode + Draft guard + Reflection-based Authorize policy regression), ~146 endpoints (+3), 46 gotchas unchanged, 33 active prod users (13 cũ + 20 mới S22+2). 7 patterns successfully applied throughout S22 (validated continued effectiveness): Pattern 7 per-NV admin opt-in flag (Mig 30 follow Mig 29), Pattern 2 EF migration 3-file rule, Pattern 8 tách endpoint narrow scope (AdjustBudget vs UpdatePeDraft), Pattern 9 defense-in-depth FE+BE guard pair (S22+1 disable 3 button), Pattern 10 Reflection-based regression test cho Authorize policy (Plan C task 4 #44, 5 test ~50 LOC), Pattern 11 test infra helper cookie-cutter (SeedWorkflowAsync + SeedApproversAsync), Pattern 12 InternalsVisibleTo csproj expose internal helper cho test.
**Mismatches discovered S22:**
1. "Đang trong quá trình duyệt = người điều chỉnh cũng là người duyệt" — em first interpret default Approver scope always allowed → bro corrected per-NV admin opt-in flag (Mig 30). Lesson: clarify default behavior vs admin opt-in TRƯỚC khi default scope expansion.
2. `PE.changelogs` field KHÔNG có trong PeDetailBundle — em first design history display trong BudgetAdjustSection, build FAIL TS2339. Fix: removed history display (defer S23+ via separate fetch endpoint).
3. Dialog `size="xl"` NOT supported — only "sm" | "md" | "lg". Use "lg" cho preview iframe.
4. API auth field `accessToken` không phải `token`. Script `seed-test-users-prod.ps1` lần đầu FAIL 401 sau auth — em fix `$authResp.accessToken`.
### 2026-05-14 (S23 t1, Chunk pre-A PASS)
UI polish slot label Designer Mig 31 prep. File `fe-admin/src/pages/system/ApprovalWorkflowsV2Page.tsx` line 873 replaced `Quyền duyệt NV #{ei + 1}``Quyền duyệt {usersList.data?.find(u => u.id === entry.approverUserId)?.fullName ?? 'Chưa chọn NV'}`. Pattern: lookup user fullName từ `usersList` query (Option A — DTO `EditLevelEntry` không có `approverFullName` field, chỉ `LevelDto` BE response có `approverUserName` nhưng edit state dùng `EditLevelEntry`). `usersList` đã in scope ~line 500, pattern lookup `.find(x => x.id === e.approverUserId)` đã dùng tại line 535 cho validation. Cookie-cutter 1 file fe-admin only (fe-user KHÔNG có Designer per Investigator K0 S1). Tailwind classes preserved (`mb-1 text-[10px] font-medium uppercase text-amber-700`). Verify: `npm run build` fe-admin PASS clean 0 TS error, 0 new warning. Bundle 1395 KB (unchanged trivial). Token ~5k.
### 2026-05-14 (S23 t1, K1 Chunk A PASS)
Mig 31 schema swap F2 storage Users → ApprovalWorkflowLevels. Pattern Mig 29 ADD-DROP no-BACKFILL Option A (accept lose 4 prod user value `fin.pp` + `pm.nv` + `nv.test` + `truong.nguyen`). Cookie-cutter 6 BE file (User.cs -1 prop + ApprovalWorkflow.cs +1 prop `AllowApproverSkipToFinal` per-Approver-slot + ApprovalWorkflowConfiguration.cs +HasDefaultValue + PurchaseEvaluationWorkflowService.cs surgical -37 LOC F2 Drafter SUBMIT branch line 121-157 stub + Mig 3-file). TransitionAsync `bool skipToFinal` 8th param KEPT cho K2 repurpose APPROVE STEP. 4 Application compile-break sites (UserFeatures.cs LIST + GET DTO mapping + SetUserAllowDrafterSkipToFinalCommandHandler NoOp + PurchaseEvaluationFeatures.cs drafter flag = false) patched với sentinel `false` + K2 marker comment (DTO/Command signature unchanged per spec — K2 sẽ refactor). Mig 31 Up() manual reorder ADD-DROP correct (no BACKFILL). Both DBs Dev + Design applied successful. Build production projects clean 0 err 0 warn. Test compile error `PurchaseEvaluationWorkflowServiceReturnModeTests.cs:253` left for K7 chunk (spec exclude test scope). Pattern `feedback_per_nv_permission_scope.md` reinforced 3× cumulative (Mig 29 F1+F3 + Mig 30 F4 + Mig 31 F2-refactor). UserConfiguration.cs file không tồn tại — User entity configured inline `ApplicationDbContext.OnModelCreating` ~line 86, không có HasDefaultValue cho `AllowDrafterSkipToFinal`, EF picks prop change tự động.
### 2026-05-14 (S23 t1, K3 Chunk C PASS)
FE Admin Designer 7th checkbox AllowApproverSkipToFinal + banner rewrite. Pattern Mig 29/30 admin opt-in per-slot mirror **reinforced 3×** cumulative (Mig 29 F1+F3 5 checkbox + Mig 30 F4 1 checkbox + Mig 31 F2-refactor 1 checkbox = 7 checkbox total per slot). Cookie-cutter 1 file fe-admin only (`ApprovalWorkflowsV2Page.tsx`, fe-user no Designer per Investigator K0 S1). 7 sub-items atomic: (1) LevelDto type +`allowApproverSkipToFinal: boolean`, (2) EditLevelEntry type +same, (3) `makeDefaultLevelEntry` default false, (4) `copyFromDefinition` propagate `?? false`, (5) inline checkbox row position **cuối list** sau F4 AllowApproverEditBudget logical grouping (Edit Section 2 → Edit Budget → Skip to Final), (6) banner rewrite line ~623 từ "F2 cấu hình ở User Management" (Plan D S22 stale) → "Cấu hình quyền duyệt riêng cho từng NV trong slot Approver bên dưới (Trả lại / Edit Section 2 / Edit Budget / Duyệt thẳng Cấp cuối)", (7) POST/PATCH mutation body `levels.map` +allowApproverSkipToFinal. Verify: `npm run build` fe-admin PASS clean 0 TS error, 0 new warning. Bundle 1395.74 KB (unchanged trivial vs baseline). Diff +26/-7 LOC. Token ~6k. K5 next chunk cleanup zombie endpoint + UsersPage column.
### 2026-05-14 (S23 t1, K5 Chunk D PASS)
Cleanup zombie F2 endpoint + UsersPage column + DTO field + stale narrative comments (Reviewer Major #1 + Major #2 + Minor #3 + Minor #4). Pattern post-refactor full cleanup atomic 1 commit. BE 7 file (UsersController.cs DELETE PATCH /allow-skip-final endpoint + SetAllowDrafterSkipToFinalBody record; UserFeatures.cs DELETE UserDto field + SetUserAllowDrafterSkipToFinalCommand + Handler + sentinel-false mappings cleanup; ApprovalWorkflow.cs REWRITE stale narrative line 78-80 Mig 31 semantic + docstring line 108; PurchaseEvaluationFeatures.cs REWRITE Command DTO comment line 401; ApprovalWorkflowConfiguration.cs APPEND Mig 31 narrative line 22-24 + clean storage move comment line 87; ApprovalWorkflowV2AdminFeatures.cs clean DTO comment line 58; IPurchaseEvaluationWorkflowService.cs + PurchaseEvaluationDtos.cs clean stale "storage Users.AllowDrafterSkipToFinal" references) + FE Admin 2 file (UsersPage.tsx DELETE "Skip cuối" column TableHeader/TableCell + FastForward import + allowSkipMut mutation hook + FastForward toggle button; types/users.ts DELETE allowDrafterSkipToFinal field). fe-user KHÔNG đụng (no UsersPage admin-only + K6 sẽ handle Workspace Drafter checkbox), FE Designer page KHÔNG đụng (K3 done — 2 stale comment line 75 + 504 leftover deferred K6). Grep `AllowDrafterSkipToFinal` + `allow-skip-final` + `allowDrafterSkipToFinal` + `Skip cuối` + `FastForward` ZERO results across src/Backend (excl migrations) + fe-admin/src. Build BE production projects clean (0 err, 2 pre-existing DocxRenderer warn). Build fe-admin clean (0 TS err, 0 new warn). Diff +42/-94 LOC trên 9 file. Token ~12k. K6 Workspace Drafter checkbox cleanup next.
### 2026-05-14 (S23 t1, K7 Chunk F PASS)
Mig 31 Approver F2 service regression tests. Sub-task 1 fix broken Drafter F2 test reference K1 flagged: `PurchaseEvaluationWorkflowServiceReturnModeTests.cs:253` `drafter.AllowDrafterSkipToFinal = true` (DELETE 3 deprecated Drafter F2 tests entire — `SkipToFinal_DrafterAllowed_SetsPointerToFinalLevel` + `SkipToFinal_DrafterDenied_NonAdmin_Throws` + `SkipToFinal_AdminBypass_Succeeds`, semantic deprecated no value). Sub-task 2 add 3 new Approver F2 tests: `ApproveV2_SkipToFinal_AdminTickFlag_SetsPhaseDaDuyet` (happy path — slot Cấp 1 Bước 1 admin tick → Phase=DaDuyet, pointer cleared, opinion + PEA + Changelog logged), `ApproveV2_SkipToFinal_FlagOff_NonAdmin_ThrowsConflictException` (denied — flag off non-admin slot user → throw "chưa được phép duyệt thẳng Cấp cuối"), `ApproveV2_SkipToFinal_FlagOff_Admin_BypassesFlagCheck` (admin bypass — flag off admin role → DaDuyet allowed). Pattern 11 SeedWorkflowAsync cookie-cutter REUSE — created `SeedApproverF2WorkflowAsync` helper (2 Steps × 2 Levels — multi-step verify skip thẳng terminal KHÔNG fallthrough advance pointer next Step), AllowApproverSkipToFinal per-slot param. PE init Phase=ChoDuyet + CurrentWorkflowStepIndex=0 + CurrentApprovalLevelOrder=1 (vs S22 happy path từ DangSoanThao). Add `using Microsoft.EntityFrameworkCore` cho `.ToListAsync()` PEL/PEA/Changelog query. File header narrative line 17-25 REWRITE để track Mig 31 refactor semantic Approver scope ChoDuyet vs Drafter-from-Nháp cũ. Verify: `dotnet build` clean 0 err 2 warn pre-existing DocxRenderer. `dotnet test SolutionErp.slnx` 104 PASS (58 Domain + 46 Infra, 3 deleted + 3 added cancel out, baseline preserved). 3 Approver F2 tests verified individually PASS. Diff +175/-92 LOC trên 1 file. Token ~14k.
### 2026-05-15 (S24, Plan M Chunk M2 PASS)
F1 edge case Bước 1 reset ChoDuyet tests (em main M1 service edit `PurchaseEvaluationWorkflowService.cs` line 287-333 đã DONE — fallback Drafter TraLai → reset (0, 1) giữ ChoDuyet + audit log "không lùi được"). Cookie-cutter 1 file test `PurchaseEvaluationWorkflowServiceReturnModeTests.cs` — 2 sub-tasks: (1) extend `SeedWorkflowAsync` helper +2 params optional `allowReturnOneLevelL1` + `allowReturnOneStepL2` (default false, không phá compat 4 test ReturnMode existing), set vào `l1.AllowReturnOneLevel` + `l2.AllowReturnOneStep` tương ứng — Pattern 3 audit-reuse EXTEND không clone helper; (2) add 2 `[Fact]` test ngay sau test admin bypass OneLevel (line 241) — `ApplyReturnMode_OneLevel_AtStep1Level1_ResetsToBuoc1Cap1_KeepsChoDuyet` (PE init Step 0 Cấp 1 + actor=a1 + slot Cấp 1 tick AllowReturnOneLevel, build PE inline vì helper `BuildPeAtLevel2` không phù hợp cho Cấp 1) + `ApplyReturnMode_OneStep_AtStep1_ResetsToBuoc1Cap1_KeepsChoDuyet` (PE Step 0 Cấp 2 + actor=a2 + slot Cấp 2 tick AllowReturnOneStep, reuse `BuildPeAtLevel2`, OneStep service check `curStepIdx > 0` → fallback ngay không quan tâm Cấp). Assert: Phase=ChoDuyet (KHÔNG TraLai như Drafter mode) + pointer (0, 1) + SLA NotNull + Changelog **ContextNote** chứa "không lùi được" (Summary field cố định `"Chuyển phase {from} → {to}"`, summary từ ApplyReturnModeAsync chèn vào comment qua line 96-99 service → LogTransition `ContextNote = comment`). K7 cascade verify NO regression: 3 ApproveV2_SkipToFinal_* tests still green (M1 edit chỉ F1 OneLevel/OneStep edge case, KHÔNG đụng F2 path `ApproveV2Async`). Verify: `dotnet test SolutionErp.slnx` clean 0 err 2 warn pre-existing DocxRenderer, **106/106 PASS** (58 Domain + 48 Infra: +2 từ 46 baseline post Plan L). 10 ReturnMode-class tests verified individually PASS (4 ReturnMode + 3 ApproveV2_SkipToFinal + 1 Reject_NonApprover + 2 edge case mới). Diff +94 LOC trên 1 test file (test add + helper signature 2 params). Token ~10k. Spec deterministic + 1 file independent + < 1h verified.
### 2026-05-15 (S24, Plan M Chunk M3 PASS)
FE rename Phase=TraLai (98) display label "Trả lại" "Cần chỉnh sửa lại" cho UAT disconnect fix. Spec scope HẸP (display badge label + status reference) tuân thủ strict: 4 file FE × 2 app = 8 edit total. **2 file types/purchaseEvaluation.ts** × 2 app: `PurchaseEvaluationPhaseLabel[98]` (raw phase badge) + `PeDisplayStatusLabel.TraLai` (display status badge main user-facing). **2 file components/pe/PeWorkflowPanel.tsx** × 2 app: rename 2 inline literal hardcode "Trả lại" trong F1 dialog tooltip (`Phase → "..."`) + confirm message (`Phiếu sẽ về "..."`) đây status display reference, KHÔNG phải action verb. Pattern 5 mirror 2 app strict applied. KHÔNG đụng: (1) action button label `← Trả lại` (verb), (2) F1 mode picker label `Trả về Người soạn thảo` (4 mode action), (3) comments narrative line 62/71/etc giữ rationale dev (rule §6.5), (4) `types/contracts.ts` + `types/budget.ts` Phase 98 'Trả lại' module Contract + Budget khác PE, ngoài scope M3. Verify: `npm run build` fe-admin PASS clean (0 TS err, 9.40s, 1395 KB bundle unchanged trivial) + `npm run build` fe-user PASS clean (0 TS err, 6.92s, 1275 KB). Diff +4/-4 LOC × 2 = 8 LOC tổng trên 4 file. Token ~9k. Decision tactical: 2 chỗ inline literal PeWorkflowPanel rename để giữ UX consistency với badge label sau rename KHÔNG drift outside spec cùng "status display reference" semantics (badge + tooltip + confirm message phải mirror). Anti-fiddle threshold <20% LOC respected.
### 2026-05-15 (S23 t4-t11 cumulative REFUSE — em main solo Plan N+O+P+Q+R+S+T+U)
8 plan consecutive em main solo, 0 Implementer spawn (REFUSE 100% per criteria #4 bug fix reasoning chain + criteria #3 cross-stack tight coupling). **Plan N+O** 5 lookup site discrimination fix cross-stack BE Service + Application + 3 regression test. **Plan P** Controller TransitionPeBody record drop fix tightly coupled FE wire audit (Investigator confirm BE-only scope ~6 LOC). **Plan Q** FE banner mx-5 layout CSS polish 2 app mirror trivial 8 LOC. **Plan R+S+T5** destructive sqlcmd cleanup prod (scripts/plan-r-*.sql + plan-s-*.sql + plan-t5-*.sql + plan-t-backup.sql, 4 files scp + sqlcmd -i, ~720 rows wiped cumulative). **Plan T** DbInitializer DemoSeed:Disabled flag config (Infrastructure + Api appsettings, ~25 LOC). **Plan U** FE sidebar truncate + tooltip 2 app mirror 25 LOC (em main solo CSS Tailwind). 7 strict-scope criteria validated cumulative S23: pattern reusable saved memory user-level Plan O wire 9 surface points (point 9 lookup discrimination 5 sites enum), Plan P wire 10 surface points (point 10 Controller body record mirror count check), Plan T DemoSeed flag pattern.
### 2026-05-15 (S24, Plan AA Chunk B PASS)
FE user read-only matrix view workflow V2 ghim (Mig 25 IsUserSelectable). 3 file: (1) CREATE `fe-user/src/types/approvalWorkflowV2.ts` (~55 LOC) subset DTO mirror BE `AwAdminOverviewDto` (7 Allow* flag per Level, 5 record type AwLevelDto/AwStepDto/AwDefinitionDto/AwTypeSummaryDto/AwAdminOverviewDto); (2) CREATE `fe-user/src/pages/pe/WorkflowMatrixViewPage.tsx` (~215 LOC) useQuery GET `/approval-workflows-v2?applicableType=N&isUserSelectable=true` (em main BE Chunk A đã thêm `IsUserSelectable bool?` param + Controller forward), render table 10 cột Bước (rowSpan) | Cấp | NV duyệt | 7 ✓/— flag cell, header với `title` tooltip tả từng cột, 3 state Loading/Error/Empty ràng, badge `Đang dùng` (emerald isActive) + `Được ghim` (amber isUserSelectable Pin icon); (3) UPDATE `fe-user/src/App.tsx` import WorkflowMatrixViewPage + route `/purchase-evaluations/workflow-matrix` đặt TRƯỚC `/workspace` (URL ordering logical matrix workspace new detail). Verify: `npm run build` fe-user PASS clean 0 TS err, 1907 modules, 2.61s, 1282 KB. Surprise: shadcn fe-user KHÔNG `Card`/`Badge` (chỉ Button/Dialog/Input/Label/Select/Textarea) fallback inline `<div className="rounded-lg border...">` + inline `<span className="rounded-full bg-...">` cho badge (mirror pattern admin Designer). Pattern reusable: **read-only mirror admin Designer page** = drop edit mutations + reuse DTO types (subset) + filter param BE-side (`IsUserSelectable=true` thay FE filter). Cookie-cutter 0 (lần đầu pattern). Pattern 5 mirror 2 app KHÔNG apply (fe-admin Designer riêng Plan AA scope fe-user only). Pattern 7 admin opt-in 7 Allow* flag wire full render trong table (10 cột total = 3 meta + 7 flag). KHÔNG ops git. Token cost ~14k.
### 2026-05-15 (S24, Plan AA wrap)
Cumulative learning post-Chunk B. **3 patterns NEW added** (13 read-only admin Designer mirror, 14 Tailwind JIT palette array full class strings, 15 HTML table rowSpan flat row builder helper). **REFUSE log 4/4 correct** S24 polish chunks (commit da218f1 hotfix px-2 trivial < 30min criteria #6, 4d60598 redesign v1 panel-per-NV color UX flow criteria #2, fbbd361 redesign v2 table rowSpan UX flow criteria #2, ee0902a wrap fix sidebar label CSS polish criteria #6) em main solo executed all 4, Implementer no-spawn. **Ambiguity Chunk B**: shadcn fe-user thiếu Card/Badge (lib subset minimal chỉ Button/Dialog/Input/Label/Select/Textarea) fallback inline `<div className="rounded-lg border bg-card p-4">` + inline `<span className="rounded-full bg-...">` mirror admin Designer DefinitionCard. Pattern reusable cho fe-user pages requiring elevated UI surface (audit shadcn fe-user/src/components/ui/ TRƯỚC khi import). Total S24 commits: 7 (a1a910f..ee0902a 1 spawn Chunk B accepted + 6 em main solo: Chunk A cross-stack + Chunk C docs + 4 polish). Token cost cumulative S24 Implementer: ~14k (Chunk B only).

View File

@ -0,0 +1,38 @@
---
name: pattern-master-page-mirror
description: Mechanical byte-identical mirror admin → user page khi spec deterministic + types/components đã có sẵn ở target app
metadata:
type: feedback
---
# Pattern 16: Byte-identical mirror admin → user page (S27 Plan CA Chunk B)
**Rule:** Khi spec yêu cầu "Move N page từ fe-admin sang fe-user" + Investigator confirm fe-user CÓ types/components parity → copy NGUYÊN nội dung file (byte-identical SHA256). KHÔNG modify logic, KHÔNG adjust import path nếu `@/` alias resolve identical.
**Why:** Cognition cookie-cutter exception document — Case 2 mechanical parallel với em main BE work. Mirror identical = audit trail rõ (SHA256 verify), 0 implicit decision, regression-safe vì admin code đã UAT pass.
**How to apply:**
1. **Pre-flight verify** (mandatory before copy):
- Investigator brief listing types/master.ts + 6 ui components (Button/Dialog/Input/Label/Select/Textarea) + DataTable + PageHeader + PermissionGuard + usePermission ở fe-user
- Read source file để xác nhận pattern + count LOC
- Glob `fe-user/src/pages/{target_dir}/` để check không duplicate
2. **Execute (parallel Write tools):**
- `Write` cho mỗi file copy (nguyên nội dung từ Read source)
- `Edit` cho menuKeys.ts extend (insert sau anchor line cụ thể)
- `Edit × 2` cho App.tsx (1 cho import block + 1 cho route block)
3. **Verify (parallel):**
- `npm run build` fe-user pass 0 TS err
- `Get-FileHash ... -Algorithm SHA256` × N pairs match byte-identical
- Read fe-user/lib/menuKeys.ts + App.tsx confirm structure
4. **Commit format Chunk X format** với "Pending Chunk Y" footer
**S27 result:** 4 page (948 LOC) + menuKeys (+5 key) + App.tsx (+9 line) = 962 LOC, 6 file, commit `06a441c`, 1916 modules build 14.14s, all 4 SHA256 match.
**Decision tree:**
- Spec nói "copy" + parity confirmed → mirror byte-identical
- Spec nói "rewrite simpler version" + drop edit → Pattern 13 read-only mirror (S24 Plan AA Chunk B)
- Spec nói "share via package" → REFUSE, em main design package boundary
**Gotcha S27:** PowerShell `$_` in `ForEach-Object` block bị Bash tool shell-escape eaten. Workaround: dùng `Get-FileHash file1, file2, file3, ...` list literal + `Format-Table` thay vì pipeline iterate.
Related: [[pattern-fe-mirror-2-app]] (Pattern 5 mirror 2 app on edit), [[pattern-readonly-admin-mirror]] (Pattern 13 read-only flavor)

View File

@ -129,16 +129,26 @@ State machine 5 trạng thái phiếu PE: Nháp / Đã gửi duyệt / **Trả l
## 📅 Recent activity (last 10 FIFO)
- **2026-05-15 (S24 t1 spawn Pre-A Plan AA User Workflow Matrix view + sidebar widen):** 5Q audit. Q1 endpoint: `ApprovalWorkflowsV2Controller.cs:16-19` đã class-level `[Authorize]` bare từ S18 2026-05-08 (gotcha #44 fixed permanent), per-method admin Workflows.Create. Handler `GetAwAdminOverviewQuery` KHÔNG có IsUserSelectable filter — cần ADD param + Where conditional. Q2 menu seed: `DbInitializer.cs:1429-1437` peOrder global increment (Group=1, leaves 2/3/4 cycle per type). Permission seed `line 1541-1547` cho 7 role (Drafter/DeptManager/Procurement/CostControl/ProjectManager/Director/AuthorizedSigner). Accounting NOT trong list — admin manual grant nếu cần. Q3 Admin Designer: `ApprovalWorkflowsV2Page.tsx` 975 lines, AwLevelDto 13 fields (7 Allow*), VI labels line 892-948 ("Trả về 1 Cấp trước"/"Trả về 1 Bước trước"/"Trả về Người chỉ định"/"Trả về Drafter (mặc định)"/"Cho phép chỉnh sửa Section 2 (Hạng mục/NCC/Báo giá) lúc đang duyệt"/"Cho phép chỉnh sửa Section ngân sách lúc đang duyệt"/"Cho phép duyệt thẳng Cấp cuối khi đang duyệt"). KHÔNG có usePermission/PermissionGuard wrap (class-level [Authorize] route guard sufficient). Q4 sidebar widen: fe-user `Layout.tsx:325` + fe-admin `Layout.tsx:218` `w-60 xl:w-72` (240/288px). PE Workspace 2-panel `[260px_1fr] xl:[320px_1fr]` (NOT 3-panel như memory `feedback_responsive_laptop_breakpoint` stale claim). After widen `w-72 xl:w-80` (288/320px) SAFE (sidebar 288 + main 992 → workspace 260+732 fit @ 1280px). `w-80 xl:w-96` THRESHOLD RISKY (sát 700px remaining). Q5 ApplicableType enum `ApprovalWorkflow.cs:45-50` {DuyetNcc=1, DuyetNccPhuongAn=2, Contract=3}. Surprises: (1) memory responsive breakpoint stale "3-panel" → cần update sau Plan AA. (2) Order strategy "Luồng duyệt" Order=2 first → shift existing leaves +1 → cần DbInitializer UPDATE Order existing (KHÔNG chỉ INSERT-if-not-exists). (3) Contract=3 chưa wire FE (chỉ DuyetNcc + DuyetNccPhuongAn). Recommendation: Proceed Plan AA — chỉ ADD param filter + 1 menu key + page mới + sidebar widen. Token cost ~32k.
- **2026-05-15 (S24 t1-t4 post-spawn, em main solo 4 polish chunks — Plan AA wrap):** 7 commits total `a1a910f..ee0902a`: BE+Layout (`ee776d5`) + FE Page (`c667802`) + Docs (`ac2c859`) + 4 polish iter UAT (`da218f1` px-2 + `4d60598` v1 panel-per-NV + `fbbd361` v2 table rowSpan + `ee0902a` sidebar label wrap). **Pattern reusable**: `inline-block icon + inline text + absolute ChevronDown` cho hanging-indent reverse wrap (label dài về đầu hàng). Mirror admin Designer style cho user view read-only. **Sidebar widen tradeoff**: `w-72 xl:w-80` + remove `truncate` FAIL fit 44+ chars label custom Mig 27 — cần combine với `text-[12px] + leading-snug` + restructure flex → block + inline. Sole `truncate` removal không đủ — phải full layout restructure NavLink. **Memory drift confirmed**: `feedback_responsive_laptop_breakpoint.md` claim "PE Workspace 3-panel" → S24 verify ACTUAL **2-panel** (260+1fr only, KHÔNG có panel thứ 3). Cross-ref update needed memory user-level (admin trigger curate sau). **Plan AA color palette success**: STEP_PALETTE 5 màu (blue/purple/emerald/amber/pink) + LEVEL_PALETTE 5 màu (violet/sky/teal/orange/rose) — **Tailwind JIT yêu cầu full class strings array, KHÔNG dynamic interpolation** (`bg-${color}-100` FAIL — class purged). Reusable cross-project cho menu hierarchy color coding (step parent + level child distinct palette). Final layout v2 table rowSpan: Step column merge per-Phòng + Level column rowSpan per-Cấp + NV column 1-row-per-approver, đẹp hơn v1 panel-per-NV stacked. Token cost iteration negligible (em main solo, no spawn).
- **2026-05-15 (S23 t8 spawn Plan R pre-flight cleanup audit):** Bro chốt cleanup destructive prod. 4 sqlcmd queries audit: 35 PE total (28 active + 7 soft) + 17 V2 (15 IsUserSelectable=false + 2 ghim) + 4 V1 (2 active + 2 inactive). FK gotcha catch: PE.ApprovalWorkflowId Restrict + ApprovalWorkflow extends `BaseEntity` NO soft-delete → hard-DELETE required; LevelOpinion → ApprovalWorkflowLevel Restrict cascade block. SQL Express limit: NO COMPRESSION + RESTORE VERIFYONLY require sysadmin. Filtered indexes (Mig 29+) require `SET QUOTED_IDENTIFIER ON`. Cascade child estimate: 446 PE children + ~140 V2 + ~37 V1 = ~620 rows. 3 Option compare → bro chốt A (Hard-DELETE PE + V2 unghim + V1 inactive, GIỮ V2 ghim + V1 active). Plan F precedent: KHÔNG drop V1 active (PE pin → BE crash).
- **2026-05-15 (S23 t6 spawn Plan P FE wire audit — confirm BE-only scope):** Em main hypothesize Plan P scope BE Controller body record drop. Investigator audit FE × 2 confirm: `PeWorkflowPanel.tsx:113-124` `api.post(/transitions, body)` SEND ĐÚNG 7 fields (TargetPhase + Decision + Comment + ReturnMode + ReturnTargetUserId + SkipToFinal). No service file (untyped object literal). BE `PurchaseEvaluationsController.cs:267` `TransitionPeBody` record CHỈ 3 fields → ASP.NET silent DROP 3 missing fields. Verdict: Plan P BE Controller ONLY ~6 LOC + no test (Mig 28/31 Domain test cover handler). Saved em main blind fix cross-stack.
- **2026-05-15 (S23 t3 spawn — UAT bug Allow* flags không hiện cho actor non-row1):** Bro UAT login `nv.test@solutions.com.vn` vào menu eoffice "Duyệt NCC → Duyệt" phiếu PE/2026/A/026 (Phase=ChoDuyet, WF=QT-DN-V2-001 v12, ở Bước 2 Cấp 1 4 NV: Trần Xuân Lưu/NV Test UAT V2/Hồ Thị Nữ Nguyên/Lê Văn Bính). Admin ĐÃ tick 7 Allow*=TRUE riêng cho slot NV Test UAT V2. Nhưng FE dialog Duyệt KHÔNG hiện checkbox SkipToFinal + Trả lại 4 mode + Edit. **Verdict: HYPOTHESIS B — BE handler picks wrong slot row.** Evidence: (1) `PurchaseEvaluationFeatures.cs:765` `var curLevel = curStep?.Levels.FirstOrDefault(l => l.Order == curLevelOrder);` — match FIRST row có `Level.Order == curLevelOrder`, NOT actor's row. Post-Mig 29 refactor, Level.Order trùng nhau cho mọi NV cùng Cấp (4 row có `Order=1` ở Step 2). EF returns Trần Xuân Lưu trước (PK order) → `FirstOrDefault` lấy row đó (all-false except Drafter=true). (2) API curl admin token + nv.test token return CÙNG `currentLevelOptions={ allowReturnOneLevel=false, OneStep=false, Assignee=false, Drafter=true, EditDetails=false, EditBudget=false, SkipToFinal=false }` — handler currentUser-agnostic confirm. (3) Workflow detail GET `/approval-workflows-v2?applicableType=1` `.types[0].active.steps[1].levels` enumerate 4 slot Bước 2 Cấp 1: NV Test (UAT V2) = ALL 7 TRUE; 3 NV còn lại = ALL FALSE (trừ Drafter=true mặc định). Admin Designer wire ĐÚNG (`fe-admin/src/pages/system/ApprovalWorkflowsV2Page.tsx:889-946` 7 checkbox per-slot). FE consumer wire ĐÚNG (`fe-user/src/components/pe/PeWorkflowPanel.tsx:51 levelOptions = evaluation.currentLevelOptions` + line 343/357/371/397 conditional render mode picker + line 425 SkipToFinal checkbox). **Root cause:** BE line 765 lookup semantic broken sau Mig 29 (S21 t5). Trước Mig 29, 1 Level row per Cấp + `ApproverUsers` join table → `FirstOrDefault(Order==X)` đúng. Sau Mig 29 split 1 Level row PER ApproverUser → `Order` field collide → cần match thêm `ApproverUserId == currentUser.UserId`. **Fix BE 1 dòng:** `var curLevel = curStep?.Levels.FirstOrDefault(l => l.Order == curLevelOrder && l.ApproverUserId == currentUser.UserId);` (admin bypass: fallback `?? curStep?.Levels.FirstOrDefault(l => l.Order == curLevelOrder)` để admin xem detail không lỗi). Edge case: Phase=DaDuyet/TraLai/TuChoi pointer null → existing guard line 760-762 skip block OK. **LOC ~2-3 LOC 1 file.** Surprise: bug PRESENT từ deploy Mig 29 (S21 t5 2026-05-13) — không phải regression S23. Lý do trước đây không bắt: UAT test users (default 13 cũ) đa số là row đầu của slot Cấp (Admin tick toàn FALSE → behavior giống nhau, không lộ); chỉ bộc lộ khi admin tick CHỌN LỌC per-NV (UAT V2 S22+2 thêm 20 user role-based + bro tick chỉ NV Test UAT V2). Cross-reference memory `feedback_per_nv_permission_scope.md` cumulative S21 t5 → S22+5 → S23 t1 — KHÔNG có entry nào nhắc bug lookup BE post-refactor.
- **2026-05-15 (S23 t2 spawn M0 — Plan M F1+F2+F3+F4 ChoDuyet semantic audit):** Bro UAT post-Plan L deploy 2026-05-15 ~02:00: "Hiện logic cũ là khi trả lại 1 cấp hoặc chỉ định hoặc edit là trạng thái draft -> cái này thay đổi lại nhé, các tính năng duyệt thẳng, trả lại 1 cấp hoặc người chỉ định hoặc cho edit thì cho xử lý đc ở trạng thái đang gửi duyệt luôn." Audit 4 BE file + 4 FE file × 2 app cho F1+F2+F3+F4 phase semantic ChoDuyet preservation. **Verdict: CODE ĐÃ ĐÚNG semantic mới — đây là DISCONNECT bro mental model vs code reality post-Mig 28/29/30/31.** Evidence per feature: **F1.OneLevel** `PurchaseEvaluationWorkflowService.cs:285-312` SWITCH branch lùi 1 Cấp cùng Step (`curLevel-1`) ELSE lùi Step trước Cấp cuối; **Phase KHÔNG đổi** (giữ ChoDuyet, line 364 reset SLA only); fallback Drafter `:303-310` CHỈ KHI đang Bước 1 Cấp 1 no further back (clear pointer + Phase=TraLai). **F1.Assignee** `:335-360` foreach Steps find ApproverUserId match → set pointer; Phase giữ ChoDuyet; ConflictException nếu không tìm thấy NV trong workflow. **F1.OneStep** `:314-333` lùi prev Step Cấp max; Phase giữ ChoDuyet; fallback Drafter nếu đang Bước 1. **F1.Drafter** `:268-275` SET `Phase=TraLai=98` + clear cả 2 pointer + SLA null — đây là CASE DUY NHẤT về "draft". **F2 skipToFinal** `:483-524` Plan K L1 ĐÃ FIX advance pointer tới Bước cuối Cấp cuối (max), **Phase giữ ChoDuyet** (NV cuối duyệt thật để DaDuyet); guard line 485 ConflictException non-admin + flag off; opinion UPSERT trước line 441-468 ensure actor's signature lưu trước. **F3 EnsureEditableForDetailsAsync** `PurchaseEvaluationDetailFeatures.cs:42-99` 2 trường hợp accepted: Drafter scope (DangSoanThao/TraLai return pe sớm line 49-51) **OR Approver scope ChoDuyet line 54-94** (V2 schema + actor match level.ApproverUserId + level.AllowApproverEditDetails flag). **F4 AdjustPurchaseEvaluationBudgetCommandHandler** `PurchaseEvaluationFeatures.cs:272-329` Drafter scope `:283-290` (DangSoanThao/TraLai + isDrafter) ELSE Approver scope ChoDuyet `:291-323` (V2 + pointer init + actor match + level.AllowApproverEditBudget flag) — Phase ChoDuyet đã handle full. **Validation Allow* flag location** `PurchaseEvaluationWorkflowService.cs:252-265` ApplyReturnModeAsync gate per slot per mode: throw ConflictException nếu disabled (Admin bypass line 252). **FE PeWorkflowPanel.tsx**: TraLai dialog `:331-422` 4 radio button (OneLevel/OneStep/Assignee/Drafter) gated bằng `levelOptions?.allowReturnXxx` flag per current Approver Cấp (line 343-396); useEffect `:60-68` S23 t2 fix default first available mode KHÔNG Drafter khi admin tick F1 modes. Skip Final checkbox `:425-442` chỉ visible khi `levelOptions?.allowApproverSkipToFinal` + Approve forward direction (line 425). **FE PeDetailTabs.tsx F3+F4 wire**: `itemsReadOnly = readOnly && !approverEditMode` line 118 bypass readOnly khi F3 enabled (Mig 28 pattern); `canAdjust = isAdmin || (!readOnly && isDrafter && isDrafterPhase) || isApproverChoDuyet` line 977 bypass readOnly khi F4 enabled (L2 fix). Mirror fe-admin/fe-user line 109-115 + 967-979 ĐỒNG BỘ rule §3.9. **Surprise**: "Trả lại" trong UI memory docs đôi khi gọi "draft" colloquial — bro confuse 2 khái niệm `Phase=TraLai=98` (Drafter sửa rồi gửi LẠI từ Step 0) vs `Phase=DangSoanThao=1` (chưa từng gửi duyệt). KHÔNG code path nào trong F1 OneLevel/OneStep/Assignee/F2/F3/F4 set targetPhase=DangSoanThao mà không nên. **Recommendation: LOW effort** (0-20 LOC): chỉ cần communication clarification em main confirm với bro 4 mode F1 + F2 + F3 + F4 đã giữ Phase=ChoDuyet trừ F1.Drafter; mở DB SELECT phiếu UAT confirm `Phase` column number sau click test; OPTIONAL touch up FE label nếu user thấy "Đã gửi duyệt" nhầm "Bản nháp". KHÔNG cần Service refactor hay handler change. Nếu bro thấy phiếu cụ thể về Phase=1 SAU click F1.OneLevel hoặc F2 → spawn lại Investigator audit DB state + log Changelog cho phiếu đó cụ thể (data debug, not code bug).
- **2026-05-15 (S23 t2 spawn L2 — F3+F4 edit menu Duyệt audit):** Bug UAT prod 409a967: admin tick F3+F4 cho slot Approver, login actor user, vào menu "Duyệt" (`?pendingMe=1` cùng `PurchaseEvaluationsListPage` 3-panel), click PE Phase=ChoDuyet → Section 2 Hạng mục/NCC/Báo giá + Section 5 Điều chỉnh ngân sách vẫn read-only. **Root cause F3 = OK / F4 = BROKEN at readOnly short-circuit:** F3 wire ĐÚNG (`PeDetailTabs.tsx:118 itemsReadOnly = readOnly && !approverEditMode` override readOnly per Mig 28 S21 t4 → ItemsTab read prop OK). F4 wire SAI (`PeDetailTabs.tsx:245` passes `readOnly={readOnly}` (=true từ menu Duyệt) xuống BudgetAdjustSection, line 973 compute `canAdjust = !readOnly && (isAdmin || (isDrafter && isDrafterPhase) || isApproverChoDuyet)``!readOnly` short-circuits to false BEFORE F4 isApproverChoDuyet evaluate. Edit pencil button hidden line 1030 `{canAdjust && <Button>Điều chỉnh</Button>}`. Asymmetric vs F3 pattern Section 2 (PeDetailTabs.tsx:113-118). **Evidence:** FE F3 OK `fe-user/src/components/pe/PeDetailTabs.tsx:113-118` + `:224 ItemsTab ev={evaluation} readOnly={itemsReadOnly}`; FE F4 BUG `:957-973 BudgetAdjustSection !readOnly gate` + `:245 readOnly={readOnly}`; menu Duyệt route `fe-user/src/pages/pe/PurchaseEvaluationsListPage.tsx:256-261 PeDetailTabs readOnly={true}` (cùng route Danh sách); BE GetPe `PurchaseEvaluationFeatures.cs:735-770 currentLevelOptions populate 7 fields` OK; BE budget-adjust handler `:281-329` Phase ChoDuyet F4 branch + actor match + flag check ĐẦY ĐỦ + return ConflictException nếu Allow=false; BE PurchaseEvaluationDraftGuard.EnsureEditableForDetailsAsync ở `PurchaseEvaluationDetailFeatures.cs:42` (8 callsites Detail+Supplier handlers). **Recommendation:** Fix `BudgetAdjustSection` line 973 mirror approverEditMode pattern Section 2: thay `canAdjust = !readOnly && (isAdmin || ...)` thành `canAdjust = isAdmin || (!readOnly && isDrafter && isDrafterPhase) || isApproverChoDuyet` — Approver bypass readOnly khi F4 conditions met. **LOC ~3-5 LOC 1 file.** Surprise: Inbox `/inbox` route (InboxPage.tsx) navigate sang `/purchase-evaluations/:id` (mobile DetailPage route, default readOnly=false) — chỉ Danh sách `?pendingMe=1` desktop 3-panel mới hard readOnly=true.
- **2026-05-14 (S23 t1 spawn K0 — Plan K F2 refactor pre-flight):** Audit F2 state cho Plan K Mig 31 (move `Users.AllowDrafterSkipToFinal``ApprovalWorkflowLevels.AllowApproverSkipToFinal` + change semantic Drafter Nháp → Approver ChoDuyet skip thẳng Cấp cuối). **Confirmed state Mig 30:** Migrations path = `Persistence/Migrations/` (not direct `Migrations`); 30 mig latest = `20260513160703_AddAllowApproverEditBudgetToLevels`; `User.cs:38` AllowDrafterSkipToFinal prop; `ApprovalWorkflow.cs:86-105` 6 Allow* props (4 ReturnMode + EditDetails + EditBudget) per Level slot; F2 Drafter branch ở `PurchaseEvaluationWorkflowService.cs:119-161` trong SUBMIT branch (line 125 `if (skipToFinal && evaluation.ApprovalWorkflowId is Guid skipAwId)` check user.AllowDrafterSkipToFinal); APPROVE STEP branch ở `~line 393-525` (advance pointer). TransitionAsync signature: `skipToFinal` là param thứ 8 (position 47:47), default=false. `TransitionPurchaseEvaluationCommand``PurchaseEvaluationFeatures.cs:393-402` với param `SkipToFinal=false` default. `ApprovalWorkflowOptionsDto``PurchaseEvaluationDtos.cs:86-92` (6 field). `PurchaseEvaluationDetailBundleDto.DrafterAllowSkipToFinal` ở line 217 + `CurrentLevelOptions` ở line 214. UsersController `PATCH /users/{id}/allow-skip-final` ở line 91-98 + `SetAllowDrafterSkipToFinalBody` ở line 105. `SetUserAllowDrafterSkipToFinalCommand``UserFeatures.cs:332`. **FE state:** fe-admin Designer `system/ApprovalWorkflowsV2Page.tsx` slot label "NV #{ei + 1}" ở line 873 (KHÔNG phải "#NV {order}" theo prompt) — inline checkbox panel 5+1=6 checkbox ở line 853-933 (4 ReturnMode + EditDetails + EditBudget). fe-admin `system/UsersPage.tsx` "Skip cuối" column line 306-318 + FastForward button toggle line 365-372 + allowSkipMut hook line 181-186. fe-admin/fe-user PeDetailTabs Drafter Workspace checkbox "Gửi thẳng Cấp cuối (skip trung gian)" ở line 287-297 (admin) / 294-304 (user). **GAP fe-user**: KHÔNG có UsersPage + ApprovalWorkflowsV2Page (admin-only mgmt) → Plan K UI changes localized fe-admin chỉ; fe-user side chỉ touch PeDetailTabs (remove old Drafter checkbox + thêm Approver toggle near Duyệt button). **Drift Dev DB**: Total=2 user (admin + test.drafter), Flagged=0 — NOT match 33-user prod seed. **Prod actual**: Total=33 / Flagged=4 (NOT 2 per S22+2 spec). 4 user flagged sẽ lose value when DROP column — acceptable per new semantic (Drafter pre-submit moot).
- **2026-05-13 (S22, no spawn — em main solo throughout):** S22 18:00→~21:00 em main solo. Cumulative state: 30 mig (+1 Mig 30 `AddAllowApproverEditBudgetToLevels` F4 per-Level slot), 104 test PASS (+20: 5 reg #44 Authorize policy + 7 ReturnMode + 7 Guard + 1 V2 actor scope reject), ~146 endpoints (+3: PATCH /users/{id}/allow-skip-final + PATCH /pe/{id}/budget-adjust + GET /pe/{id}/attachments/{attId}/view), 46 gotcha unchanged, 19 memory unchanged (recommend +1 entry — see below). Prod active users 13→33 (+20 role-based: act.nv/pp/tp, bod.1/2, equ/fin/hra/pm/qs.nv/pp/tp). **Discoveries S22:** (1) **Per-NV admin opt-in flag pattern reinforced 2×** — Mig 30 F4 cùng pattern Mig 29 F1+F3 (S21 t5). Bro corrected em main lần đầu: "phải tick checkbox như Section 2", default = admin opt-in per slot, KHÔNG = mở rộng default. Cross-ref memory `feedback_per_nv_permission_scope.md` proven 2×. (2) **Plan F drop V1 ABORTED** — pre-flight sqlcmd reveal Contract entity HOÀN TOÀN V1 chưa wire V2 (chưa có ApprovalWorkflowId column) + 4 PE V1-only + 19 PE V1+V2 mix. Lesson: drop migration cần verify entity scope toàn bộ (Contract liên đới — không chỉ PE). (3) **Identity password policy ≥12 chars** — seed 20 user FAIL 400 với "User@123456" (11 chars), `TestUser@2026` (13 chars) pass. (4) **Identity rename atomic 4 fields** confirm gotcha #38: Email + NormalizedEmail + UserName + NormalizedUserName + FullName; sqlcmd cần `SET QUOTED_IDENTIFIER ON` cho filtered unique index. (5) **API login response field name `accessToken` + `refreshToken` + `user`** — KHÔNG có field `token` (correct prior Bash example trong spec dùng `.token` sẽ fail). (6) **PS 5.1 ASCII-only script discipline** reinforced gotcha #30: `seed-test-users-prod.ps1` viết Vietnamese names without diacritics tránh parser error. Recommend bro add 1 memory entry "Admin opt-in flag pattern proven 2×" cumulative Mig 29 + Mig 30.
- **2026-05-13 (S21 t3-t5, no spawn):** Em main solo 3 turns (bug fix gotcha #45 + F1+F2+F3 workflow-level Mig 28 + refactor per-NV Mig 29). Implementer REFUSE per cross-stack reasoning chain rule. Investigator KHÔNG spawn — em main đã có context cumulative S20 t12 setup + active dev throughout. No findings to flush. Cumulative state update: 84 test, 29 mig, 45 gotcha, 19 memory entries (+2 S21 t5 pending), 6 skills unchanged. Pattern reusable saved cho future spawn: per-NV permission scope split + EF migration ADD→BACKFILL→DROP reorder.
- **2026-05-22 (S29 wrap — Plan CA + Plan B pre-flight 2 spawns + 3 patterns NEW):** S29 close 2 big plans cumulative. **Plan CA (Move Cấu hình danh mục admin → eoffice)** 7 commits `06a441c..6eec8d7` push deployed Run #229+#230 PASS. **Plan B (Contract V2 wire mirror PE V2 pattern Mig 22-26)** 11 commits `58898e8..38f1c4d` push deployed Run #231+#232 PASS. **Investigator role 2 spawn:** (1) **Plan CA pre-flight audit** (agentId a7ab5576c77ee3730, ~15K token): map 9 menu danh mục terrain + GOTCHA tree-inherit Catalogs↔Master + recommend chunk structure A/B/C/D. fe-user component parity verified (DataTable + PageHeader + PermissionGuard + 6 shadcn ui). DbInitializer SeedAdminPermissionsAsync admin role grant CRUD 27 menu key. Permission Matrix dynamic load /api/menus → tự reflect post-move. (2) **Plan B pre-flight audit Contract V1 state** (agentId abf91b30391fb0cdb, ~25K token): map `Contract.cs` 25 fields (V1 only) + `ContractPhase` enum 12 values (TraLai=98 + TuChoi=99 ready) + `ContractWorkflowService.cs` 220 LOC V1 only + sqlcmd prod 7 V1 contract (Phase 2-8 active workflow) + PE Mig 22-26 reference templates + impact assessment (COEXIST V1+V2, KHÔNG drop V1) + re-chunk 6 chunks (split A → A1 entity + A2 mig). 3 surprise: ApprovalWorkflows Prod ZERO ApplicableType=3 (Chunk A2 must seed sample) + `RejectedAtStepIndex` drift deprecated + ContractType 7 variants generic ApplicableType=3. **Patterns proven NEW Investigator perspective S29:** (a) **9-menu permission terrain map pattern** (Plan CA) — Investigator inventory file paths + line ranges + component parity check + recommend chunk before delegate Implementer. ROI cao. (b) **V1+V2 coexist boundary** (Plan B) — KHÔNG drop V1 vì 7 prod contract pinned. Mirror PE Mig 22-24 spec. (c) **Reference template paths cross-module mirror** (PE → Contract V2): Investigator cite EXACT line ranges (PE Mig 23:14-31, Mig 24:14-17, Mig 26 UNIQUE composite, Service ApproveV2Async 446-634, 519-546 UPSERT, 774-783 ResolveActor) → em main + Implementer chỉ cần mirror rename entity. ROI rất cao saving 30%+ time. **Anti-patterns observed:** (a) Investigator KHÔNG verify endpoint exists end-to-end qua live curl — defer to CICD Monitor stage 4. (b) Investigator output sometimes verbose >700 words → em main hỏi cô đọng <500 next time.
- **2026-05-22 (Plan B Contract V2 wire pre-flight audit Q1-Q5 + Bonus re-chunk):** Em main spawn Investigator pre-flight Plan B priority HIGH. 5Q audit + Bonus chunk refinement. Tag schema: `[audit, phase-9, contract]`. **Findings:** (Q1) `Contract.cs:8-58` 25 fields, V1 ready (WorkflowDefinitionId line 22 + CurrentWorkflowStepIndex line 39 + Phase line 12 + RejectedAtStepIndex line 40) MISSING `ApprovalWorkflowId Guid?` + `CurrentApprovalLevelOrder int?` need Mig 32 add. (Q2) `ContractPhase.cs:14-28` 12 values ready ChoDuyet=10 + TraLai=98 + TuChoi=99 mirror PE. `ContractWorkflowService.cs:23-219` 220 LOC V1 ONLY. NO V2 branch. Match Dept+PositionLevel line 113-126. Gen line 148-155 trigger last step Phase=DaPhatHanh (terminal khác PE DaDuyet). (Q3) Prod: 7 Contracts 100% V1Pinned (Mig 21 flat WorkflowDefinitionId), 6 in-workflow Phase 2-8 MUST coexist V1+V2, KHÔNG drop V1. ApprovalWorkflows Prod: 2 rows `ApplicableType=1 (DuyetNcc)` ZERO `ApplicableType=3 (Contract)` chưa seed. (Q4) Mig 22 `20260508053749_AddApprovalWorkflowsV2.cs:14-127` 3 CREATE TABLE shared cho cả PE+Contract, Mig 32 chỉ cần ADD COLUMN tại `Contracts`. Mig 23 `20260508072821_AddApprovalWorkflowIdToPurchaseEvaluation.cs:14-31` template 15 LOC cookie-cutter rename PeContract. Mig 24 `20260508074937_AddCurrentApprovalLevelOrderToPe.cs:14-17` 3 LOC. `ApproveV2Async` `PurchaseEvaluationWorkflowService.cs:446-634` 189 LOC clone mẫu load AW.Steps.Levels + match actorId pendingLevelGroup.ApproverUserId (line 484-495) + UPSERT opinion (line 522-546) + advance level/step (line 605-633) + skipToFinal F2 (line 561-602). Branch entry pattern line 167-171 ternary `if (evaluation.ApprovalWorkflowId is Guid awId) ApproveV2Async else ApproveV1Legacy`. (Q5) Risk LOW schema 80% shared. F1-F4 7 flags Mig 29+30+31 trên `ApprovalWorkflowLevels` (entity line 86-114) inherits FREE Contract. **Risk MEDIUM:** ContractType discriminator 1 workflow serve ALL 7 ContractType vs per-type? PE chỉ 2 type. Defer Q em main. **Bonus re-chunk 6 chunks:** A1 entity 5 LOC (em main) + A2 Mig 32 cookie-cutter (Implementer) + B Service ternary 189 LOC clone adapt gen terminal (em main critical cross-stack) + C Mig 33 ContractLevelOpinions cookie-cutter (Implementer) + D FE-User Workspace Select V2 (Implementer mirror) + E FE Section 5 LevelOpinionsV2 dynamic (Implementer mirror). **Surprises:** (a) ApprovalWorkflows Prod ZERO ApplicableType=3 recommend Chunk A2 include DbInitializer seed sample. (b) `RejectedAtStepIndex` Contract line 40 DEPRECATED chưa marked obsolete drift với PE line 50. (c) ContractType 7 variants vs ApprovalWorkflow generic ApplicableType=3 future Mig 34 thể cần `ContractTypes int[]` filter nếu Solutions cần per-type workflow. **Recommendation: PROCEED coexist V1+V2 pattern, 6 chunk re-chunk.** Token ~25K. source_path: `solution_erp/audit/investigator-plan-b-contract-v2-preflight-2026-05-22`.
- **2026-05-22 (S28 wrap Layer A governance internalized + tag schema mandatory forward):** S28 5-turn arc t1 RAG ROI verdict t2 over-reach mistake t4 bro caught t5 Layer A active. Investigator agent absorb 3 rules forward: (1) **Tag schema mandatory** every RAG ingest `[<type>, phase-<N>, <bc-or-module>]` type {bug, plan, audit, schema, ui-pattern, decision, retrospective, gotcha-doc}; phase enum 11 (`phase-9` UAT current); bc/module 8 domain {contract, pe, budget, master, identity, forms, notifications, audit-log} + 5 cross-cutting {cicd, infra, rag, governance, deploy}. (2) **4-category default + skip list** skip ephemeral state ("S28 t3 startup ngày 22"), pure file paths không context, single-file < 5-line edit confirmation. Investigator output report tags MUST include all 3. (3) **NO self-authorize cross-project rule** lesson S28 t4: stay scope-down SOLUTION_ERP self-discipline, don't generalize policy without bro consent. **Pre-flight audit query patterns Layer A §6.2** internalized: Pattern A `lookup_gotcha_<id>` (top_k=3, narrow), Pattern B `verify_precedent_pattern_X` (top_k=3 thay default 10 broad similarity rerank surface 3 best matches), Pattern C `find_code_symbol_<class.method>` (top_k=5, symbol-anchor). **Weekly Friday eval ritual:** Investigator support Plan B Contract V2 wire pre-flight audit kick-off khi bro launch. Cumulative audit checklist (foundation cicd-monitor MEMORY S22+5S23 t6 10-surface-point per-NV): (a) Contract entity V1 state inspect `Domain/Contracts/Contract.cs` + `WorkflowDefinitionId`, (b) Mig 22-23/26 PE V2 mirror reference confirm pattern reusable, (c) 4 PE V1-only audit migrate gap (per memory S22+4 Plan F ABORT), (d) 7 V1 contract scope drop boundary, (e) verify 10-surface-point per-NV checklist S22+5 cumulative reusable Contract V2 wire trước Mig 32 schema design. **source_path convention output:** `solution_erp/audit/investigator-<topic>-<date>` standardized cho tag schema BC=`pe` or `contract` switch per task. **Lesson abandoned:** S28 t2 "ghi RAG mọi tương tác" rule ABANDONED Investigator KHÔNG mass-ingest, only specific findings with full tag schema. Audit retroactive S20-S26 "Investigator spawn" claims (memory log discrepancy registry NOT loaded vs token cost ghi 28-40K) defer S28+ priority #4 uncertainty preserve cross-session, không retract memory unverified.
- **2026-05-22 (S27 wrap-up em main proxy - retrospective Investigator):** Investigator pre-flight CRITICAL miss: nếu spawn pre-Plan A.3 (RAG manual control build) would have catch Qdrant Web UI static missing PRE-em main write rag-dashboard.ps1 link `localhost:6333/dashboard`. Pattern reinforced: **pre-flight infrastructure audit MUST spawn Investigator** trước khi em main claim "X work" trong docs. Em main S27 SOLO miss external dependency check (Qdrant Web UI separate repo from binary). Anh pqhuy catch via UAT browser escalate.
**Audit cumulative S20-S26 memory log entries claiming "Investigator spawn" - re-verify retroactive:** Per pitfall confirm spawn `investigator` agent type NOT FOUND trong session S27, nghĩa registry chưa bao giờ load trong session này. Nhưng memory log S23 t1 K0 + S25 t1 audit Bug 1+2 + S26 Plan AG audit 5Q + Plan AI RAG research **đã ghi "spawn Investigator" với token cost 28-40K**. Possibility: (a) Previous sessions registry load thành công (Claude Code version hơn / file format khác / hot-reload sau /agents UI invoke), hoặc (b) Em main misattribute - actually spawn `general-purpose` agent default. Cần audit history Claude Code logs để xác minh - ngoài scope S27. **For now: trust prior memory entries but FLAG uncertainty cho future investigation.**
- **2026-05-22 (Curate session em main):** Archived 10 verbose Recent activity entries S21 S24 Plan AA `archive/2026-05-q1.md`. KEEP: S25 Bug audit + wrap, S26 Plan AG + Plan AI RAG, 2026-05-11 setup. Patterns proven section + Active workflow schemas section preserved. Memory size before: 34.9 KB after: target ~20-22 KB.
- **2026-05-19 (S25 t1 spawn audit 2 bug critical UAT Changelog logging missing):** Em main report 2 bug UAT: (1) Budget Adjust 2×click "Lịch sử thay đổi" KHÔNG show 2 entry. (2) Return Assignee mode F1 click "Lịch sử thay đổi" KHÔNG show return action. Audit 5Q: Q1-DB state Prod SSH fail (auth), fallback code inspect. Q2-Budget handler: `PurchaseEvaluationFeatures.cs:379-387` **ĐÃ log changelog** EntityType=Header + Action=Update + summary diff **code ĐÚNG, likelihood FE filter bug or UAT DB stale**. Q3-Return handler: `PurchaseEvaluationWorkflowService.cs:215-378 ApplyReturnModeAsync` **ZERO changelog log** 4 mode branches (OneLevel/OneStep/Assignee/Drafter) mutate pointers only, return summary, caller `TransitionAsync:100 LogTransitionAsync` only logs phase transition KHÔNG log mode side-effect. Q4-Schema: `PurchaseEvaluationChangelog.cs` support EntityType=Workflow(5) enum but **ZERO code populate** schema incomplete missing Kind/ChangeType subtype enum to disambiguate "budget adjust" vs "detail edit" vs "return mode" (all Header/Workflow entity cùng Action.Update). Q5-FE: Query handler `ListPurchaseEvaluationChangelogsQueryHandler:1050-1064` **KHÔNG filter logic** returns ALL entities. FE query component unknown (fe-user source not easily searchable) but **likely filter EntityType == (Supplier|Detail|Quote)** omit Header+Workflow Bug 1 Budget (Header type) hidden, Bug 2 Return (Workflow type unlogged vậy). Root cause B1: FE filter skip Header updates OR Schema gap (Header.Update collision: budget vs section 2 edit). Root cause B2: Handler **intentionally skip logging** (no companion audit table like PurchaseEvaluationApprovals for return history). Fix path B1: Option A (add Kind enum 30 LOC schema+handlers) vs B (extend EntityType 20 LOC) vs C (FE filter conditional show Budget 10 LOC FE). Fix path B2: Add `db.PurchaseEvaluationChangelogs.Add()` per mode (15-25 LOC 1 file). Cross-ref pattern memory: S23 t3 lookup bug (Level.Order collision per-NV Mig 29) schema Mig 29 OR-of-N refactor created similar subtype ambiguity. Surprise: `EntityType.Workflow=5` enum value design-only, unused 4+ mig history. Recommendation: B2 fix first (clear win, 1 file BE), then B1 design (schema + cross-module pattern audit Contracts). Token ~28k.
- **2026-05-19 (S25 wrap Plan AB Bug 1+2 audit + 6 follow-up plans em main solo):** Pre-Plan AB audit ~28K confirm root cause: Bug 1 Budget Adjust Handler ĐÃ log Changelog (Header+Update) nhưng FE HistoryTab filter strict TraLai-only loại. Bug 2 ApplyReturnModeAsync 4 mode KHÔNG add Changelog.Add() chỉ caller LogTransitionAsync log phase transition. Recommended fix path: BE add log return mode + FE filter relax + Decision badge differentiation. **Subsequent em main solo follow-ups** (Plan AC..AF, KHÔNG re-spawn Investigator): AC capture pre-call Step/Level state + skipToFinal comment enrich + FE Decision badge; AC2 FE merge synthetic Reject rows từ Changelog (reversible recovery KHÔNG DB touch); AD drop misleading dual-phase badges + parse semantic next-target hint via regex `Chuyển phase X → Y` + comment keyword "Trả về"/"vượt cấp"; AE BE preventive batch UserName fix 9 Changelog.Add sites (Drafter+Detail+Quote+Adjust); AF FE userMap fallback từ embedded domain data PeDetailBundle (drafter+approvals+approvalFlow+levelOpinions+departmentOpinions) no extra API fetch. **Patterns reusable Contract V2 audit recovery + Budget changelog UI:** (a) synthetic recovery FE merge từ existing audit table, (b) userMap fallback từ embedded domain data, (c) drop misleading badges + semantic hint via regex parsing, (d) preventive batch fix systemic gap. Cross-ref `feedback_service_hook_vs_endpoint` (audit log derived state) + new pattern user-level: FE merge synthetic + userMap fallback. **Tokens cumulative em main this session ~340K (mostly solo iterations, sub-agent leverage ~62K total).** **CICD result:** 7 commits `e23f51c..506cada` push remote, runs #215 FAIL (Plan M tests SQLite tie-break re-emerge) #216 PASS (Chunk A2 SQLite tie-break fix) #217-#221 PASS streak. Gotcha #48 SQLite tie-break pending docs.
- **2026-05-21 (S26 spawn Plan AG 5Q audit PE List tree view feedback Tra Sol):** Bro UAT 2026-05-21 screenshot phàn nàn UI Duyệt NCC flat list "đám rừng" + propose Outlook-style folder template. Em main spawn Investigator audit 5Q: Q1 prod data scale (SSH auth fail, fallback code inspect ~50-200 projects × 5-15 PE typical). Q2 Project entity `Master/Project.cs:5-14` extends AuditableEntity với Code+Name+dates+Budget+PM+Note, **MISSING MaxLength + navigation tới PE** ready for Phase 2 ProjectPackage table mở rộng. Q3 PE List structure `PurchaseEvaluationsListPage.tsx:133` 3-panel grid `lg:grid-cols-[340px_1fr_360px]` flat `<ul><li>` line 199-252 + pageSize 50 + filter type/phase/awId/search. Q4 shadcn fe-user component GAP **THIẾU Accordion/Collapsible/Tree/Card/Badge** (chỉ Button/Dialog/Input/Label/Select/Textarea) Phase 1 fallback HTML `<details>/<summary>` native + inline badge `<div>` (verified S24 Plan AA pattern). Q5 Mig 32 prep naming `AddProjectPackageTable` follow cumulative pattern. **Recommend Approach C Hybrid: Phase 1 FE-only group view ~160 LOC × 2 app (1-2 ngày) + Phase 2 ProjectPackage schema (3-5 ngày defer post-UAT). Implementer Case 2 cookie-cutter mirror 2 app ACCEPT.** Token ~30k.
- **2026-05-21 (S26 spawn Plan AI RAG distribution research 4 study cases industry-validated):** Bro plan setup RAG cho 5 dự án cùng máy localhost share infrastructure. Investigator deep research 7Q: Anthropic patterns + community tools + multi-tenant architecture + distribution mechanism + auth + cost + sync. **Findings 4 study cases:** (1) **Cursor** Merkle tree + Turbopuffer + 92% similarity reuse pattern, secret key derived Git commit hash, per-team index sharing cut indexing hoursseconds. (2) **Cline Memory Bank** markdown + JIT retrieval + git-native sync. (3) **Continue.dev Hub** slug-based YAML config sharing centralized. (4) **Sourcegraph Cody** interesting anti-pattern, BỎ embeddings sang Search API + graph IR scale 100K+ repos enterprise (alternative if good search infra sẵn). **Multi-tenant 3 patterns:** Single index (pool, cheap), Per-tenant isolated (silo, expensive), Hybrid base+delta (Cursor pattern). **Distribution combo winner cho 5-dev team:** FastMCP HTTP server VPS Hetzner $15/tháng + git-sync embeddings snapshots committed to private repo. Auth JWT RS256 + role scopes + document-level ACL. Cost reality 5 dev ~$20/month vs cloud Qdrant $50/month. Sync git-based weekly cron (real-time chỉ cần khi team > 10 dev). **Surprise:** Voyage AI **200M tokens/month free tier cover 5 devs** comfortably — coi như chỉ tốn $15 VPS. **Anthropic standards 9/9 matched** + community best practices 6/7 matched (Sourcegraph alternative N/A cho memory-heavy). Plan AI architecture chốt với em main: User-level Global MCP (Pattern C — 1 server localhost serve 5 project) thay vì team VPS pattern (anh single dev 5 dự án). Token ~40k.
- **2026-05-11 (setup):** Investigator agent initialized. Baseline knowledge load complete (44 gotchas + 14 memory entries + 6 skills + 27 mig + 81 test pass cumulative). No investigations performed yet. Awaiting first SendMessage from em main.
---
@ -149,11 +159,5 @@ State machine 5 trạng thái phiếu PE: Nháp / Đã gửi duyệt / **Trả l
- Duplicate entries detected → merge
- Stale > 3 months → remove
Last curate: 2026-05-11 (initial seed)
- **2026-05-19 (S25 t1 spawn audit 2 bug critical UAT — Changelog logging missing):** Em main report 2 bug UAT: (1) Budget Adjust 2×click → "Lịch sử thay đổi" KHÔNG show 2 entry. (2) Return Assignee mode F1 click → "Lịch sử thay đổi" KHÔNG show return action. Audit 5Q: Q1-DB state Prod SSH fail (auth), fallback code inspect. Q2-Budget handler: `PurchaseEvaluationFeatures.cs:379-387` **ĐÃ log changelog** EntityType=Header + Action=Update + summary diff → **code ĐÚNG, likelihood FE filter bug or UAT DB stale**. Q3-Return handler: `PurchaseEvaluationWorkflowService.cs:215-378 ApplyReturnModeAsync` **ZERO changelog log** — 4 mode branches (OneLevel/OneStep/Assignee/Drafter) mutate pointers only, return summary, caller `TransitionAsync:100 LogTransitionAsync` only logs phase transition KHÔNG log mode side-effect. Q4-Schema: `PurchaseEvaluationChangelog.cs` support EntityType=Workflow(5) enum but **ZERO code populate** — schema incomplete missing Kind/ChangeType subtype enum to disambiguate "budget adjust" vs "detail edit" vs "return mode" (all Header/Workflow entity cùng Action.Update). Q5-FE: Query handler `ListPurchaseEvaluationChangelogsQueryHandler:1050-1064` **KHÔNG filter logic** — returns ALL entities. FE query component unknown (fe-user source not easily searchable) but **likely filter EntityType == (Supplier|Detail|Quote)** omit Header+Workflow → Bug 1 Budget (Header type) hidden, Bug 2 Return (Workflow type unlogged vậy). Root cause B1: FE filter skip Header updates OR Schema gap (Header.Update collision: budget vs section 2 edit). Root cause B2: Handler **intentionally skip logging** (no companion audit table like PurchaseEvaluationApprovals for return history). Fix path B1: Option A (add Kind enum — 30 LOC schema+handlers) vs B (extend EntityType — 20 LOC) vs C (FE filter conditional show Budget — 10 LOC FE). Fix path B2: Add `db.PurchaseEvaluationChangelogs.Add()` per mode (15-25 LOC 1 file). Cross-ref pattern memory: S23 t3 lookup bug (Level.Order collision per-NV Mig 29) — schema Mig 29 OR-of-N refactor created similar subtype ambiguity. Surprise: `EntityType.Workflow=5` enum value design-only, unused 4+ mig history. Recommendation: B2 fix first (clear win, 1 file BE), then B1 design (schema + cross-module pattern audit Contracts). Token ~28k.
- **2026-05-19 (S25 wrap — Plan AB Bug 1+2 audit + 6 follow-up plans em main solo):** Pre-Plan AB audit ~28K confirm root cause: Bug 1 Budget Adjust Handler ĐÃ log Changelog (Header+Update) nhưng FE HistoryTab filter strict TraLai-only loại. Bug 2 ApplyReturnModeAsync 4 mode KHÔNG add Changelog.Add() — chỉ caller LogTransitionAsync log phase transition. Recommended fix path: BE add log return mode + FE filter relax + Decision badge differentiation. **Subsequent em main solo follow-ups** (Plan AC..AF, KHÔNG re-spawn Investigator): AC capture pre-call Step/Level state + skipToFinal comment enrich + FE Decision badge; AC2 FE merge synthetic Reject rows từ Changelog (reversible recovery KHÔNG DB touch); AD drop misleading dual-phase badges + parse semantic next-target hint via regex `Chuyển phase X → Y` + comment keyword "Trả về"/"vượt cấp"; AE BE preventive batch UserName fix 9 Changelog.Add sites (Drafter+Detail+Quote+Adjust); AF FE userMap fallback từ embedded domain data PeDetailBundle (drafter+approvals+approvalFlow+levelOpinions+departmentOpinions) — no extra API fetch. **Patterns reusable Contract V2 audit recovery + Budget changelog UI:** (a) synthetic recovery FE merge từ existing audit table, (b) userMap fallback từ embedded domain data, (c) drop misleading badges + semantic hint via regex parsing, (d) preventive batch fix systemic gap. Cross-ref `feedback_service_hook_vs_endpoint` (audit log derived state) + new pattern user-level: FE merge synthetic + userMap fallback. **Tokens cumulative em main this session ~340K (mostly solo iterations, sub-agent leverage ~62K total).** **CICD result:** 7 commits `e23f51c..506cada` push remote, runs #215 FAIL (Plan M tests SQLite tie-break re-emerge) → #216 PASS (Chunk A2 SQLite tie-break fix) → #217-#221 PASS streak. Gotcha #48 SQLite tie-break pending docs.
- **2026-05-21 (S26 spawn Plan AG 5Q audit — PE List tree view feedback Tra Sol):** Bro UAT 2026-05-21 screenshot phàn nàn UI Duyệt NCC flat list "đám rừng" + propose Outlook-style folder template. Em main spawn Investigator audit 5Q: Q1 prod data scale (SSH auth fail, fallback code inspect — ~50-200 projects × 5-15 PE typical). Q2 Project entity `Master/Project.cs:5-14` extends AuditableEntity với Code+Name+dates+Budget+PM+Note, **MISSING MaxLength + navigation tới PE** → ready for Phase 2 ProjectPackage table mở rộng. Q3 PE List structure `PurchaseEvaluationsListPage.tsx:133` 3-panel grid `lg:grid-cols-[340px_1fr_360px]` flat `<ul><li>` line 199-252 + pageSize 50 + filter type/phase/awId/search. Q4 shadcn fe-user component GAP **THIẾU Accordion/Collapsible/Tree/Card/Badge** (chỉ Button/Dialog/Input/Label/Select/Textarea) → Phase 1 fallback HTML `<details>/<summary>` native + inline badge `<div>` (verified S24 Plan AA pattern). Q5 Mig 32 prep naming `AddProjectPackageTable` follow cumulative pattern. **Recommend Approach C Hybrid: Phase 1 FE-only group view ~160 LOC × 2 app (1-2 ngày) + Phase 2 ProjectPackage schema (3-5 ngày defer post-UAT). Implementer Case 2 cookie-cutter mirror 2 app ACCEPT.** Token ~30k.
- **2026-05-21 (S26 spawn Plan AI RAG distribution research — 4 study cases industry-validated):** Bro plan setup RAG cho 5 dự án cùng máy localhost share infrastructure. Investigator deep research 7Q: Anthropic patterns + community tools + multi-tenant architecture + distribution mechanism + auth + cost + sync. **Findings 4 study cases:** (1) **Cursor** — Merkle tree + Turbopuffer + 92% similarity reuse pattern, secret key derived Git commit hash, per-team index sharing cut indexing hours→seconds. (2) **Cline Memory Bank** — markdown + JIT retrieval + git-native sync. (3) **Continue.dev Hub** — slug-based YAML config sharing centralized. (4) **Sourcegraph Cody** — interesting anti-pattern, BỎ embeddings sang Search API + graph IR scale 100K+ repos enterprise (alternative if good search infra sẵn). **Multi-tenant 3 patterns:** Single index (pool, cheap), Per-tenant isolated (silo, expensive), Hybrid base+delta (Cursor pattern). **Distribution combo winner cho 5-dev team:** FastMCP HTTP server VPS Hetzner $15/tháng + git-sync embeddings snapshots committed to private repo. Auth JWT RS256 + role scopes + document-level ACL. Cost reality 5 dev ~$20/month vs cloud Qdrant $50/month. Sync git-based weekly cron (real-time chỉ cần khi team > 10 dev). **Surprise:** Voyage AI **200M tokens/month free tier cover 5 devs** comfortably — coi như chỉ tốn $15 VPS. **Anthropic standards 9/9 matched** + community best practices 6/7 matched (Sourcegraph alternative N/A cho memory-heavy). Plan AI architecture chốt với em main: User-level Global MCP (Pattern C — 1 server localhost serve 5 project) thay vì team VPS pattern (anh single dev 5 dự án). Token ~40k.
**Last curate: 2026-05-22 em main full curate** — archived 10 verbose entries (S21 → S24 Plan AA) → `archive/2026-05-q1.md`. KEEP: S25 audit + wrap (Bug 1+2 Changelog), S26 Plan AG + Plan AI RAG (2 spawn), setup baseline. Patterns + Active workflow schemas foundation preserved. Per `feedback_md_compact_narrative.md` §6.5 — archive preserves full verbose entries cho cross-session audit retrieve. Next curate trigger: > 25KB OR Plan B Contract V2 wire complete.

View File

@ -0,0 +1,63 @@
# Investigator Agent — Archive Recent Activity Q1 2026-05 (S21-S24)
> **Archived:** 2026-05-22 by em main SOLUTION_ERP curate session.
> **Scope:** Recent activity FIFO entries S21 → S24 Plan AA (10 verbose entries) — moved from MEMORY.md để giữ slim < 25KB threshold.
> **Rule §6.5 compliance:** KHÔNG cắt narrative — full verbose entries preserved cho cross-session audit.
> **Source MEMORY.md before archive:** 34.9 KB.
> **KEEP in MEMORY:** S26 Plan AG + Plan AI RAG (2 spawn), S25 Bug audit + wrap, 2026-05-11 setup baseline. Patterns proven section preserved.
---
## Archive entries (FIFO chronological — earliest first)
### 2026-05-11 (setup)
Investigator agent initialized. Baseline knowledge load complete (44 gotchas + 14 memory entries + 6 skills + 27 mig + 81 test pass cumulative). No investigations performed yet. Awaiting first SendMessage from em main.
### 2026-05-13 (S21 t3-t5, no spawn)
Em main solo 3 turns (bug fix gotcha #45 + F1+F2+F3 workflow-level Mig 28 + refactor per-NV Mig 29). Implementer REFUSE per cross-stack reasoning chain rule. Investigator KHÔNG spawn — em main đã có context cumulative S20 t12 setup + active dev throughout. No findings to flush. Cumulative state update: 84 test, 29 mig, 45 gotcha, 19 memory entries (+2 S21 t5 pending), 6 skills unchanged. Pattern reusable saved cho future spawn: per-NV permission scope split + EF migration ADD→BACKFILL→DROP reorder.
### 2026-05-13 (S22, no spawn — em main solo throughout)
S22 18:00→~21:00 em main solo. Cumulative state: 30 mig (+1 Mig 30 `AddAllowApproverEditBudgetToLevels` F4 per-Level slot), 104 test PASS (+20: 5 reg #44 Authorize policy + 7 ReturnMode + 7 Guard + 1 V2 actor scope reject), ~146 endpoints (+3: PATCH /users/{id}/allow-skip-final + PATCH /pe/{id}/budget-adjust + GET /pe/{id}/attachments/{attId}/view), 46 gotcha unchanged, 19 memory unchanged (recommend +1 entry — see below). Prod active users 13→33 (+20 role-based: act.nv/pp/tp, bod.1/2, equ/fin/hra/pm/qs.nv/pp/tp).
**Discoveries S22:**
1. **Per-NV admin opt-in flag pattern reinforced 2×** — Mig 30 F4 cùng pattern Mig 29 F1+F3 (S21 t5). Bro corrected em main lần đầu: "phải tick checkbox như Section 2", default = admin opt-in per slot, KHÔNG = mở rộng default. Cross-ref memory `feedback_per_nv_permission_scope.md` proven 2×.
2. **Plan F drop V1 ABORTED** — pre-flight sqlcmd reveal Contract entity HOÀN TOÀN V1 chưa wire V2 (chưa có ApprovalWorkflowId column) + 4 PE V1-only + 19 PE V1+V2 mix. Lesson: drop migration cần verify entity scope toàn bộ (Contract liên đới — không chỉ PE).
3. **Identity password policy ≥12 chars** — seed 20 user FAIL 400 với "User@123456" (11 chars), `TestUser@2026` (13 chars) pass.
4. **Identity rename atomic 4 fields** confirm gotcha #38: Email + NormalizedEmail + UserName + NormalizedUserName + FullName; sqlcmd cần `SET QUOTED_IDENTIFIER ON` cho filtered unique index.
5. **API login response field name `accessToken` + `refreshToken` + `user`** — KHÔNG có field `token` (correct prior Bash example trong spec dùng `.token` sẽ fail).
6. **PS 5.1 ASCII-only script discipline** reinforced gotcha #30: `seed-test-users-prod.ps1` viết Vietnamese names without diacritics tránh parser error. Recommend bro add 1 memory entry "Admin opt-in flag pattern proven 2×" cumulative Mig 29 + Mig 30.
### 2026-05-14 (S23 t1 spawn K0 — Plan K F2 refactor pre-flight)
Audit F2 state cho Plan K Mig 31 (move `Users.AllowDrafterSkipToFinal``ApprovalWorkflowLevels.AllowApproverSkipToFinal` + change semantic Drafter Nháp → Approver ChoDuyet skip thẳng Cấp cuối). **Confirmed state Mig 30:** Migrations path = `Persistence/Migrations/` (not direct `Migrations`); 30 mig latest = `20260513160703_AddAllowApproverEditBudgetToLevels`; `User.cs:38` AllowDrafterSkipToFinal prop; `ApprovalWorkflow.cs:86-105` 6 Allow* props (4 ReturnMode + EditDetails + EditBudget) per Level slot; F2 Drafter branch ở `PurchaseEvaluationWorkflowService.cs:119-161` trong SUBMIT branch (line 125 `if (skipToFinal && evaluation.ApprovalWorkflowId is Guid skipAwId)` check user.AllowDrafterSkipToFinal); APPROVE STEP branch ở `~line 393-525` (advance pointer). TransitionAsync signature: `skipToFinal` là param thứ 8 (position 47:47), default=false. `TransitionPurchaseEvaluationCommand``PurchaseEvaluationFeatures.cs:393-402` với param `SkipToFinal=false` default. `ApprovalWorkflowOptionsDto``PurchaseEvaluationDtos.cs:86-92` (6 field). `PurchaseEvaluationDetailBundleDto.DrafterAllowSkipToFinal` ở line 217 + `CurrentLevelOptions` ở line 214. UsersController `PATCH /users/{id}/allow-skip-final` ở line 91-98 + `SetAllowDrafterSkipToFinalBody` ở line 105. `SetUserAllowDrafterSkipToFinalCommand``UserFeatures.cs:332`. **FE state:** fe-admin Designer `system/ApprovalWorkflowsV2Page.tsx` slot label "NV #{ei + 1}" ở line 873 (KHÔNG phải "#NV {order}" theo prompt) — inline checkbox panel 5+1=6 checkbox ở line 853-933 (4 ReturnMode + EditDetails + EditBudget). fe-admin `system/UsersPage.tsx` "Skip cuối" column line 306-318 + FastForward button toggle line 365-372 + allowSkipMut hook line 181-186. fe-admin/fe-user PeDetailTabs Drafter Workspace checkbox "Gửi thẳng Cấp cuối (skip trung gian)" ở line 287-297 (admin) / 294-304 (user). **GAP fe-user**: KHÔNG có UsersPage + ApprovalWorkflowsV2Page (admin-only mgmt) → Plan K UI changes localized fe-admin chỉ; fe-user side chỉ touch PeDetailTabs (remove old Drafter checkbox + thêm Approver toggle near Duyệt button). **Drift Dev DB**: Total=2 user (admin + test.drafter), Flagged=0 — NOT match 33-user prod seed. **Prod actual**: Total=33 / Flagged=4 (NOT 2 per S22+2 spec). 4 user flagged sẽ lose value when DROP column — acceptable per new semantic (Drafter pre-submit moot).
### 2026-05-15 (S23 t2 spawn L2 — F3+F4 edit menu Duyệt audit)
Bug UAT prod 409a967: admin tick F3+F4 cho slot Approver, login actor user, vào menu "Duyệt" (`?pendingMe=1` cùng `PurchaseEvaluationsListPage` 3-panel), click PE Phase=ChoDuyet → Section 2 Hạng mục/NCC/Báo giá + Section 5 Điều chỉnh ngân sách vẫn read-only. **Root cause F3 = OK / F4 = BROKEN at readOnly short-circuit:** F3 wire ĐÚNG (`PeDetailTabs.tsx:118 itemsReadOnly = readOnly && !approverEditMode` override readOnly per Mig 28 S21 t4 → ItemsTab read prop OK). F4 wire SAI (`PeDetailTabs.tsx:245` passes `readOnly={readOnly}` (=true từ menu Duyệt) xuống BudgetAdjustSection, line 973 compute `canAdjust = !readOnly && (isAdmin || (isDrafter && isDrafterPhase) || isApproverChoDuyet)``!readOnly` short-circuits to false BEFORE F4 isApproverChoDuyet evaluate. Edit pencil button hidden line 1030 `{canAdjust && <Button>Điều chỉnh</Button>}`. Asymmetric vs F3 pattern Section 2 (PeDetailTabs.tsx:113-118). **Evidence:** FE F3 OK `fe-user/src/components/pe/PeDetailTabs.tsx:113-118` + `:224 ItemsTab ev={evaluation} readOnly={itemsReadOnly}`; FE F4 BUG `:957-973 BudgetAdjustSection !readOnly gate` + `:245 readOnly={readOnly}`; menu Duyệt route `fe-user/src/pages/pe/PurchaseEvaluationsListPage.tsx:256-261 PeDetailTabs readOnly={true}` (cùng route Danh sách); BE GetPe `PurchaseEvaluationFeatures.cs:735-770 currentLevelOptions populate 7 fields` OK; BE budget-adjust handler `:281-329` Phase ChoDuyet F4 branch + actor match + flag check ĐẦY ĐỦ + return ConflictException nếu Allow=false; BE PurchaseEvaluationDraftGuard.EnsureEditableForDetailsAsync ở `PurchaseEvaluationDetailFeatures.cs:42` (8 callsites Detail+Supplier handlers). **Recommendation:** Fix `BudgetAdjustSection` line 973 mirror approverEditMode pattern Section 2: thay `canAdjust = !readOnly && (isAdmin || ...)` thành `canAdjust = isAdmin || (!readOnly && isDrafter && isDrafterPhase) || isApproverChoDuyet` — Approver bypass readOnly khi F4 conditions met. **LOC ~3-5 LOC 1 file.** Surprise: Inbox `/inbox` route (InboxPage.tsx) navigate sang `/purchase-evaluations/:id` (mobile DetailPage route, default readOnly=false) — chỉ Danh sách `?pendingMe=1` desktop 3-panel mới hard readOnly=true.
### 2026-05-15 (S23 t2 spawn M0 — Plan M F1+F2+F3+F4 ChoDuyet semantic audit)
Bro UAT post-Plan L deploy 2026-05-15 ~02:00: "Hiện logic cũ là khi trả lại 1 cấp hoặc chỉ định hoặc edit là trạng thái draft -> cái này thay đổi lại nhé, các tính năng duyệt thẳng, trả lại 1 cấp hoặc người chỉ định hoặc cho edit thì cho xử lý đc ở trạng thái đang gửi duyệt luôn." Audit 4 BE file + 4 FE file × 2 app cho F1+F2+F3+F4 phase semantic ChoDuyet preservation. **Verdict: CODE ĐÃ ĐÚNG semantic mới — đây là DISCONNECT bro mental model vs code reality post-Mig 28/29/30/31.** Evidence per feature: **F1.OneLevel** `PurchaseEvaluationWorkflowService.cs:285-312` SWITCH branch lùi 1 Cấp cùng Step (`curLevel-1`) ELSE lùi Step trước Cấp cuối; **Phase KHÔNG đổi** (giữ ChoDuyet, line 364 reset SLA only); fallback Drafter `:303-310` CHỈ KHI đang Bước 1 Cấp 1 no further back (clear pointer + Phase=TraLai). **F1.Assignee** `:335-360` foreach Steps find ApproverUserId match → set pointer; Phase giữ ChoDuyet; ConflictException nếu không tìm thấy NV trong workflow. **F1.OneStep** `:314-333` lùi prev Step Cấp max; Phase giữ ChoDuyet; fallback Drafter nếu đang Bước 1. **F1.Drafter** `:268-275` SET `Phase=TraLai=98` + clear cả 2 pointer + SLA null — đây là CASE DUY NHẤT về "draft". **F2 skipToFinal** `:483-524` Plan K L1 ĐÃ FIX advance pointer tới Bước cuối Cấp cuối (max), **Phase giữ ChoDuyet** (NV cuối duyệt thật để DaDuyet); guard line 485 ConflictException non-admin + flag off; opinion UPSERT trước line 441-468 ensure actor's signature lưu trước. **F3 EnsureEditableForDetailsAsync** `PurchaseEvaluationDetailFeatures.cs:42-99` 2 trường hợp accepted: Drafter scope (DangSoanThao/TraLai return pe sớm line 49-51) **OR Approver scope ChoDuyet line 54-94** (V2 schema + actor match level.ApproverUserId + level.AllowApproverEditDetails flag). **F4 AdjustPurchaseEvaluationBudgetCommandHandler** `PurchaseEvaluationFeatures.cs:272-329` Drafter scope `:283-290` (DangSoanThao/TraLai + isDrafter) ELSE Approver scope ChoDuyet `:291-323` (V2 + pointer init + actor match + level.AllowApproverEditBudget flag) — Phase ChoDuyet đã handle full. **Validation Allow* flag location** `PurchaseEvaluationWorkflowService.cs:252-265` ApplyReturnModeAsync gate per slot per mode: throw ConflictException nếu disabled (Admin bypass line 252). **FE PeWorkflowPanel.tsx**: TraLai dialog `:331-422` 4 radio button (OneLevel/OneStep/Assignee/Drafter) gated bằng `levelOptions?.allowReturnXxx` flag per current Approver Cấp (line 343-396); useEffect `:60-68` S23 t2 fix default first available mode KHÔNG Drafter khi admin tick F1 modes. Skip Final checkbox `:425-442` chỉ visible khi `levelOptions?.allowApproverSkipToFinal` + Approve forward direction (line 425). **FE PeDetailTabs.tsx F3+F4 wire**: `itemsReadOnly = readOnly && !approverEditMode` line 118 bypass readOnly khi F3 enabled (Mig 28 pattern); `canAdjust = isAdmin || (!readOnly && isDrafter && isDrafterPhase) || isApproverChoDuyet` line 977 bypass readOnly khi F4 enabled (L2 fix). Mirror fe-admin/fe-user line 109-115 + 967-979 ĐỒNG BỘ rule §3.9. **Surprise**: "Trả lại" trong UI memory docs đôi khi gọi "draft" colloquial — bro confuse 2 khái niệm `Phase=TraLai=98` (Drafter sửa rồi gửi LẠI từ Step 0) vs `Phase=DangSoanThao=1` (chưa từng gửi duyệt). KHÔNG code path nào trong F1 OneLevel/OneStep/Assignee/F2/F3/F4 set targetPhase=DangSoanThao mà không nên. **Recommendation: LOW effort** (0-20 LOC): chỉ cần communication clarification em main confirm với bro 4 mode F1 + F2 + F3 + F4 đã giữ Phase=ChoDuyet trừ F1.Drafter; mở DB SELECT phiếu UAT confirm `Phase` column number sau click test; OPTIONAL touch up FE label nếu user thấy "Đã gửi duyệt" nhầm "Bản nháp". KHÔNG cần Service refactor hay handler change. Nếu bro thấy phiếu cụ thể về Phase=1 SAU click F1.OneLevel hoặc F2 → spawn lại Investigator audit DB state + log Changelog cho phiếu đó cụ thể (data debug, not code bug).
### 2026-05-15 (S23 t3 spawn — UAT bug Allow* flags không hiện cho actor non-row1)
Bro UAT login `nv.test@solutions.com.vn` vào menu eoffice "Duyệt NCC → Duyệt" phiếu PE/2026/A/026 (Phase=ChoDuyet, WF=QT-DN-V2-001 v12, ở Bước 2 Cấp 1 4 NV: Trần Xuân Lưu/NV Test UAT V2/Hồ Thị Nữ Nguyên/Lê Văn Bính). Admin ĐÃ tick 7 Allow*=TRUE riêng cho slot NV Test UAT V2. Nhưng FE dialog Duyệt KHÔNG hiện checkbox SkipToFinal + Trả lại 4 mode + Edit. **Verdict: HYPOTHESIS B — BE handler picks wrong slot row.** Evidence: (1) `PurchaseEvaluationFeatures.cs:765` `var curLevel = curStep?.Levels.FirstOrDefault(l => l.Order == curLevelOrder);` — match FIRST row có `Level.Order == curLevelOrder`, NOT actor's row. Post-Mig 29 refactor, Level.Order trùng nhau cho mọi NV cùng Cấp (4 row có `Order=1` ở Step 2). EF returns Trần Xuân Lưu trước (PK order) → `FirstOrDefault` lấy row đó (all-false except Drafter=true). (2) API curl admin token + nv.test token return CÙNG `currentLevelOptions={ allowReturnOneLevel=false, OneStep=false, Assignee=false, Drafter=true, EditDetails=false, EditBudget=false, SkipToFinal=false }` — handler currentUser-agnostic confirm. (3) Workflow detail GET `/approval-workflows-v2?applicableType=1` `.types[0].active.steps[1].levels` enumerate 4 slot Bước 2 Cấp 1: NV Test (UAT V2) = ALL 7 TRUE; 3 NV còn lại = ALL FALSE (trừ Drafter=true mặc định). Admin Designer wire ĐÚNG (`fe-admin/src/pages/system/ApprovalWorkflowsV2Page.tsx:889-946` 7 checkbox per-slot). FE consumer wire ĐÚNG (`fe-user/src/components/pe/PeWorkflowPanel.tsx:51 levelOptions = evaluation.currentLevelOptions` + line 343/357/371/397 conditional render mode picker + line 425 SkipToFinal checkbox). **Root cause:** BE line 765 lookup semantic broken sau Mig 29 (S21 t5). Trước Mig 29, 1 Level row per Cấp + `ApproverUsers` join table → `FirstOrDefault(Order==X)` đúng. Sau Mig 29 split 1 Level row PER ApproverUser → `Order` field collide → cần match thêm `ApproverUserId == currentUser.UserId`. **Fix BE 1 dòng:** `var curLevel = curStep?.Levels.FirstOrDefault(l => l.Order == curLevelOrder && l.ApproverUserId == currentUser.UserId);` (admin bypass: fallback `?? curStep?.Levels.FirstOrDefault(l => l.Order == curLevelOrder)` để admin xem detail không lỗi). Edge case: Phase=DaDuyet/TraLai/TuChoi pointer null → existing guard line 760-762 skip block OK. **LOC ~2-3 LOC 1 file.** Surprise: bug PRESENT từ deploy Mig 29 (S21 t5 2026-05-13) — không phải regression S23. Lý do trước đây không bắt: UAT test users (default 13 cũ) đa số là row đầu của slot Cấp (Admin tick toàn FALSE → behavior giống nhau, không lộ); chỉ bộc lộ khi admin tick CHỌN LỌC per-NV (UAT V2 S22+2 thêm 20 user role-based + bro tick chỉ NV Test UAT V2). Cross-reference memory `feedback_per_nv_permission_scope.md` cumulative S21 t5 → S22+5 → S23 t1 — KHÔNG có entry nào nhắc bug lookup BE post-refactor.
### 2026-05-15 (S23 t6 spawn Plan P FE wire audit — confirm BE-only scope)
Em main hypothesize Plan P scope BE Controller body record drop. Investigator audit FE × 2 confirm: `PeWorkflowPanel.tsx:113-124` `api.post(/transitions, body)` SEND ĐÚNG 7 fields (TargetPhase + Decision + Comment + ReturnMode + ReturnTargetUserId + SkipToFinal). No service file (untyped object literal). BE `PurchaseEvaluationsController.cs:267` `TransitionPeBody` record CHỈ 3 fields → ASP.NET silent DROP 3 missing fields. Verdict: Plan P BE Controller ONLY ~6 LOC + no test (Mig 28/31 Domain test cover handler). Saved em main blind fix cross-stack.
### 2026-05-15 (S23 t8 spawn Plan R pre-flight cleanup audit)
Bro chốt cleanup destructive prod. 4 sqlcmd queries audit: 35 PE total (28 active + 7 soft) + 17 V2 (15 IsUserSelectable=false + 2 ghim) + 4 V1 (2 active + 2 inactive). FK gotcha catch: PE.ApprovalWorkflowId Restrict + ApprovalWorkflow extends `BaseEntity` NO soft-delete → hard-DELETE required; LevelOpinion → ApprovalWorkflowLevel Restrict cascade block. SQL Express limit: NO COMPRESSION + RESTORE VERIFYONLY require sysadmin. Filtered indexes (Mig 29+) require `SET QUOTED_IDENTIFIER ON`. Cascade child estimate: 446 PE children + ~140 V2 + ~37 V1 = ~620 rows. 3 Option compare → bro chốt A (Hard-DELETE PE + V2 unghim + V1 inactive, GIỮ V2 ghim + V1 active). Plan F precedent: KHÔNG drop V1 active (PE pin → BE crash).
### 2026-05-15 (S24 t1 spawn Pre-A — Plan AA User Workflow Matrix view + sidebar widen)
5Q audit. Q1 endpoint: `ApprovalWorkflowsV2Controller.cs:16-19` đã class-level `[Authorize]` bare từ S18 2026-05-08 (gotcha #44 fixed permanent), per-method admin Workflows.Create. Handler `GetAwAdminOverviewQuery` KHÔNG có IsUserSelectable filter — cần ADD param + Where conditional. Q2 menu seed: `DbInitializer.cs:1429-1437` peOrder global increment (Group=1, leaves 2/3/4 cycle per type). Permission seed `line 1541-1547` cho 7 role (Drafter/DeptManager/Procurement/CostControl/ProjectManager/Director/AuthorizedSigner). Accounting NOT trong list — admin manual grant nếu cần. Q3 Admin Designer: `ApprovalWorkflowsV2Page.tsx` 975 lines, AwLevelDto 13 fields (7 Allow*), VI labels line 892-948 ("Trả về 1 Cấp trước"/"Trả về 1 Bước trước"/"Trả về Người chỉ định"/"Trả về Drafter (mặc định)"/"Cho phép chỉnh sửa Section 2 (Hạng mục/NCC/Báo giá) lúc đang duyệt"/"Cho phép chỉnh sửa Section ngân sách lúc đang duyệt"/"Cho phép duyệt thẳng Cấp cuối khi đang duyệt"). KHÔNG có usePermission/PermissionGuard wrap (class-level [Authorize] route guard sufficient). Q4 sidebar widen: fe-user `Layout.tsx:325` + fe-admin `Layout.tsx:218` `w-60 xl:w-72` (240/288px). PE Workspace 2-panel `[260px_1fr] xl:[320px_1fr]` (NOT 3-panel như memory `feedback_responsive_laptop_breakpoint` stale claim). After widen `w-72 xl:w-80` (288/320px) SAFE (sidebar 288 + main 992 → workspace 260+732 fit @ 1280px). `w-80 xl:w-96` THRESHOLD RISKY (sát 700px remaining). Q5 ApplicableType enum `ApprovalWorkflow.cs:45-50` {DuyetNcc=1, DuyetNccPhuongAn=2, Contract=3}. Surprises: (1) memory responsive breakpoint stale "3-panel" → cần update sau Plan AA. (2) Order strategy "Luồng duyệt" Order=2 first → shift existing leaves +1 → cần DbInitializer UPDATE Order existing (KHÔNG chỉ INSERT-if-not-exists). (3) Contract=3 chưa wire FE (chỉ DuyetNcc + DuyetNccPhuongAn). Recommendation: Proceed Plan AA — chỉ ADD param filter + 1 menu key + page mới + sidebar widen. Token cost ~32k.
### 2026-05-15 (S24 t1-t4 post-spawn, em main solo 4 polish chunks — Plan AA wrap)
7 commits total `a1a910f..ee0902a`: BE+Layout (`ee776d5`) + FE Page (`c667802`) + Docs (`ac2c859`) + 4 polish iter UAT (`da218f1` px-2 + `4d60598` v1 panel-per-NV + `fbbd361` v2 table rowSpan + `ee0902a` sidebar label wrap). **Pattern reusable**: `inline-block icon + inline text + absolute ChevronDown` cho hanging-indent reverse wrap (label dài về đầu hàng). Mirror admin Designer style cho user view read-only. **Sidebar widen tradeoff**: `w-72 xl:w-80` + remove `truncate` FAIL fit 44+ chars label custom Mig 27 — cần combine với `text-[12px] + leading-snug` + restructure flex → block + inline. Sole `truncate` removal không đủ — phải full layout restructure NavLink. **Memory drift confirmed**: `feedback_responsive_laptop_breakpoint.md` claim "PE Workspace 3-panel" → S24 verify ACTUAL **2-panel** (260+1fr only, KHÔNG có panel thứ 3). Cross-ref update needed memory user-level (admin trigger curate sau). **Plan AA color palette success**: STEP_PALETTE 5 màu (blue/purple/emerald/amber/pink) + LEVEL_PALETTE 5 màu (violet/sky/teal/orange/rose) — **Tailwind JIT yêu cầu full class strings array, KHÔNG dynamic interpolation** (`bg-${color}-100` FAIL — class purged). Reusable cross-project cho menu hierarchy color coding (step parent + level child distinct palette). Final layout v2 table rowSpan: Step column merge per-Phòng + Level column rowSpan per-Cấp + NV column 1-row-per-approver, đẹp hơn v1 panel-per-NV stacked. Token cost iteration negligible (em main solo, no spawn).

View File

@ -38,6 +38,16 @@ Adversarial pre-commit reviewer for SOLUTION_ERP. Read-only verification + live
- Verify: grep diff mock markers + live curl POST/PUT/DELETE expect 2XX
- Severity: CRITICAL — block commit
### Cross-module security validation mirror (NEW S29 — Smart Friend 4× cumulative)
- Symptom: khi mirror entity/Command/Handler cross-module (PE → Contract → Budget V2), em main solo focus data shape (DTO field, FK relation, projection) MISS security validation guards
- Pattern: ApplicableType type guard cho V2 workflow pin entity — `aw.ApplicableType == ExpectedType` validate ON Create command BEFORE entity instantiation. Mirror PE pattern `PurchaseEvaluationFeatures.cs:62-77`: load workflow, assert ApplicableType=Contract(3) / PE(1) / Budget(2), throw `ConflictException` on mismatch
- Attack vector example (S29 Plan B): Drafter forge POST `/api/contracts` với `approvalWorkflowId` của PE/Budget V2 workflow → FK Restrict allows (only checks Id existence, NOT ApplicableType) → Contract pins wrong-scope workflow → semantic policy violation
- Password policy mirror (S29 Plan CA Hotfix D2): Identity ≥12 chars enforced — new seed user CreateAsync fail nếu reuse legacy 11-char `User@123456`. Verify per-user inline conditional override (e.g. `"CatalogMgr@2026"` 15 chars)
- Verify: grep `CreateXCommand` handler — expect `aw.ApplicableType == ApprovalWorkflowApplicableType.X` check. Also re-verify `IsActive=true` + `IsUserSelectable=true` server-side (FE filters but BE trusts blindly = lower-risk gap)
- Applicable cross-module forward: Contract V2 (S29 fixed), Budget V2 (future), Notification V2 (future), any new V2 workflow pin entity
- Severity: MAJOR — block push pre-commit gate
### Gotcha #17 — EF migration 3-file rule
- Symptom: commit migration nhưng thiếu `.Designer.cs` hoặc `ApplicationDbContextModelSnapshot.cs` → next migration fail
@ -144,18 +154,17 @@ Flag commit nếu thấy `<PackageReference Include="MediatR" Version="14...` ho
## 📅 Recent activity (last 10 FIFO)
- **2026-05-22 (S29 wrap — Plan CA Reviewer 2 spawn MAJOR password fix + Plan B Reviewer 2 spawn MAJOR ApplicableType fix — Smart Friend 4× cumulative):** Em main wrap S29 sau 2 big plans Plan CA (Move Cấu hình danh mục admin→eoffice, 7 commits) + Plan B (Contract V2 wire mirror PE Mig 22-26, 11 commits). **Plan CA pre-commit verify** spawn 1× (agentId a4dbdb0fb7e210694) + re-verify 1× (a2009a0ed75b40dad) ~165K cumulative — 4 chunks PASS post Chunk D2 hotfix. **MAJOR catch**: `DemoUserPassword = "User@123456"` 11 chars (existing 30 demo seed pre-S22+2 Identity policy ≥12 chars) → new catalog.manager seed CreateAsync FAIL prod. Fix: per-user inline conditional override `"CatalogMgr@2026"` 15 chars passes policy. Lesson: Identity password policy enforcement gap khi reuse legacy seed pattern. **Plan B pre-push verify** spawn 1× (agentId ace4799f663224b71) + re-verify 1× (a2f8f815522544b73) ~190K cumulative — 9 commits FAIL 1 MAJOR. **MAJOR catch**: `CreateContractCommand` thiếu validation `aw.ApplicableType == ApprovalWorkflowApplicableType.Contract(3)` — attacker forge POST với PE/Budget V2 workflow ID → FK Restrict allows only Id existence check NOT ApplicableType → Contract pin sai workflow scope semantic violation. Mirror PE pattern `PurchaseEvaluationFeatures.cs:62-77` exact. Hotfix Reviewer commit `3e92584` apply ~10-12 LOC ApplicableType guard → re-verify PASS proceed push. **Smart Friend pattern proven 4× cumulative**: (1) S22 #44 silent 403 class-level Authorize, (2) S25 #48 SQLite frozen clock tie-break, (3) S29 Plan CA Hotfix D2 password policy, (4) S29 Plan B ApplicableType cross-module. **Patterns proven NEW S29 Reviewer perspective**: Cross-module security validation mirror (PE → Contract → Budget V2) — khi mirror entity/Command, MUST mirror validation guards (ApplicableType + FK check + idempotent + password policy). Easy miss vì em main solo focus on data shape NOT security. **Cat 3 Security checklist reinforced**: ApplicableType type guard cho V2 workflow pin pattern + password ≥12 chars enforcement + IsActive/IsUserSelectable server-side re-validate. **Anti-patterns observed S29**: (a) Em main miss ApplicableType validation Plan B Chunk E1 CreateContractCommand → Reviewer catch. (b) Em main miss password ≥12 chars Plan CA Chunk D → Reviewer catch. (c) Pattern: em main solo flow tends to miss security guard cross-module (focus shape > security). **Recommendation forward**: Reviewer spawn pre-commit MANDATORY cho cross-module mirror diff (PE→Contract, PE→Budget V2 future, identity policy change). Em main solo OK cho UI polish iteration (S26 Plan AG2-AG6 pattern proven). Smart Friend guard active S30+ cho next cross-module wire (Budget V2 likely).
- **2026-05-22 (S29 Plan B Contract V2 wire pre-push spawn — FAIL 1 major):** Adversarial verify 9 commits `58898e8..14feb69` Plan B Contract V2 wire (~8,900 LOC = BE 326 + FE 219 + Mig Designer 7,970 + Mig SQL 327). Spawn ~17K. **Verdict FAIL — 1 MAJOR Cat 3 security/data integrity, 0 critical, 3 minor.** Wire claim PASS (all 9 chunks deliver — ApproveV2Async 150+ LOC mirror PE pattern, UPSERT ContractLevelOpinion, DTO populate, FE Select dropdown, Section 5 dynamic render). Schema PASS (2 mig 3-file rule complete, FK Restrict Contract→AW + Cascade Contract→LevelOpinion + Restrict LevelOpinion→Level, UNIQUE composite). Code quality PASS (dotnet build 0 err 2 pre-existing DocxRenderer warn, npm × 2 PASS 0 TS, mirror §3.9 SHA256 IDENTICAL × 3 files: ContractDetailContent.tsx + ContractCreatePage.tsx + types/contracts.ts). Test PASS 111/111 baseline preserved. Authority PASS explicit mandate. **MAJOR FOUND**: `CreateContractCommandHandler` (`ContractFeatures.cs:38-100`) accepts `ApprovalWorkflowId` from request body but DOES NOT validate `aw.ApplicableType == ApprovalWorkflowApplicableType.Contract`. PE pattern at `PurchaseEvaluationFeatures.cs:62-77` explicitly validates `aw.ApplicableType == expectedType` and throws `ConflictException`. Plan B Chunk E1 omits this guard. **Attack vector**: Drafter posts `approvalWorkflowId` of PE/Budget V2 workflow → FK Restrict allows (only checks Id existence not ApplicableType) → Contract pins wrong-scope workflow → semantic policy violation. **Acceptance criteria**: Add validation block in handler mirror PE lines 64-77 — load aw, assert ApplicableType=Contract(3), throw ConflictException on mismatch. Recommend also re-verify IsActive + IsUserSelectable server-side (FE filters but BE trusts blindly — lower risk). **Adversarial 10/10 PASS**: V1 path UNCHANGED (only additions before line 91), race B+A2 clean, B2 UPSERT scope OK post-Chunk C, Mig 32 Seed idempotent guard, E1 backward compat null default, E2 N+1 avoided via dict, E3 V1 hide Section 5 conditional, E3 adminProxy GUID comparison TS-correct, no menu visibility drift, test gate 111/111 confirmed. **Recommendation**: HOLD push. Add ~10-12 LOC ApplicableType validation guard in CreateContractCommandHandler.Handle before entity instantiation. Re-run build+test. Then PROCEED push 9 commits + 1 fix commit (10th). **Smart Friend guard active — caught major security gap via cross-reference PE pattern** (lesson Cognition: independent adversarial perspective raises quality vs em main solo). **Test gap noted defer**: ApproveV2Async ~150 LOC + UPSERT 0 unit test — gotcha #48 lesson recurring risk — recommend Plan B Wrap test-after bundle covering V2 happy path advance + OR-of-N + skipToFinal F2 + terminal gen mã + V1 regression.
- **2026-05-21 (S26 Plan AG pre-commit + 5 follow-up plans AG2-AG6 em main solo self-review):** Plan AG Chunk A+B+C pre-commit verify spawn 1× ~25K with 5-category checklist + 12 adversarial deep checks (A-L) PASS 0 critical/major/minor. Commit `0bf6c7e` 2 file +346/-116 LOC mirror IDENTICAL hash `21001E90...`. Wire claim verify: 3 chunk delivered (useMemo group nested + `<details>/<summary>` 2-level + localStorage Set persist). Schema 0 mig, 0 BE, 0 entity. Security: localStorage non-sensitive (projectId GUID + normalizedGoiThau text), XSS safe React auto-escape, no new [Authorize] needed (read-only view). Code quality: npm build × 2 PASS 0 TS err, anti-fiddle 0% drift, Mirror §3.9 byte-identical 21,521 bytes. Test coverage: Phase 9 UAT exception accept. Adversarial 12/12 PASS — edge case empty tenGoiThau/projectName/localStorage corrupt/Tailwind named groups/HTML details accessibility/degenerate cases/vi locale sort/filter-then-group order/bundle size delta. **Minor noted defer:** Selected PE inside collapsed tree không auto-expand path → recommend Plan AG2 useEffect watch selectedId. Recommendation: PASS proceed push. **Subsequent Plan AG2-AG6 em main solo self-review** (5 plan UI polish iteration UAT feedback bro Tra Sol — Panel 1 widen 400px, drop tầng gói thầu 1-level, drop single-PE flat consistent, add Drafter+Department BE+FE, 3-level Project>Năm>NCC>PE, compact card 3-row). Em main verify mỗi commit: SHA256 hash 2 file IDENTICAL + npm build × 2 app + dotnet test 111/111 PASS (Plan AG4 BE+FE cross-stack — dotnet build clean + 3 projection update LIST/INBOX/APPROVED). KHÔNG re-spawn Reviewer mỗi plan (mirror S24 Plan AA pattern: ROI thấp khi UI polish ~50-100 LOC per chunk + cost spawn ~25K × 5 = ~125K vô lý vs em main self-verify build pass + bro visual confirm). **Pattern reinforced**: Reviewer spawn 1 lần cho heavy cross-stack initial Chunk A+B+C (~370 LOC + 4 sub-agent collab), em main solo cho polish iteration. Cumulative S26 6 commits `0bf6c7e..d99069a` push remote: AG (`0bf6c7e`) + AG2 (`c5429c0`) + AG3 (`fbad4a9`) + AG4 (`2bf0118`) + AG5 (`083b601`) + AG6 (`d99069a`). 0 prod regression observed, test baseline 111 preserved. Smart Friend guard still active for next session feature spawn.
- **2026-05-19 (S25 wrap — Plan AB pre-commit verify + 6 follow-up plans em main solo self-review + 1 lesson catched by CICD):** Plan AB Chunk A pre-commit verify spawn 1× ~22K with 5-category checklist + 8 adversarial deep checks PASS 0 blocker (1 minor V1 legacy fallback acceptable). Recommended PROCEED push cdfd542. **MISSED gotcha #48**: Multi-Changelog.Add() trong same SaveChangesAsync transaction → SQLite test frozen clock CreatedAt tie-break non-deterministic → Plan M existing tests `.OrderByDescending(CreatedAt).FirstAsync()` picked wrong entry → CI Run #215 FAIL. **Lesson reinforced**: UAT mode `feedback_uat_skip_verify` skip `dotnet test` per chunk risk recurring khi BE refactor > 100 LOC + signature change. Em main resumed local `dotnet test` post Plan AB Chunk A2 fix — caught by CICD test gate (no prod impact). **Em main solo self-review** Plan AC-AF (5 plans em main solo, Reviewer KHÔNG re-spawn — em main verify build+test+npm × 2 app mỗi chunk + CICD post-deploy verify thay vai pre-commit). Cumulative S25: 0 prod regression, 6 commits PASS CICD. **Pattern caught: SQLite frozen clock multi-row tie-break** — tests querying audit table cần discriminator beyond timestamp (EntityType + Summary keyword). Cross-ref future Contract V2 test setup. **Smart Friend guard still active** for future spawn — Reviewer should ADD test filter discriminator check vào Category 5 checklist post-S25.
- **2026-05-19 (S25 Plan AB Chunk A pre-push verify, spawn):** Adversarial verify commit `cdfd542` Plan AB Chunk A — fix Changelog visibility 2 bug PE ID `3248f2f9` (Budget Adjust không hiện history + Trả lại Người chỉ định không log). Diff scope: 3 files +146/-95 LOC (BE PurchaseEvaluationWorkflowService.cs Service-only refactor + 2 FE PeDetailTabs.tsx mirror §3.9). **Verdict: PASS — 0 critical / 0 major / 1 minor observation — proceed push OK.** Wire BE: line 226-236 V1 legacy fallback return early UNCHANGED (giữ S17 behavior, KHÔNG log new Changelog — V1 phiếu read-only history per Mig 22-24 spec, acceptable). V2 ApplyReturnModeAsync refactor Drafter early returnif/else common path: Drafter mode line 285-292 set Phase=TraLai + clear pointer + SLA=null SEMANTIC IDENTICAL pre-refactor (only difference: removed `return` so fallthrough log block). OneLevel/OneStep/Assignee unchanged logic line 302-378 + SLA=AddDays(7) reset line 381. New `db.PurchaseEvaluationChangelogs.Add()` line 403-412 EntityType=Workflow(5) + Action=Update(2) + PhaseAtChange=evaluation.Phase (TraLai cho Drafter, ChoDuyet cho 3 mode khác) + UserId=actorUserId + UserName resolved via userManager.FindByIdAsync (mirror LogTransitionAsync line 700-704 pattern) + Summary `"Trả lại ({modeName}): {summary}"`. KHÔNG SaveChangesAsync mới — TransitionAsync caller line 101 downstream save persists. Wire FE 2 file `HistoryTab` filter extend 3 rule: ADD `summary.includes('Trả lại')` Workflow entity + ADD `entityType === Header(1) && summary.toLowerCase().includes('ngân sách')` cover Budget Adjust + empty placeholder text update Vietnamese chuẩn "Chưa có lịch sử trả lại / điều chỉnh ngân sách / gửi duyệt lại." Schema integrity: 0 migration mới (verify `git diff --name-only | grep Migrations/` empty); reuse PurchaseEvaluationChangelog Mig 12 + S21 t4 Mig 28 schema; PurchaseEvaluationEntityType enum line 25-33 + ChangelogAction enum ContractChangelog.cs:38-44 match BE values FE constant. Security: actorUserId param đã có từ caller TransitionAsync — non-admin user log đúng UserId không escalate permission; EF Core parameterized SQL default; FE substring filter trên BE-trusted Summary string không XSS risk. Code quality: dotnet build PASS 0 err 2 warn (DocxRenderer pre-existing); npm build fe-user PASS 394ms 0 TS err; npm build fe-admin PASS 421ms 0 TS err; anti-fiddle scope drift 0%; mirror 2 FE app §3.9 verify exact identical 17 lines (admin line 2033-2049 mirror user 2027-2043, chỉ shift 6 LOC do prior component lines khác). Test coverage: Phase 9 UAT exception ACCEPT per memory `feedback_uat_skip_verify` — test-after bundle defer (Bug fix Service refactor ideally test-before nhưng UAT mode skip OK). Cumulative state unchanged: 111 test baseline preserve. Adversarial deep checks all PASS: (1) **Drafter mode refactor regression** — line 285-292 set Phase=TraLai + clear pointer + SLA=null PRESERVED (S17 backward compat). V1 legacy 226-236 fallback return early no semantic change. (2) **Drafter summary redundancy**`$"Trả lại (Người soạn thảo): Trả về Người soạn thảo"` redundancy acceptable (audit clarity > terseness; consistent format 4 mode). (3) **actorName resolve N+1** — single `userManager.FindByIdAsync` per ApplyReturnModeAsync invocation, mirror exact LogTransitionAsync line 700-704 pattern; KHÔNG N+1 (single call per transition). (4) **FE filter substring case**`.includes('Trả lại')` exact match BE format `$"Trả lại ({modeName}): ..."` — case-sensitive Vietnamese diacritics lowercase 'l' match. (5) **FE placeholder Vietnamese** — "Chưa có lịch sử trả lại / điều chỉnh ngân sách / gửi duyệt lại." chuẩn Vietnamese diacritics. (6) **Comment update 5 bullet rule** — clear narrative, KHÔNG noise, Plan AB S25 marker rõ. (7) **Gotcha #45 regression** — Plan AB Service-only refactor KHÔNG đụng PE button TraLai click logic (gotcha #45 đã fix S21 t3 BE guard `decision != Reject throw line 64-72` defense), no regression. (8) **Anti-fiddle scope** — 3 file đúng spec, KHÔNG out-of-scope (no agent-memory commit, no docs/changelog touch). **Minor observation (non-block, design choice):** V1 legacy phiếu (line 226-236 fallback) return early TRƯỚC khi reach new Changelog.Add() block → V1 phiếu KHÔNG có new detailed "Trả lại (X): ..." log. FE filter `phaseAtChange === TraLai(98)` line 2042 vẫn catch V1 generic LogTransitionAsync log (V1 fallback set Phase=TraLai) → V1 history vẫn hiện (1 generic row "Chuyển phase X → TraLai" thay vì detailed mode breakdown). Acceptable per Mig 22-24 spec V1 = read-only history, không cần backfill audit detail. Recommendation: PASS proceed push remote. Smart Friend guard active.
- **2026-05-15 (S24 Plan AA post-wrap cumulative finalize, no re-spawn):** Post-Chunk C learning append — em main iterate **4 polish chunks back-to-back** (`da218f1` px-2 hotfix + `4d60598` redesign v1 panel-per-NV color + `fbbd361` redesign v2 table layout rowSpan + `ee0902a` wrap fix sidebar label) UAT visual feedback bro, cumulative ~1.5h, KHÔNG Reviewer spawn. **Anti-pattern observed S24 polish chunks**: UI/UX iteration thuần CSS/layout KHÔNG cần Reviewer spawn mỗi chunk — cost overhead too high (~25K spawn × 4 = ~100K cumulative). Pattern reusable: chỉ self-verify build pass + bro visual confirm post-deploy là đủ. **Smart Friend guard validation S24**: Plan AA cumulative ~620 LOC qua 7 commits (`a1a910f..ee0902a`) — Reviewer spawn 1 lần (Chunk C cumulative A+B BE+FE wire) cost ~25K = ROI tốt. Multi-spawn polish chunks 4× sẽ ~100K = ROI thấp. **Pattern saved: BUNDLE cumulative verify cho heavy chunk (wire BE / migration / cross-stack), SKIP cho < 30 min polish (CSS / layout / color)**. **Discovery #3 anomaly re-tested S24**: Plan AA Run #210 sha=ac2c859 push range mixed BE+FE+docs → CICD trigger normal 4/4 wire end-to-end + bundle hash 2 app rotate. KHÔNG reinforce docs-only anomaly hypothesis (S22 #47 gap pending separate). Defer Investigator follow-up. **Low note S24**: BE filter `IsUserSelectable=false` non-admin leak workflow chưa ghim — defer follow-up (severity Low, workflow data non-sensitive, NOTE only — Smart Friend guard didn't escalate to block commit). CICD Monitor Run #210 sha=ac2c859 PASS verify 4/4 wire end-to-end + bundle hash 2 app rotate. Cumulative state unchanged S24: 31 mig, 111 test, 47 gotcha.
- **2026-05-15 (S24 Plan AA cumulative pre-commit verify, spawn):** Adversarial verify Chunk A (BE + Layout, em main solo) + Chunk B (FE WorkflowMatrixViewPage, Implementer Case 2 PASS). Diff scope 8 file modified + 2 file CREATE — ~76 LOC BE + ~270 LOC FE, scope khớp Plan AA. **Verdict: PASS with 0 critical / 0 major / 0 minor issues — proceed commit OK.** Wire BE: Controller line 21-26 `Overview(applicableType, isUserSelectable, ct) → new GetAwAdminOverviewQuery(applicableType, isUserSelectable)` end-to-end. Handler line 114-117 `request.IsUserSelectable is bool ius → query.Where(d => d.IsUserSelectable == ius)` correct conditional. FE WorkflowMatrixViewPage line 29-33 actual `api.get<AwAdminOverviewDto>('/approval-workflows-v2', { params: { applicableType: typeInt, isUserSelectable: true } })` — real call no mock/alert. Route `/purchase-evaluations/workflow-matrix?type=N` wired App.tsx:38 + Layout.tsx resolvePath:81-86. Schema integrity: 0 migration mới (column `IsUserSelectable` from Mig 25), DbInitializer INSERT-OR-UPDATE-Order idempotent verified (MenuItemConfiguration HasKey(Key) → PK unique, ToDictionary safe; existing rows UPDATE Order only nếu mismatch; new rows INSERT). 7 Allow* flag count match BE DTO ↔ FE type. ApplicableType enum {1,2,3} — FE chỉ wire 2 type PE matrix view correct. Security: class-level `[Authorize]` bare line 18 PRESERVED (gotcha #44 protect). GET any authenticated → non-admin Drafter OK access. Non-admin pass `isUserSelectable=false` BE không block → workflow chưa ghim leak risk — Low severity (workflow data non-sensitive, NOTE only). Permission seed 7 role × WfView leaf via `SeedPurchaseEvaluationPermissionDefaultsAsync:1573` added — idempotent. Admin role qua `SeedAdminPermissionsAsync` chỉ cover `MenuKeys.All[]` static root keys (Pe_*_WfView dynamic NOT in All) — but admin uses fe-admin Designer, KHÔNG cần WfView access fe-user. Code quality: BE dotnet build PASS 0 err 2 warn (DocxRenderer pre-existing); FE fe-user npm build claim PASS (Implementer Case 2); FE fe-admin npm build VERIFY tự chạy PASS 1926 modules / 740ms / 0 TS err. Anti-fiddle 0% drift, scope khớp spec. Mirror 2 FE §3.9: Layout.tsx widen `w-72 xl:w-80` cả 2 ✓; remove truncate 3 site fe-user (MenuGroup+MenuLeaf+StaticLeaf) + 2 site fe-admin (MenuGroup+MenuLeaf, no StaticLeaf component) — structural asymmetry acceptable (fe-user only có "Hộp thư" StaticLeaf); WorkflowMatrixViewPage fe-user only correct (admin có Designer riêng). Test coverage: Phase 9 UAT exception accept — test-after default OK. Adversarial deep checks: (1) DbInitializer ToDictionary duplicate key risk → PK constraint trên Key, tree.Add chỉ thêm Pe_DuyetNcc_WfView + Pe_DuyetNccPhuongAn_WfView (2 typeCode distinct) → no in-memory dup. (2) Sidebar widen 1280px responsive verified per task spec (288 sidebar + 992 main fit). (3) Plan U revert clean — 0 remaining truncate class outside Plan AA comments. (4) Mig drift schema: column existed Mig 25, no new mig needed. 2 SaveChangesAsync non-atomic giữa menu seed + label backfill (pre-existing, OUT scope Plan AA). Comment quality: Plan AA marker rõ ràng 8+ sites, no TODO/FIXME zombie. TypeScript: `FlagCell` indexed-access type clever từ Pick 7 keys union — TS happy compile clean. Recommendations defer: (a) enforce admin/non-admin policy filter (current GET cho phép user pass isUserSelectable=false — low risk, audit follow-up); (b) MenuKeys.All[] không cover dynamic Pe_*_WfView nên Admin role không auto grant — OK vì admin dùng fe-admin Designer, NOTE để future audit khi admin cần inspect user-side matrix view. Cumulative state unchanged: 31 mig, 111 test, 47 gotcha. Smart Friend guard active — KHÔNG lower bar (3 minor risk noted defer follow-up, không block commit).
- **2026-05-15 (S23 t4-t11 cumulative em main self-review — Plan N+O+P+Q+R+S+T+U, no Reviewer spawn):** 8 plan consecutive em main self-review per UAT mode (`feedback_uat_skip_verify` rule — em main verify build + test + npm build mỗi chunk). Key validations cumulative: **Plan N** GetPe handler `PurchaseEvaluationFeatures.cs:765` per-NV `ApproverUserId` discriminator + admin fallback row đầu. **Plan O** 4 lookup sites cascade fix (`EnsureCanRejectV2Async:201` + `ApplyReturnModeAsync:248` + `EnsureEditableForDetailsAsync:72` + `AdjustBudgetCommandHandler:311`) — pattern uniform Plan N, +3 regression test `PurchaseEvaluationPerNvLookupRegressionTests.cs`. **Plan P** Controller `TransitionPeBody:267` record +3 fields mirror `TransitionPurchaseEvaluationCommand` schema — root cause 2 ngày prod bug F1+F2 wire fail. **Plan Q** FE banner mx-5 inset gap fix (CSS layout polish mirror 2 app). **Plan R+S+T5** destructive sqlcmd cleanup prod ~720 rows wiped cumulative + Plan F precedent avoid được (V1 active workflow giữ nguyên → BE healthy startup). **Plan T** DbInitializer `DemoSeed:Disabled` flag config — disable 5 demo seed methods (Workflow V1 + PE V1 + Demo Contracts + Demo PE + Sample V2). **Plan U** FE sidebar truncate + tooltip 2 app mirror — handle long DisplayLabel (Mig 27 admin custom). Anti-patterns observed: (1) **Plan N point 9 chỉ catch 1/5 sites** — em main + Investigator spawn miss 4 sites khác → Plan O cascade fix. Lesson: grep ENUMERATE TẤT CẢ lookup sites cùng pattern (KHÔNG fix theo bug report mỗi lần). (2) **Plan O caveat #1 surfaced Plan P pre-existing bug** — CICD Monitor catch Controller body record drop trong Stage 4c verify wire — pattern "verify cross-stack body↔command count match". (3) **DbInitializer auto re-seed** loop — Plan R+S clean → IIS recycle → re-seed loop. Plan T flag fix root cause. Pattern reusable cross-project: refactor schema 1-row-per-role MUST grep enum lookup sites + Controller body record MUST mirror Command record fields. Cumulative state: 111 test (+7 vs 104 baseline pre-Plan M), 31 mig (no new), 47 gotcha unchanged, `feedback_per_nv_permission_scope.md` reinforced 5 lookup sites enum + 9 wire surface points + Plan P caveat → 10 surface points. Smart Friend guard still active for future spawn.
- **2026-05-15 (S23 t3 Plan M cumulative review, spawn):** Pre-push adversarial verify 3 commits local `c2afef2..508b17a..4dd6f9c` Plan M F1 edge case Bước 1 + Phase=TraLai display rename. Diff cumulative: 26 LOC BE (Service.cs:287-333) + 116 LOC test (1 file, +2 test Fact extend SeedWorkflowAsync helper +2 params optional) + 8 LOC FE (4 file × 2 LOC mirror admin+user). **Verdict: PASS** — wire BE M1 verified hot path 287-333 OneLevel+OneStep edge case replace `Phase=TraLai+clear pointer+return` with `Set pointer (0,1) + summary "không lùi được" → fallthrough SLA reset line 364 → Phase=ChoDuyet preserved from pre-call state (entry guard 75-81 require ChoDuyet để Reject decision)`. ApplyReturnModeAsync caller line 94-100 truyền `evaluation.Phase` (now ChoDuyet) vào LogTransitionAsync → Summary="Chuyển phase ChoDuyet → ChoDuyet" + ContextNote contain "không lùi được" (matches test assert ContextNote.Contain). Drafter mode line 268-275 GIỮ Phase=TraLai semantic (unchanged). Assignee line 335-360 throw nếu không match unchanged. F2 ApproveV2Async + F3 EnsureEditableForDetailsAsync + F4 AdjustBudgetCommand handler — UNCHANGED. Schema integrity: 0 migration mới, Phase enum TraLai=98 còn dùng cho Drafter mode + display label rename only. Security: Admin bypass logic line 252-265 + Reject guard line 81 EnsureCanRejectV2Async + non-Approver block — preserved. Code quality: dotnet build PASS 0 err 2 warn (DocxRenderer pre-existing), dotnet test 106/106 PASS (58 Domain + 48 Infra, +2 từ 104 baseline), npm build admin+user PASS 0 TS err (chỉ chunk size warn pre-existing), no `--no-verify`. Test coverage: 2 Fact mới `ApplyReturnMode_OneLevel_AtStep1Level1_ResetsToBuoc1Cap1_KeepsChoDuyet` + `ApplyReturnMode_OneStep_AtStep1_ResetsToBuoc1Cap1_KeepsChoDuyet` cover OneLevel Step1Lv1 + OneStep Step1Lv2→reset. Assert đủ Phase=ChoDuyet + pointer (0,1) + SLA NotNull + ContextNote "không lùi được". Helper extend +2 params backward compat OK (default false). K7 cascade NO regression — 3 `ApproveV2_SkipToFinal_*` xanh (M1 chỉ đụng F1 ApplyReturnModeAsync, không động F2 ApproveV2Async). Mirror 2 FE app (§3.9): purchaseEvaluation.ts admin+user identical (98:'Cần chỉnh sửa lại' + PeDisplayStatus.TraLai:'Cần chỉnh sửa lại'), PeWorkflowPanel.tsx admin+user identical (2 inline literal "Phase → ..." + "Phiếu sẽ về ..." rename). Anti-fiddle: scope drift 0% — 8 LOC FE đúng spec rename Phase=TraLai display only. Anti-pattern guard: **(1) Stale narrative comment Service.cs:288-289 + 327-328** UPDATED rõ "Plan M S23 t3 — KHÔNG fallback Drafter, phiếu giữ đang duyệt" — không zombie. **(2) Audit log consistency** test+code match "không lùi được" exact. **(3) Backward compat** phiếu UAT prod đang Phase=TraLai do logic cũ — Drafter resume từ TraLai vẫn work line 105-132 (entry point `fromPhase==TraLai → targetPhase==ChoDuyet`). **Minor issues (non-block, recommend fix sau):** **(4) Inconsistent UX literal "Trả lại"** còn ở 8 user-facing locations chưa rename (decision design): PeListPanel.tsx:112 "Bản nháp + Trả lại" filter label, PeWorkflowPanel.tsx:270 button "← Trả lại", 272 tooltip "(Duyệt / Trả lại / Từ chối)", 315 "← Trả lại Drafter sửa", 342 "Chọn cách Trả lại", PeDetailTabs.tsx:152 "chỉ Bản nháp / Trả lại mới sửa", 287 "(trừ khi approver Trả lại)", ApprovalWorkflowsV2Page.tsx:639 "(Trả lại / Edit Section 2 / ...)" — phân biệt **action verb** "Trả lại" (button approver click) vs **phase result label** "Cần chỉnh sửa lại" (Phase=TraLai display in dropdown/badge); spec M3 narrow scope chỉ rename phase result label OK. Em main quyết: giữ action verb hay rename hết. **(5) Inconsistent across module** contracts.ts:29 + budget.ts:20 vẫn `98:'Trả lại'` — KHÔNG trong scope Plan M (PE-only) nhưng inconsistent giữa 3 module nếu Contract/Budget có Phase 98 — bro nếu deploy chung sẽ noticeable. Recommendation: SAFE to push 3 commit Plan M; consider follow-up chunk M4 (optional) rename 5 literal user-facing trong PeListPanel + PeWorkflowPanel + PeDetailTabs để consistent UX semantic mới. Smart Friend guard active.
- **2026-05-14 (S23 t1 Plan K1+K2 cumulative review, spawn):** Pre-K3 adversarial verify Mig 31 schema swap + Service Approver F2 branch. Diff scope: 11 BE files +4093/-83 LOC (Mig Designer 3938 lines dominate). 3 commits chuỗi `eb106f2..56868bf..db66253..364aef6` — pre-A slot label refactor + K1 Domain/Mig/Snapshot/sentinel patches + K2 DTO+Service Approver branch. **Verdict: PASS với 2 Major + 2 Minor issues** — K3 OK proceed nhưng K5 endpoint cleanup nên ưu tiên trước K7 test fix. Wire claim verify: Approver F2 branch placed ĐÚNG vị trí (line 477 AFTER UPSERT opinion line 441-468 + BEFORE advance pointer line 502) — opinion sẽ ghi trước skip terminal, audit log đầy đủ context Bước/Cấp. ApproveV1LegacyAsync skipToFinal guard tại CALLER (line 147-149 trong TransitionAsync branch) thay vì callee — pattern OK, V1 method giữ nguyên signature legacy. Schema sqlcmd verify Dev: 7 Allow* columns ApprovalWorkflowLevels (AllowApproverEditBudget/EditDetails/SkipToFinal/ReturnOneLevel/OneStep/ToAssignee/ToDrafter) + Users.AllowDrafterSkipToFinal dropped (count=0). Snapshot regen clean. EF config HasDefaultValue(false) wire. Mig 31 Up() manual reorder ADD→DROP correct per memory `feedback_ef_migration_backfill_reorder`. **Major issues caught** (Smart Friend guard active — KHÔNG để pass): (1) **Orphan UsersController endpoint zombie** — K1 sentinel commented "K2 sẽ refactor DTO + drop field" nhưng K2 chỉ refactor PE side, KHÔNG động UsersController.SetAllowDrafterSkipToFinal + UserFeatures.SetUserAllowDrafterSkipToFinalCommand + UserDto field. Endpoint PATCH `/api/users/{id}/allow-skip-final` vẫn live nhưng silent NoOp (Task.CompletedTask), admin UI tick → BE swallow → confusion UX. Cần K5 endpoint cleanup chunk (per spec). (2) **Stale Mig 28 comment ApprovalWorkflow.cs:78** — comment cũ "F2 (Drafter skip) đã move sang Users.AllowDrafterSkipToFinal" còn nguyên dù line 107-113 prop AllowApproverSkipToFinal mới. Confuse future dev. **Minor issues:** (3) Comment TransitionPurchaseEvaluationCommand DTO PurchaseEvaluationFeatures.cs:401 "F2 — Drafter skip thẳng Cấp cuối khi trình duyệt" still says Drafter (semantic outdated). (4) ApprovalWorkflowConfiguration.cs:22 stale Mig 28/29 narrative comment chưa note Mig 31. Anti-fiddle audit PASS: K1 18 LOC UserFeatures.cs sentinel patches valid compile-break workaround, K2 4 files within original spec. Anti-pattern reinforced 3×: admin opt-in per slot per-NV (Mig 29 F1+F3 + Mig 30 F4 + Mig 31 F2 — em main lần đầu sai Mig 30 default scope expansion → bro corrected). Production build PASS 0 err 2 warn (DocxRenderer unrelated). Test references K7-pending line 253. No `--no-verify` bypass. Pattern caught: "Transient sentinel pattern" — đặt sentinel + comment commit chunk khác cleanup nhưng chunk đó scope SHIFT → zombie state. Recommend explicit K5 cleanup chunk trước K7 test fix. Cumulative state: 31 mig (+1 Mig 31), 47 gotcha unchanged. Smart Friend guard active.
- **2026-05-13 (S22 18:00→21:00, no spawn):** Em main solo self-review S22 — Reviewer KHÔNG spawn per UAT mode `feedback_uat_skip_verify`. Em main verify build clean + test pass + npm build × 2 app mỗi chunk (11 commits cumulative). Key validations: (1) Plan E phân quyền strict V2 — actor.UserId scope in List + Detail, Inbox đã strict từ S17, loose UAT clause `|| ApprovalWorkflowId != null` removed. (2) S22+1 V2 actor scope guard BE helper `EnsureCanRejectV2Async` chặn request forge non-approver PATCH /transitions decision=Reject (mirror FE button disable) — defense-in-depth FE+BE pattern. (3) S22+4 AdjustBudgetCommand handler 3-tier scope (Drafter Nháp/Trả lại + Approver ChoDuyet + Admin) — S22+5 refactor ChoDuyet branch dùng `level.AllowApproverEditBudget` flag (admin opt-in per slot) thay default Approver scope (security scope correction per bro feedback). (4) S22+2 Identity password policy enforced ≥12 chars (reject `User@123456` 11 chars outdated). Anti-patterns observed: (a) Default scope expansion mistake S22+4 → S22+5 fix — KHÔNG default expand permission scope without admin tick (per-NV opt-in pattern Mig 29 lesson reinforced); (b) History display field assumption BudgetAdjustSection initial — em assume `PeDetailBundle.changelogs` exists, FAIL TS2339 — pattern verify type fields trước render; (c) PS 5.1 Vietnamese diacritics gotcha #30 reinforced — script `seed-test-users-prod.ps1` first attempt FAIL parser error, ASCII-only discipline. S22+4 attachment view endpoint + S22+4 budget-adjust endpoint chưa qua live curl verify (defer post-deploy — bro UAT direct). S22+5 Mig 30 applied LocalDB Dev + Design via `dotnet ef database update`. New gotcha discovered: #47 paths-ignore agent-memory gap (recommend add to docs/gotchas.md — pending bro decide). Cumulative state: 104 test (+20), 30 mig (+1 Mig 30), 46 gotcha unchanged (gotcha #47 pending), ~146 endpoints (+3), 33 active users. Smart Friend guard still active for future spawn.
- **2026-05-13 (S21 t3-t5, no spawn):** Em main solo verify via dotnet build + npm build × 2 app + dotnet test suite mỗi chunk. Reviewer KHÔNG spawn — em main self-review per UAT mode `feedback_uat_skip_verify` (skip dotnet test mỗi chunk, vẫn build verify). Gotcha #45 fix self-test 3 regression test (test-before §7). S21 t3-t5 push cumulative 12 commits — CICD Monitor verify post-deploy thay vai Reviewer (deploy ship + bundle hash + schema verify). Cumulative state: 84 test, 29 mig, 45 gotcha, 19 memory entries. Pattern saved cho future review focus: per-NV permission audit (Level table vs User table flag), EF migration backfill SQL injection between ADD-DROP order. Smart Friend guard still active for future spawn.
- **2026-05-11 (setup):** Reviewer agent initialized. Baseline knowledge load complete (44 gotchas + 5-category checklist + 6 skills cumulative). No reviews performed yet. Awaiting first SendMessage from em main. Smart Friend guard active.
- **2026-05-19 (S25 Plan AB Chunk A pre-push verify, spawn):** Adversarial verify commit `cdfd542` fix Changelog visibility 2 bug PE Budget Adjust không hiện history + Trả lại Người chỉ định không log. 3 files +146/-95 LOC. **Verdict: PASS proceed push, 1 minor V1 legacy fallback acceptable.** V2 ApplyReturnModeAsync refactor Drafter early returnif/else common path + new Changelog.Add EntityType=Workflow(5) Action=Update(2) PhaseAtChange=evaluation.Phase. FE HistoryTab filter extend 3 rule. Schema 0 mig. Adversarial 8 deep check PASS. Lesson narrative archived `archive/2026-05-q1.md`.
- **2026-05-22 (S28 wrap — Layer A governance Reviewer perspective + Cat 6 add):** Reviewer perspective về S28 trajectory (em main solo, KHÔNG actual review work): t1 RAG ROI verdict marginal short-term / transform long-term → t2 em main self-authorize cross-project rule "ghi RAG mọi tương tác" WITHOUT bro consent → t3 monitoring 5 metric đề xuất → t4 bro caught mistake scope-down về SOLUTION_ERP self-discipline → t5 Layer A governance broadcast active (3-Layer distributed, em apply 4-category default + skip list + tag schema mandatory + phase + BC/module enum). **Smart Friend Cat 1 "Wire claim verify" lesson S28**: em main t2 implicit interpret "chú ý X" (bro suggestion) AS "MANDATORY X" (em policy decision) → cross-project rule self-authorize. Pattern catch retroactive: scope creep từ project-local → cross-project KHÔNG bro consent là authority boundary violation. Cần check authority boundary mỗi khi em main đề xuất "rule cross-project" hoặc "mọi tương tác mandatory". **Tag schema mandatory forward S28+**: store lesson/gotcha chunk với `[lesson, phase-<N>, <bc>]` format (phase ∈ {phase-9, phase-9plus, phase-10}, BC enum ∈ {contract, pe, budget, workflow, identity, form, infra}). **Adversarial check NEW Cat 6 — Authority boundary check**: verify em main self-authorize vs bro centralized — distinguish "bro suggested option X" (advisory) vs "bro mandated X" (directive); flag any "MANDATORY ... cross-project" sourced từ em main self-decision. 5-category checklist baseline UNCHANGED (Wire BE + Schema + Security + Code quality + Test), Cat 6 add forward. **Rule cũ ABANDONED**: "RAG ghi mọi tương tác mandatory" S28 t2 over-reach — lesson learned authority boundary: implicit consent ("chú ý" / "có thể") KHÔNG = explicit mandate ("BẮT BUỘC" / "mandatory") — verify scope rõ TRƯỚC commit policy. Smart Friend guard active S28+ cho Plan B Contract V2 wire pre-commit spawn (mandatory heavy diff > 50 LOC cross-stack).
- **2026-05-22 (S27 retrospective):** Smart Friend guard catch retrospective: Anh pqhuy phát hiện Qdrant native dashboard 404 — em main miss pre-push verify `rag-onboarding-guide.md` claim "Qdrant native dashboard work". Lesson: SubAgent Smart Friend guard mandate cho docs claim hyperlinked external services. "Self-review bias - em main test SOLO miss external dependency verification" — reinforced cumulative S29.
---
@ -165,4 +174,4 @@ Flag commit nếu thấy `<PackageReference Include="MediatR" Version="14...` ho
- Duplicate entries detected → merge
- Stale > 3 months → remove
Last curate: 2026-05-11 (initial seed)
**Last curate: 2026-05-22 S29 wrap drop oldest entry** — dropped 2026-05-11 setup baseline entry (stale 11 days, baseline preserved in archive 2026-05-q1.md). KEEP: S29 wrap (Plan CA + Plan B 2 MAJOR catches), S29 Plan B pre-push detail, S26 Plan AG, S25 wrap, S25 Plan AB pre-push, S28 governance, S27 retrospective, S22 curate. 5-category checklist (Cat 3 Security ApplicableType + password ≥12 chars reinforced S29) + Smart Friend guard 4× cumulative + Cross-module security validation mirror pattern (NEW S29 added foundation) preserved. Next curate trigger: > 25KB OR Plan B Wrap test bundle complete OR Budget V2 wire start.

View File

@ -0,0 +1,73 @@
# Reviewer Agent — Archive Recent Activity Q1 2026-05 (S21-S24)
> **Archived:** 2026-05-22 by em main SOLUTION_ERP curate session.
> **Scope:** Recent activity FIFO entries S21 t3 → S24 Plan AA (7 verbose entries) — moved from MEMORY.md để giữ slim < 25KB threshold.
> **Rule §6.5 compliance:** KHÔNG cắt narrative — full verbose entries preserved cho cross-session audit.
> **Source MEMORY.md before archive:** 34.5 KB.
> **KEEP in MEMORY:** S26 Plan AG pre-commit + S25 wrap + S25 Plan AB pre-push (with full critical Adversarial deep check narrative) + 2026-05-11 setup baseline. 5-category checklist + Smart Friend guard foundation preserved.
---
## Archive entries (FIFO chronological — earliest first)
### 2026-05-13 (S21 t3-t5, no spawn)
Em main solo verify via dotnet build + npm build × 2 app + dotnet test suite mỗi chunk. Reviewer KHÔNG spawn — em main self-review per UAT mode `feedback_uat_skip_verify` (skip dotnet test mỗi chunk, vẫn build verify). Gotcha #45 fix self-test 3 regression test (test-before §7). S21 t3-t5 push cumulative 12 commits — CICD Monitor verify post-deploy thay vai Reviewer (deploy ship + bundle hash + schema verify). Cumulative state: 84 test, 29 mig, 45 gotcha, 19 memory entries. Pattern saved cho future review focus: per-NV permission audit (Level table vs User table flag), EF migration backfill SQL injection between ADD-DROP order. Smart Friend guard still active for future spawn.
### 2026-05-13 (S22 18:00→21:00, no spawn)
Em main solo self-review S22 — Reviewer KHÔNG spawn per UAT mode `feedback_uat_skip_verify`. Em main verify build clean + test pass + npm build × 2 app mỗi chunk (11 commits cumulative). Key validations:
1. Plan E phân quyền strict V2 — actor.UserId scope in List + Detail, Inbox đã strict từ S17, loose UAT clause `|| ApprovalWorkflowId != null` removed.
2. S22+1 V2 actor scope guard BE helper `EnsureCanRejectV2Async` chặn request forge non-approver PATCH /transitions decision=Reject (mirror FE button disable) — defense-in-depth FE+BE pattern.
3. S22+4 AdjustBudgetCommand handler 3-tier scope (Drafter Nháp/Trả lại + Approver ChoDuyet + Admin) — S22+5 refactor ChoDuyet branch dùng `level.AllowApproverEditBudget` flag (admin opt-in per slot) thay default Approver scope (security scope correction per bro feedback).
4. S22+2 Identity password policy enforced ≥12 chars (reject `User@123456` 11 chars outdated).
**Anti-patterns observed:** (a) Default scope expansion mistake S22+4 → S22+5 fix — KHÔNG default expand permission scope without admin tick (per-NV opt-in pattern Mig 29 lesson reinforced); (b) History display field assumption BudgetAdjustSection initial — em assume `PeDetailBundle.changelogs` exists, FAIL TS2339 — pattern verify type fields trước render; (c) PS 5.1 Vietnamese diacritics gotcha #30 reinforced — script `seed-test-users-prod.ps1` first attempt FAIL parser error, ASCII-only discipline.
S22+4 attachment view endpoint + S22+4 budget-adjust endpoint chưa qua live curl verify (defer post-deploy — bro UAT direct). S22+5 Mig 30 applied LocalDB Dev + Design via `dotnet ef database update`. New gotcha discovered: #47 paths-ignore agent-memory gap (recommend add to docs/gotchas.md — pending bro decide). Cumulative state: 104 test (+20), 30 mig (+1 Mig 30), 46 gotcha unchanged (gotcha #47 pending), ~146 endpoints (+3), 33 active users. Smart Friend guard still active for future spawn.
### 2026-05-14 (S23 t1 Plan K1+K2 cumulative review, spawn)
Pre-K3 adversarial verify Mig 31 schema swap + Service Approver F2 branch. Diff scope: 11 BE files +4093/-83 LOC (Mig Designer 3938 lines dominate). 3 commits chuỗi `eb106f2..56868bf..db66253..364aef6` — pre-A slot label refactor + K1 Domain/Mig/Snapshot/sentinel patches + K2 DTO+Service Approver branch. **Verdict: PASS với 2 Major + 2 Minor issues** — K3 OK proceed nhưng K5 endpoint cleanup nên ưu tiên trước K7 test fix. Wire claim verify: Approver F2 branch placed ĐÚNG vị trí (line 477 AFTER UPSERT opinion line 441-468 + BEFORE advance pointer line 502) — opinion sẽ ghi trước skip terminal, audit log đầy đủ context Bước/Cấp. ApproveV1LegacyAsync skipToFinal guard tại CALLER (line 147-149 trong TransitionAsync branch) thay vì callee — pattern OK, V1 method giữ nguyên signature legacy. Schema sqlcmd verify Dev: 7 Allow* columns ApprovalWorkflowLevels (AllowApproverEditBudget/EditDetails/SkipToFinal/ReturnOneLevel/OneStep/ToAssignee/ToDrafter) + Users.AllowDrafterSkipToFinal dropped (count=0). Snapshot regen clean. EF config HasDefaultValue(false) wire. Mig 31 Up() manual reorder ADD→DROP correct per memory `feedback_ef_migration_backfill_reorder`.
**Major issues caught (Smart Friend guard active — KHÔNG để pass):**
1. **Orphan UsersController endpoint zombie** — K1 sentinel commented "K2 sẽ refactor DTO + drop field" nhưng K2 chỉ refactor PE side, KHÔNG động UsersController.SetAllowDrafterSkipToFinal + UserFeatures.SetUserAllowDrafterSkipToFinalCommand + UserDto field. Endpoint PATCH `/api/users/{id}/allow-skip-final` vẫn live nhưng silent NoOp (Task.CompletedTask), admin UI tick → BE swallow → confusion UX. Cần K5 endpoint cleanup chunk (per spec).
2. **Stale Mig 28 comment ApprovalWorkflow.cs:78** — comment cũ "F2 (Drafter skip) đã move sang Users.AllowDrafterSkipToFinal" còn nguyên dù line 107-113 prop AllowApproverSkipToFinal mới. Confuse future dev.
**Minor issues:**
3. Comment TransitionPurchaseEvaluationCommand DTO PurchaseEvaluationFeatures.cs:401 "F2 — Drafter skip thẳng Cấp cuối khi trình duyệt" still says Drafter (semantic outdated).
4. ApprovalWorkflowConfiguration.cs:22 stale Mig 28/29 narrative comment chưa note Mig 31.
Anti-fiddle audit PASS: K1 18 LOC UserFeatures.cs sentinel patches valid compile-break workaround, K2 4 files within original spec. Anti-pattern reinforced 3×: admin opt-in per slot per-NV (Mig 29 F1+F3 + Mig 30 F4 + Mig 31 F2 — em main lần đầu sai Mig 30 default scope expansion → bro corrected). Production build PASS 0 err 2 warn (DocxRenderer unrelated). Test references K7-pending line 253. No `--no-verify` bypass. **Pattern caught: "Transient sentinel pattern"** — đặt sentinel + comment commit chunk khác cleanup nhưng chunk đó scope SHIFT → zombie state. Recommend explicit K5 cleanup chunk trước K7 test fix. Cumulative state: 31 mig (+1 Mig 31), 47 gotcha unchanged. Smart Friend guard active.
### 2026-05-15 (S23 t3 Plan M cumulative review, spawn)
Pre-push adversarial verify 3 commits local `c2afef2..508b17a..4dd6f9c` Plan M F1 edge case Bước 1 + Phase=TraLai display rename. Diff cumulative: 26 LOC BE (Service.cs:287-333) + 116 LOC test (1 file, +2 test Fact extend SeedWorkflowAsync helper +2 params optional) + 8 LOC FE (4 file × 2 LOC mirror admin+user). **Verdict: PASS** — wire BE M1 verified hot path 287-333 OneLevel+OneStep edge case replace `Phase=TraLai+clear pointer+return` with `Set pointer (0,1) + summary "không lùi được" → fallthrough SLA reset line 364 → Phase=ChoDuyet preserved from pre-call state (entry guard 75-81 require ChoDuyet để Reject decision)`. ApplyReturnModeAsync caller line 94-100 truyền `evaluation.Phase` (now ChoDuyet) vào LogTransitionAsync → Summary="Chuyển phase ChoDuyet → ChoDuyet" + ContextNote contain "không lùi được" (matches test assert ContextNote.Contain). Drafter mode line 268-275 GIỮ Phase=TraLai semantic (unchanged). Assignee line 335-360 throw nếu không match unchanged. F2 ApproveV2Async + F3 EnsureEditableForDetailsAsync + F4 AdjustBudgetCommand handler — UNCHANGED. Schema integrity: 0 migration mới, Phase enum TraLai=98 còn dùng cho Drafter mode + display label rename only. Security: Admin bypass logic line 252-265 + Reject guard line 81 EnsureCanRejectV2Async + non-Approver block — preserved. Code quality: dotnet build PASS 0 err 2 warn (DocxRenderer pre-existing), dotnet test 106/106 PASS (58 Domain + 48 Infra, +2 từ 104 baseline), npm build admin+user PASS 0 TS err (chỉ chunk size warn pre-existing), no `--no-verify`. Test coverage: 2 Fact mới `ApplyReturnMode_OneLevel_AtStep1Level1_ResetsToBuoc1Cap1_KeepsChoDuyet` + `ApplyReturnMode_OneStep_AtStep1_ResetsToBuoc1Cap1_KeepsChoDuyet` cover OneLevel Step1Lv1 + OneStep Step1Lv2→reset. Assert đủ Phase=ChoDuyet + pointer (0,1) + SLA NotNull + ContextNote "không lùi được". Helper extend +2 params backward compat OK (default false). K7 cascade NO regression — 3 `ApproveV2_SkipToFinal_*` xanh (M1 chỉ đụng F1 ApplyReturnModeAsync, không động F2 ApproveV2Async). Mirror 2 FE app (§3.9): purchaseEvaluation.ts admin+user identical (98:'Cần chỉnh sửa lại' + PeDisplayStatus.TraLai:'Cần chỉnh sửa lại'), PeWorkflowPanel.tsx admin+user identical (2 inline literal "Phase → ..." + "Phiếu sẽ về ..." rename). Anti-fiddle: scope drift 0% — 8 LOC FE đúng spec rename Phase=TraLai display only. Anti-pattern guard:
1. **Stale narrative comment Service.cs:288-289 + 327-328** UPDATED rõ "Plan M S23 t3 — KHÔNG fallback Drafter, phiếu giữ đang duyệt" — không zombie.
2. **Audit log consistency** test+code match "không lùi được" exact.
3. **Backward compat** phiếu UAT prod đang Phase=TraLai do logic cũ — Drafter resume từ TraLai vẫn work line 105-132 (entry point `fromPhase==TraLai → targetPhase==ChoDuyet`).
**Minor issues (non-block, recommend fix sau):**
4. **Inconsistent UX literal "Trả lại"** còn ở 8 user-facing locations chưa rename (decision design): PeListPanel.tsx:112 "Bản nháp + Trả lại" filter label, PeWorkflowPanel.tsx:270 button "← Trả lại", 272 tooltip "(Duyệt / Trả lại / Từ chối)", 315 "← Trả lại Drafter sửa", 342 "Chọn cách Trả lại", PeDetailTabs.tsx:152 "chỉ Bản nháp / Trả lại mới sửa", 287 "(trừ khi approver Trả lại)", ApprovalWorkflowsV2Page.tsx:639 "(Trả lại / Edit Section 2 / ...)" — phân biệt **action verb** "Trả lại" (button approver click) vs **phase result label** "Cần chỉnh sửa lại" (Phase=TraLai display in dropdown/badge); spec M3 narrow scope chỉ rename phase result label OK. Em main quyết: giữ action verb hay rename hết.
5. **Inconsistent across module** contracts.ts:29 + budget.ts:20 vẫn `98:'Trả lại'` — KHÔNG trong scope Plan M (PE-only) nhưng inconsistent giữa 3 module nếu Contract/Budget có Phase 98 — bro nếu deploy chung sẽ noticeable.
Recommendation: SAFE to push 3 commit Plan M; consider follow-up chunk M4 (optional) rename 5 literal user-facing trong PeListPanel + PeWorkflowPanel + PeDetailTabs để consistent UX semantic mới. Smart Friend guard active.
### 2026-05-15 (S23 t4-t11 cumulative em main self-review — Plan N+O+P+Q+R+S+T+U, no Reviewer spawn)
8 plan consecutive em main self-review per UAT mode (`feedback_uat_skip_verify` rule — em main verify build + test + npm build mỗi chunk). Key validations cumulative: **Plan N** GetPe handler `PurchaseEvaluationFeatures.cs:765` per-NV `ApproverUserId` discriminator + admin fallback row đầu. **Plan O** 4 lookup sites cascade fix (`EnsureCanRejectV2Async:201` + `ApplyReturnModeAsync:248` + `EnsureEditableForDetailsAsync:72` + `AdjustBudgetCommandHandler:311`) — pattern uniform Plan N, +3 regression test `PurchaseEvaluationPerNvLookupRegressionTests.cs`. **Plan P** Controller `TransitionPeBody:267` record +3 fields mirror `TransitionPurchaseEvaluationCommand` schema — root cause 2 ngày prod bug F1+F2 wire fail. **Plan Q** FE banner mx-5 inset gap fix (CSS layout polish mirror 2 app). **Plan R+S+T5** destructive sqlcmd cleanup prod ~720 rows wiped cumulative + Plan F precedent avoid được (V1 active workflow giữ nguyên → BE healthy startup). **Plan T** DbInitializer `DemoSeed:Disabled` flag config — disable 5 demo seed methods (Workflow V1 + PE V1 + Demo Contracts + Demo PE + Sample V2). **Plan U** FE sidebar truncate + tooltip 2 app mirror — handle long DisplayLabel (Mig 27 admin custom).
**Anti-patterns observed:**
1. **Plan N point 9 chỉ catch 1/5 sites** — em main + Investigator spawn miss 4 sites khác → Plan O cascade fix. Lesson: grep ENUMERATE TẤT CẢ lookup sites cùng pattern (KHÔNG fix theo bug report mỗi lần).
2. **Plan O caveat #1 surfaced Plan P pre-existing bug** — CICD Monitor catch Controller body record drop trong Stage 4c verify wire — pattern "verify cross-stack body↔command count match".
3. **DbInitializer auto re-seed** loop — Plan R+S clean → IIS recycle → re-seed loop. Plan T flag fix root cause.
Pattern reusable cross-project: refactor schema 1-row-per-role MUST grep enum lookup sites + Controller body record MUST mirror Command record fields. Cumulative state: 111 test (+7 vs 104 baseline pre-Plan M), 31 mig (no new), 47 gotcha unchanged, `feedback_per_nv_permission_scope.md` reinforced 5 lookup sites enum + 9 wire surface points + Plan P caveat → 10 surface points. Smart Friend guard still active for future spawn.
### 2026-05-15 (S24 Plan AA cumulative pre-commit verify, spawn)
Adversarial verify Chunk A (BE + Layout, em main solo) + Chunk B (FE WorkflowMatrixViewPage, Implementer Case 2 PASS). Diff scope 8 file modified + 2 file CREATE — ~76 LOC BE + ~270 LOC FE, scope khớp Plan AA. **Verdict: PASS with 0 critical / 0 major / 0 minor issues — proceed commit OK.** Wire BE: Controller line 21-26 `Overview(applicableType, isUserSelectable, ct) → new GetAwAdminOverviewQuery(applicableType, isUserSelectable)` end-to-end. Handler line 114-117 `request.IsUserSelectable is bool ius → query.Where(d => d.IsUserSelectable == ius)` correct conditional. FE WorkflowMatrixViewPage line 29-33 actual `api.get<AwAdminOverviewDto>('/approval-workflows-v2', { params: { applicableType: typeInt, isUserSelectable: true } })` — real call no mock/alert. Route `/purchase-evaluations/workflow-matrix?type=N` wired App.tsx:38 + Layout.tsx resolvePath:81-86. Schema integrity: 0 migration mới (column `IsUserSelectable` from Mig 25), DbInitializer INSERT-OR-UPDATE-Order idempotent verified (MenuItemConfiguration HasKey(Key) → PK unique, ToDictionary safe; existing rows UPDATE Order only nếu mismatch; new rows INSERT). 7 Allow* flag count match BE DTO ↔ FE type. ApplicableType enum {1,2,3} — FE chỉ wire 2 type PE matrix view correct. Security: class-level `[Authorize]` bare line 18 PRESERVED (gotcha #44 protect). GET any authenticated → non-admin Drafter OK access. Non-admin pass `isUserSelectable=false` BE không block → workflow chưa ghim leak risk — Low severity (workflow data non-sensitive, NOTE only). Permission seed 7 role × WfView leaf via `SeedPurchaseEvaluationPermissionDefaultsAsync:1573` added — idempotent. Admin role qua `SeedAdminPermissionsAsync` chỉ cover `MenuKeys.All[]` static root keys (Pe_*_WfView dynamic NOT in All) — but admin uses fe-admin Designer, KHÔNG cần WfView access fe-user. Code quality: BE dotnet build PASS 0 err 2 warn (DocxRenderer pre-existing); FE fe-user npm build claim PASS (Implementer Case 2); FE fe-admin npm build VERIFY tự chạy PASS 1926 modules / 740ms / 0 TS err. Anti-fiddle 0% drift, scope khớp spec. Mirror 2 FE §3.9: Layout.tsx widen `w-72 xl:w-80` cả 2 ✓; remove truncate 3 site fe-user (MenuGroup+MenuLeaf+StaticLeaf) + 2 site fe-admin (MenuGroup+MenuLeaf, no StaticLeaf component) — structural asymmetry acceptable (fe-user only có "Hộp thư" StaticLeaf); WorkflowMatrixViewPage fe-user only correct (admin có Designer riêng). Test coverage: Phase 9 UAT exception accept — test-after default OK. Adversarial deep checks: (1) DbInitializer ToDictionary duplicate key risk → PK constraint trên Key, tree.Add chỉ thêm Pe_DuyetNcc_WfView + Pe_DuyetNccPhuongAn_WfView (2 typeCode distinct) → no in-memory dup. (2) Sidebar widen 1280px responsive verified per task spec (288 sidebar + 992 main fit). (3) Plan U revert clean — 0 remaining truncate class outside Plan AA comments. (4) Mig drift schema: column existed Mig 25, no new mig needed. 2 SaveChangesAsync non-atomic giữa menu seed + label backfill (pre-existing, OUT scope Plan AA). Comment quality: Plan AA marker rõ ràng 8+ sites, no TODO/FIXME zombie. TypeScript: `FlagCell` indexed-access type clever từ Pick 7 keys union — TS happy compile clean. Recommendations defer: (a) enforce admin/non-admin policy filter (current GET cho phép user pass isUserSelectable=false — low risk, audit follow-up); (b) MenuKeys.All[] không cover dynamic Pe_*_WfView nên Admin role không auto grant — OK vì admin dùng fe-admin Designer, NOTE để future audit khi admin cần inspect user-side matrix view. Cumulative state unchanged: 31 mig, 111 test, 47 gotcha. Smart Friend guard active — KHÔNG lower bar (3 minor risk noted defer follow-up, không block commit).
### 2026-05-15 (S24 Plan AA post-wrap cumulative finalize, no re-spawn)
Post-Chunk C learning append — em main iterate **4 polish chunks back-to-back** (`da218f1` px-2 hotfix + `4d60598` redesign v1 panel-per-NV color + `fbbd361` redesign v2 table layout rowSpan + `ee0902a` wrap fix sidebar label) UAT visual feedback bro, cumulative ~1.5h, KHÔNG Reviewer spawn. **Anti-pattern observed S24 polish chunks**: UI/UX iteration thuần CSS/layout KHÔNG cần Reviewer spawn mỗi chunk — cost overhead too high (~25K spawn × 4 = ~100K cumulative). Pattern reusable: chỉ self-verify build pass + bro visual confirm post-deploy là đủ. **Smart Friend guard validation S24**: Plan AA cumulative ~620 LOC qua 7 commits (`a1a910f..ee0902a`) — Reviewer spawn 1 lần (Chunk C cumulative A+B BE+FE wire) cost ~25K = ROI tốt. Multi-spawn polish chunks 4× sẽ ~100K = ROI thấp. **Pattern saved: BUNDLE cumulative verify cho heavy chunk (wire BE / migration / cross-stack), SKIP cho < 30 min polish (CSS / layout / color)**. **Discovery #3 anomaly re-tested S24**: Plan AA Run #210 sha=ac2c859 push range mixed BE+FE+docs → CICD trigger normal 4/4 wire end-to-end + bundle hash 2 app rotate. KHÔNG reinforce docs-only anomaly hypothesis (S22 #47 gap pending separate). Defer Investigator follow-up. **Low note S24**: BE filter `IsUserSelectable=false` non-admin leak workflow chưa ghim — defer follow-up (severity Low, workflow data non-sensitive, NOTE only — Smart Friend guard didn't escalate to block commit). CICD Monitor Run #210 sha=ac2c859 PASS verify 4/4 wire end-to-end + bundle hash 2 app rotate. Cumulative state unchanged S24: 31 mig, 111 test, 47 gotcha.

File diff suppressed because one or more lines are too long

View File

@ -2,7 +2,9 @@
> **Update rule:** trước khi bắt đầu 1 task → ghi row vào `🔥 In Progress`. Xong → chuyển sang `✅ Recently Done`.
**Last updated:** 2026-05-22 (Session 29 **🎯 Plan CA wire DONE + Hotfix 1 deploy**: 6 commits + 1 hotfix push `3cb54e0..e55d96b` cumulative. Move "Cấu hình danh mục dùng chung" (9 menu: Master + Suppliers + Projects + Departments + Catalogs + 4 sub-catalogs) từ fe-admin → fe-user/eoffice + add role mới `CatalogManager` + demo user `catalog.manager@solutions.com.vn`. **Chunks:** A 80d39a0 BE Role + Seed (em main, 64 LOC) · B 06a441c FE move 4 master pages 948 LOC (Implementer Case 2 cookie-cutter mirror byte-identical SHA256 4 file) · C c995f42 Sidebar filter 2 app (em main, 14 LOC) · D 4a592cf Seed demo user (em main, 7 LOC) · D2 68bcedd Hotfix password ≥12 chars S22+2 policy (em main, Reviewer pre-push CATCH critical) · HF1 e55d96b Add 7 entries `resolvePath` staticMap fe-user (em main, anh UAT screenshot CATCH silent sidebar drop). **2 CI runs PASS:** Run #229 sha=68bcedd 3m32s + Run #230 sha=e55d96b 3m24s. Bundle hash 2× rotate: fe-admin `BvcWrq2z→leEMWFLU`, fe-user `Bg2FNeIz→DVBLmZlt→Dgn1iU9E`. **Multi-agent ROI Plan CA:** 🟦 Investigator 1 spawn ~15K (terrain map 9 menu + GOTCHA tree-inherit) + 🟨 Implementer Case 2 1 spawn ~10K (byte-identical mirror 4 page) + 🟥 Reviewer 2 spawn ~165K (CRITICAL catch password policy) + 🟩 CICD Monitor 2 spawn fail 529 transient (em main fallback manual smoke) + 👤 Chủ trì Solo Chunk A/C/D/D2/HF1 ~100K. **NEW gotcha #50** "Page move cross-app — `Layout.tsx` `resolvePath` staticMap missed mirror → silent sidebar drop" + Pattern reusable cross-project: **4-place mirror checklist** khi Implementer Case 2 cookie-cutter copy page (page + Routes + menuKeys.ts + Layout.tsx staticMap — Implementer Chunk B missed point 4). Implementer MEMORY Pattern 16-bis NEW. **State chốt S29 Plan CA:** 31 mig (no schema change) · 59 tables · ~146 endpoints · **36 FE pages (+4: fe-user master/{Suppliers,Projects,Departments,Catalogs}Page)** · **111 test pass unchanged** (UAT skip per chunk + cookie-cutter mirror) · **50 gotcha (+1 #50)** · 25 memory user-level · 6 skills · 4 sub-agents · **13 AppRoles (+1 CatalogManager)** · **34 active prod users (+1 catalog.manager@solutions.com.vn)**. Catalog manager login `CatalogMgr@2026` (15 chars policy compliant). 7 commits remote `3cb54e0..e55d96b`.)
**Last updated:** 2026-05-22 (Session 29 **FINAL wrap****🎯 Plan CA + Plan B Contract V2 wire ALL DEPLOYED PROD**: cumulative **20 commits push 4 CI Runs PASS** (#229 + #230 + #231 + #232). **2 big plans done end-to-end:** Plan CA Move "Cấu hình danh mục dùng chung" admin → eoffice (7 commits + Hotfix 1) + Plan B Contract V2 wire mirror PE Mig 22-26 (9 chunks + Hotfix Reviewer + Hotfix CICD). **Multi-agent ROI explosive S29:** 🟦 Investigator 2 spawn ~40K (Plan CA terrain map + Plan B V1 audit) + 🟨 Implementer Case 2 5 spawn ~82K (Plan CA Chunk B + Plan B A2/C/D + E3 stopped mid-task) + 🟥 **Reviewer 4 spawn ~355K** (Plan CA Hotfix D2 password ≥12 MAJOR catch + Plan B Hotfix ApplicableType=Contract MAJOR catch — Smart Friend pattern 4× cumulative) + 🟩 **CICD Monitor 3 spawn ~90K** (2 fail 529 transient em main fallback manual + Plan B SeedSampleContractWorkflowV2 CRITICAL DemoSeed gate catch — gotcha #51 NEW). **Patterns NEW saved S29:** (1) 9-menu permission terrain map (Investigator) (2) V1+V2 coexist boundary (Investigator) (3) reference template paths cross-module mirror PE→Contract (Investigator) (4) Pattern 12-bis cross-module entity cookie-cutter mirror (Implementer) (5) Pattern 16-bis 4-place mirror cross-app reinforced 2× (Implementer) (6) Cross-module security validation mirror Cat 3 (Reviewer) (7) INFRASTRUCTURE vs DEMO seed classification gotcha #51 (CICD) (8) Race condition em main + Implementer parallel BE → stash workaround OR sequential preferred (Implementer). **3 hotfix cumulative:** Plan CA HF1 resolvePath staticMap + Plan CA D2 password ≥12 chars + Plan B Reviewer ApplicableType=Contract + Plan B CICD SeedSampleContractWorkflowV2 ungate. **NEW capability prod:** demo user `catalog.manager@solutions.com.vn / CatalogMgr@2026` (role CatalogManager — full CRUD 9 menu Master+Catalogs) + Drafter có thể create V2 contract qua Workspace dropdown `QT-HD-V2-001` sample workflow. **MCP RAG tools added cho 4 agent definitions** (commit `b51fc94`) — anh restart CLI để hot-reload. State chốt: **33 mig (+2 Mig 32+33)** · **60 tables (+1 ContractLevelOpinions)** · **~148 endpoints (+1 V2 contract create)** · **38 FE pages (+3: 4 master moved + WorkflowMatrixView counted unchanged S24)** · **111 test PASS unchanged** (UAT mode skip per chunk + cookie-cutter mirror) · **51 gotcha (+1 #51 INFRASTRUCTURE vs DEMO seed)** · **25 memory user-level unchanged** · 6 skills · 4 sub-agents (MCP RAG added pending CLI restart) · **14 AppRoles (+1 CatalogManager)** · **34 active prod users (+1 catalog.manager)** · **7 V1 contracts pinned coexist V2** · Bundle hash 4× rotate cumulative S29 (fe-admin: `BvcWrq2z→leEMWFLU→BBADl46y` + fe-user: `Bg2FNeIz→DVBLmZlt→Dgn1iU9E→DA_VI3zO`).)
**Last updated S29 prev wrap1:** 2026-05-22 (Session 29 — **🎯 Plan CA wire DONE + Hotfix 1 deploy**: 6 commits + 1 hotfix push `3cb54e0..e55d96b` cumulative. Move "Cấu hình danh mục dùng chung" (9 menu: Master + Suppliers + Projects + Departments + Catalogs + 4 sub-catalogs) từ fe-admin → fe-user/eoffice + add role mới `CatalogManager` + demo user `catalog.manager@solutions.com.vn`. **Chunks:** A 80d39a0 BE Role + Seed (em main, 64 LOC) · B 06a441c FE move 4 master pages 948 LOC (Implementer Case 2 cookie-cutter mirror byte-identical SHA256 4 file) · C c995f42 Sidebar filter 2 app (em main, 14 LOC) · D 4a592cf Seed demo user (em main, 7 LOC) · D2 68bcedd Hotfix password ≥12 chars S22+2 policy (em main, Reviewer pre-push CATCH critical) · HF1 e55d96b Add 7 entries `resolvePath` staticMap fe-user (em main, anh UAT screenshot CATCH silent sidebar drop). **2 CI runs PASS:** Run #229 sha=68bcedd 3m32s + Run #230 sha=e55d96b 3m24s. Bundle hash 2× rotate: fe-admin `BvcWrq2z→leEMWFLU`, fe-user `Bg2FNeIz→DVBLmZlt→Dgn1iU9E`. **Multi-agent ROI Plan CA:** 🟦 Investigator 1 spawn ~15K (terrain map 9 menu + GOTCHA tree-inherit) + 🟨 Implementer Case 2 1 spawn ~10K (byte-identical mirror 4 page) + 🟥 Reviewer 2 spawn ~165K (CRITICAL catch password policy) + 🟩 CICD Monitor 2 spawn fail 529 transient (em main fallback manual smoke) + 👤 Chủ trì Solo Chunk A/C/D/D2/HF1 ~100K. **NEW gotcha #50** "Page move cross-app — `Layout.tsx` `resolvePath` staticMap missed mirror → silent sidebar drop" + Pattern reusable cross-project: **4-place mirror checklist** khi Implementer Case 2 cookie-cutter copy page (page + Routes + menuKeys.ts + Layout.tsx staticMap — Implementer Chunk B missed point 4). Implementer MEMORY Pattern 16-bis NEW. **State chốt S29 Plan CA:** 31 mig (no schema change) · 59 tables · ~146 endpoints · **36 FE pages (+4: fe-user master/{Suppliers,Projects,Departments,Catalogs}Page)** · **111 test pass unchanged** (UAT skip per chunk + cookie-cutter mirror) · **50 gotcha (+1 #50)** · 25 memory user-level · 6 skills · 4 sub-agents · **13 AppRoles (+1 CatalogManager)** · **34 active prod users (+1 catalog.manager@solutions.com.vn)**. Catalog manager login `CatalogMgr@2026` (15 chars policy compliant). 7 commits remote `3cb54e0..e55d96b`.)
**Last updated S28 prev:** 2026-05-22 (Session 28 **FINAL wrap****🎯 S28 cumulative 12+ bước (t1→t6): Startup 9-step + RAG ROI verdict + bro correction mistake + scope-down + Layer A governance apply + 4 sub-agent flush MEMORY**. Bro broadcast **Layer A governance active 2026-05-22** (3-Layer distributed: A project-local active / B shared_global defer / C infrastructure anh pqhuy). **Quên rule cũ "mọi tương tác PHẢI store_memory" — ABANDONED.** Em main SOLUTION_ERP apply Layer A scope local: **4-category default** (decision/lesson/pattern-proven-≥2×/session-wrap) + **skip list nguyên** + 5 SOLUTION_ERP-specific adjustment cho Phase 9 UAT mode + **tag schema mandatory** `[type, phase, bc-or-module]` với type closed enum 6 (decision/lesson/pattern/gotcha/bug-fix/session-wrap) + phase enum 11 (phase-0 → phase-10+, current **phase-9 UAT ACTIVE**) + BC/module enum 8 domain (contract/pe/budget/master/identity/forms/workflow/notifications) + 5 cross-cutting (tests/infra/cicd/multi-agent/governance/docs) + optional prefix 8 (tech/agent/risk/severity/commit/mig/gotcha/plan). **source_path convention** `solution_erp/<area>/<topic>-<date>`. **Eval ritual** weekly Friday 5 metric từ 2026-05-29 → end-trial 2026-06-05. **10 golden query draft** ready. **6 chunk stored S28 t2-t6:** Protocol cross-project (deprecated S28 t4 scope-down) · Conversation log S28 t1+t2 · Commit pattern acknowledge · 2-week monitoring 5 measure · Scope-down acknowledge · Layer A policy apply. **4 sub-agent flush MEMORY DONE post-S27 model:inherit fix** — registry hot-reload verified all 4 (Investigator agentId `a2462`/`aeda4`, Implementer `a2a2f`, Reviewer `a9e94`, CICD Monitor `a8098`). Each Recent activity entry S28 t1 → S28 wrap góc nhìn từng con preserved cumulative S1-S27. **NEW memory user-level `feedback_interaction_logging_rag.md` SCOPE-DOWN S28 t4** (anti-pattern over-reach self-authorize cross-project rule + implicit consent risk lesson). Stats unchanged: 31 mig · 59 tables · ~146 endpoints · 35 FE pages · **111 test pass** · 49 gotcha · **25 memory user-level (+1 NEW interaction logging scope-down)** · 6 skills · 4 sub-agents (all verified hot-reload) · **3,462 RAG chunks SOLUTION_ERP** (+6 chunk store S28) + 6 MCP tools active. **Hit rate verify 3 query**: Plan B Contract V2 rerank 0.84 ✓ + gotcha #48 rerank 0.84/0.82/0.82 ✓ + per-NV 10-surface checklist hit (token overflow) ✓ — > 0.7 threshold pass cumulative. **0 commit remote S28** (all docs + memory + RAG store — anh chốt push thủ công sau approve). Plan B Contract V2 wire kick-off pending 2026-05-22+ (8-chunk pre-allocated 🟦 Investigator pre-flight + 🟨 Implementer Case 2 ×4 + 👤 Chủ trì Solo Service ApproveV2Async + 🟥 Reviewer pre-commit + 🟩 CICD Monitor post-deploy).)

View File

@ -0,0 +1,325 @@
# Session 27 — 2026-05-22 — Memory Curate + RAG Manual Control + Multi-agent Setup Fix
**Dev:** Claude em main SOLUTION_ERP (sub-agent registry empty cả session - pitfall #1+#2 confirmed) + bro pqhuy
**Duration:** ~5h (start ~17:00 → end ~22:00 GMT+7)
**Base commit:** `d99069a` (S26 chốt cuối Plan AG6)
**Final commits:** TBD (anh push manual sau approve)
---
## 🎯 Làm được
### Plan A.3 — RAG Manual Control + Custom Dashboard
🟧 **5 PS scripts**`D:\.claude-rag\scripts\` (em main solo cookie-cutter mirror — Implementer Case 2 ACCEPT criteria miss vì registry empty):
- `start.ps1` (2.4 KB) - Qdrant background + health check (initial 3s warmup + TimeoutSec 5)
- `stop.ps1` (1.4 KB) - Graceful stop + verify
- `status.ps1` (6.2 KB) - 6-section terminal report colored
- `dashboard.ps1` (17.4 KB) - Generate custom HTML + open browser
- `boot.ps1` (1.4 KB) - Auto-boot sequence (Startup shortcut)
🟧 **6 `$PROFILE` aliases:** rag-start / rag-stop / rag-status / rag-dashboard / rag-restart / rag-boot
🟧 **Auto-boot Startup folder shortcut** `%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\rag-boot.lnk` — auto-run boot.ps1 mỗi lần Windows boot.
🟧 **Custom MCP Dashboard HTML** `D:\.claude-rag\dashboard.html` (13.3 KB auto-gen) — 7 panels: Qdrant + MCP + Collections + Voyage + System + Agent MEMORY + Recent Logs. Auto-refresh 60s. Anthropic orange theme.
### Plan A.4 — RAG Onboarding Guide
🟧 **`docs/guides/rag-onboarding-guide.md`** (26.3 KB / ~440 lines / 13 sections + §A2):
- 13 sections: TL;DR, Background, Pre-check, Bootstrap 3 step, 6 MCP tools matrix, 4 sub-agent brief, Permission, Workflow, Troubleshooting, Tunings, Best practices, Status monitoring §12, Distributed Approach B §13
- §A2 Cấu trúc chuẩn em main pioneer (replaces Plan A.2 skip với full file tree + 8-step bootstrap checklist + 4 Gotcha discoveries S26-S27)
- Native Qdrant vs Custom MCP dashboard comparison table
### Memory Curate (3 việc anh chốt OK)
🟧 **4 MEMORY curate** -60% size cumulative:
- cicd-monitor: 72.4 KB → 15.8 KB (-78%)
- implementer: 38.8 KB → 22.0 KB (-43%)
- investigator: 34.9 KB → 16.3 KB (-53%)
- reviewer: 34.5 KB → 17.9 KB (-48%)
- Archive total 115 KB preserved verbose entries (rule §6.5 compliance)
🟧 **Audit drift §6.4 + §9.4** sớm (trigger +8 gotcha vượt threshold):
- 5 file patched: 4 `26 migration → 31 migration` + 1 `41 bẫy → 49 bẫy`
- Audit log file: `docs/changelog/skill-audit-2026-05-late.md`
- KEEP narrative §6.5: contract-workflow `96→77 test` historical preserved
### Plan F1 — Qdrant Native Dashboard Fix (UAT catch by anh)
🟧 **Root cause:** Qdrant Windows binary zip không bundle Web UI static files. Log warn `Static content folder for Web UI './static' does not exist` + `http://localhost:6333/dashboard` returns 404. Em pioneer S26 chỉ download `qdrant-x86_64-pc-windows-msvc.zip` (28.3 MB binary) không biết Web UI cần download riêng.
🟧 **Fix:** Download `dist-qdrant.zip` v0.2.12 (6.59 MB, released 2026-05-21 — 0.3 ngày trước) từ qdrant-web-ui GitHub releases + extract vào `D:\.claude-rag\qdrant-bin\static\` + flatten `dist/` subfolder up + restart Qdrant → HTTP 200 "UI | Qdrant" ✓
🟧 **Custom Dashboard panel link "Qdrant Native Dashboard"** giờ work end-to-end.
### Plan F2 — Multi-agent Setup Pitfalls Fix (VIPIX guide catch)
🚨 **Critical discovery:** VIPIX project xuất `docs/guides/multi-agent-pitfalls.md` 2026-05-22 — anh share cho em audit SOLUTION_ERP setup. Em phát hiện:
- 4 file `.claude/agents/*.md` dùng `model: claude-opus-4-7` (full ID = 200K fallback, KHÔNG 1M Opus)
- 4 file dùng non-standard field `effort: max` → silent reject possible
- → Registry chưa load cả session S27 (Agent error: "Agent type 'investigator' not found")
🟧 **Fix applied 4 file:** `model: inherit` (kế thừa 1M Opus parent) + remove `effort: max`. Pending CLI restart cho hot-reload (pitfall #1).
🟧 **Memory user-level NEW:** `feedback_subagent_setup_pitfalls.md` (235 lines) - cross-project pitfall checklist VIPIX + SOLUTION_ERP evidence. Integrated vào MEMORY.md index.
---
## ⚠️ Anti-pattern phá vỡ S27 (retrospective analysis)
Em chủ trì kiêm **5 roles** cả session vì registry empty:
| Task S27 | Implementer ACCEPT fit? | Outcome |
|---|---|---|
| C1 Curate cicd-monitor 72KB | ❌ REFUSE #1 (judgment §6.5) | Em solo CORRECT |
| C2-C4 Curate 3 agent MEMORY | ❌ REFUSE #1 | Em solo CORRECT |
| C5 Audit drift §6.4 + §9.4 | ❌ REFUSE #1 | Em solo CORRECT |
| **A3.1 Write 5 PS scripts** | ✅ **ACCEPT Case 2 (5 file cookie-cutter mirror)** | **Em miss delegate** |
| A3.2 Dashboard HTML + generator | ❌ REFUSE #2/#7 (first time pattern) | Em solo CORRECT |
| A4 Onboarding guide 440 lines | ❌ REFUSE #2 (docs judgment) | Em solo CORRECT |
| F1 Qdrant Web UI fix | ❌ REFUSE #4 (bug reasoning chain) | Em solo CORRECT |
| **F2 4 agent files fix** | ✅ **ACCEPT Case 1 (4 file mechanical same edit)** | **Em miss delegate** |
**Verdict:** 2/8 task lẽ ra delegate Implementer (Case 1+2) nhưng registry empty → em main forced solo. Net loss ~30 phút time + miss cookie-cutter mirror discipline.
**Reviewer Smart Friend guard miss critical:**
- Em main S27 write rag-onboarding-guide.md claim "Qdrant Native Dashboard `http://localhost:6333/dashboard`" WITHOUT actual verify
- Anh pqhuy UAT browser catch 404 → escalate
- → Reviewer pre-commit spawn (nếu registry load) sẽ catch Cat 1 "Wire claim verify" — em main self-review compromised
---
## E2E verified
### Plan A.3 verify
| Item | Status | Notes |
|---|---|---|
| 5 PS scripts execute | ✅ | rag-status / rag-start / rag-stop tested live |
| Custom Dashboard HTML | ✅ | 13.3 KB, 7 panels rendered, browser opened |
| `$PROFILE` aliases | ✅ | 6 functions loaded via `. $PROFILE` |
| Startup shortcut | ✅ | `rag-boot.lnk` 1378 bytes created |
| ASCII-only PS source | ✅ | Em fix 3 file (stop/boot/dashboard) sau Unicode mangling issue |
### Plan F1 verify
| Item | Status |
|---|---|
| Qdrant native dashboard HTTP 200 | ✅ "UI | Qdrant" title |
| Collections API live | ✅ proj_solution_erp returned |
| static/ folder size | 16.07 MB (Web UI v0.2.12) |
### Plan F2 verify (partial)
| Item | Status |
|---|---|
| 4 file edit `model: inherit` | ✅ Applied |
| 4 file remove `effort: max` | ✅ Applied |
| Sub-agent spawn test | ❌ Pitfall #1 - need CLI restart |
| Re-spawn post fix | ❌ Still "Agent type not found" |
| Registry hot-reload | ❌ Pending anh restart Claude Code CLI |
### Memory curate verify
| Agent | Before | After | Status |
|---|---:|---:|---|
| cicd-monitor | 72.4 KB | 15.8 KB | ✅ -78% |
| implementer | 38.8 KB | 22.0 KB | ✅ -43% |
| investigator | 34.9 KB | 16.3 KB | ✅ -53% |
| reviewer | 34.5 KB | 17.9 KB | ✅ -48% |
| **Total** | **180.6 KB** | **72.0 KB** | **-60%** |
---
## 🐛 Bug gặp + fix
| Bug | Fix |
|---|---|
| PowerShell 5.1 mangling Unicode `✓ → âœ"` trong .ps1 source | Replace ASCII-only `[OK]/[FAIL]/->/*/` trong PS code (HTML output unicode OK) |
| `dashboard.ps1` parser fail với emoji 🧠🗄️🔌 trong here-string HTML | Rewrite ASCII-only PS source, dùng CSS-styled `icon-box` div thay emoji |
| Qdrant native `localhost:6333/dashboard` 404 | Download `dist-qdrant.zip` v0.2.12 + extract `static/` + flatten + restart |
| `start.ps1` health check timeout 30s fail false alarm | Adjust to 3s warmup + TimeoutSec 5 |
| Qdrant crashed OOM "allocation 8.4MB failed" mid-session | rag-restart recovers (data persisted) |
| 4 sub-agent `model: claude-opus-4-7` silent fallback 200K | Fix `model: inherit` per VIPIX pitfall #2 |
| Sub-agent `effort: max` non-standard field silent reject | Remove field |
| Re-bootstrap fail pydantic serialization error | Defer S28 — debug needed |
---
## 📚 Docs updates
| File | Update |
|---|---|
| `docs/STATUS.md` | Last updated S27 chốt cuối (Memory curate + RAG manual control + multi-agent fix) |
| `docs/HANDOFF.md` | TL;DR S27 với pitfall lesson + Plan B Contract V2 Pending S28 |
| `docs/changelog/sessions/2026-05-22-s27-memory-curate-rag-dashboard-multi-agent-fix.md` | File này — session log đầy đủ |
| `docs/guides/rag-onboarding-guide.md` | NEW 26.3 KB / 440 lines / 13 sections + §A2 cấu trúc chuẩn |
| `docs/changelog/skill-audit-2026-05-late.md` | NEW audit log drift trigger sớm (~100 lines) |
| `.claude/agents/*.md` × 4 | Fix `model: inherit` + remove `effort: max` |
| `.claude/agent-memory/*/MEMORY.md` × 4 | Curate (-60%) + S27 entry proxy flush |
| `.claude/agent-memory/*/archive/2026-05-*.md` × 4 | NEW archive files preserve verbose Q1 entries |
| `.claude/skills/README.md` | `26 migration → 31` + `44 bẫy → 49` |
| `.claude/skills/ef-core-migration/SKILL.md` | 4 patch count drift |
| `.claude/skills/dependency-audit-erp/SKILL.md` | `41 bẫy → 49 bẫy` |
| Memory user-level `feedback_subagent_setup_pitfalls.md` | NEW 235 lines cross-project pitfall checklist |
| Memory user-level `feedback_rag_hybrid_pattern.md` | +1 bài học #8 (Qdrant Web UI static missing fix) |
| Memory user-level `MEMORY.md` index | +1 entry feedback_subagent_setup_pitfalls |
---
## 🤝 Handoff S28+
### Pending priority HIGH
1. **Anh restart Claude Code CLI** → verify 4 sub-agent load post `model: inherit` fix. Spawn test Investigator IMMEDIATELY mỗi session start (per `feedback_subagent_setup_pitfalls.md` §4).
2. **Plan B Contract V2 wire** (carry từ S25/S26) — kick off với 4 sub-agent ACTIVE: Investigator pre-flight + Implementer Case 2 mirror PE Mig 22-23 + Reviewer pre-commit + CICD Monitor post-push.
3. **Re-bootstrap RAG** SOLUTION_ERP để index S27 changes (rag-onboarding-guide.md + skill-audit-late + 4 MEMORY updates + feedback_subagent_setup_pitfalls). Bootstrap fail pydantic — debug + retry S28.
### Pending priority MEDIUM
4. **Audit S20-S26 memory log "Investigator spawn" claims** — verify history retroactive (có thật spawn hay nhầm general-purpose default?). Defer dedicated investigation S28+.
5. **Plan AI Phase 5** distributed bootstrap 4 project khác (NamGroup/DH/Ashico/Vipix) — em main project đó tự làm khi anh mở Claude Code project (Approach B distributed).
6. **Test debt catch-up Plan C bundle** (S22+1 + S25 + S26 bug fix chưa add regression — UAT mode defer per §7).
### Pending priority LOW
7. Benchmark RAG recall@10 golden dataset 100 query (gap optional)
8. Disaster recovery weekly backup Qdrant data → Dropbox (gap optional)
9. Gotcha #48 SQLite tie-break + #49 dual-phase UI confusion add `docs/gotchas.md` (carry từ S25)
---
## 📊 Thông số cumulative S27
| Metric | S26 chốt | S27 chốt | Δ |
|---|---|---|---|
| DB tables | 59 | 59 | 0 |
| Migrations | 31 | 31 | 0 |
| Endpoints | ~146 | ~146 | 0 |
| FE pages | 35 | 35 | 0 |
| Unit tests | 111 | 111 | 0 (UAT mode defer per §7) |
| Gotchas | 49 | 49 | 0 (gotcha #48, #49 still pending docs add) |
| Memory user-level | 23 | **24** | **+1 (`feedback_subagent_setup_pitfalls.md` NEW)** |
| Skills project-local | 6 | 6 | 0 |
| Sub-agents | 4 (broken registry) | 4 (fixed, pending CLI restart) | 0 count, +1 fix |
| Docs files | — | **+2** | `rag-onboarding-guide.md` + `skill-audit-2026-05-late.md` |
| PS Scripts infra | 0 | **+5** | `D:\.claude-rag\scripts\*.ps1` |
| Custom Dashboard | 0 | **+1** | `dashboard.html` auto-gen |
| Agent MEMORY total | 180.6 KB | 72.0 KB | **-60%** (115 KB archived) |
| Commits remote | `e23f51c..d99069a` | unchanged | **0 push S27** (all local + memory + docs - anh chốt push thủ công) |
### Multi-agent ROI S27
| Agent | Spawn | Actual outcome |
|---|---|---|
| 🟦 Investigator | 0 (registry empty) | Em main solo audit + curate |
| 🟨 Implementer | 0 (registry empty) | Em main solo 5 PS scripts + 4 file fix (lẽ ra delegate Case 1+2) |
| 🟥 Reviewer | 0 (registry empty) | Em main self-review compromised (miss Qdrant 404) |
| 🟩 CICD Monitor | 0 (no remote push) | N/A |
| 👤 Em main solo | continuous | ~5h cả 8 task + meta-discovery pitfall |
**Net learning S27:** Pitfall discovery + fix infrastructure → S28+ multi-agent CÓ THỂ work properly sau CLI restart. Anti-pattern em main solo = forced không phải choice.
---
## 🎓 Patterns reusable cross-project (S27 NEW)
1. **Multi-agent setup pitfall checklist** - 4 pitfall VIPIX + SOLUTION_ERP evidence (`feedback_subagent_setup_pitfalls.md`)
2. **PS scripts ASCII-only discipline** - HTML output unicode OK, PS source phải ASCII (CSS-styled badges + icon-box thay emoji)
3. **Qdrant Windows binary 2-step setup** - binary + Web UI static download separate (gotcha #8 `feedback_rag_hybrid_pattern.md`)
4. **Custom Dashboard PS generator pattern** - here-string HTML template + collect data via Invoke-RestMethod + Out-File UTF-8 + Start-Process browser
5. **Memory curate proxy pattern khi registry empty** - em main update sub-agent MEMORY trực tiếp với retrospective analysis (REFUSE log validation + ACCEPT miss flag)
6. **Audit log file separate khi drift sớm** - `skill-audit-YYYY-MM-late.md` thay vì rewrite existing audit log (preserve historical trail per §6.5)
7. **Dashboard SNAPSHOT vs LIVE distinction** - static HTML generated by PS = snapshot tại 1 thời điểm, browser meta refresh KHÔNG re-run PS. Anti-pattern em pioneer commit: meta refresh 60s falsely impression live. Fix: add prominent timestamp + warning banner + link to Qdrant native dashboard (LIVE API fetch). Pattern reusable cho bất kỳ custom dashboard PowerShell.
8. **NSSM Windows Service Option 4b upgrade pattern** - Qdrant binary natively no `--service` flag → wrap với NSSM (3 MB binary download from nssm.cc release/2.24, retry 503 transient). install-service.ps1 + fix-service-start.ps1 elevated scripts ready. Auto-start boot-time + auto-restart on crash + survive logout. Recipe transferable cho bất kỳ database/server binary cần Windows Service mode mà không có native support.
9. **Hybrid context loading discipline (Cách A defensive)** - Layer 1 blanket auto-load ~120K (CLAUDE+STATUS+HANDOFF+MEMORY index+skills+sub-agents) + Layer 2 RAG retrieve on-demand via 6 MCP tools. Decision gate per project MD size: < 200K skip RAG / 200K-1M lazy / > 1M MANDATORY. Token budget zones 5-tier (green/warning/approach/critical). 7 anti-patterns avoid (Read full session log cũ, search vague, em main solo qualify Implementer Case 1/2, skip store_memory, heavy session > 6h). Onboarding §A3 NEW comprehensive.
---
## 🔧 Plan A.3+ Upgrade Post-Wrap — Option 4b NSSM Windows Service
> **Trigger:** Anh phản biện "Qdrant nên auto-start như database server thường (PostgreSQL/SQL Server đều Windows Service auto-start)" → em upgrade Option 4a (manual scripts S26 chốt) lên **Option 4b NSSM Windows Service**.
> **Done:** Post-initial wrap-up cùng session S27 — anh chạy 2 elevated PS scripts → service Running successfully.
### NSSM install + service registration
🟧 **Download NSSM 2.24** ~351 KB từ `https://nssm.cc/release/nssm-2.24.zip` (retry 1× sau 503 transient) → extract `D:\.claude-rag\nssm\nssm.exe` (323 KB win64 binary)
🟧 **install-service.ps1** (5.4 KB elevated script):
- Verify Admin privileges
- Remove old service nếu exist (idempotent)
- `nssm install Qdrant qdrant.exe` + AppDirectory `D:\.claude-rag\qdrant-bin`
- Log paths `logs/qdrant-service.log` + `.err` với rotation 10 MB
- `Start: SERVICE_AUTO_START` (boot-time auto-start trước login)
- `AppExit Default Restart` + `AppRestartDelay 3000` (auto-respawn 3s sau crash)
- DisplayName "Qdrant Vector DB (RAG Unified)" + Description friendly
- Start service + health verify HTTP `localhost:6333/healthz`
### Bug gặp: WAL lock conflict
🟧 **Symptom:** Step [5/6] Start failed — service Paused state, log `Can't init WAL: Kind(WouldBlock)`.
🟧 **Root cause:** Em start Qdrant manual PID 9800 ngay trước install-service.ps1 → manual process held Write-Ahead Log file lock → service không init được WAL → fail start + Paused state.
🟧 **fix-service-start.ps1** (2.4 KB elevated script) — recovery:
- Stop-Service -Force (kill Paused state)
- Kill any remaining qdrant.exe (defensive cho orphan PID)
- Wait 3s
- Start-Service Qdrant
- Verify HTTP + process PID + RAM
🟧 **Result confirmed:** Service Running PID 4476 RAM 101.8 MB. Auto-start ENABLED ✓
### 4 PS scripts updated to Service mode
🟧 **start.ps1**`Start-Service Qdrant` (cần Admin). Detect already Running + skip + show service info.
🟧 **stop.ps1**`Stop-Service Qdrant -Force` (cần Admin). Defensive kill orphan qdrant.exe nếu detected.
🟧 **status.ps1**`Get-Service Qdrant` (no Admin) thay process check + Section 1 expanded với service info (Status, StartType, DisplayName) + Process info defensive guard `try/catch` cho StartTime null khi spawn from service.
🟧 **boot.ps1** → drop Qdrant start (service auto-start) + verify service Running + regenerate Dashboard + open browser. Used by Startup folder shortcut `rag-boot.lnk`.
### Dashboard SNAPSHOT vs LIVE clarification
🟧 **Bug discovered:** Custom Dashboard `dashboard.html` show "Running" badge stale 10+ phút sau Qdrant DOWN. Root cause: `<meta http-equiv="refresh" content="60">` chỉ reload HTML cached, KHÔNG re-run PS script.
🟧 **Fix applied:** Remove meta refresh + add prominent yellow banner top:
```html
⚠ Dashboard này là STATIC SNAPSHOT tại thời điểm generated (xem timestamp).
Browser auto-refresh chỉ reload HTML cached, KHÔNG poll Qdrant API live.
Muốn data live → mở Qdrant Native Dashboard (fetch API real-time).
```
🟧 **Header timestamp** + label "SNAPSHOT (not live)" red color visible.
🟧 **Service status panel** added Get-Service info: Service (Running) / Auto-start (Automatic) / DisplayName.
### Onboarding guide §A3 NEW comprehensive
🟧 **Section §A3 Context loading Hybrid pattern** (~242 lines added) — 8 sub-sections:
- §A3.1 Why Hybrid Cách A vs Cách B vs Skip RAG (decision gate)
- §A3.2 Layer 1 Blanket auto-load checklist (~200-280 KB ≈ 50-70K tokens)
- §A3.3 Layer 2 RAG retrieve decision tree (7-branch)
- §A3.4 Token budget guidance 1M Opus (5 utilization zones)
- §A3.5 6 MCP tools examples concrete (Vietnamese + English queries)
- §A3.6 Best practices em main daily workflow (morning + in-session + EOS + monthly)
- §A3.7 7 Anti-patterns cross-project (distilled VIPIX + SOLUTION_ERP)
- §A3.8 Context monitoring + when to end session (heuristics + protocol)
### Memory user-level updates
🟧 **`feedback_rag_distributed_ownership.md`** Decision 2 upgraded Option 4a → 4b NSSM với gotcha S27 install (nssm.cc 503 transient + WAL lock conflict + Process.StartTime null guard).
### Final state Service mode
| | State |
|---|---|
| Qdrant Service | Running (Automatic) ✓ |
| HTTP /healthz | OK ✓ |
| Collection proj_solution_erp | 3460 chunks (⚠ indexed_vectors=0 HNSW broken — pending re-bootstrap S28) |
| Auto-start on Windows boot | ENABLED ✓ |
| Auto-restart on crash | ENABLED 3s delay ✓ |
| 7 PS scripts total | start/stop/status/dashboard/boot/install-service/fix-service-start |
| Onboarding guide | 42.5 KB / 682 lines / 14 sections + §A2 + §A3 |

View File

@ -0,0 +1,159 @@
# Session 29 — 2026-05-22 — Plan CA + Plan B Contract V2 wire end-to-end
> **TL;DR:** 20 commits cumulative S29 push 4 CI Runs PASS deploy production. Plan CA (Move "Cấu hình danh mục" admin → eoffice) + Plan B Contract V2 wire (mirror PE Mig 22-26). Smart Friend Reviewer + CICD Monitor cumulative 4× catches MAJOR/CRITICAL bugs pre/post deploy. 8 patterns reusable NEW.
## 🎯 Session goals (anh chốt 2026-05-22 startup S29)
1. ✅ Re-bootstrap RAG SOLUTION_ERP S27+S28 NEW content (60.9s, 2,794 chunks)
2.**Plan CA** — Move "Cấu hình danh mục dùng chung" (9 menu Master + Catalogs) từ fe-admin → fe-user/eoffice + role mới `CatalogManager`
3.**Plan B** — Contract V2 wire mirror PE Mig 22-26 (COEXIST V1+V2)
4. ✅ Add MCP RAG tools cho 4 sub-agent definitions (pending CLI restart S30)
5. ✅ End-to-end demo cross-project RAG search (NAMGROUP HRM 10 info samples)
## 📦 Commits cumulative S29 — 20 commits push `3cb54e0..38f1c4d`
### Plan CA — 7 commits
| # | SHA | Chunk | Owner |
|---|---|---|---|
| 1 | `06a441c` | B FE move 4 master pages (948 LOC byte-identical) | 🟨 Implementer |
| 2 | `80d39a0` | A BE Role CatalogManager + Seed permissions | 👤 Em main |
| 3 | `c995f42` | C Sidebar filter 2 app | 👤 Em main |
| 4 | `4a592cf` | D Seed demo user catalog.manager | 👤 Em main |
| 5 | `68bcedd` | D2 Hotfix password ≥12 chars S22+2 policy (Reviewer catch) | 👤 Em main |
| 6 | `e55d96b` | **Hotfix 1** resolvePath staticMap silent sidebar drop (anh UAT catch) | 👤 Em main |
| 7 | `6eec8d7` | Docs wrap (gotcha #50 + Pattern 16-bis + STATUS) | 👤 Em main |
### MCP RAG add — 1 commit
| # | SHA | Description |
|---|---|---|
| 8 | `b51fc94` | Add `mcp__rag-unified__search_memory + cross_project_search` cho 4 agent definitions (pending CLI restart hot-reload S30+) |
### Plan B Contract V2 wire — 11 commits + 1 docs wrap
| # | SHA | Chunk | Owner |
|---|---|---|---|
| 9 | `58898e8` | A1 Entity Contract +2 V2 fields | 👤 Em main |
| 10 | `a85e437` | A2 Mig 32 + Configuration + Seed sample (4,094 LOC EF auto-gen) | 🟨 Implementer |
| 11 | `138469d` | B Service ApproveV2Async branch + gen mã HĐ adapt | 👤 Em main |
| 12 | `26c98d3` | C Mig 33 ContractLevelOpinions cookie-cutter (4,265 LOC) | 🟨 Implementer |
| 13 | `1f199b0` | B2 UPSERT block + ResolveActorFullName helper | 👤 Em main |
| 14 | `ef23308` | E1 CreateContractCommand +ApprovalWorkflowId V2 | 👤 Em main |
| 15 | `62b50d1` | D FE Workspace V2 Select dropdown × 2 app | 🟨 Implementer |
| 16 | `48f6d22` | E2 ContractDetailDto + populate LevelOpinions[] | 👤 Em main |
| 17 | `14feb69` | E3 FE Section 5 LevelOpinionsV2 × 2 app (em main solo, Implementer stopped mid-task) | 👤 Em main |
| 18 | `3e92584` | **Hotfix Reviewer** ApplicableType=Contract validation guard (Reviewer MAJOR catch) | 👤 Em main |
| 19 | `38f1c4d` | **Hotfix CICD** SeedSampleContractWorkflowV2 OUT of DemoSeed gate (CICD CRITICAL catch — gotcha #51 NEW) | 👤 Em main |
| 20 | (this session log) | Docs wrap S29 + session log + flush 4 MEMORY | 👤 Em main |
## 🎯 Multi-agent ROI explosive S29
| Agent | Spawns | Tokens | Major catches |
|---|---:|---:|---|
| 🟦 **Investigator** | 2 | ~40K | Plan CA terrain map 9 menu + GOTCHA tree-inherit; Plan B audit Contract V1 + recommend 6→9 chunk re-split + 3 surprise (zero ApplicableType=3 prod, RejectedAtStepIndex drift, ContractType variants generic) |
| 🟨 **Implementer Case 2** | 5 | ~82K | Plan CA Chunk B 4 master pages mirror byte-identical; Plan B A2 Mig 32 + C Mig 33 + D FE Workspace V2 cookie-cutter; **E3 stopped mid-task** (complex FE judgment, em main solo finish) |
| 🟥 **Reviewer** | 4 | ~355K | **MAJOR Plan CA Hotfix D2:** Password ≥12 chars policy S22+2 catch BEFORE prod 401; **MAJOR Plan B Hotfix:** ApplicableType=Contract validation guard catch BEFORE security gap exploit |
| 🟩 **CICD Monitor** | 3 | ~90K | 2 fail 529 Anthropic transient → em main fallback manual smoke; **1 CRITICAL Plan B Hotfix:** SeedSampleContractWorkflowV2 gated by DemoSeed → V2 path BLOCKED prod → gotcha #51 NEW |
| 👤 **Em main Solo** | — | ~150K | Coordinate 4 agents + Chunk B/B2 cross-stack reasoning + E3 finish + 3 hotfix re-edit + 2 docs wrap |
| **TOTAL** | 14 spawn | ~715K | 4× Smart Friend cumulative wins |
## 🛡️ Smart Friend pattern proven 4× cumulative
| # | Session | Catch | Severity | Owner |
|---|---|---|---|---|
| 1 | S22 | Class-level `[Authorize(Policy)]` silent 403 (gotcha #44) | MAJOR | 🟥 Reviewer |
| 2 | S25 | Multi-Changelog SQLite tie-break test discriminator (gotcha #48) | MAJOR | 🟩 CICD Monitor |
| 3 | **S29** | Password ≥12 chars policy + ApplicableType=Contract validation | MAJOR | 🟥 Reviewer |
| 4 | **S29** | INFRASTRUCTURE vs DEMO seed gate confusion (gotcha #51) | CRITICAL | 🟩 CICD Monitor |
## 🆕 Patterns reusable NEW saved S29 (8 patterns)
1. **9-menu permission terrain map pattern** (Investigator) — inventory file paths + LOC + component parity check trước delegate chunk
2. **V1+V2 coexist boundary** (Investigator) — KHÔNG drop V1 nếu prod data pinned. Plan F precedent S22 (ABORT khi định drop V1)
3. **Reference template paths cross-module mirror** (Investigator) — cite EXACT line ranges PE → Contract V2 (Mig 23:14-31, Mig 24:14-17, ApproveV2Async 446-634, UPSERT 519-546, ResolveActor 774-783) → saving 30%+ time
4. **Pattern 12-bis cross-module entity cookie-cutter mirror** (Implementer) — entity + Mig + Config + DbSet 4-file mirror cho cross-module clone scenarios
5. **Pattern 16-bis 4-place mirror cross-app** (Implementer) — reinforced 2× Plan CA + Plan B Workspace
6. **Cross-module security validation mirror Cat 3** (Reviewer) — khi mirror entity/command cross-module, MUST mirror validation guards (ApplicableType + FK + idempotent). Easy miss
7. **INFRASTRUCTURE vs DEMO seed classification** (CICD #51) — decision tree: production cần để work end-to-end? YES → INFRASTRUCTURE ungate
8. **Race condition em main + Implementer parallel BE → stash workaround** (Implementer Plan B A2+B) — Implementer auto-stash em main WIP cho clean build. Forward: SEQUENTIAL chunks A→B→C khi cùng touch BE, NOT parallel
## 📊 State chốt S29
| Metric | Before S29 | After S29 | Δ |
|---|---:|---:|---|
| Migrations | 31 | **33** | +2 (Mig 32 Contract V2 + Mig 33 LevelOpinions) |
| DB tables | 59 | **60** | +1 (ContractLevelOpinions) |
| API endpoints | ~146 | **~148** | +1 (V2 contract create) |
| FE pages | 35 | **38** | +3 (4 master moved to fe-user — net +3 sau remove fe-admin) |
| Tests | 111 | **111** | unchanged (UAT mode skip; V2 logic NO test cover defer) |
| Gotchas | 49 | **51** | +2 (#50 staticMap silent drop + #51 INFRASTRUCTURE vs DEMO seed) |
| Memory user-level | 25 | **25** | unchanged |
| Skills | 6 | **6** | unchanged |
| Sub-agents | 4 | **4** | unchanged (MCP RAG added pending CLI restart) |
| AppRoles | 13 | **14** | +1 (CatalogManager) |
| Active prod users | 33 | **34** | +1 (catalog.manager@solutions.com.vn) |
| V1 contracts prod | 7 | **7** | preserved (no V2 contract yet) |
| RAG chunks SOLUTION_ERP | 3,462 | **2,794** | -668 (re-bootstrap clean indexing, dedup improvement) |
| Bundle hash rotate | — | **4×** | fe-admin: `BvcWrq2z→leEMWFLU→BBADl46y`, fe-user: `Bg2FNeIz→DVBLmZlt→Dgn1iU9E→DA_VI3zO` |
## 🚀 NEW capability prod end-to-end
**1. Catalog management user role:**
- Login `catalog.manager@solutions.com.vn / CatalogMgr@2026`
- fe-user sidebar: Master + 4 leaf (Suppliers + Projects + Departments) + Catalogs root + 4 sub (Units/Materials/Services/WorkItems)
- Full CRUD 9 menu, KHÔNG access fe-admin tools
**2. Contract V2 workflow path:**
- Login Drafter (vd `qs.hoang@solutions.com.vn`)
- `/contracts/new?type=1` → Workspace dropdown "Quy trình duyệt V2" có `QT-HD-V2-001`
- Pick V2 + create → contract pin `ApprovalWorkflowId`
- Submit ChoDuyet → workflow advance Step 1 Cấp 1
- Login `binh.le@solutions.com.vn` (CCM approver) → Approve
- Service `ApproveV2Async` UPSERT opinion + advance → terminal Phase=DaPhatHanh + gen mã HĐ RG-001
- Detail page Section 5 LevelOpinionsV2 render dynamic với opinion data
## ⚠️ Pending S30+ follow-up
1. **Anh restart Claude Code CLI** để 4 sub-agents pick up MCP RAG tools (commit `b51fc94`)
2. **Anh UAT verify V2 contract end-to-end** flow (Drafter → CCM approve → DaPhatHanh)
3. **Test bundle Plan B**: regression test ApproveV2Async (~150 LOC) + ApplicableType validation guard (xUnit Domain policy + Infra service test)
4. **Curate dedicated session 4 MEMORY** (Implementer 35.4KB > hard threshold)
5. **Anh UAT verify catalog manager** end-to-end (login + CRUD 9 menu)
## 📝 Files touched S29
### BE (~715 LOC actual + ~7,970 EF Designer = ~8,685)
- `src/Backend/SolutionErp.Domain/Identity/AppRoles.cs` (+6 CatalogManager)
- `src/Backend/SolutionErp.Domain/Contracts/Contract.cs` (+12 V2 fields + LevelOpinions nav)
- `src/Backend/SolutionErp.Domain/Contracts/ContractLevelOpinion.cs` (NEW 36)
- `src/Backend/SolutionErp.Application/Contracts/Services/IContractWorkflowService.cs` (+10 skipToFinal)
- `src/Backend/SolutionErp.Application/Contracts/ContractFeatures.cs` (+96 V2 + Hotfix Reviewer)
- `src/Backend/SolutionErp.Application/Contracts/Dtos/ContractDtos.cs` (+28 V2 DTO + LevelOpinion)
- `src/Backend/SolutionErp.Application/Common/Interfaces/IApplicationDbContext.cs` (+3 ContractLevelOpinions DbSet)
- `src/Backend/SolutionErp.Infrastructure/Persistence/ApplicationDbContext.cs` (+2 DbSet)
- `src/Backend/SolutionErp.Infrastructure/Persistence/Configurations/ContractConfiguration.cs` (+15 FK + Index)
- `src/Backend/SolutionErp.Infrastructure/Persistence/Configurations/ContractLevelOpinionConfiguration.cs` (NEW 37)
- `src/Backend/SolutionErp.Infrastructure/Persistence/DbInitializer.cs` (+~120 CatalogManager seed + SampleContractWorkflowV2 + ungate Hotfix CICD)
- `src/Backend/SolutionErp.Infrastructure/Services/ContractWorkflowService.cs` (+200 V2 branch + ApproveV2Async + UPSERT + ResolveActor)
- `src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/20260522051059_AddApprovalWorkflowToContract.cs` (NEW Mig 32)
- `src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/20260522052240_AddContractLevelOpinions.cs` (NEW Mig 33)
### FE (~1,050 LOC × 2 app sometimes byte-identical)
- `fe-user/src/pages/master/{Suppliers,Projects,Departments,Catalogs}Page.tsx` (NEW 948 byte-identical mirror admin)
- `fe-user/src/lib/menuKeys.ts` (+5 Catalogs sub-keys)
- `fe-user/src/App.tsx` (+5 master routes)
- `fe-admin/src/components/Layout.tsx` (+ADMIN_HIDDEN_MASTER_KEYS 9 keys hide)
- `fe-user/src/components/Layout.tsx` (-4 USER_HIDDEN_KEYS + 7 resolvePath staticMap Hotfix 1)
- `fe-admin/src/pages/contracts/ContractCreatePage.tsx` (+44 V2 Workspace dropdown)
- `fe-user/src/pages/contracts/ContractCreatePage.tsx` (+44 V2 Workspace mirror)
- `fe-admin/src/types/contracts.ts` (+22 V2 fields + ContractLevelOpinion)
- `fe-user/src/types/contracts.ts` (+22 V2 fields mirror)
- `fe-admin/src/components/contracts/ContractDetailContent.tsx` (+40 Section 5 V2)
- `fe-user/src/components/contracts/ContractDetailContent.tsx` (+40 Section 5 V2 mirror)
### Docs + scripts + agent setup
- `docs/STATUS.md` (S29 wrap header)
- `docs/HANDOFF.md` (S29 wrap header)
- `docs/gotchas.md` (+gotcha #50 staticMap + #51 INFRASTRUCTURE vs DEMO seed)
- `docs/changelog/sessions/2026-05-22-s29-plan-ca-plan-b-contract-v2-wire.md` (NEW this session log)
- `.claude/agents/{investigator,implementer,reviewer,cicd-monitor}.md` (+MCP RAG tools)
- `.claude/agent-memory/{investigator,implementer,reviewer,cicd-monitor}/MEMORY.md` (S29 wrap entry FIFO each agent perspective)
- `.claude/agent-memory/implementer/pattern_master_page_mirror.md` (NEW Pattern 16-bis cross-app from Plan CA Chunk B)
- `scripts/plan-ca-verify-menu.{sql,ps1}` + `plan-ca-verify-perms.{sql,ps1}` + `plan-ca-run-perms.ps1` + `plan-b-verify-prod.{sql,ps1}` + `plan-b-run-verify.ps1` (NEW verify scripts)

View File

@ -0,0 +1,116 @@
# Skill + Doc Audit — 2026-05 (late) — Drift trigger sớm
> **Cadence chính:** Cron `solution-erp-skill-audit-monthly` 9:00 ngày 1 mỗi tháng. **Next scheduled:** 2026-06-01.
> **Lần này:** chạy SỚM 2026-05-22 trigger bởi drift threshold §9.4 "+5 gotcha mới → audit cluster" (actual +8: 41→49 từ baseline 2026-05-04).
> **Workflow:** combined audit theo `docs/rules.md §6.4` (doc drift) + `§9.4` (skill staleness).
> **Origin:** em main flagged drift trong session 27 start setup; bro confirm OK proceed curate + audit cùng session.
## Phase 0 — Trigger context
| Metric | 2026-05-04 baseline | 2026-05-22 actual | Δ | Threshold trigger? |
|---|---:|---:|---:|---|
| Migrations | 16 | 31 | +15 | YES — > +5 (major schema) |
| Tests | 83 | 111 | +28 | YES — > +5 |
| Gotchas | 41 | 49 | **+8** | **YES — §9.4 "+5 gotcha mới"** |
| Skills | 6 | 6 | 0 | — |
| Memory user-level | 14 | 23 | +9 | — |
| DB tables | 55 | 59 | +4 | — |
| Active prod users | 18 | 33 | +15 | — (data not skill drift) |
**Decision:** Trigger audit SỚM thay vì đợi 2026-06-01 — drift cluster vượt 30% threshold §9.4.
## Phase 1 — Skill staleness patches applied
5 patches (Edit, KHÔNG rewrite) per rule §6.4 + §6.5:
| File:line | Stale → Patched | Reason |
|---|---|---|
| `.claude/skills/README.md:20` | `26 migration history``31 migration history (Init → RefactorSkipToFinalToApproverLevel)` + Session 23 t1 marker | Mig drift 26→31 |
| `.claude/skills/README.md:90` | `44 bẫy``49 bẫy (S25 +#48 SQLite tie-break, #49 dual-phase UI confusion)` | Gotcha drift 44→49 |
| `.claude/skills/ef-core-migration/SKILL.md:3` (description) | `26 migration sẵn (Init → AddPeLevelOpinionsForV2)``31 migration sẵn (Init → RefactorSkipToFinalToApproverLevel Mig 31 S23 t1 F2 per-Approver-slot swap)` | Description drift + frontmatter description trigger semantic match |
| `.claude/skills/ef-core-migration/SKILL.md:19` (heading) | `## Migration history (26 migration hiện có)``(31 migration hiện có)` | Section heading drift |
| `.claude/skills/ef-core-migration/SKILL.md:80` (Tests note) | `Total 77 test pass / ~3s``Total 111 test pass / ~5s` + cumulative S22-S25 regression breakdown | Test count drift 77→111 (+34) |
| `.claude/skills/ef-core-migration/SKILL.md:258` | `DbSet cho 59 bảng (26 migration)``(31 migration)` | Code pointer drift |
| `.claude/skills/dependency-audit-erp/SKILL.md:153` | `41 bẫy``49 bẫy` + add #48 + #49 cross-ref | Gotcha drift 41→49 |
**Total diff:** ~12 LOC patched. KHÔNG rewrite toàn bộ. KHÔNG cắt narrative gốc.
## Phase 2 — KEEP (rule §6.5 không cắt narrative)
Stale references PRESERVED vì là historical context:
- `contract-workflow/SKILL.md:367` `96→77 test pass (drop 19 N-stage/2-stage legacy)` — historical narrative Mig 21 drastic refactor. KEEP per §6.5 (kể chuyện cumulative).
- `permission-matrix/SKILL.md:16` `Status (post Session 6 — 2026-04-30)` — section header timestamp historical, KHÔNG drift critical.
- `migration-todos.md` mentions `77 test` / `16 mig` — historical session record. KEEP per §6.5.
- `skill-audit-2026-05.md` — historical audit record. KEEP unchanged.
- Session logs `changelog/sessions/*.md` — ALL preserved chronologically. KEEP.
## Phase 3 — Feature gap audit
**Feature mới S20-S26 nào chưa có skill cover?**
| Feature | Period | Skill cover? | Decision |
|---|---|---|---|
| ApprovalWorkflow V2 schema (Mig 22-25) | S17-S18 | Cross-ref `contract-workflow` + `ef-core-migration` Mig history | Existing skills cover, KHÔNG tạo skill mới |
| Per-NV admin opt-in flag pattern (Mig 29-31) | S21-S23 | Memory `feedback_per_nv_permission_scope.md` (user-level) | Pattern reusable cross-project — memory user-level sufficient (admin role + Mig schema discriminator), KHÔNG skill project-local |
| Multi-agent setup 4 sub-agents | S20-S21 | Memory `feedback_multi_agent_setup.md` + `.claude/agents/README.md` | Em main coordination guide — KHÔNG skill (setup-once pattern) |
| RAG Hybrid Cách A global MCP | S26 | Memory `feedback_rag_hybrid_pattern.md` + `feedback_rag_distributed_ownership.md` | Cross-project pattern — memory user-level sufficient |
| Plan AG PE List tree view UI | S26 | Pattern 19 trong `implementer/MEMORY.md` (HTML details + Tailwind named groups + localStorage Set) | Sub-agent memory — KHÔNG tạo skill cho UI pattern 1×fix |
| Mig 28-31 per-NV refactor + Plan N+O 10-surface-point checklist | S21-S23 | `cicd-monitor/MEMORY.md` 10-point checklist promoted to foundation | Cross-agent learning — em đã promote vào cicd-monitor MEMORY.md foundation section curate session 2026-05-22 |
| Gotcha #48 (SQLite tie-break multi-Changelog) | S25 Plan AB | `cicd-monitor/MEMORY.md` gotcha #48 section added curate 2026-05-22 + Reviewer 5-category Cat 5 reinforced | Cross-ref to skill `contract-workflow` (test pattern) — defer to next audit nếu pattern proven cross-module |
| FE merge synthetic audit + userMap fallback (Plan AC2 + AF) | S25 | Memory `feedback_fe_merge_synthetic_audit.md` + `feedback_fe_usermap_fallback.md` (user-level) | Pattern reusable cross-project (Contract V2 + Budget V2 future) — KHÔNG skill |
**KHÔNG tạo skill mới.** Tổng số skill giữ **6** (3 domain + 3 ops). Threshold §9.4 anti-pattern "viết skill chỉ để có thêm" respected.
## Phase 4 — Threshold check + decision
-`+8 gotcha` vượt threshold §9.4 (+5) → trigger audit sớm CHỐT
- ✅ Mig drift 26→31 (+5) trong 3 skill mention → patch 5 vị trí KHÔNG rewrite
- ✅ Test drift 77→111 (+34) trong 1 skill mention → patch 1 vị trí
- ✅ Skill drift < 30% (chỉ ef-core-migration + 2 cross-ref README/dependency-audit-erp)
- Memory 4 agent curate trong session cùng synthesize cross-agent learnings vào foundation sections
## Phase 5 — Validation per §6.5
3 câu sau consolidate (per rule §6.5):
1. **Agent mới đọc 6 tháng sau có biết "tại sao chọn approach này" không?**
narrative Mig 22-31 cumulative + Plan AA/AB/AG context preserved trong session logs + memory + 4 agent MEMORY archive. Skill `ef-core-migration` Phase 9 cross-ref block thêm S22-S25 regression breakdown rationale. 10-surface-point per-NV checklist promoted to cicd-monitor MEMORY foundation cho future per-NV refactor scan.
2. **Có còn cảm giác "kể chuyện" tích lũy không, hay chỉ là instructions khô?**
Skill `ef-core-migration` test note line 80 mở rộng "cumulative S22-S25 regression breakdown: ReturnMode + DraftGuard + Reflection AuthorizePolicy + V2 actor scope reject + per-NV lookup discrimination" giữ rationale tại sao 111 test (vs 77 ). Skill description giữ Mig 31 latest semantic "F2 per-Approver-slot swap" thay plain count.
3. **Gotcha context vẫn còn ý nghĩa khi đọc rời rạc không?**
`dependency-audit-erp` line 153 +#48 SQLite tie-break + #49 dual-phase UI confusion cross-ref. Cicd-monitor MEMORY.md gotcha #48 section full narrative root cause + fix pattern + side benefit (CI test gate catches BEFORE prod). Foundation section preserved.
## Memory curate concurrent (session same as audit)
4 agent MEMORY curate cùng session 2026-05-22 (per HANDOFF S25 wrap flag):
| Agent | Before KB | After KB | Reduction | Archive file |
|---|---:|---:|---:|---|
| cicd-monitor | 72.4 | 15.8 | **-78%** | `archive/2026-05-runs.md` (58.4 KB) |
| implementer | 38.8 | 22.0 | -43% | `archive/2026-05-q1.md` (18.1 KB) |
| investigator | 34.9 | 16.3 | -53% | `archive/2026-05-q1.md` (20.2 KB) |
| reviewer | 34.5 | 17.9 | -48% | `archive/2026-05-q1.md` (18.3 KB) |
| **Total** | **180.6** | **72.0** | **-60%** | All archives 115 KB preserved |
All 4 MEMORY now under 25KB curate threshold. Rule §6.5 KEEP narrative compliance: archive files preserve FULL verbose entries cho cross-session audit retrieve. Foundation patterns + 5-category checklist + recurring gotcha patterns kept inline.
## Đề xuất next audit
- **Cron 2026-06-01** re-trigger combined audit (cadence chính) KHÔNG conflict với late audit này (sớm 10 ngày), chỉ re-verify drift mới phát sinh sau S27+.
- **Threshold trigger sớm** nếu:
- +5 gotcha mới cluster (Plan B Contract V2 wire thể phát sinh #50+)
- Plan B Contract V2 wire complete (drastic schema change cross 4 entity Mig 32+33 estimate)
- Skill drift > 30% (count broken / path broken)
- 4 agent MEMORY vượt 25KB threshold trở lại (next curate trigger trong HANDOFF)
## Stats
- Total skill: 6 (no add/archive)
- Files patched: 3 SKILL.md (ef-core-migration + dependency-audit-erp + README)
- Lines diff: ~+12 / ~7 patch
- Memory curate concurrent: 4 file -60% size cumulative
- Effort: ~30 phút audit + ~1.5h memory curate (em main solo, không spawn sub-agent — judgment scope per Cognition "writes single-threaded")
- Audit log: 1 page (file này, theo cadence rule §6.4 — KHÔNG rewrite existing skill-audit-2026-05.md)

View File

@ -836,6 +836,50 @@ var changelog = await db.PurchaseEvaluationChangelogs
- File: `fe-user/src/components/pe/PeDetailTabs.tsx:1995-2070` (ApprovalsTab + decisionBadge + extractNextTargetHint)
- Mirror: `fe-admin/src/components/pe/PeDetailTabs.tsx`
### 51. INFRASTRUCTURE seed vs DEMO seed phân biệt — `DemoSeed:Disabled` flag gate trap (Session 29 Plan B Chunk A2 Hotfix CICD)
**Triệu chứng:** Plan B Chunk A2 Implementer scaffold `SeedSampleContractWorkflowV2Async` mirror PE `SeedSampleApprovalWorkflowsV2Async` pattern — nested inside `if (!demoSeedDisabled)` branch trong `DbInitializer.cs:105-111`. Prod has `DemoSeed:Disabled=true` (Plan T S23 t10 — UAT permanent clean slate). Run #231 PASS deploy NHƯNG `QT-HD-V2-001` workflow KHÔNG seed prod. CICD Monitor (agentId a2ea2e3a) verify Stage 4 catch smoking gun log: `"DemoSeed:Disabled=true → skip workflow + contracts + PE + sample V2 seed (Plan T S23 t10 + Plan B Chunk A2 Contract V2)"`. → Drafter Workspace dropdown V2 EMPTY → V2 contract path **BLOCKED end-to-end UAT**.
**Root cause:** Implementer mirror PE pattern (which IS gated for valid reason — PE workflow A/B already seeded V1 historically) → áp dụng nguyên xi cho Contract V2 (no V1 sample existed) → V2 path không có default workflow để Drafter pick.
**Fix Hotfix CICD (`38f1c4d`):** PROMOTE `SeedSampleContractWorkflowV2Async` ra ngoài `DemoSeed` gate:
```csharp
if (!demoSeedDisabled)
{
await SeedDemoContractsAsync(...);
await SeedDemoPurchaseEvaluationsAsync(...);
await SeedSampleApprovalWorkflowsV2Async(...); // PE: gated (historical V1 sample existed)
}
// [Plan B S29 2026-05-22 Hotfix CICD] OUT of gate
await SeedSampleContractWorkflowV2Async(...); // INFRASTRUCTURE — V2 path requires
```
**Pattern reusable — Seed classification table:**
| Category | Examples | Gate? |
|---|---|---|
| **INFRASTRUCTURE always run** | Roles, Departments, Catalogs, MenuTree, AdminPermissions, ContractTemplates, **SampleWorkflowV2 (V2 path requires)** | NO gate |
| **DEMO gated** | DemoUsers (30 sample, Plan T disabled prod), DemoContracts ([DEMO]), DemoPE ([DEMO]), SampleApprovalWorkflowsV2 (historical PE A/B legacy) | `if (!demoSeedDisabled)` |
**Decision tree khi add new Seed method:**
1. Production có cần seed này để work end-to-end không?
- YES → INFRASTRUCTURE (no gate)
- NO → DEMO (gated)
2. Có safe để admin edit/delete/disable không?
- YES → INFRASTRUCTURE OK (admin sửa qua Designer khi cần)
- NO → architect re-think (immutable seed = anti-pattern)
**Bonus — Smart Friend ROI:** CICD Monitor catch BEFORE bro UAT 401/empty experience. Cumulative pattern proven 4× S22 #44 + S25 #48 + S29 Plan B Reviewer #ApplicableType + S29 Plan B CICD #DemoSeed.
**Phòng tránh tương lai:** Khi Implementer mirror PE V2 pattern cho new module (Budget V2, Notification V2), explicit check question: "PE pattern gated/ungated, có applicable cho new module không?" — KHÔNG copy nguyên xi DemoSeed gate placement.
**References:**
- Hotfix CICD commit: `38f1c4d` (Run #232 PASS 3m33s)
- File: `src/Backend/SolutionErp.Infrastructure/Persistence/DbInitializer.cs:105-122`
- Cross-ref: Plan T S23 t10 DemoSeed flag (gotcha implicit)
- Memory cross-ref: `feedback_demo_seed_flag_disable.md` (Plan T pattern)
### 50. Page move cross-app — `Layout.tsx` `resolvePath` staticMap missed mirror → silent sidebar drop (Session 29 Plan CA Hotfix 1)
**Triệu chứng:** Plan CA move 4 master pages từ fe-admin → fe-user (commit `06a441c` Implementer Case 2). BE `/api/menus/me` return tree đầy đủ (Master + 4 children + Catalogs sub-tree 4 children, ALL CanRead=true). Admin login eoffice → sidebar group "DANH MỤC" expand chỉ thấy "Danh mục chi tiết" (Catalogs), **3 leaf Suppliers/Projects/Departments + 4 sub-catalogs biến mất silent**.
@ -904,4 +948,5 @@ const staticMap: Record<string, string> = {
23. Nếu Gitea Actions API trả 404 trên `/actions/runs` → đúng path là `/actions/tasks` (Gitea naming khác GitHub). Cache `updated_at` stale ~2 min → cross-check VPS file LastWriteTime cho time-sensitive verify (#46)
24. Nếu test `OrderByDescending(CreatedAt).First()` query audit table fail sau add Changelog mới → SQLite frozen-clock tie-break, MUST filter Summary/EntityType discriminator (#48)
25. Nếu page move cross-app (Implementer Case 2) nhưng menu leaf KHÔNG hiện sidebar dù BE trả permission OK → check `Layout.tsx` `resolvePath` staticMap miss key mapping → MenuLeaf null guard silent drop (#50). 4-place mirror checklist: page + Routes + menuKeys.ts + Layout.tsx staticMap
26. Nếu new Seed method KHÔNG chạy prod dù dotnet build PASS + deploy SUCCESS → check nested inside `if (!demoSeedDisabled)` gate (Plan T S23 t10 flag enabled prod) → INFRASTRUCTURE seed phải PROMOTE OUT of DemoSeed gate (#51). Decision tree: production cần để work end-to-end? YES → ungate
25. Nếu UI audit list show `Đã gửi duyệt → Đã gửi duyệt` lặp gây nhầm → drop dual-phase badge khi state machine self-loop, thay Decision badge + next-target hint parse từ comment (#49)

View File

@ -0,0 +1,892 @@
# RAG Unified — Onboarding Guide cho dự án mới
> **Phiên bản:** 1.0 (2026-05-22) — chốt bởi em main SOLUTION_ERP S26+S27.
> **Pattern:** Approach B Distributed Bootstrap + Option 4a Manual PowerShell Scripts (per memory user-level `feedback_rag_distributed_ownership.md`).
> **Audience:** Em main project Y/Z (NamGroup / DH Y Dược / Ashico / Vipix / dự án mới khác) khi anh pqhuy mở Claude Code session.
> **Cấu trúc nguồn:** SOLUTION_ERP em main đã pioneer setup `D:\.claude-rag\` infrastructure + 4 PS scripts + custom Dashboard. Project khác **TỰ BOOTSTRAP** theo guide này (~10 phút mỗi project), KHÔNG em main project khác làm hộ.
---
## §1 TL;DR (Quick start — 3 phút)
Anh pqhuy đã có RAG infrastructure global ở `D:\.claude-rag\` (Voyage AI + Qdrant Windows native + FastMCP). Em main project X mới chỉ cần 3 bước:
```powershell
# 1. Verify Qdrant + MCP server running (em pioneer đã setup)
rag-status
# 2. Tạo .claude/rag.json trong project root (template §4)
# 3. Bootstrap corpus
python D:\.claude-rag\bootstrap.py --project <project_id>
```
Restart Claude Code → 6 MCP tools `mcp__rag-unified__*` khả dụng trong session project mới.
**Em main project mới responsibility:**
- Tạo `.claude/rag.json` declare project_id + corpus_paths + sensitivity flag
- Run bootstrap.py ~5 phút embed
- Brief 4 sub-agent (nếu có setup multi-agent) qua MEMORY.md
- KHÔNG modify `D:\.claude-rag\server.py` (shared infrastructure)
---
## §2 Background — Tại sao Approach B Distributed?
### Bối cảnh
Anh pqhuy dev solo, có 5+ dự án cùng máy localhost (SOLUTION_ERP + NamGroup + DH Y Dược + Ashico + Vipix + tương lai). Mỗi project có 100-2000 file MD docs + agent memory.
### Quyết định chốt (per memory `feedback_rag_distributed_ownership.md`)
| Approach | Verdict |
|---|---|
| A. Em main SOLUTION_ERP làm hộ tất cả 4 project | ❌ Vi phạm Cognition "writes single-threaded" - em không có context project khác |
| **B. Distributed - 4 em main project khác tự bootstrap khi mở session** | ✅ **CHỌN** - đúng boundary, ownership rõ, sensitivity per project tự decide |
| C. Hybrid em làm 1 mẫu + 3 tự follow | 🤔 Vẫn còn cross-boundary 1 lần |
### Lý do CHỌN Approach B
1. **Cognition "writes single-threaded" principle** - mỗi em main own context flow project mình
2. **Em main project khác hiểu codebase sâu hơn** - SOLUTION_ERP em không có context active NamGroup
3. **Sensitivity per project** - vd DH Y Dược client confidential, em main DH biết set `share_to_global=false`. Em main SOLUTION_ERP KHÔNG biết → có thể set sai → leak risk
4. **Self-ownership re-bootstrap** - khi project Y thêm 50 file mới, em main Y biết để re-bootstrap. Em main X không monitor được Y
5. **Knowledge propagation natural** - em main Y bootstrap → `store_memory` pattern → `share_to_global=true` → các em main khác hưởng lợi qua RAG cross-share
---
## §3 Pre-check (Verify infrastructure global đã sẵn sàng)
Trước khi bootstrap, em main project mới CHECK:
```powershell
# 1. Aliases hoạt động (sẽ load $PROFILE auto)
rag-status
```
Output kỳ vọng:
- `[Qdrant Server] Status: Running PID xxx port 6333` (em pioneer đã start)
- `[MCP Server] Registration: rag-unified [OK] Connected (user-level)`
- `[Voyage AI] API key set: [OK]`
**Nếu Qdrant DOWN:**
```powershell
rag-start # Em pioneer setup, just start
```
**Nếu MCP NOT REGISTERED:** Em main project mới KHÔNG self-register. Báo anh pqhuy để re-add user-level (1 lần duy nhất per Windows user):
```powershell
claude mcp add -s user -e "VOYAGE_API_KEY=$env:VOYAGE_API_KEY" -- rag-unified python "D:\.claude-rag\server.py"
```
**Nếu API key MISSING:**
```powershell
[Environment]::SetEnvironmentVariable("VOYAGE_API_KEY", "pa-MiN...", "User")
# Restart Claude Code để pick up env var
```
---
## §4 Bootstrap 3 steps cho project mới
### Step 1: Tạo `.claude/rag.json` ở project root
Template generic:
```json
{
"project_id": "<kebab-or-snake-case-id>",
"display_name": "<Tên đầy đủ project>",
"corpus_paths": [
"docs/**/*.md",
".claude/agent-memory/**/MEMORY.md",
".claude/skills/**/SKILL.md"
],
"exclude_paths": [
"docs/_archive/**",
"node_modules/**",
"bin/**",
"obj/**",
".git/**"
],
"extra_corpus": [
"C:\\Users\\pqhuy\\.claude\\projects\\<project-folder>\\memory\\*.md"
],
"share_to_global": true,
"search_scope_default": ["self", "shared_global"],
"auto_reindex": true,
"chunk_size": 1500,
"chunk_overlap": 200,
"contextual_retrieval": true
}
```
**Checklist customize:**
| Field | Hướng dẫn |
|---|---|
| `project_id` | Lowercase + underscore. Ví dụ: `namgroup_main`, `dh_y_duoc`, `ashico_erp`, `vipix_multisite`. Phải UNIQUE cross 5 project. |
| `display_name` | Tên đầy đủ cho dashboard hiển thị, vd `"NamGroup - HRM"` |
| `corpus_paths` | Glob patterns relative project root. Default 3 path cover docs + agent memory + skills. Tăng thêm nếu project có folder docs khác (vd `tests/specs/**/*.md`). |
| `exclude_paths` | KHÔNG embed file tạm/binary/archive |
| `extra_corpus` | Memory user-level Claude Code (path absolute). Folder: `C:\Users\pqhuy\.claude\projects\<your-project-folder-name>\memory\*.md` |
| `share_to_global` | **CRITICAL DECISION:** `true` = chunks promote-able to shared_global collection (cross-project search visible). `false` = client confidential, chỉ self-project access. **Em main project decide based on data sensitivity** (vd DH Y Dược client → `false`, NamGroup internal HRM → `true`). |
| `search_scope_default` | Default `["self", "shared_global"]` - cân bằng project-specific + cross-project pattern reuse |
| `chunk_size` / `chunk_overlap` | Default 1500/200 chars - đã tune cho MD docs Vietnamese. KHÔNG đổi unless project có docs structure khác hẳn. |
| `contextual_retrieval` | `true` - Anthropic Contextual Retrieval prepend "From <doc> > <heading>:" - +15% recall |
### Step 2: Run bootstrap
```powershell
python D:\.claude-rag\bootstrap.py --project <project_id>
```
Expected output:
```
[1/4] Scanning corpus... Found N files
[2/4] Chunking + contextual prepend... Total chunks: M
Estimated tokens: ~XXX,XXX (Voyage free tier: 200M/month)
[3/4] Voyage-4-large embedding + Qdrant upsert + SQLite BM25... Upserted M chunks in T seconds
[4/4] Update registry...
[OK] Bootstrap complete: <project_id> ready (M chunks)
```
**Performance benchmark (SOLUTION_ERP 2026-05-22):**
- 126 files → 2,628 chunks → ~484K tokens → 60.9s embed + upsert
- 2nd bootstrap với cache hit: 6.5s (10× speedup)
### Step 3: Verify
```powershell
rag-status # See new project in [Collections] section
rag-dashboard # Visual verify via browser
```
Test query in Claude Code session (project mới):
```
search_memory("pattern X tôi đã làm chưa")
```
Should return chunks từ project mới + shared_global (nếu có).
---
## §5 6 MCP Tools matrix
| Tool | Scope | Use case |
|---|---|---|
| `search_memory(query, scope, top_k, use_rerank)` | `self` / `shared_global` / `all_projects` / `default` | Tìm pattern, gotcha, narrative trong project hiện tại + shared |
| `cross_project_search(query, top_k)` | All projects | "Pattern X tao đã làm ở dự án nào trước chưa?" - explore cross-project knowledge |
| `search_code(query, file_pattern, top_k)` | Self only (BM25-heavy) | Tìm exact symbol/function name trong source code |
| `store_memory(content, source_path, tags)` | Self | Save pattern on-the-fly cuối session, không cần re-bootstrap |
| `promote_to_shared(chunk_id)` | Self → shared_global | Pattern proven cross-project ≥2x use → visible to all projects (v0 stub, defer v1) |
| `list_projects()` | All registry | Overview projects + collection chunk counts |
**Em main project mới brief 4 sub-agent (nếu có setup):**
| Agent | Skills preload + RAG access |
|---|---|
| 🟦 Investigator | Auto-detect cwd → project_id → `mcp__rag-unified__search_memory(scope="self")` cho research patterns |
| 🟨 Implementer | Same auto-detect + `search_code` cho symbol lookup khi cookie-cutter mirror |
| 🟥 Reviewer | Same auto-detect + `cross_project_search` để verify pattern proven elsewhere trước commit |
| 🟩 CICD Monitor | Same + `list_projects` cho infra state checks |
---
## §6 4 Sub-agent setup brief (optional - cho project ≥10K LOC, ≥3 month)
Per memory `feedback_multi_agent_setup.md` - setup multi-agent CHỈ khi pass 6/6 decision gate. SOLUTION_ERP S20 turn 12 đã match (59 tables + 142 endpoints + 6 skills + 44 gotchas).
**Pattern reusable cho project mới:**
1. Tạo `.claude/agents/` folder + 4 `.md` files (Investigator + Implementer + Reviewer + CICD Monitor)
2. Tạo `.claude/agent-memory/<name>/MEMORY.md` seed per agent
3. Copy template từ SOLUTION_ERP `.claude/agents/README.md` (master coordination guide)
4. Customize project-specific:
- Skills preload (tùy stack: .NET / React / Python / Go)
- Domain entities + workflow patterns
- Live prod URLs + bearer tokens
- Gotcha catalog reference
5. End-of-session: 4 agent flush MEMORY.md updates → em main synthesize cross-agent learnings → integrate vào memory + session log
**Reference SOLUTION_ERP files (paste-friendly templates):**
- `.claude/agents/README.md` - master coordination guide với 6-criteria decision gate
- `.claude/agents/investigator.md` - frontmatter + system prompt template
- `.claude/agents/implementer.md` - REFUSE strict 6-criteria
- `.claude/agents/reviewer.md` - 5-category checklist + Smart Friend guard
- `.claude/agents/cicd-monitor.md` - 5-stage post-deploy verify
---
## §7 Permission + Security
### MCP user-level scope (1 lần setup per Windows user)
`claude mcp add -s user` registers `rag-unified``%USERPROFILE%\.claude.json` - persist across ALL Claude Code sessions cho user pqhuy. Bất kỳ project nào pqhuy mở đều có access 6 tools (KHÔNG cần re-register).
### VOYAGE_API_KEY scope
`[Environment]::SetEnvironmentVariable("VOYAGE_API_KEY", "...", "User")` - Windows User registry, persist boot-to-boot. KHÔNG commit vào git. KHÔNG share cross-machine.
### `share_to_global` flag (per-project)
- `true` → chunks có thể promote vào `shared_global` collection → cross-project search visible (em main khác có thể thấy)
- `false` → strict isolated cho project (client confidential, NDA, regulated data)
**KIẾN NGHỊ:** Em main project mới BẮT BUỘC review sensitivity TRƯỚC khi bootstrap. Vd:
- DH Y Dược (client confidential): `share_to_global=false` + exclude paths chứa client name
- NamGroup (internal HRM): `share_to_global=true` ok
- Vipix multisite (public docs): `share_to_global=true`
---
## §8 Workflow daily — Em main project mới
```powershell
# Morning - mở session Claude Code project mới
rag-status # Check infra OK
rag-dashboard # Visual sanity check
# Trong session - search context
search_memory("workflow X") # MCP tool call trong session
cross_project_search("pattern Y") # Find proven pattern elsewhere
# End of session - save new pattern
store_memory("Pattern Z chốt 2026-XX-XX...", tags=["pattern", "<topic>"])
# Periodic - re-bootstrap khi docs thay đổi nhiều
python D:\.claude-rag\bootstrap.py --project <project_id> --re-embed
```
### Khi nào re-bootstrap?
- Sau session lớn thêm > 20 file MD docs mới
- Sau drastic refactor MD structure (rename folders, restructure)
- Monthly cadence per `feedback_md_compact_narrative.md` audit + curate cycle
### Khi nào KHÔNG re-bootstrap (cache hit)?
- File MD chỉ patched < 30% content - bootstrap re-embed sẽ skip chunks unchanged (SHA256 cache hit)
- Session khác không touch corpus paths
---
## §9 Troubleshooting
### Q: `rag-status` báo Qdrant DOWN nhưng anh đã `rag-start`?
A: Check 30s timeout - Qdrant cần ~10s warmup. Retry sau:
```powershell
Start-Sleep -Seconds 10
Invoke-RestMethod -Uri "http://localhost:6333/healthz" -TimeoutSec 10
```
Nếu vẫn DOWN, check logs:
```powershell
Get-Content "D:\.claude-rag\logs\qdrant-$(Get-Date -Format 'yyyyMMdd').log" -Tail 30
```
### Q: MCP server `rag-unified` NOT CONNECTED?
A: 3 nguyên nhân thường gặp:
1. **VOYAGE_API_KEY chưa load vào Claude Code env** Restart Claude Code completely (close all windows)
2. **Python deps thiếu** `pip install fastmcp voyageai qdrant-client watchdog python-dotenv`
3. **MCP đăng ký sai path** Check `claude mcp list` - phải dòng `rag-unified: python D:\.claude-rag\server.py - [OK] Connected`
### Q: `bootstrap.py` báo Voyage rate limit hoặc 429?
A: Free tier 200M tokens/month chia equally across requests. Nếu hit limit:
- Wait 1 phút retry (Voyage rate limit window ~60s)
- Batch nhỏ hơn: `--batch-size 32` thay default 128
- Check Voyage dashboard: https://dash.voyageai.com/
### Q: SQLite FTS5 query error `no such column: in`?
A: Gotcha #48 cousin - reserved keyword. `_sanitize_fts5_query()` strip non-alphanumeric. Nếu vẫn fail, simplify query (remove `-`, `:`, `()`)
### Q: Qdrant batch upsert WinError 10053 "connection aborted"?
A: Batch quá lớn. `lib/retrieval.py:UPSERT_BATCH = 64` đã limit. Nếu vẫn fail, giảm xuống 32.
### Q: HuggingFace symlinks warning Windows?
A: Non-critical (cache works degraded efficiency). Suppress: `$env:HF_HUB_DISABLE_SYMLINKS_WARNING=1`
---
## §10 Tunings (advanced)
### Tăng recall (sacrifice cost)
```json
{
"chunk_size": 1000, // smaller chunks = more granular match
"chunk_overlap": 300, // higher overlap = catch boundary patterns
"use_rerank_default": true // rerank-2.5 = +20-30% recall vs vector-only
}
```
Cost trade-off: smaller chunks = N% more tokens to embed + rerank.
### Giảm cost (sacrifice recall)
```json
{
"chunk_size": 2500,
"chunk_overlap": 100,
"use_rerank_default": false // rely on vector + BM25 only
}
```
Saving: ~30% Voyage tokens. Recall drop ~10-15%.
### BM25 weight tuning
Default RRF k=60 (Reciprocal Rank Fusion). Tăng `k` = giảm BM25 dominance, tăng vector. Em main project mới KHÔNG tune trừ khi golden benchmark dataset.
---
## §11 Best practices cross-project
1. **`store_memory` defensive** - cuối session save new pattern proven, không đợi monthly bootstrap
2. **Cross-project search BEFORE design** - `cross_project_search("pattern X")` check trước khi implement mới
3. **Sensitivity audit per session** - khi push file mới client info confirm `share_to_global` flag đúng
4. **Dashboard daily** - mở `rag-dashboard` mỗi sáng để verify infra healthy + collection counts không drop bất thường
5. **Document `.claude/rag.json` decisions** - comment trong file do chốt sensitivity flag, exclude paths
6. **Cron monthly audit cadence** - per rule §6.4 SOLUTION_ERP, project mới adopt monthly cron skill+doc audit. Drift trigger sớm nếu +5 gotcha mới
---
## §12 Status Monitoring + Manual Control (Option 4b NSSM Windows Service ⭐ UPGRADED S27)
### Upgrade S27: Qdrant chạy as Windows Service (NSSM)
Phiên bản S26 dùng Option 4a (manual PS scripts only). **S27 upgrade lên Option 4b** với NSSM Windows Service:
| | Option 4a (S26) | **Option 4b NSSM (S27 RECOMMEND)** |
|---|---|---|
| Auto-start timing | Sau anh login Windows (Startup folder) | Boot-time (trước login) |
| Auto-restart on crash | Stay crashed | Auto-respawn 3s delay |
| Survive logout/login | Stop khi logout | Service runs background |
| Control | `rag-start/stop/status/dashboard` | Same + `Get-Service Qdrant` native |
| Setup once | ~30 phút | ~35 phút (+ install NSSM elevated) |
| OOM recovery (vd 8.4MB OOM em pioneer S27) | Manual rag-restart | Auto-respawn |
**Setup elevated PowerShell (1 lần duy nhất):**
```powershell
# 1. Download NSSM 2.24 (~3 MB, may retry nssm.cc 503 transient)
$nssmDir = "D:\.claude-rag\nssm"
Invoke-WebRequest -Uri "https://nssm.cc/release/nssm-2.24.zip" -OutFile "$nssmDir\nssm-2.24.zip"
Expand-Archive "$nssmDir\nssm-2.24.zip" $nssmDir -Force
Copy-Item "$nssmDir\nssm-2.24\win64\nssm.exe" "$nssmDir\nssm.exe"
# 2. Run install script (em pioneer write D:\.claude-rag\scripts\install-service.ps1)
& "D:\.claude-rag\scripts\install-service.ps1"
```
Script tự động: stop manual qdrant + register service + AppDirectory + log paths + Start SERVICE_AUTO_START + AppExit Default Restart 3s delay + DisplayName "Qdrant Vector DB (RAG Unified)" + Description + start service + health verify.
**Verify post-install:**
```powershell
Get-Service Qdrant # Should show Status: Running, StartType: Automatic
```
**Troubleshooting nếu start fail (WAL lock conflict):**
```powershell
# Pre-existing qdrant.exe manual holding WAL → kill + retry
& "D:\.claude-rag\scripts\fix-service-start.ps1" # elevated
```
### Aliases trong $PROFILE (~30 phút first-time setup)
```powershell
function rag-start { & "D:\.claude-rag\scripts\start.ps1" } # Start Qdrant background
function rag-stop { & "D:\.claude-rag\scripts\stop.ps1" } # Graceful stop
function rag-status { & "D:\.claude-rag\scripts\status.ps1" } # Full status report 6 section
function rag-dashboard { & "D:\.claude-rag\scripts\dashboard.ps1" } # Custom HTML dashboard + open browser
function rag-restart { & "D:\.claude-rag\scripts\stop.ps1"; Start-Sleep 2; & "D:\.claude-rag\scripts\start.ps1" }
function rag-boot { & "D:\.claude-rag\scripts\boot.ps1" } # Manual trigger boot sequence
```
### 6 PS scripts khả dụng (em pioneer SOLUTION_ERP đã build — Service mode S27)
| Script | Mục đích | Output |
|---|---|---|
| `start.ps1` | `Start-Service Qdrant` (needs Admin if stopped) | Service status + PID + RAM |
| `stop.ps1` | `Stop-Service Qdrant -Force` (needs Admin) | Status verification |
| `status.ps1` | Full status: Service + Process + MCP + Voyage + Collections + Disk + Agent MEMORY | Terminal 6 section colored (no Admin) |
| `dashboard.ps1` | Generate custom HTML dashboard (STATIC snapshot) + open browser | `D:\.claude-rag\dashboard.html` |
| `boot.ps1` | Post-login: verify service Running + regen Dashboard + open browser | Used by Startup folder shortcut |
| `install-service.ps1` | NSSM register Qdrant as Windows Service (1 lần) | Service Running + Auto-start verified |
| `fix-service-start.ps1` | Recover service after WAL lock conflict (rare) | Service Running verified |
### Custom MCP Dashboard
**URL:** `file:///D:/.claude-rag/dashboard.html` (auto-generated, auto-open by `rag-dashboard` command)
**IMPORTANT (S27 fix):** Dashboard HTML **STATIC SNAPSHOT** tại thời điểm anh chạy `rag-dashboard`. **KHÔNG auto-poll Qdrant API live** browser meta refresh chỉ reload HTML cached, KHÔNG re-run PS script. Muốn data live mở **Qdrant Native Dashboard** (`http://localhost:6333/dashboard`) fetch API real-time. Custom Dashboard chỉ dùng cho ecosystem overview (Service+MCP+Voyage+Agent MEMORY combined panel Qdrant native không ).
**Panels:**
1. **Qdrant Vector DB** - PID/version/uptime/RAM/storage + link Qdrant native dashboard
2. **MCP Server (FastMCP)** - Registration status + 6 tools matrix
3. **Collections** - List all `proj_*` + `shared_global` chunk counts
4. **Voyage AI** - API key + scope + embedding/rerank model + free tier
5. **System** - Disk usage + Python version + paths
6. **Agent MEMORY** - 4 agent MEMORY.md sizes (SOLUTION_ERP) with curate threshold warnings
7. **Recent Qdrant Logs** - last 10 lines
**Auto-refresh:** 60s via `<meta http-equiv="refresh" content="60">`. Manual refresh via "Refresh Now" button.
**Customization cho project mới (optional):**
- Edit `dashboard.ps1` Section 6 `$memDir` path point to project mới `.claude/agent-memory/`
- Add custom panels (vd Section "Recent commits" via `git log --oneline -10`)
- Color palette: change `#f59e0b` (orange) project brand color
### Auto-start on Windows boot (S27 — 2-layer)
**Layer 1 — NSSM Windows Service (auto-start Qdrant):**
- Service `Qdrant` StartType=Automatic boot-time auto-start (trước login)
- Auto-restart on crash (AppExit Default Restart 3s delay)
- Survive logout/login
**Layer 2 — Startup folder shortcut (auto-open Dashboard sau anh login):**
- Path: `%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\rag-boot.lnk`
- Target: `powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "D:\.claude-rag\scripts\boot.ps1"`
- `boot.ps1` S27 updated: verify service Running (KHÔNG start lại) + regen Dashboard + open browser
- Disable: Delete `.lnk` file (Qdrant vẫn auto-start by service)
### Qdrant Native Dashboard (built-in FREE — Web UI v0.2.12)
URL: **`http://localhost:6333/dashboard`** (browse Qdrant collections, query playground, metrics)
**Yêu cầu trước:** Em pioneer SOLUTION_ERP đã download Web UI static files vào `D:\.claude-rag\qdrant-bin\static\` (S27 hotfix - Qdrant Windows binary zip không bundle Web UI). Nếu native dashboard 404, xem Gotcha #4 §A2 để re-download.
**Khác biệt Native Qdrant vs Custom MCP Dashboard:**
| | Qdrant Native | Custom MCP (em pioneer build) |
|---|---|---|
| URL | `http://localhost:6333/dashboard` | `file:///D:/.claude-rag/dashboard.html` |
| Mục đích | Browse collections + query playground + metrics | Status overview RAG ecosystem |
| Panels | Collections, Console, Cluster, Snapshots | Qdrant+MCP+Voyage+Agent MEMORY+Logs |
| Auto-refresh | Yes (Qdrant native polling) | 60s meta refresh |
| Khi dùng | Khi cần inspect collection data, run test query | Khi cần check overall health + 4 agent MEMORY sizes + Voyage token usage |
**Bookmark cả 2** complementary, không thay thế nhau.
---
## §13 Approach B Distributed Workflow (Em main pioneer KHÔNG làm hộ)
### Responsibility split
**Em main pioneer (SOLUTION_ERP):**
- Build infrastructure `D:\.claude-rag\` (Qdrant binary + FastMCP server + lib + scripts)
- Build 4 PS scripts (start/stop/status/dashboard) + boot.ps1
- Custom MCP Dashboard HTML generator
- $PROFILE aliases setup
- Startup folder shortcut auto-boot
- Bootstrap SOLUTION_ERP corpus (proj_solution_erp)
- Xuất guide này (file `rag-onboarding-guide.md`)
- **KHÔNG bootstrap data cho project Y/Z khác** - đó việc của em main project đó
**Em main project Y/Z (NamGroup / DH Y Dược / Ashico / Vipix / khác — khi anh pqhuy mở Claude Code project đó):**
1. **Đọc guide này** (5 phút) - paste vào `docs/guides/rag-onboarding-guide.md` của project nếu chưa
2. **Tự tạo `.claude/rag.json`** - em main project hiểu CONTEXT cụ thể (project_id, corpus paths, sensitivity flag)
3. **Tự run bootstrap.py** ~5 phút embed
4. **Test query verify** hoạt động
5. **Brief 4 sub-agent của project đó** qua MEMORY.md (nếu setup multi-agent)
### Pattern reusable cross-project
**Setup-once, scale-distributed pattern** cho bất kỳ multi-project shared infrastructure (RAG + MCP + future tools):
```
1. Em main project pioneer (P1 = SOLUTION_ERP) build infra + xuất onboarding MD
2. P1 KHÔNG executable cho P2/P3/.../Pn
3. Em main P2/P3/.../Pn tự đọc MD + apply khi anh pqhuy mở session project đó
4. Cumulative knowledge graph natural - mỗi em main own context, share qua shared_global collection
```
### Anti-patterns avoided
- Em main project X bootstrap data cho project Y/Z - vi phạm Cognition boundary
- Hardcode `.claude/rag.json` cho project khác trong em main X - em không biết sensitivity
- Task Scheduler auto-start không cần (RAM cost + lose control - anh chọn manual scripts thay)
- Embed config decisions qua chat conversation - KHÔNG vào RAG, mất khi session crash
---
## §A3 Context loading Hybrid pattern — em main workflow optimization ⭐ S27 NEW
> **Audience:** Em main Opus project Y/Z (NamGroup / DH Y Dược / Ashico / Vipix / khác). Áp dụng cho mỗi session daily ops.
> **Origin:** Memory user-level `feedback_rag_hybrid_pattern.md` (Cách A defensive). Bài học S27 SOLUTION_ERP em pioneer.
### §A3.1 — Why Hybrid (Cách A defensive vs Cách B aggressive)
Em main Opus 4.7 **1M token context window** (qua `model: inherit` từ parent). KHÔNG load hết tất cả MD vào blanket wasteful + context rot.
| Cách | Blanket size | RAG usage | Decision quality | UX latency | Khi dùng |
|---|---|---|---|---|---|
| **Cách A defensive (CHỌN)** | ~120K | Supplement retrieve on-demand | 90% (control flow strong) | Smooth | **Project MD > 1M tokens cumulative** |
| Cách B aggressive | Cắt 60-70% | Cascade retrieve mỗi task | 75-80% (fragmented) | +1-2s mỗi state question | KHÔNG khuyến nghị |
| Skip RAG | Blanket all | Zero | Best decision quality | Best UX | Project MD < 200K tokens |
**Decision gate cho project mới:**
- MD < 200K tokens Skip RAG entirely
- MD 200K-1M Lazy blanket sufficient, RAG nice-to-have
- **MD > 1M → RAG MANDATORY** (vượt context window blanket)
### §A3.2 — Layer 1: Blanket auto-load (mỗi session start)
Auto-injected vào em main session khi anh mở Claude Code project:
| Component | Estimate KB | Always loaded? |
|---|---|---|
| System prompt + tool definitions | 15-20 | ✅ Always |
| `CLAUDE.md` (project root pointer) | 1-3 | ✅ Always |
| `docs/CLAUDE.md` (full project context) | 8-15 | ✅ Always |
| `docs/STATUS.md` Recently Done snapshot | 30-60 (oversize warn) | ✅ Always (per CLAUDE.md priority) |
| `docs/HANDOFF.md` brief 5 phút | 80-90 (oversize warn) | ✅ Always |
| User memory `MEMORY.md` index | 3-5 | ✅ Always |
| 6 skill descriptions | 3 | ✅ Always |
| 4 sub-agent definitions + first 200 lines MEMORY each | 50-80 | ✅ Always (post CLI restart pickup model:inherit) |
| **Tổng blanket estimate** | **~200-280 KB ≈ 50-70K tokens** | |
**KHÔNG auto-load (cần RAG retrieve hoặc Read direct):**
- Session logs cũ `docs/changelog/sessions/*.md` (~30+ files cumulative)
- Skill SKILL.md full content (chỉ description in blanket)
- Memory user-level full content (chỉ index)
- Source code BE/FE
- Archive folders `.claude/agent-memory/*/archive/*`
- Migration scripts old + DbInitializer
### §A3.3 — Layer 2: RAG retrieve decision tree
```
Em main cần thông tin → CHỌN tool:
├── Cần PATTERN proven từ session cũ / cross-project?
│ → mcp__rag-unified__search_memory("pattern X", scope="default")
│ Vd: "per-NV admin opt-in flag pattern", "FE merge synthetic audit"
├── Đang implement feature mới — kiểm tra đã làm ở project nào trước chưa?
│ → mcp__rag-unified__cross_project_search("feature Y")
│ Vd: "audit recovery pattern", "Workflow N-stage state machine"
├── Cần lookup EXACT function/class/method trong source code?
│ → mcp__rag-unified__search_code("ApproveV2Async signature")
│ BM25-heavy, exact symbol match. NOT cho semantic question.
├── Cần GOTCHA cụ thể đã gặp?
│ → search_memory("gotcha #48 SQLite tie-break") OR Read docs/gotchas.md direct
│ Decision: < 5 gotcha lookup/session → Read direct. > 5 → search_memory.
├── Cần SESSION LOG cụ thể S22-S26 history?
│ → search_memory("S23 Plan K F2 refactor") thay vì Read full session log 15-25KB
│ Saves 80-90% tokens per lookup
├── End-of-session phát hiện NEW pattern reusable?
│ → mcp__rag-unified__store_memory(content, tags=["pattern", "<topic>"])
│ KHÔNG đợi monthly bootstrap — instant index
├── Pattern proven cross-project ≥ 2× use, muốn share?
│ → mcp__rag-unified__promote_to_shared(chunk_id)
│ (v0 stub, defer v1)
└── Daily ops, không cần cụ thể?
→ Skip RAG, dùng blanket auto-load + Read on-demand specific files
```
### §A3.4 — Token budget guidance per session
**Target utilization Opus 4.7 1M context:**
| % Used | State | Action |
|---|---|---|
| 0-30% (~0-300K) | 🟢 Healthy | Continue normal work |
| 30-50% (~300-500K) | 🟢 Heavy session | OK, monitor sub-agent spawn cost |
| 50-70% (~500-700K) | 🟡 Warning | Consider end-of-session soon, save patterns via `store_memory` |
| 70-85% (~700-850K) | 🟠 Approach limit | End session ASAP, finalize HANDOFF, S+1 fresh |
| > 85% (~850K+) | 🔴 Critical | Response lag + repetition. End immediately. |
**Em main session activity sample S27 (~5h heavy):**
- Read full 4 MEMORY (180KB) + STATUS (60KB) + HANDOFF (90KB) + multiple skill files + session logs S25-S26
- Write 7 PS scripts + dashboard HTML + onboarding guide (440 lines) + 4 archive files + memory NEW
- Estimate context used: ~400-560K (40-55% utilization) — Warning state
- Pattern: Heavy session ~5-6h → end session, S+1 fresh start
**Anti-pattern wasteful:**
- ❌ Read entire `STATUS.md` (60KB) cho 1 question — use search_memory instead
- ❌ Read tất cả session log S20-S26 (~30 files ~300KB) — use search_memory specific term
- ❌ Read entire 4 sub-agent MEMORY (180KB) mỗi session — auto-injected first 200 lines, đủ; full Read only when explicit reference
- ❌ Search RAG với empty/vague query ("test", "data") — return noise
- ❌ Read full source code BE (3K LOC) cho 1 line lookup — use search_code BM25
### §A3.5 — 6 MCP tools examples concrete (em main daily)
**1. `search_memory(query, scope, top_k, use_rerank)`** — Most common
```python
# Vietnamese OK
search_memory("workflow approve V2 actor scope check")
# English OK
search_memory("multi-agent setup pitfall model inherit")
# Specific gotcha lookup (skip Read gotchas.md full)
search_memory("gotcha 48 SQLite tie-break multi Changelog")
# Cross-project default (self + shared_global)
search_memory("Per-NV admin opt-in flag pattern", scope="default")
# Self-only (project-specific narrow)
search_memory("Plan AG tree view UI", scope="self")
```
**2. `cross_project_search(query, top_k)`** — Khi explore broader
```python
# "Pattern X tao đã làm ở dự án nào trước chưa?"
cross_project_search("audit recovery synthetic rows FE merge")
cross_project_search("EF migration BACKFILL reorder discipline")
cross_project_search("Custom Dashboard PowerShell HTML generator")
```
**3. `search_code(query, file_pattern, top_k)`** — BM25 exact match
```python
# Exact function name
search_code("ApproveV2Async EnsureCanRejectV2Async")
# Specific TS prop
search_code("AllowApproverEditBudget per-Level slot", file_pattern="**/*.cs")
# Migration name lookup
search_code("Mig 29 RefactorAdvancedOptionsToPerLevelAndDrafterUser")
```
**4. `store_memory(content, source_path, tags)`** — End-of-session NEW pattern
```python
store_memory(
content="""
Pattern S27 NEW: NSSM Windows Service for Qdrant auto-start.
- 7 PS scripts (install/fix/start/stop/status/dashboard/boot)
- Service mode auto-restart on crash + survive logout
- Gotcha: WAL lock conflict if manual qdrant.exe running → kill first
Reusable cross-project bất kỳ multi-project shared infrastructure.
""",
source_path="manual/S27-nssm-pattern",
tags=["nssm", "windows-service", "qdrant", "pattern"]
)
```
**5. `promote_to_shared(chunk_id)`** — Cross-project pattern proven
```python
# v0 stub - defer v1. For now manually duplicate-index to shared via store_memory
# (Pattern proven ≥ 2× use → manual cross-share)
```
**6. `list_projects()`** — Registry overview (rare daily, common monthly audit)
```python
# Check infra health
list_projects()
# Returns: [{project_id, collection, chunk_count, last_indexed_at}, ...]
```
### §A3.6 — Best practices em main daily workflow
**Morning session start (5 phút):**
```powershell
rag-status # Check Qdrant Service + collection counts
rag-dashboard # Visual sanity (snapshot - mỗi sáng OK fresh)
```
+ Spawn test sub-agent IMMEDIATELY:
```
Agent(subagent_type="investigator", prompt="Test reply 1 sentence")
```
→ Verify registry load. Nếu fail → check `feedback_subagent_setup_pitfalls.md` §4 diagnose.
**Trong session daily ops:**
- **Default tool: `search_memory`** thay vì Read MD direct (saves 80-90% tokens per lookup)
- Read direct CHỈ KHI cần edit file đó hoặc cần see full file structure
- `cross_project_search` khi design feature mới — check proven elsewhere
- `search_code` khi need exact symbol/function lookup
**End of session save NEW patterns:**
```python
# 1. store_memory cho mỗi pattern proven phát hiện trong session
store_memory(content="Pattern Z chốt YYYY-MM-DD: ...", tags=[...])
# 2. Update HANDOFF.md với S+1 priority HIGH
# 3. Commit + push (nếu có code changes)
```
**Monthly audit cadence (re-bootstrap if drift):**
```powershell
python D:\.claude-rag\bootstrap.py --project <project_id> # incremental cache hit
python D:\.claude-rag\bootstrap.py --project <project_id> --force-reindex # clean slate
```
Trigger re-bootstrap khi:
- +20 file MD docs mới sau session lớn
- Drastic refactor MD structure (rename folders)
- Monthly audit per rule §6.4 cadence
- Sau khi clean curate 4 sub-agent MEMORY
### §A3.7 — Anti-patterns cross-project (đừng làm)
1.**Solo cả session vì assume agents chưa load** — VIPIX + SOLUTION_ERP S27 lesson. ALWAYS spawn test agent first.
2.**Read full session log cũ (15-25KB each)** — use `search_memory("S## Plan X")` instead, saves 80-90% tokens
3.**Read full 4 MEMORY.md mỗi session** — auto-injected first 200 lines blanket đủ; full Read only when explicit pattern reference
4.**search_memory với query vague** ("test", "data", "fix") — return noise. Specific terms: "gotcha #48 SQLite tie-break", "Plan AG tree view".
5.**Skip `store_memory` end-of-session** — pattern mất khi session crash. Save instant, không đợi monthly bootstrap.
6.**Em main solo task qualify Implementer Case 1/2** — vi phạm directive S22 chốt BẮT BUỘC delegate. Spawn Implementer cho cookie-cutter mechanical (≥5 file independent + deterministic spec).
7.**Heavy session > 6h không end** — context rot risk. End session + S+1 fresh.
### §A3.8 — Context monitoring + when to end session
**Em main không có exact token counter built-in** (Anthropic Claude Code CLI có thể có `/context` hoặc status bar — em check tùy version).
**Estimate heuristics:**
- Số lượng files Read full: > 10 large files (>30KB each) → likely > 40% context
- Session duration: > 5h heavy work → likely > 50% context
- Sub-agent spawn count: 4 agents × 100K each = ~400K used by sub-agent overhead alone
- Tool calls cumulative: > 100 tool calls likely > 50% context
**Signs of context full (end session ASAP):**
- 🚨 Response lag noticeable (longer wait)
- 🚨 Em main lặp lại logic đã reasoning
- 🚨 Em main miss reference to earlier session context
- 🚨 Sub-agent spawn returns wrong/incomplete results
**End session protocol:**
1. `store_memory` mọi NEW pattern phát hiện trong session
2. Update `STATUS.md` Recently Done
3. Update `HANDOFF.md` với S+1 priority HIGH list
4. Write `docs/changelog/sessions/YYYY-MM-DD-S##-<topic>.md`
5. Commit + push (per project workflow)
6. Anh restart Claude Code CLI cho S+1 fresh
---
## §A2 Cấu trúc chuẩn em main pioneer (SOLUTION_ERP) đã setup
> **Notes integrated từ Plan A.2 (skip bootstrap hộ, but provide structure documentation for 4 em main khác self-setup).**
### File tree `D:\.claude-rag\` (em pioneer SOLUTION_ERP build)
```
D:\.claude-rag\
├── bootstrap.py (150 LOC - CLI bootstrap project corpus)
├── server.py (210 LOC - FastMCP entry, 6 tool handlers)
├── README.md (project README)
├── dashboard.html (generated by dashboard.ps1 - DO NOT edit manually)
├── lib/
│ ├── __init__.py
│ ├── projects.py (140 LOC - auto-detect cwd, registry tracking)
│ ├── chunking.py (120 LOC - Markdown chunking + Anthropic Contextual Retrieval prepend)
│ ├── embed.py (140 LOC - Voyage AI wrapper, retry exponential backoff, SHA256 cache)
│ ├── retrieval.py (230 LOC - 3-layer pipeline: Vector + BM25 + RRF + Voyage rerank-2.5)
│ └── watcher.py (100 LOC - Watchdog file watcher, debounce 2s — defer v1 wire)
├── scripts/
│ ├── start.ps1 (Qdrant background hidden + health check, ASCII-only)
│ ├── stop.ps1 (Graceful stop + verify, ASCII-only)
│ ├── status.ps1 (6-section full report, ASCII-only)
│ ├── dashboard.ps1 (Generate custom HTML + open browser, ASCII-only)
│ └── boot.ps1 (Auto-boot sequence: start + dashboard, used by Startup shortcut)
├── qdrant-bin/
│ ├── qdrant.exe (88 MB v1.18.0 Windows native binary)
│ ├── qdrant-v1.18.0.zip (download archive 28.3 MB)
│ ├── .qdrant-initialized (marker file)
│ └── config/ (Qdrant config folder, currently empty - uses defaults)
├── data/
│ └── qdrant-storage/ (Qdrant data dir, ~85+ MB per project bootstrap)
├── logs/
│ └── qdrant-YYYYMMDD.log (rotated daily)
└── __pycache__/ (Python bytecode cache)
```
### 8-step Bootstrap checklist cho em main project mới
```
[ ] 1. Verify infra global ready: rag-status → 3 OK (Qdrant + MCP + Voyage)
[ ] 2. Decide project_id (lowercase + underscore, UNIQUE cross 5 project)
[ ] 3. Decide sensitivity: share_to_global=true/false (client confidential check)
[ ] 4. Identify corpus_paths (default 3 + extra docs folders project-specific)
[ ] 5. Identify exclude_paths (binary, node_modules, archive, secrets)
[ ] 6. Identify extra_corpus (memory user-level path cho project Claude Code folder)
[ ] 7. Write .claude/rag.json from template §4
[ ] 8. Run: python D:\.claude-rag\bootstrap.py --project <project_id>
[ ] 9. Verify: rag-status (see new project in [Collections] section)
[ ] 10. Test query in Claude Code session: search_memory("test")
```
### Files em pioneer ĐÃ build (KHÔNG edit cross-project)
| File | Content | Owner |
|---|---|---|
| `D:\.claude-rag\server.py` | FastMCP entry + 6 tool handlers | **Em pioneer SOLUTION_ERP** (do NOT modify per-project) |
| `D:\.claude-rag\lib\*.py` | Shared library | **Em pioneer** (modify only via PR to em pioneer project) |
| `D:\.claude-rag\bootstrap.py` | CLI bootstrap script | **Em pioneer** |
| `D:\.claude-rag\scripts\*.ps1` | 5 PowerShell scripts | **Em pioneer** (em main project mới có thể customize project-specific aliases per §12) |
| `D:\.claude-rag\qdrant-bin\` | Qdrant binary | **Shared** (Windows native v1.18.0, no Docker) |
| `D:\.claude-rag\data\` | Qdrant storage | **Shared** (all collections live here) |
| `<project>\.claude\rag.json` | Per-project config | **Em main project đó** (own this file fully) |
| `dashboard.html` | Auto-generated | **dashboard.ps1 generates, DO NOT edit manually** |
### Cấu trúc stack chốt
| Component | Tool/Version | Note |
|---|---|---|
| Embedding | Voyage-4-large (1024-dim) | 200M tokens/month free tier |
| Reranker | Voyage rerank-2.5 | $0.05/1K query, 20× cheaper than Cohere |
| Vector DB | Qdrant v1.18.0 Windows native binary | No Docker, 28.3 MB binary |
| BM25 | SQLite FTS5 (unicode61 + remove_diacritics 2) | Built-in to SQLite, no Postgres |
| Fusion | RRF k=60 (Reciprocal Rank Fusion) | Industry standard |
| MCP framework | FastMCP 3.3.1 stdio | Anthropic official SDK |
| Contextual Retrieval | "From <doc_title> > <heading_path>:" prepend | Anthropic blog Sept 2024 pattern |
| Distribution | User-level Global MCP (Approach A — 1 dev N project localhost) | NO team VPS pattern |
### 4 Gotcha discoveries S26-S27 (cho project khác tránh)
1. **Qdrant Windows binary chính thức tồn tại** - GitHub release `qdrant-x86_64-pc-windows-msvc.zip` 28.3 MB. No Docker overhead.
2. **`claude mcp add` cần `--` separator** terminate variadic `-e` flag: `claude mcp add -s user -e "VOYAGE_API_KEY=$VK" -- rag-unified python "D:\.claude-rag\server.py"`. Em pioneer trial 5 lần fail trước khi tìm ra fix.
3. **Qdrant batch upsert WinError 10053** với batch > 64 chunks → fix bằng `UPSERT_BATCH = 64` trong `lib/retrieval.py`.
4. **Qdrant Windows binary KHÔNG bundled Web UI static files** (S27 discovery 2026-05-22) — `qdrant.exe` log warn `Static content folder for Web UI './static' does not exist` + `http://localhost:6333/dashboard` returns 404. **Fix:** Download separate `dist-qdrant.zip` từ https://github.com/qdrant/qdrant-web-ui/releases/latest (6.59 MB v0.2.12) + extract vào `D:\.claude-rag\qdrant-bin\static\` (flatten `dist/` subfolder up to `static/` root level so `index.html``static\index.html`). Restart Qdrant → native dashboard work HTTP 200 "UI | Qdrant".
```powershell
$url = "https://github.com/qdrant/qdrant-web-ui/releases/download/v0.2.12/dist-qdrant.zip"
Invoke-WebRequest -Uri $url -OutFile "D:\.claude-rag\qdrant-bin\dist-qdrant.zip"
Expand-Archive -Path "D:\.claude-rag\qdrant-bin\dist-qdrant.zip" -DestinationPath "D:\.claude-rag\qdrant-bin\static" -Force
# Flatten dist/ up to static/ root:
Get-ChildItem "D:\.claude-rag\qdrant-bin\static\dist" | Move-Item -Destination "D:\.claude-rag\qdrant-bin\static" -Force
Remove-Item "D:\.claude-rag\qdrant-bin\static\dist" -Force
rag-restart
```
### PowerShell gotcha (per memory `feedback_node_cicd.md` cousin)
- **ASCII-only trong .ps1 source** - PowerShell 5.1 fragile với UTF-8 BOM. Em pioneer trial fail 2x với `✓ → âœ"` mangling. Replace ALL unicode trong code source bằng ASCII (`[OK]`, `[FAIL]`, `->`, `*`, `!`). HTML output unicode OK (file save UTF-8 + browser charset declare).
- **Bash shell KHÔNG inherit User-scope env var từ PowerShell parent** - workaround: `VK=$(powershell -NoProfile -Command "...")` mỗi Bash command + `export VOYAGE_API_KEY="$VK"`
---
## Cross-ref
- `feedback_rag_distributed_ownership.md` (memory user-level) - Decision Approach B + Option 4a rationale full
- `feedback_rag_hybrid_pattern.md` (memory user-level) - Stack architecture chốt S26 + 7 gotcha discoveries
- `feedback_multi_agent_setup.md` (memory user-level) - 4 sub-agent decision gate
- `feedback_md_compact_narrative.md` (memory user-level) - §6.5 KEEP narrative rule cho bootstrap chunking strategy
- `.claude/agents/README.md` (SOLUTION_ERP) - master coordination guide template cho project mới adopt
- Anthropic Contextual Retrieval - https://www.anthropic.com/news/contextual-retrieval
- FastMCP docs - https://fastmcp.com
- Qdrant Windows release - https://github.com/qdrant/qdrant/releases
---
## Changelog
- **2026-05-22** v1.0 chốt S27 em main SOLUTION_ERP + bro pqhuy. Initial release. 13 sections + §A2 cấu trúc chuẩn em pioneer (replace Plan A.2 skip bootstrap hộ).
- Future versions update khi 4 em main project khác feedback usage real.

View File

@ -0,0 +1,3 @@
$cs = (Get-Content 'C:\inetpub\solution-erp\api\appsettings.Production.json' | ConvertFrom-Json).ConnectionStrings.Default
$pwd = (($cs -split ';' | Where-Object { $_ -like 'Password=*' }) -replace 'Password=','')
& sqlcmd -S '.\SQLEXPRESS' -d SolutionErp -U vrapp -P $pwd -i 'C:\Temp\plan-b-verify-prod.sql'

View File

@ -0,0 +1,30 @@
SET QUOTED_IDENTIFIER ON;
GO
PRINT '=== Mig 32+33 prod apply check ===';
SELECT TOP 5 MigrationId FROM __EFMigrationsHistory ORDER BY MigrationId DESC;
GO
PRINT '=== Contracts table V2 columns ===';
SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME='Contracts'
AND COLUMN_NAME IN ('ApprovalWorkflowId','CurrentApprovalLevelOrder');
GO
PRINT '=== ContractLevelOpinions table exists ===';
SELECT COUNT(*) AS TableRowsCount FROM ContractLevelOpinions;
GO
PRINT '=== Sample workflow QT-HD-V2-001 seed verify ===';
SELECT Code, Version, ApplicableType, IsActive, IsUserSelectable
FROM ApprovalWorkflows
WHERE Code = 'QT-HD-V2-001';
GO
PRINT '=== V1 Contracts integrity check (7 prod giữ behavior) ===';
SELECT COUNT(*) AS TotalContracts,
SUM(CASE WHEN WorkflowDefinitionId IS NOT NULL THEN 1 ELSE 0 END) AS V1Pinned,
SUM(CASE WHEN ApprovalWorkflowId IS NOT NULL THEN 1 ELSE 0 END) AS V2Pinned
FROM Contracts;
GO

View File

@ -0,0 +1,3 @@
$cs = (Get-Content 'C:\inetpub\solution-erp\api\appsettings.Production.json' | ConvertFrom-Json).ConnectionStrings.Default
$pwd = (($cs -split ';' | Where-Object { $_ -like 'Password=*' }) -replace 'Password=','')
& sqlcmd -S '.\SQLEXPRESS' -d SolutionErp -U vrapp -P $pwd -i 'C:\Temp\plan-ca-verify-perms.sql'

View File

@ -0,0 +1,3 @@
$cs = (Get-Content 'C:\inetpub\solution-erp\api\appsettings.Production.json' | ConvertFrom-Json).ConnectionStrings.Default
$pwd = (($cs -split ';' | Where-Object { $_ -like 'Password=*' }) -replace 'Password=','')
& sqlcmd -S '.\SQLEXPRESS' -d SolutionErp -U vrapp -P $pwd -i 'C:\Temp\plan-ca-verify-menu.sql'

View File

@ -0,0 +1,23 @@
SET QUOTED_IDENTIFIER ON;
GO
PRINT '=== Menu items 9 catalog keys ===';
SELECT [Key], ParentKey, [Order], Label, DisplayLabel, IsVisible
FROM MenuItems
WHERE [Key] IN ('Master','Suppliers','Projects','Departments','Catalogs','CatalogUnits','CatalogMaterials','CatalogServices','CatalogWorkItems')
ORDER BY [Order];
GO
PRINT '=== CatalogManager role + permissions ===';
SELECT R.Name, COUNT(P.MenuKey) AS PermCount
FROM AspNetRoles R
LEFT JOIN Permissions P ON P.RoleId = R.Id
WHERE R.Name = 'CatalogManager'
GROUP BY R.Name;
GO
PRINT '=== catalog.manager user ===';
SELECT TOP 3 U.Email, U.FullName, U.IsActive
FROM Users U
WHERE U.Email LIKE '%catalog%';
GO

View File

@ -0,0 +1,30 @@
SET QUOTED_IDENTIFIER ON;
GO
PRINT '=== Identity table names ===';
SELECT name FROM sys.tables WHERE name LIKE '%Role%' OR name LIKE '%User%';
GO
PRINT '=== Admin role permissions for catalog menu ===';
SELECT P.MenuKey, P.CanRead, P.CanCreate, P.CanUpdate, P.CanDelete
FROM Permissions P
INNER JOIN Roles R ON R.Id = P.RoleId
WHERE R.Name = 'Admin'
AND P.MenuKey IN ('Master','Suppliers','Projects','Departments','Catalogs','CatalogUnits','CatalogMaterials','CatalogServices','CatalogWorkItems')
ORDER BY P.MenuKey;
GO
PRINT '=== ALL Admin permissions count ===';
SELECT COUNT(*) AS AdminPermCount
FROM Permissions P
INNER JOIN Roles R ON R.Id = P.RoleId
WHERE R.Name = 'Admin';
GO
PRINT '=== CatalogManager role permissions ===';
SELECT P.MenuKey, P.CanRead, P.CanCreate, P.CanUpdate, P.CanDelete
FROM Permissions P
INNER JOIN Roles R ON R.Id = P.RoleId
WHERE R.Name = 'CatalogManager'
ORDER BY P.MenuKey;
GO