diff --git a/.claude/agent-memory/cicd-monitor/MEMORY.md b/.claude/agent-memory/cicd-monitor/MEMORY.md index 86ee344..01c87b5 100644 --- a/.claude/agent-memory/cicd-monitor/MEMORY.md +++ b/.claude/agent-memory/cicd-monitor/MEMORY.md @@ -52,7 +52,7 @@ Read-only CI/CD + post-deploy verifier SOLUTION_ERP. Polls Gitea Actions API, ve - **Tests baseline:** **263 PASS** (S62 Run #286 sha 7926c21 spec; 45 Domain + 218 Infra — em-main supplied; supersedes prev 228/240/256). CI gate runs both test projects BEFORE build/deploy → status=success ⟹ test gate passed (`tasks` endpoint reports terminal as `status:success`, `conclusion` field NOT populated). Local grep undercounts (Theory/InlineData) — trust CI conclusion. Phase 9 UAT mode skip per chunk OK. - **Mig latest repo:** **Mig 55 `20260618105426_AddCcmNoteToPeWorkItemBudget`** (S74-bis Run#315 sha 8655ebf; PeWorkItemBudgets +`CcmNote nvarchar(1000) nullable` — 1 AddColumn no new table — VERIFIED APPLIED PROD: history-top==repo, sys.columns `nvarchar maxlen=2000 null=1` [2000=byte-len 1000char×2], tables88). Prev Mig 54 `AddPeSuggestedAndApprovedPrice` (S73 #313, 5 PE price cols) + Mig 53 `20260617060207_AddPeUrgentAndCeoApprovalThreshold` (S71 Run#308; PE +IsUrgentByPro/+IsUrgentByCcm bit-default-0 + ApprovalWorkflows +CeoApprovalThreshold decimal(18,2)-nullable, 3 AddColumn no new table — VERIFIED APPLIED PROD). Prev Mig 52 `AddHoSoLinkToPurchaseEvaluation` (S65 PE HoSoLink hyperlink-NAS) + Mig 51 `AddDepartmentParentId` (S65 org-tree loose-Guid) + Mig 50 `ReplaceBudgetModuleWithPeWorkItemBudgets` (S61 net-reduce). Path `src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/` (53 mig .cs non-designer total). Prod check `sqlcmd __EFMigrationsHistory ORDER BY MigrationId DESC TOP 5`. ⚠️ Table-count: `sys.tables` (is_ms_shipped=0, excl mighist) = **88** (S62 Run #286 verified — S61 Budget-replace DROPPED tables 93→88). Narrative-93 is STALE pre-S61 — when commit touches no schema, 88 is correct, don't FAIL on 88↔93. Always cross-ref COMMIT scope vs ambient count. - **Bearer:** admin `admin@solutions.com.vn/Admin@123456` (full) · UAT `nv.test@solutions.com.vn/TestUser@123456` (Drafter CCM, gotcha #44 check) -- **Bundle hash live S74-bis:** admin `BYF5vIMJ` + css `X_45M1jX` · user `CB-tiRxd` + css `Bbbo0M5H` (Run #315 sha 8655ebf; index.html `Last-Modified` admin 2026-06-18T12:08:53Z + user 12:09:45Z = deploy window; CSS unchanged from #313/#314 = PE-tab logic touched not style). Prev #314: admin `Bv3jUCNo`/user `BWlMBQz6`. Prev #313: admin `OlNyG9OD`/user `DSzSLVtL`. ⚠️ **PER #69 (S72): bundle hash ROTATES on EVERY deploy regardless of FE change (non-deterministic rebuild). So "live hash" value is only a SNAPSHOT for THIS run — do NOT treat it as a frozen-baseline to compare next run against.** To detect a real FE ship, diff `fe-*/src` in commit/range — NOT hash delta. ASYMMETRIC-deploy "anomaly" framing (S66) is RETIRED by #69: both apps rebuilt+rotated every deploy, asymmetry impossible to read from hashes alone. S50 mid-deploy transient lesson still holds: re-confirm hash AFTER status=success ALWAYS (anti-pattern #3). +- **Bundle hash live S77:** admin `jOqxW4-p` + css `DyECY611` · user `DbsznVvR` + css `JQfATaQB` (Run #319 sha 21d1f4e FE-only PeDetailTabs Block-A table-grid; index.html `Last-Modified` admin 2026-06-19T04:31:53Z + user 04:32:45Z = deploy window; admin css ALSO rotated [Block-A real style] but user css `JQfATaQB` FROZEN = asymmetric-ok gotcha#69). Prev #318: admin `BhFDF9IJ`/css `DuqjXB6Y` · user `BAkuRl3C`/css `JQfATaQB`. Prev #315: admin `BYF5vIMJ`/user `CB-tiRxd`. ⚠️ **PER #69 (S72): bundle hash ROTATES on EVERY deploy regardless of FE change (non-deterministic rebuild). So "live hash" value is only a SNAPSHOT for THIS run — do NOT treat it as a frozen-baseline to compare next run against.** To detect a real FE ship, diff `fe-*/src` in commit/range — NOT hash delta. ASYMMETRIC-deploy "anomaly" framing (S66) is RETIRED by #69: both apps rebuilt+rotated every deploy, asymmetry impossible to read from hashes alone. S50 mid-deploy transient lesson still holds: re-confirm hash AFTER status=success ALWAYS (anti-pattern #3). - **DB pw (S42, when `$PROD_DB_PASSWORD` empty):** `vrapp/buKL3TGBkD0wDDbYVw65QeX9` read from `C:\inetpub\solution-erp\api\appsettings.Production.json`→`ConnectionStrings.Default`. ⚠️ Skill-doc path `C:\inetpub\apps\SolutionErp\Api` is STALE → real path `C:\inetpub\solution-erp\api`. sqlcmd over SSH works direct (no UTF-16 encode needed). ⚠️ sys-catalog string-concat queries hit collation conflict (`Latin1_General_CI_AS_KS_WS` vs `SQL_Latin1_General_CP1_CI_AS`) → add `COLLATE DATABASE_DEFAULT` per concatenated column. ## 🔑 Critical config (flag commit nếu tái xuất) @@ -73,12 +73,14 @@ BE (test+build) ~90s · FE × 2 ~60s/app · deploy ~30s · **total ~3min code / - _(S72 Run #312 sha=`18fced6` PASS ~5m16s [governance-only 26-md+hmw.js+memory-budget.json triggers-CI, ZERO prod code, Mig53-unchanged-VERIFIED-prod, tables88, health-4x200, test306-inferred, **BUNDLE-BOTH-ROTATE-but-BENIGN fc_xkNpJ/DP-tBcg0** → NEW gotcha #69 (FE-hash NON-deterministic per-rebuild, deploy.yml Remove+Copy fe-* UNCONDITIONAL every run, "frozen-on-BE-only" invariant OVERTURNED, SPA-200-trap control-test, real-FE-change = `git diff fe-*/src` NOT hash-delta), 0-fe-src-diff-confirms-no-accidental-ship] → gotcha #69 promoted to recurring-patterns §top; FIFO-trimmed, full verbatim git `18fced6`+`39d55d4`)_ - _(S71 Run #308 sha=`ebd7e1c` PASS ~4m41s [FULL-STACK PE-urgent+CCM-threshold, **Mig53 AddPeUrgentAndCeoApprovalThreshold VERIFIED-APPLIED-PROD** 3-cols-sys-columns (IsUrgentByPro/Ccm bit0 + CeoApprovalThreshold decimal18-2-null), history-top-advance-53, tables88-addcolumn-only, bundle-BOTH-rotate BgNCjwsG/CBvh0vtf, endpoint-urgent-401-not-404, test306] → 4-axis full-stack pattern (BE+FE-both-rotate · history-top-advance + N-cols sys.columns type-verify · tables-stay-88 · endpoint-401-not-404); FIFO-trimmed, full verbatim git `ebd7e1c`)_ -- **2026-06-18 S74-bis Run #315 (run_number 315, id=429) sha=`8655ebf` PASS ~4m54s (FULL-STACK: BE Mig 55 `AddCcmNoteToPeWorkItemBudget` ô "Ghi chú từ CCM" ngân-sách-gói-thầu — CCM nhập số + ghi lý do giống PRO, anh Kiệt FDC continuation of Mig 50/53/54. 14 files: BE 9 {Controller, App Dtos+PeWorkItemBudgetFeatures+PurchaseEvaluationFeatures, Domain PeWorkItemBudget.cs, Config PeWorkItemBudgetConfiguration} + 3 Mig-file + FE 4 {PeDetailTabs+types ×2-app} + 1 test PeWorkItemBudgetTests. deploy 16/16 session after #297–#314 all PASS):** Push HEAD=`8655ebf` (nothing unpushed; range e7e99d1..8655ebf single commit). `git diff --name-only e7e99d1..8655ebf -- '*Persistence/Migrations*'` = 3 files (Mig 55 .cs/.Designer.cs/Snapshot) — REAL EF migration. Mig55 Up() read = single `AddColumn CcmNote table=PeWorkItemBudgets nvarchar(1000) maxLength:1000 nullable:true`; Down() DropColumn — matches spec exactly, AddColumn-only no new table. `.cs/.tsx` non-ignored → full pipeline RAN. GITEA_TOKEN+PROD_DB_PW both absent env → anon Gitea API (worked, public repo) + DB pw read prod `appsettings.Production.json`→`ConnectionStrings.Default` (`vrapp`/`buKL3TGBkD0wDDbYVw65QeX9`, `.\SQLEXPRESS`/`SolutionErp`, path `C:\inetpub\solution-erp\api`). Run IN-PROGRESS at spawn (running 19:05:26, exact head_sha match #315 deterministic). **★ PRE-DEPLOY DB SNAPSHOT captured (proves deploy hadn't shipped at spawn): prod history-top = Mig 54 `AddPeSuggestedAndApprovedPrice`, CcmNote col ABSENT, tables88.** Pre-poll bundle baseline (anti#3): admin `Bv3jUCNo`/css `X_45M1jX` + user `BWlMBQz6`/css `Bbbo0M5H` = still #314 live. Poll-loop iter3 status=success (created 19:05:26→success 19:10:20 ≈4m54s; iter1-2 running). CI gate (both proj pre-build ⟹ status=success ⟹ test **339** expected (45D+294I; +5 new PeWorkItemBudgetTests vs #313's 334) passed; `conclusion` empty — `tasks` terminal=`status:success` doesn't populate conclusion, trust success; 339 INFERRED gate-passes-pre-build NOT log-confirmed numeric). **🔑★ MIG 55 VERIFIED APPLIED PROD (sqlcmd-over-SSH ground-truth, POST-deploy re-query): history-top advanced Mig54→Mig55 `20260618105426_AddCcmNoteToPeWorkItemBudget` == repo HEAD ✓ (DbInitializer auto-migrate-on-boot ran on app-pool recycle). Col `CcmNote` now EXISTS sys.columns: `nvarchar | maxlen=2000 | null=1` ✓ (2000 = SQL byte-len 1000char×2 ⟹ confirms nvarchar(1000) spec; nullable ✓). sys.tables=88 UNCHANGED — AddColumn no new table ✓.** **★ BUNDLE BOTH ROTATE (FE 2-app PeDetailTabs change ⟹ both EXPECTED per gotcha #69; verified AFTER status=success + STABLE 2nd-fetch no-transient per anti#3): admin JS ROTATE `Bv3jUCNo→BYF5vIMJ`** ✓ (css `X_45M1jX` UNCHANGED — tab logic not style) **+ user JS ROTATE `BWlMBQz6→CB-tiRxd`** ✓ (css `Bbbo0M5H` UNCHANGED). Asset reachable 200 + LARGE: admin js 1,600,221b + user js 1,504,880b (control fake `/assets/index-ZZfakehash0.js`→200 size 919b/895b SPA-fallback ⟹ real JS shipped, gotcha #69 SPA-200-trap distinguished by size). index.html `Last-Modified` admin 12:08:53Z + user 12:09:45Z (=19:08-19:09VN deploy window) ✓. Smoke **4×200** health: api `/health/ready`+`/health/live` + admin root + eoffice root. 0 regression. NO prod-data mutation (read-only curls + sqlcmd SELECT-only; mig-apply+FE-copy = expected boot/deploy side-effects). Behavioral CCM-note input on PeWorkItemBudgets = anh Kiệt UAT (em confirm Mig55+col+bundles shipped per spec). **VERDICT PASS: green CI + test 339 + Mig 55 applied (CcmNote nvarchar(1000) sys.columns ground-truth, history advanced 54→55) + tables 88 + bundle BOTH rotate + health 4×200. LESSON: textbook full-stack-single-AddColumn = same 4-axis as S73 #313/S71 #308 (BE+FE-both-rotate · Mig history-top advance + col sys.columns type-verify[byte-len 2×char] · sys.tables stays 88 AddColumn-only · health 4×200). PRE-deploy DB snapshot at spawn (Mig54+col-absent) made the POST-deploy advance (Mig55+col-present) an unambiguous proof — captured baseline BEFORE poll-loop = best practice when run still building. Mig-applied proof = `__EFMigrationsHistory` top==repo-HEAD; if stuck Mig 54 ⟹ app pool didn't recycle ⟹ FAIL even if status=success+bundle-rotated. NOTE: prompt called this "Run #314" but #314 was prior FE-only `6aa4dcb` — actual run for `8655ebf` = #315 (matched by head_sha, NOT run_number). Rate-limit env this session (a sibling sub died on 529) — this verify completed without hitting it. TOOLING: bash POSIX → write PS to `$LOCALAPPDATA/Temp/x.ps1` + `powershell.exe -File $(cygpath -w ...)`; Gitea `tasks` via Invoke-RestMethod `-Body @{limit=N}` hashtable; poll-loop backgrounded + Monitor grep-until FINAL; SSH→PS base64 `-EncodedCommand` UTF-16LE for appsettings-read AND sqlcmd; sqlcmd pw via `[char]39` literal quotes + `-W -h -1`. NEVER fixed code (READ-only).** Tag `[s74bis, run315, pass, full-stack-pe-ccmnote, mig55-AddCcmNoteToPeWorkItemBudget-VERIFIED-APPLIED-PROD, ccmnote-nvarchar1000-sys-columns-maxlen2000-bytelen-null, history-top-advance-54-to-55, tables88-unchanged-addcolumn-only, bundle-BOTH-rotate-BYF5vIMJ-CB-tiRxd, css-unchanged-X_45M1jX-Bbbo0M5H, asset-200-large-1.6MB-vs-fake-919b-spa-trap, last-modified-deploy-window-12-08-12-09z, health-4x200, test339-inferred-plus5-PeWorkItemBudgetTests, deploy16of16, pre-deploy-db-snapshot-mig54-then-post-mig55-unambiguous, prompt-said-314-but-actual-315-matched-by-head-sha, rate-limit-env-sibling-died-529-this-verify-ok, anon-gitea-api-both-token-absent, behavioral-anh-kiet-uat]`. +- **2026-06-19 S76 Run #318 (run_number 318, id=432) sha=`e33481e` PASS ~4m58s (FULL-STACK: BE Mig 56 `AddProBudgetSplitToPeWorkItemBudget` — PE ngân-sách MA-TRẬN 3 cột Dự-án|PRO|CCM, PRO split số ban-hành + điều-chỉnh + badge quyền NS theo role; anh Kiệt FDC continuation Mig 50/53/54/55. 19 files: BE 9 {Controller, App ApprovalWorkflowV2AdminFeatures+PurchaseEvaluationDtos+PeWorkItemBudgetFeatures+PurchaseEvaluationFeatures, Domain PeWorkItemBudget.cs, Config PeWorkItemBudgetConfiguration} + 3 Mig-file + FE 6 {PeDetailTabs+PeWorkflowPanel+types ×2-app, admin +ApprovalWorkflowsV2Page} + 1 test PeWorkItemBudgetTests + .gitignore. deploy 17/17 session after #297–#315 all PASS):** Push HEAD=`e33481e` (nothing unpushed). `git diff --name-only e33481e~1 e33481e -- '*Persistence/Migrations*'` = 3 files (Mig 56 .cs/.Designer.cs/Snapshot) — REAL EF mig. Mig56 Up() read = **2× `AddColumn`: `ProAdjustmentAmount` + `ProInitialAmount` both decimal(18,2) precision18 scale2 nullable** + `migrationBuilder.Sql(UPDATE PeWorkItemBudgets SET ProInitialAmount=ProEstimateAmount WHERE ProEstimateAmount IS NOT NULL AND ProInitialAmount IS NULL)` data-migrate (phiếu cũ 1-cột giữ số PRO ở cột ban-hành); Down() 2× DropColumn — matches spec exactly, AddColumn-only no new table. `.cs/.tsx` non-ignored → full pipeline RAN. GITEA_TOKEN+PROD_DB_PW BOTH absent env → anon Gitea API (worked, public repo) + DB pw read prod `appsettings.Production.json`→`ConnectionStrings.Default` (`vrapp`/`buKL3TGBkD0wDDbYVw65QeX9`, `.\SQLEXPRESS`/`SolutionErp`, path `C:\inetpub\solution-erp\api`). Run IN-PROGRESS at spawn (running 11:03:00, exact head_sha match #318 deterministic; NOTE prompt said "Run #316" but #316=CANCELLED `ae957c4` — actual run for `e33481e` = #318 matched by head_sha NOT run_number). **★ PRE-DEPLOY DB SNAPSHOT captured BEFORE poll-loop (proves deploy hadn't shipped): prod history-top = Mig 55 `AddCcmNoteToPeWorkItemBudget`, ProInitial+ProAdjustment cols ABSENT (only ProEstimateAmount present), tables88.** Pre-poll bundle baseline (anti#3): admin `BYF5vIMJ`/css `X_45M1jX` + user `CB-tiRxd`/css `Bbbo0M5H` = still #315 live. Poll-loop iter6 status=success (created 11:03:00→success 11:07:58 ≈4m58s; iter1-5 running). CI gate (both proj pre-build ⟹ status=success ⟹ test **344** expected (45D+299I; +1 PeWorkItemBudgetTests vs #315's 339) passed; `conclusion` empty — `tasks` terminal=`status:success` doesn't populate conclusion, trust success; 344 INFERRED gate-passes-pre-build NOT log-confirmed numeric). **🔑★ MIG 56 VERIFIED APPLIED PROD (sqlcmd-over-SSH ground-truth, POST-deploy re-query): history-top advanced Mig55→Mig56 `20260619024427_AddProBudgetSplitToPeWorkItemBudget` == repo HEAD ✓ (DbInitializer auto-migrate-on-boot ran on app-pool recycle). BOTH cols EXIST sys.columns: `ProAdjustmentAmount` decimal len9 prec18 scale2 null=1 ✓ + `ProInitialAmount` decimal len9 prec18 scale2 null=1 ✓ — match Mig 56 spec. sys.tables=88 UNCHANGED — 2 AddColumn no new table ✓. ★ DATA-MIGRATE VERIFIED: 4 rows `ProEstimateAmount IS NOT NULL AND ProInitialAmount=ProEstimateAmount` (backfill ran on prod data) + 0 violation rows `estimate-set-but-initial-null` ✓ — UPDATE-Sql executed on real prod data per gotcha #64.** **★ BUNDLE BOTH ROTATE (FE 2-app PeDetailTabs+PeWorkflowPanel + admin ApprovalWorkflowsV2Page ⟹ both EXPECTED per gotcha #69; verified AFTER status=success + STABLE 2nd-fetch no-transient per anti#3): admin JS ROTATE `BYF5vIMJ→BhFDF9IJ` + css `X_45M1jX→DuqjXB6Y`** ✓ (css rotated too — admin had real style change ApprovalWorkflowsV2Page) **+ user JS ROTATE `CB-tiRxd→BAkuRl3C` + css `Bbbo0M5H→JQfATaQB`** ✓. Asset reachable 200 + LARGE: admin js 1,603,616b + user js 1,507,842b (control fake `/assets/index-ZZfakehash0.js`→200 size 919b/895b SPA-fallback ⟹ real JS shipped, gotcha #69 SPA-200-trap distinguished by size). index.html `Last-Modified` admin 04:06:36Z + user 04:07:28Z (=11:06-11:07VN deploy window) ✓. Smoke **4×200** health: api `/health/ready`+`/health/live` + admin root + eoffice root. 0 regression. NO prod-data mutation (read-only curls + sqlcmd SELECT-only; mig-apply+backfill-UPDATE+FE-copy = expected boot/deploy side-effects). Behavioral PRO budget-matrix 3-col + role-badge = anh Kiệt UAT (em confirm Mig56+2cols+backfill+bundles shipped per spec). **VERDICT PASS: green CI + test 344 + Mig 56 applied (ProInitial+ProAdjustment decimal(18,2) sys.columns ground-truth, history advanced 55→56) + tables 88 + backfill 4-rows-0-violation + bundle BOTH rotate (css too) + health 4×200. LESSON: full-stack-2-AddColumn-WITH-DATA-MIGRATE = S74-bis 4-axis + 5th-axis BACKFILL-VERIFY (count rows where new-col=source-col AND 0 violation) — gotcha #64 prod-data-UPDATE-runs-first-time confirmed real. PRE-deploy snapshot at spawn (Mig55+cols-absent) → POST-deploy (Mig56+2cols+4-backfill) = unambiguous proof, best-practice capture-baseline-before-poll when run still building. admin css ALSO rotated (real ApprovalWorkflowsV2Page style) — distinguishes from S74-bis css-frozen (tab-logic-only). Mig-applied proof = `__EFMigrationsHistory` top==repo-HEAD; if stuck Mig 55 ⟹ app pool didn't recycle ⟹ FAIL even if status=success+bundle-rotated. TOOLING: bash POSIX → write PS to `$LOCALAPPDATA/Temp/x.ps1` + `powershell.exe -File $(cygpath -w ...)`; Gitea `tasks` via Invoke-RestMethod `-Body @{limit=N}` hashtable; poll-loop backgrounded + grep-until FINAL; SSH→PS base64 `-EncodedCommand` UTF-16LE for appsettings-read AND sqlcmd; sqlcmd pw inline + `-W -h -1`. NEVER fixed code (READ-only).** Tag `[s76, run318, pass, full-stack-pe-budget-matrix-3col, mig56-AddProBudgetSplitToPeWorkItemBudget-VERIFIED-APPLIED-PROD, 2-cols-sys-columns-ProInitialAmount-ProAdjustmentAmount-decimal18-2-prec18-scale2-null, history-top-advance-55-to-56, tables88-unchanged-addcolumn-only, DATA-MIGRATE-backfill-4rows-ProInitial-eq-ProEstimate-0-violation-gotcha64-prod-data-update-first-time, bundle-BOTH-rotate-BhFDF9IJ-BAkuRl3C, css-BOTH-rotate-DuqjXB6Y-JQfATaQB-admin-ApprovalWorkflowsV2Page-real-style, asset-200-large-1.6MB-vs-fake-919b-spa-trap, last-modified-deploy-window-04-06-04-07z, health-4x200, test344-inferred-plus1-PeWorkItemBudgetTests, deploy17of17, pre-deploy-db-snapshot-mig55-then-post-mig56-unambiguous, prompt-said-316-but-actual-318-matched-by-head-sha-316-cancelled, anon-gitea-api-both-token-absent, behavioral-anh-kiet-uat, 5th-axis-backfill-verify-new]`. +- _(S74-bis Run #315 sha=`8655ebf` PASS ~4m54s [FULL-STACK Mig 55 `AddCcmNoteToPeWorkItemBudget` CcmNote nvarchar(1000) VERIFIED-APPLIED-PROD sys.columns maxlen2000-bytelen, history-advance-54→55, tables88-addcolumn-only, bundle-BOTH-rotate Bv3jUCNo→BYF5vIMJ/BWlMBQz6→CB-tiRxd css-frozen, asset-200-large-vs-fake-919b, health-4x200, test339, pre-deploy-snapshot-mig54-unambiguous, prompt-said-314-actual-315-head-sha] → 4-axis full-stack (BE+FE-both-rotate · history-advance+col-type-verify · tables88 · health-4x200); FIFO-trimmed, full verbatim git `8655ebf`)_ +- **2026-06-19 S77 Run #319 (run_number 319, id=433) sha=`21d1f4e` PASS ~4m46s (FE-ONLY: PE ngân-sách Block A → bảng lưới `` viền ô giống Excel [cột Khoản mục|Dự án|PRO|CCM], anh Kiệt FDC phản-hồi. 2 files ONLY: `fe-admin/src/components/pe/PeDetailTabs.tsx` + `fe-user/.../PeDetailTabs.tsx` — identical 287-line change each app. deploy 18/18 session after #297–#318 all PASS):** Push HEAD=`21d1f4e` (nothing unpushed). `git diff --name-only HEAD~1 HEAD -- '*Persistence/Migrations*'` = EMPTY + `-- 'src/Backend/**/*.cs'` = EMPTY ⟹ confirmed FE-only NO-migration NO-BE. `.tsx` non-ignored → full pipeline RAN (path-filter gates whole-workflow not per-step, gotcha #69). GITEA_TOKEN absent env → anon Gitea API (worked, public repo). Real-FE-change detection per gotcha #69 = `git diff fe-*/src` POSITIVE here (genuine `
`/``/`` grid replacing prior layout — NOT just hash-delta noise). Run IN-PROGRESS at spawn (running, exact head_sha match #319; baseline #318=`e33481e` confirmed in tasks-list). **Pre-poll bundle baseline captured BEFORE poll-loop (anti#3): admin `BhFDF9IJ`/css `DuqjXB6Y` + user `BAkuRl3C`/css `JQfATaQB` = still #318 live (matches prompt-supplied prev exactly).** Poll-loop iter5 status=success (created 11:28:28→success 11:33:14 ≈4m46s; iter1-4 running). CI gate (both test proj pre-build ⟹ status=success ⟹ test **344** [45D+299I, unchanged from #318 — FE-only no test delta] passed; `conclusion` empty — `tasks` terminal=`status:success` doesn't populate conclusion, trust success; 344 INFERRED gate-passes-pre-build NOT log-confirmed numeric). **★ BUNDLE BOTH JS ROTATE (FE 2-app PeDetailTabs change ⟹ both EXPECTED per gotcha #69; verified AFTER status=success + STABLE 2nd-fetch no-transient per anti#3): admin JS ROTATE `BhFDF9IJ→jOqxW4-p`** ✓ (css ALSO rotate `DuqjXB6Y→DyECY611` — Block-A table has real style) **+ user JS ROTATE `BAkuRl3C→DbsznVvR`** ✓ (css `JQfATaQB` UNCHANGED — user-app style untouched; asymmetric css fine per gotcha #69). Asset reachable 200 + LARGE: admin js 1,604,226b + user js 1,508,452b (control fake `/assets/index-ZZfakehash0.js`→200 size 919b/895b SPA-fallback ⟹ real JS shipped, gotcha #69 SPA-200-trap distinguished by SIZE not status). index.html `Last-Modified` admin 04:31:53Z + user 04:32:45Z (=11:31-11:32VN deploy window) ✓. Smoke **4×200**: api `/health/ready`+`/health/live` + admin root + eoffice root. 0 regression. NO migration/DB check (FE-only, per spec). NO prod-data mutation (read-only curls only; FE-copy = expected deploy side-effect). **VERDICT PASS: green CI + test 344 + bundle BOTH JS rotate (real-FE-source-diff confirmed) + real-JS-size 1.6MB/1.5MB vs fake-919b + health 4×200. LESSON: textbook FE-ONLY = 2-axis (BE-skip · BOTH-JS-rotate + real-size-vs-SPA-trap + Last-Modified-deploy-window). Real-FE-ship proof per gotcha #69 = `git diff fe-*/src` non-empty (here genuine `
` grid) — NOT hash-delta alone (hash rotates every deploy regardless). admin css ALSO rotated (Block-A real style) but user css frozen (user-app PeDetailTabs Block-A changed too BUT user css chunk unaffected) — asymmetric css normal, don't FAIL. Pre-poll baseline capture BEFORE running-run completes = best practice (matched prompt prev #318 exactly = clean diff). TOOLING: bash POSIX → write PS to `/tmp/x.ps1` + `powershell.exe -File $(cygpath -w ...)`; Gitea `tasks` via Invoke-RestMethod `-Body @{limit=N}` hashtable; poll-loop backgrounded + grep-until FINAL/POLL-DONE. NEVER fixed code (READ-only).** Tag `[s77, run319, pass, FE-ONLY-2file-PeDetailTabs, pe-budget-blockA-table-grid-excel, NO-migration-NO-BE-confirmed-empty-diff, bundle-BOTH-JS-rotate-jOqxW4-p-DbsznVvR, admin-css-ALSO-rotate-DyECY611-blockA-real-style, user-css-frozen-JQfATaQB-asymmetric-ok-gotcha69, real-JS-size-1.6MB-1.5MB-vs-fake-919b-spa-200-trap-by-size, last-modified-deploy-window-04-31-04-32z, health-4x200, test344-inferred-unchanged-FE-only-no-delta, deploy18of18, baseline-318-e33481e-matches-prompt-prev, real-FE-ship-proof-git-diff-fe-src-not-hash-delta-gotcha69, anon-gitea-api-token-absent, pre-poll-baseline-before-running-run-completes, no-DB-check-FE-only-per-spec]`. - _(S74 Run #314 sha=`6aa4dcb` PASS ~5m12s [FE-ONLY empty-candidates guard PeWorkflowPanel ×2-app, no-mig-stays-54, bundle-BOTH-js-rotate OlNyG9OD→Bv3jUCNo/DSzSLVtL→BWlMBQz6, css-frozen, asset-200-large-vs-fake-919b, health-4x200, test334, no-DB-query-needed-FE-only] → LESSON: FE-only verify lightweight, skip Mig/sqlcmd when 0 migration in diff; FIFO-trimmed, full verbatim git `6aa4dcb`)_ - _(S69b Run #307 sha=`1f8947e` PASS ~4m33s [BE-ONLY GOLIVE security "Văn phòng số" public Read+Create — NEW `SeedAllRolesOfficeModulePermissionsAsync` boot-after-revoke, allow-list 16 Office key upgrade-only; **GOLIVE-DB-VERIFIED-16of16-across-13-roles** sqlcmd prod Permissions (ALLOW-16 R∧C all roles · EXCLUDED-3 only-Admin leak0 · HRM/Personal Drafter still-0 · admin-not-downgraded), bundle-BOTH-frozen Wt54PHYl/B99fMU6X (BE-only no-rotate), no-mig-top-mig52, tables88, test292] → LESSON: BE-only golive-seed verify CORE-proof = prod Permissions DB query NOT bundle-frozen alone (seed=runtime-row-insert NOT EF-mig → no history/tables advance); FIFO-trimmed, full verbatim git `1f8947e`)_ - _(S77 #303 sha `6983609`, S76 #302, S75/S74 … pre-S78 verbatim → git `764fe70` + archive — FIFO trimmed to keep L1 under soft-cap)_ - _(S75 Run #301 sha=`6df1b2d` PASS ~2.5m [FE-both-app PE Link-hồ-sơ auto-detect render, bundle-BOTH-rotate I1fpLeYw/DrQYkzh0, no-mig-mig52, tables88, test286, fastest-streak] → FIFO-trimmed, full verbatim git + `archive/2026-06.md`)_ -- **2026-06-18 S73 Run #313 (run_number 313, id=427) sha=`1d86abc` PASS ~5m22s (FULL-STACK: BE Mig 54 PE giá-đề-xuất PRO/CCM + CEO chọn giá-chốt + CCM duyệt-done ô-tích, anh Kiệt FDC. 19 files: BE 9 {Controller +2 endpoint suggested-price/{pro,ccm} + ApprovedPrice in finalize, App PeSuggestedPriceFeatures.cs NEW, PurchaseEvaluationFeatures, Dtos, IPurchaseEvaluationWorkflowService, Domain PE, Config} + 3 Mig-file + Service PurchaseEvaluationWorkflowService + FE 6 {PeDetailTabs+PeWorkflowPanel+types ×2-app} + 3 test {PeSuggestedPriceSetterAuthzTests, PeApprovedPriceFinalizeTests, PeCcmThresholdFinalizeTests}. deploy 14/14 session after #297–#312 all PASS):** Push HEAD=`1d86abc` (nothing unpushed). `git diff --name-only 1d86abc~1 1d86abc -- '*Persistence/Migrations*'` = 3 files (Mig 54 .cs/.Designer.cs/Snapshot) — REAL EF migration. `.cs/.tsx` non-ignored → full pipeline RAN. GITEA_TOKEN + PROD_DB_PW BOTH absent env → anon Gitea API (worked, public repo) + DB pw from prod `appsettings.Production.json`→`ConnectionStrings.Default` (`vrapp`/`buKL3TGBkD0wDDbYVw65QeX9` len24, path `C:\inetpub\solution-erp\api`). Run IN-PROGRESS at spawn (running 15:51:47, exact head_sha match #313 deterministic). Poll-loop iter5 status=success (created 15:51:47→success 15:57:09 ≈5m22s). CI gate (both proj pre-deploy ⟹ status=success ⟹ test **334** baseline (45D+289I; +3 PeSuggestedPriceSetterAuthz+PeApprovedPriceFinalize+PeCcmThresholdFinalize) passed; `conclusion` empty — `tasks` terminal=`status:success` doesn't populate conclusion, trust success; 334 INFERRED gate-passes-pre-build NOT log-confirmed numeric). **🔑★ MIG 54 VERIFIED APPLIED PROD (sqlcmd over SSH ground-truth): prod `__EFMigrationsHistory` top5 = `20260618081758_AddPeSuggestedAndApprovedPrice`(54)→`AddPeUrgentAndCeoApprovalThreshold`(53)→`AddHoSoLinkToPurchaseEvaluation`(52)→`AddDepartmentParentId`(51)→`ReplaceBudgetModuleWithPeWorkItemBudgets`(50) == repo HEAD ✓ (DbInitializer auto-migrate-on-boot ran). ALL 5 NEW COLUMNS confirmed via sys.columns w/ types: `ProSuggestedMinPrice` decimal(18,2) null ✓ + `ProSuggestedMaxPrice` decimal(18,2) null ✓ + `CcmSuggestedPrice` decimal(18,2) null ✓ + `ApprovedPriceAmount` decimal(18,2) null ✓ + `ApprovedPriceSource` nvarchar null ✓ — match Mig 54 Up() spec. sys.tables=88 (excl mighist) UNCHANGED — 5 AddColumn no new table ✓.** **★ BUNDLE BOTH ROTATE (FE 2-app PE-UI change ⟹ both MUST rotate; verified AFTER status=success + STABLE 2nd-fetch no-transient per anti#3): admin ROTATE `fc_xkNpJ→OlNyG9OD`** ✓ (+css `C2Zvnd2f→X_45M1jX`) **+ user ROTATE `DP-tBcg0→DSzSLVtL`** ✓ (+css `1bS4xeUF→Bbbo0M5H`). Asset reachable 200 + LARGE: admin js 1,599,106b + user js 1,503,765b (control fake `/assets/index-ZZfakehash0.js`→200 size=919b SPA-fallback ⟹ real JS shipped, gotcha #69 SPA-200-trap distinguished by size). index.html `Last-Modified` admin 08:55:32Z + user 08:56:33Z (=15:55VN deploy window) ✓. Smoke **4×200** health: api `/health/ready`+`/health/live` + admin root + eoffice root. **★ NEW ENDPOINT PROBE: `PUT /api/purchase-evaluations/{guid}/suggested-price/pro` unauth → 401 (NOT 404)** ✓ (route EXISTS controller line 102 `SetSuggestedPricePro→UpdatePeSuggestedPriceProCommand`, class-level `[Authorize]` enforced; PRO-only authz = unit-test PeSuggestedPriceSetterAuthzTests) + `/suggested-price/ccm` unauth → 401 ✓ + PE list unauth → 401 ✓ (all reachable+gated). 0 regression. NO prod-data mutation (read-only curls + sqlcmd SELECT-only; mig-apply+FE-copy = expected boot/deploy side-effects). Behavioral PRO/CCM giá-đề-xuất + CEO chọn-giá + CCM duyệt-done = anh Kiệt UAT (em confirm Mig+5cols+endpoint+bundles shipped per spec). **VERDICT PASS: green CI + test 334 + Mig 54 applied (5 cols sys.columns ground-truth) + tables 88 + bundle BOTH rotate + new endpoint 401-not-404 + health 4×200. LESSON: full-stack PE w/ REAL Mig 54 = same 4-axis pattern as S71 #308 (BE+FE-both-rotate · Mig history-top advance + N cols sys.columns type-verify · sys.tables stays 88 AddColumn-only · new-endpoint 401-not-404 probe). Mig-applied proof = `__EFMigrationsHistory` top==repo-HEAD; if stuck Mig 53 ⟹ app pool didn't recycle ⟹ FAIL even if status=success+bundle-rotated. TOOLING: bash POSIX → write PS to `$LOCALAPPDATA/Temp/x.ps1` + `powershell.exe -File $(cygpath -w ...)`; Gitea `tasks` via Invoke-RestMethod `-Body @{limit=N}` hashtable (NOT query-string — URI parse error); poll-loop backgrounded + Monitor grep-until FINAL; SSH→PS base64 `-EncodedCommand` UTF-16LE for appsettings-read AND sqlcmd; sqlcmd pw via here-string `$P=` + `[char]39` literal object-name quotes + `-W -h -1`; read DB pw from prod appsettings when both env empty. NEVER fixed code (READ-only).** Tag `[s73, run313, pass, full-stack-pe-suggested-price, mig54-AddPeSuggestedAndApprovedPrice-VERIFIED-APPLIED-PROD, 5-cols-sys-columns-confirmed-ProSuggestedMin/Max-CcmSuggested-decimal18-2-ApprovedPriceAmount-decimal-ApprovedPriceSource-nvarchar-all-null, history-top-advance-54, tables88-unchanged-addcolumn-only, bundle-BOTH-rotate-OlNyG9OD-DSzSLVtL, asset-200-large-1.5MB-vs-fake-919b-spa-trap, endpoint-suggested-price-pro-401-not-404-route-exists-ccm-401, pe-list-401-gated, health-4x200, test334-inferred, deploy14of14, no-regression, anon-gitea-api-both-token-absent, behavioral-anh-kiet-uat]`. +- _(S73 Run #313 sha=`1d86abc` PASS ~5m22s [FULL-STACK Mig 54 `AddPeSuggestedAndApprovedPrice` PE giá-đề-xuất PRO/CCM + CEO chọn giá-chốt + CCM duyệt-done; **5 cols VERIFIED-APPLIED-PROD sys.columns** ProSuggestedMin/Max+CcmSuggested+ApprovedPriceAmount decimal(18,2)+ApprovedPriceSource nvarchar all-null, history-advance-53→54, tables88-addcolumn-only, bundle-BOTH-rotate fc_xkNpJ→OlNyG9OD/DP-tBcg0→DSzSLVtL +css, asset-200-large-vs-fake-919b, **NEW-ENDPOINT-PROBE PUT /suggested-price/pro unauth→401-NOT-404 route-exists class-Authorize** +ccm→401 +list→401, health-4x200, test334, prompt-said-N-actual-313-head-sha] → 4-axis full-stack + endpoint-401-not-404-probe; FIFO-trimmed, full verbatim git `1d86abc`)_ - _(S74 Run #300 sha=`91aaf05` PASS [FE-both-app list-redesign flex-row, bundle-BOTH-rotate PxiZQkaw/B36hGoKd, user-unfroze-from-s71-streak, no-mig-mig52, tables88, test286, TOOLING: ps1-file-not-inline-dollar + ssh-encodedcommand-base64] → FIFO-trimmed, full verbatim git + `archive/2026-06.md`)_ - _(S67 Run #292 [dept-parentid Mig51-applied-after-#291-fail, retry-chain CS7036-fix, bundle-asymmetric, tree-endpoint-200] + S65 #289 [hrm-hoso public-readonly seed-only, asymmetric read200/write403] → FIFO-trimmed, full verbatim git `d2f52ba` + `archive/2026-06.md`)_ - **Older runs (S66 #290 ← S62 #286 06-13 ← … → S29 #232) full verbatim archived → git `d2f52ba` + `archive/2026-06.md`** (incl #291 06-16 FAIL forensic [lesson=gotcha #65 + #292-inline] + #383 ex-VITRILAC); pre-S38 → `archive/2026-05-{runs,q2,q3,q4}.md`. diff --git a/.claude/agent-memory/implementer-frontend/MEMORY.md b/.claude/agent-memory/implementer-frontend/MEMORY.md index a92e1ee..c8dc42d 100644 --- a/.claude/agent-memory/implementer-frontend/MEMORY.md +++ b/.claude/agent-memory/implementer-frontend/MEMORY.md @@ -6,6 +6,12 @@ --- +## 🆕 S76 (2026-06-19) — PE budget MA TRẬN 3 cột + bảng lưới `
` + badge quyền NS (anh Kiệt FDC) +- **Part 1 mirror** (fe-user→fe-admin byte-identical, Python difflib slice-region): `PeBudgetSummaryTable` Block A → ma trận 3 cột (DỰ ÁN hiển-thị-only `—` / PRO `canEditPro` / CCM `canEditCcm`). Type `PeBudgetSummary` +`proInitialAmount`+`proAdjustmentAmount` cạnh `proEstimateAmount`(LEGACY). `proMut` body `{proInitialAmount,proAdjustmentAmount,proNote}`. proFull=proInit+proAdj. +- **Part 3 mirror:** `PeWorkflowPanel` approver `.join(' / ')` → map từng approver + badge `✎ NS PRO`(amber)/`✎ NS CCM`(sky) gate `a.canEditProBudget/canEditCcmBudget`. Type `PeCurrentApprovalLevelApprover` +2 cờ **CẢ 2 app** (purchaseEvaluation.ts types DIFFER giữa 2 app → sửa cả 2; PeDetailTabs/PeWorkflowPanel byte-identical). +- **Bảng lưới (em-main, anh phản hồi "chưa chia cột giống Excel"):** Block A flex→`
` viền ô. `BudgetCell` xếp dọc (input full ô + Lưu dưới). `BudgetNoteRow`→`BudgetNoteCell` (td colSpan=3). **LESSON: flex+gap KHÔNG ra "bảng" — phải `
` viền ô mới giống spreadsheet.** +- **cwd-misland:** Part-1 `cd fe-admin` npm build → MEMORY rơi `fe-user/.claude` stray → em-main harvest về + `.gitignore` guard `fe-*/.claude/` (S76). + ## 🆕 S73b (2026-06-18) — PE budget "Ghi chú từ CCM" mirror ×2 app (anh Kiệt FDC) Thêm dòng "Ghi chú từ CCM" trong panel TỔNG HỢP NGÂN SÁCH TRÌNH KÝ (`PeBudgetSummaryTable`), mirror y hệt "Ghi chú từ PRO" nhưng gate `canEditCcm` + `ccmNoteText`/`setCcmNoteText` + save qua `ccmMut`. 2 file ×2 app: types/purchaseEvaluation.ts (`PeBudgetSummary` +`ccmNote:string|null` sau `adjustmentAmount`) + PeDetailTabs.tsx (state mirror proNoteText + `ccmMut` body type +`ccmNote` + **2 call-site echo `ccmNote:bs.ccmNote`** Ban hành+Hiệu chỉnh — endpoint `/budget/ccm` ABSOLUTE-SET cả 3 field, thiếu=null=CLEAR + block mới chèn SAU dòng V0/hiệu chỉnh TRƯỚC Ngân sách PRO, group CCM rows). **LESSON xác nhận S73: budget-table region 2 app byte-IDENTICAL → block mới mirror identical (diff awk-PeBudgetSummary + diff sed-region đều IDENTICAL). PeBudgetSummary type block 2 app identical (KHÁC PeDetailBundle Color-records divergence) → an toàn edit cùng content.** Build PASS ×2 (admin 1945mod `index-CCPIU9Wr.js` / user 1934mod `index-j5Zh9w96.js`, 0 TS err). NO ambiguity, full precedent. KHÔNG đụng BE (DTO `ccmNote` + endpoint body = em-main song song). diff --git a/.claude/agent-memory/investigator-codebase/MEMORY.md b/.claude/agent-memory/investigator-codebase/MEMORY.md index ad39370..82d9b36 100644 --- a/.claude/agent-memory/investigator-codebase/MEMORY.md +++ b/.claude/agent-memory/investigator-codebase/MEMORY.md @@ -70,6 +70,10 @@ Bearer từ `POST api.solutions.com.vn/api/auth/login` → status matrix expecte ## 📅 Recent activity (FIFO — older → archive/git) +- **2026-06-19 (S76 P2+P3 — budget-edit-role BADGE insert-point map, designer+fe-user-flow, on-disk):** ⭐ **Display-only "✎ NS PRO/CCM" badge per approver — BE change = SMALL both DTOs.** **(A) Designer fe-admin `ApprovalWorkflowsV2Page.tsx`:** read-only render `DefinitionCard:446-454` (level group → approver `{approverUserName}` + `({approverEmail})`); DTO `LevelDto:37-54` (approverUserId/userName/email + 7 Allow* flag, **NO role/dept field**). Feed = `GetAwAdminOverview` (`/approval-workflows-v2`). **Insert badge → `:447-452`** cạnh `approverUserName`. **(B) fe-user `PeDetailTabs.tsx`:** approvalFlow render `LevelOpinionsSectionV2:588` (signed-only) — но live flow tree = `currentApproval.approvers` :131 + Panel3 separate. `PeApprovalFlow` DTO `purchaseEvaluation.ts` + BE `PurchaseEvaluationApprovalLevelApproverDto` (`PurchaseEvaluationDtos.cs:129-132` = UserId/FullName/Email, **NO role**). **(C) Role-resolve for LIST userId:** codebase uses `userManager.GetRolesAsync(u)` (per-user, N+1 risk) OR `GetUsersInRoleAsync(role)` (reverse, `PeUrgentFeatures.cs:74`). `IApplicationDbContext` exposes `DbSet Roles` :29 but **NO UserRoles join-table DbSet** → efficient batch = either (a) `userManager.GetUsersInRoleAsync(Procurement/CostControl)`→2 HashSet, mark approver if id∈set (NO N+1, 2 queries total); or (b) add `DbSet>` to interface for join. **BE build site `PurchaseEvaluationFeatures.cs:964-972`** already batches `approverInfos` via `userManager.Users.Where(Contains(allApproverIds))` — extend SELECT or post-join 2 role-sets here; handler has both `db`+`userManager` :750-751. **(D) Change size = SMALL:** +2 bool field (canEditProBudget/canEditCcmBudget) per approver DTO + 2 GetUsersInRoleAsync calls. Designer side: `GetAwAdminOverview` query needs same 2-set lookup (admin-only, cheap). Gate semantics ALREADY proven `:800-801` (canEditPro=Admin||Procurement, canEditCcm=Admin||CostControl). **(E) REC:** minimal = compute 2 HashSet once (proFans/ccmFans via GetUsersInRoleAsync), pass into approver-DTO map both sites; badge = pure display `id∈proFans→"✎ NS PRO"` `id∈ccmFans→"✎ NS CCM"`. RISK low (display-only, no authz touch) — only watch: a user can hold BOTH roles → show both badges; Admin holds neither role explicitly unless seeded → may need OR Admin note. Tag `[s76, budget-role-badge, designer+pe-flow, getusersinrole-batch-no-n1, approver-dto-add-2bool, display-only]`. + +- **2026-06-19 (PE Block-A budget editable-gate audit — submission-count lock NEXISTS, on-disk):** ⭐ **Gate = PURE ROLE, KHÔNG phase, KHÔNG số-lần-trình.** BE `PurchaseEvaluationFeatures.cs:800-801` `canEditPro=isAdmin||Procurement` · `canEditCcm=isAdmin||CostControl` (DTO arg :856). Handler `PeWorkItemBudgetFeatures.cs`: PRO `:86-91` CCM `:152-157` fail-closed ForbiddenException role-only TRƯỚC side-effect; comment `:18-20` ghi RÕ "KHÔNG ràng Phase (bảng NS = tài-liệu-sống chỉnh bất-kỳ-lúc-nào như Excel)". Validator chỉ `>=0` (Initial :136, Adjustment cho-ÂM :138), absolute-set null=clear. **FE `PeDetailTabs.tsx:1060 PeBudgetSummaryTable`:** ô "Ban hành lần đầu" :1173 + ô "hiệu chỉnh V0" :1188 dùng **CÙNG biến `bs.canEditCcm`** — ZERO phân-biệt 2 ô, ZERO lock-after-first. `drafterEditable:1066`=`!readOnly&&isEditablePhase` chỉ áp row3/row8 (drafter NS-kỳ-này), KHÔNG áp Block-A. **(b) submission-count lock = KHÔNG TỒN TẠI:** grep `submitCount|lanTrinh|firstSubmit|lockInitial|hasSubmitted|soLanTrinh` toàn `src/Backend`=0 + FE=0. Entity `PeWorkItemBudget.cs` 6 field plain, KHÔNG cờ `IsInitialLocked`/`SubmitCount`; record per-cặp(Project×WorkItem) share mọi phiếu KHÔNG track lần-trình. **Kết luận: yêu-cầu chị Trà/anh Kiệt (khóa Initial sau lần-trình-đầu, mở Adjustment) = FEATURE MỚI — cần field track first-submit-done + TÁCH gate 2 ô (Initial vs Adjustment), HIỆN cùng `canEditCcm` không tách được.** Tag `[pe-block-a-gate, role-only-no-phase, submission-count-lock-NEXISTS, initial-vs-adjustment-same-gate, fdc-feature-new]`. + - **2026-06-18 (PE price-model recon FDC "Giá chào thầu" PRO-Min/Max + CCM-proposed, on-disk):** ⭐ **"Giá chào thầu" mục c = DERIVED, KHÔNG stored column** = `WinnerQuoteTotal` = SUM(Quote.ThanhTien WHERE supplierRows==SelectedSupplierId). Computed 3 nơi đồng-predicate: submit-guard `PurchaseEvaluationWorkflowService.cs:188-192` · detail-GET `PurchaseEvaluationFeatures.cs:818-826`(→`CurrentProposalTotal`) · CEO-threshold `:833`. DTO `WinnerQuoteTotal` `PurchaseEvaluationDtos.cs:244`. **ALL money fields:** Quote(NCC) `BgVat/ChuaVat/ThanhTien` decimal non-null `PurchaseEvaluationQuote.cs:12-14` · PE-header `BudgetPeriodAmount`(row3 drafter)/`ExpectedRemainingAmount`(row8) decimal? `PurchaseEvaluation.cs:40-41` · PeWorkItemBudget(per cặp Project×WorkItem) PRO `ProEstimateAmount:27` + CCM `InitialAmount`/`AdjustmentAmount`(ÂM-OK) `:29-30` decimal? · Detail dự-toán `KhoiLuong/DonGia/ThanhTienNganSach` `PurchaseEvaluationDetail.cs:15-18`. **PRO-min/max + CCM-proposed = KHÔNG tồn-tại** (grep Min|Max|Proposed|Suggest|BidPrice|GiaChaoThau PE-entities=0) → field MỚI. **Role-gate mirror-được (`PeWorkItemBudgetFeatures.cs`):** 2 cmd tách `UpdatePeBudgetProCommand:61`+`UpdatePeBudgetCcmCommand:126`; handler fail-closed `ForbiddenException` TRƯỚC side-effect — PRO `:86-91`(`Admin||Procurement`) CCM `:150-155`(`Admin||CostControl`); capability-flag BE-computed `canEditPro/canEditCcm` `PurchaseEvaluationFeatures.cs:783-784`→DTO `PeBudgetSummaryDto:290-291`; auto-create race-safe `PeWorkItemBudgetEnsurer.EnsureTrackedAsync:34`; KHÔNG ràng Phase. NO AutoMapper (DTO project tay). **FE (fe-user `src/`; fe-admin PeDetailTabs.tsx = SHA-identical `diff -q`):** mục-c `components/pe/PeDetailTabs.tsx:1406-1417`(helper `computeGiaChaoThau` def:71 call:1393) · budget-table `PeBudgetSummaryTable:1062-`(rows:1110-1128, host `ChonNccSection:1383`) · giá-gói+CEO-threshold `:311-313` · create `PeWorkspaceCreateView.tsx` · header `PeHeaderForm.tsx`. FE type `types/purchaseEvaluation.ts` `PeBudgetSummary:292-307`+`winnerQuoteTotal:445`. ⚠️ fe-admin types DIFFER (sync cả 2). **Surprise:** PRO-Min/Max-chốt + CCM-proposed = semantic MỚI (giá-người-duyệt ≠ giá-NCC-báo); gắn PeWorkItemBudget(per-cặp role-gate-sẵn) vs column-PE(per-phiếu) = em-main quyết. Mig 53 CeoApprovalThreshold+cờ-gấp đã có khung CEO-duyệt-theo-ngưỡng. Tag `[pe-price-model, gia-chao-thau-derived, pro-minmax-ccm-proposed-NEW, role-gate-mirror, fdc]`. - **2026-06-18 (S71 PART-C audit — run-trace vs checklist-v2 FLAT + detector-refine, on-disk):** ⭐ **2 GAP THẬT (trung-thực, không inflate):** (1) **C1/C2/C8 = SUBFOLDER, canonical-v2 = FLAT → migration NEEDED, chưa làm.** `find runs/` cho thấy MỖI run-folder có `sub-md/`+`harvest/` SUBDIR (5 run: h10-{invest,implement,review}+h910-{finalize,curate}) — đúng cấu-trúc CŨ broadcast-delta phát-bỏ. ZERO flat-awareness: grep `phẳng|flat|cùng cấp` trong `.claude/workflows/`+`.claude/commands/`=0 hit. SE-adoption-commit `8c47bd0`(06-18) TRƯỚC broadcast-flat cùng-ngày → SE chưa biết. README/hmw.js/session-end đều mô-tả subfolder. C8 dual-form-acceptance close-gate cũng chưa. (2) **REFINE(b) detector = MISSING HOÀN-TOÀN.** `find .claude -name *.js/*.ps1`=CHỈ `hmw.js`(=engine ≠ detector). `.claude/hooks`+`.claude/scripts` KHÔNG tồn-tại. Repo-wide grep `bypass|scan.*runs` script=0. SE KHÔNG có bộ-dò chống-lách-engine → 3-function (whitelist/path-variants/launch-key-anchor)+relation-acceptance = n-a. **MET (đừng nhạ oan):** C3 committed THẬT — `git check-ignore runs`=exit1(NOT-ignored)+`git ls-files runs`=22 file (cả hai nấc). C4 per-turn real (`invest-synthesis.md` 43-dòng). C5 3-layer wired: L1 README:51(convention em-main) · L2 `session-start.md:71` orphan-scan `runs/*/` closed=⏳+harvest-rỗng · L3 `session-end.md:51` close-gate idempotent 5-trục. C6 ledger 2-beat (`_ledger.md:7`, 5 run đều CLOSE-beat+wf_). C7 caveat present (README §69-73 no-overclaim/fragile/G-015 TRACKED≠enforced). ⚠️ sub-md/ chỉ `.gitkeep` (read-only sub→em-main scribe, design KHÔNG phải miss). Tag `[s71, part-c-audit, subfolder-not-flat, detector-MISSING, c3-committed-real]`. diff --git a/.claude/agent-memory/reviewer/MEMORY.md b/.claude/agent-memory/reviewer/MEMORY.md index 7fe6ec9..273c049 100644 --- a/.claude/agent-memory/reviewer/MEMORY.md +++ b/.claude/agent-memory/reviewer/MEMORY.md @@ -61,6 +61,8 @@ Adversarial pre-commit reviewer SOLUTION_ERP. Read-only verify + live curl prod ## 📅 Recent activity (FIFO — older → archive/git) +- **2026-06-19 (S76 Part2+3 PE budget-edit BADGE display-only — Lens-2 FE badges+render-safety, PASS, 0 blocker):** 2 cờ bool CanEditProBudget/CanEditCcmBudget vào 2 DTO approver (AwLevelDto designer + PurchaseEvaluationApprovalLevelApproverDto PE-flow), suy từ ROLE (KHÔNG đổi authz). Badge "✎ NS PRO" (amber) / "✎ NS CCM" (sky). **PASS Lens-2:** (1) **role-set KHỚP gate** — `proBudgetEditors=GetUsersInRoleAsync(Procurement)∪Admin` / `ccmBudgetEditors=CostControl∪Admin` (ApprovalWorkflowV2AdminFeatures.cs:155-157 + PurchaseEvaluationFeatures.cs:976-978) đảo-chiều set-lookup 3-query/req no-N+1; khớp 1:1 gate thật canEditPro/canEditCcm `:800-801`=isAdmin‖Procurement / isAdmin‖CostControl; AppRoles consts verified (`AppRoles.cs:5,9,10`). (2) **role-set DEFINE trước cả 2 site** — PE-flow define `:978` < approvalFlow `:1045` < currentApproval `:1064` (cùng V2-branch scope); designer define `:155` < ToDto `:184`. (3) **DTO flag flows CẢ flow+current** — `PurchaseEvaluationApprovalFlowLevelDto.Approvers` (`:152`) + `PurchaseEvaluationCurrentApprovalDto`-path (`:145`) đều dùng `PurchaseEvaluationApprovalLevelApproverDto` → badge hiện ở Panel-flow lẫn current. (4) **PeWorkflowPanel `.join('/')→map`** giữ separator "/" (`{i>0 && /}`), giữ case rỗng `length===0?'(chưa cấu hình)'`, key=`a.userId` SAFE (validator `HaveNoDuplicateApproverInSameLevel:289` chặn dup ApproverUserId trong 1 level → no React key-collision). (5) **render-safety** — designer wrapper `flex`→`flex flex-wrap` + panel `flex flex-wrap gap-x-1 gap-y-0.5` → badge KHÔNG vỡ layout khi nhiều approver/badge. (6) **mirror 2-app PERFECT** — PeWorkflowPanel fe-admin==fe-user byte-IDENTICAL cả base lẫn now (diff empty); types +2 cờ ở CẢ 2 app (admin:235-236/user:238-239), block diff-identical (chỉ pre-existing inline-comment `//0-based` lệch = noise KHÔNG do change này). (7) **FE typecheck CLEAN** cả 2 app (`tsc --noEmit` exit 0). no mock/alert/TODO. **⚠️ SPEC-vs-DIFF MISMATCH (em-main framing wrong, NOT a code bug):** spec nói "KHÔNG migration" nhưng diff BUNDLES S76 Part1 (migration `AddProBudgetSplitToPeWorkItemBudget` + PeWorkItemBudget domain + PeBudgetSummaryDto + PeDetailTabs 306-LOC matrix rewrite WIRED PUT /budget/pro). Part1 spot-check sound (mig 3-file OK pure-ASCII gotcha#30-respect, PUT wired real not-mock). **🟡 MAJOR race STILL PRESENT (carry-over Part1, line 64 entry):** PeDetailTabs 2 PRO cell (`:1283` proInitial + `:1305` proAdjust) cùng `proMut.mutate` echo sibling từ `bs` server-snapshot, `invalidate()` fire-and-forget (`:1161` không await) → double-Save0 && !source` → new `(length===0 || !source)`, 2-app SHA-twin `4d6c89d9`. **No new deadlock — 4-fact:** (1) fix CHỈ THÊM disable-cond `length===0` lên branch đã unreachable-by-invariant (submit-guard `:194` hard-block winnerQuoteTotal<=0 ALL-paths → Ncc candidate luôn ≥1 ở ChoDuyet) ⇒ không sinh lockout mới; (2) có giá→chọn→source set→`priceMissing=false`→nút "Xác nhận" mở→duyệt OK; (3) empty (giả định)→nút khoá + amber `:537` "nhập PRO/CCM hoặc chọn NCC" = lối-thoát RÕ, setter-path KHÔNG phase-gated (mirror-budget) cho nhập giá bất kỳ lúc→candidate xuất hiện→mở lại (no hard-lock); (4) intermediate-approve `shouldPickPrice=false` (chỉ `currentIsFinalApprover||finalizeByCcm`)→nút mở bình thường, khớp BE ApplyApprovedPrice chỉ terminal `:885`+CCM-deleg `:853` (intermediate advance `:870/:893` không gọi). 7-layer threading 0-drop re-confirm (ctrl `:129/:337`→cmd `:462`→handler `:515`→iface `:30`→svc `:47`→ApproveV2 `:822`). OR-of-N `currentIsFinalApprover` true mọi viewer cấp cuối (ComputeLevelStatus `:987` position-based) nhưng nút dialog `disabled=blockedByV2Level`+`!isDisabled&&setTarget` `:310/:320`→non-approver không mở→price-selector vô hại. **LEARNED:** "fix tạo deadlock?" = THÊM-disable lên branch-unreachable-by-invariant không thể sinh lockout; verify lối-thoát = setter KHÔNG phase-gated (giá nhập-được bất kỳ lúc) + amber-message ⇒ user luôn thoát empty-state. Tag [s72ter-wire, verify-fix, no-new-deadlock, escape-hatch-amber, 7layer-0drop]. - **2026-06-18 (S72ter Mig 54 AUTHZ+SECURITY lane double-check — uncommitted priceMissing FE-fix + committed 1d86abc re-verify, PASS, 0 issue):** anh giao 3 lane laser (a setters / b CCM-finalize bypass / c controller authz) on commit 1d86abc (deployed Run #313) + 1 uncommitted FE-fix. **Uncommitted diff = 2 LOC product only** (`priceMissing` both apps, SHA-identical `4d6c89d9`) + memory/ledger noise — em main đúng kỷ luật chỉ-touch-2-file. **(a) PASS** — `PeSuggestedPriceFeatures.cs` cả 2 setter ForbiddenException TRƯỚC mọi mutate+SaveChanges (load+NotFound→role-gate `:40-41`/`:109-110`→mutate); role đúng PRO=Admin‖Procurement, CCM=Admin‖CostControl; AppRoles consts tồn tại (`:5,9,10`). Phase-guard cố-tình-thiếu, documented mirror-budget S61 (non-regression). **(b) PASS no-bypass — 3 gate trực-giao chặn non-CostControl finalize-bỏ-CEO, TẤT CẢ throw TRƯỚC `Phase=DaDuyet`(:854):** (1) approver-match `:702-713` non-admin phải ∈ pendingLevel.ApproverUserId else Forbidden → forged-caller-not-at-level KHÔNG tới được finalize block; (2) `finalizeByCcmDelegation:830-851` threshold-null→Conflict / role≠CostControl→Forbidden / `winnerQuoteTotal>=ceoThreshold` strict-`<`→Conflict — 3 throw trước set; (3) block `return` no-fallthrough. `winnerQuoteTotal` recompute server-side từ Suppliers+Quotes.ThanhTien của SelectedSupplier (`:839-847`) KHÔNG trust client; threshold từ DB `aw.CeoApprovalThreshold`. skipToFinal+finalizeByCcm combo safe (skipToFinal `:818` return non-last-slot HOẶC `:797` no-op fall-through last-slot → finalize once, 3 guard vẫn áp). **(c) PASS** — class `[Authorize]:14` → 2 endpoint mới inherit any-auth, fine-grained ở handler Forbidden (gotcha#44-safe KHÔNG class-Policy-overstrict). **FE-fix sound strict-tightening:** old `length>0 && !source` để nút ENABLED khi candidates-empty → click → BE Conflict "Chọn 1 giá chốt"; new `(length===0 || !source)` disable nút khớp amber empty-state `:537` (trước fix message-hiện-cùng-nút-enabled = UX mâu thuẫn). `winnerQuoteTotal:number` non-null → candidates-non-empty thực tế (submit-guard >0), fix thuần defensive nhưng đúng. **LEARNED:** "finalize-bypass?" load-bearing proof = đếm guard giữa caller-entry và state-mutation + xác nhận MỖI guard throw TRƯỚC mutation đầu tiên (đây Phase=DaDuyet) + recompute-vs-trust-client của giá-trị-so-ngưỡng (winnerQuoteTotal server-Sum, không nhận body) → 3 gate độc lập (approver-match ∩ role ∩ amount Pattern S67: private-static seed/init → invoke qua REFLECTION (`GetMethod(name, NonPublic|Static)` + `Invoke(null, [db, roleManager, NullLogger.Instance])`); seed MenuItem rows TRƯỚC Permission (FK MenuKey→MenuItem.Key Cascade, SQLite Error 19 nếu thiếu). Cycle-guard test: SqliteDbFixture đủ (no User); rollup-count test cần IdentityFixture (đếm User.DepartmentId active). Run: `dotnet test SolutionErp.slnx --nologo --verbosity minimal -p:BuildInParallel=false -maxcpucount:1` (MSBuild OOM → serialize build) @@ -54,6 +54,7 @@ Test theo CODE (single source truth), document mismatch header comment + report. ## 📅 Recent activity (last 10 FIFO) +- **2026-06-19 (S76 PE Mig 56 PRO-column-split — SPEC-CHANGE + compile-fix arity + test-after):** +5 test `PeWorkItemBudgetTests.cs` → **339→344 PASS** (45 Domain + Infra 294→299, 0 fail). BE done+builds (Mig 56 `AddProBudgetSplitToPeWorkItemBudget`, em main). **SPEC:** `UpdatePeBudgetProCommand` record 3→**4 param** `(PeId, ProInitialAmount, ProAdjustmentAmount, ProNote)` (mirror CCM Initial/Adjustment); handler set `rec.ProInitialAmount + rec.ProAdjustmentAmount` absolute-set (KHÔNG còn `ProEstimateAmount`). Validator `ProInitialAmount>=0` when HasValue, `ProAdjustmentAmount` KHÔNG ràng dấu (ÂM OK), ProNote max1000. **⚠️ Handler ctor `(IApplicationDbContext db, ICurrentUser)` 2-dep UNCHANGED** — chỉ COMMAND record đổi arity. Capability `PeBudgetSummaryDto.FullAmount` (PurchaseEvaluationFeatures.cs:849-864): `hasPro = ProInitial∥ProAdjust not-null`; `proFull = ProInitial+ProAdjust`; `full = hasCcm ? CCM(Initial+Adjustment) : proFull`; `FullIsEstimate = !hasCcm && hasPro`. DTO +2 field cuối `ProInitialAmount`,`ProAdjustmentAmount`. **Compile-fix:** 5 PRO call-site `new UpdatePeBudgetProCommand(...)` thêm arg ProAdjust (regex `\([^,]+,[^,]+,[^,)]+\)` = 0 hit sau vá xác nhận hết 3-arg). **Assertion-vá:** 5 PRO-handler test `.ProEstimateAmount`→`.ProInitialAmount` (handler giờ set ProInitial). **Full-fallback seed-vá:** capability đọc ProInitial → `ProEstimateAmount=500m`→`ProInitialAmount=500m` (2 chỗ region 5 + 1 CanEdit seed). **NEW 5:** (1) PRO set both ProInitial+ProAdjust ÂM(-20tr)+note all-persist 1-lệnh / (2) validator ProInitial<0 invalid (khác ProAdjust) / (3) validator ProAdjust ÂM valid (mirror CCM no-sign-constraint) / (4) full=proFull(100+50=150)+DTO surface ProInitial/ProAdjust riêng / (5) full=proFull ÂM-adjust(100-30=70 no-clamp = tổng đại số). CCM-present test thêm seed PRO(500+20) chứng minh PRO bị bỏ qua trong full. **No prod bug** — code đúng spec (PRO mirror CCM, ProAdjust ÂM intentional, ProEstimateAmount=LEGACY Mig 56 backfill→ProInitial FE bỏ). Tag [s76, pe-mig56, pro-column-split, spec-change, compile-fix-arity, proadjust-negative-allowed, profull-no-clamp, mirror-ccm, full-fallback, test-after]. - **2026-06-18 (S74 PE Mig 55 CcmNote — compile-fix arity + test-after):** +5 test `PeWorkItemBudgetTests.cs` → **334→339 PASS** (45 Domain + Infra 289→294, 0 fail). Mig 55 additive: `PeWorkItemBudget.CcmNote` (string? nvarchar(1000)) + `UpdatePeBudgetCcmCommand` record 3→**4 param** `(PeId, InitialAmount, AdjustmentAmount, CcmNote)` + DTO `PeBudgetSummaryDto +CcmNote` (slot after AdjustmentAmount). **⚠️ Handler ctor `UpdatePeBudgetCcmCommandHandler(IApplicationDbContext db, ICurrentUser)` 2-dep UNCHANGED** — chỉ COMMAND record đổi arity, KHÔNG đụng handler signature. **Compile-fix:** grep `new UpdatePeBudgetCcmCommand` toàn tests/ = 3 hit (line 388/407/421 PeWorkItemBudgetTests), thêm `null` arg4 mỗi cái. **NEW 5 test (region 4b, mirror ProNote pattern S61):** (1) CostControl set CcmNote→persist / (2) Admin set→persist / (3) **null=clear absolute-set** (pre-seed CcmNote="cũ" → null request → BeNull, chứng minh CLEAR không phải vốn-dĩ-null) / (4) **Procurement→ForbiddenException + record no-mutate** (fail-closed: role-gate line 152-157 TRƯỚC WorkItemId-check + EnsureTrackedAsync + side-effect; AsNoTracking re-read assert CcmNote/Initial/Adjustment giữ nguyên) / (5) Initial+Adjustment+CcmNote all-persist-together 1 lệnh. Handler `rec.CcmNote = request.CcmNote` absolute-set; changelog "ghi chú CCM cập nhật" khi `oldCcmNote != request.CcmNote`. **No prod bug** — code đúng spec (mirror ProNote, fail-closed role-gate giống Initial/Adjustment). Tag [s74, pe-mig55, ccmnote, compile-fix-arity, absolute-set-null-clear, fail-closed-no-mutate, mirror-pronote, test-after]. - **2026-06-18 (S72 PE Mig 54 anh Kiệt FDC — SPEC-CHANGE + 2 test-after FINANCIAL):** +28 test → **306→334 PASS** (45 Domain + Infra 261→289, 0 fail). BE Mig 54 committed+builds. Mig 54 fields PE: `ProSuggestedMin/MaxPrice` + `CcmSuggestedPrice` + `ApprovedPriceAmount` + `ApprovedPriceSource`; `TransitionAsync`/`ApproveV2Async` +3 param `finalizeByCcmDelegation`(bool) + `approvedPriceAmount`(decimal?) + `approvedPriceSource`(string?). **① SPEC-CHANGE CCM-finalize AUTO→OPT-IN — UPDATE `PeCcmThresholdFinalizeTests` 6→11 (Services ns):** S69b AUTO (gói=ceoThreshold`→Conflict, strict-`<` giữ nguyên) → ApplyApprovedPriceOnFinalize(BẮT BUỘC giá chốt human)→DaDuyet. ApproveAsync helper +3 optional param. Cover: flag-true+ngưỡng→Conflict (no-mutation reload-assert) / flag-true non-CCM→Forbidden / flag-true threshold-null→Conflict / flag-true slot-cuối+giá→DaDuyet 1-Approval no-double / flag-true đủ-ĐK null-giá→Conflict("giá chốt"). **② NEW `PeApprovedPriceFinalizeTests` 10 (Services ns) — ApplyApprovedPriceOnFinalize (private-static):** terminal DaDuyet normal-advance human valid-price→bind / null-price human→Conflict NOT-finalized(ChoDuyet reload) / garbage-source→Conflict / Theory 4 valid-source {Ncc,ProMin,ProMax,Ccm} each-set + **isSystem null-price→no-throw no-set qua REFLECTION** (⚠️OBSERVATION report: isSystem KHÔNG reachable qua public ApproveV2Async — approve-branch gate `decision==Approve` còn isSystem cần `AutoApprove`; PE no SLA-auto-job chỉ Contract SlaExpiryJob → isSystem-exempt = defensive/dead qua V2-approve; test contract mức UNIT) + đối-chứng isSystem-false→Conflict + isSystem-true amount+garbage-source→Conflict. Reflection unwrap TargetInvocationException→inner. **③ NEW `PeSuggestedPriceSetterAuthzTests` 13 (Application ns) — 2 setter handler 2-dep db+ICurrentUser mirror PeWorkItemGuard:** PRO-cmd Procurement/Admin set Min/Max·CostControl/Drafter→Forbidden+no-set·validator Min>Max invalid+negative invalid+single/null valid·unknown-PE→NotFound(existence TRƯỚC authz). CCM-cmd CostControl/Admin set·Procurement→Forbidden·validator negative invalid·NotFound. **No prod bug** — code đúng spec (OPT-IN finalize an-toàn-hơn AUTO, fail-closed order intentional). FakeCurrentUser configurable-roles. Tag [s72, pe-mig54, ccm-finalize-opt-in, spec-change, approved-price, applyapprovedprice-private-static-reflection, issystem-unreachable-observation, suggested-price-setter-authz, fail-closed, test-after]. - **2026-06-17 (S69b PE 2 feature anh Kiệt FDC — test-before-merge SECURITY+FINANCIAL workflow):** +14 test → **292→306 PASS** (45 Domain + Infra 247→261, 0 fail). BE done+builds, mirror harness PeSubmitGuardAndBypassTests/PeWorkItemGuardTests. **FEATURE B value-threshold CCM-finalize (`PeCcmThresholdFinalizeTests.cs` 5, Services ns, ApproveV2Async line 816-854):** NV duyệt role=CostControl + `aw.CeoApprovalThreshold!=null` + `winnerQuoteTotal < ngưỡng` + chưa-slot-cuối → Phase=DaDuyet bỏ CEO + pointers/SLA null. **⭐ BOUNDARY load-bearing: predicate `winnerQuoteTotal < ceoThreshold` STRICT-less-than (line 838)** → gói==đúng-ngưỡng = KHÔNG finalize = advance. Cover: (1)⭐LOAD-BEARING CCMngưỡng→advance / (3) threshold-null→advance kể-cả-CCM+gói-1đ (backward-compat) / (4) non-CCM(PRO)PRO>CCM intentional). FakeCurrentUser configurable-roles ctor. Reuse NoOpNotificationService internal qua `using ...Tests.Services`. Tag [s69b, pe-ccm-threshold-finalize, value-threshold, strict-less-than-boundary, role-based-routing, urgent-toggle-authz, forbidden-no-mutation, else-if-short-circuit, test-before-merge]. diff --git a/.claude/skills/ef-core-migration/SKILL.md b/.claude/skills/ef-core-migration/SKILL.md index 99e451a..d429620 100644 --- a/.claude/skills/ef-core-migration/SKILL.md +++ b/.claude/skills/ef-core-migration/SKILL.md @@ -1,6 +1,6 @@ --- name: ef-core-migration -description: Tạo/sửa/revert EF Core 10 migration cho SOLUTION_ERP. Dùng khi thêm entity mới, thay đổi schema, rollback migration, debug DesignTimeDbContextFactory fail. Đã có nhiều migration (số hiện tại → docs/STATUS.md canonical; mới nhất Mig 55 AddCcmNoteToPeWorkItemBudget, S74). Snapshot + Designer + Migration 3-file rule bắt buộc commit đủ. +description: Tạo/sửa/revert EF Core 10 migration cho SOLUTION_ERP. Dùng khi thêm entity mới, thay đổi schema, rollback migration, debug DesignTimeDbContextFactory fail. Đã có nhiều migration (số hiện tại → docs/STATUS.md canonical; mới nhất Mig 56 AddProBudgetSplitToPeWorkItemBudget, S76). Snapshot + Designer + Migration 3-file rule bắt buộc commit đủ. when-to-use: - "thêm migration" - "EF Core migration" @@ -16,7 +16,7 @@ when-to-use: > **Context:** .NET 10 + EF Core 10 + SQL Server. DbContext: `ApplicationDbContext` ở `Infrastructure/Persistence/`. Startup: `SolutionErp.Api`. -## Migration history (số hiện tại → `docs/STATUS.md` canonical; mới nhất Mig 55) +## Migration history (số hiện tại → `docs/STATUS.md` canonical; mới nhất Mig 56) | # | Name | Tables added / changed | |---|---|---| @@ -75,6 +75,7 @@ when-to-use: | **53** | **`AddPeUrgentAndCeoApprovalThreshold`** | **🎯 S69 — PE +`IsUrgentByPro`/`IsUrgentByCcm` (bit, cờ gấp PRO đỏ/CCM xanh) + ApprovalWorkflow +`CeoApprovalThreshold` `decimal(18,2)?` (ngưỡng gói CEO — CCM role CostControl duyệt-final khi winnerQuoteTotal < ngưỡng). 3 AddColumn, no new table. anh Kiệt FDC.** | | **54** | **`AddPeSuggestedAndApprovedPrice`** | **🎯 S73 — PE +5 cột giá đề xuất (ProSuggestedMin/Max + CcmSuggested + ApprovedPriceAmount + ApprovedPriceSource) additive-nullable — CEO chọn giá chốt + CCM duyệt-done opt-in. anh Kiệt FDC go-live 22/06.** | | **55** | **`AddCcmNoteToPeWorkItemBudget`** | **🎯 S74 — PeWorkItemBudget +`CcmNote nvarchar(1000)?` (ô "Ghi chú từ CCM" mirror ProNote). AddColumn-only, no new table.** | +| **56** | **`AddProBudgetSplitToPeWorkItemBudget`** | **🎯 S76 — PeWorkItemBudget +`ProInitialAmount`+`ProAdjustmentAmount` decimal(18,2)? (cột PRO "Ban hành lần đầu"+"V0/hiệu chỉnh", mirror CCM Initial/Adjustment). AddColumn additive + `Sql()` data-migrate `ProInitial=ProEstimate WHERE ProEstimate NOT NULL` (4 rows prod, gotcha #64). no new table. Form ngân sách → MA TRẬN 3 cột Dự án/PRO/CCM (bảng lưới `
`).** | Total: **88 bảng** dbo + `__EFMigrationsHistory` (re-ground S62 cicd `sys.tables`; last schema-changing Mig 50 net 93→88 — Mig 51-55 đều AddColumn-only, không đổi số bảng). Xem `docs/database/schema-diagram.md` migration table + §11-15 module ERD (§16+ Mig 27-55 chi tiết pending). diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md index 188bec6..6854222 100644 --- a/docs/HANDOFF.md +++ b/docs/HANDOFF.md @@ -2,7 +2,27 @@ > **Tiering rule (S40):** giữ **2-3 session gần nhất**. Cũ hơn → `docs/changelog/sessions/`. Full brief history pre-S40 → `docs/_archive/HANDOFF-preS40-fullhistory.md`. -**Last updated:** 2026-06-18 (S75 — **Harness-11 adopt: engine bộ-nhớ-và-governance tự-bảo-trì + 2 double-check** — anh giao `/check-email AI_INFRA` + `/adap-apply` (mỗi stage workflow-review) + double-check ×2 + finalize report + push + session-end. em main + **5 Workflow** (audit `wf_7fdc3bd5-930` → implement `wf_c5e5844e-7c1` → review `wf_d7ca1ff8-942` → double-check `wf_a0b68d2f-30e` → checklist-verify `wf_39cd4cbe-f07`). **0 production code.** Built 2 NO-API detector-script (`governance-detectors.ps1` C1-C5+B3 runtime **26 flag** bắt drift thật, C4 0-self-match · `memory-archive-gate.ps1` PHẦN A hysteresis/keep-floor/2-strike/A7 186/186) + `docs/governance/harness-11-engine.md` canonical (3-tier D5/D6/D7 + one-direction-lock D8) + **B1 ×11 count→pointer** (drift mig53→55/test306→339/gotcha68→69/table93→88 RESOLVED) + cadence-wire D1/D2 + agents/README. **completeness-gate FORMAL ĐẠT** (B 4/4 + C 5/5 + D 11/11; D5/D6/D7 explicit + D8 codify=YES). gotcha #30 reinforced (box-glyph ├└─ KHÔNG vào .ps1, kể cả qua Edit). 3 commit `e70c046`+`ae957c4`+`aa09e99` pushed + adap-report + email ai_infra (`2316773229f2`). State GIỮ NGUYÊN Mig 55·88 bảng·339 test·gotcha 69·bundle `BYF5vIMJ`/`CB-tiRxd`. **🔴 NEXT (anh):** restart CLI (cadence §2.1.3/§L.b + ef-core skill); curate reviewer 38.8KB+inv 31.5KB+cicd 26.8KB over-cap. Chi tiết → `2026-06-18-S75-harness-11-adopt.md`. · **Prev S74** — **PE ô "Ghi chú từ CCM" ngân sách gói thầu (Mig 55)** — anh forward 2 luồng Zalo (Trà Sol + Kiệt FDC) "CCM giống PRO cho nhập + ghi chú, hiện 0 hết" → em-main BE+Mig55 ∥ implementer-frontend FE 2 app SHA-mirror ∥ test-specialist (chết rate-limit → recover-disk). 2 fork anh chốt: **thêm CcmNote (Mig 55)** + **GIỮ phân vai CCM** (CostControl/Admin). Entity +CcmNote mirror ProNote · UpdatePeBudgetCcmCommand +CcmNote absolute-set role-gate · DTO+controller+mapping · FE dòng "Ghi chú từ CCM" gate canEditCcm. Test 334→**339** (+5). commit `8655ebf` (cicd **Run #315 PASS** — bundle admin `BYF5vIMJ`/user `CB-tiRxd`, Mig 55 applied prod). **"0 hết không nhập" = tài khoản thiếu role CostControl (KHÔNG bug)** → UAT bằng CCM/Admin. **🔴 NEXT (anh/anh Kiệt):** UAT ô CCM bằng tài khoản CCM/Admin + carry "Ngưỡng giá CEO" Mig 54. Chi tiết → session log `2026-06-18-S74-pe-ccm-note.md`. · **Prev S73** — **Mig 54 PE giá đề xuất PRO/CCM + CEO chọn giá chốt + CCM duyệt-done ô-tích (anh Kiệt FDC, go-live thứ Hai 22/06)** — em main + 6 sub + 2 review-workflow, 2 commit prod-verified (`1d86abc` feature Run #313 + `6aa4dcb` FE empty-candidates fix). Mig 54 5-cột additive-nullable + 2 setter role-gate + ApproveV2 ③ AUTO→OPT-IN + ① bind giá chốt; FE 2 app SHA-mirror; test 306→334. **Workflow review:** R1 schema-force 1/4 lane → R2 free-text 2/3 PASS (rủi ro FE empty-candidates UNREACHABLE per submit-guard `:194`). Bài học: review fan-out free-text/hmw KHÔNG ép-schema. **Governance:** em lệch mandate auto-Workflow → cam kết báo-TRƯỚC-khi-lệch. **🔴 NEXT (anh/anh Kiệt):** cấu hình "Ngưỡng giá CEO" Designer + test 3 luồng + xác nhận CCM-trước-CEO. Chi tiết → session log `2026-06-18-S73-pe-gia-de-xuat-ccm-done.md`. · **Prev S72** — **Harness-10 flat-refine + checklist-v2 adopt (re-audit 3-workflow) + sleep NO-OP** — audit `wf_13868efb-ea7` → implement `wf_ac43b5ff-7d1` → review `wf_d482e10d-5dd` (Harness-9 B1+B2). SE BEHIND flat → migrate run-trace SUBFOLDER→FLAT (hmw.js:103 + 5 doc; 5 run cũ giữ subfolder C8) + 2 broadcast 06-18 adopt + `/sleep-recovery-memory-l2` port (A8) + detector TAILORED-OUT + hmw.js H4.5→H8 doc-drift. **REVIEW (B2) bắt 2 lỗi IMPLEMENT-self-assess sót** (hmw.js:52 schema-stale + auto-check overclaim) → fixed (auto-check WIRED thật). em-main containment-check tự bắt reviewer residual-write (raw-Workflow no-writeGuard) → revert. B3 self-correct (runs path/count 14→22). Sleep `all`=NO-OP. **0 prod code, state GIỮ NGUYÊN.** adap-report + email ai_infra (`5f511fe5c0f2`). **🔴 NEXT (anh):** restart CLI (FLAT-runtime hmw.js + wired sleep-check + sleep-recovery skill). **🔴 NEXT (em):** curate investigator-codebase L1 28973 OVER + watch FD/test-spec. Chi tiết → session log `2026-06-18-S72-harness-10-flat-refine.md`. **Prev S71 — Harness-10 adopt: tracked run-trace folder + checklist 9-10** — em main + 3 Workflow (invest `wf_9c2cd2cd-2e7` → implement `wf_e4e46725-231` → review `wf_636bc95b-939`) per mandate Harness-9 PART 2. **0 production code**. Migrate `.claude/workflows/wave-*/` gitignored → `runs//` **git-TRACKED** (run.md+sub-md/+harvest/) + `_ledger.md` 2-nhịp + 3-layer anti-miss (L1 em-main@P1 / L2 session-start orphan-scan / L3 session-end close-gate idempotent) + **containment shift** (Harness-2 B6 "mọi tracked-change=vi-phạm" → "tracked-change NGOÀI run-folder + code-disjoint=vi-phạm"). hmw.js wave→run-trace (accept `args.run` + alias `wave`, path `sub-md/`, `node --check` PARSE-OK). **Review độc-lập (R2+R3) bắt C5 L1 over-claim** (doc nói hmw.js prompt-builder emit L1 reminder, grep engine=0 → engine no-fs → fixed path-a: L1 = em-main @P1 convention) — dogfood mandate B2 đúng. checklist 9-10: Part A (Harness-9 memory) + B (adap 2-workflow) done S70, Part C (Harness-10) 8/8 MỚI. **State GIỮ NGUYÊN: Mig 53·88 bảng·306 test·68 gotcha·menu 54·bundle admin `BgNCjwsG`/user `CBvh0vtf`.** + CLAUDE.md test-flush 263→306 (resolve H1 stale). **🔴 NEXT (anh):** restart CLI (hmw.js RUN-TRACE runtime + carry §2.1.2/Cat-6/H8). Chi tiết → session log `2026-06-18-S71-harness-10-run-trace.md` (pending). **Prev S70 —** **Harness-9 adopt: L2 archive dark-matter recovery + adap 2-workflow mandate** — em main + 3 Workflow (investigate `wf_be952f3c-97f` → implement `wf_a58e0d15-beb` → audit `wf_9520d8cd-4fe`) + 2 monitor bootstrap. **0 production code** (governance/memory only, commit `f36aab8` pushed). 4 over-cap sub-agent curate L1→L2 + `archive/_INDEX.md` (mục-lục substring sha-keyed) + `.gist.md` (4-field distill-gen:1, verbatim FROZEN) → **cả 4 < 25KB auto-inject cap** (P1 curate-debt CLOSED; ~240KB archive hết RAG-dark). PART 2/3 process-mandate codify (adap-apply 2-workflow + agents/README Upgrade S70 + session-start §2.1.2). **0-byte-loss git+sha verified** (Stage C audit + em-main self-gate khi 2 reviewer no-StructuredOutput → recovery-path). +memory-budget.json (seed-by-measure) + measure-agent-memory.ps1 + .ragignore + doc-drift 4-cite flush. adap-report + email-back ai_infra (`7c07b716e775`). **State THẬT GIỮ NGUYÊN: Mig 53 · 88 bảng · 306 test · 68 gotcha · menu 54 · bundle admin `BgNCjwsG`/user `CBvh0vtf`.** **🔴 NEXT (anh):** restart CLI để activate session-start §2.1.2 budget-audit + pending H8 inherit + reviewer Cat-6. Chi tiết → session log `2026-06-17-S70-harness-9-l2-recovery.md`. **Prev S69 —** **Văn phòng số (E-Office) port + golive + PE cờ gấp/ngưỡng CCM — 4 deploy prod-verified Run #305→#308, HMW-mode ON, 2 workflow fan-out**: #305 foundation PURO (shared PageHeader/KpiCard/WidgetCard + Dashboard 2-cột + sync fe-admin index.css đóng drift S66-S68) → #306 **re-skin TRỌN 10 page** (PURO layout + CSS Hồ sơ NS, phẫu-thuật-giữ-100%-logic) → #307 **Office golive public** read+create **16-key allow-list mọi role** (mirror S65, chạy SAU revoke; excluded Off_PhongHop_Manage/Off_AttendanceReport/Off_ChamCong; cicd DB-verify 16/16×13 role) → #308 **PE cờ gấp PRO/CCM + CCM duyệt-final theo ngưỡng giá trị** Mig 53 (anh Kiệt FDC sau họp sếp). **State THẬT: Mig 53 · 88 bảng · 306 test (45D+261I) · 68 gotcha · menu 54 · bundle admin `BgNCjwsG`/user `CBvh0vtf` Run #308.** 2 gotcha NEW (**#67** Tailwind accent palette thiếu-stop vỡ-màu-im-lặng · **#68** stale-diagnostic-background-agent → chỉ tin build sau-cùng). 2 truncation #53 (impl-backend positional-record + impl-frontend) → em main recover-disk + self-gate. **🔴 NEXT (anh/anh Kiệt UAT):** (1) cấu hình "Ngưỡng giá trị gói CEO" trong Workflow Designer (null=luồng cũ) + test phiếu < ngưỡng → CCM duyệt là xong, ≥ ngưỡng → lên CEO; (2) test cờ gấp PRO(đỏ)/CCM(xanh) → badge + notify CEO; (3) **xác nhận:** quy trình đặt CCM(CostControl)-trước-CEO + CEO = role Director (đích notify). **C (sau duyệt → chuyển phiếu đến dự án) chờ anh Kiệt spec form.** **NEXT (em):** 🔴 curate cicd-monitor **65.2KB** (worst, trend tăng) + inv-codebase 47 + reviewer 43.5 over-cap · doc-flush docs/CLAUDE.md full. Chi tiết → session log `2026-06-17-S69-vanphong-golive-pe-urgent-threshold.md`. **Prev S68 —** **Hồ sơ NS header chi tiết NV: làm nổi bật + fix tên render đen→trắng** — em main solo, 3 commit (2 FE + 1 docs), 2 deploy prod-verified Run #303-304 (anh UAT realtime): badge màu theo trạng thái + dòng meta đậm + tên to/drop-shadow (#303 `6983609`) → anh báo "tên đen nền xanh ko nổi bật" → diagnose **rule `h1-h4{color:#0b1220}` viết NGOÀI `@layer` thắng `text-white`** (Tailwind v4 unlayered > all layers, **gotcha #66 NEW**, load-bearing ~30+ heading → fix ĐIỂM `text-white!` không move @layer) + thu nhỏ `text-lg` (#304 `37752eb`). Bootstrap: 2 monitor RE-REPORT CLEAN + 2 doc-drift fix (dep-audit 64→65 · root CLAUDE 263→286, `11bc96d`). **State THẬT: Mig 52 · 88 bảng · 286 test · 66 gotcha · menu 53 · bundle admin `CNUv1jxY`/user `CpOskeS1` Run #304.** ⚠️ curate-debt P1: cicd-monitor **44.1KB**. Chi tiết → session log `2026-06-16-S68-hoso-header-name-color-fix.md`. **Prev S67 —** **buổi sản phẩm LỚN, 6 deploy prod-verified Run #297→#302** (anh + anh Kiệt FDC UAT realtime, HMW-mode ON): fe-user Hồ sơ NS đồng nhất font + chữ đen→**xanh đậm `brand-800`** (#297) · **fe-admin mirror master-detail** + vá accent tokens index.css [đóng pending lớn nhất HRM go-live, page SHA256 ×2] (#298) · **+23 test-after HRM** → 263→**286** (#299) · **list flex-row gọn** [hết tràn ngang rail] + đồng nhất cỡ chữ ×2 (#300) · **PE Link hồ sơ auto-detect** web→hyperlink/`O:\`→Copy (#301) → **link `file://` bấm-thử** (#302). **Research 3-agent:** auto-mở `O:\` từ web = chỉ **Edge GPO `IntranetFileLinksEnabled`** (zero-per-machine) one-click thật; default chặn https→file://. **State THẬT: Mig 52 · 88 bảng · 286 test (45D+241I) · 65 gotcha · menu 53 · bundle admin `CcrZqfht`/user `DniDFUB_` Run #302.** test-specialist trunc #53→recover-disk; 0 prod bug. Chi tiết → session log `2026-06-16-S67-hoso-visual-pe-link-research.md`. **Prev S66 — session-end closeout em-main-solo:** adopt **Harness-8 all-inherit** (7 sub demoted `claude-opus-4-8`→`inherit` → cả 11=inherit, gỡ two-tier H4) + **cicd-monitor L1 curate 86.8→28.9KB** (byte-exact sed → `archive/2026-06.md`) + **ef-core skill doc-flush Mig 50→52** + check-email AI_INFRA (**0 thư mới se-directed**). ~17 file docs/gov/config, **0 production code** → state THẬT GIỮ NGUYÊN (Mig 52 · 88 bảng · 263 test · 65 gotcha · menu 53 · bundle `BDwV5d0X`/`DbVv6rsf`). ⚠️ **Restart CLI** → H8 runtime (frontmatter no hot-reload) + reviewer Cat-6. adap-report + email-back ai_infra (`fa7f690d` MATCH). Chi tiết → session log `2026-06-16-S66-closeout-harness8-cicd-curate.md`. **Prev S65 — HRM go-live: public Hồ sơ Nhân sự + trang master-detail giống NamGroup + Department hierarchy Mig 51 + PE Link hồ sơ Mig 52 + gốc cây SOLUTION COMPANY**, ~6 deploy prod-verified Run #289→#295, anh + anh Kiệt FDC UAT realtime. **State THẬT: Mig 52 · 88 bảng · 263 test (45D+218I) · 65 gotcha · menu 53 · bundle admin `BDwV5d0X`/user `DbVv6rsf` Run #295.** Workflow fan-out chạy THẬT lần đầu [PE] — BE∥FE parallel OK nhưng FE+reviewer return-rỗng #53 → em main recover-disk + self-gate. gotcha #65 [build csproj con ≠ slnx → CS7036]. Chi tiết → session log `2026-06-16-S65-hrm-golive-employee-masterdetail-pe-link.md`. **Prev S64** adopt **Harness-7 writing-quality floor** qua `/adap-apply` + email ai_infra — em main solo, 1 commit `6afde19` docs/gov-only. Outward comms = tiếng Việt câu-hoàn-chỉnh; nội bộ giữ nén (asymmetric). reviewer +Category 6 (verified-pending-restart → cần restart CLI). Broadcast body-hash verified KHÔNG mis-stamp (gotcha #61 UTF-8). Prev S63 docs-closeout bù S60/S61/S62 — 3 session product ship CODE prod-verified nhưng KHÔNG closeout docs (UAT realtime anh Kiệt FDC). **State THẬT: Mig 50 · 88 bảng · 263 test · 64 gotcha · menu 53 · bundle admin `0xKYGhhf`/user `C81ZdG9G` Run #286.** S60-62 = PE ràng buộc gửi-duyệt + gỡ "Từ chối" (S60) · Mig 50 ngân sách per-gói-thầu Excel anh Kiệt + XÓA module Budget cũ (S61) · vượt-NS cảnh-báo-mềm (S62). Reconcile stray reviewer cwd-misland + count-flush 4 file. Chi tiết → session log `2026-06-12-S60-S62-pe-budget-workitem-softwarning.md`. Prev Session 59 ( **6 đợt ship prod-verified Run #273→#278**: wipe transactional testing data (10 PE + 7 HĐ demo + 64 notif = 0, mã reset → phiếu thật đầu tiên team tạo = **PE/2026/A/001** ✓) `56882ac` #273 · PE tree Panel 1 chốt 4 tầng **Năm > Dự án > Hạng mục > Phiếu** `0eafcd3` #274 · dọn 15 mã hạng mục demo "tự đẻ" (chị Trà Sol) + gỡ seed gốc, WorkItems 86→**71** `bbd1554` #275 · **rename 71 mã đúng format PMH anh Kiệt** (`MAT-n`/`SUB-n`/`MEP-SUB-n`/`MEP-EQU-n` + tên "STT nhóm tên"; **DB-trước-code-sau** gotcha #62 + sqlcmd `-f 65001` gotcha #61) `c869d26` #276 · UAT vòng 1: NEW `ui/SearchableSelect` gõ-lọc bỏ dấu (Hạng mục/Dự án) + auto Địa điểm + điều khoản đa dòng `faed59f` #277 · UAT vòng 2 (anh chốt ×2): ẩn Trả lại/Từ chối khi tự duyệt phiếu mình soạn + quick-add NCC ngay form (POST /suppliers any-auth, authz probe 4/4) + NCC gõ-tìm A-Z + upload multi-file `9c330d2` #278 · UAT vòng 3-6 realtime (#279/#281 cancelled-supersede-benign): bảng NCC table-fixed `f21c55d` + bỏ ô Tên ngân sách `69997da` #280 + GỠ field Điều khoản TT mọi form `80b64dd` + bỏ nút Thêm hạng mục `792c030` **#282 FINAL**. Tổng 10 đợt (8 PASS + 2 cancelled-benign). Bundle FINAL admin **`B1DtNT9C`**/user **`D6uF3Mln`**. Test 240. Gotchas 62. 0/14 spawn truncated. → session log `2026-06-11-S59-wipe-tree-pmh-uat-batch.md`. Prev S58 — **5 đợt prod-verified Run #382/#383/#384/#386** (#385 cancelled-supersede-benign): lock-demo-user fix (việc sếp deadline 15:00 ĐÓNG TRỌN — gotcha #60/E-008/AS-12, root cause password 11<12 từng phát hiện S22 nhưng const không fix) + tạm ẩn HRM/Office/Cá nhân + Danh mục cuối (`6c5fd26` #383) + **fe-user redesign theo UI/UX guide AI_INFRA giữ brand** (`e959f72` #384) + **brand polish ×2 app "thấy rõ"** (`ea793a4`: stripe 4px đỉnh + thead brand) + **PE gộp Tên-gói-thầu = chọn Hạng-mục** (anh Kiệt FDC chốt, `3ebaf84` #386 — bundle final admin `DMm9rtNA`/user `BUkOMn_Y`). Email AI_INFRA processed (guide + ACK H4 ACCEPT). Test **240**. → session log `2026-06-11-S58-lock-fix-hide-modules-redesign-pe-merge.md`. Root cause 2 tầng: lock-list = population Dev-only + `DemoUserPassword` 11 ký tự < prod RequiredLength=12 → CreateAsync silent-fail từ trước tới giờ (= "helpdesk inert" S56). Fix union 20 UAT email + password 12 ký tự → prod 55 user/34 locked, nv.cao+nv.truong sống, 5 real staff tạo. gotcha #59+#60. Commit `5998163`. Prev S57bis — **PE gắn Hạng mục (Mig 49) + Pe all-role + menu Cá nhân + Harness-4 runtime-VERIFIED**. Test 228→**240**. Bundle `CP4CB1ym`/`BmZ3VHnm`. Commit `17b23a4`+`dd117b7` → Run #381 PASS+1PARTIAL (lock NO-OP → RESOLVED S58). Prev S56 — **Pre-golive verify sweep + golive-harden 4 fix — Run #379 PASS, code golive-ready**. WF1 `pre-golive-verify` 7-stream + adversarial → 6 PASS/1 CONCERN/0 blocker = GO (key finds = ops not code). WF2 `golive-harden` fix 4: #3 LeaveBalance lost-update→atomic ExecuteUpdate+Serializable tx (NO mig) · #5 ItTicket authz Forbidden-trước-NotFound · #6 DocxRenderer null-guard · #4 Travel/Vehicle ApproveV2 tests. Test 216→**228**. Bundle FROZEN `4SUwDLD8`/`XdKzt9LL`. `sys.tables` re-ground 92→**93**. gotcha **#58** NEW. reviewer StructuredOutput-fail→em main đỡ. **2 ops VPS pending** (gán user IT + tzutil UTC+7). FE Phase 2 redesign **deferred** (recon ready). Commit `a20cde8`. Prev S55 — **Nạp master data thật từ Excel (62 dự án + 71 hạng mục + 3 NCC) + Project +4 cột (Mig 48) — prod-verified**. HMW-mode ON. Commit `69cb393` → Run #377 PASS ~4m33s. Test 216 (compile-fix only). Bundle admin `B-d6893W`/user `XdKzt9LL`. `SeedRealMasterDataAsync` ungated idempotent → coexist demo. 2 agent return truncated (BE+reviewer) → em main disk/runtime-recover. Prev S54 — IT staff tự reassign ticket (cross-stack authz) — prod-verified. 1 code commit `ca4b602` → Run #376 PASS ~4m18s. Test 203→**216**. Bundle admin `DfCfHUE9`→`DmjI8Cmn`/user `_3S0BPJ2`→`YxL_MljK` (cả 2 rotate). NO migration. Task 1 Phase 9 Ops anh dừng. ⚠️ residual: 3 agent ghi MEMORY nhầm `src/Backend/.claude` → em main reconcile. Prev S53: gotcha #57 EXT Master Mig 47 + P11-D/E + database-agent verified-runtime.) +**Last updated:** 2026-06-19 (S76 — **PE ngân sách MA TRẬN 3 cột (Dự án|PRO|CCM) + bảng lưới `
` viền-ô + badge quyền-NS theo role** — anh Kiệt FDC + chị Trà Sol go-live. anh giao `/ultra-on` "step-by-step + workflow-review-per-step, hoàn chỉnh rồi deploy". **3 commit prod-verified:** `e33481e` (feature Run #318 — Mig 56 + ma-trận-3-cột + badge designer/flow `BhFDF9IJ`/`BAkuRl3C`) → `21d1f4e` (bảng lưới `
` theo phản hồi anh "chưa chia cột giống Excel", Run #319 **`jOqxW4-p`**/**`DbsznVvR`** smoke 3×200). Mig 56 +ProInitial/ProAdjust cột PRO (additive + data-migrate ProEstimate→ProInitial 4 rows prod). Part 1 mỗi phòng sửa cột mình (PRO canEditPro / CCM canEditCcm / Dự án FE-only). Part 2+3 badge "✎ NS PRO/CCM" cạnh approver (display-only suy-role) ở Designer + flow Duyệt NCC. Test 339→**344**. reviewer bắt **race mất-dữ-liệu** Part 1 → vá `useIsFetching` 2 app (gotcha #70). **🔴 NEXT (em):** curate L1 over-cap reviewer **45KB** + inv-codebase 35KB (keep-floor-hit → manual condense) + cicd 29KB + test-spec 28KB. **NEXT (anh/anh Kiệt):** UAT bảng lưới bằng PRO/CCM (Ctrl+F5). Chi tiết → `2026-06-19-S76-pe-budget-matrix-table-grid.md`. · **Prev S75 (2026-06-18)** — **Harness-11 adopt: engine bộ-nhớ-và-governance tự-bảo-trì + 2 double-check** — anh giao `/check-email AI_INFRA` + `/adap-apply` (mỗi stage workflow-review) + double-check ×2 + finalize report + push + session-end. em main + **5 Workflow** (audit `wf_7fdc3bd5-930` → implement `wf_c5e5844e-7c1` → review `wf_d7ca1ff8-942` → double-check `wf_a0b68d2f-30e` → checklist-verify `wf_39cd4cbe-f07`). **0 production code.** Built 2 NO-API detector-script (`governance-detectors.ps1` C1-C5+B3 runtime **26 flag** bắt drift thật, C4 0-self-match · `memory-archive-gate.ps1` PHẦN A hysteresis/keep-floor/2-strike/A7 186/186) + `docs/governance/harness-11-engine.md` canonical (3-tier D5/D6/D7 + one-direction-lock D8) + **B1 ×11 count→pointer** (drift mig53→55/test306→339/gotcha68→69/table93→88 RESOLVED) + cadence-wire D1/D2 + agents/README. **completeness-gate FORMAL ĐẠT** (B 4/4 + C 5/5 + D 11/11; D5/D6/D7 explicit + D8 codify=YES). gotcha #30 reinforced (box-glyph ├└─ KHÔNG vào .ps1, kể cả qua Edit). 3 commit `e70c046`+`ae957c4`+`aa09e99` pushed + adap-report + email ai_infra (`2316773229f2`). State GIỮ NGUYÊN Mig 55·88 bảng·339 test·gotcha 69·bundle `BYF5vIMJ`/`CB-tiRxd`. **🔴 NEXT (anh):** restart CLI (cadence §2.1.3/§L.b + ef-core skill); curate reviewer 38.8KB+inv 31.5KB+cicd 26.8KB over-cap. Chi tiết → `2026-06-18-S75-harness-11-adopt.md`. · **Prev S74** — **PE ô "Ghi chú từ CCM" ngân sách gói thầu (Mig 55)** — anh forward 2 luồng Zalo (Trà Sol + Kiệt FDC) "CCM giống PRO cho nhập + ghi chú, hiện 0 hết" → em-main BE+Mig55 ∥ implementer-frontend FE 2 app SHA-mirror ∥ test-specialist (chết rate-limit → recover-disk). 2 fork anh chốt: **thêm CcmNote (Mig 55)** + **GIỮ phân vai CCM** (CostControl/Admin). Entity +CcmNote mirror ProNote · UpdatePeBudgetCcmCommand +CcmNote absolute-set role-gate · DTO+controller+mapping · FE dòng "Ghi chú từ CCM" gate canEditCcm. Test 334→**339** (+5). commit `8655ebf` (cicd **Run #315 PASS** — bundle admin `BYF5vIMJ`/user `CB-tiRxd`, Mig 55 applied prod). **"0 hết không nhập" = tài khoản thiếu role CostControl (KHÔNG bug)** → UAT bằng CCM/Admin. **🔴 NEXT (anh/anh Kiệt):** UAT ô CCM bằng tài khoản CCM/Admin + carry "Ngưỡng giá CEO" Mig 54. Chi tiết → session log `2026-06-18-S74-pe-ccm-note.md`. · **Prev S73** — **Mig 54 PE giá đề xuất PRO/CCM + CEO chọn giá chốt + CCM duyệt-done ô-tích (anh Kiệt FDC, go-live thứ Hai 22/06)** — em main + 6 sub + 2 review-workflow, 2 commit prod-verified (`1d86abc` feature Run #313 + `6aa4dcb` FE empty-candidates fix). Mig 54 5-cột additive-nullable + 2 setter role-gate + ApproveV2 ③ AUTO→OPT-IN + ① bind giá chốt; FE 2 app SHA-mirror; test 306→334. **Workflow review:** R1 schema-force 1/4 lane → R2 free-text 2/3 PASS (rủi ro FE empty-candidates UNREACHABLE per submit-guard `:194`). Bài học: review fan-out free-text/hmw KHÔNG ép-schema. **Governance:** em lệch mandate auto-Workflow → cam kết báo-TRƯỚC-khi-lệch. **🔴 NEXT (anh/anh Kiệt):** cấu hình "Ngưỡng giá CEO" Designer + test 3 luồng + xác nhận CCM-trước-CEO. Chi tiết → session log `2026-06-18-S73-pe-gia-de-xuat-ccm-done.md`. · **Prev S72** — **Harness-10 flat-refine + checklist-v2 adopt (re-audit 3-workflow) + sleep NO-OP** — audit `wf_13868efb-ea7` → implement `wf_ac43b5ff-7d1` → review `wf_d482e10d-5dd` (Harness-9 B1+B2). SE BEHIND flat → migrate run-trace SUBFOLDER→FLAT (hmw.js:103 + 5 doc; 5 run cũ giữ subfolder C8) + 2 broadcast 06-18 adopt + `/sleep-recovery-memory-l2` port (A8) + detector TAILORED-OUT + hmw.js H4.5→H8 doc-drift. **REVIEW (B2) bắt 2 lỗi IMPLEMENT-self-assess sót** (hmw.js:52 schema-stale + auto-check overclaim) → fixed (auto-check WIRED thật). em-main containment-check tự bắt reviewer residual-write (raw-Workflow no-writeGuard) → revert. B3 self-correct (runs path/count 14→22). Sleep `all`=NO-OP. **0 prod code, state GIỮ NGUYÊN.** adap-report + email ai_infra (`5f511fe5c0f2`). **🔴 NEXT (anh):** restart CLI (FLAT-runtime hmw.js + wired sleep-check + sleep-recovery skill). **🔴 NEXT (em):** curate investigator-codebase L1 28973 OVER + watch FD/test-spec. Chi tiết → session log `2026-06-18-S72-harness-10-flat-refine.md`. **Prev S71 — Harness-10 adopt: tracked run-trace folder + checklist 9-10** — em main + 3 Workflow (invest `wf_9c2cd2cd-2e7` → implement `wf_e4e46725-231` → review `wf_636bc95b-939`) per mandate Harness-9 PART 2. **0 production code**. Migrate `.claude/workflows/wave-*/` gitignored → `runs//` **git-TRACKED** (run.md+sub-md/+harvest/) + `_ledger.md` 2-nhịp + 3-layer anti-miss (L1 em-main@P1 / L2 session-start orphan-scan / L3 session-end close-gate idempotent) + **containment shift** (Harness-2 B6 "mọi tracked-change=vi-phạm" → "tracked-change NGOÀI run-folder + code-disjoint=vi-phạm"). hmw.js wave→run-trace (accept `args.run` + alias `wave`, path `sub-md/`, `node --check` PARSE-OK). **Review độc-lập (R2+R3) bắt C5 L1 over-claim** (doc nói hmw.js prompt-builder emit L1 reminder, grep engine=0 → engine no-fs → fixed path-a: L1 = em-main @P1 convention) — dogfood mandate B2 đúng. checklist 9-10: Part A (Harness-9 memory) + B (adap 2-workflow) done S70, Part C (Harness-10) 8/8 MỚI. **State GIỮ NGUYÊN: Mig 53·88 bảng·306 test·68 gotcha·menu 54·bundle admin `BgNCjwsG`/user `CBvh0vtf`.** + CLAUDE.md test-flush 263→306 (resolve H1 stale). **🔴 NEXT (anh):** restart CLI (hmw.js RUN-TRACE runtime + carry §2.1.2/Cat-6/H8). Chi tiết → session log `2026-06-18-S71-harness-10-run-trace.md` (pending). **Prev S70 —** **Harness-9 adopt: L2 archive dark-matter recovery + adap 2-workflow mandate** — em main + 3 Workflow (investigate `wf_be952f3c-97f` → implement `wf_a58e0d15-beb` → audit `wf_9520d8cd-4fe`) + 2 monitor bootstrap. **0 production code** (governance/memory only, commit `f36aab8` pushed). 4 over-cap sub-agent curate L1→L2 + `archive/_INDEX.md` (mục-lục substring sha-keyed) + `.gist.md` (4-field distill-gen:1, verbatim FROZEN) → **cả 4 < 25KB auto-inject cap** (P1 curate-debt CLOSED; ~240KB archive hết RAG-dark). PART 2/3 process-mandate codify (adap-apply 2-workflow + agents/README Upgrade S70 + session-start §2.1.2). **0-byte-loss git+sha verified** (Stage C audit + em-main self-gate khi 2 reviewer no-StructuredOutput → recovery-path). +memory-budget.json (seed-by-measure) + measure-agent-memory.ps1 + .ragignore + doc-drift 4-cite flush. adap-report + email-back ai_infra (`7c07b716e775`). **State THẬT GIỮ NGUYÊN: Mig 53 · 88 bảng · 306 test · 68 gotcha · menu 54 · bundle admin `BgNCjwsG`/user `CBvh0vtf`.** **🔴 NEXT (anh):** restart CLI để activate session-start §2.1.2 budget-audit + pending H8 inherit + reviewer Cat-6. Chi tiết → session log `2026-06-17-S70-harness-9-l2-recovery.md`. **Prev S69 —** **Văn phòng số (E-Office) port + golive + PE cờ gấp/ngưỡng CCM — 4 deploy prod-verified Run #305→#308, HMW-mode ON, 2 workflow fan-out**: #305 foundation PURO (shared PageHeader/KpiCard/WidgetCard + Dashboard 2-cột + sync fe-admin index.css đóng drift S66-S68) → #306 **re-skin TRỌN 10 page** (PURO layout + CSS Hồ sơ NS, phẫu-thuật-giữ-100%-logic) → #307 **Office golive public** read+create **16-key allow-list mọi role** (mirror S65, chạy SAU revoke; excluded Off_PhongHop_Manage/Off_AttendanceReport/Off_ChamCong; cicd DB-verify 16/16×13 role) → #308 **PE cờ gấp PRO/CCM + CCM duyệt-final theo ngưỡng giá trị** Mig 53 (anh Kiệt FDC sau họp sếp). **State THẬT: Mig 53 · 88 bảng · 306 test (45D+261I) · 68 gotcha · menu 54 · bundle admin `BgNCjwsG`/user `CBvh0vtf` Run #308.** 2 gotcha NEW (**#67** Tailwind accent palette thiếu-stop vỡ-màu-im-lặng · **#68** stale-diagnostic-background-agent → chỉ tin build sau-cùng). 2 truncation #53 (impl-backend positional-record + impl-frontend) → em main recover-disk + self-gate. **🔴 NEXT (anh/anh Kiệt UAT):** (1) cấu hình "Ngưỡng giá trị gói CEO" trong Workflow Designer (null=luồng cũ) + test phiếu < ngưỡng → CCM duyệt là xong, ≥ ngưỡng → lên CEO; (2) test cờ gấp PRO(đỏ)/CCM(xanh) → badge + notify CEO; (3) **xác nhận:** quy trình đặt CCM(CostControl)-trước-CEO + CEO = role Director (đích notify). **C (sau duyệt → chuyển phiếu đến dự án) chờ anh Kiệt spec form.** **NEXT (em):** 🔴 curate cicd-monitor **65.2KB** (worst, trend tăng) + inv-codebase 47 + reviewer 43.5 over-cap · doc-flush docs/CLAUDE.md full. Chi tiết → session log `2026-06-17-S69-vanphong-golive-pe-urgent-threshold.md`. **Prev S68 —** **Hồ sơ NS header chi tiết NV: làm nổi bật + fix tên render đen→trắng** — em main solo, 3 commit (2 FE + 1 docs), 2 deploy prod-verified Run #303-304 (anh UAT realtime): badge màu theo trạng thái + dòng meta đậm + tên to/drop-shadow (#303 `6983609`) → anh báo "tên đen nền xanh ko nổi bật" → diagnose **rule `h1-h4{color:#0b1220}` viết NGOÀI `@layer` thắng `text-white`** (Tailwind v4 unlayered > all layers, **gotcha #66 NEW**, load-bearing ~30+ heading → fix ĐIỂM `text-white!` không move @layer) + thu nhỏ `text-lg` (#304 `37752eb`). Bootstrap: 2 monitor RE-REPORT CLEAN + 2 doc-drift fix (dep-audit 64→65 · root CLAUDE 263→286, `11bc96d`). **State THẬT: Mig 52 · 88 bảng · 286 test · 66 gotcha · menu 53 · bundle admin `CNUv1jxY`/user `CpOskeS1` Run #304.** ⚠️ curate-debt P1: cicd-monitor **44.1KB**. Chi tiết → session log `2026-06-16-S68-hoso-header-name-color-fix.md`. **Prev S67 —** **buổi sản phẩm LỚN, 6 deploy prod-verified Run #297→#302** (anh + anh Kiệt FDC UAT realtime, HMW-mode ON): fe-user Hồ sơ NS đồng nhất font + chữ đen→**xanh đậm `brand-800`** (#297) · **fe-admin mirror master-detail** + vá accent tokens index.css [đóng pending lớn nhất HRM go-live, page SHA256 ×2] (#298) · **+23 test-after HRM** → 263→**286** (#299) · **list flex-row gọn** [hết tràn ngang rail] + đồng nhất cỡ chữ ×2 (#300) · **PE Link hồ sơ auto-detect** web→hyperlink/`O:\`→Copy (#301) → **link `file://` bấm-thử** (#302). **Research 3-agent:** auto-mở `O:\` từ web = chỉ **Edge GPO `IntranetFileLinksEnabled`** (zero-per-machine) one-click thật; default chặn https→file://. **State THẬT: Mig 52 · 88 bảng · 286 test (45D+241I) · 65 gotcha · menu 53 · bundle admin `CcrZqfht`/user `DniDFUB_` Run #302.** test-specialist trunc #53→recover-disk; 0 prod bug. Chi tiết → session log `2026-06-16-S67-hoso-visual-pe-link-research.md`. **Prev S66 — session-end closeout em-main-solo:** adopt **Harness-8 all-inherit** (7 sub demoted `claude-opus-4-8`→`inherit` → cả 11=inherit, gỡ two-tier H4) + **cicd-monitor L1 curate 86.8→28.9KB** (byte-exact sed → `archive/2026-06.md`) + **ef-core skill doc-flush Mig 50→52** + check-email AI_INFRA (**0 thư mới se-directed**). ~17 file docs/gov/config, **0 production code** → state THẬT GIỮ NGUYÊN (Mig 52 · 88 bảng · 263 test · 65 gotcha · menu 53 · bundle `BDwV5d0X`/`DbVv6rsf`). ⚠️ **Restart CLI** → H8 runtime (frontmatter no hot-reload) + reviewer Cat-6. adap-report + email-back ai_infra (`fa7f690d` MATCH). Chi tiết → session log `2026-06-16-S66-closeout-harness8-cicd-curate.md`. **Prev S65 — HRM go-live: public Hồ sơ Nhân sự + trang master-detail giống NamGroup + Department hierarchy Mig 51 + PE Link hồ sơ Mig 52 + gốc cây SOLUTION COMPANY**, ~6 deploy prod-verified Run #289→#295, anh + anh Kiệt FDC UAT realtime. **State THẬT: Mig 52 · 88 bảng · 263 test (45D+218I) · 65 gotcha · menu 53 · bundle admin `BDwV5d0X`/user `DbVv6rsf` Run #295.** Workflow fan-out chạy THẬT lần đầu [PE] — BE∥FE parallel OK nhưng FE+reviewer return-rỗng #53 → em main recover-disk + self-gate. gotcha #65 [build csproj con ≠ slnx → CS7036]. Chi tiết → session log `2026-06-16-S65-hrm-golive-employee-masterdetail-pe-link.md`. **Prev S64** adopt **Harness-7 writing-quality floor** qua `/adap-apply` + email ai_infra — em main solo, 1 commit `6afde19` docs/gov-only. Outward comms = tiếng Việt câu-hoàn-chỉnh; nội bộ giữ nén (asymmetric). reviewer +Category 6 (verified-pending-restart → cần restart CLI). Broadcast body-hash verified KHÔNG mis-stamp (gotcha #61 UTF-8). Prev S63 docs-closeout bù S60/S61/S62 — 3 session product ship CODE prod-verified nhưng KHÔNG closeout docs (UAT realtime anh Kiệt FDC). **State THẬT: Mig 50 · 88 bảng · 263 test · 64 gotcha · menu 53 · bundle admin `0xKYGhhf`/user `C81ZdG9G` Run #286.** S60-62 = PE ràng buộc gửi-duyệt + gỡ "Từ chối" (S60) · Mig 50 ngân sách per-gói-thầu Excel anh Kiệt + XÓA module Budget cũ (S61) · vượt-NS cảnh-báo-mềm (S62). Reconcile stray reviewer cwd-misland + count-flush 4 file. Chi tiết → session log `2026-06-12-S60-S62-pe-budget-workitem-softwarning.md`. Prev Session 59 ( **6 đợt ship prod-verified Run #273→#278**: wipe transactional testing data (10 PE + 7 HĐ demo + 64 notif = 0, mã reset → phiếu thật đầu tiên team tạo = **PE/2026/A/001** ✓) `56882ac` #273 · PE tree Panel 1 chốt 4 tầng **Năm > Dự án > Hạng mục > Phiếu** `0eafcd3` #274 · dọn 15 mã hạng mục demo "tự đẻ" (chị Trà Sol) + gỡ seed gốc, WorkItems 86→**71** `bbd1554` #275 · **rename 71 mã đúng format PMH anh Kiệt** (`MAT-n`/`SUB-n`/`MEP-SUB-n`/`MEP-EQU-n` + tên "STT nhóm tên"; **DB-trước-code-sau** gotcha #62 + sqlcmd `-f 65001` gotcha #61) `c869d26` #276 · UAT vòng 1: NEW `ui/SearchableSelect` gõ-lọc bỏ dấu (Hạng mục/Dự án) + auto Địa điểm + điều khoản đa dòng `faed59f` #277 · UAT vòng 2 (anh chốt ×2): ẩn Trả lại/Từ chối khi tự duyệt phiếu mình soạn + quick-add NCC ngay form (POST /suppliers any-auth, authz probe 4/4) + NCC gõ-tìm A-Z + upload multi-file `9c330d2` #278 · UAT vòng 3-6 realtime (#279/#281 cancelled-supersede-benign): bảng NCC table-fixed `f21c55d` + bỏ ô Tên ngân sách `69997da` #280 + GỠ field Điều khoản TT mọi form `80b64dd` + bỏ nút Thêm hạng mục `792c030` **#282 FINAL**. Tổng 10 đợt (8 PASS + 2 cancelled-benign). Bundle FINAL admin **`B1DtNT9C`**/user **`D6uF3Mln`**. Test 240. Gotchas 62. 0/14 spawn truncated. → session log `2026-06-11-S59-wipe-tree-pmh-uat-batch.md`. Prev S58 — **5 đợt prod-verified Run #382/#383/#384/#386** (#385 cancelled-supersede-benign): lock-demo-user fix (việc sếp deadline 15:00 ĐÓNG TRỌN — gotcha #60/E-008/AS-12, root cause password 11<12 từng phát hiện S22 nhưng const không fix) + tạm ẩn HRM/Office/Cá nhân + Danh mục cuối (`6c5fd26` #383) + **fe-user redesign theo UI/UX guide AI_INFRA giữ brand** (`e959f72` #384) + **brand polish ×2 app "thấy rõ"** (`ea793a4`: stripe 4px đỉnh + thead brand) + **PE gộp Tên-gói-thầu = chọn Hạng-mục** (anh Kiệt FDC chốt, `3ebaf84` #386 — bundle final admin `DMm9rtNA`/user `BUkOMn_Y`). Email AI_INFRA processed (guide + ACK H4 ACCEPT). Test **240**. → session log `2026-06-11-S58-lock-fix-hide-modules-redesign-pe-merge.md`. Root cause 2 tầng: lock-list = population Dev-only + `DemoUserPassword` 11 ký tự < prod RequiredLength=12 → CreateAsync silent-fail từ trước tới giờ (= "helpdesk inert" S56). Fix union 20 UAT email + password 12 ký tự → prod 55 user/34 locked, nv.cao+nv.truong sống, 5 real staff tạo. gotcha #59+#60. Commit `5998163`. Prev S57bis — **PE gắn Hạng mục (Mig 49) + Pe all-role + menu Cá nhân + Harness-4 runtime-VERIFIED**. Test 228→**240**. Bundle `CP4CB1ym`/`BmZ3VHnm`. Commit `17b23a4`+`dd117b7` → Run #381 PASS+1PARTIAL (lock NO-OP → RESOLVED S58). Prev S56 — **Pre-golive verify sweep + golive-harden 4 fix — Run #379 PASS, code golive-ready**. WF1 `pre-golive-verify` 7-stream + adversarial → 6 PASS/1 CONCERN/0 blocker = GO (key finds = ops not code). WF2 `golive-harden` fix 4: #3 LeaveBalance lost-update→atomic ExecuteUpdate+Serializable tx (NO mig) · #5 ItTicket authz Forbidden-trước-NotFound · #6 DocxRenderer null-guard · #4 Travel/Vehicle ApproveV2 tests. Test 216→**228**. Bundle FROZEN `4SUwDLD8`/`XdKzt9LL`. `sys.tables` re-ground 92→**93**. gotcha **#58** NEW. reviewer StructuredOutput-fail→em main đỡ. **2 ops VPS pending** (gán user IT + tzutil UTC+7). FE Phase 2 redesign **deferred** (recon ready). Commit `a20cde8`. Prev S55 — **Nạp master data thật từ Excel (62 dự án + 71 hạng mục + 3 NCC) + Project +4 cột (Mig 48) — prod-verified**. HMW-mode ON. Commit `69cb393` → Run #377 PASS ~4m33s. Test 216 (compile-fix only). Bundle admin `B-d6893W`/user `XdKzt9LL`. `SeedRealMasterDataAsync` ungated idempotent → coexist demo. 2 agent return truncated (BE+reviewer) → em main disk/runtime-recover. Prev S54 — IT staff tự reassign ticket (cross-stack authz) — prod-verified. 1 code commit `ca4b602` → Run #376 PASS ~4m18s. Test 203→**216**. Bundle admin `DfCfHUE9`→`DmjI8Cmn`/user `_3S0BPJ2`→`YxL_MljK` (cả 2 rotate). NO migration. Task 1 Phase 9 Ops anh dừng. ⚠️ residual: 3 agent ghi MEMORY nhầm `src/Backend/.claude` → em main reconcile. Prev S53: gotcha #57 EXT Master Mig 47 + P11-D/E + database-agent verified-runtime.) + +--- + +## S76 (2026-06-19) — PE ngân sách MA TRẬN 3 cột + bảng lưới `
` + badge quyền-NS theo role (anh Kiệt FDC + chị Trà Sol, go-live) + +**Anh:** forward chat Zalo (Trà Sol + Kiệt FDC) — "mỗi phòng (PRO/CCM) nhập+điều chỉnh ngân sách phòng mình; thêm cột tích quyền NS bên flow để nhìn biết ai sửa được; hiển thị ra fe-user mục Duyệt NCC" → `/ultra-on` "làm hết, step-by-step có workflow review kiểm tra đàng hoàng, hoàn chỉnh rồi deploy báo tao" → phản hồi "giao diện chưa chia cột giống Excel" → bảng lưới → "OK đồng ý, deploy public rồi session-end". + +**2 fork anh chốt (AskUserQuestion):** cột Dự án = FE hiển-thị-only · ô tích quyền NS = chỉ-hiển-thị giữ-quyền-theo-role. + +**Done (3 commit prod-verified):** +- **`e33481e`** (feature, Run #318 PASS — Mig 56 applied prod, `BhFDF9IJ`/`BAkuRl3C`): **Part 1** ma trận 3 cột. Mig 56 `AddProBudgetSplitToPeWorkItemBudget` (+ProInitialAmount/ProAdjustmentAmount cột PRO mirror CCM Initial/Adjustment; additive + `Sql()` data-migrate `ProInitial=ProEstimate` 4 rows prod). Handler `UpdatePeBudgetProCommand` (PeId,ProInitial,ProAdjust,ProNote) role-gate PRO/Admin + DTO/capability (full mỗi cột = CCM nếu hasCcm else proFull). FE 2 app SHA-mirror. **Part 2+3** badge "✎ NS PRO/CCM" cạnh approver: BE +2 cờ per approver (suy role qua 3 `GetUsersInRoleAsync` set-lookup no-N+1, khớp gate) vào AwLevelDto (designer) + PurchaseEvaluationApprovalLevelApproverDto (PE flow, 2 build-site). FE badge ở ApprovalWorkflowsV2Page + PeWorkflowPanel (2 app). Test 339→**344** (+5). +- **`21d1f4e`** (bảng lưới, Run #319 PASS — `jOqxW4-p`/`DbsznVvR`): Block A flex→`
` viền ô (header Khoản mục|Dự án|PRO|CCM + 5 hàng); `BudgetCell` xếp dọc (input full + Lưu dưới); `BudgetNoteRow`→`BudgetNoteCell` (td colSpan=3). FE-only. **LESSON: flex+gap KHÔNG ra "bảng" — phải `
` viền ô.** +- **Race fix (gotcha #70):** reviewer workflow Part2/3 bắt — 2 ô PRO echo field anh-em từ `bs` (server snapshot) + `invalidate()` fire-and-forget → lưu 2 ô liên-tiếp đè mất. Vá `useIsFetching` gate nút Lưu tới khi refetch land. 2 app. + +**Cách chạy:** HMW-mode ON. 1 verify-workflow (Step-1) + 2 review-workflow (Part 1 · Part 2/3) + em-main BE/FE + impl-FE mirror + test-spec + cicd×2. **Workflow-review-flaky** (2-3 lane empty #53) → em-main self-gate (`feedback_workflow_fanout_reliability`). cwd-misland: impl-FE MEMORY rơi stray → harvest + `.gitignore` guard `fe-*/.claude/`. + +**🔴 NEXT SESSION:** +- **Em (carry ưu-tiên):** curate L1 over-cap GẤP — reviewer **45KB** + investigator-codebase **35KB** (keep-floor-hit, entries newest lớn → **manual SPLIT/condense**, KHÔNG auto-drain) + cicd 29KB + test-spec 28KB (strike-1). archive-gate A7 GATE PASS 186/186. +- **Anh/anh Kiệt UAT:** mở phiếu PE tab ngân sách (Ctrl+F5) → bảng lưới 3 cột; login PRO nhập cột PRO / CCM nhập cột CCM. Xem badge ✎NS trong Designer + flow. +- **Ops giữ S58/S59:** tzutil VPS · anh Chương email typo · 5 real-staff pw · gán CNTT. Monthly audit 2026-07-01 (STATUS/HANDOFF re-tier + docs/CLAUDE deep-doc + schema §16+). --- diff --git a/docs/STATUS.md b/docs/STATUS.md index 57d233b..dcbd010 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -3,7 +3,7 @@ > **Update rule:** trước khi bắt đầu 1 task → ghi row `🔥 In Progress`. Xong → `✅ Recently Done`. > **Tiering rule (S40):** chỉ giữ **state hiện tại + 3 session gần nhất** ở file này. Session cũ hơn → `docs/changelog/sessions/`. Full history pre-S40 → `docs/_archive/STATUS-preS40-fullhistory.md`. (Tránh over-context — xóa double, không cắt nội dung.) -**Last updated:** 2026-06-18 (S75 — **Harness-11 adopt: engine bộ-nhớ-và-governance TỰ-BẢO-TRÌ + 2 double-check** — anh giao `/check-email AI_INFRA` + `/adap-apply` (mỗi stage workflow-review) + double-check ×2 + finalize report + push + session-end. em main + **5 Workflow** (audit `wf_7fdc3bd5-930` → implement `wf_c5e5844e-7c1` → review `wf_d7ca1ff8-942` → double-check `wf_a0b68d2f-30e` → checklist-verify `wf_39cd4cbe-f07`). **0 production code.** Built `scripts/governance-detectors.ps1` (C1 broken-pointer + C2/B3 staleness + C3 vocab-fork + C4 self-exclusion + C5 resolve, NO-API DÒ+FLAG, runtime **26 flag** bắt drift thật, C4 0-self-match) + `scripts/memory-archive-gate.ps1` (PHẦN A hysteresis 0.85/keep-floor 5/2-strike/A7 NO-API 186/186) + `docs/governance/harness-11-engine.md` canonical (3-tier D5/D6/D7 EXPLICIT + one-direction-lock D8) + **B1 ×11 count→pointer `docs/STATUS.md`** (drift mig53→55/test306→339/gotcha68→69/table93→88 RESOLVED) + cadence-wire session-start §2.1.3 (D1) + session-end §L.b(c) (D2) + agents/README Upgrade S75. **completeness-gate FORMAL ĐẠT** (B 4/4 + C 5/5 + D 11/11 đủ-trọn, A 🟡 tailored — A6 runtime cần 2×-Apply legit-by-design). **gotcha #30 reinforced** (box-glyph ├└─ KHÔNG vào .ps1 — PS5.1 -File ANSI mojibake, KỂ CẢ qua Edit render-normalize; detector pure-ASCII verified). 3 commit `e70c046`+`ae957c4`+`aa09e99` pushed + adap-report + email ai_infra (`2316773229f2`). **State THẬT GIỮ NGUYÊN: Mig 55 · 88 bảng · 339 test · gotcha 69 · menu 54 · bundle `BYF5vIMJ`/`CB-tiRxd`.** ⚠️ restart CLI (cadence §2.1.3/§L.b + ef-core skill). 🔴 curate-debt: reviewer 38.8KB + inv-codebase 31.5KB + cicd 26.8KB over-cap (archive-gate confirm; reviewer+inv keep-floor hit → manual split). → log `2026-06-18-S75-harness-11-adopt.md`. **Prev S74** — **PE ô "Ghi chú từ CCM" ngân sách gói thầu (Mig 55)** — anh forward 2 luồng Zalo (Trà Sol + Kiệt FDC) "CCM cũng giống PRO cho nhập + ghi chú, hiện 0 hết" → em-main BE+Mig55 ∥ implementer-frontend FE 2 app SHA-mirror ∥ test-specialist (chết rate-limit → recover-disk). 2 fork anh chốt qua AskUserQuestion: **thêm CcmNote (Mig 55)** + **GIỮ phân vai CCM** (CostControl/Admin). Entity `PeWorkItemBudget +CcmNote` mirror ProNote · `UpdatePeBudgetCcmCommand +CcmNote` absolute-set role-gate fail-closed · DTO+controller+mapping · FE dòng "Ghi chú từ CCM" gate `canEditCcm` (sau V0/hiệu chỉnh, absolute-set đủ 3 field). Mig 55 additive-nullable sạch (no #63). Test 334→**339** (+5 CcmNote). **Chẩn đoán "0 hết không nhập":** tài khoản thiếu role CostControl (KHÔNG bug) → UAT bằng CCM/Admin. commit `8655ebf` push main (cicd **Run #315 PASS** ~4m54s — Mig 55 applied prod, bundle admin `BYF5vIMJ`/user `CB-tiRxd`, smoke 4×200). Lessons: controller call-site thiếu arg sau +param record (grep+vá, #65 bắt) · agent-kill-529 recover-disk no-re-spawn · PS here-string commit-msg trong if-block mis-parse → `git commit -F`. → session log `2026-06-18-S74-pe-ccm-note.md`. **Prev S73** — **Mig 54 PE giá đề xuất PRO/CCM + CEO chọn giá chốt + CCM duyệt-done ô-tích (anh Kiệt FDC — go-live so-sánh-giá thứ Hai 22/06)** — em main + 6 sub + 2 review-workflow. **2 commit prod-verified:** `1d86abc` (feature, cicd Run #313 PASS — Mig 54 + bundle admin `OlNyG9OD`/user `DSzSLVtL`) + `6aa4dcb` (FE empty-candidates guard fix). **BE:** Mig 54 `AddPeSuggestedAndApprovedPrice` 5 cột additive-nullable (ProSuggestedMin/Max·CcmSuggested·ApprovedPriceAmount·Source) + 2 setter role-gate PRO/CCM (Forbidden fail-closed mirror budget) + ApproveV2 **③ AUTO→OPT-IN** (`finalizeByCcmDelegation` ô-tích, gỡ auto-threshold S69) + **① bind giá chốt** mọi nhánh DaDuyet (human bắt-buộc-chọn, isSystem-miễn) + DTO +7 field/capability. **FE 2 app SHA-mirror:** PeWorkflowPanel (bộ chọn giá + ô tích CCM + fix empty-candidates) + PeDetailTabs `SuggestedPriceRows` + types +7. **Test 306→334** (+28). **2 workflow review (bài học):** R1 schema-force CHỈ **1/4** lane returned (flaky no-StructuredOutput) → R2 **free-text 2/3 PASS** — rủi ro #1 FE empty-candidates = **UNREACHABLE** (submit-guard `:194` chặn gửi-duyệt khi winnerQuoteTotal≤0). → review fan-out dùng **free-text / hmw RUN-TRACE**, KHÔNG ép-schema inline. **Governance honest:** em lệch mandate auto-Workflow (mode-ON) + chỉ khai khi anh hỏi → **cam kết báo-TRƯỚC-khi-lệch**. Run-trace `runs/2026-06-18-mig54-pe-review/` (custom-inline, bù post-hoc). → session log `2026-06-18-S73-pe-gia-de-xuat-ccm-done.md` pending. · **Prev S72** — **Harness-10 flat-refine + checklist-v2 adopt (re-audit 3-workflow) + sleep NO-OP** — em main + 9 sub qua 3 Workflow (audit `wf_13868efb-ea7` → implement `wf_ac43b5ff-7d1` → review `wf_d482e10d-5dd`, mandate Harness-9 B1+B2). Anh chỉ "sisters làm tốt mỗi SE thiếu" → re-audit đo vs canonical: **SE BEHIND Harness-10 flat** (run-trace còn SUBFOLDER, canonical=FLAT) → migrate `hmw.js` (`:103` sub-md/→flat + `:52` schema + **H4.5→H8 doc-drift**) + `workflows/README` full-rewrite + `runs/README` + session-start/end + `agents/README` Upgrade S72 + `_ledger`; **5 run cũ S71 GIỮ subfolder** (C8 dual-accept). +2 broadcast 06-18 adopt (checklist-v2 + h10-flat-detector-refine). +`/sleep-recovery-memory-l2` port (A8, §J2-tailored SE-only, floor intact, live skill). **detector refine-b TAILORED-OUT** (SE Workflow-tool no-CLI-bypass; containment git-diff+tracked+orphan-scan G-015). **REVIEW (B2 dogfood) bắt 2 lỗi IMPLEMENT-self-assess sót** (hmw.js:52 schema-stale + sleep-cmd auto-check un-wired overclaim) → fixed (auto-check **WIRED thật**: budget.json `+last_sleep_at` + session-start:78 + session-end:48, grep-verified). em-main containment-check tự bắt 1 reviewer residual-write (raw-Workflow KHÔNG hmw.js writeGuard → reverse-finding #4) → revert. **B3 self-correct:** S71 report path tắt `runs/`+"14" → thật `.claude/workflows/runs`=22. **Sleep `all`=NO-OP** (5/9 empty-archive + 4/9 đã-gisted → 0 compressed; `last_sleep_at`=2026-06-18). **0 production code, state THẬT GIỮ NGUYÊN.** adap-report `2026-06-18-Governance-harness-10-flat-refine-checklist-v2` + email ai_infra (`5f511fe5c0f2`). ⚠️ **restart CLI** activate FLAT-runtime hmw.js + wired sleep-check + sleep-recovery skill. 🔴 **NEXT (em):** curate investigator-codebase L1 **28973 OVER** (audit-trail) + watch frontend-designer/test-specialist near-cap. → session log `2026-06-18-S72-harness-10-flat-refine.md`. **Prev S71 — Harness-10 adopt: tracked run-trace folder convention + checklist 9-10 self-verify** — em main + 3 Workflow (invest `wf_9c2cd2cd-2e7` → implement `wf_e4e46725-231` → review `wf_636bc95b-939`) theo mandate Harness-9 PART 2. **0 production code** (governance/workflow only). Migrate `.claude/workflows/wave-*/` gitignored → `runs//` **git-TRACKED** (run.md + sub-md/ + harvest/) + `_ledger.md` 2-nhịp + 3-layer anti-miss (L1 em-main@P1 ledger-check / L2 session-start orphan-scan / L3 session-end close-gate **idempotent-VERIFY-not-re-APPEND**) + **containment model shift** (Harness-2 B6 "mọi tracked-change=vi-phạm" → Harness-10 "tracked-change NGOÀI run-folder + code-disjoint=vi-phạm"; run-folder TRACKED → HIỆN git-diff = audit trực-tiếp). hmw.js wave→run-trace (accept `args.run`, alias `wave`; path `sub-md/`; `node --check` PARSE-OK). **Review độc-lập (R2+R3) bắt C5 L1 over-claim** (doc nói hmw.js prompt-builder emit L1 reminder nhưng grep engine=0 → engine no-fs → fixed path-a: L1 = em-main @P1 convention) — dogfood mandate B2 đúng (1-workflow-tự-chấm sẽ bỏ sót). **checklist 9-10:** Part A (Harness-9 memory) + Part B (adap 2-workflow) ĐÃ done S70; Part C (Harness-10) MỚI 8/8. dogfood: 3 run-trace folder TRACKED đầu + 3 entry ledger. adap-report `2026-06-18-Governance-checklist-harness-9-10` + email ai_infra. **State THẬT GIỮ NGUYÊN: Mig 53 · 88 bảng · 306 test · 68 gotcha · menu 54 · bundle admin `BgNCjwsG`/user `CBvh0vtf`.** + `CLAUDE.md` test-flush 263→306 (pre-existing uncommitted, resolve H1 stale-flag). ⚠️ restart CLI activate hmw.js RUN-TRACE runtime + (carry S66/S70) §2.1.2 budget-audit/reviewer-Cat-6/H8-inherit. ✅ **curate-debt CLOSED S71** (FINALIZE double-check `wf_73de399d-753` + curate `wf_f32987b8-03f`): reviewer 36.7→24.8KB + investigator 29.8→23.2KB (both <25600 cap, archive +N -0 0-byte-loss) + reviewer-gist gen:2 + 3 user-memory G2/G3 + budget.json re-measure. Race root-cause fixed cấu-trúc (hmw.js RUN-TRACE writeGuard). → session log `2026-06-18-S71-harness-10-run-trace.md` (pending). **Prev S70** — **Harness-9 adopt: L2 archive dark-matter recovery + adap 2-workflow mandate** — em main + 3 Workflow (investigate `wf_be952f3c-97f` → implement `wf_a58e0d15-beb` → audit `wf_9520d8cd-4fe`) + 2 monitor bootstrap. **0 production code** (governance/memory only, commit `f36aab8` pushed). 4 over-cap sub-agent (cicd-monitor/investigator-codebase/reviewer/implementer-backend) curate L1→L2 + `archive/_INDEX.md` (mục-lục con-trỏ **substring sha-keyed**, Ctrl-F fallback) + `.gist.md` (nén 4-field distill-gen:1, verbatim FROZEN) → **cả 4 < 25KB auto-inject cap** (P1 curate-debt CLOSED; ~240KB archive hết RAG-dark). PART 2/3 process-mandate codify (adap-apply + agents/README Upgrade S70 + session-start §2.1.2 budget-audit). **0-byte-loss git+sha verified** (Stage C audit + em-main self-gate khi 2 reviewer no-StructuredOutput). +`memory-budget.json` (seed-by-measure) + `scripts/measure-agent-memory.ps1` + `.ragignore` + doc-drift 4-cite flush. adap-report + email-back ai_infra (body-hash `7c07b716e775`). **State THẬT GIỮ NGUYÊN: Mig 53 · 88 bảng · 306 test · 68 gotcha · menu 54 · bundle admin `BgNCjwsG`/user `CBvh0vtf`.** ⚠️ restart CLI để activate session-start §2.1.2 + pending H8 inherit + reviewer Cat-6. → session log `2026-06-17-S70-harness-9-l2-recovery.md`. **Prev S69** — **Văn phòng số (E-Office) port + golive + PE cờ gấp/ngưỡng CCM — 4 deploy prod-verified Run #305→#308, HMW-mode ON, 2 workflow fan-out + ~14 spawn**: foundation PURO (shared PageHeader/KpiCard/WidgetCard + Dashboard 2-cột + sync fe-admin index.css đóng drift S66-S68) #305 → **re-skin TRỌN 10 page** PURO layout + CSS Hồ sơ NS, phẫu-thuật-giữ-100%-logic (reviewer verify api/queryKey byte-identical) #306 → **Office golive public** `SeedAllRolesOfficeModulePermissionsAsync` read+create 16-key allow-list mọi role (mirror S65 pattern, chạy SAU revoke; excluded Off_PhongHop_Manage/Off_AttendanceReport/Off_ChamCong giữ ẩn; cicd DB-verify 16/16×13 role) #307 → **PE cờ gấp PRO/CCM + CCM duyệt-final theo ngưỡng giá trị** Mig 53 (anh Kiệt FDC sau họp sếp) #308. **State THẬT: Mig 53 · 88 bảng · 306 test (45D+261I) · 68 gotcha · menu 54 · bundle admin `BgNCjwsG`/user `CBvh0vtf` Run #308.** 2 gotcha NEW (**#67** Tailwind accent palette thiếu-stop vỡ-màu-im-lặng · **#68** stale-diagnostic-background-agent). 2 truncation #53 (impl-backend positional-record + impl-frontend) → em main recover-disk + self-gate (cả 2 build PASS sau-cùng). C (sau duyệt → chuyển phiếu đến dự án) chờ anh Kiệt spec form. → session log `2026-06-17-S69-vanphong-golive-pe-urgent-threshold.md`. **Prev S68** — **Hồ sơ NS header chi tiết NV: làm nổi bật + fix tên render đen→trắng** — em main solo, 3 commit (2 FE `6983609`+`37752eb` · 1 docs `11bc96d`), 2 deploy prod-verified Run #303-304 (anh UAT realtime): tên `text-xl extrabold`+drop-shadow · dòng meta `text-[13px] font-medium` trắng-đậm · badge pill **màu theo trạng thái** (#303); anh báo "tên đen nền xanh ko nổi bật" → diagnose **rule `h1-h4{color:#0b1220}` viết NGOÀI `@layer` thắng `text-white`** (Tailwind v4 unlayered > all layers, **gotcha #66 NEW**) → ép `text-white!` + thu nhỏ `text-lg` (#304, grep dist confirm `!important`). Bootstrap: 2 monitor RE-REPORT CLEAN + 2 doc-drift fix (dep-audit 64→65 · root CLAUDE 263→286, H1 flag). **State THẬT: Mig 52 · 88 bảng · 286 test · 66 gotcha · menu 53 · bundle admin `CNUv1jxY`/user `CpOskeS1` Run #304.** ⚠️ curate-debt P1: cicd-monitor **44.1KB** (worst). **Prev S67:** **buổi sản phẩm LỚN, 6 deploy prod-verified Run #297→#302** (anh + anh Kiệt FDC UAT realtime, HMW-mode ON): fe-user Hồ sơ NS đồng nhất font + chữ đen→**xanh đậm `brand-800`** (#297 `ab4e681`) · **fe-admin mirror master-detail** + accent tokens index.css [đóng pending lớn nhất HRM go-live, page SHA256 ×2] (#298 `292d64d`) · **+23 test-after HRM** [Dept cycle-guard · PE HoSoLink absolute-set · HRM-perm seed] → 263→**286** (#299 `bcd619d`) · **list flex-row gọn** [bảng 3-cột→flex-row, `overflow-x-hidden` → hết tràn ngang rail] + đồng nhất cỡ chữ [18/14/13/11px] ×2 app (#300 `91aaf05`) · **PE Link hồ sơ auto-detect** `http(s)`→hyperlink/`O:\`→Copy (#301 `6df1b2d`) → **render link `file://` bấm-thử** + Copy (#302 `536dd6b`). **Workflow research 3 investigator-api** (auto-mở `O:\` từ web): default browser CHẶN https→file://; **chỉ Edge GPO `IntranetFileLinksEnabled`** (Edge-only, Intranet Zone, 1 GPO domain zero-per-machine) = one-click thật — Chrome không có; .url-download zero-install nhưng 2-click. **State THẬT: Mig 52 · 88 bảng · 286 test (45D+241I) · 65 gotcha · menu 53 · bundle admin `CcrZqfht`/user `DniDFUB_`** (#302). test-specialist truncated return #53 → em main recover-disk (3 file test đủ + 286 verify); 0 production bug. ⚠️ curate-debt: cicd-monitor 39.8KB + inv-codebase 39.4KB over-cap. **Prev S66** — **session-end closeout em-main-solo, 0 product-sub:** adopt **Harness-8 all-inherit** (7 sub demoted `claude-opus-4-8`→`inherit` → cả 11 = inherit, gỡ two-tier Harness-4) + **cicd-monitor L1 curate 86.8→28.9KB** (byte-exact sed → `archive/2026-06.md`, incl #291 forensic) + **ef-core skill doc-flush Mig 50→52** (H1 drift, +Mig 51/52 rows) + check-email AI_INFRA (**0 thư mới se-directed**). ~17 file docs/gov/config, **0 production code** → state THẬT GIỮ NGUYÊN: **Mig 52 · 88 bảng · 263 test · 65 gotcha · menu 53 · bundle admin `BDwV5d0X`/user `DbVv6rsf`**. ⚠️ **Restart CLI** để H8 runtime (frontmatter no hot-reload) + reviewer Category 6 (S64 pending). adap-report `2026-06-16-Governance-harness-8-all-inherit-workflow-fastest` + email-back ai_infra (hash `fa7f690d` round-trip MATCH). Prev S65 — **HRM go-live: public Hồ sơ Nhân sự + trang master-detail giống NamGroup + Department hierarchy + PE Link hồ sơ**, ~6 deploy prod-verified Run #289→#295, anh + anh Kiệt FDC UAT realtime): (1) **public Hồ sơ Nhân sự mọi role** — `SeedAllRolesHrmProfileReadPermissionsAsync` grant CanRead `Hrm`+`Hrm_HoSo` 13 role chạy SAU revoke S58 (upgrade-only; EmployeesController policy-based `Hrm_HoSo.Read` mở luôn API không hardcode Roles), giữ ẩn Dashboard NS, Run #289; (2) **redesign màu foundation fe-user** — accent palette teal/violet/amberx/greenx + `.app-gradient-brand`/`.card-accent`/`.icon-chip` + heading 700, brand #1F7DC1 + Be Vietnam Pro giữ, Run #290; (3) **Department hierarchy** Mig 51 `AddDepartmentParentId` (ParentId loose-Guid no-FK + `GET /departments/tree` ráp cây in-memory + rollup count theo `User.DepartmentId` + cycle-guard HashSet) + picker "Phòng cha" fe-admin (self-service org chart) + Update cycle-guard, Run #292; (4) **Hồ sơ Nhân sự master-detail giống NamGroup** — `EmployeesListPage` rewrite: 3-panel→**2-cột** (cây tổ chức gốc "SOLUTION COMPANY" + list chồng TRÁI · chi tiết 5 tab PHẢI) + tô màu accent, giữ 100% 5 satellite CRUD (16 endpoint), Run #293/#294/#295; (5) **PE mục "e. Link hồ sơ"** Mig 52 `AddHoSoLinkToPurchaseEvaluation` (`HoSoLink string?` hyperlink NAS + `` target_blank rel-noopener + null-safe) + rename "Dự trù PRO"→"Ngân sách PRO" (row+badge) ×2 app SHA256-mirror, Run #293. **State THẬT: Mig 52 · 88 bảng · 263 test (45D+218I) · 65 gotcha · menu 53 · bundle admin `BDwV5d0X`/user `DbVv6rsf`** (Run #295 `456c7a7` Employee-refine — user rotate `CZfo_PFZ→DbVv6rsf`, cicd PASS). **🔥 Workflow fan-out chạy THẬT lần đầu** (`pe-hoso-link-rename-pro` BE∥FE→review) — parallel disjoint-file OK, NHƯNG FE+reviewer return-RỖNG #53 → em main recover-disk + self-gate (bắt badge "DỰ TRÙ PRO" sót rename); **verdict: fan-out cho parallelism nhưng reviewer-stage không tin được trong harness này → verify-heavy task vẫn tự gác = tương đương spawn lẻ** (`feedback_workflow_fanout_reliability`). gotcha **#65** NEW (build csproj con ≠ `dotnet build slnx` gồm tests → miss test-compile khi đổi chữ ký record command → CI CS7036 Run #291 FAIL-gated). **Prev S64** adopt **Harness-7 writing-quality floor** — em main solo, commit `6afde19` docs/gov-only, 0 sub spawn: `rules.md §1.1` outward-VN-full-grammar + reviewer Category 6 + adap-report + email ai_infra; broadcast body-hash `a4580ea9` verified-MATCH **KHÔNG mis-stamp** [false-mismatch = gotcha #61 PS5.1 UTF-8 decode của em]. Prev S63 docs-closeout bù S60/S61/S62 — **State THẬT: Mig 50 · 88 bảng · 263 test (45D+218I) · 64 gotcha · menu 53 · bundle admin `0xKYGhhf`/user `C81ZdG9G` Run #286**; S60-62 = PE ràng buộc gửi-duyệt + bypass drafter (S60) · gỡ "Từ chối" (S60) · Mig 50 ngân sách per-gói-thầu Excel anh Kiệt + XÓA module Budget cũ (S61) · vượt-NS cảnh-báo-mềm cho lưu (S62); + reconcile stray reviewer cwd-misland; session log `2026-06-12-S60-S62-pe-budget-workitem-softwarning.md`). Prev Session 59 ( **10 đợt ship prod-verified: 8 Run PASS + 2 cancelled-supersede-benign #273→#282** (run_number API — dải đếm khác #38x S58, cùng pipeline; 2 cancel = push-đè khi UAT góp ý realtime, ancestor-verified): (1) **wipe transactional testing data** theo anh Kiệt FDC — 10 PE + 7 HĐ [DEMO] + 64 notif + 1 AwV2 cũ inactive = 0, reset PeSeq/CtSeq → phiếu thật đầu tiên team tạo chiều nay = **PE/2026/A/001** ✓, app-recycle KHÔNG resurrect (DemoSeed gate held), uploads orphan dọn (`56882ac` #273); (2+3) **PE tree Panel 1 chốt 4 tầng "📅 Năm > 📁 Dự án > 🧱 Hạng mục > Phiếu"** (anh chốt follow-up sau bản gộp "Dự án (Năm)"; `yearGroups` useMemo, expand-key v3, FE-only — list DTO đã có workItemName S57bis) (`0eafcd3` #274); (4) **dọn 15 mã hạng mục demo tự chế** theo chị Trà Sol "xóa cái đám phần thô phần hoàn thiện… MÀ ANH TỰ ĐẺ RA" — WorkItems 86→**71**, GỠ HẲN block seed demo khỏi DbInitializer, đối chiếu 71/71 khớp bảng PMH từng dòng (`bbd1554` #275, bundle frozen BE-only); (5) **rename 71 mã đúng format PMH anh Kiệt chốt** "MÃ CV gồm chữ MEP-SUB-1 rồi tên 1 MEP Sub MEP (Full) — đúng kiểu vậy" → `MAT-n`/`SUB-n`/`MEP-SUB-n`/`MEP-EQU-n` + Name "STT nhóm tên"; **DB-trước-code-sau** (gotcha **#62** NEW — seed per-code idempotent, sai thứ tự = 142 rows) + sqlcmd `-f 65001` (gotcha **#61** NEW — verify data qua API JSON, KHÔNG tin console mojibake) + FE sort numeric ×3 ×2 app (`c869d26` #276); (6) **UAT 6 vòng 11 điểm**: NEW **`ui/SearchableSelect`** combobox gõ-lọc BỎ DẤU (fold NFD — Hạng mục/Dự án/NCC) + auto Địa điểm từ Project.Location + điều khoản TT Textarea đa dòng (`faed59f` #277) · anh chốt: **ẩn cả Trả lại+Từ chối khi người duyệt = người soạn** (drafterUserId match) + **quick-add NCC ngay form** (SuppliersController POST hạ → any-auth, PUT/DELETE giữ khóa — cicd authz probe live 4/4: 401 unauth/201 nv.test/403 delete/cleanup) + upload multiple files ×2 chỗ (`9c330d2` #278) · vòng 3-6 realtime (`f21c55d` #279-cancelled / `69997da` #280 / `80b64dd` #281-cancelled / `792c030` **#282 FINAL**): **bảng NCC table-fixed** width từng cột (file dài hết vỡ layout) + **bỏ ô "Tên" ngân sách nhập tay** (chỉ còn Số tiền, hasManual detect theo amount) + **GỠ field "Điều khoản thanh toán" khỏi TẤT CẢ form phiếu** (cột per-NCC + display phiếu cũ GIỮ) + **bỏ nút "+ Thêm hạng mục"** (1 phiếu = 1 hạng mục header). Bundle FINAL admin **`B1DtNT9C`**/user **`D6uF3Mln`** (Run #282). Test 240 ×2 local + 8× CI gate. **0/14 spawn truncated** (lần đầu sau nhiều session). → session log `2026-06-11-S59-wipe-tree-pmh-uat-batch.md`. Prev S58 (2026-06-11 — **4 việc prod-verified Run #382/#383/#384**: lock-demo-user fix + tạm ẩn HRM/Office/Cá nhân + Danh mục cuối sidebar + fe-user redesign theo UI/UX guide AI_INFRA. **Việc 1 — lock fix** (Run #382, `5998163` ~3m31s): Run #381 cicd phát hiện S57bis lock = NO-OP (14 email named-person là population Dev-only). Recon dump prod: demo thật = 20 UAT-matrix `{dept}.{nv,pp,tp}@`+`bod.{1,2}@` tạo TAY 05-13; root cause sâu = `DemoUserPassword` 11 ký tự < prod `RequiredLength=12` → `CreateAsync` silent-fail MỌI startup từ trước tới giờ (= root cause "helpdesk inert phòng IT 0 user" S56). Fix: union 20 email + password 12 ký tự. Prod sau deploy: **55 user / 21 active / 34 locked** — 20 UAT + 14 named-person locked ✓, **nv.cao/nv.truong CREATED+ACTIVE (helpdesk S56 RESOLVED)** ✓, 5 real staff created ✓, guard admin/catalog.manager/nv.test/chuong.phan-typo active ✓ (anh chốt 3 quyết định AskUserQuestion). Bundle FROZEN. gotcha **#60** NEW (seed silent-fail vs prod password policy — dump population thật trước khi lock/seed-by-email). +Closeout S57bis residual: gotcha #59 commit, 4 spawn-record on-behalf (H2 4-MISS), H1 5-patch doc-drift, test 240 re-verified local. Prev S57bis (2026-06-11 sáng) — **PE gắn Hạng mục công việc (Mig 49) + mở quyền Pe all-role + menu "Cá nhân" + khóa demo user** (sếp Zalo deadline 15:00): commit `17b23a4` (Harness-4 two-tier runtime-VERIFIED spawn-test 2 chiều) + `dd117b7` (product) → Run #381 PASS ~4m25s. Mig 49 `AddWorkItemToPurchaseEvaluation`: PE.WorkItemId `Guid?` loose-Guid KHÔNG FK vật lý (convention PE — database-agent design) + IX + validator NotEmpty create + FK-guard handler Conflict + UpdateDraft null-safe. FE ×2 app PeWorkspaceCreateView/PeHeaderForm (SHA256 identical)/PeDetailTabs "Dự án – Hạng mục". Pe_* 11 key CanRead+CanCreate mọi role (130 rows/13 role — Pe_* leaf KHÔNG nằm MenuKeys.All, build qua factory). Menu Personal root@30 + Chấm công re-parent + Master write-lock `Admin,CatalogManager` ×3 controller. Test 228→**240** (+12 PeWorkItemGuardTests). Bundle rotate cả 2: admin `CP4CB1ym` / user `BmZ3VHnm`. 2 builder truncated #53 + reviewer die-0-byte ×2 → em main solo vá cross-stack + self-gate. Excel (3) đối chiếu = NO-CHANGE (S55 data identical). Prev S56 — **Pre-golive verify sweep + golive-harden 4 fix — HMW 2-workflow, prod-verified**: commit `a20cde8` → Run #379 PASS ~4m20s. WF1 `pre-golive-verify` 7-stream song song + adversarial → 6 PASS/1 CONCERN/0 blocker = **GO**; key finds = **ops not code** (prod IT-dept 0 active user → helpdesk inert + S43 LeaveBalance lost-update còn nguyên). WF2 `golive-harden` fix 4: **#3** LeaveBalance lost-update→atomic `ExecuteUpdateAsync`+Serializable tx (NO mig, exactly-once nguyên) · **#5** ItTicket authz Forbidden-trước-NotFound (fail-closed) · **#6** DocxRenderer null-guard (2 warn→0) · **#4** Travel/Vehicle ApproveV2 +4 smoke. Test **216→228**. Bundle FROZEN `4SUwDLD8`/`XdKzt9LL` (BE-only). `sys.tables` re-ground **92→93** (cicd ground-truth, Mig 48 col-only). reviewer stage StructuredOutput-fail→em main đỡ cross-stack review (3 diff clean) + bump Serializable đóng MAJOR. gotcha **#58** NEW (EF read-modify-write lost-update→ExecuteUpdate atomic). **2 ops VPS pending** (gán user phòng IT + `tzutil` UTC+7). FE Phase 2 redesign **deferred** (recon ready). Prev S55 — **Nạp master data thật từ Excel + Project +4 cột (Mig 48), HMW-mode ON**: commit `69cb393` → Run #377 PASS ~4m33s, prod-verified. Anh giao file Excel "HẠNG MỤC CÔNG VIỆC DỰ ÁN" → `/ultra-on "workflow làm xong hết"`. Nạp **62 dự án + 71 hạng mục + 3 NCC** vào Project/WorkItem/Supplier qua `SeedRealMasterDataAsync` (per-code idempotent, **UNGATED** → coexist demo, tự lên prod). **Mig 48 `AddProjectMasterFields`**: Project +4 cột nullable (Year/Investor/Location/Package, NO new table). FE ProjectsPage form +4 input ×2 app SHA256 mirror. Test 216 (compile-fix MasterCatalogFilteredUniqueTests +4 null args, no new test). Bundle admin `DmjI8Cmn`→`B-d6893W`/user `YxL_MljK`→`XdKzt9LL` (cả 2 rotate). Prod verify: Mig 48 applied · Projects spot-6/6 · WorkItems VT/TP/MEP/TB=71 · Suppliers 3 · CAL01.Investor="Công ty TNHH Calofic". **2 agent return truncated** (implementer-backend + reviewer, gotcha #53) → em main disk/runtime-recover (build/test/sqlcmd/git truth); cicd verdict-FIRST → PASS clean no-truncate. Data-quality catch: MEP col gộp 2 nhóm + divider "THIẾT BỊ" → split đúng 71/4-category. Provenance `scripts/master-import-data.generated.md`. Prev S54 — **IT staff tự reassign ticket (cross-stack authz, HMW-mode ON)**: 1 code commit `ca4b602` → Run #376 PASS ~4m18s, prod-verified. Cho tổ IT (dept Code=="IT") + Admin reassign ItTicket trên CẢ 2 app. BE: NEW `GetAssignableItStaffQuery` capability endpoint `{canReassign,staff}` + `AssignItTicketHandler` authz Admin-OR-dept-IT (Forbidden) + assignee-must-IT (Conflict) + controller `/assign` hạ `[Authorize(Roles=Admin)]`→`[Authorize]` (handler fine-grained). FE: fe-admin+fe-user ItTicketsPage **SHA256-identical** (REVERSE S53 divergence) gate nút by `canReassign`, dropdown từ `/assignable-staff` (không `/users`). Test 203→**216** (+13 authz guard test-before-merge). NO migration (DepartmentId reuse). Bundle admin `DfCfHUE9`→`DmjI8Cmn` / user `_3S0BPJ2`→`YxL_MljK` (cả 2 rotate). 6-agent fan-out (BE∥FE→test→reviewer→cicd) + em main reconcile stray-memory residual (3 agent ghi MEMORY nhầm `src/Backend/.claude` → harvest về canonical). reviewer PASS 0 blocker (role-string "Admin" chain-verified). Task 1 Phase 9 Ops KHÔNG làm (anh dừng). flag: cicd `sys.tables=93` vs STATUS 92 → monthly audit re-ground.) Prev S53 (gotcha #57 EXT Master Mig 47 + P11-D reassign-UI fe-admin + P11-E menu + database-agent verified-runtime: `44b9e54` Run #260 + `dbf6648` Run #261, test→203, bundle→`DfCfHUE9`). Prev S52 (Phase 11 P11-D+E+F deployed + database-agent adopt, HMW-mode ON): 3 commit — `e9ee97f` (database-agent DB1–DB11 read-advisory, roster 10→11, executed-file CHỜ restart) + `6a66429` Wave 1 (P11-E AttendanceReport+Excel+OtPolicy multiplier + P11-F MaTicket codegen, migration-free) + `dcf76f8` Wave 2 (P11-D ItTicket round-robin assign dept-IT + SLA timer, Mig 46). Test 186→**200**. Bundle admin `DYfjnpY0`/user `_3S0BPJ2` (cả 2 deploy verified curl độc lập — Wave 1 BE 401 wired + Wave 2 /assign 401 + Mig 46 applied health-200). ⚠️ **Session-limit hit giữa Wave 2** → recovery: BE/test verify-on-disk + em main solo FE redo + curl-self-verify thay cicd-spawn (multi-agent resilience, git/disk/prod = source-of-truth). RAG recovered (chunk 2416 rerank live) nhưng stale 05-29. Prev S51: P11-C Vehicle+Driver.) +**Last updated:** 2026-06-19 (S76 — **PE ngân sách MA TRẬN 3 cột (Dự án|PRO|CCM) + bảng lưới `
` viền-ô + badge quyền-NS theo role** — anh Kiệt FDC + chị Trà Sol, go-live; anh giao `/ultra-on` "step-by-step + workflow-review-per-step, hoàn chỉnh rồi deploy". **3 commit prod-verified:** `e33481e` (feature Run #318 — Mig 56 + ma-trận-3-cột + badge designer/flow, `BhFDF9IJ`/`BAkuRl3C`) → `21d1f4e` (bảng lưới `
` theo phản hồi anh, Run #319 **`jOqxW4-p`**/**`DbsznVvR`**, smoke 3×200). **Part 1:** PRO sửa cột PRO (canEditPro), CCM sửa cột CCM (canEditCcm), Dự án FE hiển-thị-only; Mig 56 +ProInitial/ProAdjust additive + data-migrate ProEstimate→ProInitial (4 rows prod). **Part 2+3:** badge "✎ NS PRO/CCM" cạnh approver (suy từ role, display-only no-authz) ở Workflow Designer + flow Duyệt NCC. Test 339→**344** (+5). **2 workflow review** (Part 1 PASS + Part 2/3 PASS) — reviewer bắt **race mất-dữ-liệu** Part 1 (impl self-review sót) → vá `useIsFetching` 2 app, **gotcha #70 NEW**. Lessons: flex+gap KHÔNG ra "bảng" → phải `
` viền-ô · workflow-review-flaky (2-3 lane empty #53) → em-main self-gate lane rỗng. **NEXT:** 🔴 curate L1 over-cap (reviewer **45KB** + inv-codebase 35KB keep-floor-hit manual-condense; cicd 29KB + test-spec 28KB strike-1). → log `2026-06-19-S76-pe-budget-matrix-table-grid.md`. · **Prev S75 (2026-06-18)** — **Harness-11 adopt: engine bộ-nhớ-và-governance TỰ-BẢO-TRÌ + 2 double-check** — anh giao `/check-email AI_INFRA` + `/adap-apply` (mỗi stage workflow-review) + double-check ×2 + finalize report + push + session-end. em main + **5 Workflow** (audit `wf_7fdc3bd5-930` → implement `wf_c5e5844e-7c1` → review `wf_d7ca1ff8-942` → double-check `wf_a0b68d2f-30e` → checklist-verify `wf_39cd4cbe-f07`). **0 production code.** Built `scripts/governance-detectors.ps1` (C1 broken-pointer + C2/B3 staleness + C3 vocab-fork + C4 self-exclusion + C5 resolve, NO-API DÒ+FLAG, runtime **26 flag** bắt drift thật, C4 0-self-match) + `scripts/memory-archive-gate.ps1` (PHẦN A hysteresis 0.85/keep-floor 5/2-strike/A7 NO-API 186/186) + `docs/governance/harness-11-engine.md` canonical (3-tier D5/D6/D7 EXPLICIT + one-direction-lock D8) + **B1 ×11 count→pointer `docs/STATUS.md`** (drift mig53→55/test306→339/gotcha68→69/table93→88 RESOLVED) + cadence-wire session-start §2.1.3 (D1) + session-end §L.b(c) (D2) + agents/README Upgrade S75. **completeness-gate FORMAL ĐẠT** (B 4/4 + C 5/5 + D 11/11 đủ-trọn, A 🟡 tailored — A6 runtime cần 2×-Apply legit-by-design). **gotcha #30 reinforced** (box-glyph ├└─ KHÔNG vào .ps1 — PS5.1 -File ANSI mojibake, KỂ CẢ qua Edit render-normalize; detector pure-ASCII verified). 3 commit `e70c046`+`ae957c4`+`aa09e99` pushed + adap-report + email ai_infra (`2316773229f2`). **State THẬT GIỮ NGUYÊN: Mig 55 · 88 bảng · 339 test · gotcha 69 · menu 54 · bundle `BYF5vIMJ`/`CB-tiRxd`.** ⚠️ restart CLI (cadence §2.1.3/§L.b + ef-core skill). 🔴 curate-debt: reviewer 38.8KB + inv-codebase 31.5KB + cicd 26.8KB over-cap (archive-gate confirm; reviewer+inv keep-floor hit → manual split). → log `2026-06-18-S75-harness-11-adopt.md`. **Prev S74** — **PE ô "Ghi chú từ CCM" ngân sách gói thầu (Mig 55)** — anh forward 2 luồng Zalo (Trà Sol + Kiệt FDC) "CCM cũng giống PRO cho nhập + ghi chú, hiện 0 hết" → em-main BE+Mig55 ∥ implementer-frontend FE 2 app SHA-mirror ∥ test-specialist (chết rate-limit → recover-disk). 2 fork anh chốt qua AskUserQuestion: **thêm CcmNote (Mig 55)** + **GIỮ phân vai CCM** (CostControl/Admin). Entity `PeWorkItemBudget +CcmNote` mirror ProNote · `UpdatePeBudgetCcmCommand +CcmNote` absolute-set role-gate fail-closed · DTO+controller+mapping · FE dòng "Ghi chú từ CCM" gate `canEditCcm` (sau V0/hiệu chỉnh, absolute-set đủ 3 field). Mig 55 additive-nullable sạch (no #63). Test 334→**339** (+5 CcmNote). **Chẩn đoán "0 hết không nhập":** tài khoản thiếu role CostControl (KHÔNG bug) → UAT bằng CCM/Admin. commit `8655ebf` push main (cicd **Run #315 PASS** ~4m54s — Mig 55 applied prod, bundle admin `BYF5vIMJ`/user `CB-tiRxd`, smoke 4×200). Lessons: controller call-site thiếu arg sau +param record (grep+vá, #65 bắt) · agent-kill-529 recover-disk no-re-spawn · PS here-string commit-msg trong if-block mis-parse → `git commit -F`. → session log `2026-06-18-S74-pe-ccm-note.md`. **Prev S73** — **Mig 54 PE giá đề xuất PRO/CCM + CEO chọn giá chốt + CCM duyệt-done ô-tích (anh Kiệt FDC — go-live so-sánh-giá thứ Hai 22/06)** — em main + 6 sub + 2 review-workflow. **2 commit prod-verified:** `1d86abc` (feature, cicd Run #313 PASS — Mig 54 + bundle admin `OlNyG9OD`/user `DSzSLVtL`) + `6aa4dcb` (FE empty-candidates guard fix). **BE:** Mig 54 `AddPeSuggestedAndApprovedPrice` 5 cột additive-nullable (ProSuggestedMin/Max·CcmSuggested·ApprovedPriceAmount·Source) + 2 setter role-gate PRO/CCM (Forbidden fail-closed mirror budget) + ApproveV2 **③ AUTO→OPT-IN** (`finalizeByCcmDelegation` ô-tích, gỡ auto-threshold S69) + **① bind giá chốt** mọi nhánh DaDuyet (human bắt-buộc-chọn, isSystem-miễn) + DTO +7 field/capability. **FE 2 app SHA-mirror:** PeWorkflowPanel (bộ chọn giá + ô tích CCM + fix empty-candidates) + PeDetailTabs `SuggestedPriceRows` + types +7. **Test 306→334** (+28). **2 workflow review (bài học):** R1 schema-force CHỈ **1/4** lane returned (flaky no-StructuredOutput) → R2 **free-text 2/3 PASS** — rủi ro #1 FE empty-candidates = **UNREACHABLE** (submit-guard `:194` chặn gửi-duyệt khi winnerQuoteTotal≤0). → review fan-out dùng **free-text / hmw RUN-TRACE**, KHÔNG ép-schema inline. **Governance honest:** em lệch mandate auto-Workflow (mode-ON) + chỉ khai khi anh hỏi → **cam kết báo-TRƯỚC-khi-lệch**. Run-trace `runs/2026-06-18-mig54-pe-review/` (custom-inline, bù post-hoc). → session log `2026-06-18-S73-pe-gia-de-xuat-ccm-done.md` pending. · **Prev S72** — **Harness-10 flat-refine + checklist-v2 adopt (re-audit 3-workflow) + sleep NO-OP** — em main + 9 sub qua 3 Workflow (audit `wf_13868efb-ea7` → implement `wf_ac43b5ff-7d1` → review `wf_d482e10d-5dd`, mandate Harness-9 B1+B2). Anh chỉ "sisters làm tốt mỗi SE thiếu" → re-audit đo vs canonical: **SE BEHIND Harness-10 flat** (run-trace còn SUBFOLDER, canonical=FLAT) → migrate `hmw.js` (`:103` sub-md/→flat + `:52` schema + **H4.5→H8 doc-drift**) + `workflows/README` full-rewrite + `runs/README` + session-start/end + `agents/README` Upgrade S72 + `_ledger`; **5 run cũ S71 GIỮ subfolder** (C8 dual-accept). +2 broadcast 06-18 adopt (checklist-v2 + h10-flat-detector-refine). +`/sleep-recovery-memory-l2` port (A8, §J2-tailored SE-only, floor intact, live skill). **detector refine-b TAILORED-OUT** (SE Workflow-tool no-CLI-bypass; containment git-diff+tracked+orphan-scan G-015). **REVIEW (B2 dogfood) bắt 2 lỗi IMPLEMENT-self-assess sót** (hmw.js:52 schema-stale + sleep-cmd auto-check un-wired overclaim) → fixed (auto-check **WIRED thật**: budget.json `+last_sleep_at` + session-start:78 + session-end:48, grep-verified). em-main containment-check tự bắt 1 reviewer residual-write (raw-Workflow KHÔNG hmw.js writeGuard → reverse-finding #4) → revert. **B3 self-correct:** S71 report path tắt `runs/`+"14" → thật `.claude/workflows/runs`=22. **Sleep `all`=NO-OP** (5/9 empty-archive + 4/9 đã-gisted → 0 compressed; `last_sleep_at`=2026-06-18). **0 production code, state THẬT GIỮ NGUYÊN.** adap-report `2026-06-18-Governance-harness-10-flat-refine-checklist-v2` + email ai_infra (`5f511fe5c0f2`). ⚠️ **restart CLI** activate FLAT-runtime hmw.js + wired sleep-check + sleep-recovery skill. 🔴 **NEXT (em):** curate investigator-codebase L1 **28973 OVER** (audit-trail) + watch frontend-designer/test-specialist near-cap. → session log `2026-06-18-S72-harness-10-flat-refine.md`. **Prev S71 — Harness-10 adopt: tracked run-trace folder convention + checklist 9-10 self-verify** — em main + 3 Workflow (invest `wf_9c2cd2cd-2e7` → implement `wf_e4e46725-231` → review `wf_636bc95b-939`) theo mandate Harness-9 PART 2. **0 production code** (governance/workflow only). Migrate `.claude/workflows/wave-*/` gitignored → `runs//` **git-TRACKED** (run.md + sub-md/ + harvest/) + `_ledger.md` 2-nhịp + 3-layer anti-miss (L1 em-main@P1 ledger-check / L2 session-start orphan-scan / L3 session-end close-gate **idempotent-VERIFY-not-re-APPEND**) + **containment model shift** (Harness-2 B6 "mọi tracked-change=vi-phạm" → Harness-10 "tracked-change NGOÀI run-folder + code-disjoint=vi-phạm"; run-folder TRACKED → HIỆN git-diff = audit trực-tiếp). hmw.js wave→run-trace (accept `args.run`, alias `wave`; path `sub-md/`; `node --check` PARSE-OK). **Review độc-lập (R2+R3) bắt C5 L1 over-claim** (doc nói hmw.js prompt-builder emit L1 reminder nhưng grep engine=0 → engine no-fs → fixed path-a: L1 = em-main @P1 convention) — dogfood mandate B2 đúng (1-workflow-tự-chấm sẽ bỏ sót). **checklist 9-10:** Part A (Harness-9 memory) + Part B (adap 2-workflow) ĐÃ done S70; Part C (Harness-10) MỚI 8/8. dogfood: 3 run-trace folder TRACKED đầu + 3 entry ledger. adap-report `2026-06-18-Governance-checklist-harness-9-10` + email ai_infra. **State THẬT GIỮ NGUYÊN: Mig 53 · 88 bảng · 306 test · 68 gotcha · menu 54 · bundle admin `BgNCjwsG`/user `CBvh0vtf`.** + `CLAUDE.md` test-flush 263→306 (pre-existing uncommitted, resolve H1 stale-flag). ⚠️ restart CLI activate hmw.js RUN-TRACE runtime + (carry S66/S70) §2.1.2 budget-audit/reviewer-Cat-6/H8-inherit. ✅ **curate-debt CLOSED S71** (FINALIZE double-check `wf_73de399d-753` + curate `wf_f32987b8-03f`): reviewer 36.7→24.8KB + investigator 29.8→23.2KB (both <25600 cap, archive +N -0 0-byte-loss) + reviewer-gist gen:2 + 3 user-memory G2/G3 + budget.json re-measure. Race root-cause fixed cấu-trúc (hmw.js RUN-TRACE writeGuard). → session log `2026-06-18-S71-harness-10-run-trace.md` (pending). **Prev S70** — **Harness-9 adopt: L2 archive dark-matter recovery + adap 2-workflow mandate** — em main + 3 Workflow (investigate `wf_be952f3c-97f` → implement `wf_a58e0d15-beb` → audit `wf_9520d8cd-4fe`) + 2 monitor bootstrap. **0 production code** (governance/memory only, commit `f36aab8` pushed). 4 over-cap sub-agent (cicd-monitor/investigator-codebase/reviewer/implementer-backend) curate L1→L2 + `archive/_INDEX.md` (mục-lục con-trỏ **substring sha-keyed**, Ctrl-F fallback) + `.gist.md` (nén 4-field distill-gen:1, verbatim FROZEN) → **cả 4 < 25KB auto-inject cap** (P1 curate-debt CLOSED; ~240KB archive hết RAG-dark). PART 2/3 process-mandate codify (adap-apply + agents/README Upgrade S70 + session-start §2.1.2 budget-audit). **0-byte-loss git+sha verified** (Stage C audit + em-main self-gate khi 2 reviewer no-StructuredOutput). +`memory-budget.json` (seed-by-measure) + `scripts/measure-agent-memory.ps1` + `.ragignore` + doc-drift 4-cite flush. adap-report + email-back ai_infra (body-hash `7c07b716e775`). **State THẬT GIỮ NGUYÊN: Mig 53 · 88 bảng · 306 test · 68 gotcha · menu 54 · bundle admin `BgNCjwsG`/user `CBvh0vtf`.** ⚠️ restart CLI để activate session-start §2.1.2 + pending H8 inherit + reviewer Cat-6. → session log `2026-06-17-S70-harness-9-l2-recovery.md`. **Prev S69** — **Văn phòng số (E-Office) port + golive + PE cờ gấp/ngưỡng CCM — 4 deploy prod-verified Run #305→#308, HMW-mode ON, 2 workflow fan-out + ~14 spawn**: foundation PURO (shared PageHeader/KpiCard/WidgetCard + Dashboard 2-cột + sync fe-admin index.css đóng drift S66-S68) #305 → **re-skin TRỌN 10 page** PURO layout + CSS Hồ sơ NS, phẫu-thuật-giữ-100%-logic (reviewer verify api/queryKey byte-identical) #306 → **Office golive public** `SeedAllRolesOfficeModulePermissionsAsync` read+create 16-key allow-list mọi role (mirror S65 pattern, chạy SAU revoke; excluded Off_PhongHop_Manage/Off_AttendanceReport/Off_ChamCong giữ ẩn; cicd DB-verify 16/16×13 role) #307 → **PE cờ gấp PRO/CCM + CCM duyệt-final theo ngưỡng giá trị** Mig 53 (anh Kiệt FDC sau họp sếp) #308. **State THẬT: Mig 53 · 88 bảng · 306 test (45D+261I) · 68 gotcha · menu 54 · bundle admin `BgNCjwsG`/user `CBvh0vtf` Run #308.** 2 gotcha NEW (**#67** Tailwind accent palette thiếu-stop vỡ-màu-im-lặng · **#68** stale-diagnostic-background-agent). 2 truncation #53 (impl-backend positional-record + impl-frontend) → em main recover-disk + self-gate (cả 2 build PASS sau-cùng). C (sau duyệt → chuyển phiếu đến dự án) chờ anh Kiệt spec form. → session log `2026-06-17-S69-vanphong-golive-pe-urgent-threshold.md`. **Prev S68** — **Hồ sơ NS header chi tiết NV: làm nổi bật + fix tên render đen→trắng** — em main solo, 3 commit (2 FE `6983609`+`37752eb` · 1 docs `11bc96d`), 2 deploy prod-verified Run #303-304 (anh UAT realtime): tên `text-xl extrabold`+drop-shadow · dòng meta `text-[13px] font-medium` trắng-đậm · badge pill **màu theo trạng thái** (#303); anh báo "tên đen nền xanh ko nổi bật" → diagnose **rule `h1-h4{color:#0b1220}` viết NGOÀI `@layer` thắng `text-white`** (Tailwind v4 unlayered > all layers, **gotcha #66 NEW**) → ép `text-white!` + thu nhỏ `text-lg` (#304, grep dist confirm `!important`). Bootstrap: 2 monitor RE-REPORT CLEAN + 2 doc-drift fix (dep-audit 64→65 · root CLAUDE 263→286, H1 flag). **State THẬT: Mig 52 · 88 bảng · 286 test · 66 gotcha · menu 53 · bundle admin `CNUv1jxY`/user `CpOskeS1` Run #304.** ⚠️ curate-debt P1: cicd-monitor **44.1KB** (worst). **Prev S67:** **buổi sản phẩm LỚN, 6 deploy prod-verified Run #297→#302** (anh + anh Kiệt FDC UAT realtime, HMW-mode ON): fe-user Hồ sơ NS đồng nhất font + chữ đen→**xanh đậm `brand-800`** (#297 `ab4e681`) · **fe-admin mirror master-detail** + accent tokens index.css [đóng pending lớn nhất HRM go-live, page SHA256 ×2] (#298 `292d64d`) · **+23 test-after HRM** [Dept cycle-guard · PE HoSoLink absolute-set · HRM-perm seed] → 263→**286** (#299 `bcd619d`) · **list flex-row gọn** [bảng 3-cột→flex-row, `overflow-x-hidden` → hết tràn ngang rail] + đồng nhất cỡ chữ [18/14/13/11px] ×2 app (#300 `91aaf05`) · **PE Link hồ sơ auto-detect** `http(s)`→hyperlink/`O:\`→Copy (#301 `6df1b2d`) → **render link `file://` bấm-thử** + Copy (#302 `536dd6b`). **Workflow research 3 investigator-api** (auto-mở `O:\` từ web): default browser CHẶN https→file://; **chỉ Edge GPO `IntranetFileLinksEnabled`** (Edge-only, Intranet Zone, 1 GPO domain zero-per-machine) = one-click thật — Chrome không có; .url-download zero-install nhưng 2-click. **State THẬT: Mig 52 · 88 bảng · 286 test (45D+241I) · 65 gotcha · menu 53 · bundle admin `CcrZqfht`/user `DniDFUB_`** (#302). test-specialist truncated return #53 → em main recover-disk (3 file test đủ + 286 verify); 0 production bug. ⚠️ curate-debt: cicd-monitor 39.8KB + inv-codebase 39.4KB over-cap. **Prev S66** — **session-end closeout em-main-solo, 0 product-sub:** adopt **Harness-8 all-inherit** (7 sub demoted `claude-opus-4-8`→`inherit` → cả 11 = inherit, gỡ two-tier Harness-4) + **cicd-monitor L1 curate 86.8→28.9KB** (byte-exact sed → `archive/2026-06.md`, incl #291 forensic) + **ef-core skill doc-flush Mig 50→52** (H1 drift, +Mig 51/52 rows) + check-email AI_INFRA (**0 thư mới se-directed**). ~17 file docs/gov/config, **0 production code** → state THẬT GIỮ NGUYÊN: **Mig 52 · 88 bảng · 263 test · 65 gotcha · menu 53 · bundle admin `BDwV5d0X`/user `DbVv6rsf`**. ⚠️ **Restart CLI** để H8 runtime (frontmatter no hot-reload) + reviewer Category 6 (S64 pending). adap-report `2026-06-16-Governance-harness-8-all-inherit-workflow-fastest` + email-back ai_infra (hash `fa7f690d` round-trip MATCH). Prev S65 — **HRM go-live: public Hồ sơ Nhân sự + trang master-detail giống NamGroup + Department hierarchy + PE Link hồ sơ**, ~6 deploy prod-verified Run #289→#295, anh + anh Kiệt FDC UAT realtime): (1) **public Hồ sơ Nhân sự mọi role** — `SeedAllRolesHrmProfileReadPermissionsAsync` grant CanRead `Hrm`+`Hrm_HoSo` 13 role chạy SAU revoke S58 (upgrade-only; EmployeesController policy-based `Hrm_HoSo.Read` mở luôn API không hardcode Roles), giữ ẩn Dashboard NS, Run #289; (2) **redesign màu foundation fe-user** — accent palette teal/violet/amberx/greenx + `.app-gradient-brand`/`.card-accent`/`.icon-chip` + heading 700, brand #1F7DC1 + Be Vietnam Pro giữ, Run #290; (3) **Department hierarchy** Mig 51 `AddDepartmentParentId` (ParentId loose-Guid no-FK + `GET /departments/tree` ráp cây in-memory + rollup count theo `User.DepartmentId` + cycle-guard HashSet) + picker "Phòng cha" fe-admin (self-service org chart) + Update cycle-guard, Run #292; (4) **Hồ sơ Nhân sự master-detail giống NamGroup** — `EmployeesListPage` rewrite: 3-panel→**2-cột** (cây tổ chức gốc "SOLUTION COMPANY" + list chồng TRÁI · chi tiết 5 tab PHẢI) + tô màu accent, giữ 100% 5 satellite CRUD (16 endpoint), Run #293/#294/#295; (5) **PE mục "e. Link hồ sơ"** Mig 52 `AddHoSoLinkToPurchaseEvaluation` (`HoSoLink string?` hyperlink NAS + `` target_blank rel-noopener + null-safe) + rename "Dự trù PRO"→"Ngân sách PRO" (row+badge) ×2 app SHA256-mirror, Run #293. **State THẬT: Mig 52 · 88 bảng · 263 test (45D+218I) · 65 gotcha · menu 53 · bundle admin `BDwV5d0X`/user `DbVv6rsf`** (Run #295 `456c7a7` Employee-refine — user rotate `CZfo_PFZ→DbVv6rsf`, cicd PASS). **🔥 Workflow fan-out chạy THẬT lần đầu** (`pe-hoso-link-rename-pro` BE∥FE→review) — parallel disjoint-file OK, NHƯNG FE+reviewer return-RỖNG #53 → em main recover-disk + self-gate (bắt badge "DỰ TRÙ PRO" sót rename); **verdict: fan-out cho parallelism nhưng reviewer-stage không tin được trong harness này → verify-heavy task vẫn tự gác = tương đương spawn lẻ** (`feedback_workflow_fanout_reliability`). gotcha **#65** NEW (build csproj con ≠ `dotnet build slnx` gồm tests → miss test-compile khi đổi chữ ký record command → CI CS7036 Run #291 FAIL-gated). **Prev S64** adopt **Harness-7 writing-quality floor** — em main solo, commit `6afde19` docs/gov-only, 0 sub spawn: `rules.md §1.1` outward-VN-full-grammar + reviewer Category 6 + adap-report + email ai_infra; broadcast body-hash `a4580ea9` verified-MATCH **KHÔNG mis-stamp** [false-mismatch = gotcha #61 PS5.1 UTF-8 decode của em]. Prev S63 docs-closeout bù S60/S61/S62 — **State THẬT: Mig 50 · 88 bảng · 263 test (45D+218I) · 64 gotcha · menu 53 · bundle admin `0xKYGhhf`/user `C81ZdG9G` Run #286**; S60-62 = PE ràng buộc gửi-duyệt + bypass drafter (S60) · gỡ "Từ chối" (S60) · Mig 50 ngân sách per-gói-thầu Excel anh Kiệt + XÓA module Budget cũ (S61) · vượt-NS cảnh-báo-mềm cho lưu (S62); + reconcile stray reviewer cwd-misland; session log `2026-06-12-S60-S62-pe-budget-workitem-softwarning.md`). Prev Session 59 ( **10 đợt ship prod-verified: 8 Run PASS + 2 cancelled-supersede-benign #273→#282** (run_number API — dải đếm khác #38x S58, cùng pipeline; 2 cancel = push-đè khi UAT góp ý realtime, ancestor-verified): (1) **wipe transactional testing data** theo anh Kiệt FDC — 10 PE + 7 HĐ [DEMO] + 64 notif + 1 AwV2 cũ inactive = 0, reset PeSeq/CtSeq → phiếu thật đầu tiên team tạo chiều nay = **PE/2026/A/001** ✓, app-recycle KHÔNG resurrect (DemoSeed gate held), uploads orphan dọn (`56882ac` #273); (2+3) **PE tree Panel 1 chốt 4 tầng "📅 Năm > 📁 Dự án > 🧱 Hạng mục > Phiếu"** (anh chốt follow-up sau bản gộp "Dự án (Năm)"; `yearGroups` useMemo, expand-key v3, FE-only — list DTO đã có workItemName S57bis) (`0eafcd3` #274); (4) **dọn 15 mã hạng mục demo tự chế** theo chị Trà Sol "xóa cái đám phần thô phần hoàn thiện… MÀ ANH TỰ ĐẺ RA" — WorkItems 86→**71**, GỠ HẲN block seed demo khỏi DbInitializer, đối chiếu 71/71 khớp bảng PMH từng dòng (`bbd1554` #275, bundle frozen BE-only); (5) **rename 71 mã đúng format PMH anh Kiệt chốt** "MÃ CV gồm chữ MEP-SUB-1 rồi tên 1 MEP Sub MEP (Full) — đúng kiểu vậy" → `MAT-n`/`SUB-n`/`MEP-SUB-n`/`MEP-EQU-n` + Name "STT nhóm tên"; **DB-trước-code-sau** (gotcha **#62** NEW — seed per-code idempotent, sai thứ tự = 142 rows) + sqlcmd `-f 65001` (gotcha **#61** NEW — verify data qua API JSON, KHÔNG tin console mojibake) + FE sort numeric ×3 ×2 app (`c869d26` #276); (6) **UAT 6 vòng 11 điểm**: NEW **`ui/SearchableSelect`** combobox gõ-lọc BỎ DẤU (fold NFD — Hạng mục/Dự án/NCC) + auto Địa điểm từ Project.Location + điều khoản TT Textarea đa dòng (`faed59f` #277) · anh chốt: **ẩn cả Trả lại+Từ chối khi người duyệt = người soạn** (drafterUserId match) + **quick-add NCC ngay form** (SuppliersController POST hạ → any-auth, PUT/DELETE giữ khóa — cicd authz probe live 4/4: 401 unauth/201 nv.test/403 delete/cleanup) + upload multiple files ×2 chỗ (`9c330d2` #278) · vòng 3-6 realtime (`f21c55d` #279-cancelled / `69997da` #280 / `80b64dd` #281-cancelled / `792c030` **#282 FINAL**): **bảng NCC table-fixed** width từng cột (file dài hết vỡ layout) + **bỏ ô "Tên" ngân sách nhập tay** (chỉ còn Số tiền, hasManual detect theo amount) + **GỠ field "Điều khoản thanh toán" khỏi TẤT CẢ form phiếu** (cột per-NCC + display phiếu cũ GIỮ) + **bỏ nút "+ Thêm hạng mục"** (1 phiếu = 1 hạng mục header). Bundle FINAL admin **`B1DtNT9C`**/user **`D6uF3Mln`** (Run #282). Test 240 ×2 local + 8× CI gate. **0/14 spawn truncated** (lần đầu sau nhiều session). → session log `2026-06-11-S59-wipe-tree-pmh-uat-batch.md`. Prev S58 (2026-06-11 — **4 việc prod-verified Run #382/#383/#384**: lock-demo-user fix + tạm ẩn HRM/Office/Cá nhân + Danh mục cuối sidebar + fe-user redesign theo UI/UX guide AI_INFRA. **Việc 1 — lock fix** (Run #382, `5998163` ~3m31s): Run #381 cicd phát hiện S57bis lock = NO-OP (14 email named-person là population Dev-only). Recon dump prod: demo thật = 20 UAT-matrix `{dept}.{nv,pp,tp}@`+`bod.{1,2}@` tạo TAY 05-13; root cause sâu = `DemoUserPassword` 11 ký tự < prod `RequiredLength=12` → `CreateAsync` silent-fail MỌI startup từ trước tới giờ (= root cause "helpdesk inert phòng IT 0 user" S56). Fix: union 20 email + password 12 ký tự. Prod sau deploy: **55 user / 21 active / 34 locked** — 20 UAT + 14 named-person locked ✓, **nv.cao/nv.truong CREATED+ACTIVE (helpdesk S56 RESOLVED)** ✓, 5 real staff created ✓, guard admin/catalog.manager/nv.test/chuong.phan-typo active ✓ (anh chốt 3 quyết định AskUserQuestion). Bundle FROZEN. gotcha **#60** NEW (seed silent-fail vs prod password policy — dump population thật trước khi lock/seed-by-email). +Closeout S57bis residual: gotcha #59 commit, 4 spawn-record on-behalf (H2 4-MISS), H1 5-patch doc-drift, test 240 re-verified local. Prev S57bis (2026-06-11 sáng) — **PE gắn Hạng mục công việc (Mig 49) + mở quyền Pe all-role + menu "Cá nhân" + khóa demo user** (sếp Zalo deadline 15:00): commit `17b23a4` (Harness-4 two-tier runtime-VERIFIED spawn-test 2 chiều) + `dd117b7` (product) → Run #381 PASS ~4m25s. Mig 49 `AddWorkItemToPurchaseEvaluation`: PE.WorkItemId `Guid?` loose-Guid KHÔNG FK vật lý (convention PE — database-agent design) + IX + validator NotEmpty create + FK-guard handler Conflict + UpdateDraft null-safe. FE ×2 app PeWorkspaceCreateView/PeHeaderForm (SHA256 identical)/PeDetailTabs "Dự án – Hạng mục". Pe_* 11 key CanRead+CanCreate mọi role (130 rows/13 role — Pe_* leaf KHÔNG nằm MenuKeys.All, build qua factory). Menu Personal root@30 + Chấm công re-parent + Master write-lock `Admin,CatalogManager` ×3 controller. Test 228→**240** (+12 PeWorkItemGuardTests). Bundle rotate cả 2: admin `CP4CB1ym` / user `BmZ3VHnm`. 2 builder truncated #53 + reviewer die-0-byte ×2 → em main solo vá cross-stack + self-gate. Excel (3) đối chiếu = NO-CHANGE (S55 data identical). Prev S56 — **Pre-golive verify sweep + golive-harden 4 fix — HMW 2-workflow, prod-verified**: commit `a20cde8` → Run #379 PASS ~4m20s. WF1 `pre-golive-verify` 7-stream song song + adversarial → 6 PASS/1 CONCERN/0 blocker = **GO**; key finds = **ops not code** (prod IT-dept 0 active user → helpdesk inert + S43 LeaveBalance lost-update còn nguyên). WF2 `golive-harden` fix 4: **#3** LeaveBalance lost-update→atomic `ExecuteUpdateAsync`+Serializable tx (NO mig, exactly-once nguyên) · **#5** ItTicket authz Forbidden-trước-NotFound (fail-closed) · **#6** DocxRenderer null-guard (2 warn→0) · **#4** Travel/Vehicle ApproveV2 +4 smoke. Test **216→228**. Bundle FROZEN `4SUwDLD8`/`XdKzt9LL` (BE-only). `sys.tables` re-ground **92→93** (cicd ground-truth, Mig 48 col-only). reviewer stage StructuredOutput-fail→em main đỡ cross-stack review (3 diff clean) + bump Serializable đóng MAJOR. gotcha **#58** NEW (EF read-modify-write lost-update→ExecuteUpdate atomic). **2 ops VPS pending** (gán user phòng IT + `tzutil` UTC+7). FE Phase 2 redesign **deferred** (recon ready). Prev S55 — **Nạp master data thật từ Excel + Project +4 cột (Mig 48), HMW-mode ON**: commit `69cb393` → Run #377 PASS ~4m33s, prod-verified. Anh giao file Excel "HẠNG MỤC CÔNG VIỆC DỰ ÁN" → `/ultra-on "workflow làm xong hết"`. Nạp **62 dự án + 71 hạng mục + 3 NCC** vào Project/WorkItem/Supplier qua `SeedRealMasterDataAsync` (per-code idempotent, **UNGATED** → coexist demo, tự lên prod). **Mig 48 `AddProjectMasterFields`**: Project +4 cột nullable (Year/Investor/Location/Package, NO new table). FE ProjectsPage form +4 input ×2 app SHA256 mirror. Test 216 (compile-fix MasterCatalogFilteredUniqueTests +4 null args, no new test). Bundle admin `DmjI8Cmn`→`B-d6893W`/user `YxL_MljK`→`XdKzt9LL` (cả 2 rotate). Prod verify: Mig 48 applied · Projects spot-6/6 · WorkItems VT/TP/MEP/TB=71 · Suppliers 3 · CAL01.Investor="Công ty TNHH Calofic". **2 agent return truncated** (implementer-backend + reviewer, gotcha #53) → em main disk/runtime-recover (build/test/sqlcmd/git truth); cicd verdict-FIRST → PASS clean no-truncate. Data-quality catch: MEP col gộp 2 nhóm + divider "THIẾT BỊ" → split đúng 71/4-category. Provenance `scripts/master-import-data.generated.md`. Prev S54 — **IT staff tự reassign ticket (cross-stack authz, HMW-mode ON)**: 1 code commit `ca4b602` → Run #376 PASS ~4m18s, prod-verified. Cho tổ IT (dept Code=="IT") + Admin reassign ItTicket trên CẢ 2 app. BE: NEW `GetAssignableItStaffQuery` capability endpoint `{canReassign,staff}` + `AssignItTicketHandler` authz Admin-OR-dept-IT (Forbidden) + assignee-must-IT (Conflict) + controller `/assign` hạ `[Authorize(Roles=Admin)]`→`[Authorize]` (handler fine-grained). FE: fe-admin+fe-user ItTicketsPage **SHA256-identical** (REVERSE S53 divergence) gate nút by `canReassign`, dropdown từ `/assignable-staff` (không `/users`). Test 203→**216** (+13 authz guard test-before-merge). NO migration (DepartmentId reuse). Bundle admin `DfCfHUE9`→`DmjI8Cmn` / user `_3S0BPJ2`→`YxL_MljK` (cả 2 rotate). 6-agent fan-out (BE∥FE→test→reviewer→cicd) + em main reconcile stray-memory residual (3 agent ghi MEMORY nhầm `src/Backend/.claude` → harvest về canonical). reviewer PASS 0 blocker (role-string "Admin" chain-verified). Task 1 Phase 9 Ops KHÔNG làm (anh dừng). flag: cicd `sys.tables=93` vs STATUS 92 → monthly audit re-ground.) Prev S53 (gotcha #57 EXT Master Mig 47 + P11-D reassign-UI fe-admin + P11-E menu + database-agent verified-runtime: `44b9e54` Run #260 + `dbf6648` Run #261, test→203, bundle→`DfCfHUE9`). Prev S52 (Phase 11 P11-D+E+F deployed + database-agent adopt, HMW-mode ON): 3 commit — `e9ee97f` (database-agent DB1–DB11 read-advisory, roster 10→11, executed-file CHỜ restart) + `6a66429` Wave 1 (P11-E AttendanceReport+Excel+OtPolicy multiplier + P11-F MaTicket codegen, migration-free) + `dcf76f8` Wave 2 (P11-D ItTicket round-robin assign dept-IT + SLA timer, Mig 46). Test 186→**200**. Bundle admin `DYfjnpY0`/user `_3S0BPJ2` (cả 2 deploy verified curl độc lập — Wave 1 BE 401 wired + Wave 2 /assign 401 + Mig 46 applied health-200). ⚠️ **Session-limit hit giữa Wave 2** → recovery: BE/test verify-on-disk + em main solo FE redo + curl-self-verify thay cicd-spawn (multi-agent resilience, git/disk/prod = source-of-truth). RAG recovered (chunk 2416 rerank live) nhưng stale 05-29. Prev S51: P11-C Vehicle+Driver.) --- @@ -11,30 +11,31 @@ | Metric | Value | Note | |---|---|---| -| Migrations | **55** | +S74: Mig 55 `AddCcmNoteToPeWorkItemBudget` (PeWorkItemBudget +CcmNote nvarchar(1000) — "Ghi chú từ CCM" mirror ProNote · additive-nullable, 0 backfill, no new table). +S73: Mig 54 `AddPeSuggestedAndApprovedPrice` (PE +5 cột giá đề xuất PRO Min/Max + CCM + giá chốt ApprovedPriceAmount/Source — additive-nullable, 0 backfill, prod-verified Run #313). +S69: Mig 53 `AddPeUrgentAndCeoApprovalThreshold` (PE +IsUrgentByPro/Ccm cờ gấp + ApprovalWorkflow +CeoApprovalThreshold ngưỡng CCM duyệt-final · 3 AddColumn no new table). Prev S65: Mig 51 `AddDepartmentParentId` (Department.ParentId loose-Guid no-FK — org-tree) + Mig 52 `AddHoSoLinkToPurchaseEvaluation` (PE HoSoLink nvarchar(1000) hyperlink NAS) — **cả 2 AddColumn-only, no new table** (tables giữ 88). Prev Mig 50 `ReplaceBudgetModuleWithPeWorkItemBudgets` (bảng `PeWorkItemBudgets` per-gói-thầu + DROP module Budget cũ + backfill BudgetManual→BudgetPeriod TRƯỚC DropColumn + DROP PE/Contracts.BudgetId; gotcha #63/#64). Prev Mig 49 PE WorkItemId (S57bis) | +| Migrations | **56** | +S76: Mig 56 `AddProBudgetSplitToPeWorkItemBudget` (PeWorkItemBudget +`ProInitialAmount`+`ProAdjustmentAmount` decimal(18,2)? — cột PRO "Ban hành lần đầu"+"V0/hiệu chỉnh" mirror CCM Initial/Adjustment; AddColumn additive + `Sql()` data-migrate `ProInitial=ProEstimate` 4 rows prod gotcha #64; no new table). +S74: Mig 55 `AddCcmNoteToPeWorkItemBudget` (PeWorkItemBudget +CcmNote nvarchar(1000) — "Ghi chú từ CCM" mirror ProNote · additive-nullable, 0 backfill, no new table). +S73: Mig 54 `AddPeSuggestedAndApprovedPrice` (PE +5 cột giá đề xuất PRO Min/Max + CCM + giá chốt ApprovedPriceAmount/Source — additive-nullable, 0 backfill, prod-verified Run #313). +S69: Mig 53 `AddPeUrgentAndCeoApprovalThreshold` (PE +IsUrgentByPro/Ccm cờ gấp + ApprovalWorkflow +CeoApprovalThreshold ngưỡng CCM duyệt-final · 3 AddColumn no new table). Prev S65: Mig 51 `AddDepartmentParentId` (Department.ParentId loose-Guid no-FK — org-tree) + Mig 52 `AddHoSoLinkToPurchaseEvaluation` (PE HoSoLink nvarchar(1000) hyperlink NAS) — **cả 2 AddColumn-only, no new table** (tables giữ 88). Prev Mig 50 `ReplaceBudgetModuleWithPeWorkItemBudgets` (bảng `PeWorkItemBudgets` per-gói-thầu + DROP module Budget cũ + backfill BudgetManual→BudgetPeriod TRƯỚC DropColumn + DROP PE/Contracts.BudgetId; gotcha #63/#64). Prev Mig 49 PE WorkItemId (S57bis) | | SQL tables | **88** | re-ground S62 (cicd `sys.tables` Run #286 — Mig 50 XÓA module Budget (drop nhiều bảng) + CREATE `PeWorkItemBudgets` → net 93→88) | | Master data (prod) | **71 WorkItems PMH-only S59** | 62 Projects + **WorkItems = ĐÚNG 71 mã PMH** (S59: wipe 15 demo + rename format anh Kiệt `MAT-1..16`/`SUB-1..30`/`MEP-SUB-1..9`/`MEP-EQU-1..16`, Name "STT nhóm tên") + Suppliers 22 (3 real + demo; POST mở any-auth S59 quick-add). Transactional testing data wiped S59 (PE/HĐ/Notif = 0 baseline, phiếu thật từ A/001). Provenance `scripts/master-import-data.generated.md` | | API endpoints | **~253** | +1 S54 `GET /it-tickets/assignable-staff` (capability endpoint); +3 S52 (attendances/report + report/excel + it-tickets/{id}/assign) | | FE pages | **68** | unchanged S54 (ItTicketsPage reassign = in-place 2 app); +1 S52 AttendanceReportPage | | Menu keys | **54** | +1 S69 `Off_Dashboard` (landing Văn phòng số). Prev re-ground S61 (`MenuKeys.cs` const — Mig 50 gỡ 4 `Bg_*` Budget menu cũ). Prev 57 (S58) | -| Tests | **339 PASS** | 45 Domain + 294 Infra · 0 fail / 0 skip · **S74 +5** (`PeWorkItemBudgetTests` §4b CcmNote: set CCM/Admin · null-clear absolute-set · non-priv Forbidden+no-mutate · all-3-persist) · **S73 +28** (PE opt-in finalize spec 6→11 + 10 giá-chốt `PeApprovedPriceFinalizeTests` + 13 setter-authz `PeSuggestedPriceSetterAuthzTests`) · **S69 +20** (Office-golive OfficeModulePermissionSeed 6 + PE PeCcmThresholdFinalize 5 + PeUrgentToggleAuthz 9) · **S67 +23 HRM test-after** (DepartmentTreeTests 8 cycle-guard/rollup + PeHoSoLinkTests 9 absolute-set + HrmProfilePermissionSeedTests 6 reflection-seed) → 286 · S61 +22 `PeWorkItemBudgetTests` −14 `BudgetPolicyTests` −1 → 263 · S60 +14 `PeSubmitGuardAndBypassTests` +2 spec → 256 (Domain 58→45 do drop Budget module tests) | -| Gotchas | **69** | +1 S72 **#69** FE bundle-hash non-deterministic + `deploy.yml` rebuild-FE-unconditional → bundle rotate kể cả commit governance (SPA-fallback-200 trap; cicd Run #312). +2 S69: **#67** Tailwind v4 accent palette thiếu stop (teal/violet/amberx/greenx chỉ 50/100/500/600/700; dùng -300 → teal/violet rơi DEFAULT Tailwind sai-hệ, amberx/greenx drop hẳn — build PASS, phải soi dist CSS) · **#68** IDE TS diagnostic giữa background-agent/workflow = snapshot dở-dang (TS server bắn mỗi save), chỉ tin build SẠCH sau-cùng (2× session này: re-skin + PE-FE đều false-alarm). Prev +1 S68 **#66** Tailwind v4 rule element thô `h1-h4{color:#0b1220}` viết NGOÀI `@layer` thắng utility `text-white` → heading render đen; fix ĐIỂM `text-white!` (important, grep dist confirm), KHÔNG move `@layer` (load-bearing ~30+ heading toàn app). Prev +1 S65 **#65** build csproj con (vd `SolutionErp.Api.csproj`) ≠ `dotnet build SolutionErp.slnx` (gồm tests) → miss test-compile khi đổi chữ ký record command (CreateDepartmentCommand +ParentId) → CI CS7036 FAIL-gated Run #291 (deploy chặn, prod nguyên — test-gate làm đúng việc). Fix: build full slnx trước push BE signature-change. Prev +2 S61: **#63** EF scaffold tự sinh `RenameColumn` SAI-semantics khi drop+add cùng type (test xanh không bắt — SQLite EnsureCreated không replay migration) · **#64** `dotnet ef database update` áp Design-DB 0-rows ≠ Dev-DB → data-migrate `Sql()` chạy thật lần đầu trên prod. Prev +2 S59 (#61 sqlcmd `-f 65001` · #62 rename natural-key UPDATE trước deploy) | +| Tests | **344 PASS** | 45 Domain + 299 Infra · 0 fail / 0 skip · **S76 +5** (`PeWorkItemBudgetTests` PRO split: set ProInitial+ProAdjust gồm âm · validator ProInitial≥0/ProAdjust-cho-âm · full=proFull khi CCM empty · full=CCM khi CCM present) · **S74 +5** (`PeWorkItemBudgetTests` §4b CcmNote: set CCM/Admin · null-clear absolute-set · non-priv Forbidden+no-mutate · all-3-persist) · **S73 +28** (PE opt-in finalize spec 6→11 + 10 giá-chốt `PeApprovedPriceFinalizeTests` + 13 setter-authz `PeSuggestedPriceSetterAuthzTests`) · **S69 +20** (Office-golive OfficeModulePermissionSeed 6 + PE PeCcmThresholdFinalize 5 + PeUrgentToggleAuthz 9) · **S67 +23 HRM test-after** (DepartmentTreeTests 8 cycle-guard/rollup + PeHoSoLinkTests 9 absolute-set + HrmProfilePermissionSeedTests 6 reflection-seed) → 286 · S61 +22 `PeWorkItemBudgetTests` −14 `BudgetPolicyTests` −1 → 263 · S60 +14 `PeSubmitGuardAndBypassTests` +2 spec → 256 (Domain 58→45 do drop Budget module tests) | +| Gotchas | **70** | +1 S76 **#70** (FE absolute-set echo field anh-em từ server-snapshot + `invalidate()` fire-and-forget → cửa-sổ stale-echo mất-dữ-liệu khi lưu 2 ô liên-tiếp; guard `useIsFetching` gate nút Lưu tới khi refetch land; reviewer workflow bắt, impl self-review sót). +1 S72 **#69** FE bundle-hash non-deterministic + `deploy.yml` rebuild-FE-unconditional → bundle rotate kể cả commit governance (SPA-fallback-200 trap; cicd Run #312). +2 S69: **#67** Tailwind v4 accent palette thiếu stop (teal/violet/amberx/greenx chỉ 50/100/500/600/700; dùng -300 → teal/violet rơi DEFAULT Tailwind sai-hệ, amberx/greenx drop hẳn — build PASS, phải soi dist CSS) · **#68** IDE TS diagnostic giữa background-agent/workflow = snapshot dở-dang (TS server bắn mỗi save), chỉ tin build SẠCH sau-cùng (2× session này: re-skin + PE-FE đều false-alarm). Prev +1 S68 **#66** Tailwind v4 rule element thô `h1-h4{color:#0b1220}` viết NGOÀI `@layer` thắng utility `text-white` → heading render đen; fix ĐIỂM `text-white!` (important, grep dist confirm), KHÔNG move `@layer` (load-bearing ~30+ heading toàn app). Prev +1 S65 **#65** build csproj con (vd `SolutionErp.Api.csproj`) ≠ `dotnet build SolutionErp.slnx` (gồm tests) → miss test-compile khi đổi chữ ký record command (CreateDepartmentCommand +ParentId) → CI CS7036 FAIL-gated Run #291 (deploy chặn, prod nguyên — test-gate làm đúng việc). Fix: build full slnx trước push BE signature-change. Prev +2 S61: **#63** EF scaffold tự sinh `RenameColumn` SAI-semantics khi drop+add cùng type (test xanh không bắt — SQLite EnsureCreated không replay migration) · **#64** `dotnet ef database update` áp Design-DB 0-rows ≠ Dev-DB → data-migrate `Sql()` chạy thật lần đầu trên prod. Prev +2 S59 (#61 sqlcmd `-f 65001` · #62 rename natural-key UPDATE trước deploy) | | User memory | **29** | +S75 `feedback_harness11_engine` (engine governance tự-bảo-trì + gotcha #30-via-Edit + multi-workflow-verify + git-add-specific lesson). re-ground S71 (H1 disk-count: 25 `feedback_*` + 3 `project_*`; STATUS "21" stale từ S64). +S71 `feedback_harness10_run_trace` (run-trace + 3 lesson). Prev +1 S64 `feedback_harness7_outward_writing_quality`; re-grounded S54 base 19 | | Skills | 6 | 3 domain + 3 ops | | Sub-agents | **11** | **all-inherit top-tier (Harness-8 S66 06-16 — thay thế two-tier H4):** em main Fable 5 (1M) Max (fallback Opus 4.8 1M, Fable suspended H5) · **cả 11 sub `model: inherit`** (7 demoted `claude-opus-4-8` flip S66; SE không có helper/gopher rẻ → cả 11 lên top-tier) · effort Max. 9 product/quality + 2 monitor INFORM-only. Nấc H8 = **executed-file VERIFIED-pending-restart**. Prev two-tier H4 runtime-verified S57bis (spawn-test 2 chiều). | | RAG chunks | **2423** | re-check S63 (`list_projects` — alive, +3 vs S58). Stale `last_indexed 05-29` (S42-S62 via store_memory stopgap; full re-index = AI_INFRA op cần VOYAGE_API_KEY). | -**Bundle hash live (prod):** admin **`BYF5vIMJ`** · user **`CB-tiRxd`** (S74 — **Run #315** `8655ebf` Mig 55 CcmNote, cicd PASS ~4m54s: Mig 55 applied prod CcmNote nvarchar(1000) nullable · sys.tables 88 · smoke 4×200 · 0 regression). **Prev S73** — admin **`Bv3jUCNo`** · user **`BWlMBQz6`** (Run #314 `6aa4dcb` FE empty-candidates guard fix; chuỗi 2 deploy: #313 `1d86abc` feature `OlNyG9OD`/`DSzSLVtL` → #314 **`Bv3jUCNo`**/**`BWlMBQz6`**, cicd PASS each). **Prev S72** — admin **`fc_xkNpJ`** · user **`DP-tBcg0`** (Run #312 governance commit `18fced6`; bundle ROTATE do **non-deterministic hash + deploy rebuild-FE-unconditional** (gotcha #69) — 0 FE source change, prod state/DB GIỮ NGUYÊN). **Prev S69** — admin **`BgNCjwsG`** · user **`CBvh0vtf`** (Run #308 PE cờ gấp + ngưỡng CCM; chuỗi 4 deploy session: #305 Văn phòng số foundation → #306 re-skin 10 page → #307 Office golive (BE-only) `Wt54PHYl`/`B99fMU6X` → #308 **`BgNCjwsG`**/**`CBvh0vtf`**). **Prev S68** — admin **`CNUv1jxY`** · user **`CpOskeS1`** (S68 — Run #304 `37752eb` fix tên đen→trắng; chuỗi #303 `6983609` `D532XZKG`/`CuFaBoWt` → #304 **`CNUv1jxY`**/**`CpOskeS1`**, cicd PASS each). **Prev S67** — admin `CcrZqfht`/user `DniDFUB_` (S67 — 6 deploy Run #297→#302; admin rotate chuỗi #298`xkSz9BfE`→#300`PxiZQkaw`→#301`I1fpLeYw`→#302**`CcrZqfht`** · user #297`BumgrwCJ`→#300`B36hGoKd`→#301`DrQYkzh0`→#302**`DniDFUB_`**; #299 tests BE-only cả 2 frozen, cicd PASS each). **Prev S65** — admin `BDwV5d0X`/user `DbVv6rsf` (Run #293-295). Prev S62 admin `0xKYGhhf`/user `C81ZdG9G` (Run #286). **Prev S59:** admin `B1DtNT9C` · user `D6uF3Mln` (Run **#282** `792c030` FINAL — bỏ nút Thêm hạng mục; ships kèm `80b64dd` gỡ Điều khoản TT, #281 cancelled-benign ancestor-verified). Chuỗi S59 cùng ngày: #280 `69997da` (`BKy_8OO9`/`XcZ6PRyA`, ships kèm `f21c55d` table-fixed #279-cancelled) · #278 `9c330d2` self-approve+quick-add-NCC (`BSh2fG2X`/`D22KfpPc`, authz probe 4/4) · #277 `faed59f` SearchableSelect (`ex7Tc92G`/`DzUeSk96`) · #276 `c869d26` rename 71 PMH (`BBA0KSWu`/`DzdTI18G`) · #275 `bbd1554` dọn demo WorkItems (FROZEN BE-only) · #274 `0eafcd3` tree 4 tầng (`DuU7OTym`/`DWyeTzf3`) · #273 `56882ac` wipe + tree v1 (`R9uGRxvw`/`DikfX1RD`). Prev S58: Run #386 `3ebaf84` admin `DMm9rtNA`/user `BUkOMn_Y` (chi tiết session log S58). -**Phase:** ✅ Phase 10 COMPLETE · ✅ **Phase 11 product backlog ĐÓNG TRỌN** · ✅ **Văn phòng số (E-Office) golive S69** (foundation PURO + re-skin 10 page + public read+create 16-key allow-list mọi role) prod-verified · ✅ **PE cờ gấp PRO/CCM + CCM duyệt-final theo ngưỡng (Mig 53 S69)** prod-verified · 🚫 Phase 9 Ops blocked (anh main coordinate — anh dừng). +**Bundle hash live (prod):** admin **`jOqxW4-p`** · user **`DbsznVvR`** (S76 — **Run #319** `21d1f4e` bảng lưới `
` ngân sách viền-ô; chuỗi S76 cùng ngày: #318 `e33481e` feature ma-trận-3-cột + badge quyền-NS `BhFDF9IJ`/`BAkuRl3C` → #319 **`jOqxW4-p`**/**`DbsznVvR`**, cicd PASS each, smoke 3×200, Mig 56 applied prod #318). **Prev S74** — admin **`BYF5vIMJ`** · user **`CB-tiRxd`** (S74 — **Run #315** `8655ebf` Mig 55 CcmNote, cicd PASS ~4m54s: Mig 55 applied prod CcmNote nvarchar(1000) nullable · sys.tables 88 · smoke 4×200 · 0 regression). **Prev S73** — admin **`Bv3jUCNo`** · user **`BWlMBQz6`** (Run #314 `6aa4dcb` FE empty-candidates guard fix; chuỗi 2 deploy: #313 `1d86abc` feature `OlNyG9OD`/`DSzSLVtL` → #314 **`Bv3jUCNo`**/**`BWlMBQz6`**, cicd PASS each). **Prev S72** — admin **`fc_xkNpJ`** · user **`DP-tBcg0`** (Run #312 governance commit `18fced6`; bundle ROTATE do **non-deterministic hash + deploy rebuild-FE-unconditional** (gotcha #69) — 0 FE source change, prod state/DB GIỮ NGUYÊN). **Prev S69** — admin **`BgNCjwsG`** · user **`CBvh0vtf`** (Run #308 PE cờ gấp + ngưỡng CCM; chuỗi 4 deploy session: #305 Văn phòng số foundation → #306 re-skin 10 page → #307 Office golive (BE-only) `Wt54PHYl`/`B99fMU6X` → #308 **`BgNCjwsG`**/**`CBvh0vtf`**). **Prev S68** — admin **`CNUv1jxY`** · user **`CpOskeS1`** (S68 — Run #304 `37752eb` fix tên đen→trắng; chuỗi #303 `6983609` `D532XZKG`/`CuFaBoWt` → #304 **`CNUv1jxY`**/**`CpOskeS1`**, cicd PASS each). **Prev S67** — admin `CcrZqfht`/user `DniDFUB_` (S67 — 6 deploy Run #297→#302; admin rotate chuỗi #298`xkSz9BfE`→#300`PxiZQkaw`→#301`I1fpLeYw`→#302**`CcrZqfht`** · user #297`BumgrwCJ`→#300`B36hGoKd`→#301`DrQYkzh0`→#302**`DniDFUB_`**; #299 tests BE-only cả 2 frozen, cicd PASS each). **Prev S65** — admin `BDwV5d0X`/user `DbVv6rsf` (Run #293-295). Prev S62 admin `0xKYGhhf`/user `C81ZdG9G` (Run #286). **Prev S59:** admin `B1DtNT9C` · user `D6uF3Mln` (Run **#282** `792c030` FINAL — bỏ nút Thêm hạng mục; ships kèm `80b64dd` gỡ Điều khoản TT, #281 cancelled-benign ancestor-verified). Chuỗi S59 cùng ngày: #280 `69997da` (`BKy_8OO9`/`XcZ6PRyA`, ships kèm `f21c55d` table-fixed #279-cancelled) · #278 `9c330d2` self-approve+quick-add-NCC (`BSh2fG2X`/`D22KfpPc`, authz probe 4/4) · #277 `faed59f` SearchableSelect (`ex7Tc92G`/`DzUeSk96`) · #276 `c869d26` rename 71 PMH (`BBA0KSWu`/`DzdTI18G`) · #275 `bbd1554` dọn demo WorkItems (FROZEN BE-only) · #274 `0eafcd3` tree 4 tầng (`DuU7OTym`/`DWyeTzf3`) · #273 `56882ac` wipe + tree v1 (`R9uGRxvw`/`DikfX1RD`). Prev S58: Run #386 `3ebaf84` admin `DMm9rtNA`/user `BUkOMn_Y` (chi tiết session log S58). +**Phase:** ✅ Phase 10 COMPLETE · ✅ **Phase 11 product backlog ĐÓNG TRỌN** · ✅ **Văn phòng số (E-Office) golive S69** (foundation PURO + re-skin 10 page + public read+create 16-key allow-list mọi role) prod-verified · ✅ **PE cờ gấp PRO/CCM + CCM duyệt-final theo ngưỡng (Mig 53 S69)** prod-verified · ✅ **PE ngân sách MA TRẬN 3 cột Dự án/PRO/CCM (bảng lưới `
`) + badge quyền-NS theo role trong Workflow Designer & flow Duyệt NCC (Mig 56 S76)** prod-verified · 🚫 Phase 9 Ops blocked (anh main coordinate — anh dừng). > ⚠️ **Count drift fixed S40:** endpoints ~223→**211**, FE pages 53→**65**, menu keys 85→**~53**. Tables **84 confirmed correct** (DbSet 77 + Identity 7). 3 số "khó fake" (mig/gotcha/git) luôn đúng. Cause: số "incremented mỗi session" over/under-count optimistic — re-ground định kỳ. --- -## 🔥 In Progress (S75) +## 🔥 In Progress (S76) | Task | Owner | Status | |---|---|---| +| **S76 — PE ngân sách MA TRẬN 3 cột (Dự án\|PRO\|CCM) + bảng lưới `
` + badge quyền-NS theo role (anh Kiệt FDC + chị Trà Sol, go-live)** — anh giao `/ultra-on` step-by-step + workflow-review-per-step. **3 commit prod-verified:** `e33481e` (feature Run #318 `BhFDF9IJ`/`BAkuRl3C`) → `21d1f4e` (bảng lưới Run #319 **`jOqxW4-p`**/**`DbsznVvR`**). **Part 1** form ma trận: PRO/CCM sửa cột mình (canEditPro/canEditCcm), Dự án FE-only; Mig 56 +ProInitial/ProAdjust additive + data-migrate 4 rows prod. **Part 2+3** badge "✎ NS PRO/CCM" cạnh approver (suy role, display-only) ở Workflow Designer + flow Duyệt NCC. Bảng lưới `
` viền-ô (anh phản hồi "chưa chia cột giống Excel"). Test 339→**344**. 2 workflow review bắt **race mất-dữ-liệu** → vá `useIsFetching` (gotcha #70). **NEXT:** 🔴 curate L1 (reviewer **45KB** keep-floor manual-condense + inv 35KB + cicd 29KB + test-spec 28KB) · anh/anh Kiệt UAT bằng PRO/CCM. → log `2026-06-19-S76-pe-budget-matrix-table-grid.md`. | 👤 + 🟦inv + 🟪test + 🟧impl-FE + 🟥reviewer×2wf + 🟩cicd×2 | ✅ | | **S75 — Harness-11 adopt (engine governance tự-bảo-trì) + 2 double-check (anh giao)** — `/check-email AI_INFRA` (H11 directed verify ✓ whole-file `318ff9f6`+body `b2a2fc1c`) → `/adap-apply` qua **5 Workflow** (audit→implement→review→double-check→checklist-verify, run-id = evidence). Built 2 NO-API detector-script (`governance-detectors.ps1` C1-C5+B3 runtime 26 flag · `memory-archive-gate.ps1` PHẦN A A7 186/186) + `harness-11-engine.md` canonical + B1 ×11 count→pointer + cadence-wire D1/D2 + agents/README. **completeness-gate FORMAL ĐẠT** (B 4/4 + C 5/5 + D 11/11; D5/D6/D7 explicit + D8 codify=YES). **0 production code.** 3 commit pushed (`e70c046` adopt / `ae957c4` double-check+finalize / `aa09e99` cleanup scratch). adap-report + email ai_infra (`2316773229f2`). gotcha #30 reinforced (box-glyph .ps1 trap kể cả qua Edit). **NEXT:** restart CLI (cadence §2.1.3/§L.b) · 🔴 curate reviewer 38.8KB+inv 31.5KB+cicd 26.8KB over-cap (archive-gate confirm; reviewer+inv keep-floor hit → manual split). → log `2026-06-18-S75-harness-11-adopt.md`. | 👤 + 5 Workflow (16 sub) + 2 monitor | ✅ | | **S74 — PE ô "Ghi chú từ CCM" ngân sách gói thầu (Mig 55)** — anh forward chat Trà Sol + Kiệt FDC → CCM giống PRO (nhập số + ghi chú), "0 hết" = role CostControl thiếu (không bug). em-main BE+Mig55 (entity/config/command/DTO/controller/mapping) ∥ implementer-frontend FE 2 app SHA-mirror ∥ test-specialist (chết rate-limit→recover-disk). 2 fork anh chốt: +CcmNote (Mig 55) + GIỮ phân vai CCM. Test 334→**339**. commit `8655ebf` (cicd **Run #315 PASS** — bundle `BYF5vIMJ`/`CB-tiRxd`, Mig 55 applied). → log `2026-06-18-S74-pe-ccm-note.md`. **NEXT:** anh UAT bằng CCM/Admin; curate reviewer 38.8KB + inv-codebase 31.5KB. | 👤 + 🟧 impl-FE + 🟪 test-spec(recover) + 🟩 cicd | ✅ | | **S72 — Harness-10 flat-refine + checklist-v2 adopt (re-audit 3-workflow) + sleep NO-OP** — audit `wf_13868efb-ea7` → implement `wf_ac43b5ff-7d1` → review `wf_d482e10d-5dd` (Harness-9 B1+B2). SE BEHIND flat → migrate SUBFOLDER→FLAT (hmw.js + 5 doc; 5 run cũ giữ subfolder C8) + 2 broadcast 06-18 + sleep-cmd port (A8) + detector tailored-out + hmw.js H4.5→H8. **REVIEW bắt 2 lỗi IMPLEMENT-self-assess sót** (schema-stale + auto-wire overclaim) → fixed (auto-check WIRED). 1 reviewer residual-write self-caught+revert (reverse-finding #4). B3 self-correct path/count. Sleep `all`=NO-OP. **0 prod code.** adap-report + email (`5f511fe5`). **NEXT (anh):** restart CLI (FLAT-runtime + wired sleep-check + sleep-recovery skill). **NEXT (em):** 🔴 curate investigator-codebase L1 **28973 OVER** + cicd-monitor **30123 OVER** (Run #312) + watch FD/test-spec. → session log `2026-06-18-S72-harness-10-flat-refine.md`. | 👤 + 9 sub | ✅ | diff --git a/docs/changelog/sessions/2026-06-19-S76-pe-budget-matrix-table-grid.md b/docs/changelog/sessions/2026-06-19-S76-pe-budget-matrix-table-grid.md new file mode 100644 index 0000000..7e68847 --- /dev/null +++ b/docs/changelog/sessions/2026-06-19-S76-pe-budget-matrix-table-grid.md @@ -0,0 +1,48 @@ +# S76 (2026-06-19) — PE ngân sách MA TRẬN 3 cột + bảng lưới `
` + badge quyền-NS theo role + +> **Anh Kiệt FDC + chị Trà Sol — go-live.** anh giao `/ultra-on` "làm hết, step-by-step có workflow review kiểm tra đàng hoàng, hoàn chỉnh rồi deploy báo tao". + +## Bối cảnh (chat Zalo anh forward) +- Chị Trà Sol + anh Kiệt: panel "TỔNG HỢP NGÂN SÁCH TRÌNH KÝ" — **mỗi phòng (PRO/CCM) nhập + điều chỉnh ngân sách của CHÍNH phòng mình**; ảnh Excel = ma trận cột **DỰ ÁN | PRO | CCM**, mỗi cột có Ban hành lần đầu + V0/hiệu chỉnh → full = tổng. +- Thêm: cột tích "quyền nhập/điều chỉnh NS" bên flow (nhìn biết ai sửa được) + hiển thị ra fe-user mục Duyệt NCC. + +## 2 fork anh chốt (AskUserQuestion) +1. **Cột DỰ ÁN** = FE hiển-thị-only (`—`), sau mới có người bên dự án nhập (đơn giản nhất, FE-only). +2. **Ô tích quyền NS ở flow** = chỉ-hiển-thị, GIỮ quyền theo role (PRO→cột PRO, CCM→cột CCM) — làm trước cho nhanh, KHÔNG configurable. + +## Step 1 — ĐIỀU TRA (verify-workflow: "phần này hôm qua làm chưa?") +3 lens độc-lập (code-gate + git-history + adversarial) → **CHƯA**. Hôm qua S74 chỉ thêm CcmNote. Ngân sách edit thuần ROLE (`canEditPro`/`canEditCcm`, `PurchaseEvaluationFeatures.cs:800-801`), KHÔNG có submission-count lock. Phát hiện: "Ban hành/hiệu chỉnh" cũ gán CCM, nhưng ảnh Excel cho thấy PRO mới điền → design mới phải TÁCH mỗi phòng cột riêng. + +## Done — 3 commit prod-verified + +### `e33481e` (feature, cicd Run #318 PASS, bundle admin `BhFDF9IJ` / user `BAkuRl3C`) +- **Part 1 — form ma trận 3 cột.** Data-model (em-main solo, additive-an-toàn): + - Entity `PeWorkItemBudget` +`ProInitialAmount`+`ProAdjustmentAmount` (decimal 18,2 nullable) — cột PRO; tái dùng `InitialAmount`/`AdjustmentAmount` làm cột CCM (đã canEditCcm-gate). `ProEstimateAmount` = LEGACY (FE bỏ). + - **Mig 56 `AddProBudgetSplitToPeWorkItemBudget`** — 2 AddColumn additive + `Sql()` data-migrate `UPDATE PeWorkItemBudgets SET ProInitialAmount=ProEstimateAmount WHERE ProEstimateAmount IS NOT NULL AND ProInitialAmount IS NULL` (giữ số PRO cũ; chạy SAU AddColumn; idempotent; **4 rows backfill prod** gotcha #64). Additive-only → tránh gotcha #63. ASCII-only comment (gotcha #30). + - Handler `UpdatePeBudgetProCommand` đổi `(PeId, ProInitialAmount, ProAdjustmentAmount, ProNote)` absolute-set, role-gate PRO/Admin Forbidden TRƯỚC side-effect + validator (ProInitial≥0, ProAdjust cho-âm) + controller `BudgetProBody` + DTO `PeBudgetSummaryDto` +2 field + capability `full` = CCM nếu hasCcm else proFull, `FullIsEstimate = !hasCcm && hasPro`. + - FE 2 app SHA-mirror: `PeBudgetSummaryTable` Block A ma trận (DỰ ÁN hiển-thị-only / PRO canEditPro / CCM canEditCcm) + `BudgetCell` compact + `BudgetNoteRow`. proFull/ccmFull = ban-hành + hiệu-chỉnh mỗi cột. +- **Part 2+3 — badge quyền-NS theo role (display-only).** + - BE: +2 cờ `CanEditProBudget`/`CanEditCcmBudget` per approver — tính qua 3 `GetUsersInRoleAsync` set-lookup (Procurement∪Admin / CostControl∪Admin, **no N+1**, khớp gate `canEditPro`/`canEditCcm` bit-for-bit) → vào `AwLevelDto` (designer, `ApprovalWorkflowV2AdminFeatures.cs`) + `PurchaseEvaluationApprovalLevelApproverDto` (PE flow, 2 build-site: approvalFlow + currentApproval). + - FE: badge "✎ NS PRO"(amber)/"✎ NS CCM"(sky) cạnh approver ở `ApprovalWorkflowsV2Page` (fe-admin designer) + `PeWorkflowPanel` (2 app, `.join`→map từng approver). Types +2 cờ CẢ 2 app (purchaseEvaluation.ts types DIFFER giữa 2 app). +- **Test 339→344** (+5 PRO split: set cả ProInitial+ProAdjust gồm âm · validator · full=proFull khi CCM empty · full=CCM khi CCM present). test-specialist. + +### `21d1f4e` (bảng lưới, cicd Run #319 PASS, bundle admin `jOqxW4-p` / user `DbsznVvR`) +- Anh phản hồi "giao diện vẫn chưa chia cột giống Excel" — Block A cũ dùng flex+gap (KHÔNG viền dọc) → chuyển **`
` viền ô đầy đủ** (header Khoản mục|Dự án|PRO|CCM + 5 hàng: full / ban hành / hiệu chỉnh / ghi chú PRO / ghi chú CCM). +- `BudgetCell` xếp DỌC (input full ô + nút Lưu dưới — vừa cột hẹp). `BudgetNoteRow`→`BudgetNoteCell` (td colSpan=3, ghi chú trải hết). +- FE-only, 2 app SHA-mirror. **LESSON: flex+gap KHÔNG ra "bảng" — phải `
` viền ô mới giống spreadsheet.** + +### Race fix (gotcha #70 NEW) +Reviewer workflow Part 2/3 escalate MAJOR data-loss: 2 ô PRO (ban hành + hiệu chỉnh) lưu qua CÙNG `proMut`, mỗi save echo field anh-em từ `bs` (server snapshot). Lưu ô A → `invalidate()` refetch BẤT-ĐỒNG-BỘ; trước khi refetch về, `bs.proInitial` còn CŨ → lưu ô B đè mất A. Vá: gate nút Lưu = `mut.isPending || useIsFetching({queryKey:['pe-detail',ev.id]}) > 0` (khoá tới khi refetch land). 2 app. Pattern absolute-set-echo có từ CCM Mig 50/55 prod chưa-báo-lỗi nhưng PRO nhân-đôi bề-mặt → vá trước go-live tài-chính. + +## Workflow / harness +- HMW-mode ON (`/ultra-on`). 1 verify-workflow (Step-1) + 2 review-workflow (Part 1 · Part 2/3). Mỗi step build + review trước khi sang step kế (đúng anh giao). +- **Workflow-review-flaky:** 2/3 lane Part 1 + 1/3 lane Part 2/3 return RỖNG (#53). Lane trả về thì đầy-đủ + chính xác. → em-main self-gate lane rỗng (BE đã test-spec verify + build/test pass; FE lane cross-check). Khớp `feedback_workflow_fanout_reliability`: verify-heavy → em-main self-gate ≈ spawn lẻ. +- cwd-misland (gotcha tái diễn): impl-FE `cd fe-admin` npm build → MEMORY rơi `fe-user/.claude` stray → em-main harvest về canonical + thêm `.gitignore` guard `fe-*/.claude/`. + +## State THẬT (verified) +Mig **56** · 88 bảng · **344** test (45D+299I) · gotcha **70** · menu 54 · bundle admin **`jOqxW4-p`** / user **`DbsznVvR`** (Run #319) · RAG 2428 · user-memory 29. + +## 🔴 NEXT +- **Em (carry ưu-tiên):** curate L1 over-cap — reviewer **45KB** + inv-codebase **35KB** (keep-floor-hit, entries newest lớn → manual SPLIT/condense) + cicd 29KB + test-spec 28KB (strike-1). archive-gate A7 PASS 186/186. +- **Anh/anh Kiệt:** UAT bảng lưới (Ctrl+F5) bằng PRO/CCM; xem badge ✎NS trong Designer + flow. +- **Ops giữ:** tzutil VPS · anh Chương email typo · 5 real-staff pw · gán CNTT. Monthly audit 2026-07-01. diff --git a/docs/gotchas.md b/docs/gotchas.md index 1aca16b..33c3346 100644 --- a/docs/gotchas.md +++ b/docs/gotchas.md @@ -1218,6 +1218,20 @@ for h in resp.points: # ← .points không phải iterable trực tiếp --- +### 70. FE absolute-set form echo field anh-em từ server-snapshot + `invalidate()` fire-and-forget → cửa-sổ STALE-ECHO mất dữ-liệu khi lưu 2 ô liên-tiếp (Session 76) + +**Triệu chứng:** Form ngân sách PE — 2 ô cùng cột PRO (ban hành + hiệu chỉnh) lưu qua CÙNG `proMut`, mỗi save echo field anh-em từ `bs` (server snapshot). Lưu ô A (proInitial) → `invalidate()` refetch BẤT-ĐỒNG-BỘ; TRƯỚC khi refetch về, `bs.proInitial` còn CŨ → lưu ô B (proAdjust) echo `bs.proInitial` cũ → **đè mất giá-trị A**. Workflow review (reviewer) bắt — implementer self-review SÓT. + +**Cơ chế:** BE handler absolute-set (set thẳng cả 3 field, thiếu=null=CLEAR) → FE PHẢI echo field không-đổi. Echo lấy từ `bs` (props từ query) — STALE trong cửa-sổ [mutate-resolve → refetch-land]. `mut.isPending` chỉ true trong HTTP mutate, KHÔNG cover refetch → nút Lưu re-enable khi `bs` vẫn cũ. + +**Guard:** Gate nút Lưu = `mut.isPending || useIsFetching({queryKey:['pe-detail',ev.id]}) > 0` — khoá save tới khi refetch land (`bs` fresh). Đóng cửa-sổ stale-echo. Áp CẢ PRO+CCM cells + notes. (Pattern absolute-set-echo có từ CCM Mig 50/55 prod chưa-báo-lỗi nhưng PRO nhân-đôi bề-mặt → vá trước go-live tài-chính.) + +**Credit:** reviewer S76 (2-lane workflow review Part 2/3) escalate MAJOR data-loss; em-main vá `useIsFetching` ×2 app trước deploy. + +**References:** `fe-*/src/components/pe/PeDetailTabs.tsx` (PeBudgetSummaryTable `peFetching`) · gotcha #44 (silent issue self-review bias). + +--- + ## Checklist debug bug mới 1. Build pass không? → fail → check using + package version compat